From 75c6af6d0ef580de999a02e8b02ea57efc47fe45 Mon Sep 17 00:00:00 2001 From: Sergi Granell Date: Fri, 16 May 2014 18:24:09 +0200 Subject: [PATCH 001/317] Fixed some printf format Silenced warnings --- ctrtool/Makefile | 2 +- ctrtool/exheader.c | 6 +++--- makerom/Makefile | 4 ++-- makerom/accessdesc.c | 10 +++++----- makerom/dir.c | 4 ++-- makerom/libyaml/yaml_private.h | 2 +- makerom/ncsd.c | 4 ++-- makerom/polarssl/base64.c | 6 +++--- makerom/polarssl/base64.h | 1 + makerom/polarssl/cipher.c | 1 + makerom/utf.h | 4 +--- makerom/utils.c | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 75a44fd6..5fdb2765 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -3,7 +3,7 @@ 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. +CFLAGS = -Wall -Wno-unused-variable -Wno-unused-but-set-variable -I. OUTPUT = ctrtool CC = gcc diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index 205a94d9..8c945480 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -581,11 +581,11 @@ void exheader_print(exheader_context* ctx) fprintf(stdout, "Dependency: %016llX\n", getle64(ctx->header.deplist.programid[i])); } if(savedatasize < sizeKB) - fprintf(stdout, "Savedata size: 0x%X\n", savedatasize); + fprintf(stdout, "Savedata size: 0x%llX\n", savedatasize); else if(savedatasize < sizeMB) - fprintf(stdout, "Savedata size: %dK\n", savedatasize/sizeKB); + fprintf(stdout, "Savedata size: %lluK\n", savedatasize/sizeKB); else - fprintf(stdout, "Savedata size: %dM\n", savedatasize/sizeMB); + fprintf(stdout, "Savedata size: %lluM\n", savedatasize/sizeMB); 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)); diff --git a/makerom/Makefile b/makerom/Makefile index c727cf34..f8f41181 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -15,7 +15,7 @@ YAML_OBJS = libyaml/api.o libyaml/dumper.o libyaml/emitter.o libyaml/loader.o li # Compiler Settings LIBS = -static-libgcc -static-libstdc++ CXXFLAGS = -I. -CFLAGS = --std=c99 -Wall -I. -DMAKEROM_VER_MAJOR=$(VER_MAJOR) -DMAKEROM_VER_MINOR=$(VER_MINOR) $(MAKEROM_BUILD_FLAGS) -m64 +CFLAGS = --std=c99 -Wall -Wno-unused-but-set-variable -Wno-unused-value -I. -DMAKEROM_VER_MAJOR=$(VER_MAJOR) -DMAKEROM_VER_MINOR=$(VER_MINOR) $(MAKEROM_BUILD_FLAGS) -m64 CC = gcc # MAKEROM Build Settings @@ -37,4 +37,4 @@ clean: # Windows compatibility rebuildwin: cleanwin build cleanwin: - del /Q objs $(OUTPUT).exe *.o polarssl\*.o libyaml\*.o *.cci *.cia *.cxi *.cfa \ No newline at end of file + del /Q objs $(OUTPUT).exe *.o polarssl\*.o libyaml\*.o *.cci *.cia *.cxi *.cfa diff --git a/makerom/accessdesc.c b/makerom/accessdesc.c index 4405952e..8de78844 100644 --- a/makerom/accessdesc.c +++ b/makerom/accessdesc.c @@ -114,27 +114,27 @@ int accessdesc_GetSignFromRsf(exheader_settings *exhdrset, ncch_settings *ncchse u32 out; out = 0x100; - result = base64_decode(exhdrset->keys->rsa.cxiHdrPub,&out,(const u8*)exhdrset->rsf->CommonHeaderKey.Modulus,strlen(exhdrset->rsf->CommonHeaderKey.Modulus)); + result = base64_decode(exhdrset->keys->rsa.cxiHdrPub,(size_t *)&out,(const u8*)exhdrset->rsf->CommonHeaderKey.Modulus,strlen(exhdrset->rsf->CommonHeaderKey.Modulus)); if(out != 0x100) result = POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL; if(result) goto finish; out = 0x100; - result = base64_decode(exhdrset->keys->rsa.cxiHdrPvt,&out,(const u8*)exhdrset->rsf->CommonHeaderKey.D,strlen(exhdrset->rsf->CommonHeaderKey.D)); + result = base64_decode(exhdrset->keys->rsa.cxiHdrPvt,(size_t *)&out,(const u8*)exhdrset->rsf->CommonHeaderKey.D,strlen(exhdrset->rsf->CommonHeaderKey.D)); if(out != 0x100) result = POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL; if(result) goto finish; /* Set AccessDesc */ out = 0x100; - result = base64_decode(exhdrset->exHdr->accessDescriptor.signature,&out,(const u8*)exhdrset->rsf->CommonHeaderKey.AccCtlDescSign, strlen( exhdrset->rsf->CommonHeaderKey.AccCtlDescSign)); + result = base64_decode(exhdrset->exHdr->accessDescriptor.signature,(size_t *)&out,(const u8*)exhdrset->rsf->CommonHeaderKey.AccCtlDescSign, strlen( exhdrset->rsf->CommonHeaderKey.AccCtlDescSign)); if(out != 0x100) result = POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL; if(result) goto finish; memcpy(exhdrset->exHdr->accessDescriptor.ncchRsaPubKey,exhdrset->keys->rsa.cxiHdrPub,0x100); out = 0x200; - result = base64_decode((u8*)&exhdrset->exHdr->accessDescriptor.arm11SystemLocalCapabilities,&out,(const u8*)exhdrset->rsf->CommonHeaderKey.AccCtlDescBin,strlen(exhdrset->rsf->CommonHeaderKey.AccCtlDescBin)); + result = base64_decode((u8*)&exhdrset->exHdr->accessDescriptor.arm11SystemLocalCapabilities,(size_t *)&out,(const u8*)exhdrset->rsf->CommonHeaderKey.AccCtlDescBin,strlen(exhdrset->rsf->CommonHeaderKey.AccCtlDescBin)); if(out != 0x200) result = POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL; if(result) goto finish; @@ -494,4 +494,4 @@ void b64_strcpy(char *dst, char *src) memdump(stdout,"src: ",(u8*)src,src_len+1); memdump(stdout,"dst: ",(u8*)dst,j+1); -} \ No newline at end of file +} diff --git a/makerom/dir.c b/makerom/dir.c index 68e5524f..2c2e0b41 100644 --- a/makerom/dir.c +++ b/makerom/dir.c @@ -277,7 +277,7 @@ void fs_PrintDir(fs_dir *dir, u32 depth) // This is just for simple debugging, p name = (char*)dir->file[i].name; for(u32 j = 0; j < dir->file[i].name_len; j+=2) putchar(name[j]); - printf(" (0x%lx)\n",dir->file[i].size); + printf(" (0x%llx)\n",dir->file[i].size); #endif } } @@ -324,4 +324,4 @@ void fs_FreeFiles(fs_dir *dir) fs_dir *tmp = (fs_dir*)dir->dir; for(u32 i = 0; i < dir->u_dir; i++) fs_FreeFiles(&tmp[i]); -} \ No newline at end of file +} diff --git a/makerom/libyaml/yaml_private.h b/makerom/libyaml/yaml_private.h index 31ed23cf..92bf799c 100644 --- a/makerom/libyaml/yaml_private.h +++ b/makerom/libyaml/yaml_private.h @@ -7,7 +7,7 @@ #include #include - +#include #define YAML_titleVersion_STRING "0.1.4" #define YAML_titleVersion_MAJOR 0 diff --git a/makerom/ncsd.c b/makerom/ncsd.c index 45e16a12..ec208567 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -538,12 +538,12 @@ int GetWriteableAddress(cci_settings *cciset, user_settings *usrset) if(cciset->cardinfo.writableAddress == -1){ // If not set manually or is max size if ((cciset->header.mediaSize / 2) < cciset->option.savedataSize){ // If SaveData size is greater than half the MediaSize u64 SavedataSize = cciset->option.savedataSize / KB; - fprintf(stderr,"[CCI ERROR] Too large SavedataSize %llK\n",SavedataSize); + fprintf(stderr,"[CCI ERROR] Too large SavedataSize %lldK\n",SavedataSize); return SAVE_DATA_TOO_LARGE; } if (cciset->option.savedataSize > (u64)(2047*MB)){ // Limit set by Nintendo u64 SavedataSize = cciset->option.savedataSize / KB; - fprintf(stderr,"[CCI ERROR] Too large SavedataSize %llK\n",SavedataSize); + fprintf(stderr,"[CCI ERROR] Too large SavedataSize %lldK\n",SavedataSize); return SAVE_DATA_TOO_LARGE; } if(usrset->cci.closeAlignWritableRegion) diff --git a/makerom/polarssl/base64.c b/makerom/polarssl/base64.c index f320f3c7..5a98fa9f 100644 --- a/makerom/polarssl/base64.c +++ b/makerom/polarssl/base64.c @@ -147,17 +147,17 @@ int base64_decode( unsigned char *dst, size_t *dlen, continue; if( src[i] == '=' && ++j > 2 ){ - printf("err 0 char[%d] = '%c' (0x%x)\n",i,src[i],src[i]); + printf("err 0 char[%lu] = '%c' (0x%x)\n",i,src[i],src[i]); return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); } if( src[i] > 127 || base64_dec_map[src[i]] == 127 ){ - printf("err 1 char[%d] = '%c' (0x%x)\n",i,src[i],src[i]); + printf("err 1 char[%lu] = '%c' (0x%x)\n",i,src[i],src[i]); return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); } if( base64_dec_map[src[i]] < 64 && j != 0 ){ - printf("err 2 char[%d] = '%c' (0x%x)\n",i,src[i],src[i]); + printf("err 2 char[%lu] = '%c' (0x%x)\n",i,src[i],src[i]); return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); } diff --git a/makerom/polarssl/base64.h b/makerom/polarssl/base64.h index fb0d753a..604893eb 100644 --- a/makerom/polarssl/base64.h +++ b/makerom/polarssl/base64.h @@ -28,6 +28,7 @@ #define POLARSSL_BASE64_H #include +#include #define POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL -0x002A /**< Output buffer too small. */ #define POLARSSL_ERR_BASE64_INVALID_CHARACTER -0x002C /**< Invalid character in input. */ diff --git a/makerom/polarssl/cipher.c b/makerom/polarssl/cipher.c index f20cc73b..09c38073 100644 --- a/makerom/polarssl/cipher.c +++ b/makerom/polarssl/cipher.c @@ -35,6 +35,7 @@ #include "polarssl/cipher_wrap.h" #include +#include #if defined _MSC_VER && !defined strcasecmp #define strcasecmp _stricmp diff --git a/makerom/utf.h b/makerom/utf.h index dba3f8ef..0b74f167 100644 --- a/makerom/utf.h +++ b/makerom/utf.h @@ -134,8 +134,6 @@ ConversionResult ConvertUTF32toUTF8 ( const UTF32** sourceStart, const UTF32* sourceEnd, UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); -static Boolean isLegalUTF8(const UTF8 *source, int length); - Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); unsigned getNumBytesForUTF8(UTF8 first); @@ -148,4 +146,4 @@ ConversionResult ConvertUTF8toUTF16 ( ConversionResult ConvertUTF8toUTF32 ( const UTF8** sourceStart, const UTF8* sourceEnd, - UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); \ No newline at end of file + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); diff --git a/makerom/utils.c b/makerom/utils.c index ef7af68b..44dcdbb1 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -171,7 +171,7 @@ int str_utf8_to_u16(u16 **dst, u32 *dst_len, u8 *src, u32 src_len) UTF8 *src_start = (UTF8*)src; UTF8 *src_end = (UTF8*)(src+src_len*sizeof(u8)); - return ConvertUTF8toUTF16 (&src_start, src_end, &target_start, target_end, strictConversion); + return ConvertUTF8toUTF16 ((const UTF8 **)&src_start, src_end, &target_start, target_end, strictConversion); } #endif @@ -293,7 +293,7 @@ u8* ImportFile(char *file, u64 size) u64 fsize = GetFileSize_u64(file); if(size > 0){ if(size != fsize){ - fprintf(stderr,"[!] %s has an invalid size (0x%llx)\n",fsize); + fprintf(stderr,"[!] %s has an invalid size (0x%llx)\n",file, fsize); return NULL; } } From dc8de388a31e1a957a072873ff33879864c9d7b0 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 21 Jun 2014 18:21:22 +1000 Subject: [PATCH 002/317] Fixed some errors and bad programming --- makerom/accessdesc.c | 153 ++++++++++++++++++------------------ makerom/accessdesc.h | 2 +- makerom/cia.c | 27 +++---- makerom/cia.h | 2 +- makerom/dir.c | 2 +- makerom/exheader.c | 59 ++++++-------- makerom/exheader.h | 26 ++++--- makerom/keyset.c | 45 +++++++---- makerom/keyset.h | 2 +- makerom/ncch.c | 179 ++++++++++++++++++++----------------------- makerom/ncch.h | 8 +- 11 files changed, 246 insertions(+), 259 deletions(-) diff --git a/makerom/accessdesc.c b/makerom/accessdesc.c index 8de78844..7247ab6b 100644 --- a/makerom/accessdesc.c +++ b/makerom/accessdesc.c @@ -15,57 +15,57 @@ const int RSF_RSA_DATA_LEN = 344; const int RSF_DESC_DATA_LEN = 684; -int accessdesc_SignWithKey(exheader_settings *exhdrset, ncch_settings *ncchset); -int accessdesc_GetSignFromRsf(exheader_settings *exhdrset, ncch_settings *ncchset); -int accessdesc_GetSignFromPreset(exheader_settings *exhdrset, ncch_settings *ncchset); -void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, ncch_settings *ncchset); +int accessdesc_SignWithKey(exheader_settings *exhdrset); +int accessdesc_GetSignFromRsf(exheader_settings *exhdrset); +int accessdesc_GetSignFromPreset(exheader_settings *exhdrset); +void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, keys_struct *keys); #ifndef PUBLIC_BUILD -void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, ncch_settings *ncchset); +void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, keys_struct *keys); #endif bool IsValidB64Char(char chr); u32 b64_strlen(char *str); void b64_strcpy(char *dst, char *src); -int set_AccessDesc(exheader_settings *exhdrset, ncch_settings *ncchset) +int set_AccessDesc(exheader_settings *exhdrset) { if(exhdrset->useAccessDescPreset) - return accessdesc_GetSignFromPreset(exhdrset,ncchset); - else if(ncchset->rsfSet->CommonHeaderKey.Found) // Keydata exists in RSF - return accessdesc_GetSignFromRsf(exhdrset,ncchset); - else if(!ncchset->keys->rsa.requiresPresignedDesc) // Else if The AccessDesc can be signed with key - return accessdesc_SignWithKey(exhdrset,ncchset); + return accessdesc_GetSignFromPreset(exhdrset); + else if(exhdrset->rsf->CommonHeaderKey.Found) // Keydata exists in RSF + return accessdesc_GetSignFromRsf(exhdrset); + else if(!exhdrset->keys->rsa.requiresPresignedDesc) // Else if The AccessDesc can be signed with key + return accessdesc_SignWithKey(exhdrset); else{ // No way the access desc signature can be 'obtained' fprintf(stderr,"[EXHEADER ERROR] Current keyset cannot sign AccessDesc, please appropriatly setup RSF, or specify a preset with -accessdesc\n"); return CANNOT_SIGN_ACCESSDESC; } } -int accessdesc_SignWithKey(exheader_settings *exhdrset, ncch_settings *ncchset) +int accessdesc_SignWithKey(exheader_settings *exhdrset) { /* Set RSA Keys */ memcpy(exhdrset->keys->rsa.cxiHdrPvt,exhdrset->keys->rsa.cciCfaPvt,0x100); memcpy(exhdrset->keys->rsa.cxiHdrPub,exhdrset->keys->rsa.cciCfaPub,0x100); - memcpy(&exhdrset->exHdr->accessDescriptor.ncchRsaPubKey,exhdrset->keys->rsa.cxiHdrPub,0x100); + memcpy(&exhdrset->acexDesc->ncchRsaPubKey,exhdrset->keys->rsa.cxiHdrPub,0x100); /* Copy Data From ExHeader */ - memcpy(&exhdrset->exHdr->accessDescriptor.arm11SystemLocalCapabilities,&exhdrset->exHdr->arm11SystemLocalCapabilities,sizeof(exhdr_ARM11SystemLocalCapabilities)); - memcpy(&exhdrset->exHdr->accessDescriptor.arm11KernelCapabilities,&exhdrset->exHdr->arm11KernelCapabilities,sizeof(exhdr_ARM11KernelCapabilities)); - memcpy(&exhdrset->exHdr->accessDescriptor.arm9AccessControlInfo,&exhdrset->exHdr->arm9AccessControlInfo,sizeof(exhdr_ARM9AccessControlInfo)); + memcpy(&exhdrset->acexDesc->arm11SystemLocalCapabilities,&exhdrset->exHdr->arm11SystemLocalCapabilities,sizeof(exhdr_ARM11SystemLocalCapabilities)); + memcpy(&exhdrset->acexDesc->arm11KernelCapabilities,&exhdrset->exHdr->arm11KernelCapabilities,sizeof(exhdr_ARM11KernelCapabilities)); + memcpy(&exhdrset->acexDesc->arm9AccessControlInfo,&exhdrset->exHdr->arm9AccessControlInfo,sizeof(exhdr_ARM9AccessControlInfo)); /* Adjust Data */ - u8 *flag = &exhdrset->exHdr->accessDescriptor.arm11SystemLocalCapabilities.flag; + u8 *flag = &exhdrset->acexDesc->arm11SystemLocalCapabilities.flag; u8 SystemMode = (*flag>>4)&0xF; u8 AffinityMask = (*flag>>2)&0x3; u8 IdealProcessor = 1<<((*flag>>0)&0x3); *flag = (u8)(SystemMode << 4 | AffinityMask << 2 | IdealProcessor); - exhdrset->exHdr->accessDescriptor.arm11SystemLocalCapabilities.priority /= 2; + exhdrset->acexDesc->arm11SystemLocalCapabilities.priority /= 2; /* Sign AccessDesc */ - return SignAccessDesc(exhdrset->exHdr,exhdrset->keys); + return SignAccessDesc(exhdrset->acexDesc,exhdrset->keys); } -int accessdesc_GetSignFromRsf(exheader_settings *exhdrset, ncch_settings *ncchset) +int accessdesc_GetSignFromRsf(exheader_settings *exhdrset) { /* Yaml Option Sanity Checks */ if(!exhdrset->rsf->CommonHeaderKey.Found){ @@ -127,14 +127,14 @@ int accessdesc_GetSignFromRsf(exheader_settings *exhdrset, ncch_settings *ncchse /* Set AccessDesc */ out = 0x100; - result = base64_decode(exhdrset->exHdr->accessDescriptor.signature,(size_t *)&out,(const u8*)exhdrset->rsf->CommonHeaderKey.AccCtlDescSign, strlen( exhdrset->rsf->CommonHeaderKey.AccCtlDescSign)); + result = base64_decode(exhdrset->acexDesc->signature,(size_t *)&out,(const u8*)exhdrset->rsf->CommonHeaderKey.AccCtlDescSign, strlen( exhdrset->rsf->CommonHeaderKey.AccCtlDescSign)); if(out != 0x100) result = POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL; if(result) goto finish; - memcpy(exhdrset->exHdr->accessDescriptor.ncchRsaPubKey,exhdrset->keys->rsa.cxiHdrPub,0x100); + memcpy(exhdrset->acexDesc->ncchRsaPubKey,exhdrset->keys->rsa.cxiHdrPub,0x100); out = 0x200; - result = base64_decode((u8*)&exhdrset->exHdr->accessDescriptor.arm11SystemLocalCapabilities,(size_t *)&out,(const u8*)exhdrset->rsf->CommonHeaderKey.AccCtlDescBin,strlen(exhdrset->rsf->CommonHeaderKey.AccCtlDescBin)); + result = base64_decode((u8*)&exhdrset->acexDesc->arm11SystemLocalCapabilities,(size_t *)&out,(const u8*)exhdrset->rsf->CommonHeaderKey.AccCtlDescBin,strlen(exhdrset->rsf->CommonHeaderKey.AccCtlDescBin)); if(out != 0x200) result = POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL; if(result) goto finish; @@ -142,7 +142,7 @@ int accessdesc_GetSignFromRsf(exheader_settings *exhdrset, ncch_settings *ncchse return result; } -int accessdesc_GetSignFromPreset(exheader_settings *exhdrset, ncch_settings *ncchset) +int accessdesc_GetSignFromPreset(exheader_settings *exhdrset) { u8 *desc = NULL; u8 *accessDesc = NULL; @@ -152,9 +152,9 @@ int accessdesc_GetSignFromPreset(exheader_settings *exhdrset, ncch_settings *ncc u8 *cxiPubk = NULL; u8 *cxiPvtk = NULL; - accessdesc_GetPresetData(&desc,&accessDesc,&depList,ncchset); + accessdesc_GetPresetData(&desc,&accessDesc,&depList,exhdrset->keys); #ifndef PUBLIC_BUILD - accessdesc_GetPresetSigData(&accessDescSig,&cxiPubk,&cxiPvtk,ncchset); + accessdesc_GetPresetSigData(&accessDescSig,&cxiPubk,&cxiPvtk,exhdrset->keys); #endif // Error Checking @@ -163,11 +163,12 @@ int accessdesc_GetSignFromPreset(exheader_settings *exhdrset, ncch_settings *ncc return CANNOT_SIGN_ACCESSDESC; } - if((!cxiPubk || !cxiPvtk || !accessDesc || !accessDescSig) && ncchset->keys->rsa.requiresPresignedDesc){ + if((!cxiPubk || !cxiPvtk || !accessDesc || !accessDescSig) && exhdrset->keys->rsa.requiresPresignedDesc){ fprintf(stderr,"[EXHEADER ERROR] This AccessDesc preset needs to be signed, the current keyset is incapable of doing so. Please configure RSF file with the appropriate signature data.\n"); return CANNOT_SIGN_ACCESSDESC; } + // Setting data in Exheader // Dependency List memcpy(exhdrset->exHdr->dependencyList,depList,0x180); @@ -191,23 +192,23 @@ int accessdesc_GetSignFromPreset(exheader_settings *exhdrset, ncch_settings *ncc // Setting AccessDesc Area // Signing normally if possible - if(!ncchset->keys->rsa.requiresPresignedDesc) - return accessdesc_SignWithKey(exhdrset,ncchset); + if(!exhdrset->keys->rsa.requiresPresignedDesc) + return accessdesc_SignWithKey(exhdrset); // Otherwise set static data & ncch hdr sig info memcpy(exhdrset->keys->rsa.cxiHdrPub,cxiPubk,0x100); memcpy(exhdrset->keys->rsa.cxiHdrPvt,cxiPvtk,0x100); - memcpy(&exhdrset->exHdr->accessDescriptor.signature,accessDescSig,0x100); - memcpy(&exhdrset->exHdr->accessDescriptor.ncchRsaPubKey,cxiPubk,0x100); - memcpy(&exhdrset->exHdr->accessDescriptor.arm11SystemLocalCapabilities,accessDesc,0x200); + memcpy(&exhdrset->acexDesc->signature,accessDescSig,0x100); + memcpy(&exhdrset->acexDesc->ncchRsaPubKey,cxiPubk,0x100); + memcpy(&exhdrset->acexDesc->arm11SystemLocalCapabilities,accessDesc,0x200); return 0; } -void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, ncch_settings *ncchset) +void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, keys_struct *keys) { - if(ncchset->keys->accessDescSign.presetType == desc_preset_APP){ - switch(ncchset->keys->accessDescSign.targetFirmware){ + if(keys->accessDescSign.presetType == desc_preset_APP){ + switch(keys->accessDescSign.targetFirmware){ case 0x1B: case 0x1C: *desc = (u8*)app_fw1B_desc_data; @@ -247,8 +248,8 @@ void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, ncch_set } } - else if(ncchset->keys->accessDescSign.presetType == desc_preset_EC_APP){ - switch(ncchset->keys->accessDescSign.targetFirmware){ + else if(keys->accessDescSign.presetType == desc_preset_EC_APP){ + switch(keys->accessDescSign.targetFirmware){ case 0x20: *desc = (u8*)ecapp_fw20_desc_data; *accessDesc = (u8*)ecapp_fw20_acex_data; @@ -261,8 +262,8 @@ void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, ncch_set break; } } - else if(ncchset->keys->accessDescSign.presetType == desc_preset_DLP){ - switch(ncchset->keys->accessDescSign.targetFirmware){ + else if(keys->accessDescSign.presetType == desc_preset_DLP){ + switch(keys->accessDescSign.targetFirmware){ case 0x1B: case 0x1C: *desc = (u8*)dlp_fw1B_desc_data; @@ -281,8 +282,8 @@ void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, ncch_set break; } } - else if(ncchset->keys->accessDescSign.presetType == desc_preset_DEMO){ - switch(ncchset->keys->accessDescSign.targetFirmware){ + else if(keys->accessDescSign.presetType == desc_preset_DEMO){ + switch(keys->accessDescSign.targetFirmware){ case 0x1E: *desc = (u8*)demo_fw1E_desc_data; *accessDesc = (u8*)demo_fw1E_acex_data; @@ -295,8 +296,8 @@ void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, ncch_set break; } } - else if(ncchset->keys->accessDescSign.presetType == desc_preset_FIRM){ - switch(ncchset->keys->accessDescSign.targetFirmware){ + else if(keys->accessDescSign.presetType == desc_preset_FIRM){ + switch(keys->accessDescSign.targetFirmware){ default: *desc = (u8*)firm_fw26_desc_data; *accessDesc = (u8*)firm_fw26_acex_data; @@ -307,75 +308,75 @@ void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, ncch_set } #ifndef PUBLIC_BUILD -void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, ncch_settings *ncchset) +void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, keys_struct *keys) { - if(ncchset->keys->accessDescSign.presetType == desc_preset_APP){ - switch(ncchset->keys->accessDescSign.targetFirmware){ + if(keys->accessDescSign.presetType == desc_preset_APP){ + switch(keys->accessDescSign.targetFirmware){ case 0x1B: case 0x1C: - if(ncchset->keys->keyset == pki_DEVELOPMENT){ + if(keys->keyset == pki_DEVELOPMENT){ *accessDescSig = (u8*)app_fw1B_dev_acexsig; *cxiPubk = (u8*)app_fw1B_dev_hdrpub; *cxiPvtk = (u8*)app_fw1B_dev_hdrpvt; } break; case 0x1D: - if(ncchset->keys->keyset == pki_DEVELOPMENT){ + if(keys->keyset == pki_DEVELOPMENT){ *accessDescSig = (u8*)app_fw1D_dev_acexsig; *cxiPubk = (u8*)app_fw1D_dev_hdrpub; *cxiPvtk = (u8*)app_fw1D_dev_hdrpvt; } - if(ncchset->keys->keyset == pki_PRODUCTION){ + if(keys->keyset == pki_PRODUCTION){ *accessDescSig = (u8*)app_fw1D_prod_acexsig; *cxiPubk = (u8*)app_fw1D_prod_hdrpub; *cxiPvtk = NULL; } break; case 0x1E: - if(ncchset->keys->keyset == pki_PRODUCTION){ + if(keys->keyset == pki_PRODUCTION){ *accessDescSig = (u8*)app_fw1E_prod_acexsig; *cxiPubk = (u8*)app_fw1E_prod_hdrpub; *cxiPvtk = NULL; } break; case 0x20: - if(ncchset->keys->keyset == pki_DEVELOPMENT){ + if(keys->keyset == pki_DEVELOPMENT){ *accessDescSig = (u8*)app_fw20_dev_acexsig; *cxiPubk = (u8*)app_fw20_dev_hdrpub; *cxiPvtk = NULL; } - else if(ncchset->keys->keyset == pki_PRODUCTION){ + else if(keys->keyset == pki_PRODUCTION){ *accessDescSig = (u8*)app_fw20_prod_acexsig; *cxiPubk = (u8*)app_fw20_prod_hdrpub; *cxiPvtk = NULL; } break; case 0x21: - if(ncchset->keys->keyset == pki_DEVELOPMENT){ + if(keys->keyset == pki_DEVELOPMENT){ *accessDescSig = (u8*)app_fw21_dev_acexsig; *cxiPubk = (u8*)app_fw21_dev_hdrpub; *cxiPvtk = (u8*)app_fw21_dev_hdrpvt; } - else if(ncchset->keys->keyset == pki_PRODUCTION){ + else if(keys->keyset == pki_PRODUCTION){ *accessDescSig = (u8*)app_fw21_prod_acexsig; *cxiPubk = (u8*)app_fw21_prod_hdrpub; *cxiPvtk = NULL; } break; case 0x23: - if(ncchset->keys->keyset == pki_DEVELOPMENT){ + if(keys->keyset == pki_DEVELOPMENT){ *accessDescSig = (u8*)app_fw23_dev_acexsig; *cxiPubk = (u8*)app_fw23_dev_hdrpub; *cxiPvtk = NULL; } - else if(ncchset->keys->keyset == pki_PRODUCTION){ + else if(keys->keyset == pki_PRODUCTION){ *accessDescSig = (u8*)app_fw23_prod_acexsig; *cxiPubk = (u8*)app_fw23_prod_hdrpub; *cxiPvtk = NULL; } break; case 0x27: - if(ncchset->keys->keyset == pki_PRODUCTION){ + if(keys->keyset == pki_PRODUCTION){ *accessDescSig = (u8*)app_fw27_prod_acexsig; *cxiPubk = (u8*)app_fw27_prod_hdrpub; *cxiPvtk = NULL; @@ -384,17 +385,17 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, } } - else if(ncchset->keys->accessDescSign.presetType == desc_preset_EC_APP){ - switch(ncchset->keys->accessDescSign.targetFirmware){ + else if(keys->accessDescSign.presetType == desc_preset_EC_APP){ + switch(keys->accessDescSign.targetFirmware){ case 0x20: - if(ncchset->keys->keyset == pki_PRODUCTION){ + if(keys->keyset == pki_PRODUCTION){ *accessDescSig = (u8*)ecapp_fw20_prod_acexsig; *cxiPubk = (u8*)ecapp_fw20_prod_hdrpub; *cxiPvtk = NULL; } break; case 0x23: - if(ncchset->keys->keyset == pki_PRODUCTION){ + if(keys->keyset == pki_PRODUCTION){ *accessDescSig = (u8*)ecapp_fw23_prod_acexsig; *cxiPubk = (u8*)ecapp_fw23_prod_hdrpub; *cxiPvtk = NULL; @@ -402,25 +403,25 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, break; } } - else if(ncchset->keys->accessDescSign.presetType == desc_preset_DLP){ - switch(ncchset->keys->accessDescSign.targetFirmware){ + else if(keys->accessDescSign.presetType == desc_preset_DLP){ + switch(keys->accessDescSign.targetFirmware){ case 0x1B: case 0x1C: - if(ncchset->keys->keyset == pki_DEVELOPMENT){ + if(keys->keyset == pki_DEVELOPMENT){ *accessDescSig = (u8*)dlp_fw1B_dev_acexsig; *cxiPubk = (u8*)dlp_fw1B_dev_hdrpub; *cxiPvtk = (u8*)dlp_fw1B_dev_hdrpvt; } break; case 0x1D: - if(ncchset->keys->keyset == pki_DEVELOPMENT){ + if(keys->keyset == pki_DEVELOPMENT){ *accessDescSig = (u8*)dlp_fw1D_dev_acexsig; *cxiPubk = (u8*)dlp_fw1D_dev_hdrpub; *cxiPvtk = (u8*)dlp_fw1D_dev_hdrpvt; } break; case 0x21: - if(ncchset->keys->keyset == pki_DEVELOPMENT){ + if(keys->keyset == pki_DEVELOPMENT){ *accessDescSig = (u8*)dlp_fw21_dev_acexsig; *cxiPubk = (u8*)dlp_fw21_dev_hdrpub; *cxiPvtk = (u8*)dlp_fw21_dev_hdrpvt; @@ -428,17 +429,17 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, break; } } - else if(ncchset->keys->accessDescSign.presetType == desc_preset_DEMO){ - switch(ncchset->keys->accessDescSign.targetFirmware){ + else if(keys->accessDescSign.presetType == desc_preset_DEMO){ + switch(keys->accessDescSign.targetFirmware){ case 0x1E: - if(ncchset->keys->keyset == pki_DEVELOPMENT){ + if(keys->keyset == pki_DEVELOPMENT){ *accessDescSig = (u8*)demo_fw1E_dev_acexsig; *cxiPubk = (u8*)demo_fw1E_dev_hdrpub; *cxiPvtk = NULL; } break; case 0x21: - if(ncchset->keys->keyset == pki_DEVELOPMENT){ + if(keys->keyset == pki_DEVELOPMENT){ *accessDescSig = (u8*)demo_fw21_dev_acexsig; *cxiPubk = (u8*)demo_fw21_dev_hdrpub; *cxiPvtk = (u8*)demo_fw21_dev_hdrpvt; @@ -446,10 +447,10 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, break; } } - else if(ncchset->keys->accessDescSign.presetType == desc_preset_FIRM){ - switch(ncchset->keys->accessDescSign.targetFirmware){ + else if(keys->accessDescSign.presetType == desc_preset_FIRM){ + switch(keys->accessDescSign.targetFirmware){ case 0x26: - if(ncchset->keys->keyset == pki_DEVELOPMENT){ + if(keys->keyset == pki_DEVELOPMENT){ *accessDescSig = (u8*)firm_fw26_dev_acexsig; *cxiPubk = (u8*)firm_fw26_dev_hdrpub; *cxiPvtk = NULL; @@ -492,6 +493,6 @@ void b64_strcpy(char *dst, char *src) } dst[j] = 0; - memdump(stdout,"src: ",(u8*)src,src_len+1); - memdump(stdout,"dst: ",(u8*)dst,j+1); -} + //memdump(stdout,"src: ",(u8*)src,src_len+1); + //memdump(stdout,"dst: ",(u8*)dst,j+1); +} \ No newline at end of file diff --git a/makerom/accessdesc.h b/makerom/accessdesc.h index c3324f4b..a5f01f24 100644 --- a/makerom/accessdesc.h +++ b/makerom/accessdesc.h @@ -1,3 +1,3 @@ #pragma once -int set_AccessDesc(exheader_settings *exhdrset, ncch_settings *ncchset); \ No newline at end of file +int set_AccessDesc(exheader_settings *exhdrset); \ No newline at end of file diff --git a/makerom/cia.c b/makerom/cia.c index 30df01dc..55967dc4 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -160,6 +160,7 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) { // General Stuff ciaset->keys = &usrset->common.keys; + ciaset->rsf = &usrset->common.rsfSet; ciaset->ciaSections.content.buffer = usrset->common.workingFile.buffer; ciaset->ciaSections.content.size = usrset->common.workingFile.size; usrset->common.workingFile.buffer = NULL; @@ -180,8 +181,6 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) ciaset->common.titleVersion[i] = usrset->cia.titleVersion[i]; } - ciaset->content.overrideSaveDataSize = usrset->cia.overideSaveDataSize; - // Ticket Data u64_to_u8(ciaset->tik.ticketId,u64GetRand(),BE); if(usrset->cia.randomTitleKey) @@ -264,9 +263,7 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) u8 *ncchkey = NULL; if(!ciaset->content.keyNotFound){ SetNcchUnfixedKeys(ciaset->keys,ncch0); - ncchkey = GetNCCHKey(keyType,ciaset->keys); - if(keyType == KeyIsUnFixed2) - ncchkey = GetNCCHKey(KeyIsUnFixed,ciaset->keys); + ncchkey = GetNCCHKey0(keyType,ciaset->keys); } /* Get TMD Data from ncch */ @@ -289,11 +286,14 @@ int GetCIADataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_struct *ncch_ctx, u8 CryptNCCHSection((u8*)exhdr,0x400,0,ncch_ctx,key,ncch_exhdr); u16 Category = u8_to_u16((ciaset->common.titleId+2),BE); - if(IsPatch(Category)||ciaset->content.IsCfa||ciaset->content.keyNotFound) u32_to_u8(ciaset->tmd.savedataSize,0,LE); - else u32_to_u8(ciaset->tmd.savedataSize,(u32)GetSaveDataSize_frm_exhdr(exhdr),LE); - if(ciaset->content.overrideSaveDataSize){ + if(IsPatch(Category)||ciaset->content.IsCfa||ciaset->content.keyNotFound) + u32_to_u8(ciaset->tmd.savedataSize,0,LE); + else + u32_to_u8(ciaset->tmd.savedataSize,(u32)GetSaveDataSize_frm_exhdr(exhdr),LE); + + if(ciaset->rsf->SystemControlInfo.SaveDataSize && !ciaset->content.IsCfa && ciaset->content.keyNotFound){ u64 size = 0; - GetSaveDataSizeFromString(&size,ciaset->content.overrideSaveDataSize,"CIA"); + GetSaveDataSizeFromString(&size,ciaset->rsf->SystemControlInfo.SaveDataSize,"CIA"); u32_to_u8(ciaset->tmd.savedataSize,(u32)size,LE); } @@ -524,14 +524,9 @@ int GetSettingsFromCci(cia_settings *ciaset) u64 cciContentOffsets[CCI_MAX_CONTENT]; cciContentOffsets[0] = ncch0_offset; - ncch_hdr *hdr; for(int i = 1; i < 8; i++){ if(GetPartitionSize(ciaset->ciaSections.content.buffer,i)){ cciContentOffsets[j] = GetPartitionOffset(ciaset->ciaSections.content.buffer,i); - - // Get Data from ncch HDR - GetNCCH_CommonHDR(hdr,NULL,GetPartition(ciaset->ciaSections.content.buffer,i)); - hdr = (ncch_hdr*)(ciaset->ciaSections.content.buffer + cciContentOffsets[j] + 0x100); // Get Size ciaset->content.size[j] = GetPartitionSize(ciaset->ciaSections.content.buffer,i); @@ -540,9 +535,7 @@ int GetSettingsFromCci(cia_settings *ciaset) ciaset->content.totalSize += ciaset->content.size[j]; // Get ID - u8 hash[0x20]; - ctr_sha((u8*)hdr,0x200,hash,CTR_SHA_256); - ciaset->content.id[j] = u8_to_u32(hash,BE); + ciaset->content.id[j] = u32GetRand(); // Get Index ciaset->content.index[j] = i; diff --git a/makerom/cia.h b/makerom/cia.h index ce59bd15..57bc0ba7 100644 --- a/makerom/cia.h +++ b/makerom/cia.h @@ -40,6 +40,7 @@ typedef struct FILE *out; + rsf_settings *rsf; keys_struct *keys; struct{ @@ -83,7 +84,6 @@ typedef struct bool IsCfa; bool IsDlc; bool encryptCia; - char *overrideSaveDataSize; bool keyNotFound; diff --git a/makerom/dir.c b/makerom/dir.c index 2c2e0b41..bcd57b2e 100644 --- a/makerom/dir.c +++ b/makerom/dir.c @@ -324,4 +324,4 @@ void fs_FreeFiles(fs_dir *dir) fs_dir *tmp = (fs_dir*)dir->dir; for(u32 i = 0; i < dir->u_dir; i++) fs_FreeFiles(&tmp[i]); -} +} \ No newline at end of file diff --git a/makerom/exheader.c b/makerom/exheader.c index 72391ec8..0270b359 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -6,10 +6,9 @@ /* Prototypes */ -void init_ExHeaderSettings(exheader_settings *exhdrset); void free_ExHeaderSettings(exheader_settings *exhdrset); int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings *ncchset); -int get_ExHeaderSettingsFromYaml(exheader_settings *exhdrset); +int get_ExHeaderSettingsFromRsf(exheader_settings *exhdrset); int get_ExHeaderCodeSetInfo(exhdr_CodeSetInfo *CodeSetInfo, rsf_settings *rsf); int get_ExHeaderDependencyList(u8 *DependencyList, rsf_settings *rsf); @@ -52,17 +51,17 @@ u32 GetDescPrefixBits(int numPrefixBits, u32 PrefixVal); int get_ExHeaderARM9AccessControlInfo(exhdr_ARM9AccessControlInfo *arm9, rsf_settings *rsf); /* ExHeader Signature Functions */ -int SignAccessDesc(extended_hdr *exHdr, keys_struct *keys) +int SignAccessDesc(access_descriptor *acexDesc, keys_struct *keys) { - u8 *AccessDesc = (u8*) &exHdr->accessDescriptor.ncchRsaPubKey; - u8 *Signature = (u8*) &exHdr->accessDescriptor.signature; + u8 *AccessDesc = (u8*) &acexDesc->ncchRsaPubKey; + u8 *Signature = (u8*) &acexDesc->signature; return ctr_sig(AccessDesc,0x300,Signature,keys->rsa.acexPub,keys->rsa.acexPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } -int CheckaccessDescSignature(extended_hdr *exHdr, keys_struct *keys) +int CheckAccessDescSignature(access_descriptor *acexDesc, keys_struct *keys) { - u8 *AccessDesc = (u8*) &exHdr->accessDescriptor.ncchRsaPubKey; - u8 *Signature = (u8*) &exHdr->accessDescriptor.signature; + u8 *AccessDesc = (u8*) &acexDesc->ncchRsaPubKey; + u8 *Signature = (u8*) &acexDesc->signature; return ctr_sig(AccessDesc,0x300,Signature,keys->rsa.acexPub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); } @@ -74,21 +73,20 @@ int BuildExHeader(ncch_settings *ncchset) if(ncchset->options.IsCfa) return 0; - exheader_settings *exhdrset = malloc(sizeof(exheader_settings)); + exheader_settings *exhdrset = calloc(1,sizeof(exheader_settings)); if(!exhdrset) { fprintf(stderr,"[EXHEADER ERROR] Not enough memory\n"); return MEM_ERROR; } - init_ExHeaderSettings(exhdrset); // Get Settings result = get_ExHeaderSettingsFromNcchset(exhdrset,ncchset); if(result) goto finish; - result = get_ExHeaderSettingsFromYaml(exhdrset); + result = get_ExHeaderSettingsFromRsf(exhdrset); if(result) goto finish; - result = set_AccessDesc(exhdrset,ncchset); + result = set_AccessDesc(exhdrset); if(result) goto finish; finish: @@ -97,12 +95,6 @@ int BuildExHeader(ncch_settings *ncchset) return result; } - -void init_ExHeaderSettings(exheader_settings *exhdrset) -{ - memset(exhdrset,0,sizeof(exheader_settings)); -} - void free_ExHeaderSettings(exheader_settings *exhdrset) { free(exhdrset); @@ -116,13 +108,19 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * exhdrset->useAccessDescPreset = ncchset->keys->accessDescSign.presetType != desc_preset_NONE; /* Creating Output Buffer */ - ncchset->sections.exhdr.size = 0x800; - ncchset->sections.exhdr.buffer = malloc(ncchset->sections.exhdr.size); + ncchset->sections.exhdr.size = sizeof(extended_hdr); + ncchset->sections.exhdr.buffer = calloc(1,ncchset->sections.exhdr.size); if(!ncchset->sections.exhdr.buffer) { fprintf(stderr,"[EXHEADER ERROR] Not enough memory\n"); return MEM_ERROR; } - memset(ncchset->sections.exhdr.buffer,0,ncchset->sections.exhdr.size); + + ncchset->sections.acexDesc.size = sizeof(access_descriptor); + ncchset->sections.acexDesc.buffer = calloc(1,ncchset->sections.acexDesc.size); + if(!ncchset->sections.acexDesc.buffer) { + fprintf(stderr,"[EXHEADER ERROR] Not enough memory\n"); + return MEM_ERROR; + } /* Import ExHeader Code Section template */ if(ncchset->componentFilePtrs.exhdrSize){ @@ -137,6 +135,7 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * /* Create ExHeader Struct for output */ exhdrset->exHdr = (extended_hdr*)ncchset->sections.exhdr.buffer; + exhdrset->acexDesc = (access_descriptor*)ncchset->sections.acexDesc.buffer; /* Set Code Info if Code Section was built not imported */ if(ncchset->options.IsBuildingCodeSection){ @@ -167,7 +166,7 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * return 0; } -int get_ExHeaderSettingsFromYaml(exheader_settings *exhdrset) +int get_ExHeaderSettingsFromRsf(exheader_settings *exhdrset) { int result = 0; result = get_ExHeaderCodeSetInfo(&exhdrset->exHdr->codeSetInfo, exhdrset->rsf); @@ -1171,22 +1170,14 @@ void exhdr_Print_ServiceAccessControl(extended_hdr *hdr) } /* ExHeader Binary Read Functions */ -u8* GetAccessDescSig_frm_exhdr(extended_hdr *hdr) -{ - if(!hdr) return NULL; - return hdr->accessDescriptor.signature ; -} - -u8* GetNcchHdrPubKey_frm_exhdr(extended_hdr *hdr) +u8* GetAcexRsaSig(access_descriptor *acexDesc) { - if(!hdr) return NULL; - return hdr->accessDescriptor.ncchRsaPubKey; + return acexDesc->signature ; } -u8* GetAccessDesc_frm_exhdr(extended_hdr *hdr) +u8* GetAcexNcchPubKey(access_descriptor *acexDesc) { - if(!hdr) return NULL; - return hdr->accessDescriptor.ncchRsaPubKey; + return acexDesc->ncchRsaPubKey; } u16 GetRemasterVersion_frm_exhdr(extended_hdr *hdr) diff --git a/makerom/exheader.h b/makerom/exheader.h index 431c7764..82e778f4 100644 --- a/makerom/exheader.h +++ b/makerom/exheader.h @@ -186,15 +186,17 @@ typedef struct exhdr_ARM11KernelCapabilities arm11KernelCapabilities; exhdr_ARM9AccessControlInfo arm9AccessControlInfo; // } - struct { - u8 signature[0x100]; - u8 ncchRsaPubKey[0x100]; - exhdr_ARM11SystemLocalCapabilities arm11SystemLocalCapabilities; - exhdr_ARM11KernelCapabilities arm11KernelCapabilities; - exhdr_ARM9AccessControlInfo arm9AccessControlInfo; - } accessDescriptor; } extended_hdr; +typedef struct +{ + u8 signature[0x100]; + u8 ncchRsaPubKey[0x100]; + exhdr_ARM11SystemLocalCapabilities arm11SystemLocalCapabilities; + exhdr_ARM11KernelCapabilities arm11KernelCapabilities; + exhdr_ARM9AccessControlInfo arm9AccessControlInfo; +} access_descriptor; + typedef struct { keys_struct *keys; @@ -203,12 +205,13 @@ typedef struct /* Output */ extended_hdr *exHdr; // is the exheader output buffer ptr(in ncchset) cast as exheader struct ptr; + access_descriptor *acexDesc; } exheader_settings; /* ExHeader Signature Functions */ -int SignAccessDesc(extended_hdr *ExHdr, keys_struct *keys); -int CheckaccessDescSignature(extended_hdr *ExHdr, keys_struct *keys); +int SignAccessDesc(access_descriptor *acexDesc, keys_struct *keys); +int CheckAccessDescSignature(access_descriptor *acexDesc, keys_struct *keys); /* ExHeader Build Functions */ int BuildExHeader(ncch_settings *ncchset); @@ -217,9 +220,8 @@ int BuildExHeader(ncch_settings *ncchset); void exhdr_Print_ServiceAccessControl(extended_hdr *hdr); /* ExHeader Binary Read Functions */ -u8* GetAccessDescSig_frm_exhdr(extended_hdr *hdr); -u8* GetNcchHdrPubKey_frm_exhdr(extended_hdr *hdr); -u8* GetAccessDesc_frm_exhdr(extended_hdr *hdr); +u8* GetAcexRsaSig(access_descriptor *acexDesc); +u8* GetAcexNcchPubKey(access_descriptor *acexDesc); u16 GetRemasterVersion_frm_exhdr(extended_hdr *hdr); u64 GetSaveDataSize_frm_exhdr(extended_hdr *hdr); int GetDependencyList_frm_exhdr(u8 *Dest,extended_hdr *hdr); diff --git a/makerom/keyset.c b/makerom/keyset.c index ce59b2d4..38e5cf84 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -26,6 +26,7 @@ int SetTikCert(keys_struct *keys, u8 *Cert); int SetTmdCert(keys_struct *keys, u8 *Cert); int LoadKeysFromResources(keys_struct *keys); +void SetDummyRsaData(keys_struct *keys); int LoadKeysFromKeyfile(keys_struct *keys); void CheckAccessDescKey(keys_struct *keys); void DumpKeyset(keys_struct *keys); @@ -56,6 +57,9 @@ int SetKeys(keys_struct *keys) result = LoadKeysFromKeyfile(keys); if(result) return KEYSET_ERROR; } + + if(keys->rsa.isFalseSign) + SetDummyRsaData(keys); CheckAccessDescKey(keys); @@ -82,19 +86,7 @@ int LoadKeysFromResources(keys_struct *keys) //SetSystemFixedKey(keys,(u8*)zeros_aesKey); /* RSA Keys */ - keys->rsa.isFalseSign = true; - // CIA - SetTIK_RsaKey(keys,(u8*)tpki_rsa_privExp,(u8*)tpki_rsa_pubMod); - SetTMD_RsaKey(keys,(u8*)tpki_rsa_privExp,(u8*)tpki_rsa_pubMod); - // CCI/CFA - Set_CCI_CFA_RsaKey(keys,(u8*)tpki_rsa_privExp,(u8*)tpki_rsa_pubMod); - // CXI - SetAccessDesc_RsaKey(keys,(u8*)tpki_rsa_privExp,(u8*)tpki_rsa_pubMod); - - /* Certs */ - SetCaCert(keys,(u8*)ca3_tpki_cert); - SetTikCert(keys,(u8*)xsC_tpki_cert); - SetTmdCert(keys,(u8*)cpB_tpki_cert); + keys->rsa.isFalseSign = true; } #ifndef PUBLIC_BUILD else if(keys->keyset == pki_DEVELOPMENT){ @@ -166,11 +158,32 @@ int LoadKeysFromResources(keys_struct *keys) return 0; } +void SetDummyRsaData(keys_struct *keys) +{ + if(!keys->rsa.xsPvt || !keys->rsa.xsPub) + SetTIK_RsaKey(keys,(u8*)tpki_rsa_privExp,(u8*)tpki_rsa_pubMod); + if(!keys->rsa.cpPvt || !keys->rsa.cpPub) + SetTMD_RsaKey(keys,(u8*)tpki_rsa_privExp,(u8*)tpki_rsa_pubMod); + + if(!keys->rsa.cciCfaPvt || !keys->rsa.cciCfaPub) + Set_CCI_CFA_RsaKey(keys,(u8*)tpki_rsa_privExp,(u8*)tpki_rsa_pubMod); + + if(!keys->rsa.acexPvt || !keys->rsa.acexPub) + SetAccessDesc_RsaKey(keys,(u8*)tpki_rsa_privExp,(u8*)tpki_rsa_pubMod); + + /* Certs */ + if(!keys->certs.caCert) + SetCaCert(keys,(u8*)ca3_tpki_cert); + if(!keys->certs.xsCert) + SetTikCert(keys,(u8*)xsC_tpki_cert); + if(!keys->certs.cpCert) + SetTmdCert(keys,(u8*)cpB_tpki_cert); +} + int LoadKeysFromKeyfile(keys_struct *keys) { - //else - printf("[KEYSET ERROR] Target not supported\n"); - return 0; + printf("[KEYSET ERROR] Custom keys not supported\n"); + return -1; } void CheckAccessDescKey(keys_struct *keys) diff --git a/makerom/keyset.h b/makerom/keyset.h index 58291b00..81741a33 100644 --- a/makerom/keyset.h +++ b/makerom/keyset.h @@ -65,7 +65,7 @@ typedef struct { bool isFalseSign; // CIA RSA - u8 *cpPvt; //cpPvt + u8 *cpPvt; u8 *cpPub; u8 *xsPvt; u8 *xsPub; diff --git a/makerom/ncch.c b/makerom/ncch.c index 7eac1db6..e8156db5 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -366,20 +366,28 @@ int ImportLogo(ncch_settings *ncchset) int SetupNcch(ncch_settings *ncchset, romfs_buildctx *romfs) { u64 ncchSize = 0; - u64 exhdrSize,logoSize,plnRgnSize,exefsSize,romfsSize; - u64 exhdrOffset,logoOffset,plnRgnOffset,exefsOffset,romfsOffset; + u64 exhdrSize,acexSize,logoSize,plnRgnSize,exefsSize,romfsSize; + u64 exhdrOffset,acexOffset,logoOffset,plnRgnOffset,exefsOffset,romfsOffset; u32 exefsHashSize,romfsHashSize; ncchSize += 0x200; // Sig+Hdr // Sizes for NCCH hdr if(ncchset->sections.exhdr.size){ - exhdrSize = 0x400; + exhdrSize = ncchset->sections.exhdr.size; exhdrOffset = ncchSize; - ncchSize += ncchset->sections.exhdr.size; + ncchSize += exhdrSize; } else exhdrSize = 0; + + if(ncchset->sections.acexDesc.size){ + acexSize = ncchset->sections.acexDesc.size; + acexOffset = ncchSize; + ncchSize += acexSize; + } + else + acexSize = 0; if(ncchset->sections.logo.size){ logoSize = ncchset->sections.logo.size; @@ -443,6 +451,11 @@ int SetupNcch(ncch_settings *ncchset, romfs_buildctx *romfs) ncchset->sections.exhdr.buffer = NULL; u32_to_u8(hdr->exhdrSize,exhdrSize,LE); } + if(acexSize){ + memcpy((u8*)(ncch+acexOffset),ncchset->sections.acexDesc.buffer,ncchset->sections.acexDesc.size); + free(ncchset->sections.acexDesc.buffer); + ncchset->sections.acexDesc.buffer = NULL; + } if(logoSize){ memcpy((u8*)(ncch+logoOffset),ncchset->sections.logo.buffer,ncchset->sections.logo.size); @@ -496,13 +509,14 @@ int FinaliseNcch(ncch_settings *ncchset) ncch_hdr *hdr = (ncch_hdr*)(ncch + 0x100); u8 *exhdr = (u8*)(ncch + ncchset->cryptoDetails.exhdrOffset); + u8 *acexDesc = (u8*)(ncch + ncchset->cryptoDetails.acexOffset); u8 *logo = (u8*)(ncch + ncchset->cryptoDetails.logoOffset); u8 *exefs = (u8*)(ncch + ncchset->cryptoDetails.exefsOffset); u8 *romfs = (u8*)(ncch + ncchset->cryptoDetails.romfsOffset); // Taking Hashes\n"); if(ncchset->cryptoDetails.exhdrSize) - ctr_sha(exhdr,0x400,hdr->exhdrHash,CTR_SHA_256); + ctr_sha(exhdr,ncchset->cryptoDetails.exhdrSize,hdr->exhdrHash,CTR_SHA_256); if(ncchset->cryptoDetails.logoSize) ctr_sha(logo,ncchset->cryptoDetails.logoSize,hdr->logoHash,CTR_SHA_256); if(ncchset->cryptoDetails.exefsHashDataSize) @@ -527,10 +541,8 @@ int FinaliseNcch(ncch_settings *ncchset) SetNcchUnfixedKeys(ncchset->keys, ncch); // Getting AES Keys - u8 *key0 = GetNCCHKey(keyType, ncchset->keys); - u8 *key1 = GetNCCHKey(keyType, ncchset->keys); - if(keyType == KeyIsUnFixed2) - key0 = GetNCCHKey(KeyIsUnFixed, ncchset->keys); + u8 *key0 = GetNCCHKey0(keyType, ncchset->keys); + u8 *key1 = GetNCCHKey1(keyType, ncchset->keys); if(key0 == NULL || key1 == NULL){ fprintf(stderr,"[NCCH ERROR] Failed to load ncch aes key\n"); @@ -543,9 +555,11 @@ int FinaliseNcch(ncch_settings *ncchset) memdump(stdout,"key1: ",key1,16); */ - // Crypting Exheader - if(ncchset->cryptoDetails.exhdrSize) + // Crypting Exheader/AcexDesc + if(ncchset->cryptoDetails.exhdrSize){ CryptNCCHSection(exhdr,ncchset->cryptoDetails.exhdrSize,0x0,&ncchset->cryptoDetails,key0,ncch_exhdr); + CryptNCCHSection(acexDesc,ncchset->cryptoDetails.acexSize,ncchset->cryptoDetails.exhdrSize,&ncchset->cryptoDetails,key0,ncch_exhdr); + } // Crypting ExeFs Files if(ncchset->cryptoDetails.exefsSize){ @@ -618,12 +632,12 @@ int SetCommonHeaderBasicData(ncch_settings *ncchset, ncch_hdr *hdr) hdr->flags[OtherFlag] = (NoCrypto|FixedCryptoKey); else if(ncchset->keys->aes.ncchKeyX0){ hdr->flags[OtherFlag] = UnFixedCryptoKey; - if(ncchset->keys->aes.ncchKeyX1) + if(ncchset->keys->aes.ncchKeyX1 && !ncchset->options.IsCfa) hdr->flags[SecureCrypto2] = 1; } else{ hdr->flags[OtherFlag] = FixedCryptoKey; - u8 *key = GetNCCHKey(GetNCCHKeyType(hdr),ncchset->keys); + u8 *key = GetNCCHKey0(GetNCCHKeyType(hdr),ncchset->keys); if(!key){ // for detecting absense of fixed aes keys hdr->flags[OtherFlag] = (NoCrypto|FixedCryptoKey); fprintf(stderr,"[NCCH WARNING] NCCH AES Key could not be loaded, NCCH will not be encrypted\n"); @@ -702,15 +716,13 @@ int VerifyNCCH(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) if(keyType != NoKey){ //memdump(stdout,"ncch: ",ncch,0x200); SetNcchUnfixedKeys(keys,ncch); - if(GetNCCHKey(keyType,keys) == NULL){ + if(GetNCCHKey0(keyType,keys) == NULL){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] Failed to load ncch aes key.\n"); return UNABLE_TO_LOAD_NCCH_KEY; } - key0 = GetNCCHKey(keyType,keys); - key1 = GetNCCHKey(keyType,keys); - if(keyType == KeyIsUnFixed2) - key0 = GetNCCHKey(KeyIsUnFixed,keys); + key0 = GetNCCHKey0(keyType,keys); + key1 = GetNCCHKey1(keyType,keys); } //memdump(stdout,"key0: ",key0,16); @@ -744,48 +756,60 @@ int VerifyNCCH(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) free(ncch_ctx); return NO_EXEFS_IN_CXI; } - // Get ExHeader - extended_hdr *ExHeader = malloc(ncch_ctx->exhdrSize); - if(!ExHeader){ + // Get ExHeader/AcexDesc + extended_hdr *exHdr = malloc(ncch_ctx->exhdrSize); + if(!exHdr){ fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); free(ncch_ctx); return MEM_ERROR; } - memcpy(ExHeader,ncch+ncch_ctx->exhdrOffset,ncch_ctx->exhdrSize); + memcpy(exHdr,ncch+ncch_ctx->exhdrOffset,ncch_ctx->exhdrSize); if(key0 != NULL) - CryptNCCHSection((u8*)ExHeader,ncch_ctx->exhdrSize,0,ncch_ctx,key0,ncch_exhdr); + CryptNCCHSection((u8*)exHdr,ncch_ctx->exhdrSize,0,ncch_ctx,key0,ncch_exhdr); // Checking Exheader Hash to see if decryption was sucessful - ctr_sha(ExHeader,0x400,Hash,CTR_SHA_256); + ctr_sha(exHdr,0x400,Hash,CTR_SHA_256); if(memcmp(Hash,hdr->exhdrHash,0x20) != 0){ //memdump(stdout,"Expected Hash: ",hdr->extended_header_sha_256_hash,0x20); //memdump(stdout,"Actual Hash: ",Hash,0x20); - //memdump(stdout,"Exheader: ",(u8*)ExHeader,0x400); + //memdump(stdout,"Exheader: ",(u8*)exHdr,0x400); if(!SuppressOutput) { fprintf(stderr,"[NCCH ERROR] ExHeader Hashcheck Failed\n"); fprintf(stderr,"[NCCH ERROR] CXI is corrupt\n"); } free(ncch_ctx); - free(ExHeader); + free(exHdr); return ExHeader_Hashfail; } - + free(exHdr); + // Checking RSA Sigs - u8 *hdr_pubk = GetNcchHdrPubKey_frm_exhdr(ExHeader); + access_descriptor *acexDesc = malloc(ncch_ctx->acexSize); + if(!acexDesc){ + fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); + free(ncch_ctx); + free(exHdr); + return MEM_ERROR; + } + memcpy(acexDesc,ncch+ncch_ctx->acexOffset,ncch_ctx->acexSize); + if(key0 != NULL) + CryptNCCHSection((u8*)acexDesc,ncch_ctx->acexSize,ncch_ctx->exhdrOffset,ncch_ctx,key0,ncch_exhdr); - if(CheckaccessDescSignature(ExHeader,keys) != 0 && !keys->rsa.isFalseSign){ + if(CheckAccessDescSignature(acexDesc,keys) != 0 && !keys->rsa.isFalseSign){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] AccessDesc Sigcheck Failed\n"); free(ncch_ctx); - free(ExHeader); + free(acexDesc); return ACCESSDESC_SIG_BAD; } - if(CheckCXISignature(hdr_sig,(u8*)hdr,hdr_pubk) != 0 /* && !keys->rsa.isFalseSign*/){ // This should always be correct + + u8 *hdr_pubk = GetAcexNcchPubKey(acexDesc); + + if(CheckCXISignature(hdr_sig,(u8*)hdr,hdr_pubk) != 0 && !keys->rsa.isFalseSign){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] CXI Header Sigcheck Failed\n"); - free(ncch_ctx); - free(ExHeader); + free(ncch_ctx); + free(acexDesc); return NCCH_HDR_SIG_BAD; } - free(ExHeader); } if(!CheckHash) @@ -904,7 +928,7 @@ int ModifyNcchIds(u8 *ncch, u8 *titleId, u8 *programId, keys_struct *keys) GetNCCHStruct(&ncch_struct,hdr); romfs = (ncch+ncch_struct.romfsOffset); SetNcchUnfixedKeys(keys, ncch); // For Secure Crypto - key = GetNCCHKey(keytype,keys); + key = GetNCCHKey1(keytype,keys); if(key == NULL){ fprintf(stderr,"[NCCH ERROR] Failed to load ncch aes key\n"); //free(ncch); @@ -928,7 +952,7 @@ int ModifyNcchIds(u8 *ncch, u8 *titleId, u8 *programId, keys_struct *keys) GetNCCHStruct(&ncch_struct,hdr); romfs = (ncch+ncch_struct.romfsOffset); SetNcchUnfixedKeys(keys, ncch); // For Secure Crypto - key = GetNCCHKey(keytype,keys); + key = GetNCCHKey1(keytype,keys); if(key == NULL){ fprintf(stderr,"[NCCH ERROR] Failed to load ncch aes key\n"); //free(ncch); @@ -1013,7 +1037,25 @@ ncch_key_type GetNCCHKeyType(ncch_hdr* hdr) return KeyIsUnFixed; } -u8* GetNCCHKey(ncch_key_type keytype, keys_struct *keys) +u8* GetNCCHKey0(ncch_key_type keytype, keys_struct *keys) +{ + switch(keytype){ + case NoKey: return NULL; + case KeyIsNormalFixed: + return keys->aes.normalKey; + case KeyIsSystemFixed: + return keys->aes.systemFixedKey; + case KeyIsUnFixed: + case KeyIsUnFixed2: + if(keys->aes.ncchKeyX0) + return keys->aes.unFixedKey0; + else + return NULL; + } + return NULL; +} + +u8* GetNCCHKey1(ncch_key_type keytype, keys_struct *keys) { switch(keytype){ case NoKey: return NULL; @@ -1035,65 +1077,6 @@ u8* GetNCCHKey(ncch_key_type keytype, keys_struct *keys) return NULL; } -int GetNCCHSection(u8 *dest, u64 dest_max_size, u64 src_pos, u8 *ncch, ncch_struct *ncch_ctx, keys_struct *keys, ncch_section section) -{ - if(!ncch) return MEM_ERROR; - u8 *key = NULL; - ncch_hdr* hdr = GetNCCH_CommonHDR(NULL,NULL,ncch); - ncch_key_type keytype = GetNCCHKeyType(hdr); - - if(keytype != NoKey && (section == ncch_exhdr || section == ncch_exefs || section == ncch_romfs)){ - key = GetNCCHKey(keytype,keys); - if(key == NULL){ - //fprintf(stderr,"[NCCH ERROR] Failed to load ncch aes key.\n"); - return UNABLE_TO_LOAD_NCCH_KEY; - } - } - //printf("detecting section type\n"); - u64 offset = 0; - u64 size = 0; - switch(section){ - case ncch_exhdr: - offset = ncch_ctx->exhdrOffset; - size = ncch_ctx->exhdrSize; - break; - case ncch_Logo: - offset = ncch_ctx->logoOffset; - size = ncch_ctx->logoSize; - break; - case ncch_PlainRegion: - offset = ncch_ctx->plainRegionOffset; - size = ncch_ctx->plainRegionSize; - break; - case ncch_exefs: - offset = ncch_ctx->exefsOffset; - size = ncch_ctx->exefsSize; - break; - case ncch_romfs: - offset = ncch_ctx->romfsOffset; - size = ncch_ctx->romfsSize; - break; - } - if(!offset || !size) return NCCH_SECTION_NOT_EXIST; - - if(src_pos > size) return DATA_POS_DNE; - - size = min_u64(size-src_pos,dest_max_size); - - //printf("Copying data\n"); - u8 *section_pos = (ncch + offset + src_pos); - memcpy(dest,section_pos,size); - - //printf("decrypting if needed\n"); - if(keytype != NoKey && (section == ncch_exhdr || section == ncch_exefs || section == ncch_romfs)){ // Decrypt - //memdump(stdout,"Key: ",key,16); - CryptNCCHSection(dest,size,src_pos,ncch_ctx,key,section); - //printf("no cigar\n"); - } - //printf("Got thing okay\n"); - return 0; -} - int GetNCCHStruct(ncch_struct *ctx, ncch_hdr *header) { memcpy(ctx->titleId,header->titleId,8); @@ -1105,7 +1088,9 @@ int GetNCCHStruct(ncch_struct *ctx, ncch_hdr *header) ctx->formatVersion = u8_to_u16(header->formatVersion,LE); if(!IsCfa(header)){ ctx->exhdrOffset = 0x200; - ctx->exhdrSize = u8_to_u32(header->exhdrSize,LE) + 0x400; + ctx->exhdrSize = u8_to_u32(header->exhdrSize,LE); + ctx->acexOffset = (ctx->exhdrOffset + ctx->exhdrSize); + ctx->acexSize = sizeof(access_descriptor); ctx->plainRegionOffset = (u64)(u8_to_u32(header->plainRegionOffset,LE)*media_unit); ctx->plainRegionSize = (u64)(u8_to_u32(header->plainRegionSize,LE)*media_unit); } diff --git a/makerom/ncch.h b/makerom/ncch.h index 1baef1fe..232a228e 100644 --- a/makerom/ncch.h +++ b/makerom/ncch.h @@ -74,6 +74,8 @@ typedef struct u16 formatVersion; u32 exhdrOffset; u32 exhdrSize; + u32 acexOffset; + u32 acexSize; u64 logoOffset; u64 logoSize; u64 plainRegionOffset; @@ -126,7 +128,6 @@ typedef struct buffer_struct *out; keys_struct *keys; rsf_settings *rsfSet; - struct { @@ -192,6 +193,7 @@ typedef struct struct { buffer_struct exhdr; + buffer_struct acexDesc; buffer_struct logo; buffer_struct plainRegion; buffer_struct exeFs; @@ -220,8 +222,8 @@ u32 GetNCCH_MediaUnitSize(ncch_hdr* hdr); u32 GetNCCH_MediaSize(ncch_hdr* hdr); ncch_key_type GetNCCHKeyType(ncch_hdr* hdr); -int GetNCCHSection(u8 *dest, u64 dest_max_size, u64 src_pos, u8 *ncch, ncch_struct *ncch_ctx, keys_struct *keys, ncch_section section); -u8* GetNCCHKey(ncch_key_type keytype, keys_struct *keys); +u8* GetNCCHKey0(ncch_key_type keytype, keys_struct *keys); +u8* GetNCCHKey1(ncch_key_type keytype, keys_struct *keys); int GetNCCHStruct(ncch_struct *ctx, ncch_hdr *header); void ncch_get_counter(ncch_struct *ctx, u8 counter[16], u8 type); From 8411e7e5336e2d0ca4072cdd8bc4821071a12ba8 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 21 Jun 2014 18:26:43 +1000 Subject: [PATCH 003/317] Forgot two files --- makerom/usersettings.c | 18 +++++++++--------- makerom/usersettings.h | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/makerom/usersettings.c b/makerom/usersettings.c index 39ff9cd4..db9a3ef6 100644 --- a/makerom/usersettings.c +++ b/makerom/usersettings.c @@ -210,6 +210,14 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) set->common.keys.dumpkeys = true; return 1; } + else if(strcmp(argv[i],"-fsign") == 0){ + if(ParamNum){ + PrintNoNeedParam("-fsign"); + return USR_BAD_ARG; + } + set->common.keys.rsa.isFalseSign = true; + return 1; + } // Ncch Options else if(strcmp(argv[i],"-elf") == 0){ @@ -450,14 +458,6 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) set->cia.titleVersion[1] = tmp & 63; return 2; } - else if(strcmp(argv[i],"-savesize") == 0){ - if(ParamNum != 1){ - PrintArgReqParam("-savesize",1); - return USR_ARG_REQ_PARAM; - } - set->cia.overideSaveDataSize = argv[i+1]; - return 2; - } else if(strcmp(argv[i],"-rand") == 0){ if(ParamNum){ PrintNoNeedParam("-rand"); @@ -983,6 +983,7 @@ void DisplayHelp(char *app_name) printf(" 'c' Custom Keys & Certs\n"); printf(" -ckeyID Override the automatic commonKey selection\n"); printf(" -showkeys Display the loaded keychain\n"); + printf(" -fsign Ignore invalid signatures\n"); printf("NCCH OPTIONS:\n"); printf(" -elf ELF File\n"); printf(" -icon Icon File\n"); @@ -1016,7 +1017,6 @@ void DisplayHelp(char *app_name) printf(" -minor Specify Minor Version\n"); printf(" -micro Specify Micro Version\n"); printf(" -dver Specify Data Title Version\n"); - printf(" -savesize Savedata size\n"); printf(" -rand Use a random title key\n"); printf(" -cci Convert CCI to CIA\n"); printf(" -srl Use TWL SRL as Content0\n"); diff --git a/makerom/usersettings.h b/makerom/usersettings.h index 577d76ce..54985f37 100644 --- a/makerom/usersettings.h +++ b/makerom/usersettings.h @@ -275,7 +275,6 @@ typedef struct } cci; // CCI Settings struct{ - char *overideSaveDataSize; bool randomTitleKey; bool encryptCia; bool DlcContent; From efd71def1c0a0f080fe70b44e0e9cac5d4385613 Mon Sep 17 00:00:00 2001 From: applestash Date: Wed, 25 Jun 2014 12:31:01 +1000 Subject: [PATCH 004/317] fixes --- makerom/Makefile | 4 +- makerom/lib.h | 4 +- makerom/ncch.c | 25 +-- makerom/ncsd.c | 26 ++- makerom/{yamlsettings.c => rsf_settings.c} | 40 ++-- makerom/{yamlsettings.h => rsf_settings.h} | 0 makerom/titleid.c | 226 +++++++++----------- makerom/titleid.h | 12 +- makerom/{usersettings.c => user_settings.c} | 0 makerom/{usersettings.h => user_settings.h} | 0 makerom/{yaml_ctr.c => yaml_parser.c} | 20 +- makerom/{yaml_ctr.h => yaml_parser.h} | 2 +- 12 files changed, 175 insertions(+), 184 deletions(-) rename makerom/{yamlsettings.c => rsf_settings.c} (87%) rename makerom/{yamlsettings.h => rsf_settings.h} (100%) rename makerom/{usersettings.c => user_settings.c} (100%) rename makerom/{usersettings.h => user_settings.h} (100%) rename makerom/{yaml_ctr.c => yaml_parser.c} (97%) rename makerom/{yaml_ctr.h => yaml_parser.h} (96%) diff --git a/makerom/Makefile b/makerom/Makefile index f8f41181..5c417042 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -3,8 +3,8 @@ UTILS_OBJS = utils.o dir.o utf.o keyset.o titleid.o CIA_OBJS = cia.o cia_read.o certs.o tik.o tmd.o tmd_read.o NCCH_OBJS = ncch.o exheader.o accessdesc.o exefs.o elf.o romfs.o romfs_import.o romfs_binary.o NCSD_OBJS = ncsd.o -SETTINGS_OBJS = usersettings.o yamlsettings.o -LIB_API_OBJS = crypto.o yaml_ctr.o blz.o +SETTINGS_OBJS = user_settings.o rsf_settings.o +LIB_API_OBJS = crypto.o yaml_parser.o blz.o OBJS = makerom.o $(UTILS_OBJS) $(LIB_API_OBJS) $(SETTINGS_OBJS) $(NCSD_OBJS) $(NCCH_OBJS) $(CIA_OBJS) diff --git a/makerom/lib.h b/makerom/lib.h index d4b87812..15a0284a 100644 --- a/makerom/lib.h +++ b/makerom/lib.h @@ -29,8 +29,8 @@ #include "crypto.h" #include "keyset.h" -#include "usersettings.h" +#include "user_settings.h" #include "libyaml/yaml.h" -#include "yaml_ctr.h" +#include "yaml_parser.h" diff --git a/makerom/ncch.c b/makerom/ncch.c index e8156db5..231451dc 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -9,6 +9,8 @@ #include "logo_data.h" // Contains Logos +const u32 MEDIA_UNIT = 0x200; + // Private Prototypes int SignCFA(u8 *Signature, u8 *CFA_HDR, keys_struct *keys); int CheckCFASignature(u8 *Signature, u8 *CFA_HDR, keys_struct *keys); @@ -173,27 +175,14 @@ int SetBasicOptions(ncch_settings *ncchset, user_settings *usrset) int result = 0; /* Options */ - ncchset->options.mediaSize = 0x200; - + ncchset->options.mediaSize = MEDIA_UNIT; ncchset->options.IncludeExeFsLogo = usrset->ncch.includeExefsLogo; - - if(usrset->common.rsfSet.Option.EnableCompress != -1) ncchset->options.CompressCode = usrset->common.rsfSet.Option.EnableCompress; - else ncchset->options.CompressCode = true; - - if(usrset->common.rsfSet.Option.UseOnSD != -1) ncchset->options.UseOnSD = usrset->common.rsfSet.Option.UseOnSD; - else ncchset->options.UseOnSD = false; - usrset->common.rsfSet.Option.UseOnSD = ncchset->options.UseOnSD; - - if(usrset->common.rsfSet.Option.EnableCrypt != -1) ncchset->options.Encrypt = usrset->common.rsfSet.Option.EnableCrypt; - else ncchset->options.Encrypt = true; - - if(usrset->common.rsfSet.Option.FreeProductCode != -1) ncchset->options.FreeProductCode = usrset->common.rsfSet.Option.FreeProductCode; - else ncchset->options.FreeProductCode = false; - + ncchset->options.CompressCode = usrset->common.rsfSet.Option.EnableCompress; + ncchset->options.UseOnSD = usrset->common.rsfSet.Option.UseOnSD; + ncchset->options.Encrypt = usrset->common.rsfSet.Option.EnableCrypt; + ncchset->options.FreeProductCode = usrset->common.rsfSet.Option.FreeProductCode; ncchset->options.IsCfa = (usrset->ncch.ncchType == CFA); - ncchset->options.IsBuildingCodeSection = (usrset->ncch.elfPath != NULL); - ncchset->options.UseRomFS = ((ncchset->rsfSet->Rom.HostRoot && strlen(ncchset->rsfSet->Rom.HostRoot) > 0) || usrset->ncch.romfsPath); if(ncchset->options.IsCfa && !ncchset->options.UseRomFS){ diff --git a/makerom/ncsd.c b/makerom/ncsd.c index ec208567..34aac36f 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -222,20 +222,30 @@ int ImportNcchPartitions(cci_settings *cciset) return 0; } +void WriteCCIDummyData(cci_settings *cciset) +{ + // Creating Buffer of Dummy Bytes + u64 len = NCCH0_OFFSET - 0x1200; + u8 *dummy_bytes = malloc(len); + memset(dummy_bytes,0xff,len); + WriteBuffer(dummy_bytes,len,0x1200,cciset->out); +} + +void WriteDevCardInfoData(cci_settings *cciset) +{ + WriteBuffer((u8*)&ctx.devcardinfo,sizeof(devcardinfo_hdr),0x1200,cciset->out); +} + int WriteHeaderToFile(cci_settings *cciset) { WriteBuffer(ctx.signature,0x100,0,cciset->out); WriteBuffer((u8*)&ctx.cciHdr,sizeof(cci_hdr),0x100,cciset->out); WriteBuffer((u8*)&ctx.cardinfo,sizeof(cardinfo_hdr),0x200,cciset->out); - if(!cciset->option.useDevCardInfo){ - // Creating Buffer of Dummy Bytes - u64 len = NCCH0_OFFSET - 0x1200; - u8 *dummy_bytes = malloc(len); - memset(dummy_bytes,0xff,len); - WriteBuffer(dummy_bytes,len,0x1200,cciset->out); - } + if(cciset->option.useDevCardInfo) + WriteDevCardInfoData(cciset); else - WriteBuffer((u8*)&ctx.devcardinfo,sizeof(devcardinfo_hdr),0x1200,cciset->out); + WriteCCIDummyData(cciset); + return 0; } diff --git a/makerom/yamlsettings.c b/makerom/rsf_settings.c similarity index 87% rename from makerom/yamlsettings.c rename to makerom/rsf_settings.c index 71f522ac..7d876ee2 100644 --- a/makerom/yamlsettings.c +++ b/makerom/rsf_settings.c @@ -1,5 +1,5 @@ #include "lib.h" -#include "yamlsettings.h" +#include "rsf_settings.h" void EvaluateRSF(rsf_settings *rsf, ctr_yaml_context *ctx) { @@ -52,13 +52,13 @@ void GET_Option(ctr_yaml_context *ctx, rsf_settings *rsf) while(ctx->Level == InitLevel){ if(ctx->error || ctx->done) return; // Handle childs - if(cmpYamlValue("AllowUnalignedSection",ctx)) rsf->Option.AllowUnalignedSection = SetBoolYAMLValue("AllowUnalignedSection",ctx); - if(cmpYamlValue("MediaFootPadding",ctx)) rsf->Option.MediaFootPadding = SetBoolYAMLValue("MediaFootPadding",ctx); - //else if(cmpYamlValue("NoPadding",ctx)) rsf->Option.NoPadding = SetBoolYAMLValue("NoPadding",ctx); - else if(cmpYamlValue("EnableCrypt",ctx)) rsf->Option.EnableCrypt = SetBoolYAMLValue("EnableCrypt",ctx); - else if(cmpYamlValue("EnableCompress",ctx)) rsf->Option.EnableCompress = SetBoolYAMLValue("EnableCompress",ctx); - else if(cmpYamlValue("FreeProductCode",ctx)) rsf->Option.FreeProductCode = SetBoolYAMLValue("FreeProductCode",ctx); - else if(cmpYamlValue("UseOnSD",ctx)) rsf->Option.UseOnSD = SetBoolYAMLValue("UseOnSD",ctx); + if(cmpYamlValue("AllowUnalignedSection",ctx)) SetBoolYAMLValue(&rsf->Option.AllowUnalignedSection,"AllowUnalignedSection",ctx); + else if(cmpYamlValue("MediaFootPadding",ctx)) SetBoolYAMLValue(&rsf->Option.MediaFootPadding,"MediaFootPadding",ctx); + //else if(cmpYamlValue("NoPadding",ctx)) SetBoolYAMLValue(&rsf->Option.NoPadding,"NoPadding",ctx); + else if(cmpYamlValue("EnableCrypt",ctx)) SetBoolYAMLValue(&rsf->Option.EnableCrypt,"EnableCrypt",ctx); + else if(cmpYamlValue("EnableCompress",ctx)) SetBoolYAMLValue(&rsf->Option.EnableCompress,"EnableCompress",ctx); + else if(cmpYamlValue("FreeProductCode",ctx)) SetBoolYAMLValue(&rsf->Option.EnableCompress,"FreeProductCode",ctx); + else if(cmpYamlValue("UseOnSD",ctx)) SetBoolYAMLValue(&rsf->Option.UseOnSD,"UseOnSD",ctx); else if(cmpYamlValue("PageSize",ctx)) SetSimpleYAMLValue(&rsf->Option.PageSize,"PageSize",ctx,0); //else if(cmpYamlValue("AppendSystemCall",ctx)) rsf->Option.AppendSystemCallNum = SetYAMLSequence(&rsf->Option.AppendSystemCall,"AppendSystemCall",ctx); else{ @@ -84,18 +84,18 @@ void GET_AccessControlInfo(ctr_yaml_context *ctx, rsf_settings *rsf) while(ctx->Level == InitLevel){ if(ctx->error || ctx->done) return; // Handle childs - if(cmpYamlValue("DisableDebug",ctx)) rsf->AccessControlInfo.DisableDebug = SetBoolYAMLValue("DisableDebug",ctx); - else if(cmpYamlValue("EnableForceDebug",ctx)) rsf->AccessControlInfo.EnableForceDebug = SetBoolYAMLValue("EnableForceDebug",ctx); - else if(cmpYamlValue("CanWriteSharedPage",ctx)) rsf->AccessControlInfo.CanWriteSharedPage = SetBoolYAMLValue("CanWriteSharedPage",ctx); - else if(cmpYamlValue("CanUsePrivilegedPriority",ctx)) rsf->AccessControlInfo.CanUsePrivilegedPriority = SetBoolYAMLValue("CanUsePrivilegedPriority",ctx); - else if(cmpYamlValue("CanUseNonAlphabetAndNumber",ctx)) rsf->AccessControlInfo.CanUseNonAlphabetAndNumber = SetBoolYAMLValue("CanUseNonAlphabetAndNumber",ctx); - else if(cmpYamlValue("PermitMainFunctionArgument",ctx)) rsf->AccessControlInfo.PermitMainFunctionArgument = SetBoolYAMLValue("PermitMainFunctionArgument",ctx); - else if(cmpYamlValue("CanShareDeviceMemory",ctx)) rsf->AccessControlInfo.CanShareDeviceMemory = SetBoolYAMLValue("CanShareDeviceMemory",ctx); - else if(cmpYamlValue("UseOtherVariationSaveData",ctx)) rsf->AccessControlInfo.UseOtherVariationSaveData = SetBoolYAMLValue("UseOtherVariationSaveData",ctx); - else if(cmpYamlValue("UseExtSaveData",ctx)) rsf->AccessControlInfo.UseExtSaveData = SetBoolYAMLValue("UseExtSaveData",ctx); - else if(cmpYamlValue("UseExtendedSaveDataAccessControl",ctx)) rsf->AccessControlInfo.UseExtendedSaveDataAccessControl = SetBoolYAMLValue("UseExtendedSaveDataAccessControl",ctx); - else if(cmpYamlValue("RunnableOnSleep",ctx)) rsf->AccessControlInfo.RunnableOnSleep = SetBoolYAMLValue("RunnableOnSleep",ctx); - else if(cmpYamlValue("SpecialMemoryArrange",ctx)) rsf->AccessControlInfo.SpecialMemoryArrange = SetBoolYAMLValue("SpecialMemoryArrange",ctx); + if(cmpYamlValue("DisableDebug",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.DisableDebug,"DisableDebug",ctx); + else if(cmpYamlValue("EnableForceDebug",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.EnableForceDebug,"EnableForceDebug",ctx); + else if(cmpYamlValue("CanWriteSharedPage",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.CanWriteSharedPage,"CanWriteSharedPage",ctx); + else if(cmpYamlValue("CanUsePrivilegedPriority",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.CanUsePrivilegedPriority,"CanUsePrivilegedPriority",ctx); + else if(cmpYamlValue("CanUseNonAlphabetAndNumber",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.CanUseNonAlphabetAndNumber,"CanUseNonAlphabetAndNumber",ctx); + else if(cmpYamlValue("PermitMainFunctionArgument",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.PermitMainFunctionArgument,"PermitMainFunctionArgument",ctx); + else if(cmpYamlValue("CanShareDeviceMemory",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.CanShareDeviceMemory,"CanShareDeviceMemory",ctx); + else if(cmpYamlValue("UseOtherVariationSaveData",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.UseOtherVariationSaveData,"UseOtherVariationSaveData",ctx); + else if(cmpYamlValue("UseExtSaveData",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.UseExtSaveData,"UseExtSaveData",ctx); + else if(cmpYamlValue("UseExtendedSaveDataAccessControl",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.UseExtendedSaveDataAccessControl,"UseExtendedSaveDataAccessControl",ctx); + else if(cmpYamlValue("RunnableOnSleep",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.RunnableOnSleep,"RunnableOnSleep",ctx); + else if(cmpYamlValue("SpecialMemoryArrange",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.SpecialMemoryArrange,"SpecialMemoryArrange",ctx); //else if(cmpYamlValue("ProgramId",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.ProgramId,"ProgramId",ctx,0); diff --git a/makerom/yamlsettings.h b/makerom/rsf_settings.h similarity index 100% rename from makerom/yamlsettings.h rename to makerom/rsf_settings.h diff --git a/makerom/titleid.c b/makerom/titleid.c index 0c2346bd..071b13ee 100644 --- a/makerom/titleid.c +++ b/makerom/titleid.c @@ -2,141 +2,153 @@ #include "ncch.h" #include "titleid.h" -u32 SetPIDCategoryFromName(char *Category); -u32 SetPIDCategoryFromFlags(char **CategoryFlags, u32 FlagNum); -u32 SetPIDCategoryFromFlag(u32 Category, u32 Flag, char *FlagName); +void SetPIDType(u16 *type); +int SetPIDCategoryFromName(u16 *cat, char *CategoryStr); +int SetPIDCategoryFromFlags(u16 *cat, char **CategoryFlags, u32 FlagNum); +int SetPIDCategoryFromFlag(u16 *cat, u16 flag, char *flagName); u32 SetPIDUniqueId(char *UniqueIdStr); -u16 SetTitleVariation(u16 Category, rsf_settings *yaml_set); +int SetTitleVariation(u8 *var, u16 cat, rsf_settings *rsf); u64 ConvertTwlIdToCtrId(u64 pgid) { return 0x0004800000000000 | (pgid & 0x00007FFFFFFFFFFF); } -int GetProgramID(u64 *dest, rsf_settings *yaml, bool IsForExheader) +int GetProgramID(u64 *dest, rsf_settings *rsf, bool IsForExheader) { - if(yaml->TitleInfo.Category && yaml->TitleInfo.CategoryFlags){ + int ret; + u32 uniqueId; + u16 type,category; + u8 variation; + + if(rsf->TitleInfo.Category && rsf->TitleInfo.CategoryFlags){ fprintf(stderr,"[ID ERROR] Can not set \"Cateory\" and \"CategoryFlags\" at the same time.\n"); - return PID_BAD_YAML_SET; + return PID_BAD_RSF_SET; } - u16 Type = 0x0004; - u32 m_Category = 0; - u32 UniqueId = 0; - u16 m_Variation = 0; + // Getting Type + SetPIDType(&type); + // Getting Category - if(yaml->TitleInfo.Category) - m_Category = SetPIDCategoryFromName(yaml->TitleInfo.Category); - else if(yaml->TitleInfo.CategoryFlags) - m_Category = SetPIDCategoryFromFlags(yaml->TitleInfo.CategoryFlags,yaml->TitleInfo.CategoryFlagsNum); - if(IsForExheader && yaml->TitleInfo.TargetCategory) - m_Category = SetPIDCategoryFromName(yaml->TitleInfo.TargetCategory); - if(m_Category == PID_INVALID_CATEGORY) // Error occured - return PID_BAD_YAML_SET; + if(rsf->TitleInfo.Category) + ret = SetPIDCategoryFromName(&category,rsf->TitleInfo.Category); + else if(rsf->TitleInfo.CategoryFlags) + ret = SetPIDCategoryFromFlags(&category,rsf->TitleInfo.CategoryFlags,rsf->TitleInfo.CategoryFlagsNum); + if(IsForExheader && rsf->TitleInfo.TargetCategory) + ret = SetPIDCategoryFromName(&category,rsf->TitleInfo.TargetCategory); + if(ret == PID_INVALID_CATEGORY) // Error occured + return PID_BAD_RSF_SET; // Getting UniqueId - if(yaml->TitleInfo.UniqueId) UniqueId = SetPIDUniqueId(yaml->TitleInfo.UniqueId); + if(rsf->TitleInfo.UniqueId) + GetUniqueID(&uniqueId,rsf); else{ fprintf(stderr,"[ID ERROR] ParameterNotFound: \"TitleInfo/UniqueId\"\n"); - return PID_BAD_YAML_SET; + return PID_BAD_RSF_SET; } - m_Variation = SetTitleVariation(m_Category,yaml); - if(m_Variation == PID_INVALID_VARIATION) // Error occured - return PID_BAD_YAML_SET; + // Getting Variation + if(SetTitleVariation(&variation,category,rsf) == PID_INVALID_VARIATION) + return PID_BAD_RSF_SET; - u16 Category = (u16)m_Category; - u8 Variation = (u8)m_Variation; + u64 programId = 0; + programId |= (u64)variation<<0; + programId |= (u64)uniqueId<<8; + programId |= (u64)category<<32; + programId |= (u64)type<<48; - u64 ProgramID = 0; - ProgramID |= (u64)Variation<<0; - ProgramID |= (u64)UniqueId<<8; - ProgramID |= (u64)Category<<32; - ProgramID |= (u64)Type<<48; - - *dest = ProgramID; + *dest = programId; return 0; } -int GetUniqueID(u32 *dest, rsf_settings *yaml) +void SetPIDType(u16 *type) { - if(yaml->TitleInfo.UniqueId) *dest = 0xffffff & SetPIDUniqueId(yaml->TitleInfo.UniqueId); + *type = 0x0004; +} + +int GetUniqueID(u32 *uid, rsf_settings *rsf) +{ + if(rsf->TitleInfo.UniqueId) *uid = 0xffffff & SetPIDUniqueId(rsf->TitleInfo.UniqueId); else{ fprintf(stderr,"[ID ERROR] ParameterNotFound: \"TitleInfo/UniqueId\"\n"); - return PID_BAD_YAML_SET; + return PID_BAD_RSF_SET; } return 0; } -u32 SetPIDCategoryFromName(char *Category) +int SetPIDCategoryFromName(u16 *cat, char *CategoryStr) { - if(strcmp(Category,"Application") == 0) return PROGRAM_ID_CATEGORY_APPLICATION; - else if(strcmp(Category,"SystemApplication") == 0) return PROGRAM_ID_CATEGORY_SYSTEM_APPLICATION; - else if(strcmp(Category,"Applet") == 0) return PROGRAM_ID_CATEGORY_APPLET; - else if(strcmp(Category,"Firmware") == 0) return PROGRAM_ID_CATEGORY_FIRMWARE; - else if(strcmp(Category,"Base") == 0) return PROGRAM_ID_CATEGORY_BASE; - else if(strcmp(Category,"DlpChild") == 0) return PROGRAM_ID_CATEGORY_DLP_CHILD; - else if(strcmp(Category,"Demo") == 0) return PROGRAM_ID_CATEGORY_DEMO; - else if(strcmp(Category,"Contents") == 0) return PROGRAM_ID_CATEGORY_CONTENTS; - else if(strcmp(Category,"SystemContents") == 0) return PROGRAM_ID_CATEGORY_SYSTEM_CONTENT; - else if(strcmp(Category,"SharedContents") == 0) return PROGRAM_ID_CATEGORY_SHARED_CONTENT; - else if(strcmp(Category,"AddOnContents") == 0) return PROGRAM_ID_CATEGORY_ADD_ON_CONTENTS; - else if(strcmp(Category,"Patch") == 0) return PROGRAM_ID_CATEGORY_PATCH; - else if(strcmp(Category,"AutoUpdateContents") == 0) return PROGRAM_ID_CATEGORY_AUTO_UPDATE_CONTENT; + if(strcmp(CategoryStr,"Application") == 0) *cat = PROGRAM_ID_CATEGORY_APPLICATION; + else if(strcmp(CategoryStr,"SystemApplication") == 0) *cat = PROGRAM_ID_CATEGORY_SYSTEM_APPLICATION; + else if(strcmp(CategoryStr,"Applet") == 0) *cat = PROGRAM_ID_CATEGORY_APPLET; + else if(strcmp(CategoryStr,"Firmware") == 0) *cat = PROGRAM_ID_CATEGORY_FIRMWARE; + else if(strcmp(CategoryStr,"Base") == 0) *cat = PROGRAM_ID_CATEGORY_BASE; + else if(strcmp(CategoryStr,"DlpChild") == 0) *cat = PROGRAM_ID_CATEGORY_DLP_CHILD; + else if(strcmp(CategoryStr,"Demo") == 0) *cat = PROGRAM_ID_CATEGORY_DEMO; + else if(strcmp(CategoryStr,"Contents") == 0) *cat = PROGRAM_ID_CATEGORY_CONTENTS; + else if(strcmp(CategoryStr,"SystemContents") == 0) *cat = PROGRAM_ID_CATEGORY_SYSTEM_CONTENT; + else if(strcmp(CategoryStr,"SharedContents") == 0) *cat = PROGRAM_ID_CATEGORY_SHARED_CONTENT; + else if(strcmp(CategoryStr,"AddOnContents") == 0) *cat = PROGRAM_ID_CATEGORY_ADD_ON_CONTENTS; + else if(strcmp(CategoryStr,"Patch") == 0) *cat = PROGRAM_ID_CATEGORY_PATCH; + else if(strcmp(CategoryStr,"AutoUpdateContents") == 0) *cat = PROGRAM_ID_CATEGORY_AUTO_UPDATE_CONTENT; else { - fprintf(stderr,"[ID ERROR] Invalid Category: \"%s\"\n",Category); + fprintf(stderr,"[ID ERROR] Invalid Category: \"%s\"\n",CategoryStr); return PID_INVALID_CATEGORY; } + + return 0; } -u32 SetPIDCategoryFromFlags(char **CategoryFlags, u32 FlagNum) +int SetPIDCategoryFromFlags(u16 *cat, char **CategoryFlags, u32 FlagNum) { - u32 Category = 0; + int ret = 0; for(u32 i = 0; i < FlagNum; i++){ if(strcmp(CategoryFlags[i],"Normal") == 0) - Category = SetPIDCategoryFromFlag(Category,PROGRAM_ID_CATEGORY_FLAG_NORMAL,"Normal"); + ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_NORMAL,"Normal"); else if(strcmp(CategoryFlags[i],"DlpChild") == 0) - Category = SetPIDCategoryFromFlag(Category,PROGRAM_ID_CATEGORY_FLAG_DLP_CHILD,"DlpChild"); + ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_DLP_CHILD,"DlpChild"); else if(strcmp(CategoryFlags[i],"Demo") == 0) - Category = SetPIDCategoryFromFlag(Category,PROGRAM_ID_CATEGORY_FLAG_DEMO,"Demo"); + ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_DEMO,"Demo"); else if(strcmp(CategoryFlags[i],"Contents") == 0) - Category = SetPIDCategoryFromFlag(Category,PROGRAM_ID_CATEGORY_FLAG_CONTENTS,"Contents"); + ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_CONTENTS,"Contents"); else if(strcmp(CategoryFlags[i],"AddOnContents") == 0) - Category = SetPIDCategoryFromFlag(Category,PROGRAM_ID_CATEGORY_FLAG_ADD_ON_CONTENTS,"AddOnContents"); + ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_ADD_ON_CONTENTS,"AddOnContents"); else if(strcmp(CategoryFlags[i],"Patch") == 0) - Category = SetPIDCategoryFromFlag(Category,PROGRAM_ID_CATEGORY_FLAG_PATCH,"Patch"); + ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_PATCH,"Patch"); else if(strcmp(CategoryFlags[i],"CannotExecution") == 0) - Category = SetPIDCategoryFromFlag(Category,PROGRAM_ID_CATEGORY_FLAG_CANNOT_EXECUTION,"CannotExecution"); + ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_CANNOT_EXECUTION,"CannotExecution"); else if(strcmp(CategoryFlags[i],"System") == 0) - Category = SetPIDCategoryFromFlag(Category,PROGRAM_ID_CATEGORY_FLAG_SYSTEM,"System"); + ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_SYSTEM,"System"); else if(strcmp(CategoryFlags[i],"RequireBatchUpdate") == 0) - Category = SetPIDCategoryFromFlag(Category,PROGRAM_ID_CATEGORY_FLAG_REQUIRE_BATCH_UPDATE,"RequireBatchUpdate"); + ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_REQUIRE_BATCH_UPDATE,"RequireBatchUpdate"); else if(strcmp(CategoryFlags[i],"NotRequireUserApproval") == 0) - Category = SetPIDCategoryFromFlag(Category,PROGRAM_ID_CATEGORY_FLAG_NOT_REQUIRE_USER_APPROVAL,"NotRequireUserApproval"); + ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_NOT_REQUIRE_USER_APPROVAL,"NotRequireUserApproval"); else if(strcmp(CategoryFlags[i],"NotRequireRightForMount") == 0) - Category = SetPIDCategoryFromFlag(Category,PROGRAM_ID_CATEGORY_FLAG_NOT_REQUIRE_RIGHT_FOR_MOUNT,"NotRequireRightForMount"); + ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_NOT_REQUIRE_RIGHT_FOR_MOUNT,"NotRequireRightForMount"); else if(strcmp(CategoryFlags[i],"CanSkipConvertJumpId") == 0) - Category = SetPIDCategoryFromFlag(Category,PROGRAM_ID_CATEGORY_FLAG_CAN_SKIP_CONVERT_JUMP_ID,"CanSkipConvertJumpId"); + ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_CAN_SKIP_CONVERT_JUMP_ID,"CanSkipConvertJumpId"); + + if(ret == PID_INVALID_CATEGORY) break; else { fprintf(stderr,"[ID ERROR] Invalid CategoryFlag: \"%s\"\n",CategoryFlags[i]); return PID_INVALID_CATEGORY; } - - if(Category == PID_INVALID_CATEGORY) return PID_INVALID_CATEGORY; } - return Category; + return ret; } -u32 SetPIDCategoryFromFlag(u32 Category, u32 Flag, char *FlagName) +int SetPIDCategoryFromFlag(u16 *cat, u16 flag, char *flagName) { - if(!Flag) return Category; - if((Category & Flag) == Flag){ - fprintf(stderr,"[ID ERROR] Failed to set \"%s\" for category. CategoryFlag was already set.\n",FlagName); + if(!flag) return 0; + if((*cat & flag) == flag){ + fprintf(stderr,"[ID ERROR] Failed to set \"%s\" for category. CategoryFlag was already set.\n",flagName); return PID_INVALID_CATEGORY; } - return Category |= Flag; + *cat |= flag; + + return 0; } u32 SetPIDUniqueId(char *UniqueIdStr) @@ -144,16 +156,16 @@ u32 SetPIDUniqueId(char *UniqueIdStr) return 0xffffff & strtoull(UniqueIdStr,NULL,0); } -u16 SetTitleVariation(u16 Category, rsf_settings *yaml_set) +int SetTitleVariation(u8 *var, u16 cat, rsf_settings *rsf) { - if(IsDemo(Category)){ - if(yaml_set->TitleInfo.DemoIndex){ - u16 DemoIndex = strtol(yaml_set->TitleInfo.DemoIndex,NULL,10); - if(DemoIndex > 255 || DemoIndex == 0){ + if(IsDemo(cat)){ + if(rsf->TitleInfo.DemoIndex){ + u8 DemoIndex = 0xff & strtol(rsf->TitleInfo.DemoIndex,NULL,10); + if(DemoIndex == 0){ fprintf(stderr,"[ID ERROR] Invalid demo index \"%d\"\n",DemoIndex); return PID_INVALID_VARIATION; } - return DemoIndex; + *var = DemoIndex; } else{ fprintf(stderr,"[ID ERROR] ParameterNotFound: \"TitleInfo/DemoIndex\"\n"); @@ -161,55 +173,31 @@ u16 SetTitleVariation(u16 Category, rsf_settings *yaml_set) } } - else if(IsDlpChild(Category)){ - if(yaml_set->TitleInfo.ChildIndex){ - u16 ChildIndex = strtol(yaml_set->TitleInfo.ChildIndex,NULL,10); - if(ChildIndex > 255){ - fprintf(stderr,"[ID ERROR] Invalid child index \"%d\"\n",ChildIndex); - return PID_INVALID_VARIATION; - } - return ChildIndex; - } + else if(IsDlpChild(cat)){ + if(rsf->TitleInfo.ChildIndex) + *var = 0xff & strtol(rsf->TitleInfo.ChildIndex,NULL,10); else - return 0; + *var = 0; } - else if(IsAddOnContent(Category)){ - if(yaml_set->TitleInfo.Variation){ // Might Rename to DataTitleIndex - u16 DataTitleIndex = strtol(yaml_set->TitleInfo.Variation,NULL,10); - if(DataTitleIndex > 255){ - fprintf(stderr,"[ID ERROR] Invalid variation \"%d\"\n",DataTitleIndex); - return PID_INVALID_VARIATION; - } - return DataTitleIndex; - } + else if(IsAddOnContent(cat)){ + if(rsf->TitleInfo.Variation) // Might Rename to DataTitleIndex + *var = 0xff & strtol(rsf->TitleInfo.Variation,NULL,10); else - return 0; + *var = 0; } - else if(IsContents(Category)){ - if(yaml_set->TitleInfo.ContentsIndex){ - u16 ContentsIndex = strtol(yaml_set->TitleInfo.ContentsIndex,NULL,10); - if(ContentsIndex > 255){ - fprintf(stderr,"[ID ERROR] Invalid content index \"%d\"\n",ContentsIndex); - return PID_INVALID_VARIATION; - } - return ContentsIndex; - } + else if(IsContents(cat)){ + if(rsf->TitleInfo.ContentsIndex) + *var = 0xff & strtol(rsf->TitleInfo.ContentsIndex,NULL,10); else - return 0; + *var = 0; } else{ - if(yaml_set->TitleInfo.Version){ - u16 Version = strtol(yaml_set->TitleInfo.Version,NULL,10); - if(Version > 255){ - fprintf(stderr,"[ID ERROR] Invalid Version \"%d\"\n",Version); - return PID_INVALID_VARIATION; - } - return Version; - } + if(rsf->TitleInfo.Version) + *var = 0xff & strtol(rsf->TitleInfo.Version,NULL,10); else - return 0; + *var = 0; } - return PID_INVALID_VARIATION; + return 0; } bool IsDemo(u16 Category) diff --git a/makerom/titleid.h b/makerom/titleid.h index 6da3516c..ecfd7823 100644 --- a/makerom/titleid.h +++ b/makerom/titleid.h @@ -2,10 +2,10 @@ typedef enum { - PID_BAD_YAML_SET = -1, - PID_INVALID_CATEGORY = 0x10000, - PID_INVALID_UNIQUE_ID = 0x1000000, - PID_INVALID_VARIATION = 0x100, + PID_BAD_RSF_SET = -1, + PID_INVALID_CATEGORY = -2, + PID_INVALID_UNIQUE_ID = -3, + PID_INVALID_VARIATION = -4, } Pid_Errors; typedef enum @@ -85,8 +85,8 @@ typedef enum u64 ConvertTwlIdToCtrId(u64 pgid); -int GetProgramID(u64 *dest, rsf_settings *yaml, bool IsForExheader); -int GetUniqueID(u32 *dest, rsf_settings *yaml); +int GetProgramID(u64 *dest, rsf_settings *rsf, bool IsForExheader); +int GetUniqueID(u32 *dest, rsf_settings *rsf); bool IsDemo(u16 Category); bool IsSystem(u16 Category); diff --git a/makerom/usersettings.c b/makerom/user_settings.c similarity index 100% rename from makerom/usersettings.c rename to makerom/user_settings.c diff --git a/makerom/usersettings.h b/makerom/user_settings.h similarity index 100% rename from makerom/usersettings.h rename to makerom/user_settings.h diff --git a/makerom/yaml_ctr.c b/makerom/yaml_parser.c similarity index 97% rename from makerom/yaml_ctr.c rename to makerom/yaml_parser.c index 5924a217..9eafb571 100644 --- a/makerom/yaml_ctr.c +++ b/makerom/yaml_parser.c @@ -1,5 +1,5 @@ #include "lib.h" -#include "yamlsettings.h" +#include "rsf_settings.h" // Private Prototypes void InitYamlContext(ctr_yaml_context *ctx); @@ -410,10 +410,10 @@ void SetSimpleYAMLValue(char **dest, char *key, ctr_yaml_context *ctx, u32 size_ } -bool SetBoolYAMLValue(char *key, ctr_yaml_context *ctx) +void SetBoolYAMLValue(bool *dest, char *key, ctr_yaml_context *ctx) { GetEvent(ctx); - if(ctx->error || ctx->done) return false; + if(ctx->error || ctx->done) return; if(!EventIsScalar(ctx)){ fprintf(stderr,"[RSF ERROR] '%s' requires a value\n",key); ctx->error = YAML_BAD_FORMATTING; @@ -425,12 +425,16 @@ bool SetBoolYAMLValue(char *key, ctr_yaml_context *ctx) return false; } - if(casecmpYamlValue("true",ctx)) return true; - if(casecmpYamlValue("false",ctx)) return false; + if(casecmpYamlValue("true",ctx)) + *dest = true; + else if(casecmpYamlValue("false",ctx)) + *dest = false; + else{ + fprintf(stderr,"[RSF ERROR] Invalid '%s'\n",key); + ctx->error = YAML_BAD_FORMATTING; + } - fprintf(stderr,"[RSF ERROR] Invalid '%s'\n",key); - ctx->error = YAML_BAD_FORMATTING; - return false; + return; } diff --git a/makerom/yaml_ctr.h b/makerom/yaml_parser.h similarity index 96% rename from makerom/yaml_ctr.h rename to makerom/yaml_parser.h index 9c34cdc7..35f3dcfe 100644 --- a/makerom/yaml_ctr.h +++ b/makerom/yaml_parser.h @@ -54,7 +54,7 @@ bool CheckSequenceEvent(ctr_yaml_context *ctx); // With extra implement, use if // Functions which store values void SetSimpleYAMLValue(char **dest, char *key, ctr_yaml_context *ctx, u32 size_limit); -bool SetBoolYAMLValue(char *key, ctr_yaml_context *ctx); +void SetBoolYAMLValue(bool *dest, char *key, ctr_yaml_context *ctx); u32 SetYAMLSequence(char ***dest, char *key, ctr_yaml_context *ctx); u32 SetYAMLSequenceFromMapping(char ***dest, char *key, ctr_yaml_context *ctx, bool StoreKey); //void SkipYAMLGroup(ctr_yaml_context *ctx); \ No newline at end of file From df5c7f500c45435ffd820de5dff4f1f27a22ca61 Mon Sep 17 00:00:00 2001 From: applestash Date: Wed, 25 Jun 2014 12:32:28 +1000 Subject: [PATCH 005/317] commented "custom" key option until it is supported --- makerom/user_settings.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index db9a3ef6..9ec263ed 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -181,8 +181,8 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) set->common.keys.keyset = pki_DEVELOPMENT; else if(strcasecmp(argv[i+1],"retail") == 0 || strcasecmp(argv[i+1],"production") == 0 || strcasecmp(argv[i+1],"p") == 0) set->common.keys.keyset = pki_PRODUCTION; - else if(strcasecmp(argv[i+1],"custom") == 0 || strcasecmp(argv[i+1],"c") == 0) - set->common.keys.keyset = pki_CUSTOM; + //else if(strcasecmp(argv[i+1],"custom") == 0 || strcasecmp(argv[i+1],"c") == 0) + // set->common.keys.keyset = pki_CUSTOM; else{ fprintf(stderr,"[SETTING ERROR] Unrecognised target '%s'\n",argv[i+1]); return USR_BAD_ARG; @@ -980,7 +980,7 @@ void DisplayHelp(char *app_name) printf(" 't' Test(false) Keys & prod Certs\n"); printf(" 'd' Development Keys & Certs\n"); printf(" 'p' Production Keys & Certs\n"); - printf(" 'c' Custom Keys & Certs\n"); + //printf(" 'c' Custom Keys & Certs\n"); printf(" -ckeyID Override the automatic commonKey selection\n"); printf(" -showkeys Display the loaded keychain\n"); printf(" -fsign Ignore invalid signatures\n"); From 65038633e4768a15e918f4eba093bbd6f470e55e Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 28 Jun 2014 18:38:52 +1000 Subject: [PATCH 006/317] many small changes added function to fill memory with random bytes (cleaning some code with that). Removed public build #ifdefs since 3dsguy apparently uploaded the entire source with all "private" things. --- makerom/Makefile | 4 ++-- makerom/accessdesc.c | 8 -------- makerom/cia.c | 9 +++------ makerom/crypto.c | 2 -- makerom/keyset.c | 4 ---- makerom/ncch.c | 2 +- makerom/ncsd.c | 24 +++--------------------- makerom/tik.c | 16 +++++++--------- makerom/tmd.c | 6 +++--- makerom/user_settings.c | 20 ++++++-------------- makerom/utils.c | 12 ++++++++++++ makerom/utils.h | 2 ++ 12 files changed, 39 insertions(+), 70 deletions(-) diff --git a/makerom/Makefile b/makerom/Makefile index 5c417042..79e3eadb 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -19,9 +19,9 @@ CFLAGS = --std=c99 -Wall -Wno-unused-but-set-variable -Wno-unused-value -I. -DMA CC = gcc # MAKEROM Build Settings -MAKEROM_BUILD_FLAGS = #-DPUBLIC_BUILD #-DDEBUG +MAKEROM_BUILD_FLAGS = #-DDEBUG VER_MAJOR = 0 -VER_MINOR = 8 +VER_MINOR = 9 OUTPUT = makerom main: build diff --git a/makerom/accessdesc.c b/makerom/accessdesc.c index 7247ab6b..8c316c5a 100644 --- a/makerom/accessdesc.c +++ b/makerom/accessdesc.c @@ -6,10 +6,8 @@ #include "polarssl/base64.h" #include "desc_presets.h" -#ifndef PUBLIC_BUILD #include "desc_dev_sigdata.h" #include "desc_prod_sigdata.h" -#endif const int RSF_RSA_DATA_LEN = 344; const int RSF_DESC_DATA_LEN = 684; @@ -19,9 +17,7 @@ int accessdesc_SignWithKey(exheader_settings *exhdrset); int accessdesc_GetSignFromRsf(exheader_settings *exhdrset); int accessdesc_GetSignFromPreset(exheader_settings *exhdrset); void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, keys_struct *keys); -#ifndef PUBLIC_BUILD void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, keys_struct *keys); -#endif bool IsValidB64Char(char chr); u32 b64_strlen(char *str); @@ -153,9 +149,7 @@ int accessdesc_GetSignFromPreset(exheader_settings *exhdrset) u8 *cxiPvtk = NULL; accessdesc_GetPresetData(&desc,&accessDesc,&depList,exhdrset->keys); -#ifndef PUBLIC_BUILD accessdesc_GetPresetSigData(&accessDescSig,&cxiPubk,&cxiPvtk,exhdrset->keys); -#endif // Error Checking if(!desc || !depList){ @@ -307,7 +301,6 @@ void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, keys_str } } -#ifndef PUBLIC_BUILD void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, keys_struct *keys) { if(keys->accessDescSign.presetType == desc_preset_APP){ @@ -459,7 +452,6 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, } } } -#endif bool IsValidB64Char(char chr) { diff --git a/makerom/cia.c b/makerom/cia.c index 55967dc4..7d539326 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -182,14 +182,11 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) } // Ticket Data - u64_to_u8(ciaset->tik.ticketId,u64GetRand(),BE); + rndset(ciaset->tik.ticketId,16); if(usrset->cia.randomTitleKey) - { - u64_to_u8(ciaset->common.titleKey,u64GetRand(),BE); - u64_to_u8((ciaset->common.titleKey+8),u64GetRand(),BE); - } + rndset(ciaset->common.titleKey,16); else - memset(ciaset->common.titleKey,0,16); + clrmem(ciaset->common.titleKey,16); ciaset->tik.formatVersion = 1; diff --git a/makerom/crypto.c b/makerom/crypto.c index e3cdf469..fc482e0e 100644 --- a/makerom/crypto.c +++ b/makerom/crypto.c @@ -15,13 +15,11 @@ u8* AesKeyScrambler(u8 *Key, u8 *KeyX, u8 *KeyY) for(int i = 0; i < 16; i++) Key[i] = KeyX[i] ^ ((KeyY[i] >> 2) | ((KeyY[i < 15 ? i+1 : 0] & 3) << 6)); -#ifndef PUBLIC_BUILD const u8 SCRAMBLE_SECRET[16] = {0x51, 0xD7, 0x5D, 0xBE, 0xFD, 0x07, 0x57, 0x6A, 0x1C, 0xFC, 0x2A, 0xF0, 0x94, 0x4B, 0xD5, 0x6C}; // Apply Secret to get final normal key for(int i = 0; i < 16; i++) Key[i] = Key[i] ^ SCRAMBLE_SECRET[i]; -#endif return Key; } diff --git a/makerom/keyset.c b/makerom/keyset.c index 38e5cf84..b44ecb68 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -2,10 +2,8 @@ // KeyData #include "tpki.h" // Test PKI -#ifndef PUBLIC_BUILD #include "ppki.h" // Production PKI #include "dpki.h" // Development PKI -#endif // Private Prototypes int SetRsaKeySet(u8 **PrivDest, u8 *PrivSource, u8 **PubDest, u8 *PubSource); @@ -88,7 +86,6 @@ int LoadKeysFromResources(keys_struct *keys) /* RSA Keys */ keys->rsa.isFalseSign = true; } - #ifndef PUBLIC_BUILD else if(keys->keyset == pki_DEVELOPMENT){ keys->keysetLoaded = true; /* AES Keys */ @@ -154,7 +151,6 @@ int LoadKeysFromResources(keys_struct *keys) SetTikCert(keys,(u8*)xsC_ppki_cert); SetTmdCert(keys,(u8*)cpB_ppki_cert); } -#endif return 0; } diff --git a/makerom/ncch.c b/makerom/ncch.c index 231451dc..cc53630d 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -64,7 +64,7 @@ int build_NCCH(user_settings *usrset) int result; // Init Settings\n"); - ncch_settings *ncchset = malloc(sizeof(ncch_settings)); + ncch_settings *ncchset = calloc(1,sizeof(ncch_settings)); if(!ncchset) { fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); return MEM_ERROR; diff --git a/makerom/ncsd.c b/makerom/ncsd.c index 34aac36f..f8ee18e2 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -367,34 +367,16 @@ int GetDataFromContent0(cci_settings *cciset, user_settings *usrset) memcpy(cciset->header.mediaId,hdr->titleId,8); memcpy(&cciset->content.titleId[0],hdr->titleId,8); -#ifndef PUBLIC_BUILD if(usrset->cci.useSDKStockData){ memcpy(cciset->cardinfo.initialData,stock_initial_data,0x30); memcpy(cciset->cardinfo.titleKey,stock_title_key,0x10); cciset->option.useDevCardInfo = true; } else{ - for(int i = 0; i < 0x2c/sizeof(u32); i++) - { - u32 val = u32GetRand(); - memcpy((cciset->cardinfo.initialData+i*sizeof(u32)),&val,4); - } - /* - for(int i = 0; i < 2; i++) - { - u64 val = u64GetRand(); - memcpy((cciset->cardinfo.titleKey+i*8),&val,8); - } - cciset->option.useDevCardInfo = true; - */ - } -#else - for(int i = 0; i < 0x2c/sizeof(u32); i++) - { - u32 val = u32GetRand(); - memcpy((cciset->cardinfo.initialData+i*sizeof(u32)),&val,4); + rndset(cciset->cardinfo.initialData,0x2c); + //rndset(cciset->cardinfo.titleKey,0x10); + //cciset->option.useDevCardInfo = true; } -#endif cciset->header.flags[MediaUnitSize] = hdr->flags[ContentUnitSize]; cciset->option.mediaUnit = GetNCCH_MediaUnitSize(hdr); diff --git a/makerom/tik.c b/makerom/tik.c index 5d47d5e1..ea312f09 100644 --- a/makerom/tik.c +++ b/makerom/tik.c @@ -39,7 +39,7 @@ int SetupTicketBuffer(buffer_struct *tik) int SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset) { - memset(hdr,0,sizeof(tik_hdr)); + clrmem(hdr,sizeof(tik_hdr)); memcpy(hdr->issuer,ciaset->tik.issuer,0x40); hdr->formatVersion = ciaset->tik.formatVersion; @@ -47,10 +47,8 @@ int SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset) hdr->signerCrlVersion = ciaset->cert.signerCrlVersion; if(ciaset->content.encryptCia) CryptTitleKey(hdr->encryptedTitleKey, ciaset->common.titleKey,ciaset->common.titleId,ciaset->keys,ENC); - else{ - u64_to_u8(hdr->encryptedTitleKey,u64GetRand(),BE); - u64_to_u8((hdr->encryptedTitleKey+8),u64GetRand(),BE); - } + else + rndset(hdr->encryptedTitleKey,16); memcpy(hdr->ticketId,ciaset->tik.ticketId,8); memcpy(hdr->deviceId,ciaset->tik.deviceId,8); memcpy(hdr->titleId,ciaset->common.titleId,8); @@ -66,7 +64,7 @@ int SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset) int SignTicketHeader(tik_hdr *hdr, tik_signature *sig, keys_struct *keys) { - memset(sig,0,sizeof(tik_signature)); + clrmem(sig,sizeof(tik_signature)); u32_to_u8(sig->sigType,RSA_2048_SHA256,BE); return ctr_sig((u8*)hdr,sizeof(tik_hdr),sig->data,keys->rsa.xsPub,keys->rsa.xsPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } @@ -80,7 +78,7 @@ int CryptTitleKey(u8 *EncTitleKey, u8 *DecTitleKey, u8 *TitleID, keys_struct *ke //Setting up Aes Context ctr_aes_context ctx; - memset(&ctx,0x0,sizeof(ctr_aes_context)); + clrmem(&ctx,sizeof(ctr_aes_context)); //Crypting TitleKey ctr_init_aes_cbc(&ctx,keys->aes.commonKey[keys->aes.currentCommonKey],iv,mode); @@ -91,12 +89,12 @@ int CryptTitleKey(u8 *EncTitleKey, u8 *DecTitleKey, u8 *TitleID, keys_struct *ke return 0; } -void SetLimits(tik_hdr *hdr, cia_settings *ciaset) +void SetLimits(tik_hdr *hdr, cia_settings *ciaset) // TODO? { memset(hdr->limits,0,0x40); } -void SetContentIndexData(tik_hdr *hdr, cia_settings *ciaset) +void SetContentIndexData(tik_hdr *hdr, cia_settings *ciaset) // TODO? { memset(hdr->contentIndex,0,0xAC); memcpy(hdr->contentIndex,default_contentIndex,0x30); diff --git a/makerom/tmd.c b/makerom/tmd.c index 744fe055..173c1e44 100644 --- a/makerom/tmd.c +++ b/makerom/tmd.c @@ -48,7 +48,7 @@ int SetupTMDBuffer(buffer_struct *tmd) int SetupTMDHeader(tmd_hdr *hdr, tmd_content_info_record *info_record, cia_settings *ciaset) { - memset(hdr,0,sizeof(tmd_hdr)); + clrmem(hdr,sizeof(tmd_hdr)); memcpy(hdr->issuer,ciaset->tmd.issuer,0x40); hdr->formatVersion = ciaset->tmd.formatVersion; @@ -67,14 +67,14 @@ int SetupTMDHeader(tmd_hdr *hdr, tmd_content_info_record *info_record, cia_setti int SignTMDHeader(tmd_hdr *hdr, tmd_signature *sig, keys_struct *keys) { - memset(sig,0,sizeof(tmd_signature)); + clrmem(sig,sizeof(tmd_signature)); u32_to_u8(sig->sigType,RSA_2048_SHA256,BE); return ctr_sig((u8*)hdr,sizeof(tmd_hdr),sig->data,keys->rsa.cpPub,keys->rsa.cpPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } int SetupTMDInfoRecord(tmd_content_info_record *info_record, u8 *content_record, u16 ContentCount) { - memset(info_record,0x0,sizeof(tmd_content_info_record)*0x40); + clrmem(info_record,sizeof(tmd_content_info_record)*0x40); u16_to_u8(info_record->contentIndexOffset,0x0,BE); u16_to_u8(info_record->contentCommandCount,ContentCount,BE); ctr_sha(content_record,sizeof(tmd_content_chunk)*ContentCount,info_record->contentChunkHash,CTR_SHA_256); diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 9ec263ed..13762930 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -29,12 +29,11 @@ int ParseArgs(int argc, char *argv[], user_settings *usr_settings) } // Allocating Memory for Content Path Ptrs - usr_settings->common.contentPath = malloc(CIA_MAX_CONTENT*sizeof(char*)); + usr_settings->common.contentPath = calloc(CIA_MAX_CONTENT,sizeof(char*)); if(usr_settings->common.contentPath == NULL){ fprintf(stderr,"[SETTING ERROR] Not Enough Memory\n"); return USR_MEM_ERROR; } - memset(usr_settings->common.contentPath,0,CIA_MAX_CONTENT*sizeof(char*)); // Initialise Keys InitKeys(&usr_settings->common.keys); @@ -81,7 +80,7 @@ int ParseArgs(int argc, char *argv[], user_settings *usr_settings) else if(usr_settings->common.workingFileType == infile_ncsd || usr_settings->common.workingFileType == infile_srl) source_path = usr_settings->common.workingFilePath; else source_path = usr_settings->common.contentPath[0]; u16 outfile_len = strlen(source_path) + 3; - usr_settings->common.outFileName = malloc(outfile_len); + usr_settings->common.outFileName = calloc(outfile_len,sizeof(char)); if(!usr_settings->common.outFileName){ fprintf(stderr,"[SETTING ERROR] Not Enough Memory\n"); return USR_MEM_ERROR; @@ -270,8 +269,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } u32 app_type_len = (u32)(tmp2-tmp); - char *app_type = malloc(app_type_len+1); - memset(app_type,0,app_type_len+1); + char *app_type = calloc(app_type_len+1,sizeof(char)); memcpy(app_type,tmp,app_type_len); if(strcasecmp(app_type,"App") == 0 || strcasecmp(app_type,"SDApp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_APP; @@ -366,7 +364,6 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return 2; } // Cci Options -#ifndef PUBLIC_BUILD else if(strcmp(argv[i],"-devcardcci") == 0){ if(ParamNum){ PrintNoNeedParam("-devcardcci"); @@ -375,7 +372,6 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) set->cci.useSDKStockData = true; return 1; } -#endif else if(strcmp(argv[i],"-nomodtid") == 0){ if(ParamNum){ PrintNoNeedParam("-nomodtid"); @@ -958,15 +954,12 @@ void PrintNoNeedParam(char *arg) void DisplayHelp(char *app_name) { printf("CTR MAKEROM %d.%d",MAKEROM_VER_MAJOR,MAKEROM_VER_MINOR); -#ifndef PUBLIC_BUILD - printf(" PRIVATE BUILD"); -#endif printf("\n(C) 3DSGuy 2014\n"); printf("Usage: %s [options... ]\n",app_name); printf("Option Parameter Explanation\n"); printf("GLOBAL OPTIONS:\n"); printf(" -help Display this text\n"); - printf(" -rsf RSF File\n"); + printf(" -rsf Rom Specification File (*.rsf)\n"); printf(" -f Output Format, defaults to 'cxi'\n"); //printf(" 'cxi' CTR Executable Image\n"); //printf(" 'cfa' CTR File Archive\n"); @@ -976,7 +969,8 @@ void DisplayHelp(char *app_name) //printf(" -v Verbose\n"); printf(" -DNAME=VALUE Substitute values in Spec files\n"); printf("KEY OPTIONS:\n"); - printf(" -target Target for crypto, defaults to 't'\n"); + //printf(" -target Target for crypto, defaults to 't'\n"); + printf(" -target Target for crypto, defaults to 't'\n"); printf(" 't' Test(false) Keys & prod Certs\n"); printf(" 'd' Development Keys & Certs\n"); printf(" 'p' Production Keys & Certs\n"); @@ -1005,9 +999,7 @@ void DisplayHelp(char *app_name) printf(" -romfs RomFS File\n"); printf("CCI OPTIONS:\n"); printf(" -content : Specify content files\n"); -#ifndef PUBLIC_BUILD printf(" -devcardcci Use SDK CardInfo Method\n"); -#endif printf(" -nomodtid Don't Modify Content TitleIDs\n"); printf(" -alignwr Align Writeable Region to the end of last NCCH\n"); printf(" -genupdatenote Create Update Partition Notes\n"); diff --git a/makerom/utils.c b/makerom/utils.c index 44dcdbb1..45b509d6 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -44,6 +44,18 @@ int CopyData(u8 **dest, u8 *source, u64 size) return 0; } +void rndset(void *ptr, u64 num) +{ + u8 *tmp = (u8*)ptr; + for(u64 i = 0; i < num ; i++) + tmp[i] = u8GetRand(); +} + +void clrmem(void *ptr, u64 num) +{ + memset(ptr,0,num); +} + // Misc u64 align(u64 value, u64 alignment) { diff --git a/makerom/utils.h b/makerom/utils.h index 930e31bc..5d3f10a1 100644 --- a/makerom/utils.h +++ b/makerom/utils.h @@ -10,6 +10,8 @@ typedef struct void char_to_u8_array(unsigned char destination[], char source[], int size, int endianness, int base); void endian_memcpy(u8 *destination, u8 *source, u32 size, int endianness); int CopyData(u8 **dest, u8 *source, u64 size); +void rndset(void *ptr, u64 num); +void clrmem(void *ptr, u64 num); // MISC u64 align(u64 value, u64 alignment); From 21f83ca636603d40161d483c9431dfb1f38fe0e7 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 28 Jun 2014 18:41:00 +1000 Subject: [PATCH 007/317] misc --- makerom/user_settings.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 13762930..6bee1306 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -953,8 +953,8 @@ void PrintNoNeedParam(char *arg) void DisplayHelp(char *app_name) { - printf("CTR MAKEROM %d.%d",MAKEROM_VER_MAJOR,MAKEROM_VER_MINOR); - printf("\n(C) 3DSGuy 2014\n"); + printf("CTR MAKEROM %d.%d\n",MAKEROM_VER_MAJOR,MAKEROM_VER_MINOR); + printf("(C) 3DSGuy 2014\n"); printf("Usage: %s [options... ]\n",app_name); printf("Option Parameter Explanation\n"); printf("GLOBAL OPTIONS:\n"); From 7d4f1a4092f3326564feecfe1a35ff9fd13dbcbf Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 28 Jun 2014 18:43:22 +1000 Subject: [PATCH 008/317] cleaned romfs_binary.c --- makerom/romfs_binary.c | 44 +++--------------------------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/makerom/romfs_binary.c b/makerom/romfs_binary.c index b087f207..b9cc1f02 100644 --- a/makerom/romfs_binary.c +++ b/makerom/romfs_binary.c @@ -58,7 +58,7 @@ int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) FilterRomFS(fs_raw,ctx->fs,filter_criteria); // free unfiltered FS - fs_PrintDir(fs_raw,0); + //fs_PrintDir(fs_raw,0); //printf("free discarded file ptrs\n"); fs_FreeFiles(fs_raw); // All important FPs have been moved with FilterRomFS, so only un-wanted FPs are closed here //printf("free structs in fs_raw\n"); @@ -75,7 +75,7 @@ int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) // Print Filtered FS //printf("print filtered FS\n"); - fs_PrintDir(ctx->fs,0); + //fs_PrintDir(ctx->fs,0); //printf("predict romfs size\n"); CalcRomfsSize(ctx); @@ -469,42 +469,4 @@ void GenIvfcHashTree(romfs_buildctx *ctx) } return; -} - -/* -int main(int argc, char **argv) -{ - if(argc!=3){ - if(argc == 2) - return old_main(argc,argv); - printf("usage: %s \n",argv[0]); - return -1; - } - - romfs_buildctx *ctx = calloc(1,sizeof(romfs_buildctx)); - //memset(&ctx,0,sizeof(romfs_buildctx)); - PrepareRomfsBuild(NULL, ctx, argv[1]); - - if(ctx->romfsSize){ - ctx->output = calloc(1,ctx->romfsSize); - - int ret = RomfsBuild(ctx); - if(ret!=0) return -1; - - FILE *romfs = fopen(argv[2],"wb"); - fwrite(ctx->output,ctx->romfsSize,1,romfs); - fclose(romfs); - - //printf("free output ptr\n"); - free(ctx->output); - } - - //printf("free output\n"); - FreeRomfsCtx(ctx); - //printf("free ctx\n"); - free(ctx); - - //printf("return\n"); - return 0; -} -*/ \ No newline at end of file +} \ No newline at end of file From d5edefd765c9c87aeba8692bbffef11d7bd1a9ac Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 28 Jun 2014 23:34:12 +1000 Subject: [PATCH 009/317] git ignore and description --- .gitignore | 10 ++++++++++ README.md | 2 ++ 2 files changed, 12 insertions(+) diff --git a/.gitignore b/.gitignore index 1598f76e..21388103 100644 --- a/.gitignore +++ b/.gitignore @@ -215,3 +215,13 @@ pip-log.txt .mr.developer.cfg *.o + +#CTR +*.cxi +*.cfa +*.cci +*.3ds +*.cia + +#exec +*.exe \ No newline at end of file diff --git a/README.md b/README.md index 0f48df27..bbbe88e9 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,5 @@ Project_CTR =========== ctrtool - updated version of neimod's ctrtool. + +makerom - creates CTR cxi/cfa/cci/cia files. From 3d3bbf9bf4c8468920647e5940b9e2acc639b7a2 Mon Sep 17 00:00:00 2001 From: applestash Date: Sun, 29 Jun 2014 01:39:52 +1000 Subject: [PATCH 010/317] deleted empty files --- makerom/makerom.h | 0 makerom/romfs_spec.h | 2 -- 2 files changed, 2 deletions(-) delete mode 100644 makerom/makerom.h delete mode 100644 makerom/romfs_spec.h diff --git a/makerom/makerom.h b/makerom/makerom.h deleted file mode 100644 index e69de29b..00000000 diff --git a/makerom/romfs_spec.h b/makerom/romfs_spec.h deleted file mode 100644 index 3f59c932..00000000 --- a/makerom/romfs_spec.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once - From 44ccbedee3ab4e7d7438761e0a568082a30caba0 Mon Sep 17 00:00:00 2001 From: applestash Date: Mon, 14 Jul 2014 01:58:09 +1000 Subject: [PATCH 011/317] fixed deviceId corruption bug --- makerom/cia.c | 7 ++++++- makerom/cia.h | 2 +- makerom/tik.c | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/makerom/cia.c b/makerom/cia.c index 7d539326..f3db6be1 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -182,7 +182,12 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) } // Ticket Data - rndset(ciaset->tik.ticketId,16); + rndset(ciaset->tik.ticketId,8); + clrmem(ciaset->tik.deviceId,4); + clrmem(ciaset->tik.eshopAccId,4); + ciaset->tik.licenceType = 0; + ciaset->tik.audit = 0; + if(usrset->cia.randomTitleKey) rndset(ciaset->common.titleKey,16); else diff --git a/makerom/cia.h b/makerom/cia.h index 57bc0ba7..6907deaf 100644 --- a/makerom/cia.h +++ b/makerom/cia.h @@ -62,7 +62,7 @@ typedef struct u16 version; u8 ticketId[8]; - u8 deviceId[8]; + u8 deviceId[4]; u8 licenceType; u8 audit; u8 eshopAccId[4]; diff --git a/makerom/tik.c b/makerom/tik.c index ea312f09..ba95e232 100644 --- a/makerom/tik.c +++ b/makerom/tik.c @@ -50,7 +50,7 @@ int SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset) else rndset(hdr->encryptedTitleKey,16); memcpy(hdr->ticketId,ciaset->tik.ticketId,8); - memcpy(hdr->deviceId,ciaset->tik.deviceId,8); + memcpy(hdr->deviceId,ciaset->tik.deviceId,4); memcpy(hdr->titleId,ciaset->common.titleId,8); u16_to_u8(hdr->ticketVersion,ciaset->tik.version,BE); hdr->licenceType = ciaset->tik.licenceType; From 1c8f323a1a577e2665ee29ca8d984d208d8f0b08 Mon Sep 17 00:00:00 2001 From: applestash Date: Mon, 14 Jul 2014 02:35:02 +1000 Subject: [PATCH 012/317] fixed invalid return type --- makerom/yaml_parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makerom/yaml_parser.c b/makerom/yaml_parser.c index 9eafb571..3107cd24 100644 --- a/makerom/yaml_parser.c +++ b/makerom/yaml_parser.c @@ -417,12 +417,12 @@ void SetBoolYAMLValue(bool *dest, char *key, ctr_yaml_context *ctx) if(!EventIsScalar(ctx)){ fprintf(stderr,"[RSF ERROR] '%s' requires a value\n",key); ctx->error = YAML_BAD_FORMATTING; - return false; + return; } if(!GetYamlStringSize(ctx)){ fprintf(stderr,"[RSF ERROR] '%s' requires a value\n",key); ctx->error = YAML_BAD_FORMATTING; - return false; + return; } if(casecmpYamlValue("true",ctx)) From 0d65ce286ea1bdf73b07ef0e7da9a990db361ce0 Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 15 Jul 2014 15:16:43 +1000 Subject: [PATCH 013/317] got rid of magic numbers --- makerom/cia.c | 30 ++++++++++++------------ makerom/cia.h | 1 + makerom/types.h | 8 +++++++ makerom/user_settings.c | 51 ++++++++++++++++++++++------------------- makerom/user_settings.h | 21 +++++++++++++++-- 5 files changed, 70 insertions(+), 41 deletions(-) diff --git a/makerom/cia.c b/makerom/cia.c index f3db6be1..e820a9df 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -199,7 +199,7 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) if(result) return result; // Tmd Stuff - if(usrset->cia.contentId[0] > 0xffffffff) + if(usrset->cia.contentId[0] > MAX_U32) ciaset->content.id[0] = u32GetRand(); else ciaset->content.id[0] = usrset->cia.contentId[0]; @@ -282,10 +282,10 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) int GetCIADataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_struct *ncch_ctx, u8 *key) { - extended_hdr *exhdr = malloc(0x400); - memcpy(exhdr,ncch+ncch_ctx->exhdrOffset,0x400); + extended_hdr *exhdr = malloc(sizeof(extended_hdr)); + memcpy(exhdr,ncch+ncch_ctx->exhdrOffset,sizeof(extended_hdr)); if(key != NULL) - CryptNCCHSection((u8*)exhdr,0x400,0,ncch_ctx,key,ncch_exhdr); + CryptNCCHSection((u8*)exhdr,sizeof(extended_hdr),0,ncch_ctx,key,ncch_exhdr); u16 Category = u8_to_u16((ciaset->common.titleId+2),BE); if(IsPatch(Category)||ciaset->content.IsCfa||ciaset->content.keyNotFound) @@ -300,25 +300,25 @@ int GetCIADataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_struct *ncch_ctx, u8 } if(ciaset->content.IsCfa||ciaset->content.keyNotFound){ - if(ciaset->common.titleVersion[0] == 0xffff){ // '-major' wasn't set + if(ciaset->common.titleVersion[VER_MAJOR] == MAX_U16){ // '-major' wasn't set if(ciaset->content.IsCfa){ // Is a CFA and can be decrypted fprintf(stderr,"[CIA ERROR] Invalid major version. Use \"-major\" option.\n"); return CIA_BAD_VERSION; } else // CXI which cannot be decrypted - ciaset->common.titleVersion[0] = 0; + ciaset->common.titleVersion[VER_MAJOR] = 0; } } else{ // Is a CXI and can be decrypted - if(ciaset->common.titleVersion[0] != 0xffff){ // '-major' was set + if(ciaset->common.titleVersion[VER_MAJOR] != MAX_U16){ // '-major' was set fprintf(stderr,"[CIA ERROR] Option \"-major\" cannot be applied for cxi.\n"); return CIA_BAD_VERSION; } // Setting remaster ver - ciaset->common.titleVersion[0] = GetRemasterVersion_frm_exhdr(exhdr); + ciaset->common.titleVersion[VER_MAJOR] = GetRemasterVersion_frm_exhdr(exhdr); } - u16 version = SetupVersion(ciaset->common.titleVersion[0],ciaset->common.titleVersion[1],ciaset->common.titleVersion[2]); + u16 version = SetupVersion(ciaset->common.titleVersion[VER_MAJOR],ciaset->common.titleVersion[VER_MINOR],ciaset->common.titleVersion[VER_MICRO]); ciaset->tik.version = version; ciaset->tmd.version = version; @@ -331,10 +331,10 @@ int GetMetaRegion(cia_settings *ciaset, u8 *ncch, ncch_struct *ncch_ctx, u8 *key if(ciaset->content.IsCfa || ciaset->content.keyNotFound) return 0; - extended_hdr *exhdr = malloc(0x400); - memcpy(exhdr,ncch+ncch_ctx->exhdrOffset,0x400); + extended_hdr *exhdr = malloc(sizeof(extended_hdr)); + memcpy(exhdr,ncch+ncch_ctx->exhdrOffset,sizeof(extended_hdr)); if(key != NULL) - CryptNCCHSection((u8*)exhdr,0x400,0,ncch_ctx,key,ncch_exhdr); + CryptNCCHSection((u8*)exhdr,sizeof(extended_hdr),0,ncch_ctx,key,ncch_exhdr); exefs_hdr *exefsHdr = malloc(sizeof(exefs_hdr)); memcpy(exefsHdr,ncch+ncch_ctx->exefsOffset,sizeof(exefs_hdr)); @@ -346,7 +346,7 @@ int GetMetaRegion(cia_settings *ciaset, u8 *ncch, ncch_struct *ncch_ctx, u8 *key for(int i = 0; i < MAX_EXEFS_SECTIONS; i++){ if(strncmp(exefsHdr->fileHdr[i].name,"icon",8) == 0){ icon_size = u8_to_u32(exefsHdr->fileHdr[i].size,LE); - icon_offset = u8_to_u32(exefsHdr->fileHdr[i].offset,LE) + 0x200; + icon_offset = u8_to_u32(exefsHdr->fileHdr[i].offset,LE) + sizeof(exefs_hdr); } } @@ -392,7 +392,7 @@ int GetContentFilePtrs(cia_settings *ciaset, user_settings *usrset) ciaset->content.fileSize[j] = GetFileSize_u64(usrset->common.contentPath[i]); ciaset->content.filePtrs[j] = fopen(usrset->common.contentPath[i],"rb"); - if(usrset->cia.contentId[i] == 0x100000000) + if(usrset->cia.contentId[i] > MAX_U32) ciaset->content.id[j] = u32GetRand(); else ciaset->content.id[j] = (u32)usrset->cia.contentId[i]; @@ -409,7 +409,7 @@ int GetContentFilePtrs(cia_settings *ciaset, user_settings *usrset) return FAILED_TO_OPEN_FILE; } - ciaset->content.size[j] = align(calcSize,0x10); + ciaset->content.size[j] = align(calcSize,CIA_CONTENT_ALIGN); ciaset->content.offset[j] = ciaset->content.totalSize; ciaset->content.totalSize += ciaset->content.size[j]; diff --git a/makerom/cia.h b/makerom/cia.h index 6907deaf..b810049d 100644 --- a/makerom/cia.h +++ b/makerom/cia.h @@ -1,6 +1,7 @@ #pragma once static const int CIA_ALIGN_SIZE = 0x40; +static const int CIA_CONTENT_ALIGN = 0x10; // Enums typedef enum diff --git a/makerom/types.h b/makerom/types.h index 8c4123d9..c12d2427 100644 --- a/makerom/types.h +++ b/makerom/types.h @@ -28,6 +28,14 @@ typedef enum GB = 1073741824 } file_unit_size; +typedef enum +{ + MAX_U8 = 0xff, + MAX_U16 = 0xffff, + MAX_U32 = 0xffffffff, + MAX_U64 = 0xffffffffffffffff, +} data_type_max; + typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 6bee1306..a5ebd120 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -76,9 +76,12 @@ int ParseArgs(int argc, char *argv[], user_settings *usr_settings) if(!usr_settings->common.outFileName){ char *source_path = NULL; - if(usr_settings->ncch.buildNcch0) source_path = usr_settings->common.rsfPath; - else if(usr_settings->common.workingFileType == infile_ncsd || usr_settings->common.workingFileType == infile_srl) source_path = usr_settings->common.workingFilePath; - else source_path = usr_settings->common.contentPath[0]; + if(usr_settings->ncch.buildNcch0) + source_path = usr_settings->common.rsfPath; + else if(usr_settings->common.workingFileType == infile_ncsd || usr_settings->common.workingFileType == infile_srl) + source_path = usr_settings->common.workingFilePath; + else + source_path = usr_settings->common.contentPath[0]; u16 outfile_len = strlen(source_path) + 3; usr_settings->common.outFileName = calloc(outfile_len,sizeof(char)); if(!usr_settings->common.outFileName){ @@ -117,11 +120,11 @@ void SetDefaults(user_settings *set) // CIA Info set->cia.useDataTitleVer = false; - set->cia.titleVersion[0] = 0xffff; // invalid for detection + set->cia.titleVersion[VER_MAJOR] = MAX_U16 // invalid so changes can be detected set->cia.randomTitleKey = false; - set->common.keys.aes.currentCommonKey = 0x100; // invalid for detection + set->common.keys.aes.currentCommonKey = MAX_U8 + 1; // invalid so changes can be detected for(int i = 0; i < CIA_MAX_CONTENT; i++){ - set->cia.contentId[i] = 0x100000000; + set->cia.contentId[i] = MAX_U32 + 1; // invalid so changes can be detected } } @@ -276,7 +279,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) else if(strcasecmp(app_type,"ECApp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_EC_APP; else if(strcasecmp(app_type,"Demo") == 0) set->common.keys.accessDescSign.presetType = desc_preset_DEMO; else if(strcasecmp(app_type,"DlpChild") == 0 || strcasecmp(app_type,"Dlp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_DLP; - else if(strcasecmp(app_type,"FIRM") == 0) set->common.keys.accessDescSign.presetType = desc_preset_FIRM; + //else if(strcasecmp(app_type,"FIRM") == 0) set->common.keys.accessDescSign.presetType = desc_preset_FIRM; else{ fprintf(stderr,"[SETTING ERROR] Accessdesc AppType preset '%s' not valid, please manually configure RSF\n",app_type); return USR_BAD_ARG; @@ -404,12 +407,12 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->cia.useNormTitleVer = true; - u32 tmp = strtoul(argv[i+1],NULL,10); - if(tmp > 63){ - fprintf(stderr,"[SETTING ERROR] Major version: '%d' is too large, max: '63'\n",tmp); + u32 ver = strtoul(argv[i+1],NULL,10); + if(ver > VER_MAJOR_MAX){ + fprintf(stderr,"[SETTING ERROR] Major version: '%d' is too large, max: '%d'\n",ver,VER_MAJOR_MAX); return USR_BAD_ARG; } - set->cia.titleVersion[0] = tmp; + set->cia.titleVersion[VER_MAJOR] = ver; return 2; } else if(strcmp(argv[i],"-minor") == 0){ @@ -418,12 +421,12 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->cia.useNormTitleVer = true; - u32 tmp = strtoul(argv[i+1],NULL,10); - if(tmp > 63){ - fprintf(stderr,"[SETTING ERROR] Minor version: '%d' is too large, max: '63'\n",tmp); + u32 ver = strtoul(argv[i+1],NULL,10); + if(ver > VER_MINOR_MAX){ + fprintf(stderr,"[SETTING ERROR] Minor version: '%d' is too large, max: '%d'\n",ver,VER_MINOR_MAX); return USR_BAD_ARG; } - set->cia.titleVersion[1] = tmp; + set->cia.titleVersion[VER_MINOR] = ver; return 2; } else if(strcmp(argv[i],"-micro") == 0){ @@ -431,12 +434,12 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) PrintArgReqParam("-micro",1); return USR_ARG_REQ_PARAM; } - u32 tmp = strtoul(argv[i+1],NULL,10); - if(tmp > 15){ - fprintf(stderr,"[SETTING ERROR] Micro version: '%d' is too large, max: '15'\n",tmp); + u32 ver = strtoul(argv[i+1],NULL,10); + if(ver > VER_MICRO_MAX){ + fprintf(stderr,"[SETTING ERROR] Micro version: '%d' is too large, max: '%d'\n",ver,VER_MICRO_MAX); return USR_BAD_ARG; } - set->cia.titleVersion[2] = tmp; + set->cia.titleVersion[VER_MICRO] = ver; return 2; } else if(strcmp(argv[i],"-dver") == 0){ @@ -445,13 +448,13 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->cia.useDataTitleVer = true; - u32 tmp = strtoul(argv[i+1],NULL,10); - if(tmp > 4095){ - fprintf(stderr,"[SETTING ERROR] Data version: '%d' is too large, max: '4095'\n",tmp); + u32 ver = strtoul(argv[i+1],NULL,10); + if(ver > VER_DVER_MAX){ + fprintf(stderr,"[SETTING ERROR] Data version: '%d' is too large, max: '%d'\n",ver,VER_DVER_MAX); return USR_BAD_ARG; } - set->cia.titleVersion[0] = (tmp >> 6) & 63; - set->cia.titleVersion[1] = tmp & 63; + set->cia.titleVersion[VER_MAJOR] = (ver >> 6) & VER_MAJOR_MAX; + set->cia.titleVersion[VER_MINOR] = ver & VER_MAJOR_MAX; return 2; } else if(strcmp(argv[i],"-rand") == 0){ diff --git a/makerom/user_settings.h b/makerom/user_settings.h index 54985f37..19342db5 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -1,8 +1,25 @@ #pragma once -#define CCI_MAX_CONTENT 8 -#define CIA_MAX_CONTENT 65536 +typedef enum +{ + CCI_MAX_CONTENT = 8, + CIA_MAX_CONTENT = MAX_U16, +} content_limits; +typedef enum +{ + VER_MAJOR, + VER_MINOR, + VER_MICRO +} title_ver_index; + +typedef enum +{ + VER_MAJOR_MAX = 63, + VER_MINOR_MAX = 63, + VER_MICRO_MAX = 15, + VER_DVER_MAX = 4095, +} title_ver_max; typedef enum { From 1b11fb881a12c3fa1f4d7dfc612b87645b7e3066 Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 15 Jul 2014 19:31:58 +1000 Subject: [PATCH 014/317] misc --- makerom/user_settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index a5ebd120..435d0057 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -120,7 +120,7 @@ void SetDefaults(user_settings *set) // CIA Info set->cia.useDataTitleVer = false; - set->cia.titleVersion[VER_MAJOR] = MAX_U16 // invalid so changes can be detected + set->cia.titleVersion[VER_MAJOR] = MAX_U16; // invalid so changes can be detected set->cia.randomTitleKey = false; set->common.keys.aes.currentCommonKey = MAX_U8 + 1; // invalid so changes can be detected for(int i = 0; i < CIA_MAX_CONTENT; i++){ From a021c82330c714128b075a561b7bebc2135ff497 Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 15 Jul 2014 19:34:14 +1000 Subject: [PATCH 015/317] makefile misc removed windows makefile functions, proper mingw setups include "rm" and the likes. --- makerom/Makefile | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/makerom/Makefile b/makerom/Makefile index 79e3eadb..73ea76fc 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -32,9 +32,4 @@ build: $(OBJS) $(POLAR_OBJS) $(YAML_OBJS) g++ -o $(OUTPUT) $(LIBS) $(OBJS) $(POLAR_OBJS) $(YAML_OBJS) -m64 clean: - rm -rf $(OUTPUT) $(OBJS) $(POLAR_OBJS) $(YAML_OBJS) *.cci *.cia *.cxi *.cfa - -# Windows compatibility -rebuildwin: cleanwin build -cleanwin: - del /Q objs $(OUTPUT).exe *.o polarssl\*.o libyaml\*.o *.cci *.cia *.cxi *.cfa + rm -rf $(OUTPUT) $(OBJS) $(POLAR_OBJS) $(YAML_OBJS) *.cci *.cia *.cxi *.cfa \ No newline at end of file From fc2ed91ad16b0d7da17ec8b2f0cc9efb000f8c16 Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 15 Jul 2014 20:56:33 +1000 Subject: [PATCH 016/317] makerom: cxi or cfa now detected no need to specify cxi/cfa, it is now implied by the CLI args. "-elf", "-code" or "-exheader" imply cxi, --- makerom/Makefile | 2 +- makerom/user_settings.c | 84 +++++++++++++++++++++-------------------- makerom/user_settings.h | 3 +- 3 files changed, 46 insertions(+), 43 deletions(-) diff --git a/makerom/Makefile b/makerom/Makefile index 73ea76fc..36c952e0 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -21,7 +21,7 @@ CC = gcc # MAKEROM Build Settings MAKEROM_BUILD_FLAGS = #-DDEBUG VER_MAJOR = 0 -VER_MINOR = 9 +VER_MINOR = 10 OUTPUT = makerom main: build diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 435d0057..b1af1e8e 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -101,12 +101,12 @@ void SetDefaults(user_settings *set) set->common.keys.accessDescSign.presetType = desc_preset_NONE; // Build NCCH Info - set->ncch.buildNcch0 = true; + set->ncch.buildNcch0 = false; set->ncch.includeExefsLogo = false; - set->common.outFormat = CXI; + set->common.outFormat = NCCH; set->ncch.ncchType = format_not_set; - // Yaml Settings + // RSF Settings set->common.rsfSet.Option.EnableCompress = true; set->common.rsfSet.Option.EnableCrypt = true; set->common.rsfSet.Option.UseOnSD = false; @@ -131,12 +131,8 @@ void SetDefaults(user_settings *set) int SetArgument(int argc, int i, char *argv[], user_settings *set) { u16 ParamNum = 0; - for(int j = i+1; j < argc; j++) - { - if(argv[j][0] == '-') - break; + for(int j = i+1; j < argc && argv[j][0] != '-'; j++) ParamNum++; - } // Global Settings if(strcmp(argv[i],"-rsf") == 0){ @@ -152,10 +148,12 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) PrintArgReqParam("-f",1); return USR_ARG_REQ_PARAM; } - if(strcasecmp(argv[i+1],"cxi") == 0 || strcasecmp(argv[i+1],"exec") == 0 ) set->common.outFormat = CXI; - else if(strcasecmp(argv[i+1],"cfa") == 0 || strcasecmp(argv[i+1],"data") == 0 ) set->common.outFormat = CFA; - else if(strcasecmp(argv[i+1],"cci") == 0 || strcasecmp(argv[i+1],"card") == 0 ) set->common.outFormat = CCI; - else if(strcasecmp(argv[i+1],"cia") == 0) set->common.outFormat = CIA; + if(strcasecmp(argv[i+1],"ncch") == 0) + set->common.outFormat = NCCH; + else if(strcasecmp(argv[i+1],"cci") == 0) + set->common.outFormat = CCI; + else if(strcasecmp(argv[i+1],"cia") == 0) + set->common.outFormat = CIA; else { fprintf(stderr,"[SETTING ERROR] Invalid output format '%s'\n",argv[i+1]); return USR_BAD_ARG; @@ -228,6 +226,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->ncch.elfPath = argv[i+1]; + set->ncch.ncchType |= CXI; return 2; } @@ -237,6 +236,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->ncch.iconPath = argv[i+1]; + set->ncch.ncchType |= CFA; return 2; } else if(strcmp(argv[i],"-banner") == 0){ @@ -245,6 +245,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->ncch.bannerPath = argv[i+1]; + set->ncch.ncchType |= CFA; return 2; } else if(strcmp(argv[i],"-logo") == 0){ @@ -253,6 +254,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->ncch.logoPath = argv[i+1]; + set->ncch.ncchType |= CFA; return 2; } else if(strcmp(argv[i],"-desc") == 0){ @@ -313,7 +315,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) default: break; } - + set->ncch.ncchType |= CXI; return 2; } else if(strcmp(argv[i],"-exefslogo") == 0){ @@ -322,14 +324,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_BAD_ARG; } set->ncch.includeExefsLogo = true; - return 1; - } - else if(strcmp(argv[i],"-data") == 0){ - if(ParamNum){ - PrintNoNeedParam("-data"); - return USR_ARG_REQ_PARAM; - } - set->ncch.ncchType = CFA; + set->ncch.ncchType |= CFA; return 1; } @@ -340,6 +335,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->ncch.codePath = argv[i+1]; + set->ncch.ncchType |= CXI; return 2; } else if(strcmp(argv[i],"-exheader") == 0){ @@ -348,6 +344,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->ncch.exheaderPath = argv[i+1]; + set->ncch.ncchType |= CXI; return 2; } else if(strcmp(argv[i],"-plain-region") == 0){ @@ -356,6 +353,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->ncch.plainRegionPath = argv[i+1]; + set->ncch.ncchType |= CXI; return 2; } else if(strcmp(argv[i],"-romfs") == 0){ @@ -364,6 +362,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->ncch.romfsPath = argv[i+1]; + set->ncch.ncchType |= CFA; return 2; } // Cci Options @@ -618,6 +617,23 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) int CheckArgumentCombination(user_settings *set) { + if(set->ncch.ncchType & (CXI|CFA)){ + set->ncch.buildNcch0 = true; + if(set->ncch.ncchType & CXI) + set->ncch.ncchType = CXI; + else + set->ncch.ncchType = CFA; + } + + if(set->common.outFormat == NCCH){ + if(set->ncch.ncchType) + set->common.outFormat = set->ncch.ncchType; + else{ + set->ncch.ncchType = CXI; + set->common.outFormat = CXI; + } + } + for(int i = 0; i < CIA_MAX_CONTENT; i++){ if( i > CCI_MAX_CONTENT-1 && set->common.contentPath[i] && set->common.outFormat == CCI){ fprintf(stderr,"[SETTING ERROR] Content indexes > %d are invalid for CCI\n",CCI_MAX_CONTENT-1); @@ -628,12 +644,9 @@ int CheckArgumentCombination(user_settings *set) return USR_BAD_ARG; } } - if((set->common.outFormat == CXI || set->common.outFormat == CFA) && set->ncch.ncchType != format_not_set){ - fprintf(stderr,"[SETTING ERROR] Arguments \"-f cxi|cfa\" and \"-data\" cannot be used together\n"); - return USR_BAD_ARG; - } - if(set->ncch.ncchType != format_not_set && !set->ncch.buildNcch0){ - fprintf(stderr,"[SETTING ERROR] Arguments \"-content %s:0\" and \"-data\" cannot be used together\n",set->common.contentPath[0]); + + if(set->common.contentPath[0] && set->ncch.buildNcch0){ + fprintf(stderr,"[SETTING ERROR] You cannot both import and build content 0\n"); return USR_BAD_ARG; } @@ -652,12 +665,6 @@ int CheckArgumentCombination(user_settings *set) return USR_BAD_ARG; } - // Setting set->build_ncch_type if it isn't already set - if(set->ncch.buildNcch0 && set->ncch.ncchType == format_not_set){ - if(set->common.outFormat == CCI || set->common.outFormat == CIA) set->ncch.ncchType = CXI; - else set->ncch.ncchType = set->common.outFormat; - } - bool buildCXI = set->ncch.ncchType == CXI; bool buildCFA = set->ncch.ncchType == CFA; // Detecting Required Arguments @@ -944,7 +951,7 @@ void PrintArgInvalid(char *arg) void PrintArgReqParam(char *arg, u32 paramNum) { if(paramNum == 1) - fprintf(stderr,"[SETTING ERROR] \"%s\" requires a parameter\n",arg); + fprintf(stderr,"[SETTING ERROR] \"%s\" takes one parameter\n",arg); else fprintf(stderr,"[SETTING ERROR] \"%s\" requires %d parameters\n",arg,paramNum); } @@ -963,14 +970,10 @@ void DisplayHelp(char *app_name) printf("GLOBAL OPTIONS:\n"); printf(" -help Display this text\n"); printf(" -rsf Rom Specification File (*.rsf)\n"); - printf(" -f Output Format, defaults to 'cxi'\n"); - //printf(" 'cxi' CTR Executable Image\n"); - //printf(" 'cfa' CTR File Archive\n"); - //printf(" 'cci' CTR Card Image\n"); - //printf(" 'cia' CTR Importable Archive\n"); + printf(" -f Output Format, defaults to 'ncch'\n"); printf(" -o Output File\n"); //printf(" -v Verbose\n"); - printf(" -DNAME=VALUE Substitute values in Spec files\n"); + printf(" -DNAME=VALUE Substitute values in Spec file\n"); printf("KEY OPTIONS:\n"); //printf(" -target Target for crypto, defaults to 't'\n"); printf(" -target Target for crypto, defaults to 't'\n"); @@ -994,7 +997,6 @@ void DisplayHelp(char *app_name) //printf(" 'Dlp' NAND DLP Child Application\n"); //printf(" 'FIRM' FIRM CXI\n"); printf(" -exefslogo Include Logo in ExeFs (Required for usage on <5.X Systems)\n"); - printf(" -data Specify if building a Data Archive when \"-f cia\"\n"); printf("NCCH REBUILD OPTIONS:\n"); printf(" -code Specify ExeFs code File\n"); printf(" -exheader ExHeader Template File\n"); diff --git a/makerom/user_settings.h b/makerom/user_settings.h index 19342db5..ab9caa96 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -45,7 +45,8 @@ typedef enum CXI, CFA, CCI, - CIA + CIA, + NCCH } output_format; typedef struct From 83ac682594b494d22c0d7c0aac5e8fe99f9a95ae Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 15 Jul 2014 21:19:58 +1000 Subject: [PATCH 017/317] moved rsf stuff from user_set to rsf_set --- makerom/lib.h | 3 +- makerom/rsf_settings.c | 187 +++++++++++++++++++++++++++++++++++++++- makerom/rsf_settings.h | 5 +- makerom/user_settings.c | 187 ---------------------------------------- makerom/user_settings.h | 36 ++++---- makerom/yaml_parser.h | 2 + 6 files changed, 208 insertions(+), 212 deletions(-) diff --git a/makerom/lib.h b/makerom/lib.h index 15a0284a..1799a9f3 100644 --- a/makerom/lib.h +++ b/makerom/lib.h @@ -30,7 +30,8 @@ #include "keyset.h" #include "user_settings.h" -#include "libyaml/yaml.h" #include "yaml_parser.h" +#include "rsf_settings.h" + diff --git a/makerom/rsf_settings.c b/makerom/rsf_settings.c index 7d876ee2..84c3edc6 100644 --- a/makerom/rsf_settings.c +++ b/makerom/rsf_settings.c @@ -1,5 +1,4 @@ #include "lib.h" -#include "rsf_settings.h" void EvaluateRSF(rsf_settings *rsf, ctr_yaml_context *ctx) { @@ -372,4 +371,190 @@ void GET_CommonHeaderKey(ctr_yaml_context *ctx, rsf_settings *rsf) GetEvent(ctx); } FinishEvent(ctx); +} + +void free_RsfSettings(rsf_settings *set) +{ + //Option + free(set->Option.PageSize); + /* + for(u32 i = 0; i < set->Option.AppendSystemCallNum; i++){ + free(set->Option.AppendSystemCall[i]); + } + free(set->Option.AppendSystemCall); + */ + + //AccessControlInfo + //free(set->AccessControlInfo.ProgramId); + free(set->AccessControlInfo.IdealProcessor); + free(set->AccessControlInfo.Priority); + free(set->AccessControlInfo.MemoryType); + free(set->AccessControlInfo.SystemMode); + free(set->AccessControlInfo.CoreVersion); + free(set->AccessControlInfo.HandleTableSize); + free(set->AccessControlInfo.SystemSaveDataId1); + free(set->AccessControlInfo.SystemSaveDataId2); + free(set->AccessControlInfo.OtherUserSaveDataId1); + free(set->AccessControlInfo.OtherUserSaveDataId2); + free(set->AccessControlInfo.OtherUserSaveDataId3); + free(set->AccessControlInfo.ExtSaveDataId); + free(set->AccessControlInfo.SystemMode); + free(set->AccessControlInfo.AffinityMask); + free(set->AccessControlInfo.DescVersion); + //free(set->AccessControlInfo.CryptoKey); + free(set->AccessControlInfo.ResourceLimitCategory); + free(set->AccessControlInfo.ReleaseKernelMajor); + free(set->AccessControlInfo.ReleaseKernelMinor); + free(set->AccessControlInfo.MaxCpu); + + for(u32 i = 0; i < set->AccessControlInfo.MemoryMappingNum; i++){ + free(set->AccessControlInfo.MemoryMapping[i]); + } + free(set->AccessControlInfo.MemoryMapping); + + for(u32 i = 0; i < set->AccessControlInfo.IORegisterMappingNum; i++){ + free(set->AccessControlInfo.IORegisterMapping[i]); + } + free(set->AccessControlInfo.IORegisterMapping); + + for(u32 i = 0; i < set->AccessControlInfo.FileSystemAccessNum; i++){ + free(set->AccessControlInfo.FileSystemAccess[i]); + } + free(set->AccessControlInfo.FileSystemAccess); + + for(u32 i = 0; i < set->AccessControlInfo.IoAccessControlNum; i++){ + free(set->AccessControlInfo.IoAccessControl[i]); + } + free(set->AccessControlInfo.IoAccessControl); + + for(u32 i = 0; i < set->AccessControlInfo.InterruptNumbersNum; i++){ + free(set->AccessControlInfo.InterruptNumbers[i]); + } + free(set->AccessControlInfo.InterruptNumbers); + + for(u32 i = 0; i < set->AccessControlInfo.SystemCallAccessNum; i++){ + free(set->AccessControlInfo.SystemCallAccess[i]); + } + free(set->AccessControlInfo.SystemCallAccess); + + for(u32 i = 0; i < set->AccessControlInfo.ServiceAccessControlNum; i++){ + free(set->AccessControlInfo.ServiceAccessControl[i]); + } + free(set->AccessControlInfo.ServiceAccessControl); + + for(u32 i = 0; i < set->AccessControlInfo.StorageIdNum; i++){ + free(set->AccessControlInfo.StorageId[i]); + } + free(set->AccessControlInfo.StorageId); + + for(u32 i = 0; i < set->AccessControlInfo.AccessibleSaveDataIdsNum; i++){ + free(set->AccessControlInfo.AccessibleSaveDataIds[i]); + } + free(set->AccessControlInfo.AccessibleSaveDataIds); + + //SystemControlInfo + free(set->SystemControlInfo.AppType); + free(set->SystemControlInfo.StackSize); + free(set->SystemControlInfo.RemasterVersion); + free(set->SystemControlInfo.SaveDataSize); + free(set->SystemControlInfo.JumpId); + + for(u32 i = 0; i < set->SystemControlInfo.DependencyNum; i++){ + free(set->SystemControlInfo.Dependency[i]); + } + free(set->SystemControlInfo.Dependency); + + //BasicInfo + free(set->BasicInfo.Title); + free(set->BasicInfo.CompanyCode); + free(set->BasicInfo.ProductCode); + free(set->BasicInfo.ContentType); + free(set->BasicInfo.Logo); + //free(set->BasicInfo.BackupMemoryType); + //free(set->BasicInfo.InitialCode); + + //Rom + free(set->Rom.HostRoot); + //free(set->Rom.Padding); + + for(u32 i = 0; i < set->Rom.DefaultRejectNum; i++){ + free(set->Rom.DefaultReject[i]); + } + free(set->Rom.DefaultReject); + + for(u32 i = 0; i < set->Rom.RejectNum; i++){ + free(set->Rom.Reject[i]); + } + free(set->Rom.Reject); + + for(u32 i = 0; i < set->Rom.IncludeNum; i++){ + free(set->Rom.Include[i]); + } + free(set->Rom.Include); + + for(u32 i = 0; i < set->Rom.FileNum; i++){ + free(set->Rom.File[i]); + } + free(set->Rom.File); + + //ExeFs + for(u32 i = 0; i < set->ExeFs.TextNum; i++){ + free(set->ExeFs.Text[i]); + } + free(set->ExeFs.Text); + + for(u32 i = 0; i < set->ExeFs.ReadOnlyNum; i++){ + free(set->ExeFs.ReadOnly[i]); + } + free(set->ExeFs.ReadOnly); + + for(u32 i = 0; i < set->ExeFs.ReadWriteNum; i++){ + free(set->ExeFs.ReadWrite[i]); + } + free(set->ExeFs.ReadWrite); + + //PlainRegion + for(u32 i = 0; i < set->PlainRegionNum; i++){ + free(set->PlainRegion[i]); + } + free(set->PlainRegion); + + //TitleInfo + //free(set->TitleInfo.Platform); + free(set->TitleInfo.Category); + free(set->TitleInfo.UniqueId); + free(set->TitleInfo.Version); + free(set->TitleInfo.ContentsIndex); + free(set->TitleInfo.Variation); + //free(set->TitleInfo.Use); + free(set->TitleInfo.ChildIndex); + free(set->TitleInfo.DemoIndex); + free(set->TitleInfo.TargetCategory); + + for(u32 i = 0; i < set->TitleInfo.CategoryFlagsNum; i++){ + free(set->TitleInfo.CategoryFlags[i]); + } + free(set->TitleInfo.CategoryFlags); + + //CardInfo + free(set->CardInfo.WritableAddress); + free(set->CardInfo.CardType); + free(set->CardInfo.CryptoType); + free(set->CardInfo.CardDevice); + free(set->CardInfo.MediaType); + free(set->CardInfo.MediaSize); + free(set->CardInfo.BackupWriteWaitTime); + free(set->CardInfo.SaveCrypto); + + //CommonHeaderKey + free(set->CommonHeaderKey.D); + free(set->CommonHeaderKey.P); + free(set->CommonHeaderKey.Q); + free(set->CommonHeaderKey.DP); + free(set->CommonHeaderKey.DQ); + free(set->CommonHeaderKey.InverseQ); + free(set->CommonHeaderKey.Modulus); + free(set->CommonHeaderKey.Exponent); + free(set->CommonHeaderKey.AccCtlDescSign); + free(set->CommonHeaderKey.AccCtlDescBin); } \ No newline at end of file diff --git a/makerom/rsf_settings.h b/makerom/rsf_settings.h index 9ef4015b..2b355fcd 100644 --- a/makerom/rsf_settings.h +++ b/makerom/rsf_settings.h @@ -1,6 +1,5 @@ #pragma once -int MergeSpecData(rsf_settings *out, rsf_settings *desc, rsf_settings *rsf); void EvaluateRSF(rsf_settings *rsf, ctr_yaml_context *ctx); void GET_Option(ctr_yaml_context *ctx, rsf_settings *rsf); @@ -12,4 +11,6 @@ void GET_ExeFs(ctr_yaml_context *ctx, rsf_settings *rsf); void GET_PlainRegion(ctr_yaml_context *ctx, rsf_settings *rsf); void GET_TitleInfo(ctr_yaml_context *ctx, rsf_settings *rsf); void GET_CardInfo(ctr_yaml_context *ctx, rsf_settings *rsf); -void GET_CommonHeaderKey(ctr_yaml_context *ctx, rsf_settings *rsf); \ No newline at end of file +void GET_CommonHeaderKey(ctr_yaml_context *ctx, rsf_settings *rsf); + +void free_RsfSettings(rsf_settings *set); \ No newline at end of file diff --git a/makerom/user_settings.c b/makerom/user_settings.c index b1af1e8e..a2e8ffdd 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -715,193 +715,6 @@ void init_UserSettings(user_settings *usr_settings) memset(usr_settings,0,sizeof(user_settings)); } - -void free_RsfSettings(rsf_settings *set) -{ - //Option - free(set->Option.PageSize); - /* - for(u32 i = 0; i < set->Option.AppendSystemCallNum; i++){ - free(set->Option.AppendSystemCall[i]); - } - free(set->Option.AppendSystemCall); - */ - - //AccessControlInfo - //free(set->AccessControlInfo.ProgramId); - free(set->AccessControlInfo.IdealProcessor); - free(set->AccessControlInfo.Priority); - free(set->AccessControlInfo.MemoryType); - free(set->AccessControlInfo.SystemMode); - free(set->AccessControlInfo.CoreVersion); - free(set->AccessControlInfo.HandleTableSize); - free(set->AccessControlInfo.SystemSaveDataId1); - free(set->AccessControlInfo.SystemSaveDataId2); - free(set->AccessControlInfo.OtherUserSaveDataId1); - free(set->AccessControlInfo.OtherUserSaveDataId2); - free(set->AccessControlInfo.OtherUserSaveDataId3); - free(set->AccessControlInfo.ExtSaveDataId); - free(set->AccessControlInfo.SystemMode); - free(set->AccessControlInfo.AffinityMask); - free(set->AccessControlInfo.DescVersion); - //free(set->AccessControlInfo.CryptoKey); - free(set->AccessControlInfo.ResourceLimitCategory); - free(set->AccessControlInfo.ReleaseKernelMajor); - free(set->AccessControlInfo.ReleaseKernelMinor); - free(set->AccessControlInfo.MaxCpu); - - for(u32 i = 0; i < set->AccessControlInfo.MemoryMappingNum; i++){ - free(set->AccessControlInfo.MemoryMapping[i]); - } - free(set->AccessControlInfo.MemoryMapping); - - for(u32 i = 0; i < set->AccessControlInfo.IORegisterMappingNum; i++){ - free(set->AccessControlInfo.IORegisterMapping[i]); - } - free(set->AccessControlInfo.IORegisterMapping); - - for(u32 i = 0; i < set->AccessControlInfo.FileSystemAccessNum; i++){ - free(set->AccessControlInfo.FileSystemAccess[i]); - } - free(set->AccessControlInfo.FileSystemAccess); - - for(u32 i = 0; i < set->AccessControlInfo.IoAccessControlNum; i++){ - free(set->AccessControlInfo.IoAccessControl[i]); - } - free(set->AccessControlInfo.IoAccessControl); - - for(u32 i = 0; i < set->AccessControlInfo.InterruptNumbersNum; i++){ - free(set->AccessControlInfo.InterruptNumbers[i]); - } - free(set->AccessControlInfo.InterruptNumbers); - - for(u32 i = 0; i < set->AccessControlInfo.SystemCallAccessNum; i++){ - free(set->AccessControlInfo.SystemCallAccess[i]); - } - free(set->AccessControlInfo.SystemCallAccess); - - for(u32 i = 0; i < set->AccessControlInfo.ServiceAccessControlNum; i++){ - free(set->AccessControlInfo.ServiceAccessControl[i]); - } - free(set->AccessControlInfo.ServiceAccessControl); - - for(u32 i = 0; i < set->AccessControlInfo.StorageIdNum; i++){ - free(set->AccessControlInfo.StorageId[i]); - } - free(set->AccessControlInfo.StorageId); - - for(u32 i = 0; i < set->AccessControlInfo.AccessibleSaveDataIdsNum; i++){ - free(set->AccessControlInfo.AccessibleSaveDataIds[i]); - } - free(set->AccessControlInfo.AccessibleSaveDataIds); - - //SystemControlInfo - free(set->SystemControlInfo.AppType); - free(set->SystemControlInfo.StackSize); - free(set->SystemControlInfo.RemasterVersion); - free(set->SystemControlInfo.SaveDataSize); - free(set->SystemControlInfo.JumpId); - - for(u32 i = 0; i < set->SystemControlInfo.DependencyNum; i++){ - free(set->SystemControlInfo.Dependency[i]); - } - free(set->SystemControlInfo.Dependency); - - //BasicInfo - free(set->BasicInfo.Title); - free(set->BasicInfo.CompanyCode); - free(set->BasicInfo.ProductCode); - free(set->BasicInfo.ContentType); - free(set->BasicInfo.Logo); - //free(set->BasicInfo.BackupMemoryType); - //free(set->BasicInfo.InitialCode); - - //Rom - free(set->Rom.HostRoot); - //free(set->Rom.Padding); - - for(u32 i = 0; i < set->Rom.DefaultRejectNum; i++){ - free(set->Rom.DefaultReject[i]); - } - free(set->Rom.DefaultReject); - - for(u32 i = 0; i < set->Rom.RejectNum; i++){ - free(set->Rom.Reject[i]); - } - free(set->Rom.Reject); - - for(u32 i = 0; i < set->Rom.IncludeNum; i++){ - free(set->Rom.Include[i]); - } - free(set->Rom.Include); - - for(u32 i = 0; i < set->Rom.FileNum; i++){ - free(set->Rom.File[i]); - } - free(set->Rom.File); - - //ExeFs - for(u32 i = 0; i < set->ExeFs.TextNum; i++){ - free(set->ExeFs.Text[i]); - } - free(set->ExeFs.Text); - - for(u32 i = 0; i < set->ExeFs.ReadOnlyNum; i++){ - free(set->ExeFs.ReadOnly[i]); - } - free(set->ExeFs.ReadOnly); - - for(u32 i = 0; i < set->ExeFs.ReadWriteNum; i++){ - free(set->ExeFs.ReadWrite[i]); - } - free(set->ExeFs.ReadWrite); - - //PlainRegion - for(u32 i = 0; i < set->PlainRegionNum; i++){ - free(set->PlainRegion[i]); - } - free(set->PlainRegion); - - //TitleInfo - //free(set->TitleInfo.Platform); - free(set->TitleInfo.Category); - free(set->TitleInfo.UniqueId); - free(set->TitleInfo.Version); - free(set->TitleInfo.ContentsIndex); - free(set->TitleInfo.Variation); - //free(set->TitleInfo.Use); - free(set->TitleInfo.ChildIndex); - free(set->TitleInfo.DemoIndex); - free(set->TitleInfo.TargetCategory); - - for(u32 i = 0; i < set->TitleInfo.CategoryFlagsNum; i++){ - free(set->TitleInfo.CategoryFlags[i]); - } - free(set->TitleInfo.CategoryFlags); - - //CardInfo - free(set->CardInfo.WritableAddress); - free(set->CardInfo.CardType); - free(set->CardInfo.CryptoType); - free(set->CardInfo.CardDevice); - free(set->CardInfo.MediaType); - free(set->CardInfo.MediaSize); - free(set->CardInfo.BackupWriteWaitTime); - free(set->CardInfo.SaveCrypto); - - //CommonHeaderKey - free(set->CommonHeaderKey.D); - free(set->CommonHeaderKey.P); - free(set->CommonHeaderKey.Q); - free(set->CommonHeaderKey.DP); - free(set->CommonHeaderKey.DQ); - free(set->CommonHeaderKey.InverseQ); - free(set->CommonHeaderKey.Modulus); - free(set->CommonHeaderKey.Exponent); - free(set->CommonHeaderKey.AccCtlDescSign); - free(set->CommonHeaderKey.AccCtlDescBin); -} - void free_UserSettings(user_settings *usr_settings) { // Free Content Paths diff --git a/makerom/user_settings.h b/makerom/user_settings.h index ab9caa96..95d83173 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -49,20 +49,6 @@ typedef enum NCCH } output_format; -typedef struct -{ - char *name; - char *value; -} dname_item; - - -typedef struct -{ - dname_item *items; - u32 m_items; - u32 u_items; -} dname_struct; - static const char output_extention[4][5] = {".cxi",".cfa",".cci",".cia"}; /* This does not follow style, so the rsf string names match the variables where they're stored */ @@ -244,6 +230,19 @@ typedef struct } CommonHeaderKey; } rsf_settings; +typedef struct +{ + char *name; + char *value; +} dname_item; + +typedef struct +{ + dname_item *items; + u32 m_items; + u32 u_items; +} dname_struct; + typedef struct { struct{ @@ -302,16 +301,11 @@ typedef struct u16 titleVersion[3]; u64 contentId[CIA_MAX_CONTENT]; // For CIA - } cia; // CIA Settings - - + } cia; // CIA Settings } user_settings; // Prototypes void init_UserSettings(user_settings *usr_settings); void free_UserSettings(user_settings *usr_settings); -int ParseArgs(int argc, char *argv[], user_settings *usr_settings); -void ReadYAMLtest(char *filepath); - -void free_RsfSettings(rsf_settings *set); +int ParseArgs(int argc, char *argv[], user_settings *usr_settings); \ No newline at end of file diff --git a/makerom/yaml_parser.h b/makerom/yaml_parser.h index 35f3dcfe..9d9db540 100644 --- a/makerom/yaml_parser.h +++ b/makerom/yaml_parser.h @@ -1,5 +1,7 @@ #pragma once +#include "libyaml/yaml.h" + typedef enum { YAML_API_ERROR = -1, From 5b08a4311bd671015960c2a266e09af4fe63340c Mon Sep 17 00:00:00 2001 From: applestash Date: Wed, 16 Jul 2014 01:19:34 +1000 Subject: [PATCH 018/317] misc now properly defaults to cfa when no cxi CLI options were applied --- makerom/user_settings.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index a2e8ffdd..89669e42 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -626,11 +626,12 @@ int CheckArgumentCombination(user_settings *set) } if(set->common.outFormat == NCCH){ + set->ncch.buildNcch0 = true; if(set->ncch.ncchType) set->common.outFormat = set->ncch.ncchType; else{ - set->ncch.ncchType = CXI; - set->common.outFormat = CXI; + set->ncch.ncchType = CFA; + set->common.outFormat = CFA; } } From 1833434a4e6a7376149856d81a901a16eb485d2e Mon Sep 17 00:00:00 2001 From: applestash Date: Fri, 18 Jul 2014 23:03:16 +1000 Subject: [PATCH 019/317] added fw8 (2.44) to the desc preset list --- makerom/user_settings.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 89669e42..c644f801 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -289,7 +289,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) char *target_firmware = (tmp2+1); - set->common.keys.accessDescSign.targetFirmware = strtoul(target_firmware,NULL,10); + set->common.keys.accessDescSign.targetFirmware = strtoul(target_firmware,NULL,0); switch(set->common.keys.accessDescSign.targetFirmware){ case 1: set->common.keys.accessDescSign.targetFirmware = 0x1B; // or 0x1C @@ -310,11 +310,15 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) set->common.keys.accessDescSign.targetFirmware = 0x25; // or 0x26 break; case 7: - set->common.keys.accessDescSign.targetFirmware = 0x27; + set->common.keys.accessDescSign.targetFirmware = 0x27; // or 0x28 + break; + case 8: + set->common.keys.accessDescSign.targetFirmware = 0x2C; break; default: break; } + set->ncch.ncchType |= CXI; return 2; } From 756fa2b84df6fb68b49119cee03ca8aec67e3c4b Mon Sep 17 00:00:00 2001 From: applestash Date: Fri, 18 Jul 2014 23:47:40 +1000 Subject: [PATCH 020/317] Fixed handling of writable region calculation --- makerom/ncsd.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/makerom/ncsd.c b/makerom/ncsd.c index f8ee18e2..bb8f3c06 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -425,7 +425,8 @@ u64 GetUnusedSize(u64 MediaSize, u8 CardType) case (u64)GB*2: return (u64)147324928; case (u64)GB*4: return (u64)294649856; case (u64)GB*8: return (u64)587202560; - default: return (u64)((MediaSize/MB)*0x11800); // Aprox + //default: return (u64)((MediaSize/MB)*0x11800); // Aprox + default: return 0; } } else if(CardType == CARD2){ @@ -435,7 +436,8 @@ u64 GetUnusedSize(u64 MediaSize, u8 CardType) case (u64)GB*2: return (u64)147324928; case (u64)GB*4: return (u64)294649856; case (u64)GB*8: return (u64)587202560; - default: return (u64)((MediaSize/MB)*0x11800); // Aprox + //default: return (u64)((MediaSize/MB)*0x11800); // Aprox + default: return 0; } } return 0; @@ -512,10 +514,10 @@ int GetNCSDFlags(cci_settings *cciset, rsf_settings *yaml) int GetWriteableAddress(cci_settings *cciset, user_settings *usrset) { - int result = GetSaveDataSizeFromString(&cciset->option.savedataSize,usrset->common.rsfSet.SystemControlInfo.SaveDataSize,"NCSD"); + int result = GetSaveDataSizeFromString(&cciset->option.savedataSize,usrset->common.rsfSet.SystemControlInfo.SaveDataSize,"CCI"); if(result) return result; - char *WriteableAddressStr = usrset->common.rsfSet.CardInfo.WritableAddress;; + char *WriteableAddressStr = usrset->common.rsfSet.CardInfo.WritableAddress; cciset->cardinfo.writableAddress = -1; if(cciset->header.flags[MediaTypeIndex] != CARD2) return 0; // Can only be set for Card2 Media @@ -525,7 +527,7 @@ int GetWriteableAddress(cci_settings *cciset, user_settings *usrset) fprintf(stderr,"[CCI ERROR] WritableAddress requires a Hexadecimal value\n"); return INVALID_YAML_OPT; } - cciset->cardinfo.writableAddress = strtoull((WriteableAddressStr+2),NULL,16); + cciset->cardinfo.writableAddress = strtoull(WriteableAddressStr,NULL,16); } if(cciset->cardinfo.writableAddress == -1){ // If not set manually or is max size if ((cciset->header.mediaSize / 2) < cciset->option.savedataSize){ // If SaveData size is greater than half the MediaSize @@ -539,10 +541,15 @@ int GetWriteableAddress(cci_settings *cciset, user_settings *usrset) return SAVE_DATA_TOO_LARGE; } if(usrset->cci.closeAlignWritableRegion) - cciset->cardinfo.writableAddress = align(cciset->cardinfo.cciTotalSize, cciset->option.mediaUnit); + cciset->cardinfo.writableAddress = align(cciset->cardinfo.cciTotalSize, cciset->option.mediaUnit); // invalid for "real" chips else{ - u64 UnusedSize = GetUnusedSize(cciset->header.mediaSize,cciset->header.flags[MediaTypeIndex]); // Need to look into this - cciset->cardinfo.writableAddress = cciset->header.mediaSize - UnusedSize - cciset->option.savedataSize; + u64 UnusedSize = GetUnusedSize(cciset->header.mediaSize,cciset->header.flags[MediaTypeIndex]); // Some value related to the physical implementation of gamecards + if(UnusedSize > 0) + cciset->cardinfo.writableAddress = cciset->header.mediaSize - UnusedSize - cciset->option.savedataSize; // Nintendo's method for calculating writable region offset + else{ + fprintf(stderr,"[CCI WARNING] Nintendo has no CARD2 writable region configurations for this media size, writable region will be aligned to last ncch partition\n"); + cciset->cardinfo.writableAddress = align(cciset->cardinfo.cciTotalSize, cciset->option.mediaUnit); // invalid for "real" chips + } } } return 0; From 291accd63d7e097709abe056adb8a09f4cf10149 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 19 Jul 2014 00:46:34 +1000 Subject: [PATCH 021/317] fixed displaying u64 values in printf --- makerom/lib.h | 1 + makerom/utils.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/makerom/lib.h b/makerom/lib.h index 1799a9f3..e0ef089e 100644 --- a/makerom/lib.h +++ b/makerom/lib.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include diff --git a/makerom/utils.c b/makerom/utils.c index 45b509d6..1321168e 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -305,7 +305,7 @@ u8* ImportFile(char *file, u64 size) u64 fsize = GetFileSize_u64(file); if(size > 0){ if(size != fsize){ - fprintf(stderr,"[!] %s has an invalid size (0x%llx)\n",file, fsize); + fprintf(stderr,"[!] %s has an invalid size (0x%"PRIx64")\n",file, fsize); return NULL; } } From c6e98ca578c416d2b5cbad114c8b5c7816332621 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 19 Jul 2014 01:25:11 +1000 Subject: [PATCH 022/317] renamed "-genupdatenote" to "-cverinfo" --- makerom/user_settings.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index c644f801..2486c4c8 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -394,9 +394,9 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) set->cci.closeAlignWritableRegion = true; return 1; } - else if(strcmp(argv[i],"-genupdatenote") == 0){ + else if(strcmp(argv[i],"-cverinfo") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-genupdatenote",1); + PrintArgReqParam("-cverinfo",1); return USR_BAD_ARG; } set->cci.cverCiaPath = argv[i+1]; @@ -656,7 +656,7 @@ int CheckArgumentCombination(user_settings *set) } if(set->common.outFormat == CIA && set->cci.cverCiaPath){ - fprintf(stderr,"[SETTING ERROR] You cannot use argument \"-genupdatenote\" when generating a CIA\n"); + fprintf(stderr,"[SETTING ERROR] You cannot use argument \"-cverinfo\" when generating a CIA\n"); return USR_BAD_ARG; } @@ -825,7 +825,7 @@ void DisplayHelp(char *app_name) printf(" -devcardcci Use SDK CardInfo Method\n"); printf(" -nomodtid Don't Modify Content TitleIDs\n"); printf(" -alignwr Align Writeable Region to the end of last NCCH\n"); - printf(" -genupdatenote Create Update Partition Notes\n"); + printf(" -cverinfo Include CVer title info\n"); printf("CIA OPTIONS:\n"); printf(" -content :: Specify content files\n"); printf(" -major Specify Major Version\n"); From 9c548197c1f4dbdf5a170a68882b26cae986a79d Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 26 Aug 2014 00:34:28 +1000 Subject: [PATCH 023/317] big update lots cleaned, added cia to cci conv, it 's called a block, separated reading from building, improved ncch keyx stuff, and basic verbose for keys, elf checking and romfs --- makerom/Makefile | 10 +- makerom/accessdesc.c | 111 +- makerom/cardinfo.c | 207 ++++ makerom/cardinfo.h | 53 + makerom/cia.c | 287 +++-- makerom/cia.h | 116 +- makerom/cia_build.h | 107 ++ makerom/cia_read.c | 61 - makerom/cia_read.h | 21 + makerom/crypto.c | 22 +- makerom/crypto.h | 2 +- makerom/ctr_utils.c | 14 + makerom/ctr_utils.h | 4 + .../dev_sigdata.h} | 0 makerom/{desc_presets.h => desc/presets.h} | 0 .../prod_sigdata.h} | 0 makerom/dir.c | 4 +- makerom/elf.c | 623 +++++----- makerom/elf.h | 21 +- makerom/exefs.c | 15 +- makerom/exefs.h | 34 - makerom/exefs_build.h | 28 + makerom/exefs_read.h | 9 + makerom/exheader.c | 8 +- makerom/exheader.h | 35 +- makerom/exheader_build.h | 23 + makerom/exheader_read.h | 13 + makerom/keyset.c | 110 +- makerom/keyset.h | 19 +- makerom/lib.h | 1 + makerom/makerom.c | 89 +- makerom/ncch.c | 935 +++++++-------- makerom/ncch.h | 157 +-- makerom/ncch_build.h | 88 ++ makerom/{logo_data.h => ncch_logo.h} | 0 makerom/ncch_read.h | 2 + makerom/ncsd.c | 1062 +++++++++-------- makerom/ncsd.h | 197 +-- makerom/ncsd_build.h | 72 ++ makerom/ncsd_read.h | 8 + makerom/{dpki.h => pki/dev.h} | 2 +- makerom/{dpki_legacy.h => pki/dev_legacy.h} | 0 makerom/{ppki.h => pki/prod.h} | 5 +- makerom/{ppki_legacy.h => pki/prod_legacy.h} | 0 makerom/{tpki.h => pki/test.h} | 0 makerom/romfs.c | 4 +- makerom/{romfs_binary.c => romfs_gen.c} | 9 +- makerom/{romfs_binary.h => romfs_gen.h} | 0 makerom/romfs_import.c | 6 +- makerom/rsf_settings.c | 2 +- makerom/tik.c | 35 +- makerom/tik.h | 15 +- makerom/tik_build.h | 16 + makerom/tik_read.h | 5 + makerom/titleid.c | 4 +- makerom/tmd.c | 99 +- makerom/tmd.h | 19 +- makerom/tmd_build.h | 6 + makerom/tmd_read.c | 22 - makerom/tmd_read.h | 19 + makerom/user_settings.c | 380 +++--- makerom/user_settings.h | 29 +- makerom/utils.c | 128 +- makerom/utils.h | 19 +- makerom/yaml_parser.c | 7 +- makerom/yaml_parser.h | 2 +- 66 files changed, 2910 insertions(+), 2491 deletions(-) create mode 100644 makerom/cardinfo.c create mode 100644 makerom/cardinfo.h create mode 100644 makerom/cia_build.h delete mode 100644 makerom/cia_read.c create mode 100644 makerom/cia_read.h create mode 100644 makerom/ctr_utils.c create mode 100644 makerom/ctr_utils.h rename makerom/{desc_dev_sigdata.h => desc/dev_sigdata.h} (100%) rename makerom/{desc_presets.h => desc/presets.h} (100%) rename makerom/{desc_prod_sigdata.h => desc/prod_sigdata.h} (100%) create mode 100644 makerom/exefs_build.h create mode 100644 makerom/exefs_read.h create mode 100644 makerom/exheader_build.h create mode 100644 makerom/exheader_read.h create mode 100644 makerom/ncch_build.h rename makerom/{logo_data.h => ncch_logo.h} (100%) create mode 100644 makerom/ncch_read.h create mode 100644 makerom/ncsd_build.h create mode 100644 makerom/ncsd_read.h rename makerom/{dpki.h => pki/dev.h} (99%) rename makerom/{dpki_legacy.h => pki/dev_legacy.h} (100%) rename makerom/{ppki.h => pki/prod.h} (99%) rename makerom/{ppki_legacy.h => pki/prod_legacy.h} (100%) rename makerom/{tpki.h => pki/test.h} (100%) rename makerom/{romfs_binary.c => romfs_gen.c} (98%) rename makerom/{romfs_binary.h => romfs_gen.h} (100%) create mode 100644 makerom/tik_build.h create mode 100644 makerom/tik_read.h create mode 100644 makerom/tmd_build.h delete mode 100644 makerom/tmd_read.c create mode 100644 makerom/tmd_read.h diff --git a/makerom/Makefile b/makerom/Makefile index 36c952e0..5293084a 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -1,8 +1,8 @@ # Makerom Sources -UTILS_OBJS = utils.o dir.o utf.o keyset.o titleid.o -CIA_OBJS = cia.o cia_read.o certs.o tik.o tmd.o tmd_read.o -NCCH_OBJS = ncch.o exheader.o accessdesc.o exefs.o elf.o romfs.o romfs_import.o romfs_binary.o -NCSD_OBJS = ncsd.o +UTILS_OBJS = utils.o ctr_utils.o dir.o utf.o keyset.o titleid.o +CIA_OBJS = cia.o certs.o tik.o tmd.o +NCCH_OBJS = ncch.o exheader.o accessdesc.o exefs.o elf.o romfs.o romfs_import.o romfs_gen.o +NCSD_OBJS = ncsd.o cardinfo.o SETTINGS_OBJS = user_settings.o rsf_settings.o LIB_API_OBJS = crypto.o yaml_parser.o blz.o @@ -21,7 +21,7 @@ CC = gcc # MAKEROM Build Settings MAKEROM_BUILD_FLAGS = #-DDEBUG VER_MAJOR = 0 -VER_MINOR = 10 +VER_MINOR = 11 OUTPUT = makerom main: build diff --git a/makerom/accessdesc.c b/makerom/accessdesc.c index 8c316c5a..f02d9f10 100644 --- a/makerom/accessdesc.c +++ b/makerom/accessdesc.c @@ -1,13 +1,11 @@ #include "lib.h" -#include "ncch.h" -#include "exheader.h" +#include "ncch_build.h" +#include "exheader_build.h" #include "accessdesc.h" -#include "polarssl/base64.h" - -#include "desc_presets.h" -#include "desc_dev_sigdata.h" -#include "desc_prod_sigdata.h" +#include "desc/presets.h" +#include "desc/dev_sigdata.h" +#include "desc/prod_sigdata.h" const int RSF_RSA_DATA_LEN = 344; const int RSF_DESC_DATA_LEN = 684; @@ -19,20 +17,16 @@ int accessdesc_GetSignFromPreset(exheader_settings *exhdrset); void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, keys_struct *keys); void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, keys_struct *keys); -bool IsValidB64Char(char chr); -u32 b64_strlen(char *str); -void b64_strcpy(char *dst, char *src); - int set_AccessDesc(exheader_settings *exhdrset) { - if(exhdrset->useAccessDescPreset) + if(exhdrset->useAccessDescPreset) // Use AccessDesc Template return accessdesc_GetSignFromPreset(exhdrset); else if(exhdrset->rsf->CommonHeaderKey.Found) // Keydata exists in RSF return accessdesc_GetSignFromRsf(exhdrset); else if(!exhdrset->keys->rsa.requiresPresignedDesc) // Else if The AccessDesc can be signed with key return accessdesc_SignWithKey(exhdrset); else{ // No way the access desc signature can be 'obtained' - fprintf(stderr,"[EXHEADER ERROR] Current keyset cannot sign AccessDesc, please appropriatly setup RSF, or specify a preset with -accessdesc\n"); + fprintf(stderr,"[ACEXDESC ERROR] Current keyset cannot sign AccessDesc, please appropriately set-up RSF, or specify a preset with \"-desc\"\n"); return CANNOT_SIGN_ACCESSDESC; } } @@ -65,7 +59,7 @@ int accessdesc_GetSignFromRsf(exheader_settings *exhdrset) { /* Yaml Option Sanity Checks */ if(!exhdrset->rsf->CommonHeaderKey.Found){ - fprintf(stderr,"[EXHEADER ERROR] RSF Section \"CommonHeaderKey\" not found\n"); + fprintf(stderr,"[ACEXDESC ERROR] RSF Section \"CommonHeaderKey\" not found\n"); return COMMON_HEADER_KEY_NOT_FOUND; } @@ -74,7 +68,7 @@ int accessdesc_GetSignFromRsf(exheader_settings *exhdrset) return COMMON_HEADER_KEY_NOT_FOUND; } if(b64_strlen(exhdrset->rsf->CommonHeaderKey.D) != RSF_RSA_DATA_LEN){ - fprintf(stderr,"[EXHEADER ERROR] \"CommonHeaderKey/D\" has invalid length (%d)\n",b64_strlen(exhdrset->rsf->CommonHeaderKey.D)); + fprintf(stderr,"[ACEXDESC ERROR] \"CommonHeaderKey/D\" has invalid length (%d)\n",b64_strlen(exhdrset->rsf->CommonHeaderKey.D)); return COMMON_HEADER_KEY_NOT_FOUND; } @@ -83,7 +77,7 @@ int accessdesc_GetSignFromRsf(exheader_settings *exhdrset) return COMMON_HEADER_KEY_NOT_FOUND; } if(b64_strlen(exhdrset->rsf->CommonHeaderKey.Modulus) != RSF_RSA_DATA_LEN){ - fprintf(stderr,"[EXHEADER ERROR] \"CommonHeaderKey/Modulus\" has invalid length (%d)\n",b64_strlen(exhdrset->rsf->CommonHeaderKey.Modulus)); + fprintf(stderr,"[ACEXDESC ERROR] \"CommonHeaderKey/Modulus\" has invalid length (%d)\n",b64_strlen(exhdrset->rsf->CommonHeaderKey.Modulus)); return COMMON_HEADER_KEY_NOT_FOUND; } @@ -92,7 +86,7 @@ int accessdesc_GetSignFromRsf(exheader_settings *exhdrset) return COMMON_HEADER_KEY_NOT_FOUND; } if(b64_strlen(exhdrset->rsf->CommonHeaderKey.AccCtlDescSign) != RSF_RSA_DATA_LEN){ - fprintf(stderr,"[EXHEADER ERROR] \"CommonHeaderKey/Signature\" has invalid length (%d)\n",b64_strlen(exhdrset->rsf->CommonHeaderKey.AccCtlDescSign)); + fprintf(stderr,"[ACEXDESC ERROR] \"CommonHeaderKey/Signature\" has invalid length (%d)\n",b64_strlen(exhdrset->rsf->CommonHeaderKey.AccCtlDescSign)); return COMMON_HEADER_KEY_NOT_FOUND; } @@ -101,41 +95,30 @@ int accessdesc_GetSignFromRsf(exheader_settings *exhdrset) return COMMON_HEADER_KEY_NOT_FOUND; } if(b64_strlen(exhdrset->rsf->CommonHeaderKey.AccCtlDescBin) != RSF_DESC_DATA_LEN){ - fprintf(stderr,"[EXHEADER ERROR] \"CommonHeaderKey/Descriptor\" has invalid length (%d)\n",b64_strlen(exhdrset->rsf->CommonHeaderKey.AccCtlDescBin)); + fprintf(stderr,"[ACEXDESC ERROR] \"CommonHeaderKey/Descriptor\" has invalid length (%d)\n",b64_strlen(exhdrset->rsf->CommonHeaderKey.AccCtlDescBin)); return COMMON_HEADER_KEY_NOT_FOUND; } /* Set RSA Keys */ int result = 0; - u32 out; - - out = 0x100; - result = base64_decode(exhdrset->keys->rsa.cxiHdrPub,(size_t *)&out,(const u8*)exhdrset->rsf->CommonHeaderKey.Modulus,strlen(exhdrset->rsf->CommonHeaderKey.Modulus)); - if(out != 0x100) - result = POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL; - if(result) goto finish; - - out = 0x100; - result = base64_decode(exhdrset->keys->rsa.cxiHdrPvt,(size_t *)&out,(const u8*)exhdrset->rsf->CommonHeaderKey.D,strlen(exhdrset->rsf->CommonHeaderKey.D)); - if(out != 0x100) - result = POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL; - if(result) goto finish; + // NCCH Header pubk + result = b64_decode(exhdrset->keys->rsa.cxiHdrPub,exhdrset->rsf->CommonHeaderKey.Modulus,0x100); + if(result) return result; + // NCCH Header privk + result = b64_decode(exhdrset->keys->rsa.cxiHdrPvt,exhdrset->rsf->CommonHeaderKey.D,0x100); + if(result) return result; /* Set AccessDesc */ - out = 0x100; - result = base64_decode(exhdrset->acexDesc->signature,(size_t *)&out,(const u8*)exhdrset->rsf->CommonHeaderKey.AccCtlDescSign, strlen( exhdrset->rsf->CommonHeaderKey.AccCtlDescSign)); - if(out != 0x100) - result = POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL; - if(result) goto finish; + // Signature + result = b64_decode(exhdrset->acexDesc->signature,exhdrset->rsf->CommonHeaderKey.AccCtlDescSign,0x100); + if(result) return result; + // NCCH Header pubk memcpy(exhdrset->acexDesc->ncchRsaPubKey,exhdrset->keys->rsa.cxiHdrPub,0x100); - - out = 0x200; - result = base64_decode((u8*)&exhdrset->acexDesc->arm11SystemLocalCapabilities,(size_t *)&out,(const u8*)exhdrset->rsf->CommonHeaderKey.AccCtlDescBin,strlen(exhdrset->rsf->CommonHeaderKey.AccCtlDescBin)); - if(out != 0x200) - result = POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL; - if(result) goto finish; -finish: - return result; + // Access Control + result = b64_decode((u8*)&exhdrset->acexDesc->arm11SystemLocalCapabilities,exhdrset->rsf->CommonHeaderKey.AccCtlDescBin,0x200); + if(result) return result; + + return 0; } int accessdesc_GetSignFromPreset(exheader_settings *exhdrset) @@ -153,12 +136,12 @@ int accessdesc_GetSignFromPreset(exheader_settings *exhdrset) // Error Checking if(!desc || !depList){ - fprintf(stderr,"[EXHEADER ERROR] AccessDesc preset is unavailable, please configure RSF file\n"); + fprintf(stderr,"[ACEXDESC ERROR] AccessDesc template is unavailable, please configure RSF file\n"); return CANNOT_SIGN_ACCESSDESC; } if((!cxiPubk || !cxiPvtk || !accessDesc || !accessDescSig) && exhdrset->keys->rsa.requiresPresignedDesc){ - fprintf(stderr,"[EXHEADER ERROR] This AccessDesc preset needs to be signed, the current keyset is incapable of doing so. Please configure RSF file with the appropriate signature data.\n"); + fprintf(stderr,"[ACEXDESC ERROR] This AccessDesc template needs to be signed, the current keyset is incapable of doing so. Please configure RSF file with the appropriate signature data.\n"); return CANNOT_SIGN_ACCESSDESC; } @@ -451,40 +434,4 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, break; } } -} - -bool IsValidB64Char(char chr) -{ - return (isalnum(chr) || chr == '+' || chr == '/' || chr == '='); -} - -u32 b64_strlen(char *str) -{ - u32 count = 0; - u32 i = 0; - while(str[i] != 0x0){ - if(IsValidB64Char(str[i])) { - //printf("Is Valid: %c\n",str[i]); - count++; - } - i++; - } - - return count; -} - -void b64_strcpy(char *dst, char *src) -{ - u32 src_len = strlen(src); - u32 j = 0; - for(u32 i = 0; i < src_len; i++){ - if(IsValidB64Char(src[i])){ - dst[j] = src[i]; - j++; - } - } - dst[j] = 0; - - //memdump(stdout,"src: ",(u8*)src,src_len+1); - //memdump(stdout,"dst: ",(u8*)dst,j+1); } \ No newline at end of file diff --git a/makerom/cardinfo.c b/makerom/cardinfo.c new file mode 100644 index 00000000..f2ac692f --- /dev/null +++ b/makerom/cardinfo.c @@ -0,0 +1,207 @@ +#include "lib.h" +#include "ncch_read.h" +#include "ncsd_build.h" +#include "cardinfo.h" + +void InitCardInfoHdr(cardinfo_hdr **cihdr, devcardinfo_hdr **dcihdr, cci_settings *set); +int SetWriteableAddress(cardinfo_hdr *hdr, cci_settings *set); +int SetCardInfoBitmask(cardinfo_hdr *hdr, cci_settings *set); +int SetCardInfoNotes(cardinfo_hdr *hdr, cci_settings *set); +void ImportNcch0Data(cardinfo_hdr *hdr, cci_settings *set); +void SetInitialData(cardinfo_hdr *hdr, cci_settings *set); +void SetDevCardInfo(devcardinfo_hdr *hdr, cci_settings *set); + + +int GenCardInfoHdr(cci_settings *set) +{ + cardinfo_hdr *cihdr; + devcardinfo_hdr *dcihdr; + + InitCardInfoHdr(&cihdr,&dcihdr,set); + + if(SetWriteableAddress(cihdr,set)) + return GEN_HDR_FAIL; + if(SetCardInfoBitmask(cihdr,set)) + return GEN_HDR_FAIL; + if(SetCardInfoNotes(cihdr,set)) + return GEN_HDR_FAIL; + ImportNcch0Data(cihdr,set); + SetInitialData(cihdr,set); + + if(dcihdr) + SetDevCardInfo(dcihdr,set); + + return 0; +} + +void InitCardInfoHdr(cardinfo_hdr **cihdr, devcardinfo_hdr **dcihdr, cci_settings *set) +{ + set->headers.cardinfohdr.size = sizeof(cardinfo_hdr); + if(set->options.useExternalSdkCardInfo) + set->headers.cardinfohdr.size += sizeof(devcardinfo_hdr); + + set->headers.cardinfohdr.buffer = calloc(1,set->headers.cardinfohdr.size); + + *cihdr = (cardinfo_hdr*)set->headers.cardinfohdr.buffer; + + if(set->headers.cardinfohdr.size > sizeof(cardinfo_hdr)) + *dcihdr = (devcardinfo_hdr*)(set->headers.cardinfohdr.buffer+sizeof(cardinfo_hdr)); + else + *dcihdr = NULL; + + return; +} + +u64 GetCciUnusedSize(u64 mediaSize, u8 cardType) +{ + if(cardType == mediatype_CARD1){ + switch(mediaSize){ + case (u64)MB*128: return (u64)2621440; + case (u64)MB*256: return (u64)5242880; + case (u64)MB*512: return (u64)10485760; + case (u64)GB*1: return (u64)73924608; + case (u64)GB*2: return (u64)147324928; + case (u64)GB*4: return (u64)294649856; + case (u64)GB*8: return (u64)587202560; + default: return 0; + } + } + else if(cardType == mediatype_CARD2){ + switch(mediaSize){ + case (u64)MB*512: return (u64)37224448; + case (u64)GB*1: return (u64)73924608; + case (u64)GB*2: return (u64)147324928; + case (u64)GB*4: return (u64)294649856; + case (u64)GB*8: return (u64)587202560; + default: return 0; + } + } + return 0; +} + +int SetWriteableAddress(cardinfo_hdr *hdr, cci_settings *set) +{ + if(set->romInfo.mediaType != mediatype_CARD2){ // Can only be set for Card2 Media + u32_to_u8(hdr->writableAddress,(u32)-1,LE); + return 0; + } + + char *str = set->rsf->CardInfo.WritableAddress; + set->romInfo.card2SaveOffset = -1; + + if(str){ + if(strncmp(str,"0x",2) != 0){ + fprintf(stderr,"[CCI ERROR] WritableAddress requires a Hexadecimal value\n"); + return INVALID_RSF_OPT; + } + set->romInfo.card2SaveOffset = strtoull(str,NULL,16); + } + else{ + if ((set->romInfo.mediaSize / 2) < set->romInfo.saveSize || set->romInfo.saveSize > (u64)(2047*MB)){ + u64 saveDataSize = set->romInfo.saveSize / KB; + fprintf(stderr,"[CCI ERROR] Too large SavedataSize %"PRIu64"K\n",saveDataSize); + return SAVE_DATA_TOO_LARGE; + } + if(set->options.closeAlignWR) + set->romInfo.card2SaveOffset = align(set->romInfo.usedSize, set->romInfo.blockSize); // invalid for "real" chips + else{ + u64 unusedSize = GetCciUnusedSize(set->romInfo.mediaSize,set->romInfo.mediaType); // Some value related to the physical implementation of gamecards + if(unusedSize > 0) + set->romInfo.card2SaveOffset = set->romInfo.mediaSize - unusedSize - set->romInfo.saveSize; // Nintendo's method for calculating writable region offset + else{ + fprintf(stderr,"[CCI WARNING] Nintendo does not support CARD2 for the current MediaSize, aligning save offset after last NCCH\n"); + set->romInfo.card2SaveOffset = align(set->romInfo.usedSize, set->romInfo.blockSize); // invalid for "real" chips + } + } + } + + u32_to_u8(hdr->writableAddress,(u32)(set->romInfo.card2SaveOffset/set->romInfo.blockSize),LE); + + return 0; +} + +int SetCardInfoBitmask(cardinfo_hdr *hdr, cci_settings *set) +{ + u32 bitmask = 0; + + char *str = set->rsf->CardInfo.CardType; + if(!str) + bitmask |= 0; + else{ + if(strcasecmp(str,"s1") == 0) + bitmask |= 0; + else if(strcasecmp(str,"s2") == 0) + bitmask |= 0x20; + else { + fprintf(stderr,"[CCI ERROR] Invalid CardType: %s\n",str); + return INVALID_RSF_OPT; + } + } + + str = set->rsf->CardInfo.CryptoType; + if(!str) + bitmask |= 0;//(3*0x40); + else{ + int val = strtol(str,NULL,10); + if(val < 0 || val > 3) { + fprintf(stderr,"[CCI ERROR] Invalid CryptoType: %s\n",str); + return INVALID_RSF_OPT; + } + if(val != 3) + fprintf(stderr,"[CCI WARNING] Card crypto type = '%d'\n",val); + bitmask |= val * 0x40; + } + + u32_to_u8(hdr->cardInfoBitmask,bitmask,BE); + + return 0; +} + +int SetCardInfoNotes(cardinfo_hdr *hdr, cci_settings *set) +{ + u64_to_u8(hdr->notes.mediaSizeUsed,set->romInfo.usedSize,LE); + u32_to_u8(hdr->notes.unknown,0,LE); + + if(set->options.tmdHdr){ + u64_to_u8(hdr->notes.cverTitleId,GetTmdTitleId(set->options.tmdHdr),LE); + u16_to_u8(hdr->notes.cverTitleId,GetTmdVersion(set->options.tmdHdr),LE); + } + + return 0; +} + +void ImportNcch0Data(cardinfo_hdr *hdr, cci_settings *set) +{ + u8 *ncch; + ncch_hdr *ncchHdr; + + ncch = set->content.data + set->content.dOffset[0]; + ncchHdr = (ncch_hdr*)ncch; + + memcpy(hdr->ncch0TitleId,ncchHdr->titleId,8); + memcpy(hdr->ncch0Hdr,GetNcchHdrData(ncchHdr),GetNcchHdrDataLen(ncchHdr)); + + return; +} + +void SetInitialData(cardinfo_hdr *hdr, cci_settings *set) +{ + clrmem(hdr->initialData,0x30); + if(set->options.useExternalSdkCardInfo) + memcpy(hdr->initialData,(u8*)stock_initial_data,0x30); + else + rndset(hdr->initialData,0x2c); + + return; +} + +void SetDevCardInfo(devcardinfo_hdr *hdr, cci_settings *set) +{ + clrmem(hdr,sizeof(devcardinfo_hdr)); + if(set->options.useExternalSdkCardInfo) + memcpy(hdr->titleKey,(u8*)stock_title_key,0x10); + else + rndset(hdr->titleKey,0x10); + + return; +} \ No newline at end of file diff --git a/makerom/cardinfo.h b/makerom/cardinfo.h new file mode 100644 index 00000000..271edac8 --- /dev/null +++ b/makerom/cardinfo.h @@ -0,0 +1,53 @@ +#pragma once + +typedef struct +{ + u8 reserved0[0xf8]; + u8 mediaSizeUsed[8]; + u8 reserved1[0x8]; + u8 unknown[0x4]; + u8 reserved2[0xc]; + u8 cverTitleId[8]; + u8 cverTitleVersion[2]; + u8 reserved3[0xcd6]; +} cardinfo_notes; // reserved[0xDF8]; + +typedef struct +{ + u8 writableAddress[4]; + u8 cardInfoBitmask[4]; + cardinfo_notes notes; + u8 ncch0TitleId[8]; + u8 reserved0[8]; + u8 initialData[0x30]; + u8 reserved1[0xc0]; + u8 ncch0Hdr[0x100]; +} cardinfo_hdr; + +typedef struct +{ + u8 cardDeviceReserved1[0x200]; + u8 titleKey[0x10]; + u8 cardDeviceReserved2[0xf0]; +} devcardinfo_hdr; + +static const u8 stock_initial_data[0x30] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xAD, 0x88, + 0xAC, 0x41, 0xA2, 0xB1, 0x5E, 0x8F, + 0x66, 0x9C, 0x97, 0xE5, 0xE1, 0x5E, + 0xA3, 0xEB, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 stock_title_key[0x10] = +{ + 0x6E, 0xC7, 0x5F, 0xB2, 0xE2, 0xB4, + 0x87, 0x46, 0x1E, 0xDD, 0xCB, 0xB8, + 0x97, 0x11, 0x92, 0xBA +}; + +int GenCardInfoHdr(cci_settings *set); \ No newline at end of file diff --git a/makerom/cia.c b/makerom/cia.c index e820a9df..b9ab061c 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -1,41 +1,44 @@ #include "lib.h" -#include "ncch.h" -#include "exheader.h" -#include "exefs.h" -#include "certs.h" -#include "cia.h" -#include "tik.h" -#include "tmd.h" -#include "titleid.h" + #include "srl.h" -#include "ncsd.h" +#include "ncsd_read.h" +#include "ncch_read.h" +#include "exheader_read.h" +#include "exefs_read.h" + +#include "cia_build.h" +#include "cia_read.h" +#include "tik_build.h" +#include "tmd_build.h" +#include "titleid.h" +#include "certs.h" + +const int CIA_ALIGN_SIZE = 0x40; +const int CIA_CONTENT_ALIGN = 0x10; // Private Prototypes -/* cia_settings tools */ -void init_CIASettings(cia_settings *set); -void free_CIASettings(cia_settings *set); -int get_CIASettings(cia_settings *ciaset, user_settings *usrset); +void InitCiaSettings(cia_settings *set); +void FreeCiaSettings(cia_settings *set); +int GetCiaSettings(cia_settings *ciaset, user_settings *usrset); int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset); int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset); -int GetCIADataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_struct *ncch_ctx, u8 *key); -int GetMetaRegion(cia_settings *ciaset, u8 *ncch, ncch_struct *ncch_ctx, u8 *key); +int GetTmdDataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_info *ncch_ctx, u8 *key); +int GetMetaRegion(cia_settings *ciaset, u8 *ncch, ncch_info *ncch_ctx, u8 *key); int GetContentFilePtrs(cia_settings *ciaset, user_settings *usrset); int ImportNcchContent(cia_settings *ciaset); int GetSettingsFromSrl(cia_settings *ciaset); int GetSettingsFromCci(cia_settings *ciaset); -u16 SetupVersion(u16 Major, u16 Minor, u16 Micro); +u16 SetupVersion(u16 major, u16 minor, u16 micro); void GetContentHashes(cia_settings *ciaset); void EncryptContent(cia_settings *ciaset); -int BuildCIA_CertChain(cia_settings *ciaset); -int BuildCIA_Header(cia_settings *ciaset); - -int WriteCIAtoFile(cia_settings *ciaset); +int BuildCiaCertChain(cia_settings *ciaset); +int BuildCiaHdr(cia_settings *ciaset); -int CryptContent(u8 *EncBuffer,u8 *DecBuffer,u64 size,u8 *title_key, u16 index, u8 mode); +int WriteCiaToFile(cia_settings *ciaset); int build_CIA(user_settings *usrset) @@ -50,8 +53,8 @@ int build_CIA(user_settings *usrset) } // Get Settings - init_CIASettings(ciaset); - result = get_CIASettings(ciaset,usrset); + InitCiaSettings(ciaset); + result = GetCiaSettings(ciaset,usrset); if(result) goto finish; // Create Output File @@ -65,7 +68,7 @@ int build_CIA(user_settings *usrset) // Create CIA Sections /* Certificate Chain */ - result = BuildCIA_CertChain(ciaset); + result = BuildCiaCertChain(ciaset); if(result) goto finish; /* Ticket */ @@ -77,28 +80,28 @@ int build_CIA(user_settings *usrset) if(result) goto finish; /* CIA Header */ - result = BuildCIA_Header(ciaset); + result = BuildCiaHdr(ciaset); if(result) goto finish; /* Write To File */ - result = WriteCIAtoFile(ciaset); + result = WriteCiaToFile(ciaset); if(result) goto finish; finish: if(result != FAILED_TO_CREATE_OUTFILE && ciaset->out) fclose(ciaset->out); - free_CIASettings(ciaset); + FreeCiaSettings(ciaset); return result; } -void init_CIASettings(cia_settings *set) +void InitCiaSettings(cia_settings *set) { memset(set,0,sizeof(cia_settings)); } -void free_CIASettings(cia_settings *set) +void FreeCiaSettings(cia_settings *set) { if(set->content.filePtrs){ for(u32 i = 1; i < set->content.count; i++){ @@ -117,7 +120,7 @@ void free_CIASettings(cia_settings *set) free(set); } -int get_CIASettings(cia_settings *ciaset, user_settings *usrset) +int GetCiaSettings(cia_settings *ciaset, user_settings *usrset) { int result = 0; @@ -125,26 +128,21 @@ int get_CIASettings(cia_settings *ciaset, user_settings *usrset) result = GetSettingsFromUsrset(ciaset,usrset); if(usrset->common.workingFileType == infile_ncch){ - result = GetSettingsFromNcch0(ciaset,0); - if(result) + if((result = GetSettingsFromNcch0(ciaset,0)) != 0) return result; - result = GetContentFilePtrs(ciaset,usrset); - if(result) + if((result = GetContentFilePtrs(ciaset,usrset)) != 0) return result; - result = ImportNcchContent(ciaset); - if(result) + if((result = ImportNcchContent(ciaset)) != 0) return result; } else if(usrset->common.workingFileType == infile_srl){ - result = GetSettingsFromSrl(ciaset); - if(result) + if((result = GetSettingsFromSrl(ciaset)) != 0) return result; } else if(usrset->common.workingFileType == infile_ncsd){ - result = GetSettingsFromCci(ciaset); - if(result) + if((result = GetSettingsFromCci(ciaset)) != 0) return result; } @@ -165,7 +163,9 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) ciaset->ciaSections.content.size = usrset->common.workingFile.size; usrset->common.workingFile.buffer = NULL; ciaset->ciaSections.content.size = 0; - + ciaset->content.includeUpdateNcch = usrset->cia.includeUpdateNcch; + ciaset->verbose = usrset->common.verbose; + u32_to_u8(ciaset->tmd.titleType,TYPE_CTR,BE); ciaset->content.encryptCia = usrset->common.rsfSet.Option.EnableCrypt; ciaset->content.IsDlc = usrset->cia.DlcContent; @@ -183,16 +183,19 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) // Ticket Data rndset(ciaset->tik.ticketId,8); - clrmem(ciaset->tik.deviceId,4); - clrmem(ciaset->tik.eshopAccId,4); + u32_to_u8(ciaset->tik.deviceId,usrset->cia.deviceId,BE); + u32_to_u8(ciaset->tik.eshopAccId,usrset->cia.eshopAccId,BE); ciaset->tik.licenceType = 0; ciaset->tik.audit = 0; if(usrset->cia.randomTitleKey) - rndset(ciaset->common.titleKey,16); + rndset(ciaset->common.titleKey,AES_128_KEY_SIZE); else - clrmem(ciaset->common.titleKey,16); + clrmem(ciaset->common.titleKey,AES_128_KEY_SIZE); + if(ciaset->verbose) + memdump(stdout,"[CIA] CIA title key: ",ciaset->common.titleKey,AES_128_KEY_SIZE); + ciaset->tik.formatVersion = 1; int result = GenCertChildIssuer(ciaset->tik.issuer,ciaset->keys->certs.xsCert); @@ -217,33 +220,29 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) u8 *ncch0 = (u8*)(ciaset->ciaSections.content.buffer+ncch0_offset); - if(!IsNCCH(NULL,ncch0)){ + if(!IsNcch(NULL,ncch0)){ fprintf(stderr,"[CIA ERROR] Content0 is not NCCH\n"); return CIA_INVALID_NCCH0; } /* Get Ncch0 Header */ - ncch_hdr *hdr = NULL; - hdr = GetNCCH_CommonHDR(hdr,NULL,ncch0); - if(IsCfa(hdr)){ - ciaset->content.IsCfa = true; - } + ncch_hdr *hdr = (ncch_hdr*)ncch0; + ciaset->content.IsCfa = IsCfa(hdr); ciaset->content.offset[0] = 0; - ciaset->content.size[0] = align(GetNCCH_MediaSize(hdr)*GetNCCH_MediaUnitSize(hdr),0x10); + ciaset->content.size[0] = align(GetNcchSize(hdr),0x10); ciaset->content.totalSize = ciaset->content.size[0]; /* Get Ncch0 Import Context */ - ncch_struct *ncch_ctx = malloc(sizeof(ncch_struct)); - if(!ncch_ctx){ + ncch_info *info = calloc(1,sizeof(ncch_info)); + if(!info){ fprintf(stderr,"[CIA ERROR] Not enough memory\n"); return MEM_ERROR; } - memset(ncch_ctx,0x0,sizeof(ncch_struct)); - GetNCCHStruct(ncch_ctx,hdr); + GetNcchInfo(info,hdr); /* Verify Ncch0 (Sig&Hash Checks) */ - int result = VerifyNCCH(ncch0,ciaset->keys,false,true); + int result = VerifyNcch(ncch0,ciaset->keys,false,true); if(result == UNABLE_TO_LOAD_NCCH_KEY){ ciaset->content.keyNotFound = true; if(!ciaset->content.IsCfa){ @@ -261,31 +260,35 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) /* Getting ncch key */ - ncch_key_type keyType = GetNCCHKeyType(hdr); u8 *ncchkey = NULL; - if(!ciaset->content.keyNotFound){ - SetNcchUnfixedKeys(ciaset->keys,ncch0); - ncchkey = GetNCCHKey0(keyType,ciaset->keys); + if(!ciaset->content.keyNotFound || IsNcchEncrypted(hdr)){ + SetNcchKeys(ciaset->keys,hdr); + ncchkey = ciaset->keys->aes.ncchKey0; + if(ciaset->verbose){ + printf("[CIA] NCCH AES keys:\n"); + memdump(stdout," > key0: ",ciaset->keys->aes.ncchKey0,AES_128_KEY_SIZE); + memdump(stdout," > key1: ",ciaset->keys->aes.ncchKey1,AES_128_KEY_SIZE); + } } /* Get TMD Data from ncch */ - result = GetCIADataFromNcch(ciaset,ncch0,ncch_ctx,ncchkey); // Data For TMD + result = GetTmdDataFromNcch(ciaset,ncch0,info,ncchkey); // Data For TMD if(result) goto finish; /* Get META Region from ncch */ - result = GetMetaRegion(ciaset,ncch0,ncch_ctx,ncchkey); // Meta Region + result = GetMetaRegion(ciaset,ncch0,info,ncchkey); // Meta Region /* Finish */ finish: /* Return */ - free(ncch_ctx); + free(info); return result; } -int GetCIADataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_struct *ncch_ctx, u8 *key) +int GetTmdDataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_info *ncch_ctx, u8 *key) { extended_hdr *exhdr = malloc(sizeof(extended_hdr)); memcpy(exhdr,ncch+ncch_ctx->exhdrOffset,sizeof(extended_hdr)); if(key != NULL) - CryptNCCHSection((u8*)exhdr,sizeof(extended_hdr),0,ncch_ctx,key,ncch_exhdr); + CryptNcchRegion((u8*)exhdr,sizeof(extended_hdr),0,ncch_ctx,key,ncch_exhdr); u16 Category = u8_to_u16((ciaset->common.titleId+2),BE); if(IsPatch(Category)||ciaset->content.IsCfa||ciaset->content.keyNotFound) @@ -318,28 +321,26 @@ int GetCIADataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_struct *ncch_ctx, u8 ciaset->common.titleVersion[VER_MAJOR] = GetRemasterVersion_frm_exhdr(exhdr); } - u16 version = SetupVersion(ciaset->common.titleVersion[VER_MAJOR],ciaset->common.titleVersion[VER_MINOR],ciaset->common.titleVersion[VER_MICRO]); - ciaset->tik.version = version; - ciaset->tmd.version = version; + ciaset->tmd.version = ciaset->tik.version = SetupVersion(ciaset->common.titleVersion[VER_MAJOR],ciaset->common.titleVersion[VER_MINOR],ciaset->common.titleVersion[VER_MICRO]); free(exhdr); return 0; } -int GetMetaRegion(cia_settings *ciaset, u8 *ncch, ncch_struct *ncch_ctx, u8 *key) +int GetMetaRegion(cia_settings *ciaset, u8 *ncch, ncch_info *info, u8 *key) { if(ciaset->content.IsCfa || ciaset->content.keyNotFound) return 0; extended_hdr *exhdr = malloc(sizeof(extended_hdr)); - memcpy(exhdr,ncch+ncch_ctx->exhdrOffset,sizeof(extended_hdr)); + memcpy(exhdr,ncch+info->exhdrOffset,sizeof(extended_hdr)); if(key != NULL) - CryptNCCHSection((u8*)exhdr,sizeof(extended_hdr),0,ncch_ctx,key,ncch_exhdr); + CryptNcchRegion((u8*)exhdr,sizeof(extended_hdr),0,info,key,ncch_exhdr); exefs_hdr *exefsHdr = malloc(sizeof(exefs_hdr)); - memcpy(exefsHdr,ncch+ncch_ctx->exefsOffset,sizeof(exefs_hdr)); + memcpy(exefsHdr,ncch+info->exefsOffset,sizeof(exefs_hdr)); if(key != NULL) - CryptNCCHSection((u8*)exefsHdr,sizeof(exefs_hdr),0,ncch_ctx,key,ncch_exefs); + CryptNcchRegion((u8*)exefsHdr,sizeof(exefs_hdr),0,info,key,ncch_exefs); u32 icon_size = 0; u32 icon_offset = 0; @@ -349,7 +350,7 @@ int GetMetaRegion(cia_settings *ciaset, u8 *ncch, ncch_struct *ncch_ctx, u8 *key icon_offset = u8_to_u32(exefsHdr->fileHdr[i].offset,LE) + sizeof(exefs_hdr); } } - + ciaset->ciaSections.meta.size = sizeof(cia_metadata) + icon_size; ciaset->ciaSections.meta.buffer = malloc(ciaset->ciaSections.meta.size); if(!ciaset->ciaSections.meta.buffer){ @@ -362,9 +363,9 @@ int GetMetaRegion(cia_settings *ciaset, u8 *ncch, ncch_struct *ncch_ctx, u8 *key GetCoreVersion_frm_exhdr(hdr->coreVersion,exhdr); if(icon_size > 0){ u8 *IconDestPos = (ciaset->ciaSections.meta.buffer + sizeof(cia_metadata)); - memcpy(IconDestPos,ncch+ncch_ctx->exefsOffset+icon_offset,icon_size); + memcpy(IconDestPos,ncch+info->exefsOffset+icon_offset,icon_size); if(key != NULL) - CryptNCCHSection(IconDestPos,icon_size,icon_offset,ncch_ctx,key,ncch_exefs); + CryptNcchRegion(IconDestPos,icon_size,icon_offset,info,key,ncch_exefs); //memdump(stdout,"Icon: ",IconDestPos,0x10); } @@ -389,7 +390,7 @@ int GetContentFilePtrs(cia_settings *ciaset, user_settings *usrset) fprintf(stderr,"[CIA ERROR] Failed to open \"%s\"\n",usrset->common.contentPath[i]); return FAILED_TO_OPEN_FILE; } - ciaset->content.fileSize[j] = GetFileSize_u64(usrset->common.contentPath[i]); + ciaset->content.fileSize[j] = GetFileSize64(usrset->common.contentPath[i]); ciaset->content.filePtrs[j] = fopen(usrset->common.contentPath[i],"rb"); if(usrset->cia.contentId[i] > MAX_U32) @@ -400,10 +401,10 @@ int GetContentFilePtrs(cia_settings *ciaset, user_settings *usrset) ciaset->content.index[j] = (u16)i; // Get Data from ncch HDR - GetNCCH_CommonHDR(hdr,ciaset->content.filePtrs[j],NULL); + ReadNcchHdr(hdr,ciaset->content.filePtrs[j]); // Get Size - u64 calcSize = (u64)GetNCCH_MediaSize(hdr) * (u64)GetNCCH_MediaUnitSize(hdr); + u64 calcSize = GetNcchSize(hdr); if(calcSize != ciaset->content.fileSize[j]){ fprintf(stderr,"[CIA ERROR] \"%s\" is corrupt\n",usrset->common.contentPath[i]); return FAILED_TO_OPEN_FILE; @@ -447,7 +448,7 @@ int ImportNcchContent(cia_settings *ciaset) // Import u8 *ncchpos = (u8*)(ciaset->ciaSections.content.buffer+ciaset->content.offset[i]); - ReadFile_64(ncchpos, ciaset->content.fileSize[i], 0, ciaset->content.filePtrs[i]); + ReadFile64(ncchpos, ciaset->content.fileSize[i], 0, ciaset->content.filePtrs[i]); if(ModifyNcchIds(ncchpos, NULL, ncch0hdr->programId, ciaset->keys) != 0) return -1; @@ -528,6 +529,11 @@ int GetSettingsFromCci(cia_settings *ciaset) cciContentOffsets[0] = ncch0_offset; for(int i = 1; i < 8; i++){ if(GetPartitionSize(ciaset->ciaSections.content.buffer,i)){ + ncch_hdr *ncchHdr = (ncch_hdr*)GetPartition(ciaset->ciaSections.content.buffer, i); + + if(IsUpdateCfa(ncchHdr) && !ciaset->content.includeUpdateNcch) + continue; + cciContentOffsets[j] = GetPartitionOffset(ciaset->ciaSections.content.buffer,i); // Get Size @@ -557,9 +563,9 @@ int GetSettingsFromCci(cia_settings *ciaset) return 0; } -u16 SetupVersion(u16 Major, u16 Minor, u16 Micro) +u16 SetupVersion(u16 major, u16 minor, u16 micro) { - return (((Major << 10) & 0xFC00) | ((Minor << 4) & 0x3F0) | (Micro & 0xf)); + return (((major << 10) & 0xFC00) | ((minor << 4) & 0x3F0) | (micro & 0xf)); } void GetContentHashes(cia_settings *ciaset) @@ -577,7 +583,7 @@ void EncryptContent(cia_settings *ciaset) } } -int BuildCIA_CertChain(cia_settings *ciaset) +int BuildCiaCertChain(cia_settings *ciaset) { ciaset->ciaSections.certChain.size = GetCertSize(ciaset->keys->certs.caCert) + GetCertSize(ciaset->keys->certs.xsCert) + GetCertSize(ciaset->keys->certs.cpCert); ciaset->ciaSections.certChain.buffer = malloc(ciaset->ciaSections.certChain.size); @@ -591,7 +597,7 @@ int BuildCIA_CertChain(cia_settings *ciaset) return 0; } -int BuildCIA_Header(cia_settings *ciaset) +int BuildCiaHdr(cia_settings *ciaset) { // Allocating memory for header ciaset->ciaSections.ciaHdr.size = sizeof(cia_hdr); @@ -644,7 +650,7 @@ int BuildCIA_Header(cia_settings *ciaset) return 0; } -int WriteCIAtoFile(cia_settings *ciaset) +int WriteCiaToFile(cia_settings *ciaset) { WriteBuffer(ciaset->ciaSections.ciaHdr.buffer,ciaset->ciaSections.ciaHdr.size,0,ciaset->out); WriteBuffer(ciaset->ciaSections.certChain.buffer,ciaset->ciaSections.certChain.size,ciaset->ciaSections.certChainOffset,ciaset->out); @@ -656,7 +662,7 @@ int WriteCIAtoFile(cia_settings *ciaset) } -int CryptContent(u8 *EncBuffer,u8 *DecBuffer,u64 size,u8 *title_key, u16 index, u8 mode) +int CryptContent(u8 *enc, u8 *dec, u64 size, u8 *title_key, u16 index, u8 mode) { //generating IV u8 iv[16]; @@ -667,7 +673,110 @@ int CryptContent(u8 *EncBuffer,u8 *DecBuffer,u64 size,u8 *title_key, u16 index, ctr_aes_context ctx; memset(&ctx,0x0,sizeof(ctr_aes_context)); ctr_init_aes_cbc(&ctx,title_key,iv,mode); - if(mode == ENC) ctr_aes_cbc(&ctx,DecBuffer,EncBuffer,size,ENC); - else ctr_aes_cbc(&ctx,EncBuffer,DecBuffer,size,DEC); + if(mode == ENC) ctr_aes_cbc(&ctx,dec,enc,size,ENC); + else ctr_aes_cbc(&ctx,enc,dec,size,DEC); return 0; +} + +bool IsCia(u8 *cia) +{ + if(!cia) + return false; + + cia_hdr *hdr = (cia_hdr*)cia; + + if(u8_to_u32(hdr->hdrSize,LE) != sizeof(cia_hdr) || u8_to_u16(hdr->type,LE) != 0 || u8_to_u16(hdr->version,LE) != 0) + return false; + + if(!GetCiaCertSize(hdr) || !GetCiaTikSize(hdr) || !GetCiaTmdSize(hdr) || !GetCiaContentSize(hdr)) + return false; + + return true; +} + +u64 GetCiaCertOffset(cia_hdr *hdr) +{ + u64 hdrSize = u8_to_u32(hdr->hdrSize,LE); + return align(hdrSize,CIA_ALIGN_SIZE); +} + +u64 GetCiaCertSize(cia_hdr *hdr) +{ + return u8_to_u32(hdr->certChainSize,LE); +} + +u64 GetCiaTikOffset(cia_hdr *hdr) +{ + u64 certOffset = GetCiaCertOffset(hdr); + u64 certSize = GetCiaCertSize(hdr); + return align(certOffset + certSize,CIA_ALIGN_SIZE); +} + +u64 GetCiaTikSize(cia_hdr *hdr) +{ + return u8_to_u32(hdr->tikSize,LE); +} + +u64 GetCiaTmdOffset(cia_hdr *hdr) +{ + u64 tikOffset = GetCiaTikOffset(hdr); + u64 tikSize = GetCiaTikSize(hdr); + return align(tikOffset + tikSize,CIA_ALIGN_SIZE); +} + +u64 GetCiaTmdSize(cia_hdr *hdr) +{ + return u8_to_u32(hdr->tmdSize,LE); +} + +u64 GetCiaContentOffset(cia_hdr *hdr) +{ + u64 tmdOffset = GetCiaTmdOffset(hdr); + u64 tmdSize = GetCiaTmdSize(hdr); + return align(tmdOffset + tmdSize,CIA_ALIGN_SIZE); +} + +u64 GetCiaContentSize(cia_hdr *hdr) +{ + return u8_to_u64(hdr->contentSize,LE); +} + +u64 GetCiaMetaOffset(cia_hdr *hdr) +{ + u64 contentOffset = GetCiaContentOffset(hdr); + u64 contentSize = GetCiaContentSize(hdr); + return align(contentOffset + contentSize,CIA_ALIGN_SIZE); +} + +u64 GetCiaMetaSize(cia_hdr *hdr) +{ + return u8_to_u32(hdr->metaSize,LE); +} + +u8* GetCiaCert(u8 *cia) +{ + return cia + GetCiaCertOffset((cia_hdr*)cia); +} + +u8* GetCiaTik(u8 *cia) +{ + return cia + GetCiaTikOffset((cia_hdr*)cia); +} + +u8* GetCiaTmd(u8 *cia) +{ + return cia + GetCiaTmdOffset((cia_hdr*)cia); +} + +u8* GetCiaContent(u8 *cia) +{ + return cia + GetCiaContentOffset((cia_hdr*)cia); +} + +u8* GetCiaMeta(u8 *cia) +{ + if(GetCiaMetaSize((cia_hdr*)cia)) + return cia + GetCiaMetaOffset((cia_hdr*)cia); + else + return NULL; } \ No newline at end of file diff --git a/makerom/cia.h b/makerom/cia.h index b810049d..b7dc6c2c 100644 --- a/makerom/cia.h +++ b/makerom/cia.h @@ -1,17 +1,5 @@ #pragma once -static const int CIA_ALIGN_SIZE = 0x40; -static const int CIA_CONTENT_ALIGN = 0x10; - -// Enums -typedef enum -{ - CIA_NO_NCCH0 = -1, - CIA_INVALID_NCCH0 = -2, - CIA_CONFILCTING_CONTENT_IDS = -3, - CIA_BAD_VERSION = -4, -} cia_errors; - // Structs typedef struct { @@ -34,107 +22,5 @@ typedef struct u8 padding1[0xfc]; } cia_metadata; -typedef struct -{ - u8 *inFile; - u64 inFileSize; - - FILE *out; - - rsf_settings *rsf; - keys_struct *keys; - - struct{ - u8 titleId[8]; - u16 titleVersion[4]; - u8 titleKey[16]; - } common; - - - struct{ - u8 caCrlVersion; - u8 signerCrlVersion; - } cert; - - struct{ - u8 issuer[0x40]; - u8 formatVersion; - - u16 version; - - u8 ticketId[8]; - u8 deviceId[4]; - u8 licenceType; - u8 audit; - u8 eshopAccId[4]; - } tik; - - struct{ - u8 issuer[0x40]; - u8 formatVersion; - - u16 version; - - u8 titleType[4]; - u8 savedataSize[4]; - u8 privSavedataSize[4]; - u8 twlFlag; - } tmd; - - struct{ - bool IsCfa; - bool IsDlc; - bool encryptCia; - - bool keyNotFound; - - FILE **filePtrs; - u64 fileSize[CIA_MAX_CONTENT]; - - /* Misc Records */ - u16 count; - u64 offset[CIA_MAX_CONTENT]; - u64 totalSize; - - /* Content Chunk Records */ - u64 size[CIA_MAX_CONTENT]; - u16 index[CIA_MAX_CONTENT]; - u16 flags[CIA_MAX_CONTENT]; - u32 id[CIA_MAX_CONTENT]; - u8 hash[CIA_MAX_CONTENT][0x20]; - } content; - - struct{ - buffer_struct ciaHdr; - - u32 certChainOffset; - buffer_struct certChain; - - u32 tikOffset; - buffer_struct tik; - - u32 tmdOffset; - buffer_struct tmd; - - u32 metaOffset; - buffer_struct meta; - - u64 contentOffset; - buffer_struct content; - } ciaSections; -} cia_settings; - -// Public Prototypes -int build_CIA(user_settings *usrset); +int CryptContent(u8 *enc, u8 *dec, u64 size, u8 *title_key, u16 index, u8 mode); -// Cia Read Functions -u64 GetCiaCertOffset(cia_hdr *hdr); -u64 GetCiaCertSize(cia_hdr *hdr); -u64 GetTikOffset(cia_hdr *hdr); -u64 GetTikSize(cia_hdr *hdr); -u64 GetTmdOffset(cia_hdr *hdr); -u64 GetTmdSize(cia_hdr *hdr); -u64 GetContentOffset(cia_hdr *hdr); -u64 GetContentSize(cia_hdr *hdr); -u64 GetMetaOffset(cia_hdr *hdr); -u64 GetMetaSize(cia_hdr *hdr); \ No newline at end of file diff --git a/makerom/cia_build.h b/makerom/cia_build.h new file mode 100644 index 00000000..a33717d7 --- /dev/null +++ b/makerom/cia_build.h @@ -0,0 +1,107 @@ +#pragma once +#include "cia.h" + +// Enums +typedef enum +{ + CIA_NO_NCCH0 = -1, + CIA_INVALID_NCCH0 = -2, + CIA_CONFILCTING_CONTENT_IDS = -3, + CIA_BAD_VERSION = -4, +} cia_errors; + +typedef struct +{ + u8 *inFile; + u64 inFileSize; + + FILE *out; + + rsf_settings *rsf; + keys_struct *keys; + + bool verbose; + + struct{ + u8 titleId[8]; + u16 titleVersion[4]; + u8 titleKey[16]; + } common; + + + struct{ + u8 caCrlVersion; + u8 signerCrlVersion; + } cert; + + struct{ + u8 issuer[0x40]; + u8 formatVersion; + + u16 version; + + u8 ticketId[8]; + u8 deviceId[4]; + u8 licenceType; + u8 audit; + u8 eshopAccId[4]; + } tik; + + struct{ + u8 issuer[0x40]; + u8 formatVersion; + + u16 version; + + u8 titleType[4]; + u8 savedataSize[4]; + u8 privSavedataSize[4]; + u8 twlFlag; + } tmd; + + struct{ + bool IsCfa; + bool IsDlc; + bool encryptCia; + bool includeUpdateNcch; // for cci -> cia conversions + + bool keyNotFound; + + FILE **filePtrs; + u64 fileSize[CIA_MAX_CONTENT]; + + /* Misc Records */ + u16 count; + u64 offset[CIA_MAX_CONTENT]; + u64 totalSize; + + /* Content Chunk Records */ + u64 size[CIA_MAX_CONTENT]; + u16 index[CIA_MAX_CONTENT]; + u16 flags[CIA_MAX_CONTENT]; + u32 id[CIA_MAX_CONTENT]; + u8 hash[CIA_MAX_CONTENT][0x20]; + } content; + + struct{ + buffer_struct ciaHdr; + + u32 certChainOffset; + buffer_struct certChain; + + u32 tikOffset; + buffer_struct tik; + + u32 tmdOffset; + buffer_struct tmd; + + u32 metaOffset; + buffer_struct meta; + + u64 contentOffset; + buffer_struct content; + } ciaSections; +} cia_settings; + +// Public Prototypes +int build_CIA(user_settings *usrset); \ No newline at end of file diff --git a/makerom/cia_read.c b/makerom/cia_read.c deleted file mode 100644 index dbf27aa7..00000000 --- a/makerom/cia_read.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "lib.h" -#include "cia.h" - -u64 GetCiaCertOffset(cia_hdr *hdr) -{ - u64 hdrSize = u8_to_u32(hdr->hdrSize,LE); - return align(hdrSize,CIA_ALIGN_SIZE); -} - -u64 GetCiaCertSize(cia_hdr *hdr) -{ - return u8_to_u32(hdr->certChainSize,LE); -} - -u64 GetTikOffset(cia_hdr *hdr) -{ - u64 certOffset = GetCiaCertOffset(hdr); - u64 certSize = GetCiaCertSize(hdr); - return align(certOffset + certSize,CIA_ALIGN_SIZE); -} - -u64 GetTikSize(cia_hdr *hdr) -{ - return u8_to_u32(hdr->tikSize,LE); -} - -u64 GetTmdOffset(cia_hdr *hdr) -{ - u64 tikOffset = GetTikOffset(hdr); - u64 tikSize = GetTikSize(hdr); - return align(tikOffset + tikSize,CIA_ALIGN_SIZE); -} - -u64 GetTmdSize(cia_hdr *hdr) -{ - return u8_to_u32(hdr->tmdSize,LE); -} - -u64 GetContentOffset(cia_hdr *hdr) -{ - u64 tmdOffset = GetTmdOffset(hdr); - u64 tmdSize = GetTmdSize(hdr); - return align(tmdOffset + tmdSize,CIA_ALIGN_SIZE); -} - -u64 GetContentSize(cia_hdr *hdr) -{ - return u8_to_u64(hdr->contentSize,LE); -} - -u64 GetMetaOffset(cia_hdr *hdr) -{ - u64 contentOffset = GetContentOffset(hdr); - u64 contentSize = GetContentSize(hdr); - return align(contentOffset + contentSize,CIA_ALIGN_SIZE); -} - -u64 GetMetaSize(cia_hdr *hdr) -{ - return u8_to_u32(hdr->metaSize,LE); -} \ No newline at end of file diff --git a/makerom/cia_read.h b/makerom/cia_read.h new file mode 100644 index 00000000..c75090ab --- /dev/null +++ b/makerom/cia_read.h @@ -0,0 +1,21 @@ +#pragma once +#include "cia.h" + +// Cia Read Functions +bool IsCia(u8 *cia); +u64 GetCiaCertOffset(cia_hdr *hdr); +u64 GetCiaCertSize(cia_hdr *hdr); +u64 GetCiaTikOffset(cia_hdr *hdr); +u64 GetCiaTikSize(cia_hdr *hdr); +u64 GetCiaTmdOffset(cia_hdr *hdr); +u64 GetCiaTmdSize(cia_hdr *hdr); +u64 GetCiaContentOffset(cia_hdr *hdr); +u64 GetCiaContentSize(cia_hdr *hdr); +u64 GetCiaMetaOffset(cia_hdr *hdr); +u64 GetCiaMetaSize(cia_hdr *hdr); + +u8* GetCiaCert(u8 *cia); +u8* GetCiaTik(u8 *cia); +u8* GetCiaTmd(u8 *cia); +u8* GetCiaContent(u8 *cia); +u8* GetCiaMeta(u8 *cia); diff --git a/makerom/crypto.c b/makerom/crypto.c index fc482e0e..bbc977e6 100644 --- a/makerom/crypto.c +++ b/makerom/crypto.c @@ -1,6 +1,13 @@ #include "lib.h" #include "crypto.h" +bool VerifySha256(void *data, u64 size, u8 hash[32]) +{ + u8 calchash[32]; + ctr_sha(data, size, calchash, CTR_SHA_256); + return memcmp(hash,calchash,32) == 0; +} + void ctr_sha(void *data, u64 size, u8 *hash, int mode) { switch(mode){ @@ -9,21 +16,6 @@ void ctr_sha(void *data, u64 size, u8 *hash, int mode) } } -u8* AesKeyScrambler(u8 *Key, u8 *KeyX, u8 *KeyY) -{ - // Process KeyX/KeyY to get raw normal key - for(int i = 0; i < 16; i++) - Key[i] = KeyX[i] ^ ((KeyY[i] >> 2) | ((KeyY[i < 15 ? i+1 : 0] & 3) << 6)); - - const u8 SCRAMBLE_SECRET[16] = {0x51, 0xD7, 0x5D, 0xBE, 0xFD, 0x07, 0x57, 0x6A, 0x1C, 0xFC, 0x2A, 0xF0, 0x94, 0x4B, 0xD5, 0x6C}; - - // Apply Secret to get final normal key - for(int i = 0; i < 16; i++) - Key[i] = Key[i] ^ SCRAMBLE_SECRET[i]; - - return Key; -} - void ctr_add_counter(ctr_aes_context* ctx, u32 carry) { u32 counter[4]; diff --git a/makerom/crypto.h b/makerom/crypto.h index 19b5163e..3d5aaeed 100644 --- a/makerom/crypto.h +++ b/makerom/crypto.h @@ -71,9 +71,9 @@ typedef struct extern "C" { #endif // SHA +bool VerifySha256(void *data, u64 size, u8 hash[32]); void ctr_sha(void *data, u64 size, u8 *hash, int mode); // AES -u8* AesKeyScrambler(u8 *Key, u8 *KeyX, u8 *KeyY); void ctr_add_counter(ctr_aes_context* ctx, u32 carry); 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]); diff --git a/makerom/ctr_utils.c b/makerom/ctr_utils.c new file mode 100644 index 00000000..d8e9610b --- /dev/null +++ b/makerom/ctr_utils.c @@ -0,0 +1,14 @@ +#include "lib.h" + +u32 GetCtrBlockSize(u8 flag) +{ + return 1 << (flag + 9); +} + +u8 GetCtrBlockSizeFlag(u32 size) +{ + u8 ret = 0; + for(u32 tmp = size; tmp > 0x200; tmp = tmp >> 1) + ret++; + return ret; +} \ No newline at end of file diff --git a/makerom/ctr_utils.h b/makerom/ctr_utils.h new file mode 100644 index 00000000..3c92de40 --- /dev/null +++ b/makerom/ctr_utils.h @@ -0,0 +1,4 @@ +#pragma once + +u32 GetCtrBlockSize(u8 flag); +u8 GetCtrBlockSizeFlag(u32 size); \ No newline at end of file diff --git a/makerom/desc_dev_sigdata.h b/makerom/desc/dev_sigdata.h similarity index 100% rename from makerom/desc_dev_sigdata.h rename to makerom/desc/dev_sigdata.h diff --git a/makerom/desc_presets.h b/makerom/desc/presets.h similarity index 100% rename from makerom/desc_presets.h rename to makerom/desc/presets.h diff --git a/makerom/desc_prod_sigdata.h b/makerom/desc/prod_sigdata.h similarity index 100% rename from makerom/desc_prod_sigdata.h rename to makerom/desc/prod_sigdata.h diff --git a/makerom/dir.c b/makerom/dir.c index bcd57b2e..b0fb1e74 100644 --- a/makerom/dir.c +++ b/makerom/dir.c @@ -118,10 +118,10 @@ fs_entry* fs_GetEntry(fs_DIR *dp) { entry->IsDir = false; #ifdef _WIN32 - entry->size = wGetFileSize_u64(entry->fs_name); + entry->size = wGetFileSize64(entry->fs_name); entry->fp = _wfopen(entry->fs_name,L"rb"); #else - entry->size = GetFileSize_u64(entry->fs_name); + entry->size = GetFileSize64(entry->fs_name); entry->fp = fopen(entry->fs_name,"rb"); #endif } diff --git a/makerom/elf.c b/makerom/elf.c index 2ef515ca..3f5a91c0 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -1,125 +1,111 @@ #include "lib.h" -#include "ncch.h" +#include "ncch_build.h" #include "elf_hdr.h" #include "elf.h" #include "blz.h" -int ImportPlainRegionFromFile(ncch_settings *ncchset); -int ImportExeFsCodeBinaryFromFile(ncch_settings *ncchset); +int ImportPlainRegionFromFile(ncch_settings *set); +int ImportExeFsCodeBinaryFromFile(ncch_settings *set); -u32 GetPageSize(ncch_settings *ncchset); -u32 SizeToPage(u32 memorySize, ElfContext *elf); +u32 GetPageSize(ncch_settings *set); +u32 SizeToPage(u32 memorySize, elf_context *elf); -int GetBSS_SizeFromElf(ElfContext *elf, u8 *ElfFile, ncch_settings *ncchset); -int ImportPlainRegionFromElf(ElfContext *elf, u8 *ElfFile, ncch_settings *ncchset); -int CreateExeFsCode(ElfContext *elf, u8 *ElfFile, ncch_settings *ncchset); -int CreateCodeSegmentFromElf(CodeSegment *out, ElfContext *elf, u8 *ElfFile, char **Names, u32 NameNum); -ElfSegment** GetContinuousSegments(u16 *ContinuousSegmentNum, ElfContext *elf, char **Names, u32 NameNum); -ElfSegment** GetSegments(u16 *SegmentNum, ElfContext *elf, char **Names, u32 NameNum); +int GetBSSFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set); +int ImportPlainRegionFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set); +int CreateExeFsCode(elf_context *elf, u8 *elfFile, ncch_settings *set); +int CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u8 *elfFile, char **names, u32 nameNum); +elf_segment** GetContinuousSegments(u16 *ContinuousSegmentNum, elf_context *elf, char **names, u32 nameNum); +elf_segment** GetSegments(u16 *SegmentNum, elf_context *elf, char **names, u32 nameNum); // ELF Functions -int GetElfContext(ElfContext *elf, u8 *ElfFile); -int GetElfSectionEntries(ElfContext *elf, u8 *ElfFile); -int GetElfProgramEntries(ElfContext *elf, u8 *ElfFile); -#ifdef DEBUG -void PrintElfContext(ElfContext *elf, u8 *ElfFile); -#endif -int ReadElfHdr(ElfContext *elf, u8 *ElfFile); +int GetElfContext(elf_context *elf, u8 *elfFile); +int GetElfSectionEntries(elf_context *elf, u8 *elfFile); +int GetElfProgramEntries(elf_context *elf, u8 *elfFile); +void PrintElfContext(elf_context *elf, u8 *elfFile); +int ReadElfHdr(elf_context *elf, u8 *elfFile); -int CreateElfSegments(ElfContext *elf, u8 *ElfFile); -bool IsIgnoreSection(ElfSectionEntry info); +int CreateElfSegments(elf_context *elf, u8 *elfFile); +bool IsIgnoreSection(elf_section_entry info); /* ELF Section Entry Functions */ -u8* GetELFSectionHeader(u16 Index, ElfContext *elf, u8 *ElfFile); -u8* GetELFSectionEntry(u16 Index, ElfContext *elf, u8 *ElfFile); -char* GetELFSectionEntryName(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFSectionEntryType(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFSectionEntryFlags(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFSectionEntryAddress(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFSectionEntryFileOffset(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFSectionEntrySize(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFSectionEntryAlignment(u16 Index, ElfContext *elf, u8 *ElfFile); - -u16 GetElfSectionIndexFromName(char *Name, ElfContext *elf, u8 *ElfFile); - -bool IsBss(ElfSectionEntry *Section); -bool IsData(ElfSectionEntry *Section); -bool IsRO(ElfSectionEntry *Section); -bool IsText(ElfSectionEntry *Section); +u8* GetELFSectionHeader(u16 index, elf_context *elf, u8 *elfFile); +u8* GetELFSectionEntry(u16 index, elf_context *elf, u8 *elfFile); +char* GetELFSectionEntryName(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFSectionEntryType(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFSectionEntryFlags(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFSectionEntryAddress(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFSectionEntryFileOffset(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFSectionEntrySize(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFSectionEntryAlignment(u16 index, elf_context *elf, u8 *elfFile); + +u16 GetElfSectionIndexFromName(char *name, elf_context *elf, u8 *elfFile); + +bool IsBss(elf_section_entry *section); +bool IsData(elf_section_entry *section); +bool IsRoData(elf_section_entry *section); +bool IsText(elf_section_entry *section); /* ELF Program Entry Functions */ -u8* GetELFProgramHeader(u16 Index, ElfContext *elf, u8 *ElfFile); -u8* GetELFProgramEntry(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFProgramEntryType(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFProgramEntryFlags(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFProgramEntryFileSize(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFProgramEntryFileOffset(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFProgramEntryMemorySize(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFProgramEntryVAddress(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFProgramEntryPAddress(u16 Index, ElfContext *elf, u8 *ElfFile); -u64 GetELFProgramEntryAlignment(u16 Index, ElfContext *elf, u8 *ElfFile); +u8* GetELFProgramHeader(u16 index, elf_context *elf, u8 *elfFile); +u8* GetELFProgramEntry(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFProgramEntryType(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFProgramEntryFlags(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFProgramEntryFileSize(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFProgramEntryFileOffset(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFProgramEntryMemorySize(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFProgramEntryVAddress(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFProgramEntryPAddress(u16 index, elf_context *elf, u8 *elfFile); +u64 GetELFProgramEntryAlignment(u16 index, elf_context *elf, u8 *elfFile); -int BuildExeFsCode(ncch_settings *ncchset) +int BuildExeFsCode(ncch_settings *set) { int result = 0; - if(ncchset->options.IsCfa) + if(set->options.IsCfa) return result; - if(ncchset->componentFilePtrs.plainregion){ // Import PlainRegion from file - result = ImportPlainRegionFromFile(ncchset); + if(set->componentFilePtrs.plainregion){ // Import PlainRegion from file + result = ImportPlainRegionFromFile(set); if(result) return result; } - if(!ncchset->options.IsBuildingCodeSection){ // Import ExeFs Code from file and return - result = ImportExeFsCodeBinaryFromFile(ncchset); + if(!set->options.IsBuildingCodeSection){ // Import ExeFs Code from file and return + result = ImportExeFsCodeBinaryFromFile(set); return result; } -#ifdef DEBUG - printf("[DEBUG] Import ELF\n"); -#endif /* Import ELF */ - u8 *ElfFile = malloc(ncchset->componentFilePtrs.elfSize); - if(!ElfFile) { + u8 *elfFile = malloc(set->componentFilePtrs.elfSize); + if(!elfFile) { fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR; } - ReadFile_64(ElfFile,ncchset->componentFilePtrs.elfSize,0,ncchset->componentFilePtrs.elf); + ReadFile64(elfFile,set->componentFilePtrs.elfSize,0,set->componentFilePtrs.elf); -#ifdef DEBUG - printf("[DEBUG] Create ELF Context\n"); -#endif /* Create ELF Context */ - ElfContext *elf = calloc(1,sizeof(ElfContext)); + elf_context *elf = calloc(1,sizeof(elf_context)); if(!elf) { fprintf(stderr,"[ELF ERROR] Not enough memory\n"); - free(ElfFile); + free(elfFile); return MEM_ERROR; } - result = GetElfContext(elf,ElfFile); + result = GetElfContext(elf,elfFile); if(result) goto finish; /* Setting Page Size */ - elf->pageSize = GetPageSize(ncchset); + elf->pageSize = GetPageSize(set); - if(!ncchset->componentFilePtrs.plainregion){ - result = ImportPlainRegionFromElf(elf,ElfFile,ncchset); + if(!set->componentFilePtrs.plainregion){ + result = ImportPlainRegionFromElf(elf,elfFile,set); if(result) goto finish; } -#ifdef DEBUG - PrintElfContext(elf,ElfFile); -#endif + if(set->options.verbose) + PrintElfContext(elf,elfFile); -#ifdef DEBUG - printf("[DEBUG] Create ExeFs Code\n"); -#endif - result = CreateExeFsCode(elf,ElfFile,ncchset); + result = CreateExeFsCode(elf,elfFile,set); if(result) goto finish; -#ifdef DEBUG - printf("[DEBUG] Get BSS Size\n"); -#endif - result = GetBSS_SizeFromElf(elf,ElfFile,ncchset); + + result = GetBSSFromElf(elf,elfFile,set); if(result) goto finish; finish: @@ -131,19 +117,9 @@ int BuildExeFsCode(ncch_settings *ncchset) else if(result == NOT_FIND_CODE_SECTIONS) fprintf(stderr,"[ELF ERROR] Failed to retrieve code sections from ELF\n"); else fprintf(stderr,"[ELF ERROR] Failed to process ELF file (%d)\n",result); } -#ifdef DEBUG - printf("[DEBUG] Free Segment Header/Sections\n"); -#endif - for(int i = 0; i < elf->activeSegments; i++){ -#ifdef DEBUG - printf("[DEBUG] %d\n",i); -#endif + for(int i = 0; i < elf->activeSegments; i++) free(elf->segments[i].sections); - } -#ifdef DEBUG - printf("[DEBUG] Free others\n"); -#endif - free(ElfFile); + free(elfFile); free(elf->sections); free(elf->programHeaders); free(elf->segments); @@ -151,168 +127,168 @@ int BuildExeFsCode(ncch_settings *ncchset) return result; } -int ImportPlainRegionFromFile(ncch_settings *ncchset) +int ImportPlainRegionFromFile(ncch_settings *set) { - ncchset->sections.plainRegion.size = align(ncchset->componentFilePtrs.plainregionSize,ncchset->options.mediaSize); - ncchset->sections.plainRegion.buffer = malloc(ncchset->sections.plainRegion.size); - if(!ncchset->sections.plainRegion.buffer) {fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR;} - ReadFile_64(ncchset->sections.plainRegion.buffer,ncchset->componentFilePtrs.plainregionSize,0,ncchset->componentFilePtrs.plainregion); + set->sections.plainRegion.size = align(set->componentFilePtrs.plainregionSize,set->options.blockSize); + set->sections.plainRegion.buffer = malloc(set->sections.plainRegion.size); + if(!set->sections.plainRegion.buffer) {fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR;} + ReadFile64(set->sections.plainRegion.buffer,set->componentFilePtrs.plainregionSize,0,set->componentFilePtrs.plainregion); return 0; } -int ImportExeFsCodeBinaryFromFile(ncch_settings *ncchset) +int ImportExeFsCodeBinaryFromFile(ncch_settings *set) { - u32 size = ncchset->componentFilePtrs.codeSize; + u32 size = set->componentFilePtrs.codeSize; u8 *buffer = malloc(size); if(!buffer) {fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR;} - ReadFile_64(buffer,size,0,ncchset->componentFilePtrs.code); + ReadFile64(buffer,size,0,set->componentFilePtrs.code); - ncchset->exefsSections.code.size = ncchset->componentFilePtrs.codeSize; - ncchset->exefsSections.code.buffer = malloc(ncchset->exefsSections.code.size); - if(!ncchset->exefsSections.code.buffer) {fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR;} - ReadFile_64(ncchset->exefsSections.code.buffer,ncchset->exefsSections.code.size,0,ncchset->componentFilePtrs.code); - if(ncchset->options.CompressCode){ + set->exefsSections.code.size = set->componentFilePtrs.codeSize; + set->exefsSections.code.buffer = malloc(set->exefsSections.code.size); + if(!set->exefsSections.code.buffer) {fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR;} + ReadFile64(set->exefsSections.code.buffer,set->exefsSections.code.size,0,set->componentFilePtrs.code); + if(set->options.CompressCode){ u32 new_len; - ncchset->exefsSections.code.buffer = BLZ_Code(buffer,size,&new_len,BLZ_NORMAL); - ncchset->exefsSections.code.size = new_len; + set->exefsSections.code.buffer = BLZ_Code(buffer,size,&new_len,BLZ_NORMAL); + set->exefsSections.code.size = new_len; free(buffer); } else{ - ncchset->exefsSections.code.size = size; - ncchset->exefsSections.code.buffer = buffer; + set->exefsSections.code.size = size; + set->exefsSections.code.buffer = buffer; } return 0; } -u32 GetPageSize(ncch_settings *ncchset) +u32 GetPageSize(ncch_settings *set) { - if(ncchset->rsfSet->Option.PageSize) - return strtoul(ncchset->rsfSet->Option.PageSize,NULL,10); + if(set->rsfSet->Option.PageSize) + return strtoul(set->rsfSet->Option.PageSize,NULL,10); return 0x1000; } -u32 SizeToPage(u32 memorySize, ElfContext *elf) +u32 SizeToPage(u32 memorySize, elf_context *elf) { return align(memorySize,elf->pageSize)/elf->pageSize; } -int GetBSS_SizeFromElf(ElfContext *elf, u8 *ElfFile, ncch_settings *ncchset) +int GetBSSFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set) { for(int i = 0; i < elf->sectionTableEntryCount; i++){ if(IsBss(&elf->sections[i])) { - ncchset->codeDetails.bssSize = elf->sections[i].size; + set->codeDetails.bssSize = elf->sections[i].size; return 0; } } return NOT_FIND_BSS_SIZE; } -int ImportPlainRegionFromElf(ElfContext *elf, u8 *ElfFile, ncch_settings *ncchset) // Doesn't work same as N makerom +int ImportPlainRegionFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set) // Doesn't work same as N makerom { - if(!ncchset->rsfSet->PlainRegionNum) return 0; - u16 *Index = calloc(ncchset->rsfSet->PlainRegionNum,sizeof(u16)); + if(!set->rsfSet->PlainRegionNum) return 0; + u16 *index = calloc(set->rsfSet->PlainRegionNum,sizeof(u16)); - /* Getting Index Values for each section */ - for(int i = 0; i < ncchset->rsfSet->PlainRegionNum; i++){ - Index[i] = GetElfSectionIndexFromName(ncchset->rsfSet->PlainRegion[i],elf,ElfFile); + /* Getting index Values for each section */ + for(int i = 0; i < set->rsfSet->PlainRegionNum; i++){ + index[i] = GetElfSectionIndexFromName(set->rsfSet->PlainRegion[i],elf,elfFile); } // Eliminating Duplicated Sections - for(int i = ncchset->rsfSet->PlainRegionNum - 1; i >= 0; i--){ + for(int i = set->rsfSet->PlainRegionNum - 1; i >= 0; i--){ for(int j = i-1; j >= 0; j--){ - if(Index[i] == Index[j]) Index[i] = 0; + if(index[i] == index[j]) index[i] = 0; } } /* Calculating Total Size of Data */ - u64 TotalSize = 0; - for(int i = 0; i < ncchset->rsfSet->PlainRegionNum; i++){ - TotalSize += elf->sections[Index[i]].size; + u64 totalSize = 0; + for(int i = 0; i < set->rsfSet->PlainRegionNum; i++){ + totalSize += elf->sections[index[i]].size; } /* Creating Output Buffer */ - ncchset->sections.plainRegion.size = align(TotalSize,ncchset->options.mediaSize); - ncchset->sections.plainRegion.buffer = malloc(ncchset->sections.plainRegion.size); - if(!ncchset->sections.plainRegion.buffer) {fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR;} - memset(ncchset->sections.plainRegion.buffer,0,ncchset->sections.plainRegion.size); + set->sections.plainRegion.size = align(totalSize,set->options.blockSize); + set->sections.plainRegion.buffer = malloc(set->sections.plainRegion.size); + if(!set->sections.plainRegion.buffer) {fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR;} + memset(set->sections.plainRegion.buffer,0,set->sections.plainRegion.size); /* Storing Sections */ u64 pos = 0; - for(int i = 0; i < ncchset->rsfSet->PlainRegionNum; i++){ - memcpy((ncchset->sections.plainRegion.buffer+pos),elf->sections[Index[i]].ptr,elf->sections[Index[i]].size); - pos += elf->sections[Index[i]].size; + for(int i = 0; i < set->rsfSet->PlainRegionNum; i++){ + memcpy((set->sections.plainRegion.buffer+pos),elf->sections[index[i]].ptr,elf->sections[index[i]].size); + pos += elf->sections[index[i]].size; } return 0; } -int CreateExeFsCode(ElfContext *elf, u8 *ElfFile, ncch_settings *ncchset) +int CreateExeFsCode(elf_context *elf, u8 *elfFile, ncch_settings *set) { /* Getting Code Segments */ - CodeSegment Text; - memset(&Text,0,sizeof(CodeSegment)); - CodeSegment RO; - memset(&RO,0,sizeof(CodeSegment)); - CodeSegment Data; - memset(&Data,0,sizeof(CodeSegment)); - - int result = CreateCodeSegmentFromElf(&Text,elf,ElfFile,ncchset->rsfSet->ExeFs.Text,ncchset->rsfSet->ExeFs.TextNum); + code_segment text; + memset(&text,0,sizeof(code_segment)); + code_segment rodata; + memset(&rodata,0,sizeof(code_segment)); + code_segment rwdata; + memset(&rwdata,0,sizeof(code_segment)); + + int result = CreateCodeSegmentFromElf(&text,elf,elfFile,set->rsfSet->ExeFs.Text,set->rsfSet->ExeFs.TextNum); if(result) return result; - result = CreateCodeSegmentFromElf(&RO,elf,ElfFile,ncchset->rsfSet->ExeFs.ReadOnly,ncchset->rsfSet->ExeFs.ReadOnlyNum); + result = CreateCodeSegmentFromElf(&rodata,elf,elfFile,set->rsfSet->ExeFs.ReadOnly,set->rsfSet->ExeFs.ReadOnlyNum); if(result) return result; - result = CreateCodeSegmentFromElf(&Data,elf,ElfFile,ncchset->rsfSet->ExeFs.ReadWrite,ncchset->rsfSet->ExeFs.ReadWriteNum); + result = CreateCodeSegmentFromElf(&rwdata,elf,elfFile,set->rsfSet->ExeFs.ReadWrite,set->rsfSet->ExeFs.ReadWriteNum); if(result) return result; /* Allocating Buffer for ExeFs Code */ - u32 size = (Text.maxPageNum + RO.maxPageNum + Data.maxPageNum)*elf->pageSize; + u32 size = (text.maxPageNum + rodata.maxPageNum + rwdata.maxPageNum)*elf->pageSize; u8 *code = malloc(size); /* Writing Code into Buffer */ - u8 *TextPos = (code + 0); - u8 *ROPos = (code + Text.maxPageNum*elf->pageSize); - u8 *DataPos = (code + (Text.maxPageNum + RO.maxPageNum)*elf->pageSize); - if(Text.size) memcpy(TextPos,Text.data,Text.size); - if(RO.size) memcpy(ROPos,RO.data,RO.size); - if(Data.size) memcpy(DataPos,Data.data,Data.size); + u8 *textPos = (code + 0); + u8 *rodataPos = (code + text.maxPageNum*elf->pageSize); + u8 *rwdataPos = (code + (text.maxPageNum + rodata.maxPageNum)*elf->pageSize); + if(text.size) memcpy(textPos,text.data,text.size); + if(rodata.size) memcpy(rodataPos,rodata.data,rodata.size); + if(rwdata.size) memcpy(rwdataPos,rwdata.data,rwdata.size); /* Compressing If needed */ - if(ncchset->options.CompressCode){ + if(set->options.CompressCode){ u32 new_len; - ncchset->exefsSections.code.buffer = BLZ_Code(code,size,&new_len,BLZ_NORMAL); - ncchset->exefsSections.code.size = new_len; + set->exefsSections.code.buffer = BLZ_Code(code,size,&new_len,BLZ_NORMAL); + set->exefsSections.code.size = new_len; free(code); } else{ - ncchset->exefsSections.code.size = size; - ncchset->exefsSections.code.buffer = code; + set->exefsSections.code.size = size; + set->exefsSections.code.buffer = code; } - /* Setting CodeSegment Data and freeing original buffers */ - ncchset->codeDetails.textAddress = Text.address; - ncchset->codeDetails.textMaxPages = Text.maxPageNum; - ncchset->codeDetails.textSize = Text.size; - if(Text.size) free(Text.data); + /* Setting code_segment rwdata and freeing original buffers */ + set->codeDetails.textAddress = text.address; + set->codeDetails.textMaxPages = text.maxPageNum; + set->codeDetails.textSize = text.size; + if(text.size) free(text.data); - ncchset->codeDetails.roAddress = RO.address; - ncchset->codeDetails.roMaxPages = RO.maxPageNum; - ncchset->codeDetails.roSize = RO.size; - if(RO.size) free(RO.data); + set->codeDetails.roAddress = rodata.address; + set->codeDetails.roMaxPages = rodata.maxPageNum; + set->codeDetails.roSize = rodata.size; + if(rodata.size) free(rodata.data); - ncchset->codeDetails.rwAddress = Data.address; - ncchset->codeDetails.rwMaxPages = Data.maxPageNum; - ncchset->codeDetails.rwSize = Data.size; - if(Data.size) free(Data.data); + set->codeDetails.rwAddress = rwdata.address; + set->codeDetails.rwMaxPages = rwdata.maxPageNum; + set->codeDetails.rwSize = rwdata.size; + if(rwdata.size) free(rwdata.data); /* Return */ return 0; } -int CreateCodeSegmentFromElf(CodeSegment *out, ElfContext *elf, u8 *ElfFile, char **Names, u32 NameNum) +int CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u8 *elfFile, char **names, u32 nameNum) { u16 ContinuousSegmentNum = 0; - memset(out,0,sizeof(CodeSegment)); - ElfSegment **ContinuousSegments = GetContinuousSegments(&ContinuousSegmentNum,elf,Names,NameNum); + memset(out,0,sizeof(code_segment)); + elf_segment **ContinuousSegments = GetContinuousSegments(&ContinuousSegmentNum,elf,names,nameNum); if (ContinuousSegments == NULL){ if(!ContinuousSegmentNum){// Nothing Was Found //printf("Nothing was found\n"); @@ -366,17 +342,17 @@ int CreateCodeSegmentFromElf(CodeSegment *out, ElfContext *elf, u8 *ElfFile, cha */ //u32 size = 0; for (int j = 0; j < ContinuousSegments[i]->sectionNum; j++){ - ElfSectionEntry *Section = &ContinuousSegments[i]->sections[j]; - if (!IsBss(Section)){ - u8 *pos = (out->data + (Section->address - ContinuousSegments[i]->vAddr)); - memcpy(pos,Section->ptr,Section->size); - //size += Section->size; + elf_section_entry *section = &ContinuousSegments[i]->sections[j]; + if (!IsBss(section)){ + u8 *pos = (out->data + (section->address - ContinuousSegments[i]->vAddr)); + memcpy(pos,section->ptr,section->size); + //size += section->size; } //else if (j == (ContinuousSegments[i]->sectionNum-1)) - //memorySize -= Section->size; + //memorySize -= section->size; //'else - //size += Section->size; + //size += section->size; } } @@ -385,10 +361,10 @@ int CreateCodeSegmentFromElf(CodeSegment *out, ElfContext *elf, u8 *ElfFile, cha } -ElfSegment** GetContinuousSegments(u16 *ContinuousSegmentNum, ElfContext *elf, char **Names, u32 NameNum) +elf_segment** GetContinuousSegments(u16 *ContinuousSegmentNum, elf_context *elf, char **names, u32 nameNum) { u16 SegmentNum = 0; - ElfSegment **Segments = GetSegments(&SegmentNum, elf, Names, NameNum); + elf_segment **Segments = GetSegments(&SegmentNum, elf, names, nameNum); if (Segments == NULL || SegmentNum == 0){ // No Segments for the names were found //printf("Not Found Segment\n"); return NULL; @@ -413,18 +389,18 @@ ElfSegment** GetContinuousSegments(u16 *ContinuousSegmentNum, ElfContext *elf, c } -ElfSegment** GetSegments(u16 *SegmentNum, ElfContext *elf, char **Names, u32 NameNum) +elf_segment** GetSegments(u16 *SegmentNum, elf_context *elf, char **names, u32 nameNum) { - if (Names == NULL) + if (names == NULL) { return NULL; } - ElfSegment **Segments = calloc(NameNum,sizeof(ElfSegment*)); - *SegmentNum = 0; // There can be a max of NameNum Segments, however, they might not all exist - for (int i = 0; i < NameNum; i++){ + elf_segment **Segments = calloc(nameNum,sizeof(elf_segment*)); + *SegmentNum = 0; // There can be a max of nameNum Segments, however, they might not all exist + for (int i = 0; i < nameNum; i++){ for(int j = 0; j < elf->activeSegments; j++){ - if(strcmp(Names[i],elf->segments[j].name) == 0){ // If there is a match, store Segment data pointer & increment index + if(strcmp(names[i],elf->segments[j].name) == 0){ // If there is a match, store Segment data pointer & increment index Segments[*SegmentNum] = &elf->segments[j]; *SegmentNum = *SegmentNum + 1; } @@ -435,95 +411,93 @@ ElfSegment** GetSegments(u16 *SegmentNum, ElfContext *elf, char **Names, u32 Nam // ELF Functions -int GetElfContext(ElfContext *elf, u8 *ElfFile) +int GetElfContext(elf_context *elf, u8 *elfFile) { - if(u8_to_u32(ElfFile,BE) != ELF_MAGIC) return NOT_ELF_FILE; + if(u8_to_u32(elfFile,BE) != ELF_MAGIC) return NOT_ELF_FILE; - elf->Is64bit = (ElfFile[4] == elf_64_bit); - elf->IsLittleEndian = (ElfFile[5] == elf_little_endian); + elf->Is64bit = (elfFile[4] == elf_64_bit); + elf->IsLittleEndian = (elfFile[5] == elf_little_endian); - int result = ReadElfHdr(elf,ElfFile); + int result = ReadElfHdr(elf,elfFile); if(result) return result; - result = GetElfSectionEntries(elf,ElfFile); + result = GetElfSectionEntries(elf,elfFile); if(result) return result; - result = GetElfProgramEntries(elf,ElfFile); + result = GetElfProgramEntries(elf,elfFile); if(result) return result; - result = CreateElfSegments(elf,ElfFile); + result = CreateElfSegments(elf,elfFile); if(result) return result; return 0; } -int GetElfSectionEntries(ElfContext *elf, u8 *ElfFile) +int GetElfSectionEntries(elf_context *elf, u8 *elfFile) { - elf->sections = calloc(elf->sectionTableEntryCount,sizeof(ElfSectionEntry)); + elf->sections = calloc(elf->sectionTableEntryCount,sizeof(elf_section_entry)); if(!elf->sections) { fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR; } for(int i = 0; i < elf->sectionTableEntryCount; i++){ - elf->sections[i].name = GetELFSectionEntryName(i,elf,ElfFile); - elf->sections[i].type = GetELFSectionEntryType(i,elf,ElfFile); - elf->sections[i].flags = GetELFSectionEntryFlags(i,elf,ElfFile); - elf->sections[i].ptr = GetELFSectionEntry(i,elf,ElfFile); - elf->sections[i].offsetInFile = GetELFSectionEntryFileOffset(i,elf,ElfFile); - elf->sections[i].size = GetELFSectionEntrySize(i,elf,ElfFile); - elf->sections[i].address = GetELFSectionEntryAddress(i,elf,ElfFile); - elf->sections[i].alignment = GetELFSectionEntryAlignment(i,elf,ElfFile); + elf->sections[i].name = GetELFSectionEntryName(i,elf,elfFile); + elf->sections[i].type = GetELFSectionEntryType(i,elf,elfFile); + elf->sections[i].flags = GetELFSectionEntryFlags(i,elf,elfFile); + elf->sections[i].ptr = GetELFSectionEntry(i,elf,elfFile); + elf->sections[i].offsetInFile = GetELFSectionEntryFileOffset(i,elf,elfFile); + elf->sections[i].size = GetELFSectionEntrySize(i,elf,elfFile); + elf->sections[i].address = GetELFSectionEntryAddress(i,elf,elfFile); + elf->sections[i].alignment = GetELFSectionEntryAlignment(i,elf,elfFile); } return 0; } -int GetElfProgramEntries(ElfContext *elf, u8 *ElfFile) +int GetElfProgramEntries(elf_context *elf, u8 *elfFile) { - elf->programHeaders = calloc(elf->programTableEntryCount,sizeof(ElfProgramEntry)); + elf->programHeaders = calloc(elf->programTableEntryCount,sizeof(elf_program_entry)); if(!elf->programHeaders) { fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR; } for(int i = 0; i < elf->programTableEntryCount; i++){ - elf->programHeaders[i].type = GetELFProgramEntryType(i,elf,ElfFile); - elf->programHeaders[i].flags = GetELFProgramEntryFlags(i,elf,ElfFile); - elf->programHeaders[i].ptr = GetELFProgramEntry(i,elf,ElfFile); - elf->programHeaders[i].offsetInFile = GetELFProgramEntryFileOffset(i,elf,ElfFile); - elf->programHeaders[i].sizeInFile = GetELFProgramEntryFileSize(i,elf,ElfFile); - elf->programHeaders[i].physicalAddress = GetELFProgramEntryPAddress(i,elf,ElfFile); - elf->programHeaders[i].virtualAddress = GetELFProgramEntryVAddress(i,elf,ElfFile); - elf->programHeaders[i].sizeInMemory = GetELFProgramEntryMemorySize(i,elf,ElfFile); - elf->programHeaders[i].alignment = GetELFProgramEntryAlignment(i,elf,ElfFile); + elf->programHeaders[i].type = GetELFProgramEntryType(i,elf,elfFile); + elf->programHeaders[i].flags = GetELFProgramEntryFlags(i,elf,elfFile); + elf->programHeaders[i].ptr = GetELFProgramEntry(i,elf,elfFile); + elf->programHeaders[i].offsetInFile = GetELFProgramEntryFileOffset(i,elf,elfFile); + elf->programHeaders[i].sizeInFile = GetELFProgramEntryFileSize(i,elf,elfFile); + elf->programHeaders[i].physicalAddress = GetELFProgramEntryPAddress(i,elf,elfFile); + elf->programHeaders[i].virtualAddress = GetELFProgramEntryVAddress(i,elf,elfFile); + elf->programHeaders[i].sizeInMemory = GetELFProgramEntryMemorySize(i,elf,elfFile); + elf->programHeaders[i].alignment = GetELFProgramEntryAlignment(i,elf,elfFile); } return 0; } -#ifdef DEBUG -void PrintElfContext(ElfContext *elf, u8 *ElfFile) +void PrintElfContext(elf_context *elf, u8 *elfFile) { - printf("[+] Basic Details\n"); + printf("[ELF] Basic Details\n"); printf(" Class: %s\n",elf->Is64bit ? "64-bit" : "32-bit"); printf(" Data: %s\n",elf->IsLittleEndian ? "Little Endian" : "Big Endian"); - printf("\n[+] Program Table Data\n"); - printf(" Offset: 0x%lx\n",elf->programTableOffset); + printf("[ELF] Program Table Data\n"); + printf(" Offset: 0x%"PRIX64"\n",elf->programTableOffset); printf(" Size: 0x%x\n",elf->programTableEntrySize); printf(" Count: 0x%x\n",elf->programTableEntryCount); - printf("\n[+] Section Table Data\n"); - printf(" Offset: 0x%lx\n",elf->sectionTableOffset); + printf("[ELF] Section Table Data\n"); + printf(" Offset: 0x%"PRIX64"\n",elf->sectionTableOffset); printf(" Size: 0x%x\n",elf->sectionTableEntrySize); printf(" Count: 0x%x\n",elf->sectionTableEntryCount); - printf(" Lable Index: 0x%x\n",elf->sectionHeaderNameEntryIndex); + printf(" Label index: 0x%x\n",elf->sectionHeaderNameEntryIndex); for(int i = 0; i < elf->activeSegments; i++){ printf(" Segment [%d][%s]\n",i,elf->segments[i].name); - printf(" > Size : 0x%x\n",elf->segments[i].header->sizeInFile); - printf(" > Address : 0x%x\n",elf->segments[i].vAddr); + printf(" > Size : 0x%"PRIX64"\n",elf->segments[i].header->sizeInFile); + printf(" > Address : 0x%"PRIX64"\n",elf->segments[i].vAddr); printf(" > Sections : %d\n",elf->segments[i].sectionNum); - for(int j = 0; j < elf->segments[i].sectionNum; j++){ + for(int j = 0; j < elf->segments[i].sectionNum; j++) printf(" > Section [%d][%s]\n",j,elf->segments[i].sections[j].name); - } /* char outpath[100]; @@ -538,12 +512,11 @@ void PrintElfContext(ElfContext *elf, u8 *ElfFile) } } -#endif -int ReadElfHdr(ElfContext *elf, u8 *ElfFile) +int ReadElfHdr(elf_context *elf, u8 *elfFile) { if(elf->Is64bit){ - elf_64_hdr *hdr = (elf_64_hdr*)ElfFile; + elf_64_hdr *hdr = (elf_64_hdr*)elfFile; u16 Architecture = u8_to_u16(hdr->targetArchitecture,elf->IsLittleEndian); u16 Type = u8_to_u16(hdr->type,elf->IsLittleEndian); @@ -561,7 +534,7 @@ int ReadElfHdr(ElfContext *elf, u8 *ElfFile) elf->sectionHeaderNameEntryIndex = u8_to_u16(hdr->sectionHeaderNameEntryIndex,elf->IsLittleEndian); } else{ - elf_32_hdr *hdr = (elf_32_hdr*)ElfFile; + elf_32_hdr *hdr = (elf_32_hdr*)elfFile; u16 Architecture = u8_to_u16(hdr->targetArchitecture,elf->IsLittleEndian); u16 Type = u8_to_u16(hdr->type,elf->IsLittleEndian); @@ -583,129 +556,129 @@ int ReadElfHdr(ElfContext *elf, u8 *ElfFile) /* Section Hdr Functions */ -u8* GetELFSectionHeader(u16 Index, ElfContext *elf, u8 *ElfFile) +u8* GetELFSectionHeader(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->sectionTableEntryCount) return NULL; + if(index >= elf->sectionTableEntryCount) return NULL; - return (ElfFile + elf->sectionTableOffset + elf->sectionTableEntrySize*Index); + return (elfFile + elf->sectionTableOffset + elf->sectionTableEntrySize*index); } -u8* GetELFSectionEntry(u16 Index, ElfContext *elf, u8 *ElfFile) +u8* GetELFSectionEntry(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->sectionTableEntryCount) return NULL; + if(index >= elf->sectionTableEntryCount) return NULL; - return (u8*) (ElfFile + GetELFSectionEntryFileOffset(Index,elf,ElfFile)); + return (u8*) (elfFile + GetELFSectionEntryFileOffset(index,elf,elfFile)); } -char* GetELFSectionEntryName(u16 Index, ElfContext *elf, u8 *ElfFile) +char* GetELFSectionEntryName(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->sectionTableEntryCount) return 0; + if(index >= elf->sectionTableEntryCount) return 0; u64 NameIndex = 0; if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); NameIndex = u8_to_u64(shdr->sh_name,elf->IsLittleEndian); } else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); NameIndex = u8_to_u32(shdr->sh_name,elf->IsLittleEndian); } - u8 *NameTable = GetELFSectionEntry(elf->sectionHeaderNameEntryIndex,elf,ElfFile); + u8 *NameTable = GetELFSectionEntry(elf->sectionHeaderNameEntryIndex,elf,elfFile); return (char*)(NameTable+NameIndex); } -u64 GetELFSectionEntryType(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFSectionEntryType(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->sectionTableEntryCount) return 0; + if(index >= elf->sectionTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); return u8_to_u64(shdr->sh_type,elf->IsLittleEndian); } else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); return u8_to_u32(shdr->sh_type,elf->IsLittleEndian); } return 0; } -u64 GetELFSectionEntryFlags(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFSectionEntryFlags(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->sectionTableEntryCount) return 0; + if(index >= elf->sectionTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); return u8_to_u64(shdr->sh_flags,elf->IsLittleEndian); } else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); return u8_to_u32(shdr->sh_flags,elf->IsLittleEndian); } return 0; } -u64 GetELFSectionEntryAddress(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFSectionEntryAddress(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->sectionTableEntryCount) return 0; + if(index >= elf->sectionTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); return u8_to_u64(shdr->sh_addr,elf->IsLittleEndian); } else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); return u8_to_u32(shdr->sh_addr,elf->IsLittleEndian); } return 0; } -u64 GetELFSectionEntryFileOffset(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFSectionEntryFileOffset(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->sectionTableEntryCount) return 0; + if(index >= elf->sectionTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); return u8_to_u64(shdr->sh_offset,elf->IsLittleEndian); } else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); return u8_to_u32(shdr->sh_offset,elf->IsLittleEndian); } return 0; } -u64 GetELFSectionEntrySize(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFSectionEntrySize(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->sectionTableEntryCount) return 0; + if(index >= elf->sectionTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); return u8_to_u64(shdr->sh_size,elf->IsLittleEndian); } else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); return u8_to_u32(shdr->sh_size,elf->IsLittleEndian); } return 0; } -u64 GetELFSectionEntryAlignment(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFSectionEntryAlignment(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->sectionTableEntryCount) return 0; + if(index >= elf->sectionTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); return u8_to_u64(shdr->sh_addralign,elf->IsLittleEndian); } else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(Index,elf,ElfFile); + elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); return u8_to_u32(shdr->sh_addralign,elf->IsLittleEndian); } @@ -713,166 +686,166 @@ u64 GetELFSectionEntryAlignment(u16 Index, ElfContext *elf, u8 *ElfFile) } -u16 GetElfSectionIndexFromName(char *Name, ElfContext *elf, u8 *ElfFile) +u16 GetElfSectionIndexFromName(char *name, elf_context *elf, u8 *elfFile) { for(int i = 0; i < elf->sectionTableEntryCount; i++){ - if(strcmp(Name,elf->sections[i].name) == 0) return i; + if(strcmp(name,elf->sections[i].name) == 0) return i; } return 0; // Assuming 0 is always empty } -bool IsBss(ElfSectionEntry *Section) +bool IsBss(elf_section_entry *section) { - if(Section->type == 8 && Section->flags == 3) + if(section->type == 8 && section->flags == 3) return true; return false; } -bool IsData(ElfSectionEntry *Section) +bool IsData(elf_section_entry *section) { - if(Section->type == 1 && Section->flags == 3) + if(section->type == 1 && section->flags == 3) return true; return false; } -bool IsRO(ElfSectionEntry *Section) +bool IsRoData(elf_section_entry *section) { - if(Section->type == 1 && Section->flags == 2) + if(section->type == 1 && section->flags == 2) return true; return false; } -bool IsText(ElfSectionEntry *Section) +bool IsText(elf_section_entry *section) { - if(Section->type == 1 && Section->flags == 6) + if(section->type == 1 && section->flags == 6) return true; return false; } /* ProgramHeader Functions */ -u8* GetELFProgramHeader(u16 Index, ElfContext *elf, u8 *ElfFile) +u8* GetELFProgramHeader(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->programTableEntryCount) return NULL; + if(index >= elf->programTableEntryCount) return NULL; - return (ElfFile + elf->programTableOffset + elf->programTableEntrySize*Index); + return (elfFile + elf->programTableOffset + elf->programTableEntrySize*index); } -u8* GetELFProgramEntry(u16 Index, ElfContext *elf, u8 *ElfFile) +u8* GetELFProgramEntry(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->programTableEntryCount) return NULL; + if(index >= elf->programTableEntryCount) return NULL; - return (u8*) (ElfFile + GetELFProgramEntryFileOffset(Index,elf,ElfFile)); + return (u8*) (elfFile + GetELFProgramEntryFileOffset(index,elf,elfFile)); return NULL; } -u64 GetELFProgramEntryType(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFProgramEntryType(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->programTableEntryCount) return 0; + if(index >= elf->programTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u64(phdr->p_type,elf->IsLittleEndian); } else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u32(phdr->p_type,elf->IsLittleEndian); } return 0; } -u64 GetELFProgramEntryFlags(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFProgramEntryFlags(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->programTableEntryCount) return 0; + if(index >= elf->programTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u64(phdr->p_flags,elf->IsLittleEndian); } else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u32(phdr->p_flags,elf->IsLittleEndian); } return 0; } -u64 GetELFProgramEntryFileSize(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFProgramEntryFileSize(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->programTableEntryCount) return 0; + if(index >= elf->programTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u64(phdr->p_filesz,elf->IsLittleEndian); } else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u32(phdr->p_filesz,elf->IsLittleEndian); } return 0; } -u64 GetELFProgramEntryFileOffset(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFProgramEntryFileOffset(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->programTableEntryCount) return 0; + if(index >= elf->programTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u64(phdr->p_offset,elf->IsLittleEndian); } else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u32(phdr->p_offset,elf->IsLittleEndian); } return 0; } -u64 GetELFProgramEntryMemorySize(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFProgramEntryMemorySize(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->programTableEntryCount) return 0; + if(index >= elf->programTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u64(phdr->p_memsz,elf->IsLittleEndian); } else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u32(phdr->p_memsz,elf->IsLittleEndian); } return 0; } -u64 GetELFProgramEntryVAddress(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFProgramEntryVAddress(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->programTableEntryCount) return 0; + if(index >= elf->programTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u64(phdr->p_vaddr,elf->IsLittleEndian); } else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u32(phdr->p_vaddr,elf->IsLittleEndian); } return 0; } -u64 GetELFProgramEntryPAddress(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFProgramEntryPAddress(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->programTableEntryCount) return 0; + if(index >= elf->programTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u64(phdr->p_paddr,elf->IsLittleEndian); } else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u32(phdr->p_paddr,elf->IsLittleEndian); } @@ -880,16 +853,16 @@ u64 GetELFProgramEntryPAddress(u16 Index, ElfContext *elf, u8 *ElfFile) } -u64 GetELFProgramEntryAlignment(u16 Index, ElfContext *elf, u8 *ElfFile) +u64 GetELFProgramEntryAlignment(u16 index, elf_context *elf, u8 *elfFile) { - if(Index >= elf->programTableEntryCount) return 0; + if(index >= elf->programTableEntryCount) return 0; if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u64(phdr->p_align,elf->IsLittleEndian); } else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(Index,elf,ElfFile); + elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); return u8_to_u32(phdr->p_align,elf->IsLittleEndian); } @@ -897,16 +870,16 @@ u64 GetELFProgramEntryAlignment(u16 Index, ElfContext *elf, u8 *ElfFile) } -int CreateElfSegments(ElfContext *elf, u8 *ElfFile) +int CreateElfSegments(elf_context *elf, u8 *elfFile) { int num = 0; // Interate through Each Program Header elf->activeSegments = 0; - elf->segments = calloc(elf->programTableEntryCount,sizeof(ElfSegment)); - ElfSegment *segment = malloc(sizeof(ElfSegment)); // Temporary Buffer + elf->segments = calloc(elf->programTableEntryCount,sizeof(elf_segment)); + elf_segment *segment = malloc(sizeof(elf_segment)); // Temporary Buffer for (int i = 0; i < elf->programTableEntryCount; i++){ if (elf->programHeaders[i].sizeInMemory != 0 && elf->programHeaders[i].type == 1){ - memset(segment,0,sizeof(ElfSegment)); + memset(segment,0,sizeof(elf_segment)); bool foundFirstSection = false; u32 size = 0; @@ -917,7 +890,7 @@ int CreateElfSegments(ElfContext *elf, u8 *ElfFile) u16 SectionInfoCapacity = 10; segment->sectionNum = 0; - segment->sections = calloc(SectionInfoCapacity,sizeof(ElfSectionEntry)); + segment->sections = calloc(SectionInfoCapacity,sizeof(elf_section_entry)); // Itterate Through Section Headers for (int j = num; j < elf->sectionTableEntryCount; j++){ @@ -936,15 +909,15 @@ int CreateElfSegments(ElfContext *elf, u8 *ElfFile) } if(segment->sectionNum < SectionInfoCapacity) - memcpy(&segment->sections[segment->sectionNum],&elf->sections[j],sizeof(ElfSectionEntry)); + memcpy(&segment->sections[segment->sectionNum],&elf->sections[j],sizeof(elf_section_entry)); else{ SectionInfoCapacity = SectionInfoCapacity*2; - ElfSectionEntry *tmp = calloc(SectionInfoCapacity,sizeof(ElfSectionEntry)); + elf_section_entry *tmp = calloc(SectionInfoCapacity,sizeof(elf_section_entry)); for(int k = 0; k < segment->sectionNum; k++) - memcpy(&tmp[k],&segment->sections[k],sizeof(ElfSectionEntry)); + memcpy(&tmp[k],&segment->sections[k],sizeof(elf_section_entry)); free(segment->sections); segment->sections = tmp; - memcpy(&segment->sections[segment->sectionNum],&elf->sections[j],sizeof(ElfSectionEntry)); + memcpy(&segment->sections[segment->sectionNum],&elf->sections[j],sizeof(elf_section_entry)); } segment->sectionNum++; @@ -955,7 +928,7 @@ int CreateElfSegments(ElfContext *elf, u8 *ElfFile) size += padding + elf->sections[j].size; } - //printf("Section Name: %s",elf->sections[j].name); + //printf("Section name: %s",elf->sections[j].name); //printf(" 0x%lx",elf->sections[j].size); //printf(" (Total Size: 0x%x)\n",size); @@ -969,7 +942,7 @@ int CreateElfSegments(ElfContext *elf, u8 *ElfFile) } if(segment->sectionNum){ segment->header = &elf->programHeaders[i]; - memcpy(&elf->segments[elf->activeSegments],segment,sizeof(ElfSegment)); + memcpy(&elf->segments[elf->activeSegments],segment,sizeof(elf_segment)); elf->activeSegments++; } else{ @@ -985,7 +958,7 @@ int CreateElfSegments(ElfContext *elf, u8 *ElfFile) return 0; } -bool IsIgnoreSection(ElfSectionEntry info) +bool IsIgnoreSection(elf_section_entry info) { if (info.address) return false; diff --git a/makerom/elf.h b/makerom/elf.h index 431a3ed7..d3a24640 100644 --- a/makerom/elf.h +++ b/makerom/elf.h @@ -23,7 +23,7 @@ typedef struct u64 size; u64 address; u64 alignment; -} ElfSectionEntry; +} elf_section_entry; typedef struct { @@ -36,18 +36,17 @@ typedef struct u64 physicalAddress; u64 sizeInMemory; u64 alignment; - -} ElfProgramEntry; +} elf_program_entry; typedef struct { char *name; u64 vAddr; - ElfProgramEntry *header; + elf_program_entry *header; u32 sectionNum; - ElfSectionEntry *sections; -} ElfSegment; + elf_section_entry *sections; +} elf_segment; typedef struct { @@ -55,7 +54,7 @@ typedef struct u32 size; u32 maxPageNum; u8 *data; -} CodeSegment; +} code_segment; typedef struct { @@ -73,12 +72,12 @@ typedef struct u16 sectionHeaderNameEntryIndex; - ElfSectionEntry *sections; - ElfProgramEntry *programHeaders; + elf_section_entry *sections; + elf_program_entry *programHeaders; u16 activeSegments; - ElfSegment *segments; + elf_segment *segments; -} ElfContext; +} elf_context; int BuildExeFsCode(ncch_settings *ncchset); \ No newline at end of file diff --git a/makerom/exefs.c b/makerom/exefs.c index c1e53284..1d3f2304 100644 --- a/makerom/exefs.c +++ b/makerom/exefs.c @@ -1,6 +1,6 @@ #include "lib.h" -#include "ncch.h" -#include "exefs.h" +#include "ncch_build.h" +#include "exefs_build.h" // Private Prototypes u32 PredictExeFS_Size(exefs_buildctx *ctx); @@ -18,7 +18,7 @@ int BuildExeFs(ncch_settings *ncchset) fprintf(stderr,"[EXEFS ERROR] Not enough memory\n"); return MEM_ERROR; } - ctx->mediaUnit = ncchset->options.mediaSize; + ctx->blockSize = ncchset->options.blockSize; /* Importing ExeFs */ if(ncchset->exefsSections.code.size) @@ -56,10 +56,9 @@ int BuildExeFs(ncch_settings *ncchset) u32 PredictExeFS_Size(exefs_buildctx *ctx) { - u32 exefs_size = 0x200; // Size of header - for(int i = 0; i < ctx->fileCount; i++){ - exefs_size += align(ctx->fileSize[i],ctx->mediaUnit); - } + u32 exefs_size = sizeof(exefs_hdr); // Size of header + for(int i = 0; i < ctx->fileCount; i++) + exefs_size += align(ctx->fileSize[i],ctx->blockSize); //exefs_size = align(ctx->exefs_size,ctx->mediaUnit); return exefs_size; } @@ -70,7 +69,7 @@ int GenerateExeFS_Header(exefs_buildctx *ctx, u8 *outbuff) if(i == 0) ctx->fileOffset[i] = 0; else - ctx->fileOffset[i] = align((ctx->fileOffset[i-1]+ctx->fileSize[i-1]),ctx->mediaUnit); + ctx->fileOffset[i] = align((ctx->fileOffset[i-1]+ctx->fileSize[i-1]),ctx->blockSize); memcpy(ctx->fileHdr[i].name,ctx->fileName[i],8); u32_to_u8(ctx->fileHdr[i].offset,ctx->fileOffset[i],LE); diff --git a/makerom/exefs.h b/makerom/exefs.h index 52afe84b..f6e6c136 100644 --- a/makerom/exefs.h +++ b/makerom/exefs.h @@ -2,14 +2,6 @@ #define MAX_EXEFS_SECTIONS 10 // DO NOT CHANGE -typedef enum -{ - PTR_ERROR = -10, - EXEFS_MAX_REACHED = -11, - EXEFS_SECTION_NAME_ERROR = -12, - -} exefs_errors; - typedef struct { char name[8]; @@ -23,29 +15,3 @@ typedef struct u8 reserved[0x20]; u8 fileHashes[MAX_EXEFS_SECTIONS][0x20]; } exefs_hdr; - -typedef struct -{ - //Input - int fileCount; - u8 *file[MAX_EXEFS_SECTIONS]; - u32 fileSize[MAX_EXEFS_SECTIONS]; - u32 fileOffset[MAX_EXEFS_SECTIONS]; - char fileName[MAX_EXEFS_SECTIONS][8]; - u32 mediaUnit; - - //Working Data - exefs_filehdr fileHdr[MAX_EXEFS_SECTIONS]; - u8 fileHashes[MAX_EXEFS_SECTIONS][0x20]; - -} exefs_buildctx; - -/* ExeFs Build Functions */ -int BuildExeFs(ncch_settings *ncchset); - -/* ExeFs Read Functions */ -bool DoesExeFsSectionExist(char *section, u8 *ExeFs); -u8* GetExeFsSection(char *section, u8 *ExeFs); -u8* GetExeFsSectionHash(char *section, u8 *ExeFs); -u32 GetExeFsSectionSize(char *section, u8 *ExeFs); -u32 GetExeFsSectionOffset(char *section, u8 *ExeFs); diff --git a/makerom/exefs_build.h b/makerom/exefs_build.h new file mode 100644 index 00000000..93f45a44 --- /dev/null +++ b/makerom/exefs_build.h @@ -0,0 +1,28 @@ +#pragma once +#include "exefs.h" + +typedef enum +{ + PTR_ERROR = -10, + EXEFS_MAX_REACHED = -11, + EXEFS_SECTION_NAME_ERROR = -12, +} exefs_errors; + +typedef struct +{ + //Input + int fileCount; + u8 *file[MAX_EXEFS_SECTIONS]; + u32 fileSize[MAX_EXEFS_SECTIONS]; + u32 fileOffset[MAX_EXEFS_SECTIONS]; + char fileName[MAX_EXEFS_SECTIONS][8]; + u32 blockSize; + + //Working Data + exefs_filehdr fileHdr[MAX_EXEFS_SECTIONS]; + u8 fileHashes[MAX_EXEFS_SECTIONS][0x20]; + +} exefs_buildctx; + +/* ExeFs Build Functions */ +int BuildExeFs(ncch_settings *ncchset); \ No newline at end of file diff --git a/makerom/exefs_read.h b/makerom/exefs_read.h new file mode 100644 index 00000000..990729b8 --- /dev/null +++ b/makerom/exefs_read.h @@ -0,0 +1,9 @@ +#pragma once +#include "exefs.h" + +/* ExeFs Read Functions */ +bool DoesExeFsSectionExist(char *section, u8 *ExeFs); +u8* GetExeFsSection(char *section, u8 *ExeFs); +u8* GetExeFsSectionHash(char *section, u8 *ExeFs); +u32 GetExeFsSectionSize(char *section, u8 *ExeFs); +u32 GetExeFsSectionOffset(char *section, u8 *ExeFs); \ No newline at end of file diff --git a/makerom/exheader.c b/makerom/exheader.c index 0270b359..9c99f258 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -1,6 +1,6 @@ #include "lib.h" -#include "ncch.h" -#include "exheader.h" +#include "ncch_build.h" +#include "exheader_build.h" #include "accessdesc.h" #include "titleid.h" @@ -124,13 +124,13 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * /* Import ExHeader Code Section template */ if(ncchset->componentFilePtrs.exhdrSize){ - u32 import_size = 0x30; //min_u64(0x30,ncchset->componentFilePtrs.exhdrSize); + u32 import_size = 0x30; //min64(0x30,ncchset->componentFilePtrs.exhdrSize); u32 import_offset = 0x10; if((import_size+import_offset) > ncchset->componentFilePtrs.exhdrSize){ fprintf(stderr,"[EXHEADER ERROR] Exheader Template is too small\n"); return FAILED_TO_IMPORT_FILE; } - ReadFile_64((ncchset->sections.exhdr.buffer+import_offset),import_size,import_offset,ncchset->componentFilePtrs.exhdr); + ReadFile64((ncchset->sections.exhdr.buffer+import_offset),import_size,import_offset,ncchset->componentFilePtrs.exhdr); } /* Create ExHeader Struct for output */ diff --git a/makerom/exheader.h b/makerom/exheader.h index 82e778f4..d2d216fb 100644 --- a/makerom/exheader.h +++ b/makerom/exheader.h @@ -1,12 +1,5 @@ #pragma once -typedef enum -{ - COMMON_HEADER_KEY_NOT_FOUND = -10, - EXHDR_BAD_YAML_OPT = -11, - CANNOT_SIGN_ACCESSDESC = -12 -} exheader_errors; - typedef enum { infoflag_COMPRESS_EXEFS_0 = 1, @@ -197,37 +190,11 @@ typedef struct exhdr_ARM9AccessControlInfo arm9AccessControlInfo; } access_descriptor; -typedef struct -{ - keys_struct *keys; - rsf_settings *rsf; - bool useAccessDescPreset; - - /* Output */ - extended_hdr *exHdr; // is the exheader output buffer ptr(in ncchset) cast as exheader struct ptr; - access_descriptor *acexDesc; -} exheader_settings; - - /* ExHeader Signature Functions */ int SignAccessDesc(access_descriptor *acexDesc, keys_struct *keys); int CheckAccessDescSignature(access_descriptor *acexDesc, keys_struct *keys); -/* ExHeader Build Functions */ -int BuildExHeader(ncch_settings *ncchset); - -/* ExHeader Binary Print Functions */ -void exhdr_Print_ServiceAccessControl(extended_hdr *hdr); - -/* ExHeader Binary Read Functions */ -u8* GetAcexRsaSig(access_descriptor *acexDesc); -u8* GetAcexNcchPubKey(access_descriptor *acexDesc); -u16 GetRemasterVersion_frm_exhdr(extended_hdr *hdr); -u64 GetSaveDataSize_frm_exhdr(extended_hdr *hdr); -int GetDependencyList_frm_exhdr(u8 *Dest,extended_hdr *hdr); -void GetCoreVersion_frm_exhdr(u8 *Dest, extended_hdr *hdr); - -/* ExHeader Settings Read from Yaml */ +/* ExHeader Settings Read from Rsf */ int GetSaveDataSizeFromString(u64 *out, char *string, char *moduleName); int GetRemasterVersion_rsf(u16 *RemasterVersion, user_settings *usrset); diff --git a/makerom/exheader_build.h b/makerom/exheader_build.h new file mode 100644 index 00000000..f7fd894c --- /dev/null +++ b/makerom/exheader_build.h @@ -0,0 +1,23 @@ +#pragma once +#include "exheader.h" + +typedef enum +{ + COMMON_HEADER_KEY_NOT_FOUND = -10, + EXHDR_BAD_YAML_OPT = -11, + CANNOT_SIGN_ACCESSDESC = -12 +} exheader_errors; + +typedef struct +{ + keys_struct *keys; + rsf_settings *rsf; + bool useAccessDescPreset; + + /* Output, these ptrs where created originally in ncchset */ + extended_hdr *exHdr; + access_descriptor *acexDesc; +} exheader_settings; + +/* ExHeader Build Functions */ +int BuildExHeader(ncch_settings *ncchset); \ No newline at end of file diff --git a/makerom/exheader_read.h b/makerom/exheader_read.h new file mode 100644 index 00000000..9c09ca91 --- /dev/null +++ b/makerom/exheader_read.h @@ -0,0 +1,13 @@ +#pragma once +#include "exheader.h" + +/* ExHeader Binary Print Functions */ +void exhdr_Print_ServiceAccessControl(extended_hdr *hdr); + +/* ExHeader Binary Read Functions */ +u8* GetAcexRsaSig(access_descriptor *acexDesc); +u8* GetAcexNcchPubKey(access_descriptor *acexDesc); +u16 GetRemasterVersion_frm_exhdr(extended_hdr *hdr); +u64 GetSaveDataSize_frm_exhdr(extended_hdr *hdr); +int GetDependencyList_frm_exhdr(u8 *Dest,extended_hdr *hdr); +void GetCoreVersion_frm_exhdr(u8 *Dest, extended_hdr *hdr); diff --git a/makerom/keyset.c b/makerom/keyset.c index b44ecb68..a39ce548 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -1,14 +1,16 @@ #include "lib.h" // KeyData -#include "tpki.h" // Test PKI -#include "ppki.h" // Production PKI -#include "dpki.h" // Development PKI +#include "pki/test.h" // Test PKI +#include "pki/prod.h" // Production PKI +#include "pki/dev.h" // Development PKI // Private Prototypes int SetRsaKeySet(u8 **PrivDest, u8 *PrivSource, u8 **PubDest, u8 *PubSource); int SetunFixedKey(keys_struct *keys, u8 *unFixedKey); -void InitcommonKeySlots(keys_struct *keys); +void InitCommonKeySlots(keys_struct *keys); +void InitNcchKeyXSlots(keys_struct *keys); +int SetNcchKeyX(keys_struct *keys, u8 *keyX, u8 index); FILE* keyset_OpenFile(char *dir, char *name, bool FileRequired); void keysetOpenError(char *file); @@ -33,11 +35,12 @@ void DumpKeyset(keys_struct *keys); void InitKeys(keys_struct *keys) { memset(keys,0,sizeof(keys_struct)); - InitcommonKeySlots(keys); + InitCommonKeySlots(keys); + InitNcchKeyXSlots(keys); keys->rsa.cxiHdrPub = malloc(RSA_2048_KEY_SIZE); keys->rsa.cxiHdrPvt = malloc(RSA_2048_KEY_SIZE); - keys->aes.unFixedKey0 = malloc(16); - keys->aes.unFixedKey1 = malloc(16); + keys->aes.ncchKey0 = malloc(AES_128_KEY_SIZE); + keys->aes.ncchKey1 = malloc(AES_128_KEY_SIZE); } void PrintBadKeySize(char *path, u32 size) @@ -45,6 +48,21 @@ void PrintBadKeySize(char *path, u32 size) fprintf(stderr,"[KEYSET ERROR] %s has invalid size (0x%x)\n",path,size); } +u8* AesKeyScrambler(u8 *key, u8 *keyX, u8 *keyY) +{ + // Process keyX/keyY to get raw normal key + for(int i = 0; i < 16; i++) + key[i] = keyX[i] ^ ((keyY[i] >> 2) | ((keyY[i < 15 ? i+1 : 0] & 3) << 6)); // keyX[i] ^ + + const u8 SCRAMBLE_SECRET[16] = {0x51, 0xD7, 0x5D, 0xBE, 0xFD, 0x07, 0x57, 0x6A, 0x1C, 0xFC, 0x2A, 0xF0, 0x94, 0x4B, 0xD5, 0x6C}; + + // Apply Secret to get final normal key + for(int i = 0; i < 16; i++) + key[i] = key[i] ^ SCRAMBLE_SECRET[i]; + + return key; +} + int SetKeys(keys_struct *keys) { int result = 0; @@ -90,9 +108,9 @@ int LoadKeysFromResources(keys_struct *keys) keys->keysetLoaded = true; /* AES Keys */ // CIA - for(int i = 0; i < 2; i++){ + for(int i = 0; i < 2; i++) SetCommonKey(keys,(u8*)ctr_common_etd_key_dpki[i],i); - } + if(keys->aes.currentCommonKey > 0xff) SetCurrentCommonKey(keys,0); @@ -100,10 +118,9 @@ int LoadKeysFromResources(keys_struct *keys) SetNormalKey(keys,(u8*)dev_fixed_ncch_key[0]); SetSystemFixedKey(keys,(u8*)dev_fixed_ncch_key[1]); - /* - keys->aes.ncchKeyX0 = (u8*)dev_unfixed_ncch_keyX[0]; - keys->aes.ncchKeyX1 = (u8*)dev_unfixed_ncch_keyX[1]; - */ + for(int i = 0; i < 2; i++) + SetNcchKeyX(keys,(u8*)dev_unfixed_ncch_keyX[i],i); + /* RSA Keys */ // CIA @@ -123,18 +140,19 @@ int LoadKeysFromResources(keys_struct *keys) keys->keysetLoaded = true; /* AES Keys */ // CIA - for(int i = 0; i < 6; i++){ - keys->aes.commonKey[i] = malloc(16); - AesKeyScrambler(keys->aes.commonKey[i],(u8*)ctr_common_etd_keyX_ppki,(u8*)ctr_common_etd_keyY_ppki[i]); - } - SetCurrentCommonKey(keys,1); + //for(int i = 0; i < 6; i++){ + // keys->aes.commonKey[i] = malloc(16); + // AesKeyScrambler(keys->aes.commonKey[i],(u8*)ctr_common_etd_keyX_ppki,(u8*)ctr_common_etd_keyY_ppki[i]); + //} + if(keys->aes.currentCommonKey > 0xff) + SetCurrentCommonKey(keys,0); // NCCH keys->aes.normalKey = NULL; keys->aes.systemFixedKey = NULL; /* - keys->aes.ncchKeyX0 = (u8*)prod_unfixed_ncch_keyX[0]; - keys->aes.ncchKeyX1 = (u8*)prod_unfixed_ncch_keyX[1]; + for(int i = 0; i < 2; i++) + SetNcchKeyX(keys,(u8*)prod_unfixed_ncch_keyX[i],i); */ /* RSA Keys */ @@ -264,15 +282,19 @@ void FreeKeys(keys_struct *keys) { // AES if(keys->aes.commonKey){ - for(int i = 0; i < 256; i++){ + for(int i = 0; i <= MAX_CMN_KEY; i++) free(keys->aes.commonKey[i]); - } } free(keys->aes.commonKey); free(keys->aes.normalKey); free(keys->aes.systemFixedKey); - free(keys->aes.unFixedKey0); - free(keys->aes.unFixedKey1); + if(keys->aes.ncchKeyX){ + for(int i = 0; i <= MAX_NCCH_KEYX; i++) + free(keys->aes.ncchKeyX[i]); + } + free(keys->aes.ncchKeyX); + free(keys->aes.ncchKey0); + free(keys->aes.ncchKey1); // RSA free(keys->rsa.xsPvt); @@ -309,18 +331,28 @@ int SetRsaKeySet(u8 **PrivDest, u8 *PrivSource, u8 **PubDest, u8 *PubSource) return 0; } -int SetCommonKey(keys_struct *keys, u8 *commonKey, u8 Index) +int SetCommonKey(keys_struct *keys, u8 *commonKey, u8 index) { if(!keys) return -1; - return CopyData(&keys->aes.commonKey[Index],commonKey,16); + return CopyData(&keys->aes.commonKey[index],commonKey,AES_128_KEY_SIZE); } -void InitcommonKeySlots(keys_struct *keys) +void InitCommonKeySlots(keys_struct *keys) { - if(!keys->aes.commonKey){ - keys->aes.commonKey = malloc(sizeof(u8*)*256); - memset(keys->aes.commonKey,0,sizeof(u8*)*256); - } + if(!keys->aes.commonKey) + keys->aes.commonKey = calloc(MAX_CMN_KEY+1,sizeof(u8*)); +} + +int SetNcchKeyX(keys_struct *keys, u8 *keyX, u8 index) +{ + if(!keys) return -1; + return CopyData(&keys->aes.ncchKeyX[index],keyX,AES_128_KEY_SIZE); +} + +void InitNcchKeyXSlots(keys_struct *keys) +{ + if(!keys->aes.ncchKeyX) + keys->aes.ncchKeyX = calloc(MAX_NCCH_KEYX+1,sizeof(u8*)); } int SetCurrentCommonKey(keys_struct *keys, u8 Index) @@ -342,22 +374,6 @@ int SetSystemFixedKey(keys_struct *keys, u8 *systemFixedKey) return CopyData(&keys->aes.systemFixedKey,systemFixedKey,16); } -int SetNcchUnfixedKeys(keys_struct *keys, u8 *ncchSig) -{ - if(!keys) return -1; - - //memdump(stdout,"keyY: ",ncchSig,16); - //memdump(stdout,"keyX0: ",keys->aes.ncchKeyX0,16); - //memdump(stdout,"keyX1: ",keys->aes.ncchKeyX1,16); - - if(keys->aes.ncchKeyX0) - AesKeyScrambler(keys->aes.unFixedKey0,keys->aes.ncchKeyX0,ncchSig); - if(keys->aes.ncchKeyX1) - AesKeyScrambler(keys->aes.unFixedKey1,keys->aes.ncchKeyX1,ncchSig); - - return 0; -} - int SetTIK_RsaKey(keys_struct *keys, u8 *PrivateExp, u8 *PublicMod) { if(!keys) return -1; diff --git a/makerom/keyset.h b/makerom/keyset.h index 81741a33..9d6c0c60 100644 --- a/makerom/keyset.h +++ b/makerom/keyset.h @@ -1,5 +1,12 @@ #pragma once +typedef enum +{ + AES_128_KEY_SIZE = 16, + MAX_CMN_KEY = MAX_U8, + MAX_NCCH_KEYX = MAX_U8 +} keydata_limits; + typedef enum { KEYSET_ERROR = -10, @@ -54,11 +61,10 @@ typedef struct // NCCH Keys u8 *normalKey; u8 *systemFixedKey; - - u8 *ncchKeyX0; - u8 *ncchKeyX1; - u8 *unFixedKey0; - u8 *unFixedKey1; + u8 **ncchKeyX; + + u8 *ncchKey0; + u8 *ncchKey1; } aes; struct @@ -101,5 +107,4 @@ int SetCurrentCommonKey(keys_struct *keys, u8 Index); int SetNormalKey(keys_struct *keys, u8 *systemFixedKey); int SetSystemFixedKey(keys_struct *keys, u8 *systemFixedKey); - -int SetNcchUnfixedKeys(keys_struct *keys, u8 *ncchSig); \ No newline at end of file +u8* AesKeyScrambler(u8 *key, u8 *keyX, u8 *keyY); diff --git a/makerom/lib.h b/makerom/lib.h index e0ef089e..a35a1463 100644 --- a/makerom/lib.h +++ b/makerom/lib.h @@ -27,6 +27,7 @@ #include "types.h" #include "utils.h" +#include "ctr_utils.h" #include "crypto.h" #include "keyset.h" diff --git a/makerom/makerom.c b/makerom/makerom.c index fe72bb8f..3d891ce9 100644 --- a/makerom/makerom.c +++ b/makerom/makerom.c @@ -1,73 +1,66 @@ #include "lib.h" -#include "ncch.h" -#include "ncsd.h" -#include "cia.h" +#include "ncch_build.h" +#include "ncsd_build.h" +#include "cia_build.h" int main(int argc, char *argv[]) { // Setting up user settings - user_settings *usrset = calloc(1,sizeof(user_settings)); - if(usrset == NULL) { + user_settings *set = calloc(1,sizeof(user_settings)); + if(set == NULL) { fprintf(stderr,"[!] Not enough memory\n"); return -1; } - init_UserSettings(usrset); + init_UserSettings(set); initRand(); int result; - -#ifdef DEBUG - printf("[DEBUG] Parseing Args\n"); -#endif // Parsing command args - result = ParseArgs(argc,argv,usrset); - if(result < 0) goto finish; + if((result = ParseArgs(argc,argv,set)) < 0) + goto finish; -#ifdef DEBUG - printf("[DEBUG] Importing Yaml Settings\n"); -#endif // Import RSF Settings if present - result = GetYamlSettings(usrset); - if(result < 0) goto finish; + if((result = GetRsfSettings(set)) < 0) + goto finish; // Setup Content 0 - if(!usrset->ncch.buildNcch0){ // Import Content - if(usrset->common.workingFileType == infile_ncch){ - if(!AssertFile(usrset->common.contentPath[0])){ - fprintf(stderr,"[MAKEROM ERROR] Failed to open Content 0: %s\n",usrset->common.contentPath[0]); + if(!set->ncch.buildNcch0){ // Import Content + if(set->common.workingFileType == infile_ncch){ + if(!AssertFile(set->common.contentPath[0])){ + fprintf(stderr,"[MAKEROM ERROR] Failed to open Content 0: %s\n",set->common.contentPath[0]); goto finish; } - u64 fileSize = GetFileSize_u64(usrset->common.contentPath[0]); + u64 fileSize = GetFileSize64(set->common.contentPath[0]); u64 calcSize = 0; - FILE *ncch0 = fopen(usrset->common.contentPath[0],"rb"); + FILE *ncch0 = fopen(set->common.contentPath[0],"rb"); ncch_hdr hdr; - GetNCCH_CommonHDR(&hdr,ncch0,NULL); - calcSize = GetNCCH_MediaSize(&hdr) * GetNCCH_MediaUnitSize(&hdr); + ReadNcchHdr(&hdr,ncch0); + calcSize = GetNcchSize(&hdr); if(calcSize != fileSize){ fprintf(stderr,"[MAKEROM ERROR] Content 0 is corrupt\n"); fclose(ncch0); goto finish; } - usrset->common.workingFile.size = fileSize; - usrset->common.workingFile.buffer = malloc(fileSize); - ReadFile_64(usrset->common.workingFile.buffer, usrset->common.workingFile.size,0,ncch0); + set->common.workingFile.size = fileSize; + set->common.workingFile.buffer = malloc(fileSize); + ReadFile64(set->common.workingFile.buffer, set->common.workingFile.size,0,ncch0); fclose(ncch0); } - else if(usrset->common.workingFileType == infile_srl || usrset->common.workingFileType == infile_ncsd){ - if(!AssertFile(usrset->common.workingFilePath)) { - fprintf(stderr,"[MAKEROM ERROR] Failed to open %s: %s\n",usrset->common.workingFileType == infile_srl? "SRL":"CCI",usrset->common.workingFilePath); + else{ + if(!AssertFile(set->common.workingFilePath)) { + fprintf(stderr,"[MAKEROM ERROR] Failed to open: %s\n",set->common.workingFilePath); goto finish; } - u64 size = GetFileSize_u64(usrset->common.workingFilePath); - usrset->common.workingFile.size = align(size,0x10); - usrset->common.workingFile.buffer = malloc(usrset->common.workingFile.size); - FILE *fp = fopen(usrset->common.workingFilePath,"rb"); - ReadFile_64(usrset->common.workingFile.buffer,size,0,fp); + u64 size = GetFileSize64(set->common.workingFilePath); + set->common.workingFile.size = align(size,0x10); + set->common.workingFile.buffer = malloc(set->common.workingFile.size); + FILE *fp = fopen(set->common.workingFilePath,"rb"); + ReadFile64(set->common.workingFile.buffer,size,0,fp); fclose(fp); } } @@ -75,48 +68,48 @@ int main(int argc, char *argv[]) #ifdef DEBUG printf("[DEBUG] Build NCCH0\n"); #endif - result = build_NCCH(usrset); + result = build_NCCH(set); if(result < 0) { - //fprintf(stderr,"[ERROR] %s generation failed\n",usrset->build_ncch_type == CXI? "CXI" : "CFA"); + //fprintf(stderr,"[ERROR] %s generation failed\n",set->build_ncch_type == CXI? "CXI" : "CFA"); fprintf(stderr,"[RESULT] Failed to build outfile\n"); goto finish; } } // Make CCI - if(usrset->common.outFormat == CCI){ + if(set->common.outFormat == CCI){ #ifdef DEBUG printf("[DEBUG] Building CCI\n"); #endif - result = build_CCI(usrset); + result = build_CCI(set); if(result < 0) { fprintf(stderr,"[RESULT] Failed to build CCI\n"); goto finish; } } // Make CIA - else if(usrset->common.outFormat == CIA){ + else if(set->common.outFormat == CIA){ #ifdef DEBUG printf("[DEBUG] Building CIA\n"); #endif - result = build_CIA(usrset); + result = build_CIA(set); if(result < 0) { fprintf(stderr,"[RESULT] Failed to build CIA\n"); goto finish; } } // No Container Raw CXI/CFA - else if(usrset->common.outFormat == CXI || usrset->common.outFormat == CFA){ + else if(set->common.outFormat == CXI || set->common.outFormat == CFA){ #ifdef DEBUG printf("[DEBUG] Outputting NCCH, because No Container\n"); #endif - FILE *ncch_out = fopen(usrset->common.outFileName,"wb"); + FILE *ncch_out = fopen(set->common.outFileName,"wb"); if(!ncch_out) { - fprintf(stderr,"[MAKEROM ERROR] Failed to create '%s'\n",usrset->common.outFileName); - fprintf(stderr,"[RESULT] Failed to build '%s'\n",usrset->common.outFormat == CXI? "CXI" : "CFA"); + fprintf(stderr,"[MAKEROM ERROR] Failed to create '%s'\n",set->common.outFileName); + fprintf(stderr,"[RESULT] Failed to build '%s'\n",set->common.outFormat == CXI? "CXI" : "CFA"); result = FAILED_TO_CREATE_OUTFILE; goto finish; } - WriteBuffer(usrset->common.workingFile.buffer,usrset->common.workingFile.size,0,ncch_out); + WriteBuffer(set->common.workingFile.buffer,set->common.workingFile.size,0,ncch_out); fclose(ncch_out); } @@ -124,7 +117,7 @@ int main(int argc, char *argv[]) #ifdef DEBUG printf("[DEBUG] Free Context\n"); #endif - free_UserSettings(usrset); + free_UserSettings(set); #ifdef DEBUG printf("[DEBUG] Finished returning (result=%d)\n",result); #endif diff --git a/makerom/ncch.c b/makerom/ncch.c index cc53630d..e81ede85 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -1,26 +1,28 @@ #include "lib.h" #include "dir.h" -#include "ncch.h" -#include "exheader.h" +#include "ncch_build.h" +#include "exheader_build.h" +#include "exheader_read.h" #include "elf.h" -#include "exefs.h" +#include "exefs_build.h" +#include "exefs_read.h" #include "romfs.h" #include "titleid.h" -#include "logo_data.h" // Contains Logos +#include "ncch_logo.h" // Contains Logos -const u32 MEDIA_UNIT = 0x200; +const u32 NCCH_BLOCK_SIZE = 0x200; // Private Prototypes -int SignCFA(u8 *Signature, u8 *CFA_HDR, keys_struct *keys); -int CheckCFASignature(u8 *Signature, u8 *CFA_HDR, keys_struct *keys); -int SignCXI(u8 *Signature, u8 *CXI_HDR, keys_struct *keys); -int CheckCXISignature(u8 *Signature, u8 *CXI_HDR, u8 *PubK); - -void init_NCCHSettings(ncch_settings *set); -void free_NCCHSettings(ncch_settings *set); -int get_NCCHSettings(ncch_settings *ncchset, user_settings *usrset); -int SetBasicOptions(ncch_settings *ncchset, user_settings *usrset); +int SignCFA(ncch_hdr *hdr, keys_struct *keys); +int CheckCFASignature(ncch_hdr *hdr, keys_struct *keys); +int SignCXI(ncch_hdr *hdr, keys_struct *keys); +int CheckCXISignature(ncch_hdr *hdr, u8 *pubk); + +void InitNcchSettings(ncch_settings *set); +void FreeNcchSettings(ncch_settings *set); +int GetNcchSettings(ncch_settings *ncchset, user_settings *usrset); +int GetBasicOptions(ncch_settings *ncchset, user_settings *usrset); int CreateInputFilePtrs(ncch_settings *ncchset, user_settings *usrset); int ImportNonCodeExeFsSections(ncch_settings *ncchset); int ImportLogo(ncch_settings *ncchset); @@ -31,29 +33,29 @@ int SetCommonHeaderBasicData(ncch_settings *ncchset, ncch_hdr *hdr); bool IsValidProductCode(char *ProductCode, bool FreeProductCode); int BuildCommonHeader(ncch_settings *ncchset); -int EncryptNCCHSections(ncch_settings *ncchset); +int EnCryptNcchRegions(ncch_settings *ncchset); int WriteNCCHSectionsToBuffer(ncch_settings *ncchset); // Code -int SignCFA(u8 *Signature, u8 *CFA_HDR, keys_struct *keys) +int SignCFA(ncch_hdr *hdr, keys_struct *keys) { - return ctr_sig(CFA_HDR,sizeof(ncch_hdr),Signature,keys->rsa.cciCfaPub,keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + return ctr_sig(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cciCfaPub,keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } -int CheckCFASignature(u8 *Signature, u8 *CFA_HDR, keys_struct *keys) +int CheckCFASignature(ncch_hdr *hdr, keys_struct *keys) { - return ctr_sig(CFA_HDR,sizeof(ncch_hdr),Signature,keys->rsa.cciCfaPub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); + return ctr_sig(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cciCfaPub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); } -int SignCXI(u8 *Signature, u8 *CXI_HDR, keys_struct *keys) +int SignCXI(ncch_hdr *hdr, keys_struct *keys) { - return ctr_sig(CXI_HDR,sizeof(ncch_hdr),Signature,keys->rsa.cxiHdrPub,keys->rsa.cxiHdrPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + return ctr_sig(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cxiHdrPub,keys->rsa.cxiHdrPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } -int CheckCXISignature(u8 *Signature, u8 *CXI_HDR, u8 *PubK) +int CheckCXISignature(ncch_hdr *hdr, u8 *pubk) { - int result = ctr_sig(CXI_HDR,sizeof(ncch_hdr),Signature,PubK,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); + int result = ctr_sig(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),pubk,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); return result; } @@ -63,21 +65,26 @@ int build_NCCH(user_settings *usrset) { int result; - // Init Settings\n"); + // Init Settings ncch_settings *ncchset = calloc(1,sizeof(ncch_settings)); if(!ncchset) { fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); return MEM_ERROR; } - init_NCCHSettings(ncchset); + InitNcchSettings(ncchset); - // Get Settings\n"); - result = get_NCCHSettings(ncchset,usrset); + // Get Settings + result = GetNcchSettings(ncchset,usrset); if(result) goto finish; + // Import Data + result = ImportNonCodeExeFsSections(ncchset); + if(result) return result; + result = ImportLogo(ncchset); + if(result) return result; - if(!ncchset->options.IsCfa){ // CXI Specfic Sections + if(!ncchset->options.IsCfa){ // CXI Specific Sections // Build ExeFs Code Section\n"); result = BuildExeFsCode(ncchset); if(result) goto finish; @@ -94,18 +101,18 @@ int build_NCCH(user_settings *usrset) // Prepare for RomFs\n"); - romfs_buildctx romfs_ctx; - memset(&romfs_ctx,0,sizeof(romfs_buildctx)); - result = SetupRomFs(ncchset,&romfs_ctx); + romfs_buildctx romfs; + memset(&romfs,0,sizeof(romfs_buildctx)); + result = SetupRomFs(ncchset,&romfs); if(result) goto finish; // Setup NCCH including final memory allocation\n"); - result = SetupNcch(ncchset,&romfs_ctx); + result = SetupNcch(ncchset,&romfs); if(result) goto finish; // Build RomFs\n"); - result = BuildRomFs(&romfs_ctx); + result = BuildRomFs(&romfs); if(result) goto finish; // Finalise NCCH (Hashes/Signatures and crypto)\n"); @@ -115,16 +122,16 @@ int build_NCCH(user_settings *usrset) finish: if(result) fprintf(stderr,"[NCCH ERROR] NCCH Build Process Failed\n"); - free_NCCHSettings(ncchset); + FreeNcchSettings(ncchset); return result; } -void init_NCCHSettings(ncch_settings *set) +void InitNcchSettings(ncch_settings *set) { memset(set,0,sizeof(ncch_settings)); } -void free_NCCHSettings(ncch_settings *set) +void FreeNcchSettings(ncch_settings *set) { if(set->componentFilePtrs.elf) fclose(set->componentFilePtrs.elf); if(set->componentFilePtrs.banner) fclose(set->componentFilePtrs.banner); @@ -149,7 +156,7 @@ void free_NCCHSettings(ncch_settings *set) free(set); } -int get_NCCHSettings(ncch_settings *ncchset, user_settings *usrset) +int GetNcchSettings(ncch_settings *ncchset, user_settings *usrset) { int result = 0; ncchset->out = &usrset->common.workingFile; @@ -157,33 +164,32 @@ int get_NCCHSettings(ncch_settings *ncchset, user_settings *usrset) ncchset->rsfSet = &usrset->common.rsfSet; ncchset->keys = &usrset->common.keys; - result = SetBasicOptions(ncchset,usrset); + result = GetBasicOptions(ncchset,usrset); if(result) return result; result = CreateInputFilePtrs(ncchset,usrset); if(result) return result; - result = ImportNonCodeExeFsSections(ncchset); - if(result) return result; - result = ImportLogo(ncchset); - if(result) return result; return 0; } -int SetBasicOptions(ncch_settings *ncchset, user_settings *usrset) +int GetBasicOptions(ncch_settings *ncchset, user_settings *usrset) { int result = 0; /* Options */ - ncchset->options.mediaSize = MEDIA_UNIT; + ncchset->options.blockSize = NCCH_BLOCK_SIZE; + ncchset->options.verbose = usrset->common.verbose; ncchset->options.IncludeExeFsLogo = usrset->ncch.includeExefsLogo; - ncchset->options.CompressCode = usrset->common.rsfSet.Option.EnableCompress; - ncchset->options.UseOnSD = usrset->common.rsfSet.Option.UseOnSD; - ncchset->options.Encrypt = usrset->common.rsfSet.Option.EnableCrypt; - ncchset->options.FreeProductCode = usrset->common.rsfSet.Option.FreeProductCode; + ncchset->options.CompressCode = ncchset->rsfSet->Option.EnableCompress; + ncchset->options.UseOnSD = ncchset->rsfSet->Option.UseOnSD; + ncchset->options.Encrypt = ncchset->rsfSet->Option.EnableCrypt; + ncchset->options.FreeProductCode = ncchset->rsfSet->Option.FreeProductCode; ncchset->options.IsCfa = (usrset->ncch.ncchType == CFA); ncchset->options.IsBuildingCodeSection = (usrset->ncch.elfPath != NULL); ncchset->options.UseRomFS = ((ncchset->rsfSet->Rom.HostRoot && strlen(ncchset->rsfSet->Rom.HostRoot) > 0) || usrset->ncch.romfsPath); + ncchset->options.useSecCrypto = usrset->ncch.useSecCrypto; + ncchset->options.keyXID = usrset->ncch.keyXID; if(ncchset->options.IsCfa && !ncchset->options.UseRomFS){ fprintf(stderr,"[NCCH ERROR] \"Rom/HostRoot\" must be set\n"); @@ -196,7 +202,7 @@ int SetBasicOptions(ncch_settings *ncchset, user_settings *usrset) int CreateInputFilePtrs(ncch_settings *ncchset, user_settings *usrset) { if(usrset->ncch.romfsPath){ - ncchset->componentFilePtrs.romfsSize = GetFileSize_u64(usrset->ncch.romfsPath); + ncchset->componentFilePtrs.romfsSize = GetFileSize64(usrset->ncch.romfsPath); ncchset->componentFilePtrs.romfs = fopen(usrset->ncch.romfsPath,"rb"); if(!ncchset->componentFilePtrs.romfs){ fprintf(stderr,"[NCCH ERROR] Failed to open RomFs file '%s'\n",usrset->ncch.romfsPath); @@ -204,7 +210,7 @@ int CreateInputFilePtrs(ncch_settings *ncchset, user_settings *usrset) } } if(usrset->ncch.elfPath){ - ncchset->componentFilePtrs.elfSize = GetFileSize_u64(usrset->ncch.elfPath); + ncchset->componentFilePtrs.elfSize = GetFileSize64(usrset->ncch.elfPath); ncchset->componentFilePtrs.elf = fopen(usrset->ncch.elfPath,"rb"); if(!ncchset->componentFilePtrs.elf){ fprintf(stderr,"[NCCH ERROR] Failed to open elf file '%s'\n",usrset->ncch.elfPath); @@ -212,7 +218,7 @@ int CreateInputFilePtrs(ncch_settings *ncchset, user_settings *usrset) } } if(usrset->ncch.bannerPath){ - ncchset->componentFilePtrs.bannerSize = GetFileSize_u64(usrset->ncch.bannerPath); + ncchset->componentFilePtrs.bannerSize = GetFileSize64(usrset->ncch.bannerPath); ncchset->componentFilePtrs.banner = fopen(usrset->ncch.bannerPath,"rb"); if(!ncchset->componentFilePtrs.banner){ fprintf(stderr,"[NCCH ERROR] Failed to open banner file '%s'\n",usrset->ncch.bannerPath); @@ -220,7 +226,7 @@ int CreateInputFilePtrs(ncch_settings *ncchset, user_settings *usrset) } } if(usrset->ncch.iconPath){ - ncchset->componentFilePtrs.iconSize = GetFileSize_u64(usrset->ncch.iconPath); + ncchset->componentFilePtrs.iconSize = GetFileSize64(usrset->ncch.iconPath); ncchset->componentFilePtrs.icon = fopen(usrset->ncch.iconPath,"rb"); if(!ncchset->componentFilePtrs.icon){ fprintf(stderr,"[NCCH ERROR] Failed to open icon file '%s'\n",usrset->ncch.iconPath); @@ -228,7 +234,7 @@ int CreateInputFilePtrs(ncch_settings *ncchset, user_settings *usrset) } } if(usrset->ncch.logoPath){ - ncchset->componentFilePtrs.logoSize = GetFileSize_u64(usrset->ncch.logoPath); + ncchset->componentFilePtrs.logoSize = GetFileSize64(usrset->ncch.logoPath); ncchset->componentFilePtrs.logo = fopen(usrset->ncch.logoPath,"rb"); if(!ncchset->componentFilePtrs.logo){ fprintf(stderr,"[NCCH ERROR] Failed to open logo file '%s'\n",usrset->ncch.logoPath); @@ -237,7 +243,7 @@ int CreateInputFilePtrs(ncch_settings *ncchset, user_settings *usrset) } if(usrset->ncch.codePath){ - ncchset->componentFilePtrs.codeSize = GetFileSize_u64(usrset->ncch.codePath); + ncchset->componentFilePtrs.codeSize = GetFileSize64(usrset->ncch.codePath); ncchset->componentFilePtrs.code = fopen(usrset->ncch.codePath,"rb"); if(!ncchset->componentFilePtrs.code){ fprintf(stderr,"[NCCH ERROR] Failed to open ExeFs Code file '%s'\n",usrset->ncch.codePath); @@ -245,7 +251,7 @@ int CreateInputFilePtrs(ncch_settings *ncchset, user_settings *usrset) } } if(usrset->ncch.exheaderPath){ - ncchset->componentFilePtrs.exhdrSize = GetFileSize_u64(usrset->ncch.exheaderPath); + ncchset->componentFilePtrs.exhdrSize = GetFileSize64(usrset->ncch.exheaderPath); ncchset->componentFilePtrs.exhdr = fopen(usrset->ncch.exheaderPath,"rb"); if(!ncchset->componentFilePtrs.exhdr){ fprintf(stderr,"[NCCH ERROR] Failed to open ExHeader file '%s'\n",usrset->ncch.exheaderPath); @@ -253,7 +259,7 @@ int CreateInputFilePtrs(ncch_settings *ncchset, user_settings *usrset) } } if(usrset->ncch.plainRegionPath){ - ncchset->componentFilePtrs.plainregionSize = GetFileSize_u64(usrset->ncch.plainRegionPath); + ncchset->componentFilePtrs.plainregionSize = GetFileSize64(usrset->ncch.plainRegionPath); ncchset->componentFilePtrs.plainregion = fopen(usrset->ncch.plainRegionPath,"rb"); if(!ncchset->componentFilePtrs.plainregion){ fprintf(stderr,"[NCCH ERROR] Failed to open PlainRegion file '%s'\n",usrset->ncch.plainRegionPath); @@ -263,88 +269,88 @@ int CreateInputFilePtrs(ncch_settings *ncchset, user_settings *usrset) return 0; } -int ImportNonCodeExeFsSections(ncch_settings *ncchset) +int ImportNonCodeExeFsSections(ncch_settings *set) { - if(ncchset->componentFilePtrs.banner){ - ncchset->exefsSections.banner.size = ncchset->componentFilePtrs.bannerSize; - ncchset->exefsSections.banner.buffer = malloc(ncchset->exefsSections.banner.size); - if(!ncchset->exefsSections.banner.buffer) { + if(set->componentFilePtrs.banner){ + set->exefsSections.banner.size = set->componentFilePtrs.bannerSize; + set->exefsSections.banner.buffer = malloc(set->exefsSections.banner.size); + if(!set->exefsSections.banner.buffer) { fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); return MEM_ERROR; } - ReadFile_64(ncchset->exefsSections.banner.buffer,ncchset->exefsSections.banner.size,0,ncchset->componentFilePtrs.banner); + ReadFile64(set->exefsSections.banner.buffer,set->exefsSections.banner.size,0,set->componentFilePtrs.banner); } - if(ncchset->componentFilePtrs.icon){ - ncchset->exefsSections.icon.size = ncchset->componentFilePtrs.iconSize; - ncchset->exefsSections.icon.buffer = malloc(ncchset->exefsSections.icon.size); - if(!ncchset->exefsSections.icon.buffer) { + if(set->componentFilePtrs.icon){ + set->exefsSections.icon.size = set->componentFilePtrs.iconSize; + set->exefsSections.icon.buffer = malloc(set->exefsSections.icon.size); + if(!set->exefsSections.icon.buffer) { fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); return MEM_ERROR; } - ReadFile_64(ncchset->exefsSections.icon.buffer,ncchset->exefsSections.icon.size,0,ncchset->componentFilePtrs.icon); + ReadFile64(set->exefsSections.icon.buffer,set->exefsSections.icon.size,0,set->componentFilePtrs.icon); } return 0; } -int ImportLogo(ncch_settings *ncchset) +int ImportLogo(ncch_settings *set) { - if(ncchset->componentFilePtrs.logo){ - ncchset->sections.logo.size = align(ncchset->componentFilePtrs.logoSize,ncchset->options.mediaSize); - ncchset->sections.logo.buffer = malloc(ncchset->sections.logo.size); - if(!ncchset->sections.logo.buffer) { + if(set->componentFilePtrs.logo){ + set->sections.logo.size = align(set->componentFilePtrs.logoSize,set->options.blockSize); + set->sections.logo.buffer = malloc(set->sections.logo.size); + if(!set->sections.logo.buffer) { fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); return MEM_ERROR; } - memset(ncchset->sections.logo.buffer,0,ncchset->sections.logo.size); - ReadFile_64(ncchset->sections.logo.buffer,ncchset->componentFilePtrs.logoSize,0,ncchset->componentFilePtrs.logo); + memset(set->sections.logo.buffer,0,set->sections.logo.size); + ReadFile64(set->sections.logo.buffer,set->componentFilePtrs.logoSize,0,set->componentFilePtrs.logo); } - else if(ncchset->rsfSet->BasicInfo.Logo){ - if(strcasecmp(ncchset->rsfSet->BasicInfo.Logo,"nintendo") == 0){ - ncchset->sections.logo.size = 0x2000; - ncchset->sections.logo.buffer = malloc(ncchset->sections.logo.size); - if(!ncchset->sections.logo.buffer) { + else if(set->rsfSet->BasicInfo.Logo){ + if(strcasecmp(set->rsfSet->BasicInfo.Logo,"nintendo") == 0){ + set->sections.logo.size = 0x2000; + set->sections.logo.buffer = malloc(set->sections.logo.size); + if(!set->sections.logo.buffer) { fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); return MEM_ERROR; } - memcpy(ncchset->sections.logo.buffer,Nintendo_LZ,0x2000); + memcpy(set->sections.logo.buffer,Nintendo_LZ,0x2000); } - else if(strcasecmp(ncchset->rsfSet->BasicInfo.Logo,"licensed") == 0){ - ncchset->sections.logo.size = 0x2000; - ncchset->sections.logo.buffer = malloc(ncchset->sections.logo.size); - if(!ncchset->sections.logo.buffer) { + else if(strcasecmp(set->rsfSet->BasicInfo.Logo,"licensed") == 0){ + set->sections.logo.size = 0x2000; + set->sections.logo.buffer = malloc(set->sections.logo.size); + if(!set->sections.logo.buffer) { fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); return MEM_ERROR; } - memcpy(ncchset->sections.logo.buffer,Nintendo_LicensedBy_LZ,0x2000); + memcpy(set->sections.logo.buffer,Nintendo_LicensedBy_LZ,0x2000); } - else if(strcasecmp(ncchset->rsfSet->BasicInfo.Logo,"distributed") == 0){ - ncchset->sections.logo.size = 0x2000; - ncchset->sections.logo.buffer = malloc(ncchset->sections.logo.size); - if(!ncchset->sections.logo.buffer) { + else if(strcasecmp(set->rsfSet->BasicInfo.Logo,"distributed") == 0){ + set->sections.logo.size = 0x2000; + set->sections.logo.buffer = malloc(set->sections.logo.size); + if(!set->sections.logo.buffer) { fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); return MEM_ERROR; } - memcpy(ncchset->sections.logo.buffer,Nintendo_DistributedBy_LZ,0x2000); + memcpy(set->sections.logo.buffer,Nintendo_DistributedBy_LZ,0x2000); } - else if(strcasecmp(ncchset->rsfSet->BasicInfo.Logo,"ique") == 0){ - ncchset->sections.logo.size = 0x2000; - ncchset->sections.logo.buffer = malloc(ncchset->sections.logo.size); - if(!ncchset->sections.logo.buffer) { + else if(strcasecmp(set->rsfSet->BasicInfo.Logo,"ique") == 0){ + set->sections.logo.size = 0x2000; + set->sections.logo.buffer = malloc(set->sections.logo.size); + if(!set->sections.logo.buffer) { fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); return MEM_ERROR; } - memcpy(ncchset->sections.logo.buffer,iQue_with_ISBN_LZ,0x2000); + memcpy(set->sections.logo.buffer,iQue_with_ISBN_LZ,0x2000); } - else if(strcasecmp(ncchset->rsfSet->BasicInfo.Logo,"iqueforsystem") == 0){ - ncchset->sections.logo.size = 0x2000; - ncchset->sections.logo.buffer = malloc(ncchset->sections.logo.size); - if(!ncchset->sections.logo.buffer) { + else if(strcasecmp(set->rsfSet->BasicInfo.Logo,"iqueforsystem") == 0){ + set->sections.logo.size = 0x2000; + set->sections.logo.buffer = malloc(set->sections.logo.size); + if(!set->sections.logo.buffer) { fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); return MEM_ERROR; } - memcpy(ncchset->sections.logo.buffer,iQue_without_ISBN_LZ,0x2000); + memcpy(set->sections.logo.buffer,iQue_without_ISBN_LZ,0x2000); } - else if(strcasecmp(ncchset->rsfSet->BasicInfo.Logo,"none") != 0){ + else if(strcasecmp(set->rsfSet->BasicInfo.Logo,"none") != 0){ fprintf(stderr,"[NCCH ERROR] Invalid logo name\n"); return NCCH_BAD_YAML_SET; } @@ -352,61 +358,61 @@ int ImportLogo(ncch_settings *ncchset) return 0; } -int SetupNcch(ncch_settings *ncchset, romfs_buildctx *romfs) +int SetupNcch(ncch_settings *set, romfs_buildctx *romfs) { u64 ncchSize = 0; u64 exhdrSize,acexSize,logoSize,plnRgnSize,exefsSize,romfsSize; u64 exhdrOffset,acexOffset,logoOffset,plnRgnOffset,exefsOffset,romfsOffset; u32 exefsHashSize,romfsHashSize; - ncchSize += 0x200; // Sig+Hdr - + ncchSize += sizeof(ncch_hdr); // Sig+Hdr + // Sizes for NCCH hdr - if(ncchset->sections.exhdr.size){ - exhdrSize = ncchset->sections.exhdr.size; + if(set->sections.exhdr.size){ + exhdrSize = set->sections.exhdr.size; exhdrOffset = ncchSize; ncchSize += exhdrSize; } else exhdrSize = 0; - if(ncchset->sections.acexDesc.size){ - acexSize = ncchset->sections.acexDesc.size; + if(set->sections.acexDesc.size){ + acexSize = set->sections.acexDesc.size; acexOffset = ncchSize; ncchSize += acexSize; } else acexSize = 0; - if(ncchset->sections.logo.size){ - logoSize = ncchset->sections.logo.size; - logoOffset = align(ncchSize,ncchset->options.mediaSize); + if(set->sections.logo.size){ + logoSize = set->sections.logo.size; + logoOffset = align(ncchSize,set->options.blockSize); ncchSize = logoOffset + logoSize; } else logoSize = 0; - if(ncchset->sections.plainRegion.size){ - plnRgnSize = align(ncchset->sections.plainRegion.size,ncchset->options.mediaSize); - plnRgnOffset = align(ncchSize,ncchset->options.mediaSize); + if(set->sections.plainRegion.size){ + plnRgnSize = align(set->sections.plainRegion.size,set->options.blockSize); + plnRgnOffset = align(ncchSize,set->options.blockSize); ncchSize = plnRgnOffset + plnRgnSize; } else plnRgnSize = 0; - if(ncchset->sections.exeFs.size){ - exefsHashSize = align(sizeof(exefs_hdr),ncchset->options.mediaSize); - exefsSize = align(ncchset->sections.exeFs.size,ncchset->options.mediaSize); - exefsOffset = align(ncchSize,ncchset->options.mediaSize); + if(set->sections.exeFs.size){ + exefsHashSize = align(sizeof(exefs_hdr),set->options.blockSize); + exefsSize = align(set->sections.exeFs.size,set->options.blockSize); + exefsOffset = align(ncchSize,set->options.blockSize); ncchSize = exefsOffset + exefsSize; } else exefsSize = 0; if(romfs->romfsSize){ - romfsHashSize = align(romfs->romfsHeaderSize,ncchset->options.mediaSize); - romfsSize = align(romfs->romfsSize,ncchset->options.mediaSize); - //romfsOffset = align(ncchSize,0x200); // Old makerom method, SDK 2.x and prior + romfsHashSize = align(romfs->romfsHeaderSize,set->options.blockSize); + romfsSize = align(romfs->romfsSize,set->options.blockSize); + //romfsOffset = align(ncchSize,set->options.blockSize); // Old makerom method, SDK 2.x and prior romfsOffset = align(ncchSize,0x1000); ncchSize = romfsOffset + romfsSize; } @@ -416,7 +422,8 @@ int SetupNcch(ncch_settings *ncchset, romfs_buildctx *romfs) // Aligning Total NCCH Size - ncchSize = align(ncchSize,ncchset->options.mediaSize); + ncchSize = align(ncchSize,set->options.blockSize); + u8 *ncch = calloc(1,ncchSize); if(!ncch){ fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); @@ -424,240 +431,231 @@ int SetupNcch(ncch_settings *ncchset, romfs_buildctx *romfs) } // Setting up hdr\n"); - ncch_hdr *hdr = (ncch_hdr*)(ncch+0x100); - int ret = SetCommonHeaderBasicData(ncchset,hdr); + ncch_hdr *hdr = (ncch_hdr*)ncch; + int ret = SetCommonHeaderBasicData(set,hdr); if(ret != 0){ free(ncch); return ret; } - u32_to_u8(hdr->ncchSize,ncchSize/ncchset->options.mediaSize,LE); + u32_to_u8(hdr->ncchSize,ncchSize/set->options.blockSize,LE); - // Copy already built sections to ncch\n"); + // Copy already built sections to ncch if(exhdrSize){ - memcpy((u8*)(ncch+exhdrOffset),ncchset->sections.exhdr.buffer,ncchset->sections.exhdr.size); - free(ncchset->sections.exhdr.buffer); - ncchset->sections.exhdr.buffer = NULL; + memcpy((u8*)(ncch+exhdrOffset),set->sections.exhdr.buffer,set->sections.exhdr.size); + free(set->sections.exhdr.buffer); + set->sections.exhdr.buffer = NULL; u32_to_u8(hdr->exhdrSize,exhdrSize,LE); } if(acexSize){ - memcpy((u8*)(ncch+acexOffset),ncchset->sections.acexDesc.buffer,ncchset->sections.acexDesc.size); - free(ncchset->sections.acexDesc.buffer); - ncchset->sections.acexDesc.buffer = NULL; + memcpy((u8*)(ncch+acexOffset),set->sections.acexDesc.buffer,set->sections.acexDesc.size); + free(set->sections.acexDesc.buffer); + set->sections.acexDesc.buffer = NULL; } if(logoSize){ - memcpy((u8*)(ncch+logoOffset),ncchset->sections.logo.buffer,ncchset->sections.logo.size); - free(ncchset->sections.logo.buffer); - ncchset->sections.logo.buffer = NULL; - u32_to_u8(hdr->logoOffset,logoOffset/ncchset->options.mediaSize,LE); - u32_to_u8(hdr->logoSize,logoSize/ncchset->options.mediaSize,LE); + memcpy((u8*)(ncch+logoOffset),set->sections.logo.buffer,set->sections.logo.size); + free(set->sections.logo.buffer); + set->sections.logo.buffer = NULL; + u32_to_u8(hdr->logoOffset,logoOffset/set->options.blockSize,LE); + u32_to_u8(hdr->logoSize,logoSize/set->options.blockSize,LE); } if(plnRgnSize){ - memcpy((u8*)(ncch+plnRgnOffset),ncchset->sections.plainRegion.buffer,ncchset->sections.plainRegion.size); - free(ncchset->sections.plainRegion.buffer); - ncchset->sections.plainRegion.buffer = NULL; - u32_to_u8(hdr->plainRegionOffset,plnRgnOffset/ncchset->options.mediaSize,LE); - u32_to_u8(hdr->plainRegionSize,plnRgnSize/ncchset->options.mediaSize,LE); + memcpy((u8*)(ncch+plnRgnOffset),set->sections.plainRegion.buffer,set->sections.plainRegion.size); + free(set->sections.plainRegion.buffer); + set->sections.plainRegion.buffer = NULL; + u32_to_u8(hdr->plainRegionOffset,plnRgnOffset/set->options.blockSize,LE); + u32_to_u8(hdr->plainRegionSize,plnRgnSize/set->options.blockSize,LE); } if(exefsSize){ - memcpy((u8*)(ncch+exefsOffset),ncchset->sections.exeFs.buffer,ncchset->sections.exeFs.size); - free(ncchset->sections.exeFs.buffer); + memcpy((u8*)(ncch+exefsOffset),set->sections.exeFs.buffer,set->sections.exeFs.size); + free(set->sections.exeFs.buffer); - ncchset->sections.exeFs.buffer = NULL; + set->sections.exeFs.buffer = NULL; - u32_to_u8(hdr->exefsOffset,exefsOffset/ncchset->options.mediaSize,LE); + u32_to_u8(hdr->exefsOffset,exefsOffset/set->options.blockSize,LE); - u32_to_u8(hdr->exefsSize,exefsSize/ncchset->options.mediaSize,LE); + u32_to_u8(hdr->exefsSize,exefsSize/set->options.blockSize,LE); - u32_to_u8(hdr->exefsHashSize,exefsHashSize/ncchset->options.mediaSize,LE); + u32_to_u8(hdr->exefsHashSize,exefsHashSize/set->options.blockSize,LE); } // Point Romfs CTX to output buffer, if exists\n"); if(romfsSize){ romfs->output = ncch + romfsOffset; - u32_to_u8(hdr->romfsOffset,romfsOffset/ncchset->options.mediaSize,LE); - u32_to_u8(hdr->romfsSize,romfsSize/ncchset->options.mediaSize,LE); - u32_to_u8(hdr->romfsHashSize,romfsHashSize/ncchset->options.mediaSize,LE); + u32_to_u8(hdr->romfsOffset,romfsOffset/set->options.blockSize,LE); + u32_to_u8(hdr->romfsSize,romfsSize/set->options.blockSize,LE); + u32_to_u8(hdr->romfsHashSize,romfsHashSize/set->options.blockSize,LE); } - ncchset->out->buffer = ncch; - ncchset->out->size = ncchSize; + set->out->buffer = ncch; + set->out->size = ncchSize; - GetNCCHStruct(&ncchset->cryptoDetails,hdr); + GetNcchInfo(&set->cryptoDetails,hdr); return 0; } -int FinaliseNcch(ncch_settings *ncchset) +int FinaliseNcch(ncch_settings *set) { - u8 *ncch = ncchset->out->buffer; - - ncch_hdr *hdr = (ncch_hdr*)(ncch + 0x100); - u8 *exhdr = (u8*)(ncch + ncchset->cryptoDetails.exhdrOffset); - u8 *acexDesc = (u8*)(ncch + ncchset->cryptoDetails.acexOffset); - u8 *logo = (u8*)(ncch + ncchset->cryptoDetails.logoOffset); - u8 *exefs = (u8*)(ncch + ncchset->cryptoDetails.exefsOffset); - u8 *romfs = (u8*)(ncch + ncchset->cryptoDetails.romfsOffset); - - // Taking Hashes\n"); - if(ncchset->cryptoDetails.exhdrSize) - ctr_sha(exhdr,ncchset->cryptoDetails.exhdrSize,hdr->exhdrHash,CTR_SHA_256); - if(ncchset->cryptoDetails.logoSize) - ctr_sha(logo,ncchset->cryptoDetails.logoSize,hdr->logoHash,CTR_SHA_256); - if(ncchset->cryptoDetails.exefsHashDataSize) - ctr_sha(exefs,ncchset->cryptoDetails.exefsHashDataSize,hdr->exefsHash,CTR_SHA_256); - if(ncchset->cryptoDetails.romfsHashDataSize) - ctr_sha(romfs,ncchset->cryptoDetails.romfsHashDataSize,hdr->romfsHash,CTR_SHA_256); - - // Signing NCCH\n"); + u8 *ncch = set->out->buffer; + + ncch_hdr *hdr = (ncch_hdr*)ncch; + u8 *exhdr = (u8*)(ncch + set->cryptoDetails.exhdrOffset); + u8 *acexDesc = (u8*)(ncch + set->cryptoDetails.acexOffset); + u8 *logo = (u8*)(ncch + set->cryptoDetails.logoOffset); + u8 *exefs = (u8*)(ncch + set->cryptoDetails.exefsOffset); + u8 *romfs = (u8*)(ncch + set->cryptoDetails.romfsOffset); + + // Taking Hashes + if(set->cryptoDetails.exhdrSize) + ctr_sha(exhdr,set->cryptoDetails.exhdrSize,hdr->exhdrHash,CTR_SHA_256); + if(set->cryptoDetails.logoSize) + ctr_sha(logo,set->cryptoDetails.logoSize,hdr->logoHash,CTR_SHA_256); + if(set->cryptoDetails.exefsHashDataSize) + ctr_sha(exefs,set->cryptoDetails.exefsHashDataSize,hdr->exefsHash,CTR_SHA_256); + if(set->cryptoDetails.romfsHashDataSize) + ctr_sha(romfs,set->cryptoDetails.romfsHashDataSize,hdr->romfsHash,CTR_SHA_256); + + // Signing NCCH int sig_result = Good; - if(ncchset->options.IsCfa) sig_result = SignCFA(ncch,(u8*)hdr,ncchset->keys); - else sig_result = SignCXI(ncch,(u8*)hdr,ncchset->keys); + if(set->options.IsCfa) + sig_result = SignCFA(hdr,set->keys); + else + sig_result = SignCXI(hdr,set->keys); if(sig_result != Good){ - fprintf(stderr,"[NCCH ERROR] Failed to sign %s header\n",ncchset->options.IsCfa ? "CFA" : "CXI"); + fprintf(stderr,"[NCCH ERROR] Failed to sign %s header\n",set->options.IsCfa ? "CFA" : "CXI"); return sig_result; } - //memdump(stdout,"ncch: ",ncch,0x200); // Crypting NCCH\n"); - ncch_key_type keyType = GetNCCHKeyType(hdr); - if(keyType != NoKey){ - SetNcchUnfixedKeys(ncchset->keys, ncch); - - // Getting AES Keys - u8 *key0 = GetNCCHKey0(keyType, ncchset->keys); - u8 *key1 = GetNCCHKey1(keyType, ncchset->keys); - - if(key0 == NULL || key1 == NULL){ - fprintf(stderr,"[NCCH ERROR] Failed to load ncch aes key\n"); - free(ncch); + if(IsNcchEncrypted(hdr)){ + if(!SetNcchKeys(set->keys, hdr)){ + fprintf(stderr,"[NCCH ERROR] Failed to load NCCH AES key\n"); return -1; } - /* - memdump(stdout,"key0: ",key0,16); - memdump(stdout,"key1: ",key1,16); - */ + if(set->options.verbose){ + printf("[NCCH] NCCH AES keys:\n"); + memdump(stdout," > key0: ",set->keys->aes.ncchKey0,AES_128_KEY_SIZE); + memdump(stdout," > key1: ",set->keys->aes.ncchKey1,AES_128_KEY_SIZE); + } // Crypting Exheader/AcexDesc - if(ncchset->cryptoDetails.exhdrSize){ - CryptNCCHSection(exhdr,ncchset->cryptoDetails.exhdrSize,0x0,&ncchset->cryptoDetails,key0,ncch_exhdr); - CryptNCCHSection(acexDesc,ncchset->cryptoDetails.acexSize,ncchset->cryptoDetails.exhdrSize,&ncchset->cryptoDetails,key0,ncch_exhdr); + if(set->cryptoDetails.exhdrSize){ + CryptNcchRegion(exhdr,set->cryptoDetails.exhdrSize,0x0,&set->cryptoDetails,set->keys->aes.ncchKey0,ncch_exhdr); + CryptNcchRegion(acexDesc,set->cryptoDetails.acexSize,set->cryptoDetails.exhdrSize,&set->cryptoDetails,set->keys->aes.ncchKey0,ncch_exhdr); } // Crypting ExeFs Files - if(ncchset->cryptoDetails.exefsSize){ + if(set->cryptoDetails.exefsSize){ exefs_hdr *exefsHdr = (exefs_hdr*)exefs; for(int i = 0; i < MAX_EXEFS_SECTIONS; i++){ u8 *key = NULL; if(strncmp(exefsHdr->fileHdr[i].name,"icon",8) == 0 || strncmp(exefsHdr->fileHdr[i].name,"banner",8) == 0) - key = key0; + key = set->keys->aes.ncchKey0; else - key = key1; - - u32 offset = u8_to_u32(exefsHdr->fileHdr[i].offset,LE) + 0x200; + key = set->keys->aes.ncchKey1; + + u32 offset = u8_to_u32(exefsHdr->fileHdr[i].offset,LE) + sizeof(exefs_hdr); u32 size = u8_to_u32(exefsHdr->fileHdr[i].size,LE); if(size) - CryptNCCHSection((exefs+offset),align(size,ncchset->options.mediaSize),offset,&ncchset->cryptoDetails,key,ncch_exefs); + CryptNcchRegion((exefs+offset),align(size,set->options.blockSize),offset,&set->cryptoDetails,key,ncch_exefs); } // Crypting ExeFs Header - CryptNCCHSection(exefs,0x200,0x0,&ncchset->cryptoDetails,key0,ncch_exefs); + CryptNcchRegion(exefs,sizeof(exefs_hdr),0x0,&set->cryptoDetails,set->keys->aes.ncchKey0,ncch_exefs); } // Crypting RomFs - if(ncchset->cryptoDetails.romfsSize) - CryptNCCHSection(romfs,ncchset->cryptoDetails.romfsSize,0x0,&ncchset->cryptoDetails,key1,ncch_romfs); + if(set->cryptoDetails.romfsSize) + CryptNcchRegion(romfs,set->cryptoDetails.romfsSize,0x0,&set->cryptoDetails,set->keys->aes.ncchKey1,ncch_romfs); } return 0; } -int SetCommonHeaderBasicData(ncch_settings *ncchset, ncch_hdr *hdr) +int SetCommonHeaderBasicData(ncch_settings *set, ncch_hdr *hdr) { /* NCCH Magic */ memcpy(hdr->magic,"NCCH",4); /* NCCH Format Version */ - if(!ncchset->options.IsCfa) + if(!set->options.IsCfa) u16_to_u8(hdr->formatVersion,0x2,LE); /* Setting ProgramId/TitleId */ u64 ProgramId = 0; - int result = GetProgramID(&ProgramId,ncchset->rsfSet,false); + int result = GetProgramID(&ProgramId,set->rsfSet,false); if(result) return result; u64_to_u8(hdr->programId,ProgramId,LE); u64_to_u8(hdr->titleId,ProgramId,LE); /* Get Product Code and Maker Code */ - if(ncchset->rsfSet->BasicInfo.ProductCode){ - if(!IsValidProductCode((char*)ncchset->rsfSet->BasicInfo.ProductCode,ncchset->options.FreeProductCode)){ + if(set->rsfSet->BasicInfo.ProductCode){ + if(!IsValidProductCode((char*)set->rsfSet->BasicInfo.ProductCode,set->options.FreeProductCode)){ fprintf(stderr,"[NCCH ERROR] Invalid Product Code\n"); return NCCH_BAD_YAML_SET; } - memcpy(hdr->productCode,ncchset->rsfSet->BasicInfo.ProductCode,strlen((char*)ncchset->rsfSet->BasicInfo.ProductCode)); + memcpy(hdr->productCode,set->rsfSet->BasicInfo.ProductCode,strlen((char*)set->rsfSet->BasicInfo.ProductCode)); } else memcpy(hdr->productCode,"CTR-P-CTAP",10); - if(ncchset->rsfSet->BasicInfo.CompanyCode){ - if(strlen((char*)ncchset->rsfSet->BasicInfo.CompanyCode) != 2){ + if(set->rsfSet->BasicInfo.CompanyCode){ + if(strlen((char*)set->rsfSet->BasicInfo.CompanyCode) != 2){ fprintf(stderr,"[NCCH ERROR] CompanyCode length must be 2\n"); return NCCH_BAD_YAML_SET; } - memcpy(hdr->makerCode,ncchset->rsfSet->BasicInfo.CompanyCode,2); + memcpy(hdr->makerCode,set->rsfSet->BasicInfo.CompanyCode,2); } else memcpy(hdr->makerCode,"00",2); // Setting Encryption Settings - if(!ncchset->options.Encrypt) - hdr->flags[OtherFlag] = (NoCrypto|FixedCryptoKey); - else if(ncchset->keys->aes.ncchKeyX0){ - hdr->flags[OtherFlag] = UnFixedCryptoKey; - if(ncchset->keys->aes.ncchKeyX1 && !ncchset->options.IsCfa) - hdr->flags[SecureCrypto2] = 1; + if(!set->options.Encrypt) + hdr->flags[ncchflag_OTHER_FLAG] = (otherflag_NoCrypto|otherflag_FixedCryptoKey); + else if(set->options.useSecCrypto){ + hdr->flags[ncchflag_OTHER_FLAG] = otherflag_Clear; + hdr->flags[ncchflag_CONTENT_KEYX] = set->options.keyXID; } - else{ - hdr->flags[OtherFlag] = FixedCryptoKey; - u8 *key = GetNCCHKey0(GetNCCHKeyType(hdr),ncchset->keys); - if(!key){ // for detecting absense of fixed aes keys - hdr->flags[OtherFlag] = (NoCrypto|FixedCryptoKey); - fprintf(stderr,"[NCCH WARNING] NCCH AES Key could not be loaded, NCCH will not be encrypted\n"); - } + else + hdr->flags[ncchflag_OTHER_FLAG] = otherflag_FixedCryptoKey; + + if(!SetNcchKeys(set->keys,hdr) && set->options.Encrypt){ + hdr->flags[ncchflag_OTHER_FLAG] = (otherflag_NoCrypto|otherflag_FixedCryptoKey); + set->options.Encrypt = false; + fprintf(stderr,"[NCCH WARNING] NCCH AES Key could not be loaded, NCCH will not be encrypted\n"); } - - /* Set ContentUnitSize */ - hdr->flags[ContentUnitSize] = 0; // 0x200 + hdr->flags[ncchflag_CONTENT_BLOCK_SIZE] = GetCtrBlockSizeFlag(set->options.blockSize); /* Setting ContentPlatform */ - hdr->flags[ContentPlatform] = 1; // CTR + hdr->flags[ncchflag_CONTENT_PLATFORM] = 1; // CTR /* Setting OtherFlag */ - if(!ncchset->options.UseRomFS) - hdr->flags[OtherFlag] |= NoMountRomFs; + if(!set->options.UseRomFS) + hdr->flags[ncchflag_OTHER_FLAG] |= otherflag_NoMountRomFs; /* Setting ContentType */ - hdr->flags[ContentType] = 0; - if(ncchset->options.UseRomFS) hdr->flags[ContentType] |= content_Data; - if(!ncchset->options.IsCfa) hdr->flags[ContentType] |= content_Executable; - if(ncchset->rsfSet->BasicInfo.ContentType){ - if(strcmp(ncchset->rsfSet->BasicInfo.ContentType,"Application") == 0) hdr->flags[ContentType] |= 0; - else if(strcmp(ncchset->rsfSet->BasicInfo.ContentType,"SystemUpdate") == 0) hdr->flags[ContentType] |= content_SystemUpdate; - else if(strcmp(ncchset->rsfSet->BasicInfo.ContentType,"Manual") == 0) hdr->flags[ContentType] |= content_Manual; - else if(strcmp(ncchset->rsfSet->BasicInfo.ContentType,"Child") == 0) hdr->flags[ContentType] |= content_Child; - else if(strcmp(ncchset->rsfSet->BasicInfo.ContentType,"Trial") == 0) hdr->flags[ContentType] |= content_Trial; + hdr->flags[ncchflag_CONTENT_TYPE] = 0; + if(set->options.UseRomFS) hdr->flags[ncchflag_CONTENT_TYPE] |= content_Data; + if(!set->options.IsCfa) hdr->flags[ncchflag_CONTENT_TYPE] |= content_Executable; + if(set->rsfSet->BasicInfo.ContentType){ + if(strcmp(set->rsfSet->BasicInfo.ContentType,"Application") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= 0; + else if(strcmp(set->rsfSet->BasicInfo.ContentType,"SystemUpdate") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= content_SystemUpdate; + else if(strcmp(set->rsfSet->BasicInfo.ContentType,"Manual") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= content_Manual; + else if(strcmp(set->rsfSet->BasicInfo.ContentType,"Child") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= content_Child; + else if(strcmp(set->rsfSet->BasicInfo.ContentType,"Trial") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= content_Trial; else{ - fprintf(stderr,"[NCCH ERROR] Invalid ContentType '%s'\n",ncchset->rsfSet->BasicInfo.ContentType); + fprintf(stderr,"[NCCH ERROR] Invalid ContentType '%s'\n",set->rsfSet->BasicInfo.ContentType); return NCCH_BAD_YAML_SET; } } @@ -667,17 +665,27 @@ int SetCommonHeaderBasicData(ncch_settings *ncchset, ncch_hdr *hdr) bool IsValidProductCode(char *ProductCode, bool FreeProductCode) { - if(strlen(ProductCode) > 16) return false; + if(strlen(ProductCode) > 16) + return false; if(FreeProductCode) return true; - - if(strlen(ProductCode) < 10) return false; - if(strncmp(ProductCode,"CTR-",4) != 0) return false; - if(ProductCode[5] != '-') return false; - if(!isdigit(ProductCode[4]) && !isupper(ProductCode[4])) return false; - for(int i = 6; i < 10; i++){ - if(!isdigit(ProductCode[i]) && !isupper(ProductCode[i])) return false; + + if(strlen(ProductCode) < 10) + return false; + + if(strncmp(ProductCode,"CTR",3) != 0) + return false; + + for(int i = 3; i < 10; i++){ + if(i == 3 || i == 5){ + if(ProductCode[i] != '-') + return false; + } + else{ + if(!isdigit(ProductCode[i]) && !isupper(ProductCode[i])) + return false; + } } return true; @@ -685,80 +693,64 @@ bool IsValidProductCode(char *ProductCode, bool FreeProductCode) // NCCH Read Functions -int VerifyNCCH(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) +int VerifyNcch(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) { // Setup - u8 Hash[0x20]; - u8 *hdr_sig = ncch; - ncch_hdr* hdr = GetNCCH_CommonHDR(NULL,NULL,ncch); + ncch_hdr* hdr = (ncch_hdr*)ncch; - ncch_struct *ncch_ctx = calloc(1,sizeof(ncch_struct)); - if(!ncch_ctx){ + ncch_info *ncchInfo = calloc(1,sizeof(ncch_info)); + if(!ncchInfo){ fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); return MEM_ERROR; } - GetNCCHStruct(ncch_ctx,hdr); - - ncch_key_type keyType = GetNCCHKeyType(hdr); - u8 *key0 = NULL; - u8 *key1 = NULL; - if(keyType != NoKey){ - //memdump(stdout,"ncch: ",ncch,0x200); - SetNcchUnfixedKeys(keys,ncch); - if(GetNCCHKey0(keyType,keys) == NULL){ - if(!SuppressOutput) - fprintf(stderr,"[NCCH ERROR] Failed to load ncch aes key.\n"); - return UNABLE_TO_LOAD_NCCH_KEY; - } - key0 = GetNCCHKey0(keyType,keys); - key1 = GetNCCHKey1(keyType,keys); - } + GetNcchInfo(ncchInfo,hdr); - //memdump(stdout,"key0: ",key0,16); - //memdump(stdout,"key1: ",key1,16); + if(!SetNcchKeys(keys, hdr)){ + fprintf(stderr,"[NCCH ERROR] Failed to load ncch aes key\n"); + return UNABLE_TO_LOAD_NCCH_KEY; + } if(IsCfa(hdr)){ - if(CheckCFASignature(hdr_sig,(u8*)hdr,keys) != Good && !keys->rsa.isFalseSign){ + if(CheckCFASignature(hdr,keys) != Good && !keys->rsa.isFalseSign){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] CFA Sigcheck Failed\n"); - free(ncch_ctx); + free(ncchInfo); return NCCH_HDR_SIG_BAD; } - if(!ncch_ctx->romfsSize){ + if(!ncchInfo->romfsSize){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] CFA is corrupt\n"); - free(ncch_ctx); + free(ncchInfo); return NO_ROMFS_IN_CFA; } } else{ // IsCxi // Checking for necessary sections - if(!ncch_ctx->exhdrSize){ + if(!ncchInfo->exhdrSize){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] CXI is corrupt\n"); - free(ncch_ctx); + free(ncchInfo); return NO_EXHEADER_IN_CXI; } - if(!ncch_ctx->exefsSize){ + if(!ncchInfo->exefsSize){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] CXI is corrupt\n"); - free(ncch_ctx); + free(ncchInfo); return NO_EXEFS_IN_CXI; } // Get ExHeader/AcexDesc - extended_hdr *exHdr = malloc(ncch_ctx->exhdrSize); + extended_hdr *exHdr = malloc(ncchInfo->exhdrSize); if(!exHdr){ fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); - free(ncch_ctx); + free(ncchInfo); return MEM_ERROR; } - memcpy(exHdr,ncch+ncch_ctx->exhdrOffset,ncch_ctx->exhdrSize); - if(key0 != NULL) - CryptNCCHSection((u8*)exHdr,ncch_ctx->exhdrSize,0,ncch_ctx,key0,ncch_exhdr); + memcpy(exHdr,ncch+ncchInfo->exhdrOffset,ncchInfo->exhdrSize); + if(IsNcchEncrypted(hdr)) + CryptNcchRegion((u8*)exHdr,ncchInfo->exhdrSize,0,ncchInfo,keys->aes.ncchKey0,ncch_exhdr); // Checking Exheader Hash to see if decryption was sucessful - ctr_sha(exHdr,0x400,Hash,CTR_SHA_256); - if(memcmp(Hash,hdr->exhdrHash,0x20) != 0){ + if(!VerifySha256(exHdr, ncchInfo->exhdrSize, hdr->exhdrHash)){ //memdump(stdout,"Expected Hash: ",hdr->extended_header_sha_256_hash,0x20); //memdump(stdout,"Actual Hash: ",Hash,0x20); //memdump(stdout,"Exheader: ",(u8*)exHdr,0x400); @@ -766,36 +758,34 @@ int VerifyNCCH(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) fprintf(stderr,"[NCCH ERROR] ExHeader Hashcheck Failed\n"); fprintf(stderr,"[NCCH ERROR] CXI is corrupt\n"); } - free(ncch_ctx); + free(ncchInfo); free(exHdr); - return ExHeader_Hashfail; + return EXHDR_CORRUPT; } free(exHdr); // Checking RSA Sigs - access_descriptor *acexDesc = malloc(ncch_ctx->acexSize); + access_descriptor *acexDesc = malloc(ncchInfo->acexSize); if(!acexDesc){ fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); - free(ncch_ctx); + free(ncchInfo); free(exHdr); return MEM_ERROR; } - memcpy(acexDesc,ncch+ncch_ctx->acexOffset,ncch_ctx->acexSize); - if(key0 != NULL) - CryptNCCHSection((u8*)acexDesc,ncch_ctx->acexSize,ncch_ctx->exhdrOffset,ncch_ctx,key0,ncch_exhdr); + memcpy(acexDesc,ncch+ncchInfo->acexOffset,ncchInfo->acexSize); + if(IsNcchEncrypted(hdr)) + CryptNcchRegion((u8*)acexDesc,ncchInfo->acexSize,ncchInfo->exhdrSize,ncchInfo,keys->aes.ncchKey0,ncch_exhdr); if(CheckAccessDescSignature(acexDesc,keys) != 0 && !keys->rsa.isFalseSign){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] AccessDesc Sigcheck Failed\n"); - free(ncch_ctx); + free(ncchInfo); free(acexDesc); return ACCESSDESC_SIG_BAD; } - - u8 *hdr_pubk = GetAcexNcchPubKey(acexDesc); - - if(CheckCXISignature(hdr_sig,(u8*)hdr,hdr_pubk) != 0 && !keys->rsa.isFalseSign){ + + if(CheckCXISignature(hdr,GetAcexNcchPubKey(acexDesc)) != 0 && !keys->rsa.isFalseSign){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] CXI Header Sigcheck Failed\n"); - free(ncch_ctx); + free(ncchInfo); free(acexDesc); return NCCH_HDR_SIG_BAD; } @@ -805,93 +795,70 @@ int VerifyNCCH(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) return 0; /* Checking ExeFs Hash, if present */ - if(ncch_ctx->exefsSize) + if(ncchInfo->exefsSize) { - u8 *ExeFs = malloc(ncch_ctx->exefsHashDataSize); - if(!ExeFs){ + u8 *exefs = malloc(ncchInfo->exefsHashDataSize); + if(!exefs){ fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); - free(ncch_ctx); + free(ncchInfo); return MEM_ERROR; } - memcpy(ExeFs,ncch+ncch_ctx->exefsOffset,ncch_ctx->exefsHashDataSize); - if(key0 != NULL) - CryptNCCHSection(ExeFs,ncch_ctx->exefsHashDataSize,0,ncch_ctx,key0,ncch_exefs); - ctr_sha(ExeFs,ncch_ctx->exefsHashDataSize,Hash,CTR_SHA_256); - free(ExeFs); - if(memcmp(Hash,hdr->exefsHash,0x20) != 0){ + memcpy(exefs,ncch+ncchInfo->exefsOffset,ncchInfo->exefsHashDataSize); + if(IsNcchEncrypted(hdr)) + CryptNcchRegion(exefs,ncchInfo->exefsHashDataSize,0,ncchInfo,keys->aes.ncchKey0,ncch_exefs); + if(!VerifySha256(exefs, ncchInfo->exefsHashDataSize, hdr->exefsHash)){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] ExeFs Hashcheck Failed\n"); - free(ncch_ctx); - return ExeFs_Hashfail; + free(ncchInfo); + free(exefs); + return EXEFS_CORRUPT; } + free(exefs); } /* Checking RomFs hash, if present */ - if(ncch_ctx->romfsSize){ - u8 *RomFs = malloc(ncch_ctx->romfsHashDataSize); - if(!RomFs){ + if(ncchInfo->romfsSize){ + u8 *romfs = malloc(ncchInfo->romfsHashDataSize); + if(!romfs){ fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); - free(ncch_ctx); + free(ncchInfo); return MEM_ERROR; } - memcpy(RomFs,ncch+ncch_ctx->romfsOffset,ncch_ctx->romfsHashDataSize); - if(key1 != NULL) - CryptNCCHSection(RomFs,ncch_ctx->romfsHashDataSize,0,ncch_ctx,key1,ncch_romfs); - ctr_sha(RomFs,ncch_ctx->romfsHashDataSize,Hash,CTR_SHA_256); - free(RomFs); - if(memcmp(Hash,hdr->romfsHash,0x20) != 0){ + memcpy(romfs,ncch+ncchInfo->romfsOffset,ncchInfo->romfsHashDataSize); + if(IsNcchEncrypted(hdr)) + CryptNcchRegion(romfs,ncchInfo->romfsHashDataSize,0,ncchInfo,keys->aes.ncchKey1,ncch_romfs); + if(!VerifySha256(romfs,ncchInfo->romfsHashDataSize,hdr->romfsHash)){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] RomFs Hashcheck Failed\n"); - free(ncch_ctx); - return ExeFs_Hashfail; + free(ncchInfo); + free(romfs); + return ROMFS_CORRUPT; } + free(romfs); } /* Checking the Logo Hash, if present */ - if(ncch_ctx->logoSize){ - u8 *logo = (ncch+ncch_ctx->logoOffset); - ctr_sha(logo,ncch_ctx->logoSize,Hash,CTR_SHA_256); - if(memcmp(Hash,hdr->logoHash,0x20) != 0){ + if(ncchInfo->logoSize){ + u8 *logo = (ncch+ncchInfo->logoOffset); + if(!VerifySha256(logo,ncchInfo->logoSize,hdr->logoHash)){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] Logo Hashcheck Failed\n"); - free(ncch_ctx); - return Logo_Hashfail; + free(ncchInfo); + return LOGO_CORRUPT; } } - free(ncch_ctx); + free(ncchInfo); return 0; } - -u8* RetargetNCCH(FILE *fp, u64 size, u8 *TitleId, u8 *ProgramId, keys_struct *keys) -{ - u8 *ncch = calloc(1,size); - if(!ncch){ - fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); - return NULL; - } - ReadFile_64(ncch,size,0,fp); // Importing - - if(ModifyNcchIds(ncch,TitleId, ProgramId, keys) != 0){ - free(ncch); - return NULL; - } - - return ncch; -} - int ModifyNcchIds(u8 *ncch, u8 *titleId, u8 *programId, keys_struct *keys) { - if(!IsNCCH(NULL,ncch)){ - //free(ncch); + if(!IsNcch(NULL,ncch)) return -1; - } - ncch_hdr *hdr = NULL; - hdr = GetNCCH_CommonHDR(NULL,NULL,ncch); + ncch_hdr *hdr = (ncch_hdr*)ncch; if(/*keys->rsa.requiresPresignedDesc && */!IsCfa(hdr)){ fprintf(stderr,"[NCCH ERROR] CXI's ID cannot be modified without the ability to resign the AccessDesc\n"); // Not yet yet, requires AccessDesc Privk, may implement anyway later - //free(ncch); return -1; } @@ -901,29 +868,19 @@ int ModifyNcchIds(u8 *ncch, u8 *titleId, u8 *programId, keys_struct *keys) if(titleIdMatches && programIdMatches) return 0;// if no modification is required don't do anything - if(titleIdMatches){ // If TitleID Same, no crypto required, just resign. - memcpy(hdr->programId,programId,8); - SignCFA(ncch,(u8*)hdr,keys); - return 0; - } - ncch_key_type keytype = GetNCCHKeyType(hdr); - ncch_struct ncch_struct; - u8 *key = NULL; + ncch_info ncchInfo; u8 *romfs = NULL; //Decrypting if necessary - if(keytype != NoKey){ - GetNCCHStruct(&ncch_struct,hdr); - romfs = (ncch+ncch_struct.romfsOffset); - SetNcchUnfixedKeys(keys, ncch); // For Secure Crypto - key = GetNCCHKey1(keytype,keys); - if(key == NULL){ + if(IsNcchEncrypted(hdr)){ + GetNcchInfo(&ncchInfo,hdr); + romfs = (ncch+ncchInfo.romfsOffset); + if(!SetNcchKeys(keys, hdr)){ fprintf(stderr,"[NCCH ERROR] Failed to load ncch aes key\n"); - //free(ncch); return -1; } - CryptNCCHSection(romfs,ncch_struct.romfsSize,0,&ncch_struct,key,ncch_romfs); + CryptNcchRegion(romfs,ncchInfo.romfsSize,0,&ncchInfo,keys->aes.ncchKey1,ncch_romfs); } // Editing data and resigning @@ -931,192 +888,188 @@ int ModifyNcchIds(u8 *ncch, u8 *titleId, u8 *programId, keys_struct *keys) memcpy(hdr->titleId,titleId,8); if(programId) memcpy(hdr->programId,programId,8); - SignCFA(ncch,(u8*)hdr,keys); - - //Checking New Key Type - keytype = GetNCCHKeyType(hdr); + SignCFA(hdr,keys); // Re-encrypting if necessary - if(keytype != NoKey){ - GetNCCHStruct(&ncch_struct,hdr); - romfs = (ncch+ncch_struct.romfsOffset); - SetNcchUnfixedKeys(keys, ncch); // For Secure Crypto - key = GetNCCHKey1(keytype,keys); - if(key == NULL){ + if(IsNcchEncrypted(hdr)){ + GetNcchInfo(&ncchInfo,hdr); + romfs = (ncch+ncchInfo.romfsOffset); + if(!SetNcchKeys(keys, hdr)){ fprintf(stderr,"[NCCH ERROR] Failed to load ncch aes key\n"); - //free(ncch); return -1; } - CryptNCCHSection(romfs,ncch_struct.romfsSize,0,&ncch_struct,key,ncch_romfs); + CryptNcchRegion(romfs,ncchInfo.romfsSize,0,&ncchInfo,keys->aes.ncchKey1,ncch_romfs); } return 0; } -ncch_hdr* GetNCCH_CommonHDR(void *out, FILE *fp, u8 *buf) +void ReadNcchHdr(ncch_hdr *hdr, FILE *fp) { - if(!fp && !buf) return NULL; - if(fp){ - if(!out) return NULL; - ReadFile_64(out,0x100,0x100,fp); - return (ncch_hdr*)out; - } - else{ - return (ncch_hdr*)(buf+0x100); - } + if(!fp || !hdr) + return; + + ReadFile64(hdr,sizeof(ncch_hdr),0,fp); + + return; } +u8* GetNcchHdrSig(ncch_hdr *hdr) +{ + return (u8*)hdr->signature; +} + +u8* GetNcchHdrData(ncch_hdr *hdr) +{ + return (u8*)hdr->magic; +} -bool IsNCCH(FILE *fp, u8 *buf) +u32 GetNcchHdrSigLen(ncch_hdr *hdr) { - if(!fp && !buf) return false; - ncch_hdr *ncchHDR = NULL; + return 0x100; +} + +u32 GetNcchHdrDataLen(ncch_hdr *hdr) +{ + return 0x100; +} + +bool IsNcch(FILE *fp, u8 *buf) +{ + if(!fp && !buf) + return false; + + ncch_hdr *hdr; bool result; + if(fp) { - ncchHDR = malloc(sizeof(ncch_hdr)); - GetNCCH_CommonHDR(ncchHDR,fp,NULL); - result = (memcmp(ncchHDR->magic,"NCCH",4) == 0); - free(ncchHDR); + hdr = malloc(sizeof(ncch_hdr)); + ReadNcchHdr(hdr,fp); + result = (memcmp(hdr->magic,"NCCH",4) == 0); + free(hdr); } else { - ncchHDR = GetNCCH_CommonHDR(ncchHDR,NULL,buf); - result = (memcmp(ncchHDR->magic,"NCCH",4) == 0); + hdr = (ncch_hdr*)buf; + result = (memcmp(hdr->magic,"NCCH",4) == 0); } return result; } bool IsCfa(ncch_hdr* hdr) { - return (((hdr->flags[ContentType] & content_Data) == content_Data) && ((hdr->flags[ContentType] & content_Executable) != content_Executable)); + return (((hdr->flags[ncchflag_CONTENT_TYPE] & content_Data) == content_Data) && ((hdr->flags[ncchflag_CONTENT_TYPE] & content_Executable) != content_Executable)); } -u32 GetNCCH_MediaUnitSize(ncch_hdr* hdr) +bool IsUpdateCfa(ncch_hdr* hdr) { - u16 formatVersion = u8_to_u16(hdr->formatVersion,LE); - u32 ret = 0; - if (formatVersion == 1) - ret = 1; - else if (formatVersion == 2 || formatVersion == 0) - ret = 1 << (hdr->flags[ContentUnitSize] + 9); - return ret; - //return 0x200*pow(2,hdr->flags[ContentUnitSize]); + return (((hdr->flags[ncchflag_CONTENT_TYPE] & content_SystemUpdate) == content_SystemUpdate) && ((hdr->flags[ncchflag_CONTENT_TYPE] & content_Child) != content_Child) && IsCfa(hdr)); } -u32 GetNCCH_MediaSize(ncch_hdr* hdr) +u32 GetNcchBlockSize(ncch_hdr* hdr) { - return u8_to_u32(hdr->ncchSize,LE); + /* + u16 formatVersion = u8_to_u16(hdr->formatVersion,LE); + if (formatVersion == 1) + return 1; + */ + return GetCtrBlockSize(hdr->flags[ncchflag_CONTENT_BLOCK_SIZE]); //formatVersion == 2 || formatVersion == 0 } -ncch_key_type GetNCCHKeyType(ncch_hdr* hdr) -{ - // Non-Secure Key Options - if((hdr->flags[OtherFlag] & NoCrypto) == NoCrypto) - return NoKey; - if((hdr->flags[OtherFlag] & FixedCryptoKey) == FixedCryptoKey){ - if((hdr->programId[4] & 0x10) == 0x10) - return KeyIsSystemFixed; - else - return KeyIsNormalFixed; - } - - // Secure Key Options - if(hdr->flags[SecureCrypto2]) - return KeyIsUnFixed2; - return KeyIsUnFixed; +u64 GetNcchSize(ncch_hdr* hdr) +{ + return (u64)u8_to_u32(hdr->ncchSize,LE) * (u64)GetNcchBlockSize(hdr); } -u8* GetNCCHKey0(ncch_key_type keytype, keys_struct *keys) +bool IsNcchEncrypted(ncch_hdr *hdr) { - switch(keytype){ - case NoKey: return NULL; - case KeyIsNormalFixed: - return keys->aes.normalKey; - case KeyIsSystemFixed: - return keys->aes.systemFixedKey; - case KeyIsUnFixed: - case KeyIsUnFixed2: - if(keys->aes.ncchKeyX0) - return keys->aes.unFixedKey0; - else - return NULL; - } - return NULL; + return (hdr->flags[ncchflag_OTHER_FLAG] & otherflag_NoCrypto) != otherflag_NoCrypto; } -u8* GetNCCHKey1(ncch_key_type keytype, keys_struct *keys) +bool SetNcchKeys(keys_struct *keys, ncch_hdr *hdr) { - switch(keytype){ - case NoKey: return NULL; - case KeyIsNormalFixed: - return keys->aes.normalKey; - case KeyIsSystemFixed: - return keys->aes.systemFixedKey; - case KeyIsUnFixed: - if(keys->aes.ncchKeyX0) - return keys->aes.unFixedKey0; - else - return NULL; - case KeyIsUnFixed2: - if(keys->aes.ncchKeyX1) - return keys->aes.unFixedKey1; - else - return NULL; + if(!IsNcchEncrypted(hdr)) + return true; + + if((hdr->flags[ncchflag_OTHER_FLAG] & otherflag_FixedCryptoKey) == otherflag_FixedCryptoKey){ + if((hdr->programId[4] & 0x10) == 0x10 && keys->aes.systemFixedKey){ + memcpy(keys->aes.ncchKey0,keys->aes.systemFixedKey,AES_128_KEY_SIZE); + memcpy(keys->aes.ncchKey1,keys->aes.systemFixedKey,AES_128_KEY_SIZE); + return true; + } + else if(keys->aes.normalKey){ + memcpy(keys->aes.ncchKey0,keys->aes.normalKey,AES_128_KEY_SIZE); + memcpy(keys->aes.ncchKey1,keys->aes.normalKey,AES_128_KEY_SIZE); + return true; + } + return false; } - return NULL; + + if(keys->aes.ncchKeyX[0]) + AesKeyScrambler(keys->aes.ncchKey0,keys->aes.ncchKeyX[0],hdr->signature); + else + return false; + + if(keys->aes.ncchKeyX[hdr->flags[ncchflag_CONTENT_KEYX]]) + AesKeyScrambler(keys->aes.ncchKey1,keys->aes.ncchKeyX[hdr->flags[ncchflag_CONTENT_KEYX]],hdr->signature); + else + return false; + + return true; } -int GetNCCHStruct(ncch_struct *ctx, ncch_hdr *header) +int GetNcchInfo(ncch_info *info, ncch_hdr *hdr) { - memcpy(ctx->titleId,header->titleId,8); - memcpy(ctx->programId,header->programId,8); + memcpy(info->titleId,hdr->titleId,8); + memcpy(info->programId,hdr->programId,8); - u32 media_unit = GetNCCH_MediaUnitSize(header); + u32 media_unit = GetNcchBlockSize(hdr); - ctx->formatVersion = u8_to_u16(header->formatVersion,LE); - if(!IsCfa(header)){ - ctx->exhdrOffset = 0x200; - ctx->exhdrSize = u8_to_u32(header->exhdrSize,LE); - ctx->acexOffset = (ctx->exhdrOffset + ctx->exhdrSize); - ctx->acexSize = sizeof(access_descriptor); - ctx->plainRegionOffset = (u64)(u8_to_u32(header->plainRegionOffset,LE)*media_unit); - ctx->plainRegionSize = (u64)(u8_to_u32(header->plainRegionSize,LE)*media_unit); + info->formatVersion = u8_to_u16(hdr->formatVersion,LE); + if(!IsCfa(hdr)){ + info->exhdrOffset = 0x200; + info->exhdrSize = u8_to_u32(hdr->exhdrSize,LE); + info->acexOffset = (info->exhdrOffset + info->exhdrSize); + info->acexSize = sizeof(access_descriptor); + info->plainRegionOffset = (u64)(u8_to_u32(hdr->plainRegionOffset,LE)*media_unit); + info->plainRegionSize = (u64)(u8_to_u32(hdr->plainRegionSize,LE)*media_unit); } - ctx->logoOffset = (u64)(u8_to_u32(header->logoOffset,LE)*media_unit); - ctx->logoSize = (u64)(u8_to_u32(header->logoSize,LE)*media_unit); - ctx->exefsOffset = (u64)(u8_to_u32(header->exefsOffset,LE)*media_unit); - ctx->exefsSize = (u64)(u8_to_u32(header->exefsSize,LE)*media_unit); - ctx->exefsHashDataSize = (u64)(u8_to_u32(header->exefsHashSize,LE)*media_unit); - ctx->romfsOffset = (u64) (u8_to_u32(header->romfsOffset,LE)*media_unit); - ctx->romfsSize = (u64) (u8_to_u32(header->romfsSize,LE)*media_unit); - ctx->romfsHashDataSize = (u64)(u8_to_u32(header->romfsHashSize,LE)*media_unit); + info->logoOffset = (u64)(u8_to_u32(hdr->logoOffset,LE)*media_unit); + info->logoSize = (u64)(u8_to_u32(hdr->logoSize,LE)*media_unit); + info->exefsOffset = (u64)(u8_to_u32(hdr->exefsOffset,LE)*media_unit); + info->exefsSize = (u64)(u8_to_u32(hdr->exefsSize,LE)*media_unit); + info->exefsHashDataSize = (u64)(u8_to_u32(hdr->exefsHashSize,LE)*media_unit); + info->romfsOffset = (u64) (u8_to_u32(hdr->romfsOffset,LE)*media_unit); + info->romfsSize = (u64) (u8_to_u32(hdr->romfsSize,LE)*media_unit); + info->romfsHashDataSize = (u64)(u8_to_u32(hdr->romfsHashSize,LE)*media_unit); return 0; } -void CryptNCCHSection(u8 *buffer, u64 size, u64 src_pos, ncch_struct *ctx, u8 key[16], u8 type) +void CryptNcchRegion(u8 *buffer, u64 size, u64 src_pos, ncch_info *ctx, u8 key[16], u8 type) { if(type < 1 || type > 3) return; u8 counter[0x10]; - ncch_get_counter(ctx,counter,type); ctr_aes_context aes_ctx; memset(&aes_ctx,0x0,sizeof(ctr_aes_context)); + + GetNcchAesCounter(ctx,counter,type); ctr_init_counter(&aes_ctx, key, counter); + if(src_pos > 0){ u32 carry = 0; carry = align(src_pos,0x10); carry /= 0x10; ctr_add_counter(&aes_ctx,carry); } - ctr_crypt_counter(&aes_ctx, buffer, buffer, size); return; } -void ncch_get_counter(ncch_struct *ctx, u8 counter[16], u8 type) +void GetNcchAesCounter(ncch_info *ctx, u8 counter[16], u8 type) { u8 *titleId = ctx->titleId; u32 i; diff --git a/makerom/ncch.h b/makerom/ncch.h index 232a228e..e87ae48e 100644 --- a/makerom/ncch.h +++ b/makerom/ncch.h @@ -15,10 +15,10 @@ typedef enum ACCESSDESC_SIG_BAD = -10, NCCH_HDR_SIG_BAD = -11, // HashCheck Errors - ExHeader_Hashfail = -12, - Logo_Hashfail = -13, - ExeFs_Hashfail = -14, - RomFs_Hashfail = -15, + EXHDR_CORRUPT = -12, + LOGO_CORRUPT = -13, + EXEFS_CORRUPT = -14, + ROMFS_CORRUPT = -15, // Others NCCH_BAD_YAML_SET = -16, DATA_POS_DNE = -17, @@ -29,34 +29,23 @@ typedef enum ncch_exhdr = 1, ncch_exefs, ncch_romfs, - ncch_Logo, - ncch_PlainRegion, } ncch_section; typedef enum { - NoKey, - KeyIsNormalFixed, - KeyIsSystemFixed, - KeyIsUnFixed, - KeyIsUnFixed2, -} ncch_key_type; - -typedef enum -{ - SecureCrypto2 = 3, - ContentPlatform = 4, - ContentType = 5, - ContentUnitSize = 6, - OtherFlag = 7 + ncchflag_CONTENT_KEYX = 3, + ncchflag_CONTENT_PLATFORM = 4, + ncchflag_CONTENT_TYPE = 5, + ncchflag_CONTENT_BLOCK_SIZE = 6, + ncchflag_OTHER_FLAG = 7 } ncch_flags; typedef enum { - UnFixedCryptoKey = 0x0, - FixedCryptoKey = 0x1, - NoMountRomFs = 0x2, - NoCrypto = 0x4, + otherflag_Clear = 0, + otherflag_FixedCryptoKey = (1 << 0), + otherflag_NoMountRomFs = (1 << 1), + otherflag_NoCrypto = (1 << 2), } ncch_otherflag_bitmask; typedef enum @@ -88,10 +77,11 @@ typedef struct u64 romfsHashDataSize; u8 titleId[8]; u8 programId[8]; -} ncch_struct; +} ncch_info; typedef struct { + u8 signature[0x100]; u8 magic[4]; u8 ncchSize[4]; u8 titleId[8]; @@ -122,109 +112,24 @@ typedef struct u8 romfsHash[0x20]; } ncch_hdr; - -typedef struct -{ - buffer_struct *out; - keys_struct *keys; - rsf_settings *rsfSet; - - struct - { - u32 mediaSize; - bool IncludeExeFsLogo; - bool CompressCode; - bool UseOnSD; - bool Encrypt; - bool FreeProductCode; - bool IsCfa; - bool IsBuildingCodeSection; - bool UseRomFS; - } options; - - struct - { - FILE *elf; - u64 elfSize; - - FILE *banner; - u64 bannerSize; - - FILE *icon; - u64 iconSize; - - FILE *logo; - u64 logoSize; - - FILE *code; - u64 codeSize; - - FILE *exhdr; - u64 exhdrSize; - - FILE *romfs; - u64 romfsSize; - - FILE *plainregion; - u64 plainregionSize; - } componentFilePtrs; - - struct - { - buffer_struct code; - buffer_struct banner; - buffer_struct icon; - } exefsSections; - - struct - { - u32 textAddress; - u32 textSize; - u32 textMaxPages; - u32 roAddress; - u32 roSize; - u32 roMaxPages; - u32 rwAddress; - u32 rwSize; - u32 rwMaxPages; - u32 bssSize; - } codeDetails; - - struct - { - buffer_struct exhdr; - buffer_struct acexDesc; - buffer_struct logo; - buffer_struct plainRegion; - buffer_struct exeFs; - } sections; - - ncch_struct cryptoDetails; - - -} ncch_settings; - -// NCCH Build Functions -int build_NCCH(user_settings *usrset); - - // NCCH Read Functions -int VerifyNCCH(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput); +int VerifyNcch(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput); -u8* RetargetNCCH(FILE *fp, u64 size, u8 *TitleId, u8 *ProgramId, keys_struct *keys); int ModifyNcchIds(u8 *ncch, u8 *titleId, u8 *programId, keys_struct *keys); - -ncch_hdr* GetNCCH_CommonHDR(void *out, FILE *fp, u8 *buf); -bool IsNCCH(FILE *fp, u8 *buf); +void ReadNcchHdr(ncch_hdr *hdr, FILE *fp); +u8* GetNcchHdrSig(ncch_hdr *hdr); +u8* GetNcchHdrData(ncch_hdr *hdr); +u32 GetNcchHdrSigLen(ncch_hdr *hdr); +u32 GetNcchHdrDataLen(ncch_hdr *hdr); +bool IsNcch(FILE *fp, u8 *buf); bool IsCfa(ncch_hdr* hdr); -u32 GetNCCH_MediaUnitSize(ncch_hdr* hdr); -u32 GetNCCH_MediaSize(ncch_hdr* hdr); -ncch_key_type GetNCCHKeyType(ncch_hdr* hdr); - -u8* GetNCCHKey0(ncch_key_type keytype, keys_struct *keys); -u8* GetNCCHKey1(ncch_key_type keytype, keys_struct *keys); - -int GetNCCHStruct(ncch_struct *ctx, ncch_hdr *header); -void ncch_get_counter(ncch_struct *ctx, u8 counter[16], u8 type); -void CryptNCCHSection(u8 *buffer, u64 size, u64 src_pos, ncch_struct *ctx, u8 key[16], u8 type); \ No newline at end of file +bool IsUpdateCfa(ncch_hdr* hdr); +u32 GetNcchBlockSize(ncch_hdr* hdr); +u8 GetBlockSizeFlag(u32 size); +u64 GetNcchSize(ncch_hdr* hdr); +bool IsNcchEncrypted(ncch_hdr *hdr); +bool SetNcchKeys(keys_struct *keys, ncch_hdr *hdr); +int GetNcchInfo(ncch_info *ctx, ncch_hdr *header); +void GetNcchAesCounter(ncch_info *ctx, u8 counter[16], u8 type); +void CryptNcchRegion(u8 *buffer, u64 size, u64 src_pos, ncch_info *ctx, u8 key[16], u8 type); \ No newline at end of file diff --git a/makerom/ncch_build.h b/makerom/ncch_build.h new file mode 100644 index 00000000..96f47c4e --- /dev/null +++ b/makerom/ncch_build.h @@ -0,0 +1,88 @@ +#pragma once +#include "ncch.h" + +typedef struct +{ + buffer_struct *out; + keys_struct *keys; + rsf_settings *rsfSet; + + struct + { + u32 blockSize; + bool verbose; + bool IncludeExeFsLogo; + bool CompressCode; + bool UseOnSD; + bool Encrypt; + bool FreeProductCode; + bool IsCfa; + bool IsBuildingCodeSection; + bool UseRomFS; + + bool useSecCrypto; + u8 keyXID; + } options; + + struct + { + FILE *elf; + u64 elfSize; + + FILE *banner; + u64 bannerSize; + + FILE *icon; + u64 iconSize; + + FILE *logo; + u64 logoSize; + + FILE *code; + u64 codeSize; + + FILE *exhdr; + u64 exhdrSize; + + FILE *romfs; + u64 romfsSize; + + FILE *plainregion; + u64 plainregionSize; + } componentFilePtrs; + + struct + { + buffer_struct code; + buffer_struct banner; + buffer_struct icon; + } exefsSections; + + struct + { + u32 textAddress; + u32 textSize; + u32 textMaxPages; + u32 roAddress; + u32 roSize; + u32 roMaxPages; + u32 rwAddress; + u32 rwSize; + u32 rwMaxPages; + u32 bssSize; + } codeDetails; + + struct + { + buffer_struct exhdr; + buffer_struct acexDesc; + buffer_struct logo; + buffer_struct plainRegion; + buffer_struct exeFs; + } sections; + + ncch_info cryptoDetails; +} ncch_settings; + +// NCCH Build Functions +int build_NCCH(user_settings *usrset); \ No newline at end of file diff --git a/makerom/logo_data.h b/makerom/ncch_logo.h similarity index 100% rename from makerom/logo_data.h rename to makerom/ncch_logo.h diff --git a/makerom/ncch_read.h b/makerom/ncch_read.h new file mode 100644 index 00000000..a185bd04 --- /dev/null +++ b/makerom/ncch_read.h @@ -0,0 +1,2 @@ +#pragma once +#include "ncch.h" \ No newline at end of file diff --git a/makerom/ncsd.c b/makerom/ncsd.c index bb8f3c06..2827f5b4 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -1,684 +1,700 @@ #include "lib.h" -#include "ncch.h" -#include "exheader.h" -#include "ncsd.h" -#include "cia.h" -#include "tmd.h" - -// Private Prototypes - -/* RSA Crypto */ -int SignCCI(u8 *Signature, u8 *NCSD_HDR, keys_struct *keys); -int CheckCCISignature(u8 *Signature, u8 *NCSD_HDR, keys_struct *keys); - -/* cci_settings tools */ -void init_CCISettings(cci_settings *set); -int get_CCISettings(cci_settings *cciset, user_settings *usrset); -void free_CCISettings(cci_settings *set); - -/* CCI Data Gen/Write */ -int BuildCCIHeader(cci_settings *cciset, user_settings *usrset); -int BuildCardInfoHeader(cci_settings *cciset, user_settings *usrset); -int WriteHeaderToFile(cci_settings *cciset); -int WriteContentToFile(cci_settings *cciset,user_settings *usrset); -int WriteDummyBytes(cci_settings *cciset); - -/* Get Data from Content Files */ -int CheckContent0(cci_settings *cciset, user_settings *usrset); -int GetDataFromContent0(cci_settings *cciset, user_settings *usrset); -int GetContentFP(cci_settings *cciset, user_settings *usrset); -int ImportNcchPartitions(cci_settings *cciset); -int ImportCverDetails(cci_settings *cciset, user_settings *usrset); - -/* Get Data from YAML Settings */ -int GetNCSDFlags(cci_settings *cciset, rsf_settings *yaml); -int GetMediaSize(cci_settings *cciset, user_settings *usrset); -u64 GetUnusedSize(u64 MediaSize, u8 CardType); -int GetWriteableAddress(cci_settings *cciset, user_settings *usrset); -int GetCardInfoBitmask(cci_settings *cciset, user_settings *usrset); - -int CheckMediaSize(cci_settings *cciset); - -static InternalCCI_Context ctx; + +#include "ncch_read.h" +#include "exheader_read.h" +#include "tik_read.h" +#include "tmd_read.h" +#include "cia_read.h" + +#include "ncsd_build.h" +#include "cardinfo.h" +#include "titleid.h" + + const int NCCH0_OFFSET = 0x4000; +const int CCI_BLOCK_SIZE = 0x200; + +void ImportCciSettings(cci_settings *set, user_settings *usrset); +void FreeCciSettings(cci_settings *set); +int ImportCciNcch(cci_settings *set); +int ProcessNcchForCci(cci_settings *set); +int GenCciHdr(cci_settings *set); +int CheckRomConfig(cci_settings *set); +void WriteCciDataToOutput(cci_settings *set); -// Code int build_CCI(user_settings *usrset) { int result = 0; - - // Init Settings - cci_settings *cciset = calloc(1,sizeof(cci_settings)); - if(!cciset) { - fprintf(stderr,"[CCI ERROR] Not enough memory\n"); + cci_settings *set = calloc(1,sizeof(cci_settings)); + if(!set){ + fprintf(stderr,"[CCI ERROR] Not enough memory\n"); return MEM_ERROR; } - init_CCISettings(cciset); + ImportCciSettings(set,usrset); - // Get Settings - result = get_CCISettings(cciset,usrset); - if(result) goto finish; - - // Import Content - result = ImportNcchPartitions(cciset); - if(result) goto finish; - - // Create Output File - cciset->out = fopen(usrset->common.outFileName,"wb"); - if(!cciset->out){ - fprintf(stderr,"[CCI ERROR] Failed to create '%s'\n",usrset->common.outFileName); - result = FAILED_TO_CREATE_OUTFILE; + if(ImportCciNcch(set)){ + result = FAILED_TO_IMPORT_FILE; + goto finish; + } + + if(ProcessNcchForCci(set)){ + result = FAILED_TO_IMPORT_FILE; + goto finish; + } + + if(GenCciHdr(set)){ + result = GEN_HDR_FAIL; + goto finish; + } + + if(CheckRomConfig(set)){ + result = CCI_CONFIG_FAIL; + goto finish; + } + + if(GenCardInfoHdr(set)){ + result = GEN_HDR_FAIL; goto finish; } - - // Generate NCSD Header and Additional Header - result = BuildCCIHeader(cciset,usrset); - if(result) goto finish; - BuildCardInfoHeader(cciset,usrset); - // Write to File - WriteHeaderToFile(cciset); - result = WriteContentToFile(cciset,usrset); - if(result) + set->out = fopen(usrset->common.outFileName,"wb"); + if(!set->out){ + fprintf(stderr,"[CCI ERROR] Failed to create '%s'\n",usrset->common.outFileName); + result = FAILED_TO_CREATE_OUTFILE; goto finish; + } - // Fill out file if necessary - if(cciset->option.fillOutCci) - WriteDummyBytes(cciset); + WriteCciDataToOutput(set); - // Close output file finish: - if(result != FAILED_TO_CREATE_OUTFILE && cciset->out) fclose(cciset->out); - free_CCISettings(cciset); + FreeCciSettings(set); return result; } - -int SignCCI(u8 *Signature, u8 *NCSD_HDR, keys_struct *keys) -{ - return ctr_sig(NCSD_HDR,sizeof(cci_hdr),Signature,keys->rsa.cciCfaPub,keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN); -} - -int CheckCCISignature(u8 *Signature, u8 *NCSD_HDR, keys_struct *keys) +void ImportCciSettings(cci_settings *set, user_settings *usrset) { - return ctr_sig(NCSD_HDR,sizeof(cci_hdr),Signature,keys->rsa.cciCfaPub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); -} - -void init_CCISettings(cci_settings *set) -{ - memset(set,0,sizeof(cci_settings)); - memset(&ctx,0,sizeof(InternalCCI_Context)); -} - -int get_CCISettings(cci_settings *cciset, user_settings *usrset) -{ - cciset->keys = &usrset->common.keys; - int result = 0; - - /* Importing Data from Content */ - result = CheckContent0(cciset,usrset); - if(result) return result; - - result = GetDataFromContent0(cciset,usrset); - if(result) return result; - - result = GetContentFP(cciset,usrset); - if(result) return result; + set->keys = &usrset->common.keys; + set->rsf = &usrset->common.rsfSet; - - /* Getting Data from YAML */ - result = GetNCSDFlags(cciset,&usrset->common.rsfSet); - if(result) return result; - - result = GetMediaSize(cciset,usrset); - if(result) return result; - - result = CheckMediaSize(cciset); - if(result) return result; - - /** Card Info Header Data **/ - result = GetWriteableAddress(cciset,usrset); - if(result) return result; - - result = GetCardInfoBitmask(cciset,usrset); - if(result) return result; + set->content.data = usrset->common.workingFile.buffer; + set->content.dataLen = usrset->common.workingFile.size; + set->content.dataType = usrset->common.workingFileType; - result = ImportCverDetails(cciset,usrset); - if(result) return result; - - /* All Done */ - return 0; + set->content.path = usrset->common.contentPath; + set->content.dSize = usrset->common.contentSize; + + usrset->common.workingFile.buffer = NULL; + usrset->common.workingFile.size = 0; + + set->options.verbose = usrset->common.verbose; + set->options.padCci = set->rsf->Option.MediaFootPadding; + set->options.noModTid = usrset->cci.dontModifyNcchTitleID; + set->options.useExternalSdkCardInfo = usrset->cci.useSDKStockData; + set->options.closeAlignWR = usrset->cci.closeAlignWritableRegion; + + set->options.cverDataType = usrset->cci.cverDataType; + set->options.cverDataPath = usrset->cci.cverDataPath; + + set->romInfo.blockSize = CCI_BLOCK_SIZE; + set->romInfo.saveSize = 0; } -void free_CCISettings(cci_settings *set) +void FreeCciSettings(cci_settings *set) { - if(set->content.filePtrs){ - for(int i = 1; i < 8; i++) { - if(set->content.filePtrs[i]) fclose(set->content.filePtrs[i]); - } - free(set->content.filePtrs); - } + free(set->options.tmdHdr); + free(set->content.data); + free(set->headers.ccihdr.buffer); + free(set->headers.cardinfohdr.buffer); + if(set->out) + fclose(set->out); free(set); } -int BuildCCIHeader(cci_settings *cciset, user_settings *usrset) +int ImportNcchForCci(cci_settings *set) { - memcpy((u8*)ctx.cciHdr.magic,"NCSD",4); - u32_to_u8((u8*)ctx.cciHdr.mediaSize,(cciset->header.mediaSize/cciset->option.mediaUnit),LE); - memcpy((u8*)ctx.cciHdr.titleId,cciset->header.mediaId,8); - memcpy((u8*)ctx.cciHdr.flags,cciset->header.flags,8); - - // Content - for(int i = 0; i < 8; i++){ - u32_to_u8((u8*)ctx.cciHdr.offset_sizeTable[i].offset,(cciset->content.offset[i]/cciset->option.mediaUnit),LE); - u32_to_u8((u8*)ctx.cciHdr.offset_sizeTable[i].size,(cciset->content.size[i]/cciset->option.mediaUnit),LE); - memcpy((u8*)ctx.cciHdr.contentIdTable[i],cciset->content.titleId[i],8); - ctx.cciHdr.contentFsType[i] = cciset->content.fsType[i]; - ctx.cciHdr.contentCryptoType[i] = cciset->content.cryptoType[i]; - } - - // Signature - if(SignCCI(ctx.signature,(u8*)&ctx.cciHdr,cciset->keys) != Good){ - fprintf(stderr,"[CCI ERROR] Failed to sign CCI\n"); - return CCI_SIG_FAIL; + for(int i = 0; i < CCI_MAX_CONTENT; i++){ + if(i == 0){ + set->content.active[i] = true; + set->content.dSize[i] = set->content.dataLen; + set->content.dOffset[i] = 0; + } + else if(set->content.dSize[i] && set->content.path[i]){ + set->content.active[i] = true; + set->content.dOffset[i] = set->content.dataLen; + set->content.dataLen += set->content.dSize[i]; + } + else + set->content.active[i] = false; } - return 0; -} - -int BuildCardInfoHeader(cci_settings *cciset, user_settings *usrset) -{ - u32_to_u8((u8*)ctx.cardinfo.writableAddress,(cciset->cardinfo.writableAddress/cciset->option.mediaUnit),LE); - u32_to_u8((u8*)ctx.cardinfo.cardInfoBitmask,cciset->cardinfo.cardInfoBitmask,BE); - u32_to_u8((u8*)ctx.cardinfo.mediaSizeUsed,cciset->cardinfo.cciTotalSize,LE); - memcpy(ctx.cardinfo.cverTitleId,cciset->cardinfo.cverTitleId,8); - memcpy(ctx.cardinfo.cverTitleVersion,cciset->cardinfo.cverTitleVersion,2); - memcpy((u8*)ctx.cardinfo.ncch0TitleId,cciset->content.titleId[0],8); - memcpy((u8*)ctx.cardinfo.initialData,cciset->cardinfo.initialData,0x30); - memcpy((u8*)ctx.cardinfo.ncch0Hdr,&cciset->cardinfo.ncchHdr,0x100); - memcpy((u8*)ctx.devcardinfo.titleKey,cciset->cardinfo.titleKey,0x10); - return 0; -} - -int ImportNcchPartitions(cci_settings *cciset) -{ - cciset->content.data->buffer = realloc(cciset->content.data->buffer,cciset->content.data->size); - if(!cciset->content.data->buffer){ + set->content.data = realloc(set->content.data,set->content.dataLen); + if(!set->content.data){ fprintf(stderr,"[CCI ERROR] Not enough memory\n"); return MEM_ERROR; } - ncch_hdr *ncch0hdr = (ncch_hdr*)(cciset->content.data->buffer+0x100); + FILE *ncch; for(int i = 1; i < CCI_MAX_CONTENT; i++){ - if(!cciset->content.size[i]) + if(!set->content.active[i]) continue; - u8 *ncchpos = (u8*)(cciset->content.data->buffer+cciset->content.offset[i]-cciset->content.offset[0]); + u8 *ncchpos = (u8*)(set->content.data+set->content.dOffset[i]); - ReadFile_64(ncchpos, cciset->content.fileSize[i], 0, cciset->content.filePtrs[i]); - if(ModifyNcchIds(ncchpos, cciset->content.titleId[i], ncch0hdr->programId, cciset->keys) != 0) - return -1; + ncch = fopen(set->content.path[i],"rb"); + + ReadFile64(ncchpos, set->content.dSize[i], 0, ncch); + + fclose(ncch); } + return 0; } -void WriteCCIDummyData(cci_settings *cciset) -{ - // Creating Buffer of Dummy Bytes - u64 len = NCCH0_OFFSET - 0x1200; - u8 *dummy_bytes = malloc(len); - memset(dummy_bytes,0xff,len); - WriteBuffer(dummy_bytes,len,0x1200,cciset->out); -} - -void WriteDevCardInfoData(cci_settings *cciset) -{ - WriteBuffer((u8*)&ctx.devcardinfo,sizeof(devcardinfo_hdr),0x1200,cciset->out); -} - -int WriteHeaderToFile(cci_settings *cciset) +bool CanCiaBeCci(u16 cat, u16 count, tmd_content_chunk *content) { - WriteBuffer(ctx.signature,0x100,0,cciset->out); - WriteBuffer((u8*)&ctx.cciHdr,sizeof(cci_hdr),0x100,cciset->out); - WriteBuffer((u8*)&ctx.cardinfo,sizeof(cardinfo_hdr),0x200,cciset->out); - if(cciset->option.useDevCardInfo) - WriteDevCardInfoData(cciset); - else - WriteCCIDummyData(cciset); + if(cat != PROGRAM_ID_CATEGORY_APPLICATION && cat != PROGRAM_ID_CATEGORY_SYSTEM_APPLICATION) + return false; - return 0; -} - -int WriteContentToFile(cci_settings *cciset,user_settings *usrset) -{ - // Write Content 0 - WriteBuffer(cciset->content.data->buffer,cciset->content.data->size,NCCH0_OFFSET,cciset->out); - free(cciset->content.data->buffer); - cciset->content.data->buffer = NULL; - cciset->content.data->size = 0; - return 0; + if(count > CCI_MAX_CONTENT) + return false; + + for(int i = 0; i < count; i++){ + if(GetTmdContentIndex(content[i]) >= CCI_MAX_CONTENT) + return false; + } + + return true; } -int WriteDummyBytes(cci_settings *cciset) +void GenRsfInputFromTmd(tmd_hdr *tmd, tmd_content_chunk *info, cci_settings *set) { - // Seeking end of CCI Data - fseek_64(cciset->out,cciset->cardinfo.cciTotalSize); - - // Determining Size of Dummy Bytes - u64 len = cciset->header.mediaSize - cciset->cardinfo.cciTotalSize; + if(!set->rsf->CardInfo.MediaSize){ + set->rsf->CardInfo.MediaSize = calloc(20,sizeof(char)); + u64 contentSize = NCCH0_OFFSET; + u16 contentNum = GetTmdContentCount(tmd); + for(int i = 0; i < contentNum; i++) + contentSize += GetTmdContentSize(info[i]); + + if(contentSize < (u64)128*MB) + strcpy(set->rsf->CardInfo.MediaSize,"128MB"); + else if(contentSize < (u64)256*MB) + strcpy(set->rsf->CardInfo.MediaSize,"256MB"); + else if(contentSize < (u64)512*MB) + strcpy(set->rsf->CardInfo.MediaSize,"512MB"); + else if(contentSize < (u64)1*GB) + strcpy(set->rsf->CardInfo.MediaSize,"1GB"); + else if(contentSize < (u64)2*GB) + strcpy(set->rsf->CardInfo.MediaSize,"2GB"); + else if(contentSize < (u64)4*GB) + strcpy(set->rsf->CardInfo.MediaSize,"4GB"); + else if(contentSize < (u64)8*GB) + strcpy(set->rsf->CardInfo.MediaSize,"8GB"); + else{ + free(set->rsf->CardInfo.MediaSize); + set->rsf->CardInfo.MediaSize = NULL; + } + + if(set->options.verbose && set->rsf->CardInfo.MediaSize) + printf("[CCI] Auto generating RSF setting \"CardInfo/MediaSize: %s\" \n",set->rsf->CardInfo.MediaSize); + } - // Creating Buffer of Dummy Bytes - u8 *dummy_bytes = malloc(cciset->option.mediaUnit); - memset(dummy_bytes,0xff,cciset->option.mediaUnit); + if(!set->rsf->CardInfo.MediaType){ + set->rsf->CardInfo.MediaType = calloc(20,sizeof(char)); + if(set->romInfo.saveSize < (u64)1*MB) + strcpy(set->rsf->CardInfo.MediaType,"Card1"); + else + strcpy(set->rsf->CardInfo.MediaType,"Card2"); + + if(set->options.verbose) + printf("[CCI] Auto generating RSF setting \"CardInfo/MediaType: %s\" \n",set->rsf->CardInfo.MediaType); + } - // Writing Dummy Bytes to file - for(u64 i = 0; i < len; i += cciset->option.mediaUnit) - fwrite(dummy_bytes,cciset->option.mediaUnit,1,cciset->out); + if(!set->rsf->CardInfo.CardDevice){ + set->rsf->CardInfo.CardDevice = calloc(20,sizeof(char)); + if(set->romInfo.saveSize < (u64)1*MB && set->romInfo.saveSize > 0) + strcpy(set->rsf->CardInfo.CardDevice,"NorFlash"); + else + strcpy(set->rsf->CardInfo.CardDevice,"None"); + + if(set->options.verbose) + printf("[CCI] Auto generating RSF setting \"CardInfo/CardDevice: %s\" \n",set->rsf->CardInfo.CardDevice); + } - return 0; + return; } -int GetContentFP(cci_settings *cciset, user_settings *usrset) +int ProcessCiaForCci(cci_settings *set) { - cciset->content.filePtrs = calloc(8,sizeof(FILE*)); - if(!cciset->content.filePtrs){ - fprintf(stderr,"[CCI ERROR] Not enough memory\n"); - return MEM_ERROR; + if(!IsCia(set->content.data)){ + fprintf(stderr,"[CCI ERROR] CIA is corrupt\n"); + return FAILED_TO_IMPORT_FILE; } + + tik_hdr *tik = GetTikHdr(GetCiaTik(set->content.data)); + tmd_hdr *tmd = GetTmdHdr(GetCiaTmd(set->content.data)); + tmd_content_chunk *contentInfo = GetTmdContentInfo(GetCiaTmd(set->content.data)); + u64 contentOffset = GetCiaContentOffset((cia_hdr*)set->content.data); - for(int i = 1; i < 8; i++){ - if(usrset->common.contentPath[i]){ - if(!AssertFile(usrset->common.contentPath[i])){ // Checking if file could be opened - fprintf(stderr,"[CCI ERROR] Failed to open '%s'\n",usrset->common.contentPath[i]); - return FAILED_TO_OPEN_FILE; - } - - cciset->content.fileSize[i] = GetFileSize_u64(usrset->common.contentPath[i]); - cciset->content.filePtrs[i] = fopen(usrset->common.contentPath[i],"rb"); - /* - if(!cciset->content.filePtrs[i]){ // Checking if file could be opened - fprintf(stderr,"[CCI ERROR] Failed to open '%s'\n",usrset->common.contentPath[i]); - return FAILED_TO_OPEN_FILE; - } - */ - if(!IsNCCH(cciset->content.filePtrs[i],NULL)){ // Checking if NCCH - fprintf(stderr,"[CCI ERROR] Content '%s' is invalid\n",usrset->common.contentPath[i]); - return NCSD_INVALID_NCCH; + u16 titleCat = (GetTmdTitleId(tmd) >> 32) & 0xffff; + u16 contentCount = GetTmdContentCount(tmd); + set->romInfo.saveSize = GetTmdSaveSize(tmd); + if(set->romInfo.saveSize > 0 && set->romInfo.saveSize < (u64)(128*KB)) + set->romInfo.saveSize = (u64)(128*KB); + else if(set->romInfo.saveSize > (u64)(128*KB) && set->romInfo.saveSize < (u64)(512*KB)) + set->romInfo.saveSize = (u64)(512*KB); + else if(set->romInfo.saveSize > (u64)(512*KB)) + set->romInfo.saveSize = align(set->romInfo.saveSize,MB); + + if(!CanCiaBeCci(titleCat,contentCount,contentInfo)){ + fprintf(stderr,"[CCI ERROR] This CIA cannot be converted to CCI\n"); + return INCOMPAT_CIA; + } + + GenRsfInputFromTmd(tmd,contentInfo,set); + + bool canDecrypt; + u8 titleKey[AES_128_KEY_SIZE]; + canDecrypt = GetTikTitleKey(titleKey,tik,set->keys); + if(set->options.verbose){ + if(canDecrypt) + memdump(stdout,"[CCI] CIA title key: ",titleKey,AES_128_KEY_SIZE); + else + fprintf(stdout,"[CCI] CIA title key could not be decrypted\n"); + } + + for(u16 i = 0; i < contentCount; i++){ + u16 index = GetTmdContentIndex(contentInfo[i]); + set->content.active[index] = true; + set->content.dOffset[index] = contentOffset; + set->content.dSize[index] = GetTmdContentSize(contentInfo[i]); + u8 *content = set->content.data + contentOffset; + if(IsTmdContentEncrypted(contentInfo[i])){ + if(canDecrypt){ + CryptContent(content,content,set->content.dSize[index],titleKey,i,DEC); } - - // Getting NCCH Header - ncch_hdr *hdr = malloc(sizeof(ncch_hdr)); - GetNCCH_CommonHDR(hdr,cciset->content.filePtrs[i],NULL); - - if(usrset->cci.dontModifyNcchTitleID) - memcpy(&cciset->content.titleId[i], hdr->titleId, 8); else{ - memcpy(&cciset->content.titleId[i], cciset->header.mediaId, 8); // Set TitleID - u16_to_u8(&cciset->content.titleId[i][6], (i+4), LE); - } - - u64 contentSize = (u64)GetNCCH_MediaSize(hdr)* (u64)GetNCCH_MediaUnitSize(hdr); - if(contentSize != cciset->content.fileSize[i]){ - fprintf(stderr,"[CCI ERROR] Content '%s' is corrupt\n",usrset->common.contentPath[i]); - return NCSD_INVALID_NCCH; + fprintf(stderr,"[CCI ERROR] Failed to decrypt CIA content: 0x%08x\n",GetTmdContentId(contentInfo[i])); + return INCOMPAT_CIA; } - - cciset->content.size[i] = align(contentSize,cciset->option.mediaUnit); - cciset->content.offset[i] = cciset->cardinfo.cciTotalSize; - - cciset->content.data->size += cciset->content.size[i]; - cciset->cardinfo.cciTotalSize += cciset->content.size[i]; - - free(hdr); } + if(!ValidateTmdContent(content,contentInfo[i])){ + fprintf(stderr,"[CCI ERROR] CIA content: 0x%08x is corrupt\n",GetTmdContentId(contentInfo[i])); + return NCSD_INVALID_NCCH; + } + + contentOffset += set->content.dSize[index]; } + return 0; } -int CheckContent0(cci_settings *cciset, user_settings *usrset) +int ImportCciNcch(cci_settings *set) { - if(!usrset->common.workingFile.buffer || !usrset->common.workingFile.size) - return NCSD_NO_NCCH0; - cciset->content.data = &usrset->common.workingFile; + if(set->content.dataType == infile_ncch) + return ImportNcchForCci(set); + else if(set->content.dataType == infile_cia) + return ProcessCiaForCci(set); + else + fprintf(stderr,"[CCI ERROR] Unrecognised input data type\n"); - if(!IsNCCH(NULL,cciset->content.data->buffer)) - return NCSD_INVALID_NCCH0; + if(set->rsf->SystemControlInfo.SaveDataSize) + GetSaveDataSizeFromString(&set->romInfo.saveSize,set->rsf->SystemControlInfo.SaveDataSize,"CCI"); - return 0; + return FAILED_TO_IMPORT_FILE; } -int GetDataFromContent0(cci_settings *cciset, user_settings *usrset) -{ - cciset->cardinfo.cciTotalSize = NCCH0_OFFSET; - ncch_hdr *hdr; +int ProcessCverDataForCci(cci_settings *set) +{ + u64 tmdSize,tmdOffset; - hdr = GetNCCH_CommonHDR(NULL,NULL,cciset->content.data->buffer); + u64 dataSize = GetFileSize64(set->options.cverDataPath); + FILE *data = fopen(set->options.cverDataPath,"rb"); - memcpy(&cciset->cardinfo.ncchHdr,hdr,sizeof(ncch_hdr)); - u16 ncch_format_ver = u8_to_u16(hdr->formatVersion,LE); - if(ncch_format_ver > 2){ - fprintf(stderr,"[CCI ERROR] NCCH type %d not supported\n",ncch_format_ver); - return FAILED_TO_IMPORT_FILE; - } - - //memdump(stdout,"ncch0 head: ",(cciset->ncch0+0x100),0x100); - //memdump(stdout,"ncch0 head: ",(u8*)(hdr),0x100); + if(set->options.cverDataType == CVER_DTYPE_CIA){ + cia_hdr *ciaHdr = calloc(1,sizeof(cia_hdr)); + ReadFile64(ciaHdr,sizeof(cia_hdr),0,data); - memcpy(cciset->header.mediaId,hdr->titleId,8); - memcpy(&cciset->content.titleId[0],hdr->titleId,8); - if(usrset->cci.useSDKStockData){ - memcpy(cciset->cardinfo.initialData,stock_initial_data,0x30); - memcpy(cciset->cardinfo.titleKey,stock_title_key,0x10); - cciset->option.useDevCardInfo = true; + tmdSize = GetCiaTmdSize(ciaHdr); + tmdOffset = GetCiaTmdOffset(ciaHdr); + + free(ciaHdr); } else{ - rndset(cciset->cardinfo.initialData,0x2c); - //rndset(cciset->cardinfo.titleKey,0x10); - //cciset->option.useDevCardInfo = true; + tmdSize = dataSize; + tmdOffset = 0; } - cciset->header.flags[MediaUnitSize] = hdr->flags[ContentUnitSize]; - cciset->option.mediaUnit = GetNCCH_MediaUnitSize(hdr); + u8 *tmd = calloc(1,tmdSize); + + ReadFile64(tmd,tmdSize,tmdOffset,data); + fclose(data); - cciset->content.size[0] = (u64)(GetNCCH_MediaSize(hdr) * cciset->option.mediaUnit); - cciset->content.offset[0] = cciset->cardinfo.cciTotalSize; + tmd_hdr *tmdHdr = GetTmdHdr(tmd); + if(!tmdHdr){ + fprintf(stderr,"[CCI ERROR] Corrupt cver TMD\n"); + free(tmd); + return FAILED_TO_IMPORT_FILE; + } + + set->options.tmdHdr = calloc(1,sizeof(tmd_hdr)); + memcpy(set->options.tmdHdr,tmdHdr,sizeof(tmd_hdr)); - cciset->content.data->size = cciset->content.size[0]; - cciset->cardinfo.cciTotalSize += cciset->content.size[0]; + free(tmd); + return 0; } -int GetMediaSize(cci_settings *cciset, user_settings *usrset) +void GetNewNcchIdForCci(u8 *newTid, u8 *srcTid, u8 index, tmd_hdr *tmdHdr) { - char *mediaSizeStr = usrset->common.rsfSet.CardInfo.MediaSize; - if(!mediaSizeStr) cciset->header.mediaSize = (u64)GB*2; - else{ - if(strcasecmp(mediaSizeStr,"128MB") == 0) cciset->header.mediaSize = (u64)MB*128; - else if(strcasecmp(mediaSizeStr,"256MB") == 0) cciset->header.mediaSize = (u64)MB*256; - else if(strcasecmp(mediaSizeStr,"512MB") == 0) cciset->header.mediaSize = (u64)MB*512; - else if(strcasecmp(mediaSizeStr,"1GB") == 0) cciset->header.mediaSize = (u64)GB*1; - else if(strcasecmp(mediaSizeStr,"2GB") == 0) cciset->header.mediaSize = (u64)GB*2; - else if(strcasecmp(mediaSizeStr,"4GB") == 0) cciset->header.mediaSize = (u64)GB*4; - else if(strcasecmp(mediaSizeStr,"8GB") == 0) cciset->header.mediaSize = (u64)GB*8; - else if(strcasecmp(mediaSizeStr,"16GB") == 0) cciset->header.mediaSize = (u64)GB*16; - else if(strcasecmp(mediaSizeStr,"32GB") == 0) cciset->header.mediaSize = (u64)GB*32; - else { - fprintf(stderr,"[CCI ERROR] Invalid MediaSize: %s\n",mediaSizeStr); - return INVALID_YAML_OPT; - } + u64 titleId = u8_to_u64(srcTid,LE) & 0xffffffffffff; + if(tmdHdr && index == 7) + titleId |= (u64)(GetTmdVersion(tmdHdr)) << 48; + else + titleId |= (u64)(index+4) << 48; + + u64_to_u8(newTid,titleId,LE); +} + +int ProcessNcchForCci(cci_settings *set) +{ + u8 *ncch; + ncch_hdr *hdr; + + u8 titleId[8]; + u8 srcId[8]; + + if(set->options.cverDataPath && set->content.active[7]){ + if(ProcessCverDataForCci(set)) + return FAILED_TO_IMPORT_FILE; } - cciset->option.fillOutCci = usrset->common.rsfSet.Option.MediaFootPadding; + for(int i = 0; i < CCI_MAX_CONTENT; i++){ + if(set->content.active[i]){ + ncch = set->content.data + set->content.dOffset[i]; + if(!IsNcch(NULL,ncch)){ + fprintf(stderr,"[CCI ERROR] NCCH %d is corrupt\n",i); + return NCSD_INVALID_NCCH; + } + hdr = (ncch_hdr*)ncch; + if(i > 0 && !set->options.noModTid){ + if(set->options.verbose){ + printf("[CCI] Modifying NCCH %d IDs\n",i); + printf("[Old Ids]\n"); + memdump(stdout," > TitleId: 0x",hdr->titleId,8); + memdump(stdout," > ProgramId: 0x",hdr->programId,8); + } + GetNewNcchIdForCci(titleId,srcId,i,set->options.tmdHdr); + if(ModifyNcchIds(ncch, titleId, srcId, set->keys)) + return -1; + if(set->options.verbose){ + printf("[New Ids]\n"); + memdump(stdout," > TitleId: 0x",hdr->titleId,8); + memdump(stdout," > ProgramId: 0x",hdr->programId,8); + } + } + set->content.titleId[i] = u8_to_u64(hdr->titleId,LE); + if(i == 0) + memcpy(srcId,hdr->titleId,8); + } + } return 0; } -u64 GetUnusedSize(u64 MediaSize, u8 CardType) -{ - if(CardType == CARD1){ - switch(MediaSize){ - case (u64)MB*128: return (u64)2621440; - case (u64)MB*256: return (u64)5242880; - case (u64)MB*512: return (u64)10485760; - case (u64)GB*1: return (u64)73924608; - case (u64)GB*2: return (u64)147324928; - case (u64)GB*4: return (u64)294649856; - case (u64)GB*8: return (u64)587202560; - //default: return (u64)((MediaSize/MB)*0x11800); // Aprox - default: return 0; - } - } - else if(CardType == CARD2){ - switch(MediaSize){ - case (u64)MB*512: return (u64)37224448; - case (u64)GB*1: return (u64)73924608; - case (u64)GB*2: return (u64)147324928; - case (u64)GB*4: return (u64)294649856; - case (u64)GB*8: return (u64)587202560; - //default: return (u64)((MediaSize/MB)*0x11800); // Aprox - default: return 0; +int SetMediaSize(u8 *mediaSize, cci_settings *set) +{ + char *str = set->rsf->CardInfo.MediaSize; + if(!str) + set->romInfo.mediaSize = (u64)GB*2; + else{ + if(strcasecmp(str,"128MB") == 0) set->romInfo.mediaSize = (u64)MB*128; + else if(strcasecmp(str,"256MB") == 0) set->romInfo.mediaSize = (u64)MB*256; + else if(strcasecmp(str,"512MB") == 0) set->romInfo.mediaSize = (u64)MB*512; + else if(strcasecmp(str,"1GB") == 0) set->romInfo.mediaSize = (u64)GB*1; + else if(strcasecmp(str,"2GB") == 0) set->romInfo.mediaSize = (u64)GB*2; + else if(strcasecmp(str,"4GB") == 0) set->romInfo.mediaSize = (u64)GB*4; + else if(strcasecmp(str,"8GB") == 0) set->romInfo.mediaSize = (u64)GB*8; + //else if(strcasecmp(str,"16GB") == 0) set->romInfo.mediaSize = (u64)GB*16; + //else if(strcasecmp(str,"32GB") == 0) set->romInfo.mediaSize = (u64)GB*32; + else { + fprintf(stderr,"[CCI ERROR] Invalid MediaSize: %s\n",str); + return INVALID_RSF_OPT; } } + + u32_to_u8(mediaSize,(set->romInfo.mediaSize/set->romInfo.blockSize),LE); + return 0; } -int GetNCSDFlags(cci_settings *cciset, rsf_settings *yaml) +int SetBackupWriteWaitTime(u8 *flag, rsf_settings *rsf) { - /* BackupWriteWaitTime */ - cciset->header.flags[FW6x_BackupWriteWaitTime] = 0; - if(yaml->CardInfo.BackupWriteWaitTime){ - u32 WaitTime = strtoul(yaml->CardInfo.BackupWriteWaitTime,NULL,0); - if(WaitTime > 255){ - fprintf(stderr,"[CCI ERROR] Invalid Card BackupWriteWaitTime (%d) : must 0-255\n",WaitTime); - return EXHDR_BAD_YAML_OPT; + char *str = rsf->CardInfo.BackupWriteWaitTime; + if(!str) + *flag = 0; + else{ + u32 waitTime = strtoul(str,NULL,0); + if(waitTime > 255){ + fprintf(stderr,"[CCI ERROR] Invalid Card BackupWriteWaitTime (%d) : must 0-255\n",waitTime); + return INVALID_RSF_OPT; } - cciset->header.flags[FW6x_BackupWriteWaitTime] = (u8)WaitTime; + *flag = (u8)waitTime; } + + return 0; +} + +int SetMediaType(u8 *flag, cci_settings *set) +{ + char *str = set->rsf->CardInfo.MediaType; - /* MediaType */ - if(!yaml->CardInfo.MediaType) cciset->header.flags[MediaTypeIndex] = CARD1; + if(!str) + *flag = mediatype_CARD1; else{ - if(strcasecmp(yaml->CardInfo.MediaType,"Card1") == 0) cciset->header.flags[MediaTypeIndex] = CARD1; - else if(strcasecmp(yaml->CardInfo.MediaType,"Card2") == 0) cciset->header.flags[MediaTypeIndex] = CARD2; + if(strcasecmp(str,"Card1") == 0) + *flag = mediatype_CARD1; + else if(strcasecmp(str,"Card2") == 0) + *flag = mediatype_CARD2; else { - fprintf(stderr,"[CCI ERROR] Invalid MediaType: %s\n",yaml->CardInfo.MediaType); - return INVALID_YAML_OPT; + fprintf(stderr,"[CCI ERROR] Invalid MediaType: %s\n",str); + return INVALID_RSF_OPT; } } + + return 0; +} - /* Platform */ - cciset->header.flags[MediaPlatformIndex] = CTR; - +int SetCardDevice(u8 *flags, u64 saveSize, rsf_settings *rsf) +{ u8 saveCrypto; - if(!yaml->CardInfo.SaveCrypto) saveCrypto = 3; + if(!rsf->CardInfo.SaveCrypto) + saveCrypto = 3; else{ - if(strcasecmp(yaml->CardInfo.SaveCrypto,"fw1") == 0 || strcasecmp(yaml->CardInfo.SaveCrypto,"ctr fail") == 0 ) saveCrypto = 1; - else if(strcasecmp(yaml->CardInfo.SaveCrypto,"fw2") == 0) saveCrypto = 2; - else if(strcasecmp(yaml->CardInfo.SaveCrypto,"fw3") == 0) saveCrypto = 3; - else if(strcasecmp(yaml->CardInfo.SaveCrypto,"fw6") == 0) saveCrypto = 6; + if(strcasecmp(rsf->CardInfo.SaveCrypto,"fw1") == 0 || strcasecmp(rsf->CardInfo.SaveCrypto,"ctr fail") == 0 ) saveCrypto = 1; + else if(strcasecmp(rsf->CardInfo.SaveCrypto,"fw2") == 0) saveCrypto = 2; + else if(strcasecmp(rsf->CardInfo.SaveCrypto,"fw3") == 0) saveCrypto = 3; + else if(strcasecmp(rsf->CardInfo.SaveCrypto,"fw6") == 0) saveCrypto = 6; else { - fprintf(stderr,"[CCI ERROR] Invalid SaveCrypto: %s\n",yaml->CardInfo.SaveCrypto); - return INVALID_YAML_OPT; + fprintf(stderr,"[CCI ERROR] Invalid SaveCrypto: %s\n",rsf->CardInfo.SaveCrypto); + return INVALID_RSF_OPT; } } /* FW6x SaveCrypto */ - cciset->header.flags[FW6x_SaveCryptoFlag] = saveCrypto == 6; + if(saveCrypto == 6) + flags[cciflag_FW6_SAVE_CRYPTO] = 1; + else + flags[cciflag_FW6_SAVE_CRYPTO] = 0; /* CardDevice */ - if(saveCrypto > 1){ - u8 flag = CardDeviceFlag; - if(saveCrypto == 2) flag = OldCardDeviceFlag; - if(!yaml->CardInfo.CardDevice) cciset->header.flags[flag] = CARD_DEVICE_NONE; - else{ - if(strcmp(yaml->CardInfo.CardDevice,"NorFlash") == 0) { - cciset->header.flags[flag] = CARD_DEVICE_NOR_FLASH; - if(cciset->header.flags[MediaTypeIndex] == CARD2){ - fprintf(stderr,"[CCI WARNING] 'CardDevice: NorFlash' is invalid on Card2\n"); - cciset->header.flags[flag] = CARD_DEVICE_NONE; - } - } - else if(strcmp(yaml->CardInfo.CardDevice,"None") == 0) cciset->header.flags[flag] = CARD_DEVICE_NONE; - else if(strcmp(yaml->CardInfo.CardDevice,"BT") == 0) cciset->header.flags[flag] = CARD_DEVICE_BT; - else { - fprintf(stderr,"[CCI ERROR] Invalid CardDevice: %s\n",yaml->CardInfo.CardDevice); - return INVALID_YAML_OPT; - } + u8 cardDevice = 0; + if(!rsf->CardInfo.CardDevice) + cardDevice = carddevice_NONE; + else{ + if(strcmp(rsf->CardInfo.CardDevice,"NorFlash") == 0) + cardDevice = carddevice_NOR_FLASH; + else if(strcmp(rsf->CardInfo.CardDevice,"None") == 0) + cardDevice = carddevice_NONE; + else if(strcmp(rsf->CardInfo.CardDevice,"BT") == 0) + cardDevice = carddevice_BT; + else { + fprintf(stderr,"[CCI ERROR] Invalid CardDevice: %s\n",rsf->CardInfo.CardDevice); + return INVALID_RSF_OPT; } } - return 0; -} - -int GetWriteableAddress(cci_settings *cciset, user_settings *usrset) -{ - int result = GetSaveDataSizeFromString(&cciset->option.savedataSize,usrset->common.rsfSet.SystemControlInfo.SaveDataSize,"CCI"); - if(result) return result; - - char *WriteableAddressStr = usrset->common.rsfSet.CardInfo.WritableAddress; - - cciset->cardinfo.writableAddress = -1; - if(cciset->header.flags[MediaTypeIndex] != CARD2) return 0; // Can only be set for Card2 Media - - if(WriteableAddressStr){ - if(strncmp(WriteableAddressStr,"0x",2) != 0){ - fprintf(stderr,"[CCI ERROR] WritableAddress requires a Hexadecimal value\n"); - return INVALID_YAML_OPT; - } - cciset->cardinfo.writableAddress = strtoull(WriteableAddressStr,NULL,16); - } - if(cciset->cardinfo.writableAddress == -1){ // If not set manually or is max size - if ((cciset->header.mediaSize / 2) < cciset->option.savedataSize){ // If SaveData size is greater than half the MediaSize - u64 SavedataSize = cciset->option.savedataSize / KB; - fprintf(stderr,"[CCI ERROR] Too large SavedataSize %lldK\n",SavedataSize); - return SAVE_DATA_TOO_LARGE; - } - if (cciset->option.savedataSize > (u64)(2047*MB)){ // Limit set by Nintendo - u64 SavedataSize = cciset->option.savedataSize / KB; - fprintf(stderr,"[CCI ERROR] Too large SavedataSize %lldK\n",SavedataSize); - return SAVE_DATA_TOO_LARGE; + + if(flags[cciflag_MEDIA_TYPE] == mediatype_CARD1){ + if(saveSize != (u64)(128*KB) && saveSize != (u64)(512*KB) && cardDevice == carddevice_NOR_FLASH){ + fprintf(stderr,"[CCI ERROR] 'CardDevice: NorFlash' can only be used with save-data sizes: 128K & 512K\n"); + return INVALID_RSF_OPT; } - if(usrset->cci.closeAlignWritableRegion) - cciset->cardinfo.writableAddress = align(cciset->cardinfo.cciTotalSize, cciset->option.mediaUnit); // invalid for "real" chips - else{ - u64 UnusedSize = GetUnusedSize(cciset->header.mediaSize,cciset->header.flags[MediaTypeIndex]); // Some value related to the physical implementation of gamecards - if(UnusedSize > 0) - cciset->cardinfo.writableAddress = cciset->header.mediaSize - UnusedSize - cciset->option.savedataSize; // Nintendo's method for calculating writable region offset - else{ - fprintf(stderr,"[CCI WARNING] Nintendo has no CARD2 writable region configurations for this media size, writable region will be aligned to last ncch partition\n"); - cciset->cardinfo.writableAddress = align(cciset->cardinfo.cciTotalSize, cciset->option.mediaUnit); // invalid for "real" chips - } + } + if(flags[cciflag_MEDIA_TYPE] == mediatype_CARD2){ + if(cardDevice == carddevice_NOR_FLASH){ + fprintf(stderr,"[CCI WARNING] 'CardDevice: NorFlash' is invalid for Card2\n"); + cardDevice = carddevice_NONE; } } + + if(saveCrypto > 1) + flags[saveCrypto == 2? cciflag_CARD_DEVICE_OLD : cciflag_CARD_DEVICE] = cardDevice; + return 0; } -int GetCardInfoBitmask(cci_settings *cciset, user_settings *usrset) +int SetCciFlags(u8 *flags, cci_settings *set) { - char *str = usrset->common.rsfSet.CardInfo.CardType; - if(!str) cciset->cardinfo.cardInfoBitmask |= 0; - else{ - if(strcasecmp(str,"s1") == 0) cciset->cardinfo.cardInfoBitmask |= 0; - else if(strcasecmp(str,"s2") == 0) cciset->cardinfo.cardInfoBitmask |= 0x20; - else { - fprintf(stderr,"[CCI ERROR] Invalid CardType: %s\n",str); - return INVALID_YAML_OPT; - } - } + // Backup Write Wait Time + if(SetBackupWriteWaitTime(&flags[cciflag_BACKUP_WRITE_WAIT_TIME], set->rsf)) + return INVALID_RSF_OPT; + // Platform + flags[cciflag_MEDIA_PLATFORM] = cciplatform_CTR; + // Card Type + if(SetMediaType(&flags[cciflag_MEDIA_TYPE], set)) + return INVALID_RSF_OPT; + // Media Unit + flags[cciflag_MEDIA_BLOCK_SIZE] = GetCtrBlockSizeFlag(set->romInfo.blockSize); + // Card Device + if(SetCardDevice(flags, set->romInfo.saveSize, set->rsf)) + return INVALID_RSF_OPT; - str = usrset->common.rsfSet.CardInfo.CryptoType; - if(!str) cciset->cardinfo.cardInfoBitmask |= 0;//(3*0x40); - else{ - int Value = strtol(str,NULL,10); - if(Value < 0 || Value > 3) { - fprintf(stderr,"[CCI ERROR] Invalid CryptoType: %s\n",str); - return INVALID_YAML_OPT; - } - if(Value != 3){ - fprintf(stderr,"[CCI WARNING] Card crypto type = '%d'\n",Value); - } - cciset->cardinfo.cardInfoBitmask |= (Value*0x40); - } + set->romInfo.mediaType = flags[cciflag_MEDIA_TYPE]; + set->romInfo.cardDevice = flags[cciflag_CARD_DEVICE] | flags[cciflag_CARD_DEVICE_OLD]; return 0; } -int ImportCverDetails(cci_settings *cciset, user_settings *usrset) +void SetCciNcchInfo(cci_hdr *hdr, cci_settings *set) { - if(!usrset->cci.cverCiaPath){ - memset(cciset->cardinfo.cverTitleId,0,8); - memset(cciset->cardinfo.cverTitleVersion,0,2); - return 0; - } - if(!cciset->content.size[7]){ - fprintf(stderr,"[CCI WARNING] Update Partition (content 7) is not specified, cver details will not be set\n"); - memset(cciset->cardinfo.cverTitleId,0,8); - memset(cciset->cardinfo.cverTitleVersion,0,2); - return 0; - } + u64 ncchSize,ncchOffset; - if(!AssertFile(usrset->cci.cverCiaPath)){ - fprintf(stderr,"[CCI ERROR] Failed to open \"%s\"\n",usrset->cci.cverCiaPath); - return FAILED_TO_IMPORT_FILE; + ncchOffset = NCCH0_OFFSET; + + for(int i = 0; i < CCI_MAX_CONTENT; i++){ + if(set->content.active[i]){ + set->content.cOffset[i] = ncchOffset; + ncchSize = align(set->content.dSize[i],set->romInfo.blockSize); + + u32_to_u8(hdr->offset_sizeTable[i].offset,(ncchOffset/set->romInfo.blockSize),LE); + u32_to_u8(hdr->offset_sizeTable[i].size,(ncchSize/set->romInfo.blockSize),LE); + u64_to_u8(hdr->ncchIdTable[i],set->content.titleId[i],LE); + + ncchOffset += ncchSize; + } } - FILE *cia = fopen(usrset->cci.cverCiaPath,"rb"); - cia_hdr *ciaHdr = calloc(1,sizeof(cia_hdr)); - ReadFile_64(ciaHdr,sizeof(cia_hdr),0,cia); - u64 tmdSize = GetTmdSize(ciaHdr); - u64 tmdOffset = GetTmdOffset(ciaHdr); - u8 *tmd = calloc(1,tmdSize); - ReadFile_64(tmd,tmdSize,tmdOffset,cia); - tmd_hdr *tmdHdr = GetTmdHdr(tmd); - //memdump(stdout,"tmd: ",(u8*)tmdHdr,sizeof(tmd_hdr)); - + set->romInfo.usedSize = ncchOffset; + + return; +} - endian_memcpy(cciset->cardinfo.cverTitleId,tmdHdr->titleID,8,LE); - endian_memcpy(cciset->cardinfo.cverTitleVersion,tmdHdr->titleVersion,2,LE); - if(!usrset->cci.dontModifyNcchTitleID) - endian_memcpy(&cciset->content.titleId[7][6],tmdHdr->titleVersion,2,LE); +int GenCciHdr(cci_settings *set) +{ + set->headers.ccihdr.size = sizeof(cci_hdr); + set->headers.ccihdr.buffer = calloc(1,set->headers.ccihdr.size); + if(!set->headers.ccihdr.buffer){ + set->headers.ccihdr.size = 0; + fprintf(stderr,"[CCI ERROR] Not enough memory\n"); + return MEM_ERROR; + } - fclose(cia); - free(ciaHdr); - free(tmd); + cci_hdr *hdr = (cci_hdr*)set->headers.ccihdr.buffer; + + // Magic & TitleId + memcpy(hdr->magic,"NCSD",4); + u64_to_u8(hdr->titleId,set->content.titleId[0],LE); + + + if(SetMediaSize(hdr->mediaSize,set)) + return GEN_HDR_FAIL; + if(SetCciFlags(hdr->flags,set)) + return GEN_HDR_FAIL; + SetCciNcchInfo(hdr,set); + + // Sign Header + ctr_sig(&hdr->magic,sizeof(cci_hdr)-RSA_2048_KEY_SIZE,hdr->signature,set->keys->rsa.cciCfaPub,set->keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + + return 0; +} +int CheckRomConfig(cci_settings *set) +{ + u64 cciUsedSize; + if(set->romInfo.mediaType == mediatype_CARD2) + cciUsedSize = set->romInfo.card2SaveOffset + set->romInfo.saveSize; + else + cciUsedSize = set->romInfo.usedSize; + + if(cciUsedSize > set->romInfo.mediaSize){ + char *str = set->rsf->CardInfo.MediaSize; + if(!str) + str = "2GB"; + + fprintf(stderr,"[CCI ERROR] MediaSize '%s' is insufficient for the CCI data\n",str); + return CCI_CONFIG_FAIL; + } return 0; } -int CheckMediaSize(cci_settings *cciset) +void WriteCciDataToOutput(cci_settings *set) { - if(cciset->cardinfo.cciTotalSize > cciset->header.mediaSize){ - char *MediaSizeStr = NULL; - switch(cciset->header.mediaSize){ - case (u64)128*MB: MediaSizeStr = " '128MB'"; break; - case (u64)256*MB: MediaSizeStr = " '256MB'"; break; - case (u64)512*MB: MediaSizeStr = " '512MB'"; break; - case (u64)1*GB: MediaSizeStr = " '1GB'"; break; - case (u64)2*GB: MediaSizeStr = " '2GB'"; break; - case (u64)4*GB: MediaSizeStr = " '4GB'"; break; - case (u64)8*GB: MediaSizeStr = " '8GB'"; break; - case (u64)16*GB: MediaSizeStr = " '16GB'"; break; - case (u64)32*GB: MediaSizeStr = " '32GB'"; break; - default: MediaSizeStr = ""; break; + // NCSD Header + WriteBuffer(set->headers.ccihdr.buffer, set->headers.ccihdr.size, 0, set->out); + // Card Info Header + WriteBuffer(set->headers.cardinfohdr.buffer, set->headers.cardinfohdr.size, set->headers.ccihdr.size, set->out); + + // Dummy data between header and first NCCH + u64 len = set->content.cOffset[0] - (set->headers.ccihdr.size + set->headers.cardinfohdr.size); + u8 *dummy_data = malloc(len); + if(set->headers.cardinfohdr.size > sizeof(cardinfo_hdr)) // additional debug header data exists + memset(dummy_data, 0x00, len); + else // normal production cci image + memset(dummy_data, 0xff, len); + WriteBuffer(dummy_data, len, (set->headers.ccihdr.size + set->headers.cardinfohdr.size),set->out); + free(dummy_data); + + // NCCH Partitions + u8 *ncch; + for(int i = 0; i < CCI_MAX_CONTENT; i++){ + if(set->content.active[i]){ + ncch = set->content.data + set->content.dOffset[i]; + WriteBuffer(ncch, set->content.dSize[i], set->content.cOffset[i], set->out); } - fprintf(stderr,"[CCI ERROR] MediaSize%s is too Small\n",MediaSizeStr); - return INVALID_YAML_OPT; + } + + // Cci Padding + if(set->options.padCci){ + fseek_64(set->out,set->romInfo.usedSize); + + // Determining Size of Padding + u64 len = set->romInfo.mediaSize - set->romInfo.usedSize; + + // Create Padding chunk + u8 *pad = malloc(set->romInfo.blockSize); + memset(pad,0xff,set->romInfo.blockSize); + + // Writing Dummy Bytes to file + for(u64 i = 0; i < len; i += set->romInfo.blockSize) + fwrite(pad,set->romInfo.blockSize,1,set->out); + + free(pad); } - return 0; + + return; } bool IsCci(u8 *ncsd) { - cci_hdr *hdr = (cci_hdr*)(ncsd+0x100); + cci_hdr *hdr = (cci_hdr*)ncsd; if(!hdr) return false; if(memcmp(hdr->magic,"NCSD",4)!=0) return false; - if(hdr->flags[MediaPlatformIndex] != CTR) return false; - if(hdr->flags[MediaTypeIndex] != CARD1 && hdr->flags[MediaTypeIndex] != CARD2) return false; + if(hdr->flags[cciflag_MEDIA_PLATFORM] != cciplatform_CTR) return false; + if(hdr->flags[cciflag_MEDIA_TYPE] != mediatype_CARD1 && hdr->flags[cciflag_MEDIA_TYPE] != mediatype_CARD2) return false; return true; } -u8* GetPartition(u8 *ncsd, u8 index) -{ - return (u8*)(ncsd+GetPartitionOffset(ncsd,index)); -} - - u64 GetPartitionOffset(u8 *ncsd, u8 index) { - cci_hdr *hdr = (cci_hdr*)(ncsd+0x100); - u32 media_size = 0x200*pow(2,hdr->flags[MediaUnitSize]); + cci_hdr *hdr = (cci_hdr*)ncsd; + u32 media_size = 1 << (hdr->flags[cciflag_MEDIA_BLOCK_SIZE] + 9); u32 offset = u8_to_u64(hdr->offset_sizeTable[index].offset,LE); - return offset*media_size; + return (u64)offset*(u64)media_size; } u64 GetPartitionSize(u8 *ncsd, u8 index) { - cci_hdr *hdr = (cci_hdr*)(ncsd+0x100); - u32 media_size = 0x200*pow(2,hdr->flags[MediaUnitSize]); + cci_hdr *hdr = (cci_hdr*)ncsd; + u32 media_size = 1 << (hdr->flags[cciflag_MEDIA_BLOCK_SIZE] + 9); u32 size = u8_to_u64(hdr->offset_sizeTable[index].size,LE); - return size*media_size; + return (u64)size*(u64)media_size; } + +u8* GetPartition(u8 *ncsd, u8 index) +{ + return ncsd + GetPartitionOffset(ncsd,index); +} \ No newline at end of file diff --git a/makerom/ncsd.h b/makerom/ncsd.h index d86a1bb0..3b20f657 100644 --- a/makerom/ncsd.h +++ b/makerom/ncsd.h @@ -1,199 +1,54 @@ #pragma once -// Enums typedef enum { - NCSD_NO_NCCH0 = -1, - NCSD_INVALID_NCCH0 = -2, - NCSD_INVALID_NCCH = -3, - INVALID_YAML_OPT = -4, - CCI_SIG_FAIL = -5, - -} ncsd_errors; + cciflag_BACKUP_WRITE_WAIT_TIME = 0, + cciflag_FW6_SAVE_CRYPTO = 1, + cciflag_CARD_DEVICE = 3, + cciflag_MEDIA_PLATFORM = 4, + cciflag_MEDIA_TYPE = 5, + cciflag_MEDIA_BLOCK_SIZE = 6, + cciflag_CARD_DEVICE_OLD = 7 +} cci_flagindex; typedef enum { - FW6x_BackupWriteWaitTime = 0, - FW6x_SaveCryptoFlag = 1, - CardDeviceFlag = 3, - MediaPlatformIndex = 4, - MediaTypeIndex = 5, - MediaUnitSize = 6, - OldCardDeviceFlag = 7 -} FlagIndex; + carddevice_NOR_FLASH = 1, + carddevice_NONE = 2, + carddevice_BT = 3 +} cci_carddevice; typedef enum { - CARD_DEVICE_NOR_FLASH = 1, - CARD_DEVICE_NONE = 2, - CARD_DEVICE_BT = 3 -} _CardDevice; + cciplatform_CTR = 1, +} cci_platform; typedef enum { - CTR = 1, -} _PlatformIndex; - -typedef enum -{ - INNER_DEVICE, - CARD1, - CARD2, - EXTENDED_DEVICE -} _TypeIndex; + mediatype_INNER_DEVICE, // NAND + mediatype_CARD1, + mediatype_CARD2, + mediatype_EXTENDED_DEVICE +} cci_mediatype; // Structs typedef struct { u8 offset[4]; u8 size[4]; -} partition_offsetsize; - -typedef struct -{ - u8 magic[4]; - u8 mediaSize[4]; - u8 titleId[8]; - u8 partitionsFsType[8]; - u8 partitionsCryptoType[8]; - partition_offsetsize offset_sizeTable[8]; - u8 exhdrHash[0x20]; - u8 additionalHdrSize[0x4]; - u8 sectorZeroOffset[0x4]; - u8 partitionFlags[8]; - u8 partitionIdTable[8][8]; - u8 padding[0x30]; -} ncsd_hdr; +} ncch_offsetsize; typedef struct { + u8 signature[0x100]; u8 magic[4]; u8 mediaSize[4]; u8 titleId[8]; - u8 contentFsType[8]; - u8 contentCryptoType[8]; - partition_offsetsize offset_sizeTable[8]; - u8 padding0[0x28]; + u8 padding0[0x10]; + ncch_offsetsize offset_sizeTable[8]; + u8 padding1[0x28]; u8 flags[8]; - u8 contentIdTable[8][8]; - u8 padding1[0x30]; + u8 ncchIdTable[8][8]; + u8 padding2[0x30]; } cci_hdr; -typedef struct -{ - u8 writableAddress[4]; - u8 cardInfoBitmask[4]; - // Notes: reserved[0xDF8]; - u8 reserved0[0xf8]; - u8 mediaSizeUsed[8]; - u8 reserved1[0x8]; - u8 unknown[0x4]; - u8 reserved2[0xc]; - u8 cverTitleId[8]; - u8 cverTitleVersion[2]; - u8 reserved3[0xcd6]; - // - u8 ncch0TitleId[8]; - u8 reserved4[8]; - u8 initialData[0x30]; - u8 reserved5[0xc0]; - u8 ncch0Hdr[0x100]; -} cardinfo_hdr; - -typedef struct -{ - u8 cardDeviceReserved1[0x200]; - u8 titleKey[0x10]; - u8 cardDeviceReserved2[0xf0]; -} devcardinfo_hdr; - -typedef struct -{ - u8 signature[0x100]; - cci_hdr cciHdr; - cardinfo_hdr cardinfo; - devcardinfo_hdr devcardinfo; -} InternalCCI_Context; - -typedef struct -{ - FILE *out; - keys_struct *keys; - - struct{ - bool fillOutCci; - bool useDevCardInfo; - u32 mediaUnit; - - u64 savedataSize; - } option; - - struct{ - /* Data */ - buffer_struct *data; - - /* Misc Records */ - FILE **filePtrs; - u64 fileSize[CCI_MAX_CONTENT]; - u16 count; - - /* Details for NCSD header */ - u8 fsType[CCI_MAX_CONTENT]; - u8 cryptoType[CCI_MAX_CONTENT]; - u64 offset[CCI_MAX_CONTENT]; - u64 size[CCI_MAX_CONTENT]; - u8 titleId[CCI_MAX_CONTENT][8]; - } content; - - struct{ - u64 mediaSize; - u8 mediaId[8]; - u8 flags[8]; - } header; - - struct{ - u64 writableAddress; - u32 cardInfoBitmask; - - u64 cciTotalSize; - - // cver details - u8 cverTitleId[8]; - u8 cverTitleVersion[2]; - - u8 initialData[0x30]; - ncch_hdr ncchHdr; - u8 titleKey[0x10]; - } cardinfo; -} cci_settings; - -#ifndef PUBLIC_BUILD -static const u8 stock_initial_data[0x30] = -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xAD, 0x88, - 0xAC, 0x41, 0xA2, 0xB1, 0x5E, 0x8F, - 0x66, 0x9C, 0x97, 0xE5, 0xE1, 0x5E, - 0xA3, 0xEB, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static const u8 stock_title_key[0x10] = -{ - 0x6E, 0xC7, 0x5F, 0xB2, 0xE2, 0xB4, - 0x87, 0x46, 0x1E, 0xDD, 0xCB, 0xB8, - 0x97, 0x11, 0x92, 0xBA -}; -#endif - -// Public Prototypes -// Build Functions -int build_CCI(user_settings *usrset); - -// Read Functions -bool IsCci(u8 *ncsd); -u8* GetPartition(u8 *ncsd, u8 index); -u64 GetPartitionOffset(u8 *ncsd, u8 index); -u64 GetPartitionSize(u8 *ncsd, u8 index); \ No newline at end of file diff --git a/makerom/ncsd_build.h b/makerom/ncsd_build.h new file mode 100644 index 00000000..35d2677e --- /dev/null +++ b/makerom/ncsd_build.h @@ -0,0 +1,72 @@ +#pragma once +#include "ncsd.h" +#include "tmd_read.h" + + +// Enums +typedef enum +{ + NCSD_NO_NCCH0 = -1, + NCSD_INVALID_NCCH0 = -2, + NCSD_INVALID_NCCH = -3, + INVALID_RSF_OPT = -4, + GEN_HDR_FAIL = -5, + INCOMPAT_CIA = -6, + CCI_CONFIG_FAIL = -7, +} ncsd_errors; + +typedef struct +{ + rsf_settings *rsf; + keys_struct *keys; + + FILE *out; + + struct{ + bool verbose; + bool padCci; + bool noModTid; + bool useExternalSdkCardInfo; + bool closeAlignWR; + + u8 cverDataType; + char *cverDataPath; + tmd_hdr *tmdHdr; + } options; + + struct{ + u32 blockSize; + + u64 mediaSize; + u64 usedSize; + + u8 mediaType; + u8 cardDevice; + u64 saveSize; + u64 card2SaveOffset; + } romInfo; + + struct{ + u8 *data; + u64 dataLen; + infile_type dataType; + + char **path; + + bool active[CCI_MAX_CONTENT]; + u64 dOffset[CCI_MAX_CONTENT]; + u64 *dSize; + u64 titleId[CCI_MAX_CONTENT]; + + u64 cOffset[CCI_MAX_CONTENT]; + } content; + + struct{ + buffer_struct ccihdr; + buffer_struct cardinfohdr; + } headers; +} cci_settings; + +// Public Prototypes +// Build Functions +int build_CCI(user_settings *usrset); \ No newline at end of file diff --git a/makerom/ncsd_read.h b/makerom/ncsd_read.h new file mode 100644 index 00000000..ac152e35 --- /dev/null +++ b/makerom/ncsd_read.h @@ -0,0 +1,8 @@ +#pragma once +#include "ncsd.h" + +// Read Functions +bool IsCci(u8 *ncsd); +u8* GetPartition(u8 *ncsd, u8 index); +u64 GetPartitionOffset(u8 *ncsd, u8 index); +u64 GetPartitionSize(u8 *ncsd, u8 index); \ No newline at end of file diff --git a/makerom/dpki.h b/makerom/pki/dev.h similarity index 99% rename from makerom/dpki.h rename to makerom/pki/dev.h index 867a7076..db7d9761 100644 --- a/makerom/dpki.h +++ b/makerom/pki/dev.h @@ -1,7 +1,7 @@ #pragma once #ifdef PKI_LEGACY -#include "dpki_legacy.h" +#include "pki/dev_legacy.h" #endif // AES KEYS diff --git a/makerom/dpki_legacy.h b/makerom/pki/dev_legacy.h similarity index 100% rename from makerom/dpki_legacy.h rename to makerom/pki/dev_legacy.h diff --git a/makerom/ppki.h b/makerom/pki/prod.h similarity index 99% rename from makerom/ppki.h rename to makerom/pki/prod.h index 41380edc..6935a951 100644 --- a/makerom/ppki.h +++ b/makerom/pki/prod.h @@ -1,7 +1,7 @@ #pragma once #ifdef PKI_LEGACY -#include "ppki_legacy.h" +#include "pki/prod_legacy.h" #endif // AES KEYS @@ -13,8 +13,7 @@ static const unsigned char prod_unfixed_ncch_keyX[2][16] = // Dummy static const unsigned char ctr_common_etd_keyX_ppki[16] = // Dummy { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char ctr_common_etd_keyY_ppki[6][16] = diff --git a/makerom/ppki_legacy.h b/makerom/pki/prod_legacy.h similarity index 100% rename from makerom/ppki_legacy.h rename to makerom/pki/prod_legacy.h diff --git a/makerom/tpki.h b/makerom/pki/test.h similarity index 100% rename from makerom/tpki.h rename to makerom/pki/test.h diff --git a/makerom/romfs.c b/makerom/romfs.c index b09a2cdb..64190c44 100644 --- a/makerom/romfs.c +++ b/makerom/romfs.c @@ -1,8 +1,8 @@ #include "lib.h" #include "dir.h" -#include "ncch.h" +#include "ncch_build.h" #include "romfs.h" -#include "romfs_binary.h" +#include "romfs_gen.h" #include "romfs_import.h" void FreeRomFsCtx(romfs_buildctx *ctx); diff --git a/makerom/romfs_binary.c b/makerom/romfs_gen.c similarity index 98% rename from makerom/romfs_binary.c rename to makerom/romfs_gen.c index b9cc1f02..49718950 100644 --- a/makerom/romfs_binary.c +++ b/makerom/romfs_gen.c @@ -1,6 +1,6 @@ #include "lib.h" #include "dir.h" -#include "ncch.h" +#include "ncch_build.h" #include "romfs.h" const int ROMFS_BLOCK_SIZE = 0x1000; @@ -75,7 +75,10 @@ int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) // Print Filtered FS //printf("print filtered FS\n"); - //fs_PrintDir(ctx->fs,0); + if(ncchset->options.verbose){ + printf("[ROMFS] File System:\n"); + fs_PrintDir(ctx->fs,0); + } //printf("predict romfs size\n"); CalcRomfsSize(ctx); @@ -349,7 +352,7 @@ int AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) u64_to_u8(entry->dataoffset,ctx->u_dataLen,LE); u64_to_u8(entry->datasize,file->size,LE); u8 *data_pos = (ctx->data + ctx->u_dataLen); - ReadFile_64(data_pos,file->size,0,file->fp); + ReadFile64(data_pos,file->size,0,file->fp); ctx->u_dataLen += file->size; // adding file size } else diff --git a/makerom/romfs_binary.h b/makerom/romfs_gen.h similarity index 100% rename from makerom/romfs_binary.h rename to makerom/romfs_gen.h diff --git a/makerom/romfs_import.c b/makerom/romfs_import.c index f4dd71b9..48d68f64 100644 --- a/makerom/romfs_import.c +++ b/makerom/romfs_import.c @@ -1,6 +1,6 @@ #include "lib.h" #include "dir.h" -#include "ncch.h" +#include "ncch_build.h" #include "romfs.h" int PrepareImportRomFsBinaryFromFile(ncch_settings *ncchset, romfs_buildctx *ctx) @@ -11,7 +11,7 @@ int PrepareImportRomFsBinaryFromFile(ncch_settings *ncchset, romfs_buildctx *ctx ivfc_hdr *hdr = calloc(1,sizeof(ivfc_hdr)); - ReadFile_64(hdr,sizeof(ivfc_hdr),0,ctx->romfsBinary); + ReadFile64(hdr,sizeof(ivfc_hdr),0,ctx->romfsBinary); if(memcmp(hdr->magic,"IVFC",4) != 0){ fprintf(stderr,"[ROMFS ERROR] Invalid RomFS Binary.\n"); return INVALID_ROMFS_FILE; @@ -24,7 +24,7 @@ int PrepareImportRomFsBinaryFromFile(ncch_settings *ncchset, romfs_buildctx *ctx int ImportRomFsBinaryFromFile(romfs_buildctx *ctx) { - ReadFile_64(ctx->output,ctx->romfsSize,0,ctx->romfsBinary); + ReadFile64(ctx->output,ctx->romfsSize,0,ctx->romfsBinary); if(memcmp(ctx->output,"IVFC",4) != 0){ fprintf(stderr,"[ROMFS ERROR] Invalid RomFS Binary.\n"); return INVALID_ROMFS_FILE; diff --git a/makerom/rsf_settings.c b/makerom/rsf_settings.c index 84c3edc6..aa7e9388 100644 --- a/makerom/rsf_settings.c +++ b/makerom/rsf_settings.c @@ -56,7 +56,7 @@ void GET_Option(ctr_yaml_context *ctx, rsf_settings *rsf) //else if(cmpYamlValue("NoPadding",ctx)) SetBoolYAMLValue(&rsf->Option.NoPadding,"NoPadding",ctx); else if(cmpYamlValue("EnableCrypt",ctx)) SetBoolYAMLValue(&rsf->Option.EnableCrypt,"EnableCrypt",ctx); else if(cmpYamlValue("EnableCompress",ctx)) SetBoolYAMLValue(&rsf->Option.EnableCompress,"EnableCompress",ctx); - else if(cmpYamlValue("FreeProductCode",ctx)) SetBoolYAMLValue(&rsf->Option.EnableCompress,"FreeProductCode",ctx); + else if(cmpYamlValue("FreeProductCode",ctx)) SetBoolYAMLValue(&rsf->Option.FreeProductCode,"FreeProductCode",ctx); else if(cmpYamlValue("UseOnSD",ctx)) SetBoolYAMLValue(&rsf->Option.UseOnSD,"UseOnSD",ctx); else if(cmpYamlValue("PageSize",ctx)) SetSimpleYAMLValue(&rsf->Option.PageSize,"PageSize",ctx,0); //else if(cmpYamlValue("AppendSystemCall",ctx)) rsf->Option.AppendSystemCallNum = SetYAMLSequence(&rsf->Option.AppendSystemCall,"AppendSystemCall",ctx); diff --git a/makerom/tik.c b/makerom/tik.c index ba95e232..352d5565 100644 --- a/makerom/tik.c +++ b/makerom/tik.c @@ -1,6 +1,6 @@ #include "lib.h" -#include "cia.h" -#include "tik.h" +#include "cia_build.h" +#include "tik_build.h" // Private Prototypes int SetupTicketBuffer(buffer_struct *tik); @@ -99,3 +99,34 @@ void SetContentIndexData(tik_hdr *hdr, cia_settings *ciaset) // TODO? memset(hdr->contentIndex,0,0xAC); memcpy(hdr->contentIndex,default_contentIndex,0x30); } + +tik_hdr *GetTikHdr(u8 *tik) +{ + u32 sigType = u8_to_u32(tik,BE); + + switch(sigType){ + case(RSA_4096_SHA1): + case(RSA_4096_SHA256): + return (tik_hdr*)(tik+0x240); + case(RSA_2048_SHA1): + case(RSA_2048_SHA256): + return (tik_hdr*)(tik+0x140); + case(ECC_SHA1): + case(ECC_SHA256): + return (tik_hdr*)(tik+0x7C); + } + + return NULL; +} + +bool GetTikTitleKey(u8 *titleKey, tik_hdr *hdr, keys_struct *keys) +{ + if(keys->aes.commonKey[hdr->keyId] == NULL) + return false; + + keys->aes.currentCommonKey = hdr->keyId; + + CryptTitleKey(hdr->encryptedTitleKey, titleKey, hdr->titleId, keys, DEC); + + return true; +} \ No newline at end of file diff --git a/makerom/tik.h b/makerom/tik.h index d6d6b1e3..d8b7c054 100644 --- a/makerom/tik.h +++ b/makerom/tik.h @@ -1,15 +1,5 @@ #pragma once -static const unsigned char default_contentIndex[0x30] = -{ - 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0xAC, - 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x84, - 0x00, 0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 -}; - typedef enum { lic_Permanent = 0, @@ -63,6 +53,5 @@ typedef struct u8 contentIndex[0xAC]; } tik_hdr; -// Prototypes -int BuildTicket(cia_settings *ciaset); -int CryptTitleKey(u8 *EncTitleKey, u8 *DecTitleKey, u8 *TitleID, keys_struct *keys, u8 mode); \ No newline at end of file + + diff --git a/makerom/tik_build.h b/makerom/tik_build.h new file mode 100644 index 00000000..ecea0222 --- /dev/null +++ b/makerom/tik_build.h @@ -0,0 +1,16 @@ +#pragma once +#include "tik.h" + +static const unsigned char default_contentIndex[0x30] = +{ + 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0xAC, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x84, + 0x00, 0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 +}; + +// Prototypes +int BuildTicket(cia_settings *ciaset); +int CryptTitleKey(u8 *EncTitleKey, u8 *DecTitleKey, u8 *TitleID, keys_struct *keys, u8 mode); \ No newline at end of file diff --git a/makerom/tik_read.h b/makerom/tik_read.h new file mode 100644 index 00000000..ab4c6e12 --- /dev/null +++ b/makerom/tik_read.h @@ -0,0 +1,5 @@ +#pragma once +#include "tik.h" + +tik_hdr *GetTikHdr(u8 *tik); +bool GetTikTitleKey(u8 *titleKey, tik_hdr *hdr, keys_struct *keys); \ No newline at end of file diff --git a/makerom/titleid.c b/makerom/titleid.c index 071b13ee..d4479c42 100644 --- a/makerom/titleid.c +++ b/makerom/titleid.c @@ -1,5 +1,5 @@ #include "lib.h" -#include "ncch.h" +#include "ncch_read.h" #include "titleid.h" void SetPIDType(u16 *type); @@ -22,7 +22,7 @@ int GetProgramID(u64 *dest, rsf_settings *rsf, bool IsForExheader) u8 variation; if(rsf->TitleInfo.Category && rsf->TitleInfo.CategoryFlags){ - fprintf(stderr,"[ID ERROR] Can not set \"Cateory\" and \"CategoryFlags\" at the same time.\n"); + fprintf(stderr,"[ID ERROR] Can not set \"Category\" and \"CategoryFlags\" at the same time.\n"); return PID_BAD_RSF_SET; } diff --git a/makerom/tmd.c b/makerom/tmd.c index 173c1e44..e81f6688 100644 --- a/makerom/tmd.c +++ b/makerom/tmd.c @@ -1,6 +1,6 @@ #include "lib.h" -#include "cia.h" -#include "tmd.h" +#include "cia_build.h" +#include "tmd_build.h" // Private Prototypes int SetupTMDBuffer(buffer_struct *tik); @@ -85,11 +85,96 @@ int SetupTMDContentRecord(u8 *content_record, cia_settings *ciaset) { for(int i = 0; i < ciaset->content.count; i++){ tmd_content_chunk *ptr = (tmd_content_chunk*)(content_record+sizeof(tmd_content_chunk)*i); - u32_to_u8(ptr->contentID,ciaset->content.id[i],BE); - u16_to_u8(ptr->contentIndex,ciaset->content.index[i],BE); - u16_to_u8(ptr->contentFlags,ciaset->content.flags[i],BE); - u64_to_u8(ptr->contentSize,ciaset->content.size[i],BE); - memcpy(ptr->contentHash,ciaset->content.hash[i],0x20); + u32_to_u8(ptr->id,ciaset->content.id[i],BE); + u16_to_u8(ptr->index,ciaset->content.index[i],BE); + u16_to_u8(ptr->flags,ciaset->content.flags[i],BE); + u64_to_u8(ptr->size,ciaset->content.size[i],BE); + memcpy(ptr->hash,ciaset->content.hash[i],0x20); } return 0; +} + +tmd_hdr *GetTmdHdr(u8 *tmd) +{ + u32 sigType = u8_to_u32(tmd,BE); + + switch(sigType){ + case(RSA_4096_SHA1): + case(RSA_4096_SHA256): + return (tmd_hdr*)(tmd+0x240); + case(RSA_2048_SHA1): + case(RSA_2048_SHA256): + return (tmd_hdr*)(tmd+0x140); + case(ECC_SHA1): + case(ECC_SHA256): + return (tmd_hdr*)(tmd+0x7C); + } + + return NULL; +} + +tmd_content_chunk* GetTmdContentInfo(u8 *tmd) +{ + tmd_hdr *hdr = GetTmdHdr(tmd); + if(!hdr) + return NULL; + + return (tmd_content_chunk*)((u8*)hdr + sizeof(tmd_hdr) + (sizeof(tmd_content_info_record)*64)); +} + +u64 GetTmdTitleId(tmd_hdr *hdr) +{ + return u8_to_u64(hdr->titleID,BE); +} + +u32 GetTmdSaveSize(tmd_hdr *hdr) +{ + return u8_to_u32(hdr->savedataSize,BE); +} + +u16 GetTmdContentCount(tmd_hdr *hdr) +{ + return u8_to_u16(hdr->contentCount,BE); +} + +u16 GetTmdVersion(tmd_hdr *hdr) +{ + return u8_to_u16(hdr->titleVersion,BE); +} + +u32 GetTmdContentId(tmd_content_chunk info) +{ + return u8_to_u32(info.id,BE); +} + +u16 GetTmdContentIndex(tmd_content_chunk info) +{ + return u8_to_u16(info.index,BE); +} + +u16 GetTmdContentFlags(tmd_content_chunk info) +{ + return u8_to_u16(info.flags,BE); +} + +u64 GetTmdContentSize(tmd_content_chunk info) +{ + return u8_to_u64(info.size,BE); +} + +u8* GetTmdContentHash(tmd_content_chunk *info) +{ + return (u8*)info->hash; +} + +bool IsTmdContentEncrypted(tmd_content_chunk info) +{ + return (GetTmdContentFlags(info) & content_Encrypted) == content_Encrypted; +} + +bool ValidateTmdContent(u8 *data, tmd_content_chunk info) +{ + u8 hash[32]; + ctr_sha(data,GetTmdContentSize(info),hash,CTR_SHA_256); + return memcmp(hash,GetTmdContentHash(&info),32) == 0; } \ No newline at end of file diff --git a/makerom/tmd.h b/makerom/tmd.h index 6f16e024..db24cdbc 100644 --- a/makerom/tmd.h +++ b/makerom/tmd.h @@ -15,11 +15,11 @@ typedef enum typedef struct { - u8 contentID[4]; - u8 contentIndex[2]; - u8 contentFlags[2]; - u8 contentSize[8]; - u8 contentHash[0x20]; // SHA 256 + u8 id[4]; + u8 index[2]; + u8 flags[2]; + u8 size[8]; + u8 hash[0x20]; // SHA 256 } tmd_content_chunk; typedef struct @@ -58,11 +58,4 @@ typedef struct u8 bootContent[2]; u8 padding3[2]; u8 infoRecordHash[0x20]; // SHA-256 -} tmd_hdr; - -// Prototypes -u32 PredictTMDSize(u16 ContentCount); -int BuildTMD(cia_settings *ciaset); - -// Read TMD -tmd_hdr *GetTmdHdr(u8 *tmd); \ No newline at end of file +} tmd_hdr; \ No newline at end of file diff --git a/makerom/tmd_build.h b/makerom/tmd_build.h new file mode 100644 index 00000000..52c7bb6b --- /dev/null +++ b/makerom/tmd_build.h @@ -0,0 +1,6 @@ +#pragma once +#include "tmd.h" + +// Prototypes +u32 PredictTMDSize(u16 ContentCount); +int BuildTMD(cia_settings *ciaset); diff --git a/makerom/tmd_read.c b/makerom/tmd_read.c deleted file mode 100644 index 54276478..00000000 --- a/makerom/tmd_read.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "lib.h" -#include "cia.h" -#include "tmd.h" - -tmd_hdr *GetTmdHdr(u8 *tmd) -{ - u32 sigType = u8_to_u32(tmd,BE); - - switch(sigType){ - case(RSA_4096_SHA1): - case(RSA_4096_SHA256): - return (tmd_hdr*)(tmd+0x240); - case(RSA_2048_SHA1): - case(RSA_2048_SHA256): - return (tmd_hdr*)(tmd+0x140); - case(ECC_SHA1): - case(ECC_SHA256): - return (tmd_hdr*)(tmd+0x7C); - } - - return NULL; -} \ No newline at end of file diff --git a/makerom/tmd_read.h b/makerom/tmd_read.h new file mode 100644 index 00000000..e09f4713 --- /dev/null +++ b/makerom/tmd_read.h @@ -0,0 +1,19 @@ +#pragma once +#include "tmd.h" + +// Read TMD +tmd_hdr *GetTmdHdr(u8 *tmd); +tmd_content_chunk* GetTmdContentInfo(u8 *tmd); +u64 GetTmdTitleId(tmd_hdr *hdr); +u32 GetTmdSaveSize(tmd_hdr *hdr); +u16 GetTmdContentCount(tmd_hdr *hdr); +u16 GetTmdVersion(tmd_hdr *hdr); + +u32 GetTmdContentId(tmd_content_chunk info); +u16 GetTmdContentIndex(tmd_content_chunk info); +u16 GetTmdContentFlags(tmd_content_chunk info); +u64 GetTmdContentSize(tmd_content_chunk info); +u8* GetTmdContentHash(tmd_content_chunk info); + +bool IsTmdContentEncrypted(tmd_content_chunk info); +bool ValidateTmdContent(u8 *data, tmd_content_chunk info); \ No newline at end of file diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 2486c4c8..423278bf 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -10,9 +10,9 @@ void PrintArgInvalid(char *arg); void PrintArgReqParam(char *arg, u32 paramNum); void PrintNoNeedParam(char *arg); -int ParseArgs(int argc, char *argv[], user_settings *usr_settings) +int ParseArgs(int argc, char *argv[], user_settings *set) { - if(argv == NULL || usr_settings == NULL) + if(argv == NULL || set == NULL) return USR_PTR_PASS_FAIL; if(argc < 2){ @@ -29,26 +29,23 @@ int ParseArgs(int argc, char *argv[], user_settings *usr_settings) } // Allocating Memory for Content Path Ptrs - usr_settings->common.contentPath = calloc(CIA_MAX_CONTENT,sizeof(char*)); - if(usr_settings->common.contentPath == NULL){ + set->common.contentPath = calloc(CIA_MAX_CONTENT,sizeof(char*)); + if(set->common.contentPath == NULL){ fprintf(stderr,"[SETTING ERROR] Not Enough Memory\n"); return USR_MEM_ERROR; } // Initialise Keys - InitKeys(&usr_settings->common.keys); + InitKeys(&set->common.keys); // Setting Defaults - SetDefaults(usr_settings); + SetDefaults(set); // Parsing Arguments -#ifdef DEBUG - fprintf(stdout,"[DEBUG] Parsing Args\n"); -#endif int set_result; int i = 1; while(i < argc){ - set_result = SetArgument(argc,i,argv,usr_settings); + set_result = SetArgument(argc,i,argv,set); if(set_result < 1){ fprintf(stderr,"[RESULT] Invalid arguments, see '%s -help'\n",argv[0]); return set_result; @@ -57,39 +54,30 @@ int ParseArgs(int argc, char *argv[], user_settings *usr_settings) } // Checking arguments -#ifdef DEBUG - fprintf(stdout,"[DEBUG] Checking Args\n"); -#endif - set_result = CheckArgumentCombination(usr_settings); - if(set_result) return set_result; + if((set_result = CheckArgumentCombination(set)) != 0) + return set_result; // Setting Keys -#ifdef DEBUG - fprintf(stdout,"[DEBUG] Setting Keys\n"); -#endif - set_result = SetKeys(&usr_settings->common.keys); - if(set_result) return set_result; + if((set_result = SetKeys(&set->common.keys)) != 0) + return set_result; -#ifdef DEBUG - fprintf(stdout,"[DEBUG] Generating output path name if required\n"); -#endif - - if(!usr_settings->common.outFileName){ + // Generating outpath if required + if(!set->common.outFileName){ char *source_path = NULL; - if(usr_settings->ncch.buildNcch0) - source_path = usr_settings->common.rsfPath; - else if(usr_settings->common.workingFileType == infile_ncsd || usr_settings->common.workingFileType == infile_srl) - source_path = usr_settings->common.workingFilePath; + if(set->ncch.buildNcch0) + source_path = set->common.rsfPath; + else if(set->common.workingFileType == infile_ncsd || set->common.workingFileType == infile_cia || set->common.workingFileType == infile_srl) + source_path = set->common.workingFilePath; else - source_path = usr_settings->common.contentPath[0]; - u16 outfile_len = strlen(source_path) + 3; - usr_settings->common.outFileName = calloc(outfile_len,sizeof(char)); - if(!usr_settings->common.outFileName){ + source_path = set->common.contentPath[0]; + u16 outfile_len = strlen(source_path) + 0x10; + set->common.outFileName = calloc(outfile_len,sizeof(char)); + if(!set->common.outFileName){ fprintf(stderr,"[SETTING ERROR] Not Enough Memory\n"); return USR_MEM_ERROR; } - usr_settings->common.outFileName_mallocd = true; - append_filextention(usr_settings->common.outFileName,outfile_len,source_path,(char*)&output_extention[usr_settings->common.outFormat-1]); + set->common.outFileName_mallocd = true; + append_filextention(set->common.outFileName,outfile_len,source_path,(char*)&output_extention[set->common.outFormat-1]); } return 0; } @@ -101,12 +89,14 @@ void SetDefaults(user_settings *set) set->common.keys.accessDescSign.presetType = desc_preset_NONE; // Build NCCH Info + set->ncch.useSecCrypto = false; set->ncch.buildNcch0 = false; set->ncch.includeExefsLogo = false; set->common.outFormat = NCCH; set->ncch.ncchType = format_not_set; // RSF Settings + clrmem(&set->common.rsfSet,sizeof(rsf_settings)); set->common.rsfSet.Option.EnableCompress = true; set->common.rsfSet.Option.EnableCrypt = true; set->common.rsfSet.Option.UseOnSD = false; @@ -119,13 +109,15 @@ void SetDefaults(user_settings *set) set->cci.useSDKStockData = false; // CIA Info + set->cia.includeUpdateNcch = false; + set->cia.deviceId = 0; + set->cia.eshopAccId = 0; set->cia.useDataTitleVer = false; set->cia.titleVersion[VER_MAJOR] = MAX_U16; // invalid so changes can be detected set->cia.randomTitleKey = false; set->common.keys.aes.currentCommonKey = MAX_U8 + 1; // invalid so changes can be detected - for(int i = 0; i < CIA_MAX_CONTENT; i++){ + for(int i = 0; i < CIA_MAX_CONTENT; i++) set->cia.contentId[i] = MAX_U32 + 1; // invalid so changes can be detected - } } int SetArgument(int argc, int i, char *argv[], user_settings *set) @@ -137,7 +129,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) // Global Settings if(strcmp(argv[i],"-rsf") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-rsf",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->common.rsfPath = argv[i+1]; @@ -145,7 +137,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-f") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-f",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } if(strcasecmp(argv[i+1],"ncch") == 0) @@ -162,17 +154,25 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-o") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-o",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->common.outFileName = argv[i+1]; set->common.outFileName_mallocd = false; return 2; } + else if(strcmp(argv[i],"-v") == 0){ + if(ParamNum){ + PrintNoNeedParam(argv[i]); + return USR_BAD_ARG; + } + set->common.verbose = true; + return 1; + } // Key Options else if(strcmp(argv[i],"-target") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-target",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } if(strcasecmp(argv[i+1],"test") == 0 || strcasecmp(argv[i+1],"t") == 0) @@ -181,7 +181,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) set->common.keys.keyset = pki_DEVELOPMENT; else if(strcasecmp(argv[i+1],"retail") == 0 || strcasecmp(argv[i+1],"production") == 0 || strcasecmp(argv[i+1],"p") == 0) set->common.keys.keyset = pki_PRODUCTION; - //else if(strcasecmp(argv[i+1],"custom") == 0 || strcasecmp(argv[i+1],"c") == 0) + //else if(strcasecmp(argv[i+1],"custom") == 0 || strcasecmp(argv[i+1],"c") == 0) // given all known keys are here, this isn't needed // set->common.keys.keyset = pki_CUSTOM; else{ fprintf(stderr,"[SETTING ERROR] Unrecognised target '%s'\n",argv[i+1]); @@ -189,22 +189,36 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } return 2; } - else if(strcmp(argv[i],"-ckeyID") == 0){ + else if(strcmp(argv[i],"-ckeyid") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-ckeyID",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->common.keys.aes.currentCommonKey = strtol(argv[i+1],NULL,0); - if(set->common.keys.aes.currentCommonKey > 0xff) + if(set->common.keys.aes.currentCommonKey > MAX_CMN_KEY) + { + fprintf(stderr,"[SETTING ERROR] Invalid Common Key Index: 0x%x\n",set->common.keys.aes.currentCommonKey); + return USR_BAD_ARG; + } + return 2; + } + else if(strcmp(argv[i],"-ncchseckey") == 0){ + if(ParamNum != 1){ + PrintArgReqParam(argv[i],1); + return USR_ARG_REQ_PARAM; + } + set->ncch.useSecCrypto = true; + set->ncch.keyXID = strtol(argv[i+1],NULL,0); + if(set->ncch.keyXID > MAX_NCCH_KEYX) { - fprintf(stderr,"[SETTING ERROR] Invalid Common Key ID: 0x%x\n",set->common.keys.aes.currentCommonKey); + fprintf(stderr,"[SETTING ERROR] Invalid NCCH KeyX Index: 0x%x\n",set->ncch.keyXID); return USR_BAD_ARG; } return 2; } else if(strcmp(argv[i],"-showkeys") == 0){ if(ParamNum){ - PrintNoNeedParam("-showkeys"); + PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->common.keys.dumpkeys = true; @@ -212,7 +226,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-fsign") == 0){ if(ParamNum){ - PrintNoNeedParam("-fsign"); + PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->common.keys.rsa.isFalseSign = true; @@ -222,7 +236,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) // Ncch Options else if(strcmp(argv[i],"-elf") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-elf",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->ncch.elfPath = argv[i+1]; @@ -232,7 +246,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) else if(strcmp(argv[i],"-icon") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-icon",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->ncch.iconPath = argv[i+1]; @@ -241,7 +255,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-banner") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-banner",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->ncch.bannerPath = argv[i+1]; @@ -250,7 +264,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-logo") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-logo",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->ncch.logoPath = argv[i+1]; @@ -259,7 +273,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-desc") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-desc",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } char *tmp = argv[i+1]; @@ -281,7 +295,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) else if(strcasecmp(app_type,"ECApp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_EC_APP; else if(strcasecmp(app_type,"Demo") == 0) set->common.keys.accessDescSign.presetType = desc_preset_DEMO; else if(strcasecmp(app_type,"DlpChild") == 0 || strcasecmp(app_type,"Dlp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_DLP; - //else if(strcasecmp(app_type,"FIRM") == 0) set->common.keys.accessDescSign.presetType = desc_preset_FIRM; + else if(strcasecmp(app_type,"FIRM") == 0) set->common.keys.accessDescSign.presetType = desc_preset_FIRM; else{ fprintf(stderr,"[SETTING ERROR] Accessdesc AppType preset '%s' not valid, please manually configure RSF\n",app_type); return USR_BAD_ARG; @@ -324,7 +338,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-exefslogo") == 0){ if(ParamNum){ - PrintNoNeedParam("-exefslogo"); + PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->ncch.includeExefsLogo = true; @@ -335,7 +349,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) // Ncch Rebuild Options else if(strcmp(argv[i],"-code") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-code",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->ncch.codePath = argv[i+1]; @@ -344,16 +358,16 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-exheader") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-exheader",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->ncch.exheaderPath = argv[i+1]; set->ncch.ncchType |= CXI; return 2; } - else if(strcmp(argv[i],"-plain-region") == 0){ + else if(strcmp(argv[i],"-plainrgn") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-plain-region",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->ncch.plainRegionPath = argv[i+1]; @@ -362,7 +376,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-romfs") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-romfs",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->ncch.romfsPath = argv[i+1]; @@ -370,9 +384,9 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return 2; } // Cci Options - else if(strcmp(argv[i],"-devcardcci") == 0){ + else if(strcmp(argv[i],"-devcci") == 0){ if(ParamNum){ - PrintNoNeedParam("-devcardcci"); + PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->cci.useSDKStockData = true; @@ -380,7 +394,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-nomodtid") == 0){ if(ParamNum){ - PrintNoNeedParam("-nomodtid"); + PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->cci.dontModifyNcchTitleID = true; @@ -388,7 +402,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-alignwr") == 0){ if(ParamNum){ - PrintNoNeedParam("-alignwr"); + PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->cci.closeAlignWritableRegion = true; @@ -396,17 +410,42 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-cverinfo") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-cverinfo",1); + PrintArgReqParam(argv[i],1); + return USR_BAD_ARG; + } + char *pos = strstr(argv[i+1],":"); + if(!pos || strlen(pos) < 2){ + fprintf(stderr,"[SETTING ERROR] Bad argument '%s %s', correct format:\n",argv[i],argv[i+1]); + fprintf(stderr," %s :<'cia'/'tmd'>\n",argv[i]); return USR_BAD_ARG; } - set->cci.cverCiaPath = argv[i+1]; + + char *dtype = pos + 1; + if(strcasecmp(dtype,"tmd") == 0) + set->cci.cverDataType = CVER_DTYPE_TMD; + else if(strcasecmp(dtype,"cia") == 0) + set->cci.cverDataType = CVER_DTYPE_CIA; + else{ + fprintf(stderr,"[SETTING ERROR] Unrecognised cver data type:\"%s\"\n",dtype); + return USR_BAD_ARG; + } + + u32 path_len = (pos-argv[i+1])+1; + set->cci.cverDataPath = calloc(path_len,sizeof(char)); + strncpy(set->cci.cverDataPath,argv[i+1],path_len-1); + + if(!AssertFile(set->cci.cverDataPath)){ + fprintf(stderr,"[SETTING ERROR] Failed to open '%s'\n",set->cci.cverDataPath); + return USR_BAD_ARG; + } + return 2; } // Cia Options else if(strcmp(argv[i],"-major") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-major",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->cia.useNormTitleVer = true; @@ -420,7 +459,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-minor") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-minor",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->cia.useNormTitleVer = true; @@ -434,7 +473,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-micro") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-micro",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } u32 ver = strtoul(argv[i+1],NULL,10); @@ -447,7 +486,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } else if(strcmp(argv[i],"-dver") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-dver",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->cia.useDataTitleVer = true; @@ -457,70 +496,100 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_BAD_ARG; } set->cia.titleVersion[VER_MAJOR] = (ver >> 6) & VER_MAJOR_MAX; - set->cia.titleVersion[VER_MINOR] = ver & VER_MAJOR_MAX; + set->cia.titleVersion[VER_MINOR] = ver & VER_MINOR_MAX; + return 2; + } + else if(strcmp(argv[i],"-deviceid") == 0){ + if(ParamNum != 1){ + PrintArgReqParam(argv[i],1); + return USR_ARG_REQ_PARAM; + } + set->cia.deviceId = strtoul(argv[i+1],NULL,16); + return 2; + } + else if(strcmp(argv[i],"-esaccid") == 0){ + if(ParamNum != 1){ + PrintArgReqParam(argv[i],1); + return USR_ARG_REQ_PARAM; + } + set->cia.eshopAccId = strtoul(argv[i+1],NULL,16); return 2; } else if(strcmp(argv[i],"-rand") == 0){ if(ParamNum){ - PrintNoNeedParam("-rand"); + PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->cia.randomTitleKey = true; return 1; } - else if(strcmp(argv[i],"-cci") == 0){ + else if(strcmp(argv[i],"-dlc") == 0){ + if(ParamNum){ + PrintNoNeedParam(argv[i]); + return USR_BAD_ARG; + } + set->cia.DlcContent = true; + return 1; + } + else if(strcmp(argv[i],"-srl") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-cci",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->ncch.buildNcch0 = false; - set->common.workingFileType = infile_ncsd; + set->common.workingFileType = infile_srl; set->common.workingFilePath = argv[i+1]; set->common.outFormat = CIA; return 2; + } - else if(strcmp(argv[i],"-srl") == 0){ + + // Ncch Container Conversion + else if(strcmp(argv[i],"-ccitocia") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-srl",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } set->ncch.buildNcch0 = false; - set->common.workingFileType = infile_srl; + set->common.workingFileType = infile_ncsd; set->common.workingFilePath = argv[i+1]; set->common.outFormat = CIA; return 2; - } - else if(strcmp(argv[i],"-dlc") == 0){ + else if(strcmp(argv[i],"-ciatocci") == 0){ + if(ParamNum != 1){ + PrintArgReqParam(argv[i],1); + return USR_ARG_REQ_PARAM; + } + set->ncch.buildNcch0 = false; + set->common.workingFileType = infile_cia; + set->common.workingFilePath = argv[i+1]; + set->common.outFormat = CCI; + return 2; + } + else if(strcmp(argv[i],"-inclupd") == 0){ if(ParamNum){ - PrintNoNeedParam("-dlc"); + PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } - set->cia.DlcContent = true; + set->cia.includeUpdateNcch = true; return 1; } - + // Other Setting else if(strcmp(argv[i],"-content") == 0){ if(ParamNum != 1){ - PrintArgReqParam("-content",1); + PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } char *pos = strstr(argv[i+1],":"); - if(!pos){ + if(!pos || strlen(pos) < 2){ fprintf(stderr,"[SETTING ERROR] Bad argument '%s %s', correct format:\n",argv[i],argv[i+1]); - fprintf(stderr," -content :\n"); + fprintf(stderr," %s :\n",argv[i]); fprintf(stderr," If generating a CIA, then use the format:\n"); - fprintf(stderr," -content ::\n"); + fprintf(stderr," %s ::\n",argv[i]); return USR_BAD_ARG; } - if(strlen(pos) < 2){ - fprintf(stderr,"[SETTING ERROR] Bad argument '%s %s', correct format:\n",argv[i],argv[i+1]); - fprintf(stderr," -content :\n"); - fprintf(stderr," If generating a CIA, then use the format:\n"); - fprintf(stderr," -content ::\n"); - return USR_BAD_ARG; - } /* Getting Content Index */ u16 content_index = strtol((char*)(pos+1),NULL,0); @@ -528,7 +597,6 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) /* Storing Content Filepath */ u32 path_len = (u32)(pos-argv[i+1])+1; - if(content_index == 0) set->ncch.buildNcch0 = false; if(set->common.contentPath[content_index] != NULL){ fprintf(stderr,"[SETTING ERROR] Content %d is already specified\n",content_index); return USR_BAD_ARG; @@ -539,12 +607,16 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_MEM_ERROR; } strncpy(set->common.contentPath[content_index],argv[i+1],path_len-1); - + if(!AssertFile(set->common.contentPath[content_index])){ + fprintf(stderr,"[SETTING ERROR] '%s' could not be opened\n",set->common.contentPath[content_index]); + return USR_BAD_ARG; + } + set->common.contentSize[content_index] = GetFileSize64(set->common.contentPath[content_index]); + /* Get ContentID for CIA gen */ char *pos2 = strstr(pos+1,":"); - if(pos2) { + if(pos2) set->cia.contentId[content_index] = strtoul((pos2+1),NULL,0); - } /* Return Next Arg Pos*/ return 2; @@ -563,7 +635,6 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) fprintf(stderr,"[SETTING ERROR] Not enough memory\n"); return MEM_ERROR; } - //memset(set->dname.items,0,sizeof(dname_item)*set->dname.m_items); } else if(set->dname.m_items == set->dname.u_items){ set->dname.m_items *= 2; @@ -655,7 +726,7 @@ int CheckArgumentCombination(user_settings *set) return USR_BAD_ARG; } - if(set->common.outFormat == CIA && set->cci.cverCiaPath){ + if(set->common.outFormat == CIA && set->cci.cverDataPath){ fprintf(stderr,"[SETTING ERROR] You cannot use argument \"-cverinfo\" when generating a CIA\n"); return USR_BAD_ARG; } @@ -700,7 +771,7 @@ int CheckArgumentCombination(user_settings *set) return USR_BAD_ARG; } if(!buildCXI && set->ncch.plainRegionPath){ - PrintArgInvalid("-plain-region"); + PrintArgInvalid("-plainrgn"); return USR_BAD_ARG; } if(!set->ncch.buildNcch0 && set->ncch.includeExefsLogo){ @@ -715,45 +786,47 @@ int CheckArgumentCombination(user_settings *set) return 0; } -void init_UserSettings(user_settings *usr_settings) +void init_UserSettings(user_settings *set) { - memset(usr_settings,0,sizeof(user_settings)); + memset(set,0,sizeof(user_settings)); } -void free_UserSettings(user_settings *usr_settings) +void free_UserSettings(user_settings *set) { // Free Content Paths - if(usr_settings->common.contentPath){ + if(set->common.contentPath){ for(int i = 0; i < CIA_MAX_CONTENT; i++) - free(usr_settings->common.contentPath[i]); - free(usr_settings->common.contentPath); + free(set->common.contentPath[i]); + free(set->common.contentPath); } // free -DNAME=VALUE - for(u32 i = 0; i < usr_settings->dname.u_items; i++){ - free(usr_settings->dname.items[i].name); - free(usr_settings->dname.items[i].value); + for(u32 i = 0; i < set->dname.u_items; i++){ + free(set->dname.items[i].name); + free(set->dname.items[i].value); } - free(usr_settings->dname.items); + free(set->dname.items); + free(set->cci.cverDataPath); + // Free Spec File Setting - free_RsfSettings(&usr_settings->common.rsfSet); + free_RsfSettings(&set->common.rsfSet); // Free Key Data - FreeKeys(&usr_settings->common.keys); + FreeKeys(&set->common.keys); // Free Working File - free(usr_settings->common.workingFile.buffer); + free(set->common.workingFile.buffer); // Free outfile path, if malloc'd - if(usr_settings->common.outFileName_mallocd) - free(usr_settings->common.outFileName); + if(set->common.outFileName_mallocd) + free(set->common.outFileName); // Clear settings - init_UserSettings(usr_settings); + init_UserSettings(set); // Free - free(usr_settings); + free(set); } void PrintNeedsArg(char *arg) @@ -787,53 +860,52 @@ void DisplayHelp(char *app_name) printf("Option Parameter Explanation\n"); printf("GLOBAL OPTIONS:\n"); printf(" -help Display this text\n"); - printf(" -rsf Rom Specification File (*.rsf)\n"); - printf(" -f Output Format, defaults to 'ncch'\n"); - printf(" -o Output File\n"); - //printf(" -v Verbose\n"); - printf(" -DNAME=VALUE Substitute values in Spec file\n"); + printf(" -rsf ROM Spec File (*.rsf)\n"); + printf(" -f Output format, defaults to 'ncch'\n"); + printf(" -o Output file\n"); + printf(" -v Verbose output\n"); + printf(" -DNAME=VALUE Substitute values in RSF file\n"); printf("KEY OPTIONS:\n"); - //printf(" -target Target for crypto, defaults to 't'\n"); printf(" -target Target for crypto, defaults to 't'\n"); printf(" 't' Test(false) Keys & prod Certs\n"); printf(" 'd' Development Keys & Certs\n"); printf(" 'p' Production Keys & Certs\n"); - //printf(" 'c' Custom Keys & Certs\n"); - printf(" -ckeyID Override the automatic commonKey selection\n"); - printf(" -showkeys Display the loaded keychain\n"); + printf(" -ckeyid Override the automatic common key selection\n"); + printf(" -ncchseckey Ncch keyX index ('0'=1.0+, '1'=7.0+)\n"); + printf(" -showkeys Display the loaded key chain\n"); printf(" -fsign Ignore invalid signatures\n"); printf("NCCH OPTIONS:\n"); - printf(" -elf ELF File\n"); - printf(" -icon Icon File\n"); - printf(" -banner Banner File\n"); - printf(" -logo Logo File (Overrides \"BasicInfo/Logo\" in RSF)\n"); - printf(" -desc : Specify Access Descriptor Preset\n"); - //printf(" AppTypes:\n"); - //printf(" 'SDApp' Normal SD Application\n"); - //printf(" 'ECApp' SD Application with DLC Capability\n"); - //printf(" 'Demo' SD Demo Application\n"); - //printf(" 'Dlp' NAND DLP Child Application\n"); - //printf(" 'FIRM' FIRM CXI\n"); - printf(" -exefslogo Include Logo in ExeFs (Required for usage on <5.X Systems)\n"); + printf(" -elf ELF file\n"); + printf(" -icon Icon file\n"); + printf(" -banner Banner file\n"); + printf(" -logo Logo file (Overrides \"BasicInfo/Logo\" in RSF)\n"); + printf(" -desc : Specify Access Descriptor template\n"); + printf(" -exefslogo Include Logo in ExeFS (Required for usage on <5.0 systems)\n"); printf("NCCH REBUILD OPTIONS:\n"); - printf(" -code Specify ExeFs code File\n"); - printf(" -exheader ExHeader Template File\n"); - printf(" -plain-region PlainRegion File\n"); - printf(" -romfs RomFS File\n"); + printf(" -code Decompressed ExeFS \".code\"\n"); + printf(" -exheader Exheader template\n"); + printf(" -plainrgn Plain Region binary\n"); + printf(" -romfs RomFS binary\n"); printf("CCI OPTIONS:\n"); - printf(" -content : Specify content files\n"); - printf(" -devcardcci Use SDK CardInfo Method\n"); + printf(" -content : Specify content files\n"); + printf(" -devcci Use external CTRSDK \"CardInfo\" method\n"); printf(" -nomodtid Don't Modify Content TitleIDs\n"); - printf(" -alignwr Align Writeable Region to the end of last NCCH\n"); - printf(" -cverinfo Include CVer title info\n"); + printf(" -alignwr Align writeable region to the end of last NCCH\n"); + printf(" -cverinfo : Include cver title info\n"); printf("CIA OPTIONS:\n"); - printf(" -content :: Specify content files\n"); - printf(" -major Specify Major Version\n"); - printf(" -minor Specify Minor Version\n"); - printf(" -micro Specify Micro Version\n"); - printf(" -dver Specify Data Title Version\n"); + printf(" -content :: Specify content files\n"); + printf(" -major Major version\n"); + printf(" -minor Minor version\n"); + printf(" -micro Micro version\n"); + printf(" -dver Data-title version\n"); + printf(" -deviceid 3DS unique device ID\n"); + printf(" -esaccid e-Shop account ID\n"); printf(" -rand Use a random title key\n"); - printf(" -cci Convert CCI to CIA\n"); - printf(" -srl Use TWL SRL as Content0\n"); printf(" -dlc Create DLC CIA\n"); + printf(" -srl Package a TWL SRL in a CIA\n"); + printf("NCCH CONTAINER CONVERSION:\n"); + printf(" -ccitocia Convert CCI to CIA\n"); + printf(" -ciatocci Convert CIA to CCI\n"); + printf(" -inclupd Include \"Update NCCH\" in CCI to CIA conversion\n"); + } \ No newline at end of file diff --git a/makerom/user_settings.h b/makerom/user_settings.h index 95d83173..89d9a9ef 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -21,6 +21,12 @@ typedef enum VER_DVER_MAX = 4095, } title_ver_max; +typedef enum +{ + CVER_DTYPE_TMD, + CVER_DTYPE_CIA, +} cver_data_type; + typedef enum { USR_PTR_PASS_FAIL = -1, @@ -37,6 +43,7 @@ typedef enum infile_ncch, infile_ncsd, infile_srl, + infile_cia, } infile_type; typedef enum @@ -49,7 +56,7 @@ typedef enum NCCH } output_format; -static const char output_extention[4][5] = {".cxi",".cfa",".cci",".cia"}; +static const char output_extention[5][5] = {".cxi",".cfa",".cci",".cia",".app"}; /* This does not follow style, so the rsf string names match the variables where they're stored */ typedef struct @@ -246,6 +253,8 @@ typedef struct typedef struct { struct{ + bool verbose; + char *rsfPath; bool outFileName_mallocd; char *outFileName; @@ -259,9 +268,10 @@ typedef struct // Content Details char **contentPath; + u64 contentSize[CIA_MAX_CONTENT]; char *workingFilePath; - infile_type workingFileType; // Could Be ncch/ncsd/srl. This is mainly used for CIA gen + infile_type workingFileType; // Could Be ncch/ncsd/srl/cia. buffer_struct workingFile; } common; @@ -282,23 +292,32 @@ typedef struct char *exheaderPath; // for .code details char *plainRegionPath; // prebuilt Plain Region char *romfsPath; // Prebuild _cleartext_ romfs binary + + bool useSecCrypto; + u8 keyXID; } ncch; // Ncch0 Build struct{ bool useSDKStockData; // incase we want to use the SDK stock data, for whatever reason. bool dontModifyNcchTitleID; bool closeAlignWritableRegion; - char *cverCiaPath; + + u8 cverDataType; + char *cverDataPath; } cci; // CCI Settings struct{ bool randomTitleKey; bool encryptCia; bool DlcContent; + bool includeUpdateNcch; bool useNormTitleVer; bool useDataTitleVer; u16 titleVersion[3]; + + u32 deviceId; + u32 eshopAccId; u64 contentId[CIA_MAX_CONTENT]; // For CIA } cia; // CIA Settings @@ -306,6 +325,6 @@ typedef struct // Prototypes -void init_UserSettings(user_settings *usr_settings); -void free_UserSettings(user_settings *usr_settings); +void init_UserSettings(user_settings *set); +void free_UserSettings(user_settings *set); int ParseArgs(int argc, char *argv[], user_settings *usr_settings); \ No newline at end of file diff --git a/makerom/utils.c b/makerom/utils.c index 1321168e..bda3076b 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -1,25 +1,9 @@ #include "lib.h" #include "utf.h" -// Memory -void char_to_u8_array(unsigned char destination[], char source[], int size, int endianness, int base) -{ - char tmp[size][2]; - unsigned char *byte_array = malloc(size*sizeof(unsigned char)); - memset(byte_array, 0, size); - memset(destination, 0, size); - memset(tmp, 0, size*2); - - for (int i = 0; i < size; i ++){ - tmp[i][0] = source[(i*2)]; - tmp[i][1] = source[((i*2)+1)]; - tmp[i][2] = '\0'; - byte_array[i] = (unsigned char)strtol(tmp[i], NULL, base); - } - endian_memcpy(destination,byte_array,size,endianness); - free(byte_array); -} +#include "polarssl/base64.h" +// Memory void endian_memcpy(u8 *destination, u8 *source, u32 size, int endianness) { for (u32 i = 0; i < size; i++){ @@ -65,13 +49,13 @@ u64 align(u64 value, u64 alignment) return value; } -u64 min_u64(u64 a, u64 b) +u64 min64(u64 a, u64 b) { if(a < b) return a; return b; } -u64 max_u64(u64 a, u64 b) +u64 max64(u64 a, u64 b) { if(a > b) return a; return b; @@ -187,6 +171,56 @@ int str_utf8_to_u16(u16 **dst, u32 *dst_len, u8 *src, u32 src_len) } #endif +// Base64 +bool IsValidB64Char(char chr) +{ + return (isalnum(chr) || chr == '+' || chr == '/' || chr == '='); +} + +u32 b64_strlen(char *str) +{ + u32 count = 0; + u32 i = 0; + while(str[i] != 0x0){ + if(IsValidB64Char(str[i])) { + //printf("Is Valid: %c\n",str[i]); + count++; + } + i++; + } + + return count; +} + +void b64_strcpy(char *dst, char *src) +{ + u32 src_len = strlen(src); + u32 j = 0; + for(u32 i = 0; i < src_len; i++){ + if(IsValidB64Char(src[i])){ + dst[j] = src[i]; + j++; + } + } + dst[j] = 0; + + //memdump(stdout,"src: ",(u8*)src,src_len+1); + //memdump(stdout,"dst: ",(u8*)dst,j+1); +} + +int b64_decode(u8 *dst, char *src, u32 dst_size) +{ + int ret; + u32 size = dst_size; + + ret = base64_decode(dst,(size_t*)&size,(const u8*)src,strlen(src)); + + if(size != dst_size) + ret = POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL; + + return ret; +} + // Pseudo-Random Number Generator void initRand(void) { @@ -227,7 +261,7 @@ bool AssertFile(char *filename) #endif } -u64 GetFileSize_u64(char *filename) +u64 GetFileSize64(char *filename) { #ifdef _WIN32 struct _stat64 st; @@ -262,7 +296,7 @@ char *getcwdir(char *buffer,int maxlen) #endif } -int TruncateFile_u64(char *filename, u64 filelen) +int TruncateFile64(char *filename, u64 filelen) { #ifdef _WIN32 HANDLE fh; @@ -291,7 +325,7 @@ int TruncateFile_u64(char *filename, u64 filelen) // Wide Char IO #ifdef _WIN32 -u64 wGetFileSize_u64(u16 *filename) +u64 wGetFileSize64(u16 *filename) { struct _stat64 st; _wstat64((wchar_t*)filename, &st); @@ -302,12 +336,10 @@ u64 wGetFileSize_u64(u16 *filename) //IO Misc u8* ImportFile(char *file, u64 size) { - u64 fsize = GetFileSize_u64(file); - if(size > 0){ - if(size != fsize){ - fprintf(stderr,"[!] %s has an invalid size (0x%"PRIx64")\n",file, fsize); - return NULL; - } + u64 fsize = GetFileSize64(file); + if(size > 0 && size != fsize){ + fprintf(stderr,"[!] %s has an invalid size (0x%"PRIx64")\n",file, fsize); + return NULL; } u8 *data = (u8*)calloc(1,fsize); @@ -328,7 +360,7 @@ void WriteBuffer(void *buffer, u64 size, u64 offset, FILE *output) fwrite(buffer,size,1,output); } -void ReadFile_64(void *outbuff, u64 size, u64 offset, FILE *file) +void ReadFile64(void *outbuff, u64 size, u64 offset, FILE *file) { fseek_64(file,offset); fread(outbuff,size,1,file); @@ -368,32 +400,32 @@ u32 u8_to_u32(u8 *value, u8 endianness) u64 u8_to_u64(u8 *value, u8 endianness) { - u64 u64_return = 0; + u64 ret = 0; switch(endianness){ case(BE): - u64_return |= (u64)value[7]<<0; - u64_return |= (u64)value[6]<<8; - u64_return |= (u64)value[5]<<16; - u64_return |= (u64)value[4]<<24; - u64_return |= (u64)value[3]<<32; - u64_return |= (u64)value[2]<<40; - u64_return |= (u64)value[1]<<48; - u64_return |= (u64)value[0]<<56; + ret |= (u64)value[7]<<0; + ret |= (u64)value[6]<<8; + ret |= (u64)value[5]<<16; + ret |= (u64)value[4]<<24; + ret |= (u64)value[3]<<32; + ret |= (u64)value[2]<<40; + ret |= (u64)value[1]<<48; + ret |= (u64)value[0]<<56; break; //return (value[7]<<0) | (value[6]<<8) | (value[5]<<16) | (value[4]<<24) | (value[3]<<32) | (value[2]<<40) | (value[1]<<48) | (value[0]<<56); case(LE): - u64_return |= (u64)value[0]<<0; - u64_return |= (u64)value[1]<<8; - u64_return |= (u64)value[2]<<16; - u64_return |= (u64)value[3]<<24; - u64_return |= (u64)value[4]<<32; - u64_return |= (u64)value[5]<<40; - u64_return |= (u64)value[6]<<48; - u64_return |= (u64)value[7]<<56; + ret |= (u64)value[0]<<0; + ret |= (u64)value[1]<<8; + ret |= (u64)value[2]<<16; + ret |= (u64)value[3]<<24; + ret |= (u64)value[4]<<32; + ret |= (u64)value[5]<<40; + ret |= (u64)value[6]<<48; + ret |= (u64)value[7]<<56; break; //return (value[0]<<0) | (value[1]<<8) | (value[2]<<16) | (value[3]<<24) | (value[4]<<32) | (value[5]<<40) | (value[6]<<48) | (value[7]<<56); } - return u64_return; + return ret; } int u16_to_u8(u8 *out_value, u16 in_value, u8 endianness) diff --git a/makerom/utils.h b/makerom/utils.h index 5d3f10a1..8c7d5a44 100644 --- a/makerom/utils.h +++ b/makerom/utils.h @@ -7,7 +7,6 @@ typedef struct } buffer_struct; // Memory -void char_to_u8_array(unsigned char destination[], char source[], int size, int endianness, int base); void endian_memcpy(u8 *destination, u8 *source, u32 size, int endianness); int CopyData(u8 **dest, u8 *source, u64 size); void rndset(void *ptr, u64 num); @@ -15,8 +14,8 @@ void clrmem(void *ptr, u64 num); // MISC u64 align(u64 value, u64 alignment); -u64 min_u64(u64 a, u64 b); -u64 max_u64(u64 a, u64 b); +u64 min64(u64 a, u64 b); +u64 max64(u64 a, u64 b); // Strings void memdump(FILE* fout, const char* prefix, const u8* data, u32 size); @@ -28,6 +27,12 @@ int str_u32_to_u16(u16 **dst, u32 *dst_len, u32 *src, u32 src_len); int str_utf8_to_u16(u16 **dst, u32 *dst_len, u8 *src, u32 src_len); #endif +// Base64 +bool IsValidB64Char(char chr); +u32 b64_strlen(char *str); +void b64_strcpy(char *dst, char *src); +int b64_decode(u8 *dst, char *src, u32 dst_size); + // Pseudo-Random Number Generator void initRand(void); u8 u8GetRand(void); @@ -37,20 +42,20 @@ u64 u64GetRand(void); //Char IO bool AssertFile(char *filename); -u64 GetFileSize_u64(char *filename); +u64 GetFileSize64(char *filename); int makedir(const char* dir); char *getcwdir(char *buffer,int maxlen); -int TruncateFile_u64(char *filename, u64 filelen); +int TruncateFile64(char *filename, u64 filelen); //Wide Char IO #ifdef _WIN32 -u64 wGetFileSize_u64(u16 *filename); +u64 wGetFileSize64(u16 *filename); #endif //IO Misc u8* ImportFile(char *file, u64 size); void WriteBuffer(void *buffer, u64 size, u64 offset, FILE *output); -void ReadFile_64(void *outbuff, u64 size, u64 offset, FILE *file); +void ReadFile64(void *outbuff, u64 size, u64 offset, FILE *file); int fseek_64(FILE *fp, u64 file_pos); //Data Size conversion diff --git a/makerom/yaml_parser.c b/makerom/yaml_parser.c index 3107cd24..9ebc645a 100644 --- a/makerom/yaml_parser.c +++ b/makerom/yaml_parser.c @@ -11,17 +11,14 @@ void CheckEvent(ctr_yaml_context *ctx); void BadYamlFormatting(void); // Code -int GetYamlSettings(user_settings *set) +int GetRsfSettings(user_settings *set) { - memset(&set->common.rsfSet,0,sizeof(rsf_settings)); int ret = 0; if(set->common.rsfPath) { - FILE *rsf = fopen(set->common.rsfPath,"rb"); - if(!rsf) { + if(!AssertFile(set->common.rsfPath)) { fprintf(stderr,"[RSF ERROR] Failed to open %s\n",set->common.rsfPath); return FAILED_TO_OPEN_FILE; } - fclose(rsf); ret = ParseSpecFile(&set->common.rsfSet,set->common.rsfPath, &set->dname); } return ret; diff --git a/makerom/yaml_parser.h b/makerom/yaml_parser.h index 9d9db540..d9cd5d34 100644 --- a/makerom/yaml_parser.h +++ b/makerom/yaml_parser.h @@ -31,7 +31,7 @@ typedef struct } ctr_yaml_context; // Public Prototypes -int GetYamlSettings(user_settings *set); +int GetRsfSettings(user_settings *set); // For scalar events char *GetYamlString(ctr_yaml_context *ctx); From 35b2865b084e300a2230d548fd5dcc294a894fdd Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 26 Aug 2014 00:54:59 +1000 Subject: [PATCH 024/317] commented debug code --- makerom/keyset.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/makerom/keyset.c b/makerom/keyset.c index a39ce548..66ebeff6 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -118,9 +118,10 @@ int LoadKeysFromResources(keys_struct *keys) SetNormalKey(keys,(u8*)dev_fixed_ncch_key[0]); SetSystemFixedKey(keys,(u8*)dev_fixed_ncch_key[1]); + /* for(int i = 0; i < 2; i++) SetNcchKeyX(keys,(u8*)dev_unfixed_ncch_keyX[i],i); - + */ /* RSA Keys */ // CIA From e04414b853aa56e1596d91a81be4667767d8b491 Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 26 Aug 2014 01:08:57 +1000 Subject: [PATCH 025/317] misc --- makerom/ncch.h | 1 - 1 file changed, 1 deletion(-) diff --git a/makerom/ncch.h b/makerom/ncch.h index e87ae48e..c54ced2e 100644 --- a/makerom/ncch.h +++ b/makerom/ncch.h @@ -126,7 +126,6 @@ bool IsNcch(FILE *fp, u8 *buf); bool IsCfa(ncch_hdr* hdr); bool IsUpdateCfa(ncch_hdr* hdr); u32 GetNcchBlockSize(ncch_hdr* hdr); -u8 GetBlockSizeFlag(u32 size); u64 GetNcchSize(ncch_hdr* hdr); bool IsNcchEncrypted(ncch_hdr *hdr); bool SetNcchKeys(keys_struct *keys, ncch_hdr *hdr); From 291b64b5985d0a46ec25f6ca21232585bd53d4ac Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 26 Aug 2014 13:32:55 +1000 Subject: [PATCH 026/317] ncch key stuff --- makerom/ncch.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/makerom/ncch.c b/makerom/ncch.c index e81ede85..98778727 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -629,6 +629,7 @@ int SetCommonHeaderBasicData(ncch_settings *set, ncch_hdr *hdr) if(!SetNcchKeys(set->keys,hdr) && set->options.Encrypt){ hdr->flags[ncchflag_OTHER_FLAG] = (otherflag_NoCrypto|otherflag_FixedCryptoKey); + hdr->flags[ncchflag_CONTENT_KEYX] = 0; set->options.Encrypt = false; fprintf(stderr,"[NCCH WARNING] NCCH AES Key could not be loaded, NCCH will not be encrypted\n"); } @@ -992,17 +993,20 @@ bool SetNcchKeys(keys_struct *keys, ncch_hdr *hdr) return true; if((hdr->flags[ncchflag_OTHER_FLAG] & otherflag_FixedCryptoKey) == otherflag_FixedCryptoKey){ - if((hdr->programId[4] & 0x10) == 0x10 && keys->aes.systemFixedKey){ + if((hdr->programId[4] & 0x10) == 0x10){ + if(!keys->aes.systemFixedKey) + return false; memcpy(keys->aes.ncchKey0,keys->aes.systemFixedKey,AES_128_KEY_SIZE); memcpy(keys->aes.ncchKey1,keys->aes.systemFixedKey,AES_128_KEY_SIZE); return true; } - else if(keys->aes.normalKey){ + else{ + if(!keys->aes.normalKey) + return false; memcpy(keys->aes.ncchKey0,keys->aes.normalKey,AES_128_KEY_SIZE); memcpy(keys->aes.ncchKey1,keys->aes.normalKey,AES_128_KEY_SIZE); return true; } - return false; } if(keys->aes.ncchKeyX[0]) @@ -1024,7 +1028,7 @@ int GetNcchInfo(ncch_info *info, ncch_hdr *hdr) memcpy(info->programId,hdr->programId,8); - u32 media_unit = GetNcchBlockSize(hdr); + u32 block_size = GetNcchBlockSize(hdr); info->formatVersion = u8_to_u16(hdr->formatVersion,LE); if(!IsCfa(hdr)){ @@ -1032,18 +1036,18 @@ int GetNcchInfo(ncch_info *info, ncch_hdr *hdr) info->exhdrSize = u8_to_u32(hdr->exhdrSize,LE); info->acexOffset = (info->exhdrOffset + info->exhdrSize); info->acexSize = sizeof(access_descriptor); - info->plainRegionOffset = (u64)(u8_to_u32(hdr->plainRegionOffset,LE)*media_unit); - info->plainRegionSize = (u64)(u8_to_u32(hdr->plainRegionSize,LE)*media_unit); + info->plainRegionOffset = (u64)(u8_to_u32(hdr->plainRegionOffset,LE)*block_size); + info->plainRegionSize = (u64)(u8_to_u32(hdr->plainRegionSize,LE)*block_size); } - info->logoOffset = (u64)(u8_to_u32(hdr->logoOffset,LE)*media_unit); - info->logoSize = (u64)(u8_to_u32(hdr->logoSize,LE)*media_unit); - info->exefsOffset = (u64)(u8_to_u32(hdr->exefsOffset,LE)*media_unit); - info->exefsSize = (u64)(u8_to_u32(hdr->exefsSize,LE)*media_unit); - info->exefsHashDataSize = (u64)(u8_to_u32(hdr->exefsHashSize,LE)*media_unit); - info->romfsOffset = (u64) (u8_to_u32(hdr->romfsOffset,LE)*media_unit); - info->romfsSize = (u64) (u8_to_u32(hdr->romfsSize,LE)*media_unit); - info->romfsHashDataSize = (u64)(u8_to_u32(hdr->romfsHashSize,LE)*media_unit); + info->logoOffset = (u64)(u8_to_u32(hdr->logoOffset,LE)*block_size); + info->logoSize = (u64)(u8_to_u32(hdr->logoSize,LE)*block_size); + info->exefsOffset = (u64)(u8_to_u32(hdr->exefsOffset,LE)*block_size); + info->exefsSize = (u64)(u8_to_u32(hdr->exefsSize,LE)*block_size); + info->exefsHashDataSize = (u64)(u8_to_u32(hdr->exefsHashSize,LE)*block_size); + info->romfsOffset = (u64) (u8_to_u32(hdr->romfsOffset,LE)*block_size); + info->romfsSize = (u64) (u8_to_u32(hdr->romfsSize,LE)*block_size); + info->romfsHashDataSize = (u64)(u8_to_u32(hdr->romfsHashSize,LE)*block_size); return 0; } @@ -1079,8 +1083,7 @@ void GetNcchAesCounter(ncch_info *ctx, u8 counter[16], u8 type) if (ctx->formatVersion == 2 || ctx->formatVersion == 0) { - for(i=0; i<8; i++) - counter[i] = titleId[7-i]; + endian_memcpy(counter,titleId,8,LE); counter[8] = type; } else if (ctx->formatVersion == 1) From b239091cd2339956ad5132c62a3500078e8cba93 Mon Sep 17 00:00:00 2001 From: profi200 Date: Tue, 26 Aug 2014 06:02:11 +0200 Subject: [PATCH 027/317] Format string fixes for GCC on Windows --- ctrtool/cia.c | 3 ++- ctrtool/exheader.c | 15 ++++++++------- ctrtool/ivfc.c | 7 ++++--- ctrtool/ncch.c | 7 ++++--- ctrtool/ncsd.c | 3 ++- ctrtool/tmd.c | 3 ++- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/ctrtool/cia.c b/ctrtool/cia.c index dd205a89..f1e97038 100644 --- a/ctrtool/cia.c +++ b/ctrtool/cia.c @@ -4,6 +4,7 @@ #include "types.h" #include "utils.h" #include "cia.h" +#include void cia_init(cia_context* ctx) @@ -304,5 +305,5 @@ void cia_print(cia_context* ctx) 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)); + fprintf(stdout, "Content size: 0x%016"PRIx64"\n", getle64(header->contentsize)); } diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index 8c945480..33d54b38 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -5,6 +5,7 @@ #include "exheader.h" #include "utils.h" #include "ncch.h" +#include void exheader_init(exheader_context* ctx) { @@ -428,7 +429,7 @@ void exheader_print_arm11storageinfo(exheader_context* ctx) accessiblesaveID[i] = 0; } - fprintf(stdout, "Ext savedata id: 0x%08llX\n",extdataID); + fprintf(stdout, "Ext savedata id: 0x%08"PRIX64"\n",extdataID); for(i = 0; i < 2; i++) fprintf(stdout, "System savedata id %d: 0x%08x %s\n",i+1,systemsaveID[i],exheader_getvalidstring(ctx->validsystemsaveID[i])); for(i = 0; i < 3; i++) @@ -578,17 +579,17 @@ void exheader_print(exheader_context* ctx) 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, "Dependency: %016"PRIX64"\n", getle64(ctx->header.deplist.programid[i])); } if(savedatasize < sizeKB) - fprintf(stdout, "Savedata size: 0x%llX\n", savedatasize); + fprintf(stdout, "Savedata size: 0x%"PRIX64"\n", savedatasize); else if(savedatasize < sizeMB) - fprintf(stdout, "Savedata size: %lluK\n", savedatasize/sizeKB); + fprintf(stdout, "Savedata size: %"PRIu64"K\n", savedatasize/sizeKB); else - fprintf(stdout, "Savedata size: %lluM\n", savedatasize/sizeMB); - fprintf(stdout, "Jump id: %016llX\n", getle64(ctx->header.systeminfo.jumpid)); + fprintf(stdout, "Savedata size: %"PRIu64"M\n", savedatasize/sizeMB); + fprintf(stdout, "Jump id: %016"PRIx64"\n", getle64(ctx->header.systeminfo.jumpid)); - fprintf(stdout, "Program id: %016llX %s\n", getle64(ctx->header.arm11systemlocalcaps.programid), exheader_getvalidstring(ctx->validprogramid)); + fprintf(stdout, "Program id: %016"PRIx64" %s\n", getle64(ctx->header.arm11systemlocalcaps.programid), exheader_getvalidstring(ctx->validprogramid)); fprintf(stdout, "Core version: 0x%X\n", getle32(ctx->header.arm11systemlocalcaps.coreversion)); fprintf(stdout, "System mode: 0x%X\n", (ctx->header.arm11systemlocalcaps.flag>>4)&0xF); fprintf(stdout, "Ideal processor: %d %s\n", (ctx->header.arm11systemlocalcaps.flag>>0)&0x3, exheader_getvalidstring(ctx->valididealprocessor)); diff --git a/ctrtool/ivfc.c b/ctrtool/ivfc.c index 5ba15b8c..a4796ac3 100644 --- a/ctrtool/ivfc.c +++ b/ctrtool/ivfc.c @@ -5,6 +5,7 @@ #include "utils.h" #include "ivfc.h" #include "ctr.h" +#include void ivfc_init(ivfc_context* ctx) { @@ -168,9 +169,9 @@ void ivfc_print(ivfc_context* ctx) 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, " Data offset: 0x%016"PRIx64"\n", ctx->offset + level->dataoffset); + fprintf(stdout, " Data size: 0x%016"PRIx64"\n", level->datasize); + fprintf(stdout, " Hash offset: 0x%016"PRIx64"\n", ctx->offset + level->hashoffset); fprintf(stdout, " Hash block size: 0x%08x\n", level->hashblocksize); } } diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 1f0c4520..de2328d3 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -6,6 +6,7 @@ #include "utils.h" #include "ctr.h" #include "settings.h" +#include static int programid_is_system(u8 programid[8]) { @@ -561,10 +562,10 @@ void ncch_print(ncch_context* ctx) 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, "Partition id: %016"PRIx64"\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, "Program id: %016"PRIx64"\n", getle64(header->programid)); if(ctx->logohashcheck == Unchecked) memdump(stdout, "Logo hash: ", header->logohash, 0x20); else if(ctx->logohashcheck == Good) @@ -579,7 +580,7 @@ void ncch_print(ncch_context* ctx) 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, "Flags: %016"PRIx64"\n", getle64(header->flags)); fprintf(stdout, " > Mediaunit size: 0x%x\n", mediaunitsize); if (header->flags[7] & 4) fprintf(stdout, " > Crypto key: None\n"); diff --git a/ctrtool/ncsd.c b/ctrtool/ncsd.c index d1ece64c..cf2336c2 100644 --- a/ctrtool/ncsd.c +++ b/ctrtool/ncsd.c @@ -5,6 +5,7 @@ #include "ncsd.h" #include "utils.h" #include "ctr.h" +#include void ncsd_init(ncsd_context* ctx) @@ -122,7 +123,7 @@ void ncsd_print(ncsd_context* ctx) 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)); + fprintf(stdout, "Media id: %016"PRIX64"\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); diff --git a/ctrtool/tmd.c b/ctrtool/tmd.c index 572331b4..d86d4a5f 100644 --- a/ctrtool/tmd.c +++ b/ctrtool/tmd.c @@ -4,6 +4,7 @@ #include #include "tmd.h" #include "utils.h" +#include void tmd_init(tmd_context* ctx) @@ -158,7 +159,7 @@ void tmd_print(tmd_context* ctx) fprintf(stdout, "[shared]"); } fprintf(stdout, "\n"); - fprintf(stdout, "Content size: %016llx\n", getbe64(chunk->size)); + fprintf(stdout, "Content size: %016"PRIx64"\n", getbe64(chunk->size)); switch(ctx->content_hash_stat[getbe16(chunk->index)]) { case 1: memdump(stdout, "Content hash [OK]: ", chunk->hash, 32); break; From 1a925d98bc2c9c6b6ab066b2b96eb15754373075 Mon Sep 17 00:00:00 2001 From: profi200 Date: Tue, 26 Aug 2014 06:08:47 +0200 Subject: [PATCH 028/317] Nitpicking --- ctrtool/exheader.c | 2 +- ctrtool/ncsd.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index 33d54b38..593af092 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -589,7 +589,7 @@ void exheader_print(exheader_context* ctx) fprintf(stdout, "Savedata size: %"PRIu64"M\n", savedatasize/sizeMB); fprintf(stdout, "Jump id: %016"PRIx64"\n", getle64(ctx->header.systeminfo.jumpid)); - fprintf(stdout, "Program id: %016"PRIx64" %s\n", getle64(ctx->header.arm11systemlocalcaps.programid), exheader_getvalidstring(ctx->validprogramid)); + fprintf(stdout, "Program id: %016"PRIX64" %s\n", getle64(ctx->header.arm11systemlocalcaps.programid), exheader_getvalidstring(ctx->validprogramid)); fprintf(stdout, "Core version: 0x%X\n", getle32(ctx->header.arm11systemlocalcaps.coreversion)); fprintf(stdout, "System mode: 0x%X\n", (ctx->header.arm11systemlocalcaps.flag>>4)&0xF); fprintf(stdout, "Ideal processor: %d %s\n", (ctx->header.arm11systemlocalcaps.flag>>0)&0x3, exheader_getvalidstring(ctx->valididealprocessor)); diff --git a/ctrtool/ncsd.c b/ctrtool/ncsd.c index cf2336c2..07c211f6 100644 --- a/ctrtool/ncsd.c +++ b/ctrtool/ncsd.c @@ -123,7 +123,7 @@ void ncsd_print(ncsd_context* ctx) else memdump(stdout, "Signature (FAIL): ", header->signature, 0x100); fprintf(stdout, "Media size: 0x%08x\n", getle32(header->mediasize)); - fprintf(stdout, "Media id: %016"PRIX64"\n", getle64(header->mediaid)); + fprintf(stdout, "Media id: %016"PRIx64"\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); From a90378b6f4db83569a8953765146310fe398e3bf Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 26 Aug 2014 15:44:15 +1000 Subject: [PATCH 029/317] misc --- makerom/ncsd.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/makerom/ncsd.c b/makerom/ncsd.c index 2827f5b4..4f01c649 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -681,17 +681,14 @@ bool IsCci(u8 *ncsd) u64 GetPartitionOffset(u8 *ncsd, u8 index) { cci_hdr *hdr = (cci_hdr*)ncsd; - u32 media_size = 1 << (hdr->flags[cciflag_MEDIA_BLOCK_SIZE] + 9); - u32 offset = u8_to_u64(hdr->offset_sizeTable[index].offset,LE); - return (u64)offset*(u64)media_size; + u32 offset = ; + return (u64)u8_to_u64(hdr->offset_sizeTable[index].offset,LE) * (u64)GetCtrBlockSize(hdr->flags[cciflag_MEDIA_BLOCK_SIZE]); } u64 GetPartitionSize(u8 *ncsd, u8 index) { cci_hdr *hdr = (cci_hdr*)ncsd; - u32 media_size = 1 << (hdr->flags[cciflag_MEDIA_BLOCK_SIZE] + 9); - u32 size = u8_to_u64(hdr->offset_sizeTable[index].size,LE); - return (u64)size*(u64)media_size; + return (u64)u8_to_u64(hdr->offset_sizeTable[index].size,LE) * (u64)GetCtrBlockSize(hdr->flags[cciflag_MEDIA_BLOCK_SIZE]); } u8* GetPartition(u8 *ncsd, u8 index) From e5b6a1bd722814204d831cd13c7279b122eaaf3a Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 26 Aug 2014 16:03:06 +1000 Subject: [PATCH 030/317] misc --- makerom/ncsd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/makerom/ncsd.c b/makerom/ncsd.c index 4f01c649..c554eb0e 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -681,7 +681,6 @@ bool IsCci(u8 *ncsd) u64 GetPartitionOffset(u8 *ncsd, u8 index) { cci_hdr *hdr = (cci_hdr*)ncsd; - u32 offset = ; return (u64)u8_to_u64(hdr->offset_sizeTable[index].offset,LE) * (u64)GetCtrBlockSize(hdr->flags[cciflag_MEDIA_BLOCK_SIZE]); } From d0f4ea17f86f02a965415bfbbf25b6192ed5bec3 Mon Sep 17 00:00:00 2001 From: applestash Date: Wed, 27 Aug 2014 00:25:01 +1000 Subject: [PATCH 031/317] standardized case for PRIx64 macro --- ctrtool/exheader.c | 8 ++++---- makerom/elf.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index 593af092..afb19279 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -429,7 +429,7 @@ void exheader_print_arm11storageinfo(exheader_context* ctx) accessiblesaveID[i] = 0; } - fprintf(stdout, "Ext savedata id: 0x%08"PRIX64"\n",extdataID); + fprintf(stdout, "Ext savedata id: 0x%08"PRIx64"\n",extdataID); for(i = 0; i < 2; i++) fprintf(stdout, "System savedata id %d: 0x%08x %s\n",i+1,systemsaveID[i],exheader_getvalidstring(ctx->validsystemsaveID[i])); for(i = 0; i < 3; i++) @@ -579,17 +579,17 @@ void exheader_print(exheader_context* ctx) for(i=0; i<0x30; i++) { if (getle64(ctx->header.deplist.programid[i]) != 0x0000000000000000UL) - fprintf(stdout, "Dependency: %016"PRIX64"\n", getle64(ctx->header.deplist.programid[i])); + fprintf(stdout, "Dependency: %016"PRIx64"\n", getle64(ctx->header.deplist.programid[i])); } if(savedatasize < sizeKB) - fprintf(stdout, "Savedata size: 0x%"PRIX64"\n", savedatasize); + fprintf(stdout, "Savedata size: 0x%"PRIx64"\n", savedatasize); else if(savedatasize < sizeMB) fprintf(stdout, "Savedata size: %"PRIu64"K\n", savedatasize/sizeKB); else fprintf(stdout, "Savedata size: %"PRIu64"M\n", savedatasize/sizeMB); fprintf(stdout, "Jump id: %016"PRIx64"\n", getle64(ctx->header.systeminfo.jumpid)); - fprintf(stdout, "Program id: %016"PRIX64" %s\n", getle64(ctx->header.arm11systemlocalcaps.programid), exheader_getvalidstring(ctx->validprogramid)); + fprintf(stdout, "Program id: %016"PRIx64" %s\n", getle64(ctx->header.arm11systemlocalcaps.programid), exheader_getvalidstring(ctx->validprogramid)); fprintf(stdout, "Core version: 0x%X\n", getle32(ctx->header.arm11systemlocalcaps.coreversion)); fprintf(stdout, "System mode: 0x%X\n", (ctx->header.arm11systemlocalcaps.flag>>4)&0xF); fprintf(stdout, "Ideal processor: %d %s\n", (ctx->header.arm11systemlocalcaps.flag>>0)&0x3, exheader_getvalidstring(ctx->valididealprocessor)); diff --git a/makerom/elf.c b/makerom/elf.c index 3f5a91c0..532d6669 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -483,18 +483,18 @@ void PrintElfContext(elf_context *elf, u8 *elfFile) printf(" Class: %s\n",elf->Is64bit ? "64-bit" : "32-bit"); printf(" Data: %s\n",elf->IsLittleEndian ? "Little Endian" : "Big Endian"); printf("[ELF] Program Table Data\n"); - printf(" Offset: 0x%"PRIX64"\n",elf->programTableOffset); + printf(" Offset: 0x%"PRIx64"\n",elf->programTableOffset); printf(" Size: 0x%x\n",elf->programTableEntrySize); printf(" Count: 0x%x\n",elf->programTableEntryCount); printf("[ELF] Section Table Data\n"); - printf(" Offset: 0x%"PRIX64"\n",elf->sectionTableOffset); + printf(" Offset: 0x%"PRIx64"\n",elf->sectionTableOffset); printf(" Size: 0x%x\n",elf->sectionTableEntrySize); printf(" Count: 0x%x\n",elf->sectionTableEntryCount); printf(" Label index: 0x%x\n",elf->sectionHeaderNameEntryIndex); for(int i = 0; i < elf->activeSegments; i++){ printf(" Segment [%d][%s]\n",i,elf->segments[i].name); - printf(" > Size : 0x%"PRIX64"\n",elf->segments[i].header->sizeInFile); - printf(" > Address : 0x%"PRIX64"\n",elf->segments[i].vAddr); + printf(" > Size : 0x%"PRIx64"\n",elf->segments[i].header->sizeInFile); + printf(" > Address : 0x%"PRIx64"\n",elf->segments[i].vAddr); printf(" > Sections : %d\n",elf->segments[i].sectionNum); for(int j = 0; j < elf->segments[i].sectionNum; j++) printf(" > Section [%d][%s]\n",j,elf->segments[i].sections[j].name); From 2448dee471750d17e786b2c5e1348e7b938ac522 Mon Sep 17 00:00:00 2001 From: applestash Date: Thu, 28 Aug 2014 22:35:20 +1000 Subject: [PATCH 032/317] made bss optional --- makerom/elf.c | 10 +++++----- makerom/elf.h | 9 ++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/makerom/elf.c b/makerom/elf.c index 532d6669..d30db0a5 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -113,7 +113,6 @@ int BuildExeFsCode(ncch_settings *set) if(result == NOT_ELF_FILE) fprintf(stderr,"[ELF ERROR] Not ELF File\n"); else if(result == NOT_ARM_ELF) fprintf(stderr,"[ELF ERROR] Not ARM ELF\n"); else if(result == NON_EXECUTABLE_ELF) fprintf(stderr,"[ELF ERROR] Not Executeable ELF\n"); - else if(result == NOT_FIND_BSS_SIZE) fprintf(stderr,"[ELF ERROR] BSS Size Could not be found\n"); else if(result == NOT_FIND_CODE_SECTIONS) fprintf(stderr,"[ELF ERROR] Failed to retrieve code sections from ELF\n"); else fprintf(stderr,"[ELF ERROR] Failed to process ELF file (%d)\n",result); } @@ -175,13 +174,14 @@ u32 SizeToPage(u32 memorySize, elf_context *elf) int GetBSSFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set) { + set->codeDetails.bssSize = 0; + for(int i = 0; i < elf->sectionTableEntryCount; i++){ - if(IsBss(&elf->sections[i])) { + if(IsBss(&elf->sections[i])) set->codeDetails.bssSize = elf->sections[i].size; - return 0; - } } - return NOT_FIND_BSS_SIZE; + + return 0; } int ImportPlainRegionFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set) // Doesn't work same as N makerom diff --git a/makerom/elf.h b/makerom/elf.h index d3a24640..4ce9121b 100644 --- a/makerom/elf.h +++ b/makerom/elf.h @@ -6,11 +6,10 @@ typedef enum NOT_ARM_ELF = -11, NON_EXECUTABLE_ELF = -12, ELF_SECTION_NOT_FOUND = -13, - NOT_FIND_BSS_SIZE = -14, - NOT_FIND_CODE_SECTIONS = -15, - ELF_SEGMENT_SECTION_SIZE_MISMATCH = -16, - ELF_SEGMENTS_NOT_CONTINUOUS = -17, - ELF_SEGMENTS_NOT_FOUND = -18, + NOT_FIND_CODE_SECTIONS = -14, + ELF_SEGMENT_SECTION_SIZE_MISMATCH = -15, + ELF_SEGMENTS_NOT_CONTINUOUS = -16, + ELF_SEGMENTS_NOT_FOUND = -17, } elf_errors; typedef struct From 64665019c38da0ff294c167135a814a36fc2a997 Mon Sep 17 00:00:00 2001 From: applestash Date: Thu, 28 Aug 2014 22:38:18 +1000 Subject: [PATCH 033/317] removed debug printf statments this isn't a prototype anymore, idk why they were there --- makerom/makerom.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/makerom/makerom.c b/makerom/makerom.c index 3d891ce9..321f4aec 100644 --- a/makerom/makerom.c +++ b/makerom/makerom.c @@ -65,9 +65,6 @@ int main(int argc, char *argv[]) } } else{// Build Content 0 -#ifdef DEBUG - printf("[DEBUG] Build NCCH0\n"); -#endif result = build_NCCH(set); if(result < 0) { //fprintf(stderr,"[ERROR] %s generation failed\n",set->build_ncch_type == CXI? "CXI" : "CFA"); @@ -77,9 +74,6 @@ int main(int argc, char *argv[]) } // Make CCI if(set->common.outFormat == CCI){ -#ifdef DEBUG - printf("[DEBUG] Building CCI\n"); -#endif result = build_CCI(set); if(result < 0) { fprintf(stderr,"[RESULT] Failed to build CCI\n"); @@ -88,9 +82,6 @@ int main(int argc, char *argv[]) } // Make CIA else if(set->common.outFormat == CIA){ -#ifdef DEBUG - printf("[DEBUG] Building CIA\n"); -#endif result = build_CIA(set); if(result < 0) { fprintf(stderr,"[RESULT] Failed to build CIA\n"); @@ -99,9 +90,6 @@ int main(int argc, char *argv[]) } // No Container Raw CXI/CFA else if(set->common.outFormat == CXI || set->common.outFormat == CFA){ -#ifdef DEBUG - printf("[DEBUG] Outputting NCCH, because No Container\n"); -#endif FILE *ncch_out = fopen(set->common.outFileName,"wb"); if(!ncch_out) { fprintf(stderr,"[MAKEROM ERROR] Failed to create '%s'\n",set->common.outFileName); @@ -114,12 +102,6 @@ int main(int argc, char *argv[]) } finish: -#ifdef DEBUG - printf("[DEBUG] Free Context\n"); -#endif free_UserSettings(set); -#ifdef DEBUG - printf("[DEBUG] Finished returning (result=%d)\n",result); -#endif return result; } \ No newline at end of file From 61da4a110758837ccd5b48d569800162f35d6eb1 Mon Sep 17 00:00:00 2001 From: applestash Date: Fri, 29 Aug 2014 21:50:18 +1000 Subject: [PATCH 034/317] made text and data ELF regions compulsory --- makerom/elf.c | 9 +++++++-- makerom/elf.h | 9 +++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/makerom/elf.c b/makerom/elf.c index d30db0a5..03adee20 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -113,7 +113,8 @@ int BuildExeFsCode(ncch_settings *set) if(result == NOT_ELF_FILE) fprintf(stderr,"[ELF ERROR] Not ELF File\n"); else if(result == NOT_ARM_ELF) fprintf(stderr,"[ELF ERROR] Not ARM ELF\n"); else if(result == NON_EXECUTABLE_ELF) fprintf(stderr,"[ELF ERROR] Not Executeable ELF\n"); - else if(result == NOT_FIND_CODE_SECTIONS) fprintf(stderr,"[ELF ERROR] Failed to retrieve code sections from ELF\n"); + else if(result == NOT_FIND_TEXT_SEGMENT) fprintf(stderr,"[ELF ERROR] Failed to retrieve text sections from ELF\n"); + else if(result == NOT_FIND_DATA_SEGMENT) fprintf(stderr,"[ELF ERROR] Failed to retrieve data sections from ELF\n"); else fprintf(stderr,"[ELF ERROR] Failed to process ELF file (%d)\n",result); } for(int i = 0; i < elf->activeSegments; i++) @@ -239,9 +240,13 @@ int CreateExeFsCode(elf_context *elf, u8 *elfFile, ncch_settings *set) result = CreateCodeSegmentFromElf(&rwdata,elf,elfFile,set->rsfSet->ExeFs.ReadWrite,set->rsfSet->ExeFs.ReadWriteNum); if(result) return result; + /* Checking the existence of essential ELF Segments */ + if(!text.size) return NOT_FIND_TEXT_SEGMENT; + if(!rwdata.size) return NOT_FIND_DATA_SEGMENT; + /* Allocating Buffer for ExeFs Code */ u32 size = (text.maxPageNum + rodata.maxPageNum + rwdata.maxPageNum)*elf->pageSize; - u8 *code = malloc(size); + u8 *code = calloc(1,size); /* Writing Code into Buffer */ u8 *textPos = (code + 0); diff --git a/makerom/elf.h b/makerom/elf.h index 4ce9121b..585af39f 100644 --- a/makerom/elf.h +++ b/makerom/elf.h @@ -6,10 +6,11 @@ typedef enum NOT_ARM_ELF = -11, NON_EXECUTABLE_ELF = -12, ELF_SECTION_NOT_FOUND = -13, - NOT_FIND_CODE_SECTIONS = -14, - ELF_SEGMENT_SECTION_SIZE_MISMATCH = -15, - ELF_SEGMENTS_NOT_CONTINUOUS = -16, - ELF_SEGMENTS_NOT_FOUND = -17, + NOT_FIND_TEXT_SEGMENT = -14, + NOT_FIND_DATA_SEGMENT = -15 + ELF_SEGMENT_SECTION_SIZE_MISMATCH = -16, + ELF_SEGMENTS_NOT_CONTINUOUS = -17, + ELF_SEGMENTS_NOT_FOUND = -18, } elf_errors; typedef struct From 5e9c14621b737263c17d1c63e68a01f344d59df5 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 30 Aug 2014 00:07:47 +1000 Subject: [PATCH 035/317] fix --- makerom/elf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/elf.h b/makerom/elf.h index 585af39f..269f2093 100644 --- a/makerom/elf.h +++ b/makerom/elf.h @@ -7,7 +7,7 @@ typedef enum NON_EXECUTABLE_ELF = -12, ELF_SECTION_NOT_FOUND = -13, NOT_FIND_TEXT_SEGMENT = -14, - NOT_FIND_DATA_SEGMENT = -15 + NOT_FIND_DATA_SEGMENT = -15, ELF_SEGMENT_SECTION_SIZE_MISMATCH = -16, ELF_SEGMENTS_NOT_CONTINUOUS = -17, ELF_SEGMENTS_NOT_FOUND = -18, From 5ae26c3436425424988a4f30cbafbbcba90389ca Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 30 Aug 2014 00:23:11 +1000 Subject: [PATCH 036/317] moved error detection to proper place --- makerom/ncch.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/makerom/ncch.c b/makerom/ncch.c index 98778727..10c4a92f 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -858,17 +858,16 @@ int ModifyNcchIds(u8 *ncch, u8 *titleId, u8 *programId, keys_struct *keys) ncch_hdr *hdr = (ncch_hdr*)ncch; - if(/*keys->rsa.requiresPresignedDesc && */!IsCfa(hdr)){ - fprintf(stderr,"[NCCH ERROR] CXI's ID cannot be modified without the ability to resign the AccessDesc\n"); // Not yet yet, requires AccessDesc Privk, may implement anyway later - return -1; - } - bool titleIdMatches = titleId == NULL? true : memcmp(titleId,hdr->titleId,8) == 0; bool programIdMatches = programId == NULL? true : memcmp(programId,hdr->programId,8) == 0; if(titleIdMatches && programIdMatches) return 0;// if no modification is required don't do anything + if(/*keys->rsa.requiresPresignedDesc && */!IsCfa(hdr)){ + fprintf(stderr,"[NCCH ERROR] CXI's ID cannot be modified without the ability to resign the AccessDesc\n"); // Not yet yet, requires AccessDesc Privk, may implement anyway later + return -1; + } ncch_info ncchInfo; u8 *romfs = NULL; From 1320a06bd0c548b803ced9c67ddd426532462cf3 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 30 Aug 2014 00:56:00 +1000 Subject: [PATCH 037/317] added roundup() to utils --- makerom/utils.c | 7 ++++++- makerom/utils.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/makerom/utils.c b/makerom/utils.c index bda3076b..450427fd 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -41,10 +41,15 @@ void clrmem(void *ptr, u64 num) } // Misc +u64 roundup(u64 value, u64 alignment) +{ + return value + alignment - value % alignment; +} + u64 align(u64 value, u64 alignment) { if(value % alignment != 0) - return value + alignment - value % alignment; + return roundup(value,alignment); else return value; } diff --git a/makerom/utils.h b/makerom/utils.h index 8c7d5a44..da32454f 100644 --- a/makerom/utils.h +++ b/makerom/utils.h @@ -13,6 +13,7 @@ void rndset(void *ptr, u64 num); void clrmem(void *ptr, u64 num); // MISC +u64 roundup(u64 value, u64 alignment); u64 align(u64 value, u64 alignment); u64 min64(u64 a, u64 b); u64 max64(u64 a, u64 b); From 1858c055c284547f414fafc2736d5e41aa51bc21 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 30 Aug 2014 00:56:19 +1000 Subject: [PATCH 038/317] implemented proper tik content idx generation --- makerom/tik.c | 110 ++++++++++++++++++++++++++++++++++++++++---------- makerom/tik.h | 21 +++++++++- 2 files changed, 108 insertions(+), 23 deletions(-) diff --git a/makerom/tik.c b/makerom/tik.c index 352d5565..5f2e233c 100644 --- a/makerom/tik.c +++ b/makerom/tik.c @@ -3,32 +3,42 @@ #include "tik_build.h" // Private Prototypes -int SetupTicketBuffer(buffer_struct *tik); -int SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset); -int SignTicketHeader(tik_hdr *hdr, tik_signature *sig, keys_struct *keys); +int SetupTicketBuffer(cia_settings *set); +void SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset); +int SignTicketHeader(buffer_struct *tik, keys_struct *keys); void SetLimits(tik_hdr *hdr, cia_settings *ciaset); -void SetContentIndexData(tik_hdr *hdr, cia_settings *ciaset); +u32 GetContentIndexSegNum(cia_settings *set); +void SetContentIndexHeader(tik_content_index_hdr *hdr, cia_settings *set); +void SetContentIndexData(tik_content_index_struct *data, cia_settings *set); -int BuildTicket(cia_settings *ciaset) +int BuildTicket(cia_settings *set) { int result = 0; - result = SetupTicketBuffer(&ciaset->ciaSections.tik); + result = SetupTicketBuffer(set); if(result) return result; // Setting Ticket Struct Ptrs - tik_signature *sig = (tik_signature*)ciaset->ciaSections.tik.buffer; - tik_hdr *hdr = (tik_hdr*)(ciaset->ciaSections.tik.buffer+sizeof(tik_signature)); - - result = SetupTicketHeader(hdr,ciaset); - if(result) return result; - result = SignTicketHeader(hdr,sig,ciaset->keys); + buffer_struct *tik = &set->ciaSections.tik; + + tik_hdr *hdr = (tik_hdr*) (tik->buffer + sizeof(tik_signature)); + tik_content_index_hdr *idxHdr = (tik_content_index_hdr*) (tik->buffer + sizeof(tik_signature) + sizeof(tik_hdr)); + tik_content_index_struct *idxData = (tik_content_index_struct*) (tik->buffer + sizeof(tik_signature) + sizeof(tik_hdr) + sizeof(tik_content_index_hdr)); + + + SetupTicketHeader(hdr,set); + SetContentIndexHeader(idxHdr,set); + SetContentIndexData(idxData,set); + + result = SignTicketHeader(tik,set->keys); return 0; } -int SetupTicketBuffer(buffer_struct *tik) +int SetupTicketBuffer(cia_settings *set) { - tik->size = sizeof(tik_signature) + sizeof(tik_hdr); + buffer_struct *tik = &set->ciaSections.tik; + + tik->size = sizeof(tik_signature) + sizeof(tik_hdr) + sizeof(tik_content_index_hdr) + sizeof(tik_content_index_struct)*GetContentIndexSegNum(set); tik->buffer = calloc(1,tik->size); if(!tik->buffer) { fprintf(stderr,"[TIK ERROR] Not enough memory\n"); @@ -37,7 +47,7 @@ int SetupTicketBuffer(buffer_struct *tik) return 0; } -int SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset) +void SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset) { clrmem(hdr,sizeof(tik_hdr)); @@ -58,15 +68,19 @@ int SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset) memcpy(hdr->eshopAccId,ciaset->tik.eshopAccId,4); hdr->audit = ciaset->tik.audit; SetLimits(hdr,ciaset); - SetContentIndexData(hdr,ciaset); - return 0; } -int SignTicketHeader(tik_hdr *hdr, tik_signature *sig, keys_struct *keys) +int SignTicketHeader(buffer_struct *tik, keys_struct *keys) { + tik_signature *sig = (tik_signature*)tik->buffer; + u8 *data = tik->buffer + sizeof(tik_signature); + u32 len = tik->size - sizeof(tik_signature); + + clrmem(sig,sizeof(tik_signature)); u32_to_u8(sig->sigType,RSA_2048_SHA256,BE); - return ctr_sig((u8*)hdr,sizeof(tik_hdr),sig->data,keys->rsa.xsPub,keys->rsa.xsPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + + return ctr_sig(data,len,sig->data,keys->rsa.xsPub,keys->rsa.xsPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } int CryptTitleKey(u8 *EncTitleKey, u8 *DecTitleKey, u8 *TitleID, keys_struct *keys, u8 mode) @@ -94,10 +108,62 @@ void SetLimits(tik_hdr *hdr, cia_settings *ciaset) // TODO? memset(hdr->limits,0,0x40); } -void SetContentIndexData(tik_hdr *hdr, cia_settings *ciaset) // TODO? +u32 GetContentIndexSegNum(cia_settings *set) { - memset(hdr->contentIndex,0,0xAC); - memcpy(hdr->contentIndex,default_contentIndex,0x30); + u32 num, level, i; + + num = level = 0; + + for( i = 0; i < set->content.count; i++) + { + if(set->content.index[i] >= level) + { + level = roundup(set->content.index[i],0x400); + num++; + } + } + return num; +} + +void SetContentIndexHeader(tik_content_index_hdr *hdr, cia_settings *set) +{ + u32 hdrSize = sizeof(tik_content_index_hdr); + u32 segNum = GetContentIndexSegNum(set); + u32 segSize = sizeof(tik_content_index_struct); + u32 segTotalSize = segSize * segNum; + u32 totalSize = hdrSize + segTotalSize; + + u32_to_u8(hdr->unk0,0x00010014,BE); + u32_to_u8(hdr->totalSize,totalSize,BE); + u32_to_u8(hdr->unk1,0x00000014,BE); + u32_to_u8(hdr->unk2,0x00010014,BE); + u32_to_u8(hdr->unk3,0x00000000,BE); + u32_to_u8(hdr->hdrSize,hdrSize,BE); + u32_to_u8(hdr->segNum,segNum,BE); + u32_to_u8(hdr->segSize,segSize,BE); + u32_to_u8(hdr->segTotalSize,segTotalSize,BE); + u32_to_u8(hdr->unk4,0x00030000,BE); +} + +void SetContentIndexData(tik_content_index_struct *data, cia_settings *set) +{ + u32 level, i; + int j; + + j = -1; + level = 0; + + for( i = 0; i < set->content.count; i++) + { + if(set->content.index[i] >= level) + { + level = roundup(set->content.index[i],0x400); + j++; + u32_to_u8(data[j].level,(set->content.index[i]/0x400)*0x400,BE); + } + data[j].index[(set->content.index[i] & 0x3ff)/8] |= 1 << (set->content.index[i] & 0x7); + } + } tik_hdr *GetTikHdr(u8 *tik) diff --git a/makerom/tik.h b/makerom/tik.h index d8b7c054..8cc351d5 100644 --- a/makerom/tik.h +++ b/makerom/tik.h @@ -20,6 +20,26 @@ typedef enum right_AccessTitle = 5 } tik_item_rights; +typedef struct +{ + u8 unk0[4]; + u8 totalSize[4]; + u8 unk1[4]; + u8 unk2[4]; + u8 unk3[4]; + u8 hdrSize[4]; + u8 segNum[4]; + u8 segSize[4]; + u8 segTotalSize[4]; + u8 unk4[4]; +} tik_content_index_hdr; + +typedef struct +{ + u8 level[4]; + u8 index[0x80]; +} tik_content_index_struct; + typedef struct { u8 sigType[4]; @@ -50,7 +70,6 @@ typedef struct u8 audit; u8 padding5[0x42]; u8 limits[0x40]; - u8 contentIndex[0xAC]; } tik_hdr; From a6c4112ee198c9302be9d49f538b9cc7b6aabe71 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 30 Aug 2014 00:58:58 +1000 Subject: [PATCH 039/317] fixed ptr misalignment --- makerom/cia.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/cia.c b/makerom/cia.c index b9ab061c..4c65e0b7 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -443,7 +443,7 @@ int ImportNcchContent(cia_settings *ciaset) return MEM_ERROR; } - ncch_hdr *ncch0hdr = (ncch_hdr*)(ciaset->ciaSections.content.buffer+0x100); + ncch_hdr *ncch0hdr = (ncch_hdr*)ciaset->ciaSections.content.buffer; for(int i = 1; i < ciaset->content.count; i++){ // Import u8 *ncchpos = (u8*)(ciaset->ciaSections.content.buffer+ciaset->content.offset[i]); From 7f8c1f92a4ebf8c91ce1b8bb341fb1a0999496b1 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 30 Aug 2014 01:05:12 +1000 Subject: [PATCH 040/317] simplified cia hdr content idx calc --- makerom/cia.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/makerom/cia.c b/makerom/cia.c index 4c65e0b7..095fb28f 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -629,24 +629,9 @@ int BuildCiaHdr(cia_settings *ciaset) ciaset->ciaSections.contentOffset = align(ciaset->ciaSections.tmdOffset+ciaset->ciaSections.tmd.size,0x40); ciaset->ciaSections.metaOffset = align(ciaset->ciaSections.contentOffset+ciaset->content.totalSize,0x40); - for(int i = 0; i < ciaset->content.count; i++){ - // This works by treating the 0x2000 byte index array as an array of 2048 u32 values - - // Used for determining which u32 chunk to write the value to - u16 section = ciaset->content.index[i]/32; - - // Calculating the value added to the u32 - u32 value = 1 << (0x1F-ciaset->content.index[i]); - - // Retrieving current u32 block - u32 cur_content_index_section = u8_to_u32(hdr->contentIndex+(sizeof(u32)*section),BE); - - // Adding value to block - cur_content_index_section += value; + for(int i = 0; i < ciaset->content.count; i++) + hdr->contentIndex[ciaset->content.index[i]/8] |= 1 << (7 - (ciaset->content.index[i] & 7)); - // Returning block - u32_to_u8(hdr->contentIndex+(sizeof(u32)*section),cur_content_index_section,BE); - } return 0; } From 9828b8b06136890426f7319f8b911b706b0604e6 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 30 Aug 2014 01:41:42 +1000 Subject: [PATCH 041/317] relaxed -f argument -f cxi and -f cfa can be used, but has same effect as -f ncch --- makerom/user_settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 423278bf..97505ab0 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -140,7 +140,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; } - if(strcasecmp(argv[i+1],"ncch") == 0) + if(strcasecmp(argv[i+1],"ncch") == 0 || strcasecmp(argv[i+1],"cxi") == 0 || strcasecmp(argv[i+1],"cfa") == 0) set->common.outFormat = NCCH; else if(strcasecmp(argv[i+1],"cci") == 0) set->common.outFormat = CCI; From 8eb185915837d567fc59971ce483137a37e3a8ac Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 30 Aug 2014 01:47:03 +1000 Subject: [PATCH 042/317] completely moved "code"+"exhdr" parsing to elf.c --- makerom/elf.c | 38 ++++++++++++++++++++++++++++++++++++-- makerom/exheader.c | 29 +++++++++-------------------- makerom/exheader.h | 6 +++--- 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/makerom/elf.c b/makerom/elf.c index 03adee20..7bd07ded 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -1,5 +1,6 @@ #include "lib.h" #include "ncch_build.h" +#include "exheader_read.h" #include "elf_hdr.h" #include "elf.h" #include "blz.h" @@ -140,7 +141,10 @@ int ImportExeFsCodeBinaryFromFile(ncch_settings *set) { u32 size = set->componentFilePtrs.codeSize; u8 *buffer = malloc(size); - if(!buffer) {fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR;} + if(!buffer) { + fprintf(stderr,"[ELF ERROR] Not enough memory\n"); + return MEM_ERROR; + } ReadFile64(buffer,size,0,set->componentFilePtrs.code); set->exefsSections.code.size = set->componentFilePtrs.codeSize; @@ -157,6 +161,36 @@ int ImportExeFsCodeBinaryFromFile(ncch_settings *set) set->exefsSections.code.size = size; set->exefsSections.code.buffer = buffer; } + + size = set->componentFilePtrs.exhdrSize; + if(size < sizeof(extended_hdr)){ + fprintf(stderr,"[ELF ERROR] Exheader code info template is too small\n"); + return FAILED_TO_IMPORT_FILE; + } + extended_hdr *exhdr = malloc(size); + if(!exhdr) { + fprintf(stderr,"[ELF ERROR] Not enough memory\n"); + return MEM_ERROR; + } + ReadFile64(exhdr,size,0,set->componentFilePtrs.exhdr); + + /* Setting code_segment data */ + set->codeDetails.textAddress = u8_to_u32(exhdr->codeSetInfo.text.address,LE); + set->codeDetails.textMaxPages = u8_to_u32(exhdr->codeSetInfo.text.numMaxPages,LE); + set->codeDetails.textSize = u8_to_u32(exhdr->codeSetInfo.text.codeSize,LE); + + set->codeDetails.roAddress = u8_to_u32(exhdr->codeSetInfo.rodata.address,LE); + set->codeDetails.roMaxPages = u8_to_u32(exhdr->codeSetInfo.rodata.numMaxPages,LE); + set->codeDetails.roSize = u8_to_u32(exhdr->codeSetInfo.rodata.codeSize,LE); + + set->codeDetails.rwAddress = u8_to_u32(exhdr->codeSetInfo.data.address,LE); + set->codeDetails.rwMaxPages = u8_to_u32(exhdr->codeSetInfo.data.numMaxPages,LE); + set->codeDetails.rwSize = u8_to_u32(exhdr->codeSetInfo.data.codeSize,LE); + + set->codeDetails.bssSize = u8_to_u32(exhdr->codeSetInfo.bssSize,LE); + + free(exhdr); + return 0; } @@ -269,7 +303,7 @@ int CreateExeFsCode(elf_context *elf, u8 *elfFile, ncch_settings *set) set->exefsSections.code.buffer = code; } - /* Setting code_segment rwdata and freeing original buffers */ + /* Setting code_segment data and freeing original buffers */ set->codeDetails.textAddress = text.address; set->codeDetails.textMaxPages = text.maxPageNum; set->codeDetails.textSize = text.size; diff --git a/makerom/exheader.c b/makerom/exheader.c index 9c99f258..d7dba89a 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -121,17 +121,6 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * fprintf(stderr,"[EXHEADER ERROR] Not enough memory\n"); return MEM_ERROR; } - - /* Import ExHeader Code Section template */ - if(ncchset->componentFilePtrs.exhdrSize){ - u32 import_size = 0x30; //min64(0x30,ncchset->componentFilePtrs.exhdrSize); - u32 import_offset = 0x10; - if((import_size+import_offset) > ncchset->componentFilePtrs.exhdrSize){ - fprintf(stderr,"[EXHEADER ERROR] Exheader Template is too small\n"); - return FAILED_TO_IMPORT_FILE; - } - ReadFile64((ncchset->sections.exhdr.buffer+import_offset),import_size,import_offset,ncchset->componentFilePtrs.exhdr); - } /* Create ExHeader Struct for output */ exhdrset->exHdr = (extended_hdr*)ncchset->sections.exhdr.buffer; @@ -142,17 +131,17 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * /* BSS Size */ u32_to_u8(exhdrset->exHdr->codeSetInfo.bssSize,ncchset->codeDetails.bssSize,LE); /* Data */ - u32_to_u8(exhdrset->exHdr->codeSetInfo.dataSectionInfo.address,ncchset->codeDetails.rwAddress,LE); - u32_to_u8(exhdrset->exHdr->codeSetInfo.dataSectionInfo.codeSize,ncchset->codeDetails.rwSize,LE); - u32_to_u8(exhdrset->exHdr->codeSetInfo.dataSectionInfo.numMaxPages,ncchset->codeDetails.rwMaxPages,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.data.address,ncchset->codeDetails.rwAddress,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.data.codeSize,ncchset->codeDetails.rwSize,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.data.numMaxPages,ncchset->codeDetails.rwMaxPages,LE); /* RO */ - u32_to_u8(exhdrset->exHdr->codeSetInfo.readOnlySectionInfo.address,ncchset->codeDetails.roAddress,LE); - u32_to_u8(exhdrset->exHdr->codeSetInfo.readOnlySectionInfo.codeSize,ncchset->codeDetails.roSize,LE); - u32_to_u8(exhdrset->exHdr->codeSetInfo.readOnlySectionInfo.numMaxPages,ncchset->codeDetails.roMaxPages,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.rodata.address,ncchset->codeDetails.roAddress,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.rodata.codeSize,ncchset->codeDetails.roSize,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.rodata.numMaxPages,ncchset->codeDetails.roMaxPages,LE); /* Text */ - u32_to_u8(exhdrset->exHdr->codeSetInfo.textSectionInfo.address,ncchset->codeDetails.textAddress,LE); - u32_to_u8(exhdrset->exHdr->codeSetInfo.textSectionInfo.codeSize,ncchset->codeDetails.textSize,LE); - u32_to_u8(exhdrset->exHdr->codeSetInfo.textSectionInfo.numMaxPages,ncchset->codeDetails.textMaxPages,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.text.address,ncchset->codeDetails.textAddress,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.text.codeSize,ncchset->codeDetails.textSize,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.text.numMaxPages,ncchset->codeDetails.textMaxPages,LE); } /* Set Simple Flags */ diff --git a/makerom/exheader.h b/makerom/exheader.h index d2d216fb..ee1e2d45 100644 --- a/makerom/exheader.h +++ b/makerom/exheader.h @@ -99,11 +99,11 @@ typedef struct u8 padding0[5]; u8 flag; u8 remasterVersion[2]; // le u16 - exhdr_CodeSegmentInfo textSectionInfo; + exhdr_CodeSegmentInfo text; u8 stackSize[4]; // le u32 - exhdr_CodeSegmentInfo readOnlySectionInfo; + exhdr_CodeSegmentInfo rodata; u8 padding1[4]; - exhdr_CodeSegmentInfo dataSectionInfo; + exhdr_CodeSegmentInfo data; u8 bssSize[4]; // le u32 } exhdr_CodeSetInfo; From 652a50744a01dd5e8dc2e96a1c677ecc2184b116 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 30 Aug 2014 01:47:47 +1000 Subject: [PATCH 043/317] makerom 0.12 bug fixes and proper cia ticket generation --- makerom/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makerom/Makefile b/makerom/Makefile index 5293084a..48ecc523 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -1,7 +1,7 @@ # Makerom Sources UTILS_OBJS = utils.o ctr_utils.o dir.o utf.o keyset.o titleid.o CIA_OBJS = cia.o certs.o tik.o tmd.o -NCCH_OBJS = ncch.o exheader.o accessdesc.o exefs.o elf.o romfs.o romfs_import.o romfs_gen.o +NCCH_OBJS = ncch.o exheader.o accessdesc.o exefs.o elf.o romfs.o romfs_import.o romfs_gen.o NCSD_OBJS = ncsd.o cardinfo.o SETTINGS_OBJS = user_settings.o rsf_settings.o LIB_API_OBJS = crypto.o yaml_parser.o blz.o @@ -21,7 +21,7 @@ CC = gcc # MAKEROM Build Settings MAKEROM_BUILD_FLAGS = #-DDEBUG VER_MAJOR = 0 -VER_MINOR = 11 +VER_MINOR = 12 OUTPUT = makerom main: build From 96960526e8208dc0bf0863309a4e1759b3ff444f Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 30 Aug 2014 12:34:24 +1000 Subject: [PATCH 044/317] refactored cia code an attempt make errors in tik/tmd values rarer in occurence --- makerom/cia.c | 36 +++++++++++++++++------------------- makerom/cia_build.h | 19 ++++++++++--------- makerom/tik.c | 30 ++++++++++++++++-------------- makerom/titleid.c | 10 ++++++++++ makerom/titleid.h | 3 +++ makerom/tmd.c | 11 ++++++----- 6 files changed, 62 insertions(+), 47 deletions(-) diff --git a/makerom/cia.c b/makerom/cia.c index 095fb28f..07eefdf3 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -166,7 +166,7 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) ciaset->content.includeUpdateNcch = usrset->cia.includeUpdateNcch; ciaset->verbose = usrset->common.verbose; - u32_to_u8(ciaset->tmd.titleType,TYPE_CTR,BE); + ciaset->tmd.titleType = TYPE_CTR; ciaset->content.encryptCia = usrset->common.rsfSet.Option.EnableCrypt; ciaset->content.IsDlc = usrset->cia.DlcContent; if(ciaset->keys->aes.commonKey[ciaset->keys->aes.currentCommonKey] == NULL && ciaset->content.encryptCia){ @@ -182,10 +182,10 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) } // Ticket Data - rndset(ciaset->tik.ticketId,8); - u32_to_u8(ciaset->tik.deviceId,usrset->cia.deviceId,BE); - u32_to_u8(ciaset->tik.eshopAccId,usrset->cia.eshopAccId,BE); - ciaset->tik.licenceType = 0; + ciaset->tik.ticketId = u64GetRand(); + ciaset->tik.deviceId = usrset->cia.deviceId; + ciaset->tik.eshopAccId = usrset->cia.eshopAccId; + ciaset->tik.licenceType = lic_Permanent; ciaset->tik.audit = 0; if(usrset->cia.randomTitleKey) @@ -208,6 +208,7 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) ciaset->content.id[0] = usrset->cia.contentId[0]; ciaset->tmd.formatVersion = 1; + ciaset->tmd.accessRights = 0; result = GenCertChildIssuer(ciaset->tmd.issuer,ciaset->keys->certs.cpCert); return 0; } @@ -256,7 +257,7 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) } /* Gen Settings From Ncch0 */ - endian_memcpy(ciaset->common.titleId,hdr->titleId,8,LE); + ciaset->common.titleId = u8_to_u64(hdr->titleId,LE); /* Getting ncch key */ @@ -289,19 +290,17 @@ int GetTmdDataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_info *ncch_ctx, u8 * memcpy(exhdr,ncch+ncch_ctx->exhdrOffset,sizeof(extended_hdr)); if(key != NULL) CryptNcchRegion((u8*)exhdr,sizeof(extended_hdr),0,ncch_ctx,key,ncch_exhdr); - - u16 Category = u8_to_u16((ciaset->common.titleId+2),BE); - if(IsPatch(Category)||ciaset->content.IsCfa||ciaset->content.keyNotFound) - u32_to_u8(ciaset->tmd.savedataSize,0,LE); - else - u32_to_u8(ciaset->tmd.savedataSize,(u32)GetSaveDataSize_frm_exhdr(exhdr),LE); - if(ciaset->rsf->SystemControlInfo.SaveDataSize && !ciaset->content.IsCfa && ciaset->content.keyNotFound){ + if(IsPatch(GetTidCategory(ciaset->common.titleId))||ciaset->content.IsCfa) + ciaset->tmd.savedataSize = 0; + else if(ciaset->content.keyNotFound && ciaset->rsf->SystemControlInfo.SaveDataSize){ // if it's a title which can have save data, but save data size could not be read from exhdr u64 size = 0; GetSaveDataSizeFromString(&size,ciaset->rsf->SystemControlInfo.SaveDataSize,"CIA"); - u32_to_u8(ciaset->tmd.savedataSize,(u32)size,LE); + ciaset->tmd.savedataSize = (u32)(size & MAX_U32); } - + else + ciaset->tmd.savedataSize = (u32)(GetSaveDataSize_frm_exhdr(exhdr) & MAX_U32); + if(ciaset->content.IsCfa||ciaset->content.keyNotFound){ if(ciaset->common.titleVersion[VER_MAJOR] == MAX_U16){ // '-major' wasn't set if(ciaset->content.IsCfa){ // Is a CFA and can be decrypted @@ -479,8 +478,7 @@ int GetSettingsFromSrl(cia_settings *ciaset) } // Generate and store Converted TitleID - u64_to_u8(ciaset->common.titleId,ConvertTwlIdToCtrId(u8_to_u64(hdr->title_id,LE)),BE); - //memdump(stdout,"SRL TID: ",ciaset->TitleID,8); + ciaset->common.titleId = ConvertTwlIdToCtrId(u8_to_u64(hdr->title_id,LE)); // Get TWL Flag ciaset->tmd.twlFlag = ((hdr->reserved_flags[3] & 6) >> 1); @@ -491,8 +489,8 @@ int GetSettingsFromSrl(cia_settings *ciaset) ciaset->tmd.version = version; // Get SaveDataSize (Public and Private) - memcpy(ciaset->tmd.savedataSize,hdr->pubSaveDataSize,4); - memcpy(ciaset->tmd.privSavedataSize,hdr->privSaveDataSize,4); + ciaset->tmd.savedataSize = u8_to_u32(hdr->pubSaveDataSize,LE); + ciaset->tmd.privSavedataSize = u8_to_u32(hdr->privSaveDataSize,LE); // Setting CIA Content Settings ciaset->content.count = 1; diff --git a/makerom/cia_build.h b/makerom/cia_build.h index a33717d7..ad0de783 100644 --- a/makerom/cia_build.h +++ b/makerom/cia_build.h @@ -23,7 +23,7 @@ typedef struct bool verbose; struct{ - u8 titleId[8]; + u64 titleId; u16 titleVersion[4]; u8 titleKey[16]; } common; @@ -39,12 +39,12 @@ typedef struct u8 formatVersion; u16 version; - - u8 ticketId[8]; - u8 deviceId[4]; + + u64 ticketId; + u32 deviceId; u8 licenceType; u8 audit; - u8 eshopAccId[4]; + u32 eshopAccId; } tik; struct{ @@ -52,10 +52,11 @@ typedef struct u8 formatVersion; u16 version; - - u8 titleType[4]; - u8 savedataSize[4]; - u8 privSavedataSize[4]; + u32 accessRights; + + u32 titleType; + u32 savedataSize; + u32 privSavedataSize; u8 twlFlag; } tmd; diff --git a/makerom/tik.c b/makerom/tik.c index 5f2e233c..db1f974e 100644 --- a/makerom/tik.c +++ b/makerom/tik.c @@ -50,24 +50,26 @@ int SetupTicketBuffer(cia_settings *set) void SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset) { clrmem(hdr,sizeof(tik_hdr)); - + memcpy(hdr->issuer,ciaset->tik.issuer,0x40); hdr->formatVersion = ciaset->tik.formatVersion; hdr->caCrlVersion = ciaset->cert.caCrlVersion; hdr->signerCrlVersion = ciaset->cert.signerCrlVersion; - if(ciaset->content.encryptCia) - CryptTitleKey(hdr->encryptedTitleKey, ciaset->common.titleKey,ciaset->common.titleId,ciaset->keys,ENC); - else - rndset(hdr->encryptedTitleKey,16); - memcpy(hdr->ticketId,ciaset->tik.ticketId,8); - memcpy(hdr->deviceId,ciaset->tik.deviceId,4); - memcpy(hdr->titleId,ciaset->common.titleId,8); + u64_to_u8(hdr->ticketId,ciaset->tik.ticketId,BE); + u32_to_u8(hdr->deviceId,ciaset->tik.deviceId,BE); + u64_to_u8(hdr->titleId,ciaset->common.titleId,BE); u16_to_u8(hdr->ticketVersion,ciaset->tik.version,BE); hdr->licenceType = ciaset->tik.licenceType; hdr->keyId = ciaset->keys->aes.currentCommonKey; - memcpy(hdr->eshopAccId,ciaset->tik.eshopAccId,4); + u32_to_u8(hdr->eshopAccId,ciaset->tik.eshopAccId,BE); hdr->audit = ciaset->tik.audit; SetLimits(hdr,ciaset); + + // Crypt TitleKey + if(ciaset->content.encryptCia) + CryptTitleKey(hdr->encryptedTitleKey, ciaset->common.titleKey, hdr->titleId, ciaset->keys, ENC); + else + rndset(hdr->encryptedTitleKey,AES_128_KEY_SIZE); } int SignTicketHeader(buffer_struct *tik, keys_struct *keys) @@ -83,12 +85,12 @@ int SignTicketHeader(buffer_struct *tik, keys_struct *keys) return ctr_sig(data,len,sig->data,keys->rsa.xsPub,keys->rsa.xsPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } -int CryptTitleKey(u8 *EncTitleKey, u8 *DecTitleKey, u8 *TitleID, keys_struct *keys, u8 mode) +int CryptTitleKey(u8 *encKey, u8 *decKey, u8 *titleId, keys_struct *keys, u8 mode) { //Generating IV u8 iv[16]; memset(&iv,0x0,16); - memcpy(iv,TitleID,0x8); + memcpy(iv,titleId,0x8); //Setting up Aes Context ctr_aes_context ctx; @@ -96,9 +98,9 @@ int CryptTitleKey(u8 *EncTitleKey, u8 *DecTitleKey, u8 *TitleID, keys_struct *ke //Crypting TitleKey ctr_init_aes_cbc(&ctx,keys->aes.commonKey[keys->aes.currentCommonKey],iv,mode); - if(mode == ENC) ctr_aes_cbc(&ctx,DecTitleKey,EncTitleKey,0x10,ENC); - else ctr_aes_cbc(&ctx,EncTitleKey,DecTitleKey,0x10,DEC); - + if(mode == ENC) ctr_aes_cbc(&ctx,decKey,encKey,0x10,ENC); + else ctr_aes_cbc(&ctx,encKey,decKey,0x10,DEC); + // Return return 0; } diff --git a/makerom/titleid.c b/makerom/titleid.c index d4479c42..b9045341 100644 --- a/makerom/titleid.c +++ b/makerom/titleid.c @@ -14,6 +14,16 @@ u64 ConvertTwlIdToCtrId(u64 pgid) return 0x0004800000000000 | (pgid & 0x00007FFFFFFFFFFF); } +u16 GetTidCategory(u64 titleId) +{ + return (titleId>>32) & MAX_U16; +} + +u32 GetTidUniqueId(u64 titleId) +{ + return (titleId>>8) & 0xFFFFFF; +} + int GetProgramID(u64 *dest, rsf_settings *rsf, bool IsForExheader) { int ret; diff --git a/makerom/titleid.h b/makerom/titleid.h index ecfd7823..8f96d537 100644 --- a/makerom/titleid.h +++ b/makerom/titleid.h @@ -88,6 +88,9 @@ u64 ConvertTwlIdToCtrId(u64 pgid); int GetProgramID(u64 *dest, rsf_settings *rsf, bool IsForExheader); int GetUniqueID(u32 *dest, rsf_settings *rsf); +u16 GetTidCategory(u64 titleId); +u32 GetTidUniqueId(u64 titleId); + bool IsDemo(u16 Category); bool IsSystem(u16 Category); bool IsDlpChild(u16 Category); diff --git a/makerom/tmd.c b/makerom/tmd.c index e81f6688..6abb6e53 100644 --- a/makerom/tmd.c +++ b/makerom/tmd.c @@ -54,11 +54,12 @@ int SetupTMDHeader(tmd_hdr *hdr, tmd_content_info_record *info_record, cia_setti hdr->formatVersion = ciaset->tmd.formatVersion; hdr->caCrlVersion = ciaset->cert.caCrlVersion; hdr->signerCrlVersion = ciaset->cert.signerCrlVersion; - memcpy(hdr->titleID,ciaset->common.titleId,8); - memcpy(hdr->titleType,ciaset->tmd.titleType,4); - memcpy(hdr->savedataSize,ciaset->tmd.savedataSize,4); - memcpy(hdr->privSavedataSize,ciaset->tmd.privSavedataSize,4); + u64_to_u8(hdr->titleID,ciaset->common.titleId,BE); + u32_to_u8(hdr->titleType,ciaset->tmd.titleType,BE); + u32_to_u8(hdr->savedataSize,ciaset->tmd.savedataSize,LE); + u32_to_u8(hdr->privSavedataSize,ciaset->tmd.privSavedataSize,LE); hdr->twlFlag = ciaset->tmd.twlFlag; + u32_to_u8(hdr->accessRights,ciaset->tmd.accessRights,BE); u16_to_u8(hdr->titleVersion,ciaset->tmd.version,BE); u16_to_u8(hdr->contentCount,ciaset->content.count,BE); ctr_sha(info_record,sizeof(tmd_content_info_record)*64,hdr->infoRecordHash,CTR_SHA_256); @@ -129,7 +130,7 @@ u64 GetTmdTitleId(tmd_hdr *hdr) u32 GetTmdSaveSize(tmd_hdr *hdr) { - return u8_to_u32(hdr->savedataSize,BE); + return u8_to_u32(hdr->savedataSize,LE); } u16 GetTmdContentCount(tmd_hdr *hdr) From a624b6db8b37898a435fa4b7b7ecae55ae031ec8 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 30 Aug 2014 12:56:11 +1000 Subject: [PATCH 045/317] bug fix prevented cia from "decrypting" cleartext ncch images --- makerom/cia.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/cia.c b/makerom/cia.c index 07eefdf3..b0051af1 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -262,7 +262,7 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) /* Getting ncch key */ u8 *ncchkey = NULL; - if(!ciaset->content.keyNotFound || IsNcchEncrypted(hdr)){ + if(!ciaset->content.keyNotFound && IsNcchEncrypted(hdr)){ SetNcchKeys(ciaset->keys,hdr); ncchkey = ciaset->keys->aes.ncchKey0; if(ciaset->verbose){ From 1cf7485274dac2b67ffcbc14a4abfb7c27357acd Mon Sep 17 00:00:00 2001 From: sbJFn5r Date: Sun, 31 Aug 2014 22:24:09 -0400 Subject: [PATCH 046/317] Update ctr.h --- ctrtool/ctr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ctrtool/ctr.h b/ctrtool/ctr.h index 724f4c02..93210517 100644 --- a/ctrtool/ctr.h +++ b/ctrtool/ctr.h @@ -26,6 +26,7 @@ typedef enum FILETYPE_LZSS, FILETYPE_FIRM, FILETYPE_CWAV, + FILETYPE_EXEFS, FILETYPE_ROMFS } ctr_filetypes; From 7210dbc7d8af454485b269579dbcf4cde83734f3 Mon Sep 17 00:00:00 2001 From: sbJFn5r Date: Sun, 31 Aug 2014 22:24:56 -0400 Subject: [PATCH 047/317] Update types.h --- ctrtool/types.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ctrtool/types.h b/ctrtool/types.h index 0ec14420..bade7f15 100644 --- a/ctrtool/types.h +++ b/ctrtool/types.h @@ -21,7 +21,8 @@ enum flags VerboseFlag = (1<<3), VerifyFlag = (1<<4), RawFlag = (1<<5), - ShowKeysFlag = (1<<6) + ShowKeysFlag = (1<<6), + DecompressCodeFlag = (1<<7) }; enum validstate From ea2dbc023bf30f381d0242b199dd5c5d67d5dce9 Mon Sep 17 00:00:00 2001 From: sbJFn5r Date: Sun, 31 Aug 2014 22:26:38 -0400 Subject: [PATCH 048/317] Update main.c --- ctrtool/main.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ctrtool/main.c b/ctrtool/main.c index 2f837f0a..5846fa2a 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -78,6 +78,9 @@ static void usage(const char *argv0) "CWAV options:\n" " --wav=file Specify wav output file.\n" " --wavloops=count Specify wav loop count, default 0.\n" + "EXEFS options:\n" + " --decompresscode Decompress .code section\n" + " (only needed when using raw EXEFS file)\n" "ROMFS options:\n" " --romfsdir=dir Specify RomFS directory path.\n" " --listromfs List files in RomFS.\n" @@ -142,6 +145,7 @@ int main(int argc, char* argv[]) {"listromfs", 0, NULL, 18}, {"wavloops", 1, NULL, 19}, {"logo", 1, NULL, 20}, + {"decompresscode", 0, NULL, 21}, {NULL}, }; @@ -201,6 +205,8 @@ int main(int argc, char* argv[]) ctx.filetype = FILETYPE_FIRM; else if (!strcmp(optarg, "cwav")) ctx.filetype = FILETYPE_CWAV; + else if (!strcmp(optarg, "exefs")) + ctx.filetype = FILETYPE_EXEFS; else if (!strcmp(optarg, "romfs")) ctx.filetype = FILETYPE_ROMFS; break; @@ -226,6 +232,7 @@ int main(int argc, char* argv[]) case 18: settings_set_list_romfs_files(&ctx.usersettings, 1); break; case 19: settings_set_cwav_loopcount(&ctx.usersettings, strtoul(optarg, 0, 0)); break; case 20: settings_set_logo_path(&ctx.usersettings, optarg); break; + case 21: ctx.actions |= DecompressCodeFlag; break; default: usage(argv[0]); @@ -424,6 +431,19 @@ int main(int argc, char* argv[]) break; } + + case FILETYPE_EXEFS: + { + exefs_context exefsctx; + + exefs_init(&exefsctx); + exefs_set_file(&exefsctx, ctx.infile); + exefs_set_size(&exefsctx, ctx.infilesize); + exefs_set_usersettings(&exefsctx, &ctx.usersettings); + exefs_process(&exefsctx, ctx.actions); + + break; + } case FILETYPE_ROMFS: { From 2b4a79f517606a2ff334e6d7d8a99d0fd69c26e7 Mon Sep 17 00:00:00 2001 From: sbJFn5r Date: Sun, 31 Aug 2014 22:27:36 -0400 Subject: [PATCH 049/317] Update exefs.c --- ctrtool/exefs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ctrtool/exefs.c b/ctrtool/exefs.c index a4dce903..78070710 100644 --- a/ctrtool/exefs.c +++ b/ctrtool/exefs.c @@ -129,7 +129,7 @@ void exefs_save(exefs_context* ctx, u32 index, u32 flags) ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); ctr_add_counter(&ctx->aes, offset / 0x10); - if (index == 0 && ctx->compressedflag && ((flags & RawFlag) == 0)) + if (index == 0 && (ctx->compressedflag || (flags & DecompressCodeFlag)) && ((flags & RawFlag) == 0)) { fprintf(stdout, "Decompressing section %s to %s...\n", name, outfname); @@ -200,6 +200,8 @@ void exefs_save(exefs_context* ctx, u32 index, u32 flags) } clean: + if (fout) + fclose(fout); free(compressedbuffer); free(decompressedbuffer); return; From 030b638c923eba6162f562178aef4dd7a6aecfbf Mon Sep 17 00:00:00 2001 From: applestash Date: Mon, 1 Sep 2014 13:05:05 +1000 Subject: [PATCH 050/317] misc --- makerom/ncch.c | 17 +++++------------ makerom/ncch.h | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/makerom/ncch.c b/makerom/ncch.c index 10c4a92f..2398c27e 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -19,7 +19,6 @@ int CheckCFASignature(ncch_hdr *hdr, keys_struct *keys); int SignCXI(ncch_hdr *hdr, keys_struct *keys); int CheckCXISignature(ncch_hdr *hdr, u8 *pubk); -void InitNcchSettings(ncch_settings *set); void FreeNcchSettings(ncch_settings *set); int GetNcchSettings(ncch_settings *ncchset, user_settings *usrset); int GetBasicOptions(ncch_settings *ncchset, user_settings *usrset); @@ -71,7 +70,6 @@ int build_NCCH(user_settings *usrset) fprintf(stderr,"[NCCH ERROR] Not enough memory\n"); return MEM_ERROR; } - InitNcchSettings(ncchset); // Get Settings result = GetNcchSettings(ncchset,usrset); @@ -126,11 +124,6 @@ int build_NCCH(user_settings *usrset) return result; } -void InitNcchSettings(ncch_settings *set) -{ - memset(set,0,sizeof(ncch_settings)); -} - void FreeNcchSettings(ncch_settings *set) { if(set->componentFilePtrs.elf) fclose(set->componentFilePtrs.elf); @@ -193,7 +186,7 @@ int GetBasicOptions(ncch_settings *ncchset, user_settings *usrset) if(ncchset->options.IsCfa && !ncchset->options.UseRomFS){ fprintf(stderr,"[NCCH ERROR] \"Rom/HostRoot\" must be set\n"); - return NCCH_BAD_YAML_SET; + return NCCH_BAD_RSF_SET; } return result; @@ -352,7 +345,7 @@ int ImportLogo(ncch_settings *set) } else if(strcasecmp(set->rsfSet->BasicInfo.Logo,"none") != 0){ fprintf(stderr,"[NCCH ERROR] Invalid logo name\n"); - return NCCH_BAD_YAML_SET; + return NCCH_BAD_RSF_SET; } } return 0; @@ -602,7 +595,7 @@ int SetCommonHeaderBasicData(ncch_settings *set, ncch_hdr *hdr) if(set->rsfSet->BasicInfo.ProductCode){ if(!IsValidProductCode((char*)set->rsfSet->BasicInfo.ProductCode,set->options.FreeProductCode)){ fprintf(stderr,"[NCCH ERROR] Invalid Product Code\n"); - return NCCH_BAD_YAML_SET; + return NCCH_BAD_RSF_SET; } memcpy(hdr->productCode,set->rsfSet->BasicInfo.ProductCode,strlen((char*)set->rsfSet->BasicInfo.ProductCode)); } @@ -611,7 +604,7 @@ int SetCommonHeaderBasicData(ncch_settings *set, ncch_hdr *hdr) if(set->rsfSet->BasicInfo.CompanyCode){ if(strlen((char*)set->rsfSet->BasicInfo.CompanyCode) != 2){ fprintf(stderr,"[NCCH ERROR] CompanyCode length must be 2\n"); - return NCCH_BAD_YAML_SET; + return NCCH_BAD_RSF_SET; } memcpy(hdr->makerCode,set->rsfSet->BasicInfo.CompanyCode,2); } @@ -657,7 +650,7 @@ int SetCommonHeaderBasicData(ncch_settings *set, ncch_hdr *hdr) else if(strcmp(set->rsfSet->BasicInfo.ContentType,"Trial") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= content_Trial; else{ fprintf(stderr,"[NCCH ERROR] Invalid ContentType '%s'\n",set->rsfSet->BasicInfo.ContentType); - return NCCH_BAD_YAML_SET; + return NCCH_BAD_RSF_SET; } } diff --git a/makerom/ncch.h b/makerom/ncch.h index c54ced2e..be77e95c 100644 --- a/makerom/ncch.h +++ b/makerom/ncch.h @@ -20,7 +20,7 @@ typedef enum EXEFS_CORRUPT = -14, ROMFS_CORRUPT = -15, // Others - NCCH_BAD_YAML_SET = -16, + NCCH_BAD_RSF_SET = -16, DATA_POS_DNE = -17, } ncch_errors; From 2e6341ac0da493ed8537adff6f1e27b664167ab2 Mon Sep 17 00:00:00 2001 From: sbJFn5r Date: Mon, 1 Sep 2014 14:09:28 -0400 Subject: [PATCH 051/317] Forgot to add 'exefs' to the '-t' options display --- ctrtool/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/main.c b/ctrtool/main.c index 5846fa2a..f80823a7 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -57,7 +57,7 @@ static void usage(const char *argv0) " --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" + " firm, cwav, exefs, romfs]\n" "LZSS options:\n" " --lzssout=file Specify lzss output file\n" "CXI/CCI options:\n" From 6d3ad5cfe4619710d50980cafc1e994de6b1ea11 Mon Sep 17 00:00:00 2001 From: applestash Date: Sun, 7 Sep 2014 21:10:55 +1000 Subject: [PATCH 052/317] implementation fixes cleaned AES interface, corrected some header files, other misc --- makerom/cia.c | 20 +++---- makerom/cia.h | 2 +- makerom/crypto.c | 128 ++++++++++---------------------------------- makerom/crypto.h | 15 +----- makerom/ncch.c | 96 ++++++++++----------------------- makerom/ncch.h | 10 ++-- makerom/ncsd.c | 10 ++-- makerom/tik.c | 17 +++--- makerom/tik_build.h | 13 +---- makerom/tmd.c | 4 +- 10 files changed, 83 insertions(+), 232 deletions(-) diff --git a/makerom/cia.c b/makerom/cia.c index b0051af1..a1d182a2 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -289,7 +289,7 @@ int GetTmdDataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_info *ncch_ctx, u8 * extended_hdr *exhdr = malloc(sizeof(extended_hdr)); memcpy(exhdr,ncch+ncch_ctx->exhdrOffset,sizeof(extended_hdr)); if(key != NULL) - CryptNcchRegion((u8*)exhdr,sizeof(extended_hdr),0,ncch_ctx,key,ncch_exhdr); + CryptNcchRegion((u8*)exhdr,sizeof(extended_hdr),0,ncch_ctx->titleId,key,ncch_exhdr); if(IsPatch(GetTidCategory(ciaset->common.titleId))||ciaset->content.IsCfa) ciaset->tmd.savedataSize = 0; @@ -334,12 +334,12 @@ int GetMetaRegion(cia_settings *ciaset, u8 *ncch, ncch_info *info, u8 *key) extended_hdr *exhdr = malloc(sizeof(extended_hdr)); memcpy(exhdr,ncch+info->exhdrOffset,sizeof(extended_hdr)); if(key != NULL) - CryptNcchRegion((u8*)exhdr,sizeof(extended_hdr),0,info,key,ncch_exhdr); + CryptNcchRegion((u8*)exhdr,sizeof(extended_hdr),0,info->titleId,key,ncch_exhdr); exefs_hdr *exefsHdr = malloc(sizeof(exefs_hdr)); memcpy(exefsHdr,ncch+info->exefsOffset,sizeof(exefs_hdr)); if(key != NULL) - CryptNcchRegion((u8*)exefsHdr,sizeof(exefs_hdr),0,info,key,ncch_exefs); + CryptNcchRegion((u8*)exefsHdr,sizeof(exefs_hdr),0,info->titleId,key,ncch_exefs); u32 icon_size = 0; u32 icon_offset = 0; @@ -364,7 +364,7 @@ int GetMetaRegion(cia_settings *ciaset, u8 *ncch, ncch_info *info, u8 *key) u8 *IconDestPos = (ciaset->ciaSections.meta.buffer + sizeof(cia_metadata)); memcpy(IconDestPos,ncch+info->exefsOffset+icon_offset,icon_size); if(key != NULL) - CryptNcchRegion(IconDestPos,icon_size,icon_offset,info,key,ncch_exefs); + CryptNcchRegion(IconDestPos,icon_size,icon_offset,info->titleId,key,ncch_exefs); //memdump(stdout,"Icon: ",IconDestPos,0x10); } @@ -628,7 +628,7 @@ int BuildCiaHdr(cia_settings *ciaset) ciaset->ciaSections.metaOffset = align(ciaset->ciaSections.contentOffset+ciaset->content.totalSize,0x40); for(int i = 0; i < ciaset->content.count; i++) - hdr->contentIndex[ciaset->content.index[i]/8] |= 1 << (7 - (ciaset->content.index[i] & 7)); + hdr->contentIndex[ciaset->content.index[i]/8] |= 0x80 >> (ciaset->content.index[i] & 7); return 0; } @@ -645,19 +645,15 @@ int WriteCiaToFile(cia_settings *ciaset) } -int CryptContent(u8 *enc, u8 *dec, u64 size, u8 *title_key, u16 index, u8 mode) +int CryptContent(u8 *input, u8 *output, u64 size, u8 *title_key, u16 index, u8 mode) { //generating IV u8 iv[16]; - memset(&iv,0x0,16); + clrmem(&iv,16); iv[0] = (index >> 8) & 0xff; iv[1] = index & 0xff; //Crypting content - ctr_aes_context ctx; - memset(&ctx,0x0,sizeof(ctr_aes_context)); - ctr_init_aes_cbc(&ctx,title_key,iv,mode); - if(mode == ENC) ctr_aes_cbc(&ctx,dec,enc,size,ENC); - else ctr_aes_cbc(&ctx,enc,dec,size,DEC); + AesCbc(title_key,iv,input,output,size,mode); return 0; } diff --git a/makerom/cia.h b/makerom/cia.h index b7dc6c2c..29da9567 100644 --- a/makerom/cia.h +++ b/makerom/cia.h @@ -22,5 +22,5 @@ typedef struct u8 padding1[0xfc]; } cia_metadata; -int CryptContent(u8 *enc, u8 *dec, u64 size, u8 *title_key, u16 index, u8 mode); +int CryptContent(u8 *input, u8 *output, u64 size, u8 *title_key, u16 index, u8 mode); diff --git a/makerom/crypto.c b/makerom/crypto.c index bbc977e6..71dbdd39 100644 --- a/makerom/crypto.c +++ b/makerom/crypto.c @@ -16,115 +16,43 @@ void ctr_sha(void *data, u64 size, u8 *hash, int mode) } } -void ctr_add_counter(ctr_aes_context* ctx, u32 carry) +void SetAesCtrOffset(u8 *ctr, u64 offset) { - 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_init_counter(ctr_aes_context* ctx, u8 key[16], u8 ctr[16]) -{ - aes_setkey_enc(&ctx->aes, key, 128); - memcpy(ctx->ctr, ctr, 16); + u64_to_u8(ctr+8,u8_to_u64(ctr+8,BE)|align(offset,16)/16,BE); } -void ctr_crypt_counter_block(ctr_aes_context* ctx, u8 input[16], u8 output[16]) +void AesCtr(u8 *key, u8 *ctr, u8 *input, u8 *output, u64 length, u64 offset) { - 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); break; - case(DEC): aes_setkey_dec(&ctx->aes, key, 128); break; - } - memcpy(ctx->iv, iv, 16); + aes_context aes; + u64 nc_off = 0; + + clrmem(&aes,sizeof(aes_context)); + aes_setkey_enc(&aes, key, 128); + SetAesCtrOffset(ctr,offset); + + aes_crypt_ctr(&aes, length, &nc_off, ctr, stream, input, output); + + + return; } -void ctr_aes_cbc(ctr_aes_context* ctx,u8* input,u8* output,u32 size,u8 mode) +void AesCbc(u8 *key, u8 *iv, u8 *input, u8 *output, u64 length, u8 mode) { + aes_context aes; + clrmem(&aes,sizeof(aes_context)); + switch(mode){ - case(ENC): aes_crypt_cbc(&ctx->aes, AES_ENCRYPT, size, ctx->iv, input, output); break; - case(DEC): aes_crypt_cbc(&ctx->aes, AES_DECRYPT, size, ctx->iv, input, output); break; + case(ENC): + aes_setkey_enc(&aes, key, 128); + aes_crypt_cbc(&aes, AES_ENCRYPT, length, iv, input, output); + return; + case(DEC): + aes_setkey_dec(&aes, key, 128); + aes_crypt_cbc(&aes, AES_DECRYPT, length, iv, input, output); + return; + default: + return; } } diff --git a/makerom/crypto.h b/makerom/crypto.h index 3d5aaeed..9a29a107 100644 --- a/makerom/crypto.h +++ b/makerom/crypto.h @@ -55,13 +55,6 @@ typedef enum RSAKEY_PUB } rsakeytype; -typedef struct -{ - u8 ctr[16]; - u8 iv[16]; - aes_context aes; -} ctr_aes_context; - typedef struct { rsa_context rsa; @@ -74,12 +67,8 @@ extern "C" { bool VerifySha256(void *data, u64 size, u8 hash[32]); void ctr_sha(void *data, u64 size, u8 *hash, int mode); // AES -void ctr_add_counter(ctr_aes_context* ctx, u32 carry); -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_aes_cbc(ctr_aes_context* ctx,u8 key[16],u8 iv[16], u8 mode); -void ctr_aes_cbc(ctr_aes_context* ctx,u8* input,u8* output,u32 size,u8 mode); +void AesCtr(u8 *key, u8 *ctr, u8 *input, u8 *output, u64 length, u64 offset); +void AesCbc(u8 *key, u8 *iv, u8 *input, u8 *output, u64 length, u8 mode); // RSA void ctr_rsa_free(ctr_rsa_context* ctx); int ctr_rsa_init(ctr_rsa_context* ctx, u8 *modulus, u8 *private_exp, u8 *exponent, u8 rsa_type, u8 mode); diff --git a/makerom/ncch.c b/makerom/ncch.c index 2398c27e..5688842e 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -31,12 +31,7 @@ int FinaliseNcch(ncch_settings *ncchset); int SetCommonHeaderBasicData(ncch_settings *ncchset, ncch_hdr *hdr); bool IsValidProductCode(char *ProductCode, bool FreeProductCode); -int BuildCommonHeader(ncch_settings *ncchset); -int EnCryptNcchRegions(ncch_settings *ncchset); -int WriteNCCHSectionsToBuffer(ncch_settings *ncchset); - // Code - int SignCFA(ncch_hdr *hdr, keys_struct *keys) { return ctr_sig(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cciCfaPub,keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN); @@ -540,8 +535,8 @@ int FinaliseNcch(ncch_settings *set) // Crypting Exheader/AcexDesc if(set->cryptoDetails.exhdrSize){ - CryptNcchRegion(exhdr,set->cryptoDetails.exhdrSize,0x0,&set->cryptoDetails,set->keys->aes.ncchKey0,ncch_exhdr); - CryptNcchRegion(acexDesc,set->cryptoDetails.acexSize,set->cryptoDetails.exhdrSize,&set->cryptoDetails,set->keys->aes.ncchKey0,ncch_exhdr); + CryptNcchRegion(exhdr,set->cryptoDetails.exhdrSize,0x0,set->cryptoDetails.titleId,set->keys->aes.ncchKey0,ncch_exhdr); + CryptNcchRegion(acexDesc,set->cryptoDetails.acexSize,set->cryptoDetails.exhdrSize,set->cryptoDetails.titleId,set->keys->aes.ncchKey0,ncch_exhdr); } // Crypting ExeFs Files @@ -558,16 +553,16 @@ int FinaliseNcch(ncch_settings *set) u32 size = u8_to_u32(exefsHdr->fileHdr[i].size,LE); if(size) - CryptNcchRegion((exefs+offset),align(size,set->options.blockSize),offset,&set->cryptoDetails,key,ncch_exefs); + CryptNcchRegion((exefs+offset),align(size,set->options.blockSize),offset,set->cryptoDetails.titleId,key,ncch_exefs); } // Crypting ExeFs Header - CryptNcchRegion(exefs,sizeof(exefs_hdr),0x0,&set->cryptoDetails,set->keys->aes.ncchKey0,ncch_exefs); + CryptNcchRegion(exefs,sizeof(exefs_hdr),0x0,set->cryptoDetails.titleId,set->keys->aes.ncchKey0,ncch_exefs); } // Crypting RomFs if(set->cryptoDetails.romfsSize) - CryptNcchRegion(romfs,set->cryptoDetails.romfsSize,0x0,&set->cryptoDetails,set->keys->aes.ncchKey1,ncch_romfs); + CryptNcchRegion(romfs,set->cryptoDetails.romfsSize,0x0,set->cryptoDetails.titleId,set->keys->aes.ncchKey1,ncch_romfs); } return 0; @@ -584,12 +579,12 @@ int SetCommonHeaderBasicData(ncch_settings *set, ncch_hdr *hdr) /* Setting ProgramId/TitleId */ - u64 ProgramId = 0; - int result = GetProgramID(&ProgramId,set->rsfSet,false); + u64 programId = 0; + int result = GetProgramID(&programId,set->rsfSet,false); if(result) return result; - u64_to_u8(hdr->programId,ProgramId,LE); - u64_to_u8(hdr->titleId,ProgramId,LE); + u64_to_u8(hdr->programId,programId,LE); + u64_to_u8(hdr->titleId,programId,LE); /* Get Product Code and Maker Code */ if(set->rsfSet->BasicInfo.ProductCode){ @@ -741,7 +736,7 @@ int VerifyNcch(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) } memcpy(exHdr,ncch+ncchInfo->exhdrOffset,ncchInfo->exhdrSize); if(IsNcchEncrypted(hdr)) - CryptNcchRegion((u8*)exHdr,ncchInfo->exhdrSize,0,ncchInfo,keys->aes.ncchKey0,ncch_exhdr); + CryptNcchRegion((u8*)exHdr,ncchInfo->exhdrSize,0,ncchInfo->titleId,keys->aes.ncchKey0,ncch_exhdr); // Checking Exheader Hash to see if decryption was sucessful if(!VerifySha256(exHdr, ncchInfo->exhdrSize, hdr->exhdrHash)){ @@ -768,7 +763,7 @@ int VerifyNcch(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) } memcpy(acexDesc,ncch+ncchInfo->acexOffset,ncchInfo->acexSize); if(IsNcchEncrypted(hdr)) - CryptNcchRegion((u8*)acexDesc,ncchInfo->acexSize,ncchInfo->exhdrSize,ncchInfo,keys->aes.ncchKey0,ncch_exhdr); + CryptNcchRegion((u8*)acexDesc,ncchInfo->acexSize,ncchInfo->exhdrSize,ncchInfo->titleId,keys->aes.ncchKey0,ncch_exhdr); if(CheckAccessDescSignature(acexDesc,keys) != 0 && !keys->rsa.isFalseSign){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] AccessDesc Sigcheck Failed\n"); @@ -799,7 +794,7 @@ int VerifyNcch(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) } memcpy(exefs,ncch+ncchInfo->exefsOffset,ncchInfo->exefsHashDataSize); if(IsNcchEncrypted(hdr)) - CryptNcchRegion(exefs,ncchInfo->exefsHashDataSize,0,ncchInfo,keys->aes.ncchKey0,ncch_exefs); + CryptNcchRegion(exefs,ncchInfo->exefsHashDataSize,0,ncchInfo->titleId,keys->aes.ncchKey0,ncch_exefs); if(!VerifySha256(exefs, ncchInfo->exefsHashDataSize, hdr->exefsHash)){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] ExeFs Hashcheck Failed\n"); free(ncchInfo); @@ -819,7 +814,7 @@ int VerifyNcch(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) } memcpy(romfs,ncch+ncchInfo->romfsOffset,ncchInfo->romfsHashDataSize); if(IsNcchEncrypted(hdr)) - CryptNcchRegion(romfs,ncchInfo->romfsHashDataSize,0,ncchInfo,keys->aes.ncchKey1,ncch_romfs); + CryptNcchRegion(romfs,ncchInfo->romfsHashDataSize,0,ncchInfo->titleId,keys->aes.ncchKey1,ncch_romfs); if(!VerifySha256(romfs,ncchInfo->romfsHashDataSize,hdr->romfsHash)){ if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] RomFs Hashcheck Failed\n"); free(ncchInfo); @@ -873,7 +868,7 @@ int ModifyNcchIds(u8 *ncch, u8 *titleId, u8 *programId, keys_struct *keys) fprintf(stderr,"[NCCH ERROR] Failed to load ncch aes key\n"); return -1; } - CryptNcchRegion(romfs,ncchInfo.romfsSize,0,&ncchInfo,keys->aes.ncchKey1,ncch_romfs); + CryptNcchRegion(romfs,ncchInfo.romfsSize,0,ncchInfo.titleId,keys->aes.ncchKey1,ncch_romfs); } // Editing data and resigning @@ -891,7 +886,7 @@ int ModifyNcchIds(u8 *ncch, u8 *titleId, u8 *programId, keys_struct *keys) fprintf(stderr,"[NCCH ERROR] Failed to load ncch aes key\n"); return -1; } - CryptNcchRegion(romfs,ncchInfo.romfsSize,0,&ncchInfo,keys->aes.ncchKey1,ncch_romfs); + CryptNcchRegion(romfs,ncchInfo.romfsSize,0,ncchInfo.titleId,keys->aes.ncchKey1,ncch_romfs); } return 0; @@ -1016,15 +1011,14 @@ bool SetNcchKeys(keys_struct *keys, ncch_hdr *hdr) int GetNcchInfo(ncch_info *info, ncch_hdr *hdr) { - memcpy(info->titleId,hdr->titleId,8); - memcpy(info->programId,hdr->programId,8); + info->titleId = u8_to_u64(hdr->titleId,LE); + info->programId = u8_to_u64(hdr->programId,LE); - u32 block_size = GetNcchBlockSize(hdr); info->formatVersion = u8_to_u16(hdr->formatVersion,LE); if(!IsCfa(hdr)){ - info->exhdrOffset = 0x200; + info->exhdrOffset = sizeof(ncch_hdr); info->exhdrSize = u8_to_u32(hdr->exhdrSize,LE); info->acexOffset = (info->exhdrOffset + info->exhdrSize); info->acexSize = sizeof(access_descriptor); @@ -1043,53 +1037,17 @@ int GetNcchInfo(ncch_info *info, ncch_hdr *hdr) return 0; } -void CryptNcchRegion(u8 *buffer, u64 size, u64 src_pos, ncch_info *ctx, u8 key[16], u8 type) +void GetNcchAesCounter(u8 ctr[16], u64 titleId, u8 type) { - if(type < 1 || type > 3) - return; - u8 counter[0x10]; - ctr_aes_context aes_ctx; - memset(&aes_ctx,0x0,sizeof(ctr_aes_context)); - - GetNcchAesCounter(ctx,counter,type); - ctr_init_counter(&aes_ctx, key, counter); - - if(src_pos > 0){ - u32 carry = 0; - carry = align(src_pos,0x10); - carry /= 0x10; - ctr_add_counter(&aes_ctx,carry); - } - - ctr_crypt_counter(&aes_ctx, buffer, buffer, size); - return; + clrmem(ctr,16); + u64_to_u8(ctr,titleId,BE); + ctr[8] = type; } -void GetNcchAesCounter(ncch_info *ctx, u8 counter[16], u8 type) +void CryptNcchRegion(u8 *buffer, u64 size, u64 src_pos, u64 titleId, u8 key[16], u8 type) { - u8 *titleId = ctx->titleId; - u32 i; - u32 x = 0; - - memset(counter, 0, 16); - - if (ctx->formatVersion == 2 || ctx->formatVersion == 0) - { - endian_memcpy(counter,titleId,8,LE); - counter[8] = type; - } - else if (ctx->formatVersion == 1) - { - switch(type){ - case ncch_exhdr : x = ctx->exhdrOffset; break; - case ncch_exefs : x = ctx->exefsOffset; break; - case ncch_romfs : x = ctx->romfsOffset; break; - } - for(i=0; i<8; i++) - counter[i] = titleId[i]; - for(i=0; i<4; i++) - counter[12+i] = x>>((3-i)*8); - } - - //memdump(stdout,"CTR: ",counter,16); + u8 ctr[0x10]; + GetNcchAesCounter(ctr,titleId,type); + AesCtr(key,ctr,buffer,buffer,size,src_pos); + return; } \ No newline at end of file diff --git a/makerom/ncch.h b/makerom/ncch.h index be77e95c..f2918830 100644 --- a/makerom/ncch.h +++ b/makerom/ncch.h @@ -75,8 +75,8 @@ typedef struct u64 romfsOffset; u64 romfsSize; u64 romfsHashDataSize; - u8 titleId[8]; - u8 programId[8]; + u64 titleId; + u64 programId; } ncch_info; typedef struct @@ -113,7 +113,7 @@ typedef struct } ncch_hdr; // NCCH Read Functions -int VerifyNcch(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput); +int VerifyNcch(u8 *ncch, keys_struct *keys, bool checkHash, bool suppressOutput); int ModifyNcchIds(u8 *ncch, u8 *titleId, u8 *programId, keys_struct *keys); @@ -130,5 +130,5 @@ u64 GetNcchSize(ncch_hdr* hdr); bool IsNcchEncrypted(ncch_hdr *hdr); bool SetNcchKeys(keys_struct *keys, ncch_hdr *hdr); int GetNcchInfo(ncch_info *ctx, ncch_hdr *header); -void GetNcchAesCounter(ncch_info *ctx, u8 counter[16], u8 type); -void CryptNcchRegion(u8 *buffer, u64 size, u64 src_pos, ncch_info *ctx, u8 key[16], u8 type); \ No newline at end of file +void GetNcchAesCounter(u8 ctr[16], u64 titleId, u8 type); +void CryptNcchRegion(u8 *buffer, u64 size, u64 src_pos, u64 titleId, u8 key[16], u8 type); \ No newline at end of file diff --git a/makerom/ncsd.c b/makerom/ncsd.c index c554eb0e..cc8cfaf5 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -150,9 +150,9 @@ int ImportNcchForCci(cci_settings *set) return 0; } -bool CanCiaBeCci(u16 cat, u16 count, tmd_content_chunk *content) +bool CanCiaBeCci(u64 titleId, u16 count, tmd_content_chunk *content) { - if(cat != PROGRAM_ID_CATEGORY_APPLICATION && cat != PROGRAM_ID_CATEGORY_SYSTEM_APPLICATION) + if(GetTidCategory(titleId) != PROGRAM_ID_CATEGORY_APPLICATION && GetTidCategory(titleId) != PROGRAM_ID_CATEGORY_SYSTEM_APPLICATION) return false; if(count > CCI_MAX_CONTENT) @@ -235,7 +235,6 @@ int ProcessCiaForCci(cci_settings *set) tmd_content_chunk *contentInfo = GetTmdContentInfo(GetCiaTmd(set->content.data)); u64 contentOffset = GetCiaContentOffset((cia_hdr*)set->content.data); - u16 titleCat = (GetTmdTitleId(tmd) >> 32) & 0xffff; u16 contentCount = GetTmdContentCount(tmd); set->romInfo.saveSize = GetTmdSaveSize(tmd); if(set->romInfo.saveSize > 0 && set->romInfo.saveSize < (u64)(128*KB)) @@ -245,7 +244,7 @@ int ProcessCiaForCci(cci_settings *set) else if(set->romInfo.saveSize > (u64)(512*KB)) set->romInfo.saveSize = align(set->romInfo.saveSize,MB); - if(!CanCiaBeCci(titleCat,contentCount,contentInfo)){ + if(!CanCiaBeCci(GetTmdTitleId(tmd),contentCount,contentInfo)){ fprintf(stderr,"[CCI ERROR] This CIA cannot be converted to CCI\n"); return INCOMPAT_CIA; } @@ -269,9 +268,8 @@ int ProcessCiaForCci(cci_settings *set) set->content.dSize[index] = GetTmdContentSize(contentInfo[i]); u8 *content = set->content.data + contentOffset; if(IsTmdContentEncrypted(contentInfo[i])){ - if(canDecrypt){ + if(canDecrypt) CryptContent(content,content,set->content.dSize[index],titleKey,i,DEC); - } else{ fprintf(stderr,"[CCI ERROR] Failed to decrypt CIA content: 0x%08x\n",GetTmdContentId(contentInfo[i])); return INCOMPAT_CIA; diff --git a/makerom/tik.c b/makerom/tik.c index db1f974e..16cb963e 100644 --- a/makerom/tik.c +++ b/makerom/tik.c @@ -11,6 +11,7 @@ u32 GetContentIndexSegNum(cia_settings *set); void SetContentIndexHeader(tik_content_index_hdr *hdr, cia_settings *set); void SetContentIndexData(tik_content_index_struct *data, cia_settings *set); +int CryptTitleKey(u8 *input, u8 *output, u8 *titleId, keys_struct *keys, u8 mode); int BuildTicket(cia_settings *set) { @@ -67,7 +68,7 @@ void SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset) // Crypt TitleKey if(ciaset->content.encryptCia) - CryptTitleKey(hdr->encryptedTitleKey, ciaset->common.titleKey, hdr->titleId, ciaset->keys, ENC); + CryptTitleKey(ciaset->common.titleKey, hdr->encryptedTitleKey, hdr->titleId, ciaset->keys, ENC); else rndset(hdr->encryptedTitleKey,AES_128_KEY_SIZE); } @@ -85,21 +86,15 @@ int SignTicketHeader(buffer_struct *tik, keys_struct *keys) return ctr_sig(data,len,sig->data,keys->rsa.xsPub,keys->rsa.xsPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } -int CryptTitleKey(u8 *encKey, u8 *decKey, u8 *titleId, keys_struct *keys, u8 mode) +int CryptTitleKey(u8 *input, u8 *output, u8 *titleId, keys_struct *keys, u8 mode) { //Generating IV u8 iv[16]; - memset(&iv,0x0,16); + clrmem(&iv,16); memcpy(iv,titleId,0x8); - - //Setting up Aes Context - ctr_aes_context ctx; - clrmem(&ctx,sizeof(ctr_aes_context)); - + //Crypting TitleKey - ctr_init_aes_cbc(&ctx,keys->aes.commonKey[keys->aes.currentCommonKey],iv,mode); - if(mode == ENC) ctr_aes_cbc(&ctx,decKey,encKey,0x10,ENC); - else ctr_aes_cbc(&ctx,encKey,decKey,0x10,DEC); + AesCbc(keys->aes.commonKey[keys->aes.currentCommonKey],iv,input,output,0x10,mode); // Return return 0; diff --git a/makerom/tik_build.h b/makerom/tik_build.h index ecea0222..471f56e6 100644 --- a/makerom/tik_build.h +++ b/makerom/tik_build.h @@ -1,16 +1,5 @@ #pragma once #include "tik.h" -static const unsigned char default_contentIndex[0x30] = -{ - 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0xAC, - 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x84, - 0x00, 0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 -}; - // Prototypes -int BuildTicket(cia_settings *ciaset); -int CryptTitleKey(u8 *EncTitleKey, u8 *DecTitleKey, u8 *TitleID, keys_struct *keys, u8 mode); \ No newline at end of file +int BuildTicket(cia_settings *ciaset); \ No newline at end of file diff --git a/makerom/tmd.c b/makerom/tmd.c index 6abb6e53..f3b7f114 100644 --- a/makerom/tmd.c +++ b/makerom/tmd.c @@ -175,7 +175,5 @@ bool IsTmdContentEncrypted(tmd_content_chunk info) bool ValidateTmdContent(u8 *data, tmd_content_chunk info) { - u8 hash[32]; - ctr_sha(data,GetTmdContentSize(info),hash,CTR_SHA_256); - return memcmp(hash,GetTmdContentHash(&info),32) == 0; + return VerifySha256(data, GetTmdContentSize(info), GetTmdContentHash(&info)); } \ No newline at end of file From 475cda33ed0ae7514a7f4414728a11e1632af79e Mon Sep 17 00:00:00 2001 From: applestash Date: Sun, 7 Sep 2014 21:47:34 +1000 Subject: [PATCH 053/317] ctrtool: added cci ncch selection --- ctrtool/main.c | 7 ++++--- ctrtool/ncsd.c | 15 +++++++++++++-- ctrtool/ncsd.h | 2 ++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/ctrtool/main.c b/ctrtool/main.c index f80823a7..9f2b766a 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -61,7 +61,7 @@ static void usage(const char *argv0) "LZSS options:\n" " --lzssout=file Specify lzss output file\n" "CXI/CCI options:\n" - " -n, --ncch=offs Specify offset for NCCH header.\n" + " -n, --ncch=index Specify NCCH partition index.\n" " --exefs=file Specify ExeFS file path.\n" " --exefsdir=dir Specify ExeFS directory path.\n" " --romfs=file Specify RomFS file path.\n" @@ -96,7 +96,7 @@ int main(int argc, char* argv[]) u8 magic[4]; char infname[512]; int c; - u32 ncchoffset = ~0; + u32 ncchindex = 0; char keysetfname[512] = "keys.xml"; keyset tmpkeys; unsigned int checkkeysetfile = 0; @@ -180,7 +180,7 @@ int main(int argc, char* argv[]) break; case 'n': - ncchoffset = strtoul(optarg, 0, 0); + ncchindex = strtoul(optarg, 0, 0); break; case 'k': @@ -331,6 +331,7 @@ int main(int argc, char* argv[]) ncsd_init(&ncsdctx); ncsd_set_file(&ncsdctx, ctx.infile); ncsd_set_size(&ncsdctx, ctx.infilesize); + ncsd_set_ncch_index(&ncsdctx, ncchindex); ncsd_set_usersettings(&ncsdctx, &ctx.usersettings); ncsd_process(&ncsdctx, ctx.actions); diff --git a/ctrtool/ncsd.c b/ctrtool/ncsd.c index 07c211f6..cfa55a34 100644 --- a/ctrtool/ncsd.c +++ b/ctrtool/ncsd.c @@ -28,6 +28,11 @@ void ncsd_set_size(ncsd_context* ctx, u32 size) ctx->size = size; } +void ncsd_set_ncch_index(ncsd_context* ctx, u32 ncch_index) +{ + ctx->ncch_index = ncch_index; +} + void ncsd_set_usersettings(ncsd_context* ctx, settings* usersettings) { ctx->usersettings = usersettings; @@ -74,9 +79,15 @@ void ncsd_process(ncsd_context* ctx, u32 actions) if (actions & InfoFlag) ncsd_print(ctx); + if(ctx->ncch_index > 7 || ctx->header.partitiongeometry[ctx->ncch_index].size == 0) + { + fprintf(stderr," ERROR NCSD partition %d, does not exist\n",ctx->ncch_index); + return; + } + ncch_set_file(&ctx->ncch, ctx->file); - ncch_set_offset(&ctx->ncch, ctx->header.partitiongeometry[0].offset * ncsd_get_mediaunit_size(ctx)); - ncch_set_size(&ctx->ncch, ctx->header.partitiongeometry[0].size * ncsd_get_mediaunit_size(ctx)); + ncch_set_offset(&ctx->ncch, ctx->header.partitiongeometry[ctx->ncch_index].offset * ncsd_get_mediaunit_size(ctx)); + ncch_set_size(&ctx->ncch, ctx->header.partitiongeometry[ctx->ncch_index].size * ncsd_get_mediaunit_size(ctx)); ncch_set_usersettings(&ctx->ncch, ctx->usersettings); ncch_process(&ctx->ncch, actions); } diff --git a/ctrtool/ncsd.h b/ctrtool/ncsd.h index d8862139..58b85061 100644 --- a/ctrtool/ncsd.h +++ b/ctrtool/ncsd.h @@ -35,6 +35,7 @@ typedef struct FILE* file; u32 offset; u32 size; + u32 ncch_index; ctr_ncsdheader header; settings* usersettings; int headersigcheck; @@ -45,6 +46,7 @@ typedef struct 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_ncch_index(ncsd_context* ctx, u32 ncch_index); 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); From 7113ade91dd30dc4b2c3c74cacf6231bf4f7eca4 Mon Sep 17 00:00:00 2001 From: applestash Date: Sun, 7 Sep 2014 22:39:02 +1000 Subject: [PATCH 054/317] makerom: exefs corrected max exefs sections to 8 --- makerom/exefs.c | 11 +++++------ makerom/exefs.h | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/makerom/exefs.c b/makerom/exefs.c index 1d3f2304..591d3f55 100644 --- a/makerom/exefs.c +++ b/makerom/exefs.c @@ -65,19 +65,18 @@ u32 PredictExeFS_Size(exefs_buildctx *ctx) int GenerateExeFS_Header(exefs_buildctx *ctx, u8 *outbuff) { + exefs_hdr *exefs = (exefs_hdr*)outbuff; for(int i = 0; i < ctx->fileCount; i++){ if(i == 0) ctx->fileOffset[i] = 0; else ctx->fileOffset[i] = align((ctx->fileOffset[i-1]+ctx->fileSize[i-1]),ctx->blockSize); - memcpy(ctx->fileHdr[i].name,ctx->fileName[i],8); - u32_to_u8(ctx->fileHdr[i].offset,ctx->fileOffset[i],LE); - u32_to_u8(ctx->fileHdr[i].size,ctx->fileSize[i],LE); - ctr_sha(ctx->file[i],ctx->fileSize[i],ctx->fileHashes[9-i],CTR_SHA_256); + memcpy(exefs->fileHdr[i].name,ctx->fileName[i],8); + u32_to_u8(exefs->fileHdr[i].offset,ctx->fileOffset[i],LE); + u32_to_u8(exefs->fileHdr[i].size,ctx->fileSize[i],LE); + ctr_sha(ctx->file[i],ctx->fileSize[i],exefs->fileHashes[MAX_EXEFS_SECTIONS-1-i],CTR_SHA_256); } - memcpy(outbuff,ctx->fileHdr,sizeof(exefs_filehdr)*10); - memcpy(outbuff+0xc0,ctx->fileHashes,0x20*10); return 0; } diff --git a/makerom/exefs.h b/makerom/exefs.h index f6e6c136..75b61f92 100644 --- a/makerom/exefs.h +++ b/makerom/exefs.h @@ -1,6 +1,6 @@ #pragma once -#define MAX_EXEFS_SECTIONS 10 // DO NOT CHANGE +#define MAX_EXEFS_SECTIONS 8 typedef struct { @@ -12,6 +12,6 @@ typedef struct typedef struct { exefs_filehdr fileHdr[MAX_EXEFS_SECTIONS]; - u8 reserved[0x20]; + u8 reserved[0x80]; u8 fileHashes[MAX_EXEFS_SECTIONS][0x20]; } exefs_hdr; From 5c2144187762cad5d63bdec652eb7bd357534fac Mon Sep 17 00:00:00 2001 From: applestash Date: Mon, 8 Sep 2014 11:20:50 +1000 Subject: [PATCH 055/317] cleaned makefiles --- ctrtool/Makefile | 2 +- makerom/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 5fdb2765..f1a4e603 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -1,7 +1,7 @@ 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++ +LIBS = -static-libstdc++ CXXFLAGS = -I. CFLAGS = -Wall -Wno-unused-variable -Wno-unused-but-set-variable -I. OUTPUT = ctrtool diff --git a/makerom/Makefile b/makerom/Makefile index 48ecc523..4cf0a1c9 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -9,7 +9,7 @@ LIB_API_OBJS = crypto.o yaml_parser.o blz.o OBJS = makerom.o $(UTILS_OBJS) $(LIB_API_OBJS) $(SETTINGS_OBJS) $(NCSD_OBJS) $(NCCH_OBJS) $(CIA_OBJS) # Libraries -POLAR_OBJS = polarssl/aes.o polarssl/bignum.o polarssl/rsa.o polarssl/sha1.o polarssl/sha2.o polarssl/padlock.o polarssl/md.o polarssl/md_wrap.o polarssl/md2.o polarssl/md4.o polarssl/md5.o polarssl/sha4.o polarssl/base64.o polarssl/cipher.o polarssl/cipher_wrap.o polarssl/camellia.o polarssl/des.o polarssl/blowfish.o +POLAR_OBJS = polarssl/aes.o polarssl/rsa.o polarssl/sha1.o polarssl/sha2.o polarssl/base64.o polarssl/bignum.o polarssl/padlock.o polarssl/md.o polarssl/md_wrap.o polarssl/md5.o polarssl/sha4.o YAML_OBJS = libyaml/api.o libyaml/dumper.o libyaml/emitter.o libyaml/loader.o libyaml/parser.o libyaml/reader.o libyaml/scanner.o libyaml/writer.o # Compiler Settings From 07aa854e60dbd46c310e144dfc27a077d58b8be3 Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 9 Sep 2014 13:43:17 +1000 Subject: [PATCH 056/317] ctrtool: tmd bug fix --- ctrtool/cia.c | 2 +- ctrtool/tmd.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ctrtool/cia.c b/ctrtool/cia.c index f1e97038..030b09da 100644 --- a/ctrtool/cia.c +++ b/ctrtool/cia.c @@ -222,7 +222,7 @@ void cia_process(cia_context* ctx, u32 actions) 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); + tmd_process(&ctx->tmd, (actions & ~InfoFlag)); if (actions & VerifyFlag) { diff --git a/ctrtool/tmd.c b/ctrtool/tmd.c index d86d4a5f..e6a2b15e 100644 --- a/ctrtool/tmd.c +++ b/ctrtool/tmd.c @@ -42,12 +42,10 @@ void tmd_process(tmd_context* ctx, u32 actions) fseek(ctx->file, ctx->offset, SEEK_SET); fread(ctx->buffer, 1, ctx->size, ctx->file); - /* if (actions & InfoFlag) { tmd_print(ctx); } - */ } } From b4b22944a40508f2117070e107c466f20aadb192 Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 9 Sep 2014 14:12:55 +1000 Subject: [PATCH 057/317] ctrtool: allow specify tik titlekey --- ctrtool/cia.c | 4 +++- ctrtool/keyset.cpp | 12 ++++++++++++ ctrtool/keyset.h | 3 +++ ctrtool/main.c | 3 +++ ctrtool/settings.c | 7 +++++++ ctrtool/settings.h | 1 + 6 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ctrtool/cia.c b/ctrtool/cia.c index 030b09da..c0963a54 100644 --- a/ctrtool/cia.c +++ b/ctrtool/cia.c @@ -217,7 +217,9 @@ void cia_process(cia_context* ctx, u32 actions) if (settings_get_common_key(ctx->usersettings)) tik_get_decrypted_titlekey(&ctx->tik, ctx->titlekey); - + else if(settings_get_title_key(ctx->usersettings)) + memcpy(ctx->titlekey, settings_get_title_key(ctx->usersettings), 16); + tmd_set_file(&ctx->tmd, ctx->file); tmd_set_offset(&ctx->tmd, ctx->offsettmd); tmd_set_size(&ctx->tmd, ctx->sizetmd); diff --git a/ctrtool/keyset.cpp b/ctrtool/keyset.cpp index bc7e23cc..f11e185d 100644 --- a/ctrtool/keyset.cpp +++ b/ctrtool/keyset.cpp @@ -175,6 +175,8 @@ void keyset_merge(keyset* keys, keyset* src) keyset_set_key128(&keys->ncchfixedsystemkey, src->ncchfixedsystemkey.data); if (src->commonkey.valid) keyset_set_key128(&keys->commonkey, src->commonkey.data); + if (src->titlekey.valid) + keyset_set_key128(&keys->titlekey, src->titlekey.data); } void keyset_set_key128(key128* key, unsigned char* keydata) @@ -198,6 +200,16 @@ void keyset_parse_commonkey(keyset* keys, char* keytext, int keylen) keyset_parse_key128(&keys->commonkey, keytext, keylen); } +void keyset_set_titlekey(keyset* keys, unsigned char* keydata) +{ + keyset_set_key128(&keys->titlekey, keydata); +} + +void keyset_parse_titlekey(keyset* keys, char* keytext, int keylen) +{ + keyset_parse_key128(&keys->titlekey, keytext, keylen); +} + void keyset_set_ncchkey(keyset* keys, unsigned char* keydata) { keyset_set_key128(&keys->ncchkey, keydata); diff --git a/ctrtool/keyset.h b/ctrtool/keyset.h index f695f541..2cf28668 100644 --- a/ctrtool/keyset.h +++ b/ctrtool/keyset.h @@ -43,6 +43,7 @@ typedef struct typedef struct { key128 commonkey; + key128 titlekey; key128 ncchkey; key128 ncchfixedsystemkey; rsakey2048 ncsdrsakey; @@ -56,6 +57,8 @@ 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_titlekey(keyset* keys, unsigned char* keydata); +void keyset_parse_titlekey(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); diff --git a/ctrtool/main.c b/ctrtool/main.c index 9f2b766a..98671ab4 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -53,6 +53,7 @@ static void usage(const char *argv0) " -y, --verify Verify hashes and signatures.\n" " --unitsize=size Set media unit size (default 0x200).\n" " --commonkey=key Set common key.\n" + " --titlekey=key Set tik title key.\n" " --ncchkey=key Set ncch key.\n" " --ncchsyskey=key Set ncch fixed system key.\n" " --showkeys Show the keys being used.\n" @@ -146,6 +147,7 @@ int main(int argc, char* argv[]) {"wavloops", 1, NULL, 19}, {"logo", 1, NULL, 20}, {"decompresscode", 0, NULL, 21}, + {"titlekey", 1, NULL, 22}, {NULL}, }; @@ -233,6 +235,7 @@ int main(int argc, char* argv[]) case 19: settings_set_cwav_loopcount(&ctx.usersettings, strtoul(optarg, 0, 0)); break; case 20: settings_set_logo_path(&ctx.usersettings, optarg); break; case 21: ctx.actions |= DecompressCodeFlag; break; + case 22: keyset_parse_titlekey(&tmpkeys, optarg, strlen(optarg)); break; default: usage(argv[0]); diff --git a/ctrtool/settings.c b/ctrtool/settings.c index 888bdc94..cbc7aec1 100644 --- a/ctrtool/settings.c +++ b/ctrtool/settings.c @@ -152,6 +152,13 @@ unsigned char* settings_get_common_key(settings* usersettings) return 0; } +unsigned char* settings_get_title_key(settings* usersettings) +{ + if (usersettings && usersettings->keys.titlekey.valid) + return usersettings->keys.titlekey.data; + else + return 0; +} int settings_get_ignore_programid(settings* usersettings) { diff --git a/ctrtool/settings.h b/ctrtool/settings.h index 39d3c2de..c75bb94b 100644 --- a/ctrtool/settings.h +++ b/ctrtool/settings.h @@ -47,6 +47,7 @@ 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); +unsigned char* settings_get_title_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); From c7c2c3f73e62dfb0430d12480a4e59134da36caf Mon Sep 17 00:00:00 2001 From: applestash Date: Tue, 16 Sep 2014 19:15:55 +1000 Subject: [PATCH 058/317] makerom: fixes Moved away from ctrtool's polarssl api completely. Brought certs.c/h inline with code style, fixed bugs relating to tmd savedata field generation and not recording savedata size from rsf (ncsd.c) --- makerom/certs.c | 142 ++++++++++---------------- makerom/certs.h | 29 +++--- makerom/cia.c | 26 ++--- makerom/crypto.c | 240 ++++++++++++++++++++------------------------ makerom/crypto.h | 53 ++++------ makerom/exefs.c | 2 +- makerom/exheader.c | 12 +-- makerom/ncch.c | 18 ++-- makerom/ncsd.c | 11 +- makerom/romfs_gen.c | 2 +- makerom/tik.c | 12 +-- makerom/tmd.c | 7 +- 12 files changed, 237 insertions(+), 317 deletions(-) diff --git a/makerom/certs.c b/makerom/certs.c index f02b3b5a..199461bb 100644 --- a/makerom/certs.c +++ b/makerom/certs.c @@ -2,57 +2,64 @@ #include "certs.h" // Cert Sizes - -u32 GetCertSize(u8 *cert) -{ - u32 SigSize = 0; - u32 SigPadding = 0; - GetCertSigSectionSizes(&SigSize,&SigPadding,cert); - if(!SigSize || !SigPadding) return 0; - - Cert_Struct *certcore = (Cert_Struct*)(cert+4+SigSize+SigPadding); - - u32 PubKSectionSize = GetCertPubkSectionSize((pubk_types)u8_to_u32(certcore->KeyType,BE)); - - return (4+SigSize+SigPadding+sizeof(Cert_Struct)+PubKSectionSize); -} - -void GetCertSigSectionSizes(u32 *SigSize, u32 *SigPadding, u8 *cert) +void GetCertSigSectionSizes(u32 *sign_size, u32 *sign_padlen, u8 *cert) { - sig_types sig = (sig_types)u8_to_u32(cert,BE); + u32 sig = u8_to_u32(cert,BE); switch(sig){ case RSA_4096_SHA1 : - *SigSize = 0x200; - *SigPadding = 0x3C; + *sign_size = 0x200; + *sign_padlen = 0x3C; break; case RSA_2048_SHA1 : - *SigSize = 0x100; - *SigPadding = 0x3C; + *sign_size = 0x100; + *sign_padlen = 0x3C; break; case ECC_SHA1 : - *SigSize = 0x3C; - *SigPadding = 0x40; + *sign_size = 0x3C; + *sign_padlen = 0x40; break; case RSA_4096_SHA256 : - *SigSize = 0x200; - *SigPadding = 0x3C; + *sign_size = 0x200; + *sign_padlen = 0x3C; break; case RSA_2048_SHA256 : - *SigSize = 0x100; - *SigPadding = 0x3C; + *sign_size = 0x100; + *sign_padlen = 0x3C; break; case ECC_SHA256 : - *SigSize = 0x3C; - *SigPadding = 0x40; + *sign_size = 0x3C; + *sign_padlen = 0x40; break; default : - *SigSize = 0; - *SigPadding = 0; + *sign_size = 0; + *sign_padlen = 0; break; } return; } +u32 GetCertSize(u8 *cert) +{ + u32 sign_size = 0; + u32 sign_padlen = 0; + GetCertSigSectionSizes(&sign_size,&sign_padlen,cert); + if(!sign_size || !sign_padlen) + return 0; + + return sizeof(u32) + sign_size + sign_padlen + sizeof(cert_hdr) + GetCertPubkSectionSize(GetCertPubkType(cert)); +} + + +cert_hdr* GetCertHdr(u8 *cert) +{ + u32 sign_size = 0; + u32 sign_padlen = 0; + GetCertSigSectionSizes(&sign_size,&sign_padlen,cert); + if(!sign_size || !sign_padlen) return NULL; + + return (cert_hdr*)(cert+4+sign_size+sign_padlen); +} + u32 GetCertPubkSectionSize(pubk_types type) { switch(type){ @@ -66,80 +73,41 @@ u32 GetCertPubkSectionSize(pubk_types type) // Issuer/Name Functions u8 *GetCertIssuer(u8 *cert) { - u32 SigSize = 0; - u32 SigPadding = 0; - GetCertSigSectionSizes(&SigSize,&SigPadding,cert); - if(!SigSize || !SigPadding) return 0; - - Cert_Struct *certcore = (Cert_Struct*)(cert+4+SigSize+SigPadding); - return certcore->Issuer; + cert_hdr *hdr = GetCertHdr(cert); + return hdr->issuer; } u8 *GetCertName(u8 *cert) { - u32 SigSize = 0; - u32 SigPadding = 0; - GetCertSigSectionSizes(&SigSize,&SigPadding,cert); - if(!SigSize || !SigPadding) return 0; - - Cert_Struct *certcore = (Cert_Struct*)(cert+4+SigSize+SigPadding); - return certcore->Name; + cert_hdr *hdr = GetCertHdr(cert); + return hdr->name; } -int GenCertChildIssuer(u8 *dest, u8 *cert) +void GenCertChildIssuer(u8 *dest, u8 *cert) { - u8 *issuer = GetCertIssuer(cert); - u8 *name = GetCertName(cert); - - /* - u32 out_size = strlen((char*)issuer) + strlen((char*)name) + 1; - if(out_size > 0x40) return MEM_ERROR; - */ - - snprintf((char*)dest,0x40,"%s-%s",issuer,name); - - /* - strcat((char*)dest,(char*)issuer); - strcat((char*)dest,"-"); - strcat((char*)dest,(char*)name); - */ - return 0; + snprintf((char*)dest,0x40,"%s-%s",GetCertIssuer(cert),GetCertName(cert)); } // Pubk pubk_types GetCertPubkType(u8 *cert) { - u32 SigSize = 0; - u32 SigPadding = 0; - GetCertSigSectionSizes(&SigSize,&SigPadding,cert); - if(!SigSize || !SigPadding) return 0; - - Cert_Struct *certcore = (Cert_Struct*)(cert+4+SigSize+SigPadding); + cert_hdr *hdr = GetCertHdr(cert); - return (pubk_types)u8_to_u32(certcore->KeyType,BE); + return (pubk_types)u8_to_u32(hdr->keyType,BE); } u8 *GetCertPubk(u8 *cert) { - u32 SigSize = 0; - u32 SigPadding = 0; - GetCertSigSectionSizes(&SigSize,&SigPadding,cert); - if(!SigSize || !SigPadding) return 0; - return (cert+4+SigSize+SigPadding+sizeof(Cert_Struct)); + if(!GetCertHdr(cert)) + return NULL; + return ((u8*)GetCertHdr(cert)) + sizeof(cert_hdr); } bool VerifyCert(u8 *cert, u8 *pubk) { - u32 SigSize = 0; - u32 SigPadding = 0; - GetCertSigSectionSizes(&SigSize,&SigPadding,cert); - if(!SigSize || !SigPadding) return 0; - - - u8 *signature = (cert+4); - u8 *data = (cert+4+SigSize+SigPadding); - u32 datasize = sizeof(Cert_Struct) + GetCertPubkSectionSize(GetCertPubkType(cert)); - - int result = ctr_sig(data,datasize,signature,pubk,NULL,u8_to_u32(cert,BE),CTR_RSA_VERIFY); + if(!GetCertHdr(cert)) + return false; + u8 *signature = (cert+sizeof(u32)); + u8 *data = (u8*)GetCertHdr(cert); + u32 datasize = sizeof(cert_hdr) + GetCertPubkSectionSize(GetCertPubkType(cert)); - if(result == 0) return true; - else return false; + return RsaSignVerify(data,datasize,signature,pubk,NULL,u8_to_u32(cert,BE),CTR_RSA_VERIFY); } \ No newline at end of file diff --git a/makerom/certs.h b/makerom/certs.h index 0ab05d15..b88b3324 100644 --- a/makerom/certs.h +++ b/makerom/certs.h @@ -2,41 +2,40 @@ typedef struct { - u8 Issuer[0x40]; - u8 KeyType[4]; - u8 Name[0x40]; - u8 Unknown[4]; -} Cert_Struct; + u8 issuer[0x40]; + u8 keyType[4]; + u8 name[0x40]; + u8 id[4]; +} cert_hdr; typedef struct { - u8 Modulus[0x200]; - u8 PubExponent[4]; - u8 Padding[0x34]; + u8 modulus[0x200]; + u8 pubExponent[4]; + u8 padding[0x34]; } rsa_4096_pubk_struct; typedef struct { - u8 Modulus[0x100]; - u8 PubExponent[4]; - u8 Padding[0x34]; + u8 modulus[0x100]; + u8 pubExponent[4]; + u8 padding[0x34]; } rsa_2048_pubk_struct; typedef struct { - u8 PubK[0x3C]; - u8 Padding[0x3C]; + u8 pubK[0x3C]; + u8 padding[0x3C]; } ecc_pubk_struct; // Cert Sizes u32 GetCertSize(u8 *cert); -void GetCertSigSectionSizes(u32 *SigSize, u32 *SigPadding, u8 *cert); u32 GetCertPubkSectionSize(pubk_types type); // Issuer/Name Functions u8 *GetCertIssuer(u8 *cert); u8 *GetCertName(u8 *cert); -int GenCertChildIssuer(u8 *dest, u8 *cert); +void GenCertChildIssuer(u8 *dest, u8 *cert); // Pubk pubk_types GetCertPubkType(u8 *cert); diff --git a/makerom/cia.c b/makerom/cia.c index a1d182a2..64925c1e 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -17,7 +17,6 @@ const int CIA_ALIGN_SIZE = 0x40; const int CIA_CONTENT_ALIGN = 0x10; // Private Prototypes -void InitCiaSettings(cia_settings *set); void FreeCiaSettings(cia_settings *set); int GetCiaSettings(cia_settings *ciaset, user_settings *usrset); @@ -53,7 +52,6 @@ int build_CIA(user_settings *usrset) } // Get Settings - InitCiaSettings(ciaset); result = GetCiaSettings(ciaset,usrset); if(result) goto finish; @@ -96,11 +94,6 @@ int build_CIA(user_settings *usrset) return result; } -void InitCiaSettings(cia_settings *set) -{ - memset(set,0,sizeof(cia_settings)); -} - void FreeCiaSettings(cia_settings *set) { if(set->content.filePtrs){ @@ -198,8 +191,7 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) ciaset->tik.formatVersion = 1; - int result = GenCertChildIssuer(ciaset->tik.issuer,ciaset->keys->certs.xsCert); - if(result) return result; + GenCertChildIssuer(ciaset->tik.issuer,ciaset->keys->certs.xsCert); // Tmd Stuff if(usrset->cia.contentId[0] > MAX_U32) @@ -209,7 +201,7 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) ciaset->tmd.formatVersion = 1; ciaset->tmd.accessRights = 0; - result = GenCertChildIssuer(ciaset->tmd.issuer,ciaset->keys->certs.cpCert); + GenCertChildIssuer(ciaset->tmd.issuer,ciaset->keys->certs.cpCert); return 0; } @@ -293,14 +285,16 @@ int GetTmdDataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_info *ncch_ctx, u8 * if(IsPatch(GetTidCategory(ciaset->common.titleId))||ciaset->content.IsCfa) ciaset->tmd.savedataSize = 0; - else if(ciaset->content.keyNotFound && ciaset->rsf->SystemControlInfo.SaveDataSize){ // if it's a title which can have save data, but save data size could not be read from exhdr + else if(!ciaset->content.keyNotFound) + ciaset->tmd.savedataSize = (u32)(GetSaveDataSize_frm_exhdr(exhdr) & MAX_U32); + else if(ciaset->rsf->SystemControlInfo.SaveDataSize){ // if it's a title which can have save data, but save data size could not be read from exhdr u64 size = 0; GetSaveDataSizeFromString(&size,ciaset->rsf->SystemControlInfo.SaveDataSize,"CIA"); ciaset->tmd.savedataSize = (u32)(size & MAX_U32); } - else - ciaset->tmd.savedataSize = (u32)(GetSaveDataSize_frm_exhdr(exhdr) & MAX_U32); - + else + ciaset->tmd.savedataSize = 0; + if(ciaset->content.IsCfa||ciaset->content.keyNotFound){ if(ciaset->common.titleVersion[VER_MAJOR] == MAX_U16){ // '-major' wasn't set if(ciaset->content.IsCfa){ // Is a CFA and can be decrypted @@ -569,7 +563,7 @@ u16 SetupVersion(u16 major, u16 minor, u16 micro) void GetContentHashes(cia_settings *ciaset) { for(int i = 0; i < ciaset->content.count; i++) - ctr_sha(ciaset->ciaSections.content.buffer+ciaset->content.offset[i],ciaset->content.size[i],ciaset->content.hash[i],CTR_SHA_256); + ShaCalc(ciaset->ciaSections.content.buffer+ciaset->content.offset[i],ciaset->content.size[i],ciaset->content.hash[i],CTR_SHA_256); } void EncryptContent(cia_settings *ciaset) @@ -653,7 +647,7 @@ int CryptContent(u8 *input, u8 *output, u64 size, u8 *title_key, u16 index, u8 m iv[0] = (index >> 8) & 0xff; iv[1] = index & 0xff; //Crypting content - AesCbc(title_key,iv,input,output,size,mode); + AesCbcCrypt(title_key,iv,input,output,size,mode); return 0; } diff --git a/makerom/crypto.c b/makerom/crypto.c index 71dbdd39..9fd99ee7 100644 --- a/makerom/crypto.c +++ b/makerom/crypto.c @@ -1,14 +1,24 @@ #include "lib.h" #include "crypto.h" +const u8 RSA_PUB_EXP[0x3] = {0x01,0x00,0x01}; +const int HASH_MAX_LEN = 0x20; + +int ctr_rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + bool VerifySha256(void *data, u64 size, u8 hash[32]) { u8 calchash[32]; - ctr_sha(data, size, calchash, CTR_SHA_256); + ShaCalc(data, size, calchash, CTR_SHA_256); return memcmp(hash,calchash,32) == 0; } -void ctr_sha(void *data, u64 size, u8 *hash, int mode) +void ShaCalc(void *data, u64 size, u8 *hash, int mode) { switch(mode){ case(CTR_SHA_1): sha1((u8*)data, size, hash); break; @@ -21,7 +31,7 @@ void SetAesCtrOffset(u8 *ctr, u64 offset) u64_to_u8(ctr+8,u8_to_u64(ctr+8,BE)|align(offset,16)/16,BE); } -void AesCtr(u8 *key, u8 *ctr, u8 *input, u8 *output, u64 length, u64 offset) +void AesCtrCrypt(u8 *key, u8 *ctr, u8 *input, u8 *output, u64 length, u64 offset) { u8 stream[16]; aes_context aes; @@ -37,7 +47,7 @@ void AesCtr(u8 *key, u8 *ctr, u8 *input, u8 *output, u64 length, u64 offset) return; } -void AesCbc(u8 *key, u8 *iv, u8 *input, u8 *output, u64 length, u8 mode) +void AesCbcCrypt(u8 *key, u8 *iv, u8 *input, u8 *output, u64 length, u8 mode) { aes_context aes; clrmem(&aes,sizeof(aes_context)); @@ -56,168 +66,138 @@ void AesCbc(u8 *key, u8 *iv, u8 *input, u8 *output, u64 length, u8 mode) } } -void ctr_rsa_free(ctr_rsa_context* ctx) -{ - rsa_free(&ctx->rsa); -} - -int ctr_rsa_init(ctr_rsa_context* ctx, u8 *modulus, u8 *private_exp, u8 *exponent, u8 rsa_type, u8 mode) +bool RsaKeyInit(rsa_context* ctx, u8 *modulus, u8 *private_exp, u8 *exponent, u8 rsa_type) { // Sanity Check - if(ctx == NULL || modulus == NULL ||(private_exp == NULL && mode == RSAKEY_PRIV) || (exponent == NULL && mode == RSAKEY_PUB)) - return Fail; - rsa_init(&ctx->rsa, RSA_PKCS_V15, 0); + if(!ctx) + return false; + + rsa_init(ctx, RSA_PKCS_V15, 0); + u16 n_size = 0; u16 d_size = 0; u16 e_size = 0; + switch(rsa_type){ case RSA_2048: - ctx->rsa.len = 0x100; + ctx->len = 0x100; n_size = 0x100; d_size = 0x100; e_size = 3; break; case RSA_4096: - ctx->rsa.len = 0x200; + ctx->len = 0x200; n_size = 0x200; d_size = 0x200; e_size = 3; break; - default: return Fail; - } - - switch(mode){ - case(RSAKEY_PUB): - if (mpi_read_binary(&ctx->rsa.N, modulus, n_size)) - goto clean; - if (mpi_read_binary(&ctx->rsa.E, exponent, e_size)) - goto clean; - break; - case(RSAKEY_PRIV): - if (mpi_read_binary(&ctx->rsa.N, modulus, n_size)) - goto clean; - if (mpi_read_binary(&ctx->rsa.D, private_exp, d_size)) - goto clean; - break; - default: return Fail; + default: return false; } + + if (modulus && mpi_read_binary(&ctx->N, modulus, n_size)) + goto clean; + if (exponent && mpi_read_binary(&ctx->E, exponent, e_size)) + goto clean; + if (private_exp && mpi_read_binary(&ctx->D, private_exp, d_size)) + goto clean; + - return Good; + return true; clean: - ctr_rsa_free(ctx); - return Fail; + rsa_free(ctx); + return false; } -int ctr_sig(void *data, u64 size, u8 *signature, u8 *modulus, u8 *private_exp, u32 type, u8 mode) +u8 GetRsaType(u32 sig_type) { - int result = 0; - int hashtype, hashlen, sigtype; - if(data == NULL || signature == NULL || modulus == NULL ||(private_exp == NULL && mode == CTR_RSA_SIGN)) - return Fail; - - switch(type){ + switch(sig_type){ case RSA_4096_SHA1: - hashtype = CTR_SHA_1; - hashlen = 0x14; - sigtype = RSA_4096; case RSA_4096_SHA256: - hashtype = CTR_SHA_256; - hashlen = 0x20; - sigtype = RSA_4096; - break; + return RSA_4096; case RSA_2048_SHA1: - hashtype = CTR_SHA_1; - hashlen = 0x14; - sigtype = RSA_2048; case RSA_2048_SHA256: - hashtype = CTR_SHA_256; - hashlen = 0x20; - sigtype = RSA_2048; - break; + return RSA_2048; + } + return INVALID_SIG_TYPE; +} + +u32 GetSigHashType(u32 sig_type) +{ + switch(sig_type){ + case RSA_4096_SHA1: + case RSA_2048_SHA1: case ECC_SHA1: - hashtype = CTR_SHA_1; - hashlen = 0x14; - sigtype = ECC; + return CTR_SHA_1; + case RSA_4096_SHA256: + case RSA_2048_SHA256: case ECC_SHA256: - hashtype = CTR_SHA_256; - hashlen = 0x20; - sigtype = ECC; - break; - default: return Fail; + return CTR_SHA_256; } - - u8 hash[hashlen]; - memset(hash,0,hashlen); - ctr_sha(data,size,hash,hashtype); - //memdump(stdout,"Data: ",data,size); - //memdump(stdout,"HashFor Sig: ",hash,hashlen); - - if(sigtype == RSA_2048 || sigtype == RSA_4096) - result = ctr_rsa(hash,signature,modulus,private_exp,type,mode); - else if(sigtype == ECC){ - printf("[!] ECC is not yet implemented\n"); - result = Fail; - } - return result; + return 0; } -int ctr_rsa(u8 *hash, u8 *signature, u8 *modulus, u8 *private_exp, u32 type, u8 mode) +int GetRsaHashType(u32 sig_type) { - int result = 0; - // Sanity Check - if(hash == NULL || signature == NULL || modulus == NULL ||(private_exp == NULL && mode == CTR_RSA_SIGN)) - return Fail; - - // Getting details from sig type - int hashtype; - int hashlen; - int sigtype; - switch(type){ - case RSA_4096_SHA1: - hashtype = SIG_RSA_SHA1; - hashlen = 0x14; - sigtype = RSA_4096; - break; - case RSA_4096_SHA256: - hashtype = SIG_RSA_SHA256; - hashlen = 0x14; - sigtype = RSA_4096; - break; - case RSA_2048_SHA1: - hashtype = SIG_RSA_SHA1; - hashlen = 0x20; - sigtype = RSA_2048; - break; - case RSA_2048_SHA256: - hashtype = SIG_RSA_SHA256; - hashlen = 0x20; - sigtype = RSA_2048; - break; - default: return Fail; + switch(sig_type){ + case RSA_4096_SHA1: + case RSA_2048_SHA1: + return SIG_RSA_SHA1; + case RSA_4096_SHA256: + case RSA_2048_SHA256: + return SIG_RSA_SHA256; } - - // Setting up - ctr_rsa_context ctx; - u8 exponent[3] = {0x01,0x00,0x01}; - switch(mode){ - case CTR_RSA_VERIFY: - result = ctr_rsa_init(&ctx,modulus,NULL,(u8*)exponent,sigtype,RSAKEY_PUB); - break; - case CTR_RSA_SIGN: - result = ctr_rsa_init(&ctx,modulus,private_exp,NULL,sigtype,RSAKEY_PRIV); - break; + return 0; +} + +u32 GetSigHashLen(u32 sig_type) +{ + switch(sig_type){ + case RSA_4096_SHA1: + return 0x14; + case RSA_4096_SHA256: + return 0x20; + case RSA_2048_SHA1: + return 0x14; + case RSA_2048_SHA256: + return 0x20; + case ECC_SHA1: + return 0x14; + case ECC_SHA256: + return 0x20; } - if(result)return result; + return 0; +} + +bool CalcHashForSign(void *data, u64 len, u8 *hash, u32 sig_type) +{ + if(GetSigHashType(sig_type) == 0) + return false; + + ShaCalc(data, len, hash, GetSigHashType(sig_type)); - switch(mode){ - case CTR_RSA_VERIFY: - return rsa_pkcs1_verify(&ctx.rsa,RSA_PUBLIC,hashtype,hashlen,hash,signature); - case CTR_RSA_SIGN: - return ctr_rsa_rsassa_pkcs1_v15_sign(&ctx.rsa,RSA_PRIVATE,hashtype,hashlen,hash,signature); - } - return Fail; -} + return true; +} + +int RsaSignVerify(void *data, u64 len, u8 *sign, u8 *mod, u8 *priv_exp, u32 sig_type, u8 rsa_mode) +{ + int rsa_result = 0; + rsa_context ctx; + u8 hash[HASH_MAX_LEN]; + + if(!RsaKeyInit(&ctx, mod, priv_exp, (u8*)RSA_PUB_EXP, GetRsaType(sig_type))) + return -1; + + if(!CalcHashForSign(data, len, hash, sig_type)) + return -1; + if(rsa_mode == CTR_RSA_VERIFY) + rsa_result = rsa_pkcs1_verify(&ctx, RSA_PUBLIC, GetRsaHashType(sig_type), 0, hash, sign); + else // CTR_RSA_SIGN + rsa_result = ctr_rsa_rsassa_pkcs1_v15_sign(&ctx, RSA_PRIVATE, GetRsaHashType(sig_type), 0, hash, sign); + + rsa_free(&ctx); + return rsa_result; +} /** * Hacked from rsa.c, polarssl doesn't like generating signatures when only D and N are present diff --git a/makerom/crypto.h b/makerom/crypto.h index 9a29a107..617fdc84 100644 --- a/makerom/crypto.h +++ b/makerom/crypto.h @@ -14,25 +14,26 @@ typedef enum RSA_4096_SHA256 = 0x00010003, RSA_2048_SHA256 = 0x00010004, ECC_SHA256 = 0x00010005 -} sig_types; +} ctr_sig_types; typedef enum { - RSA_2048 = 0, - RSA_4096 = 1, - ECC = 2, -} ctr_sig_types; + CTR_RSA_VERIFY, + CTR_RSA_SIGN, +} ctr_rsa_mode; typedef enum { - CTR_RSA_VERIFY = 0, - CTR_RSA_SIGN = 1, -} ctr_rsa_functions; + RSA_4096, + RSA_2048, + ECC, + INVALID_SIG_TYPE, +} sig_types; typedef enum { - CTR_SHA_1 = 1, - CTR_SHA_256 = 256, + CTR_SHA_1, + CTR_SHA_256, } ctr_sha_modes; typedef enum @@ -46,42 +47,22 @@ typedef enum { ENC, DEC -} aescbcmode; +} aes_mode; -typedef enum -{ - RSAKEY_INVALID, - RSAKEY_PRIV, - RSAKEY_PUB -} rsakeytype; - -typedef struct -{ - rsa_context rsa; -} ctr_rsa_context; #ifdef __cplusplus extern "C" { #endif // SHA bool VerifySha256(void *data, u64 size, u8 hash[32]); -void ctr_sha(void *data, u64 size, u8 *hash, int mode); +void ShaCalc(void *data, u64 size, u8 *hash, int mode); // AES -void AesCtr(u8 *key, u8 *ctr, u8 *input, u8 *output, u64 length, u64 offset); -void AesCbc(u8 *key, u8 *iv, u8 *input, u8 *output, u64 length, u8 mode); +void AesCtrCrypt(u8 *key, u8 *ctr, u8 *input, u8 *output, u64 length, u64 offset); +void AesCbcCrypt(u8 *key, u8 *iv, u8 *input, u8 *output, u64 length, u8 mode); // RSA -void ctr_rsa_free(ctr_rsa_context* ctx); -int ctr_rsa_init(ctr_rsa_context* ctx, u8 *modulus, u8 *private_exp, u8 *exponent, u8 rsa_type, u8 mode); -int ctr_rsa(u8 *hash, u8 *signature, u8 *modulus, u8 *private_exp, u32 type, u8 mode); -int ctr_rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, - int mode, - int hash_id, - unsigned int hashlen, - const unsigned char *hash, - unsigned char *sig ); +int RsaSignVerify(void *data, u64 len, u8 *sign, u8 *mod, u8 *priv_exp, u32 sig_type, u8 rsa_mode); + -// Signature Functions -int ctr_sig(void *data, u64 size, u8 *signature, u8 *modulus, u8 *private_exp, u32 type, u8 mode); #ifdef __cplusplus } diff --git a/makerom/exefs.c b/makerom/exefs.c index 591d3f55..e11f5681 100644 --- a/makerom/exefs.c +++ b/makerom/exefs.c @@ -75,7 +75,7 @@ int GenerateExeFS_Header(exefs_buildctx *ctx, u8 *outbuff) memcpy(exefs->fileHdr[i].name,ctx->fileName[i],8); u32_to_u8(exefs->fileHdr[i].offset,ctx->fileOffset[i],LE); u32_to_u8(exefs->fileHdr[i].size,ctx->fileSize[i],LE); - ctr_sha(ctx->file[i],ctx->fileSize[i],exefs->fileHashes[MAX_EXEFS_SECTIONS-1-i],CTR_SHA_256); + ShaCalc(ctx->file[i],ctx->fileSize[i],exefs->fileHashes[MAX_EXEFS_SECTIONS-1-i],CTR_SHA_256); } return 0; } diff --git a/makerom/exheader.c b/makerom/exheader.c index d7dba89a..730a03aa 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -53,16 +53,16 @@ int get_ExHeaderARM9AccessControlInfo(exhdr_ARM9AccessControlInfo *arm9, rsf_set /* ExHeader Signature Functions */ int SignAccessDesc(access_descriptor *acexDesc, keys_struct *keys) { - u8 *AccessDesc = (u8*) &acexDesc->ncchRsaPubKey; - u8 *Signature = (u8*) &acexDesc->signature; - return ctr_sig(AccessDesc,0x300,Signature,keys->rsa.acexPub,keys->rsa.acexPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + u8 *data = (u8*) &acexDesc->ncchRsaPubKey; + u8 *sign = (u8*) &acexDesc->signature; + return RsaSignVerify(data,0x300,sign,keys->rsa.acexPub,keys->rsa.acexPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } int CheckAccessDescSignature(access_descriptor *acexDesc, keys_struct *keys) { - u8 *AccessDesc = (u8*) &acexDesc->ncchRsaPubKey; - u8 *Signature = (u8*) &acexDesc->signature; - return ctr_sig(AccessDesc,0x300,Signature,keys->rsa.acexPub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); + u8 *data = (u8*) &acexDesc->ncchRsaPubKey; + u8 *sign = (u8*) &acexDesc->signature; + return RsaSignVerify(data,0x300,sign,keys->rsa.acexPub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); } /* ExHeader Build Functions */ diff --git a/makerom/ncch.c b/makerom/ncch.c index 5688842e..e79e041f 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -34,22 +34,22 @@ bool IsValidProductCode(char *ProductCode, bool FreeProductCode); // Code int SignCFA(ncch_hdr *hdr, keys_struct *keys) { - return ctr_sig(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cciCfaPub,keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + return RsaSignVerify(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cciCfaPub,keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } int CheckCFASignature(ncch_hdr *hdr, keys_struct *keys) { - return ctr_sig(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cciCfaPub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); + return RsaSignVerify(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cciCfaPub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); } int SignCXI(ncch_hdr *hdr, keys_struct *keys) { - return ctr_sig(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cxiHdrPub,keys->rsa.cxiHdrPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + return RsaSignVerify(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cxiHdrPub,keys->rsa.cxiHdrPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } int CheckCXISignature(ncch_hdr *hdr, u8 *pubk) { - int result = ctr_sig(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),pubk,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); + int result = RsaSignVerify(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),pubk,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); return result; } @@ -500,13 +500,13 @@ int FinaliseNcch(ncch_settings *set) // Taking Hashes if(set->cryptoDetails.exhdrSize) - ctr_sha(exhdr,set->cryptoDetails.exhdrSize,hdr->exhdrHash,CTR_SHA_256); + ShaCalc(exhdr,set->cryptoDetails.exhdrSize,hdr->exhdrHash,CTR_SHA_256); if(set->cryptoDetails.logoSize) - ctr_sha(logo,set->cryptoDetails.logoSize,hdr->logoHash,CTR_SHA_256); + ShaCalc(logo,set->cryptoDetails.logoSize,hdr->logoHash,CTR_SHA_256); if(set->cryptoDetails.exefsHashDataSize) - ctr_sha(exefs,set->cryptoDetails.exefsHashDataSize,hdr->exefsHash,CTR_SHA_256); + ShaCalc(exefs,set->cryptoDetails.exefsHashDataSize,hdr->exefsHash,CTR_SHA_256); if(set->cryptoDetails.romfsHashDataSize) - ctr_sha(romfs,set->cryptoDetails.romfsHashDataSize,hdr->romfsHash,CTR_SHA_256); + ShaCalc(romfs,set->cryptoDetails.romfsHashDataSize,hdr->romfsHash,CTR_SHA_256); // Signing NCCH int sig_result = Good; @@ -1048,6 +1048,6 @@ void CryptNcchRegion(u8 *buffer, u64 size, u64 src_pos, u64 titleId, u8 key[16], { u8 ctr[0x10]; GetNcchAesCounter(ctr,titleId,type); - AesCtr(key,ctr,buffer,buffer,size,src_pos); + AesCtrCrypt(key,ctr,buffer,buffer,size,src_pos); return; } \ No newline at end of file diff --git a/makerom/ncsd.c b/makerom/ncsd.c index cc8cfaf5..39d70364 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -10,7 +10,6 @@ #include "cardinfo.h" #include "titleid.h" - const int NCCH0_OFFSET = 0x4000; const int CCI_BLOCK_SIZE = 0x200; @@ -288,15 +287,15 @@ int ProcessCiaForCci(cci_settings *set) int ImportCciNcch(cci_settings *set) { + if(set->rsf->SystemControlInfo.SaveDataSize) + GetSaveDataSizeFromString(&set->romInfo.saveSize,set->rsf->SystemControlInfo.SaveDataSize,"CCI"); + if(set->content.dataType == infile_ncch) return ImportNcchForCci(set); else if(set->content.dataType == infile_cia) return ProcessCiaForCci(set); else - fprintf(stderr,"[CCI ERROR] Unrecognised input data type\n"); - - if(set->rsf->SystemControlInfo.SaveDataSize) - GetSaveDataSizeFromString(&set->romInfo.saveSize,set->rsf->SystemControlInfo.SaveDataSize,"CCI"); + fprintf(stderr,"[CCI ERROR] Unrecognised input data type\n"); return FAILED_TO_IMPORT_FILE; } @@ -594,7 +593,7 @@ int GenCciHdr(cci_settings *set) SetCciNcchInfo(hdr,set); // Sign Header - ctr_sig(&hdr->magic,sizeof(cci_hdr)-RSA_2048_KEY_SIZE,hdr->signature,set->keys->rsa.cciCfaPub,set->keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + RsaSignVerify(&hdr->magic,sizeof(cci_hdr)-RSA_2048_KEY_SIZE,hdr->signature,set->keys->rsa.cciCfaPub,set->keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN); return 0; } diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 49718950..6a9498f0 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -467,7 +467,7 @@ void GenIvfcHashTree(romfs_buildctx *ctx) for(u32 j = 0; j < numHashes; j++){ u8 *datapos = (u8*)(ctx->level[i+1].pos + ROMFS_BLOCK_SIZE * j); u8 *hashpos = (u8*)(ctx->level[i].pos + 0x20 * j); - ctr_sha(datapos, ROMFS_BLOCK_SIZE, hashpos, CTR_SHA_256); + ShaCalc(datapos, ROMFS_BLOCK_SIZE, hashpos, CTR_SHA_256); } } diff --git a/makerom/tik.c b/makerom/tik.c index 16cb963e..9267baa9 100644 --- a/makerom/tik.c +++ b/makerom/tik.c @@ -15,9 +15,8 @@ int CryptTitleKey(u8 *input, u8 *output, u8 *titleId, keys_struct *keys, u8 mode int BuildTicket(cia_settings *set) { - int result = 0; - result = SetupTicketBuffer(set); - if(result) return result; + if(SetupTicketBuffer(set)) + return MEM_ERROR; // Setting Ticket Struct Ptrs buffer_struct *tik = &set->ciaSections.tik; @@ -31,8 +30,7 @@ int BuildTicket(cia_settings *set) SetContentIndexHeader(idxHdr,set); SetContentIndexData(idxData,set); - result = SignTicketHeader(tik,set->keys); - return 0; + return SignTicketHeader(tik,set->keys); } int SetupTicketBuffer(cia_settings *set) @@ -83,7 +81,7 @@ int SignTicketHeader(buffer_struct *tik, keys_struct *keys) clrmem(sig,sizeof(tik_signature)); u32_to_u8(sig->sigType,RSA_2048_SHA256,BE); - return ctr_sig(data,len,sig->data,keys->rsa.xsPub,keys->rsa.xsPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + return RsaSignVerify(data,len,sig->data,keys->rsa.xsPub,keys->rsa.xsPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } int CryptTitleKey(u8 *input, u8 *output, u8 *titleId, keys_struct *keys, u8 mode) @@ -94,7 +92,7 @@ int CryptTitleKey(u8 *input, u8 *output, u8 *titleId, keys_struct *keys, u8 mode memcpy(iv,titleId,0x8); //Crypting TitleKey - AesCbc(keys->aes.commonKey[keys->aes.currentCommonKey],iv,input,output,0x10,mode); + AesCbcCrypt(keys->aes.commonKey[keys->aes.currentCommonKey],iv,input,output,0x10,mode); // Return return 0; diff --git a/makerom/tmd.c b/makerom/tmd.c index f3b7f114..9407ef60 100644 --- a/makerom/tmd.c +++ b/makerom/tmd.c @@ -62,7 +62,7 @@ int SetupTMDHeader(tmd_hdr *hdr, tmd_content_info_record *info_record, cia_setti u32_to_u8(hdr->accessRights,ciaset->tmd.accessRights,BE); u16_to_u8(hdr->titleVersion,ciaset->tmd.version,BE); u16_to_u8(hdr->contentCount,ciaset->content.count,BE); - ctr_sha(info_record,sizeof(tmd_content_info_record)*64,hdr->infoRecordHash,CTR_SHA_256); + ShaCalc(info_record,sizeof(tmd_content_info_record)*64,hdr->infoRecordHash,CTR_SHA_256); return 0; } @@ -70,7 +70,8 @@ int SignTMDHeader(tmd_hdr *hdr, tmd_signature *sig, keys_struct *keys) { clrmem(sig,sizeof(tmd_signature)); u32_to_u8(sig->sigType,RSA_2048_SHA256,BE); - return ctr_sig((u8*)hdr,sizeof(tmd_hdr),sig->data,keys->rsa.cpPub,keys->rsa.cpPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + + return RsaSignVerify((u8*)hdr,sizeof(tmd_hdr),sig->data,keys->rsa.cpPub,keys->rsa.cpPvt,RSA_2048_SHA256,CTR_RSA_SIGN); } int SetupTMDInfoRecord(tmd_content_info_record *info_record, u8 *content_record, u16 ContentCount) @@ -78,7 +79,7 @@ int SetupTMDInfoRecord(tmd_content_info_record *info_record, u8 *content_record, clrmem(info_record,sizeof(tmd_content_info_record)*0x40); u16_to_u8(info_record->contentIndexOffset,0x0,BE); u16_to_u8(info_record->contentCommandCount,ContentCount,BE); - ctr_sha(content_record,sizeof(tmd_content_chunk)*ContentCount,info_record->contentChunkHash,CTR_SHA_256); + ShaCalc(content_record,sizeof(tmd_content_chunk)*ContentCount,info_record->contentChunkHash,CTR_SHA_256); return 0; } From 1e8e4666b591905a8e367daa7401f29843117d71 Mon Sep 17 00:00:00 2001 From: applestash Date: Wed, 17 Sep 2014 11:23:07 +1000 Subject: [PATCH 059/317] makerom: fixed cxi rebuild bug --- makerom/exheader.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/makerom/exheader.c b/makerom/exheader.c index 730a03aa..ac902768 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -126,23 +126,20 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * exhdrset->exHdr = (extended_hdr*)ncchset->sections.exhdr.buffer; exhdrset->acexDesc = (access_descriptor*)ncchset->sections.acexDesc.buffer; - /* Set Code Info if Code Section was built not imported */ - if(ncchset->options.IsBuildingCodeSection){ - /* BSS Size */ - u32_to_u8(exhdrset->exHdr->codeSetInfo.bssSize,ncchset->codeDetails.bssSize,LE); - /* Data */ - u32_to_u8(exhdrset->exHdr->codeSetInfo.data.address,ncchset->codeDetails.rwAddress,LE); - u32_to_u8(exhdrset->exHdr->codeSetInfo.data.codeSize,ncchset->codeDetails.rwSize,LE); - u32_to_u8(exhdrset->exHdr->codeSetInfo.data.numMaxPages,ncchset->codeDetails.rwMaxPages,LE); - /* RO */ - u32_to_u8(exhdrset->exHdr->codeSetInfo.rodata.address,ncchset->codeDetails.roAddress,LE); - u32_to_u8(exhdrset->exHdr->codeSetInfo.rodata.codeSize,ncchset->codeDetails.roSize,LE); - u32_to_u8(exhdrset->exHdr->codeSetInfo.rodata.numMaxPages,ncchset->codeDetails.roMaxPages,LE); - /* Text */ - u32_to_u8(exhdrset->exHdr->codeSetInfo.text.address,ncchset->codeDetails.textAddress,LE); - u32_to_u8(exhdrset->exHdr->codeSetInfo.text.codeSize,ncchset->codeDetails.textSize,LE); - u32_to_u8(exhdrset->exHdr->codeSetInfo.text.numMaxPages,ncchset->codeDetails.textMaxPages,LE); - } + /* BSS Size */ + u32_to_u8(exhdrset->exHdr->codeSetInfo.bssSize,ncchset->codeDetails.bssSize,LE); + /* Data */ + u32_to_u8(exhdrset->exHdr->codeSetInfo.data.address,ncchset->codeDetails.rwAddress,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.data.codeSize,ncchset->codeDetails.rwSize,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.data.numMaxPages,ncchset->codeDetails.rwMaxPages,LE); + /* RO */ + u32_to_u8(exhdrset->exHdr->codeSetInfo.rodata.address,ncchset->codeDetails.roAddress,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.rodata.codeSize,ncchset->codeDetails.roSize,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.rodata.numMaxPages,ncchset->codeDetails.roMaxPages,LE); + /* Text */ + u32_to_u8(exhdrset->exHdr->codeSetInfo.text.address,ncchset->codeDetails.textAddress,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.text.codeSize,ncchset->codeDetails.textSize,LE); + u32_to_u8(exhdrset->exHdr->codeSetInfo.text.numMaxPages,ncchset->codeDetails.textMaxPages,LE); /* Set Simple Flags */ if(ncchset->options.CompressCode) From 744abbe8525ef377ef6bebc2958b736b802a4bb4 Mon Sep 17 00:00:00 2001 From: applestash Date: Wed, 17 Sep 2014 11:30:19 +1000 Subject: [PATCH 060/317] makerom: new cmd line arg introduced "-i", works the same way as "-content", but is shorter and is what ctr_makecia uses --- makerom/user_settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 97505ab0..ff987a13 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -577,7 +577,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } // Other Setting - else if(strcmp(argv[i],"-content") == 0){ + else if(strcmp(argv[i],"-content") == 0 || strcmp(argv[i],"-i") == 0){ if(ParamNum != 1){ PrintArgReqParam(argv[i],1); return USR_ARG_REQ_PARAM; From 274b523e9c6a81b68eefaa0de5749cb20677e6b6 Mon Sep 17 00:00:00 2001 From: applestash Date: Thu, 18 Sep 2014 11:03:57 +1000 Subject: [PATCH 061/317] makerom: misc and small changes Altered behaviour to make CIA file generated to more closely match CIAs made by Nintendo's private CIA generator. As seen in gamecard updates. --- makerom/cia.c | 110 +++++++++++++++++++++++++++----------------- makerom/cia_build.h | 2 +- makerom/ncch.c | 2 + 3 files changed, 71 insertions(+), 43 deletions(-) diff --git a/makerom/cia.c b/makerom/cia.c index 64925c1e..251e027e 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -175,7 +175,7 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) } // Ticket Data - ciaset->tik.ticketId = u64GetRand(); + ciaset->tik.ticketId = u64GetRand() & 0x0004FFFFFFFFFFFF; ciaset->tik.deviceId = usrset->cia.deviceId; ciaset->tik.eshopAccId = usrset->cia.eshopAccId; ciaset->tik.licenceType = lic_Permanent; @@ -235,9 +235,9 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) GetNcchInfo(info,hdr); /* Verify Ncch0 (Sig&Hash Checks) */ - int result = VerifyNcch(ncch0,ciaset->keys,false,true); + int result = VerifyNcch(ncch0, ciaset->keys, false, ciaset->verbose == false); if(result == UNABLE_TO_LOAD_NCCH_KEY){ - ciaset->content.keyNotFound = true; + ciaset->content.keyFound = false; if(!ciaset->content.IsCfa){ fprintf(stderr,"[CIA WARNING] CXI AES Key could not be loaded\n"); fprintf(stderr," Meta Region, SaveDataSize, Remaster Version cannot be obtained\n"); @@ -254,7 +254,7 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) /* Getting ncch key */ u8 *ncchkey = NULL; - if(!ciaset->content.keyNotFound && IsNcchEncrypted(hdr)){ + if(ciaset->content.keyFound && IsNcchEncrypted(hdr)){ SetNcchKeys(ciaset->keys,hdr); ncchkey = ciaset->keys->aes.ncchKey0; if(ciaset->verbose){ @@ -278,90 +278,116 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) int GetTmdDataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_info *ncch_ctx, u8 *key) { - extended_hdr *exhdr = malloc(sizeof(extended_hdr)); - memcpy(exhdr,ncch+ncch_ctx->exhdrOffset,sizeof(extended_hdr)); - if(key != NULL) - CryptNcchRegion((u8*)exhdr,sizeof(extended_hdr),0,ncch_ctx->titleId,key,ncch_exhdr); + int result = 0; + extended_hdr *exhdr = NULL; + + if(!ciaset->content.IsCfa && ciaset->content.keyFound){ + exhdr = malloc(sizeof(extended_hdr)); + memcpy(exhdr,ncch+ncch_ctx->exhdrOffset,sizeof(extended_hdr)); + if(key != NULL) + CryptNcchRegion((u8*)exhdr,sizeof(extended_hdr),0,ncch_ctx->titleId,key,ncch_exhdr); + } - if(IsPatch(GetTidCategory(ciaset->common.titleId))||ciaset->content.IsCfa) + // If this title is a Patch or a CFA SaveData size cannot be set in TMD + if( IsPatch(GetTidCategory(ciaset->common.titleId)) || ciaset->content.IsCfa ) ciaset->tmd.savedataSize = 0; - else if(!ciaset->content.keyNotFound) + // Otherwise read Savedata size from exheader, but only first word of savesize is read + else if(ciaset->content.keyFound) ciaset->tmd.savedataSize = (u32)(GetSaveDataSize_frm_exhdr(exhdr) & MAX_U32); - else if(ciaset->rsf->SystemControlInfo.SaveDataSize){ // if it's a title which can have save data, but save data size could not be read from exhdr + // if it's a title which can have save data, but save data size could not be read from exhdr + else if(ciaset->rsf->SystemControlInfo.SaveDataSize){ u64 size = 0; GetSaveDataSizeFromString(&size,ciaset->rsf->SystemControlInfo.SaveDataSize,"CIA"); ciaset->tmd.savedataSize = (u32)(size & MAX_U32); } + // the user has neglected to provide any means of determining the savedata size else ciaset->tmd.savedataSize = 0; - if(ciaset->content.IsCfa||ciaset->content.keyNotFound){ + // Process title version + // If this is a CFA, the -major must be set, since CFAs don't have an exheader + if(ciaset->content.IsCfa){ if(ciaset->common.titleVersion[VER_MAJOR] == MAX_U16){ // '-major' wasn't set - if(ciaset->content.IsCfa){ // Is a CFA and can be decrypted - fprintf(stderr,"[CIA ERROR] Invalid major version. Use \"-major\" option.\n"); - return CIA_BAD_VERSION; - } - else // CXI which cannot be decrypted - ciaset->common.titleVersion[VER_MAJOR] = 0; + fprintf(stderr,"[CIA ERROR] Invalid major version. Use \"-major\" option.\n"); + result = CIA_BAD_VERSION; + goto cleanup; } } - else{ // Is a CXI and can be decrypted + // Otherwise this is a CXI, if it can be decrypted, -major cannot be set and must be read from exheader + else if(ciaset->content.keyFound){ if(ciaset->common.titleVersion[VER_MAJOR] != MAX_U16){ // '-major' was set fprintf(stderr,"[CIA ERROR] Option \"-major\" cannot be applied for cxi.\n"); - return CIA_BAD_VERSION; + result = CIA_BAD_VERSION; + goto cleanup; } // Setting remaster ver ciaset->common.titleVersion[VER_MAJOR] = GetRemasterVersion_frm_exhdr(exhdr); } + // CXI which cannot be decrypted + else{ + if(ciaset->common.titleVersion[VER_MAJOR] == MAX_U16) // '-major' wasn't set + ciaset->common.titleVersion[VER_MAJOR] = 0; + } + + // Calculate u16 title version ciaset->tmd.version = ciaset->tik.version = SetupVersion(ciaset->common.titleVersion[VER_MAJOR],ciaset->common.titleVersion[VER_MINOR],ciaset->common.titleVersion[VER_MICRO]); +cleanup: free(exhdr); - return 0; + return result; } int GetMetaRegion(cia_settings *ciaset, u8 *ncch, ncch_info *info, u8 *key) { - if(ciaset->content.IsCfa || ciaset->content.keyNotFound) + extended_hdr *exhdr; + exefs_hdr *exefsHdr; + cia_metadata *hdr; + u32 icon_size, icon_offset; + + // Do not proceed if this is a CFA or can't be decrypted + if(ciaset->content.IsCfa || !ciaset->content.keyFound) return 0; - extended_hdr *exhdr = malloc(sizeof(extended_hdr)); + // Obtain (&decrypt) exheader + exhdr = malloc(sizeof(extended_hdr)); memcpy(exhdr,ncch+info->exhdrOffset,sizeof(extended_hdr)); if(key != NULL) CryptNcchRegion((u8*)exhdr,sizeof(extended_hdr),0,info->titleId,key,ncch_exhdr); - exefs_hdr *exefsHdr = malloc(sizeof(exefs_hdr)); + // Obtain (&decrypt) exefs header + exefsHdr = malloc(sizeof(exefs_hdr)); memcpy(exefsHdr,ncch+info->exefsOffset,sizeof(exefs_hdr)); if(key != NULL) CryptNcchRegion((u8*)exefsHdr,sizeof(exefs_hdr),0,info->titleId,key,ncch_exefs); - u32 icon_size = 0; - u32 icon_offset = 0; - for(int i = 0; i < MAX_EXEFS_SECTIONS; i++){ - if(strncmp(exefsHdr->fileHdr[i].name,"icon",8) == 0){ - icon_size = u8_to_u32(exefsHdr->fileHdr[i].size,LE); - icon_offset = u8_to_u32(exefsHdr->fileHdr[i].offset,LE) + sizeof(exefs_hdr); - } - } + // Only continue if icon exists + if(!DoesExeFsSectionExist("icon",(u8*)exefsHdr)) + goto cleanup; + + icon_size = GetExeFsSectionSize("icon",(u8*)exefsHdr); + icon_offset = GetExeFsSectionOffset("icon",(u8*)exefsHdr); + // Allocating memory for Meta region ciaset->ciaSections.meta.size = sizeof(cia_metadata) + icon_size; - ciaset->ciaSections.meta.buffer = malloc(ciaset->ciaSections.meta.size); + ciaset->ciaSections.meta.buffer = calloc(1,ciaset->ciaSections.meta.size); if(!ciaset->ciaSections.meta.buffer){ fprintf(stderr,"[CIA ERROR] Not enough memory\n"); return MEM_ERROR; } - cia_metadata *hdr = (cia_metadata*)ciaset->ciaSections.meta.buffer; - memset(hdr,0,sizeof(cia_metadata)); + + // Writing Dependency List & Core Version to Meta region + hdr = (cia_metadata*)ciaset->ciaSections.meta.buffer; GetDependencyList_frm_exhdr(hdr->dependencyList,exhdr); GetCoreVersion_frm_exhdr(hdr->coreVersion,exhdr); - if(icon_size > 0){ - u8 *IconDestPos = (ciaset->ciaSections.meta.buffer + sizeof(cia_metadata)); - memcpy(IconDestPos,ncch+info->exefsOffset+icon_offset,icon_size); - if(key != NULL) - CryptNcchRegion(IconDestPos,icon_size,icon_offset,info->titleId,key,ncch_exefs); - //memdump(stdout,"Icon: ",IconDestPos,0x10); - } + + // Writing Icon data to Meta region + u8 *iconPos = (ciaset->ciaSections.meta.buffer + sizeof(cia_metadata)); + memcpy(iconPos,ncch+info->exefsOffset+icon_offset,icon_size); + if(key != NULL) + CryptNcchRegion(iconPos,icon_size,icon_offset,info->titleId,key,ncch_exefs); +cleanup: free(exefsHdr); free(exhdr); return 0; diff --git a/makerom/cia_build.h b/makerom/cia_build.h index ad0de783..0c24277e 100644 --- a/makerom/cia_build.h +++ b/makerom/cia_build.h @@ -66,7 +66,7 @@ typedef struct bool encryptCia; bool includeUpdateNcch; // for cci -> cia conversions - bool keyNotFound; + bool keyFound; FILE **filePtrs; u64 fileSize[CIA_MAX_CONTENT]; diff --git a/makerom/ncch.c b/makerom/ncch.c index e79e041f..2fc5c0ca 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -1011,6 +1011,8 @@ bool SetNcchKeys(keys_struct *keys, ncch_hdr *hdr) int GetNcchInfo(ncch_info *info, ncch_hdr *hdr) { + clrmem(info, sizeof(ncch_info)); + info->titleId = u8_to_u64(hdr->titleId,LE); info->programId = u8_to_u64(hdr->programId,LE); From 3c97d7dae392def40cb7d169c5475bf35435ff86 Mon Sep 17 00:00:00 2001 From: applestash Date: Thu, 18 Sep 2014 11:07:44 +1000 Subject: [PATCH 062/317] makerom: misc --- makerom/cia.c | 1 + 1 file changed, 1 insertion(+) diff --git a/makerom/cia.c b/makerom/cia.c index 251e027e..d27cfc1b 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -235,6 +235,7 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) GetNcchInfo(info,hdr); /* Verify Ncch0 (Sig&Hash Checks) */ + ciaset->content.keyFound = true; int result = VerifyNcch(ncch0, ciaset->keys, false, ciaset->verbose == false); if(result == UNABLE_TO_LOAD_NCCH_KEY){ ciaset->content.keyFound = false; From a513fb32cbe2458c6f0152dd2d8b681eaa14f4ae Mon Sep 17 00:00:00 2001 From: applestash Date: Fri, 19 Sep 2014 10:55:58 +1000 Subject: [PATCH 063/317] makerom: misc tik id bug fix --- makerom/cia.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/makerom/cia.c b/makerom/cia.c index d27cfc1b..8c496bc3 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -175,7 +175,7 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) } // Ticket Data - ciaset->tik.ticketId = u64GetRand() & 0x0004FFFFFFFFFFFF; + ciaset->tik.ticketId = 0x0004000000000000 | (u64GetRand() & 0x0000FFFFFFFFFFFF); ciaset->tik.deviceId = usrset->cia.deviceId; ciaset->tik.eshopAccId = usrset->cia.eshopAccId; ciaset->tik.licenceType = lic_Permanent; @@ -253,7 +253,7 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) ciaset->common.titleId = u8_to_u64(hdr->titleId,LE); - /* Getting ncch key */ + /* Getting NCCH key */ u8 *ncchkey = NULL; if(ciaset->content.keyFound && IsNcchEncrypted(hdr)){ SetNcchKeys(ciaset->keys,hdr); @@ -265,11 +265,11 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) } } - /* Get TMD Data from ncch */ - result = GetTmdDataFromNcch(ciaset,ncch0,info,ncchkey); // Data For TMD + /* Get TMD Data from NCCH */ + result = GetTmdDataFromNcch(ciaset,ncch0,info,ncchkey); if(result) goto finish; - /* Get META Region from ncch */ - result = GetMetaRegion(ciaset,ncch0,info,ncchkey); // Meta Region + /* Get Meta Region from NCCH */ + result = GetMetaRegion(ciaset,ncch0,info,ncchkey); /* Finish */ finish: /* Return */ @@ -387,7 +387,7 @@ int GetMetaRegion(cia_settings *ciaset, u8 *ncch, ncch_info *info, u8 *key) memcpy(iconPos,ncch+info->exefsOffset+icon_offset,icon_size); if(key != NULL) CryptNcchRegion(iconPos,icon_size,icon_offset,info->titleId,key,ncch_exefs); - + cleanup: free(exefsHdr); free(exhdr); From ce6c1754ab2eeba520b60c45b71e16061fcabbb1 Mon Sep 17 00:00:00 2001 From: enler Date: Sat, 20 Sep 2014 00:06:04 +0800 Subject: [PATCH 064/317] Update romfs_gen.c a bit fix --- makerom/romfs_gen.c | 87 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 8 deletions(-) diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 6a9498f0..cc5aacc0 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -18,6 +18,7 @@ int PopulateRomfs(romfs_buildctx *ctx); void BuildRomfsHeader(romfs_buildctx *ctx); void BuildIvfcHeader(romfs_buildctx *ctx); void GenIvfcHashTree(romfs_buildctx *ctx); +u32 CalcPathHash(u32 parent,wchar_t * path,u32 start,u32 length); int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) @@ -328,6 +329,52 @@ u32 GetDirUTableIndex(romfs_buildctx *ctx, fs_dir *dir) return ret; } +void AddDirHashKey(romfs_buildctx *ctx,u32 parent,wchar_t * path,u32 dirOffset) +{ + u32 hash = CalcPathHash(parent,path,0,wcslen(path)); + u32 index = hash % ctx->m_dirUTableEntry; + if(ctx->dirUTable[index] == 0xffffffff) ctx->dirUTable[index] = dirOffset; + else + { + romfs_direntry * curdir = (romfs_direntry*)(ctx->dirTable + ctx->dirUTable[index]); + while(1) + { + if(*(u32*)curdir->weirdoffset == 0xffffffff) + { + *(u32*)curdir->weirdoffset = dirOffset; + break; + } + else + { + curdir = (romfs_direntry*)(ctx->dirTable + *(u32*)curdir->weirdoffset); + } + } + } +} + +void AddFileHashKey(romfs_buildctx *ctx,u32 parent,wchar_t * path,u32 fileOffset) +{ + u32 hash = CalcPathHash(parent,path,0,wcslen(path)); + u32 index = hash % ctx->m_fileUTableEntry; + if(ctx->fileUTable[index] == 0xffffffff) ctx->fileUTable[index] = fileOffset; + else + { + romfs_fileentry * curfile = (romfs_fileentry*)(ctx->fileTable + ctx->fileUTable[index]); + while(1) + { + if(*(u32*)curfile->weirdoffset == 0xffffffff) + { + *(u32*)curfile->weirdoffset = fileOffset; + break; + } + else + { + curfile = (romfs_fileentry*)(ctx->fileTable + *(u32*)curfile->weirdoffset); + } + } + } +} + int AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) { romfs_fileentry *entry = (romfs_fileentry*)(ctx->fileTable + ctx->u_fileTableLen); @@ -335,9 +382,9 @@ int AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) u32_to_u8(entry->parentdiroffset,parent,LE); u32_to_u8(entry->siblingoffset,sibling,LE); - u32 uTableIndex = GetFileUTableIndex(ctx,file); - u32_to_u8(entry->weirdoffset,ctx->fileUTable[uTableIndex],LE); - ctx->fileUTable[uTableIndex] = ctx->u_fileTableLen; + //u32 uTableIndex = GetFileUTableIndex(ctx,file); + u32_to_u8(entry->weirdoffset,0xffffffff,LE); + //ctx->fileUTable[uTableIndex] = ctx->u_fileTableLen; // Import Name u32_to_u8(entry->namesize,file->name_len,LE); @@ -357,7 +404,7 @@ int AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) } else u64_to_u8(entry->dataoffset,0x40,LE); - + AddFileHashKey(ctx,parent,file->name,ctx->u_fileTableLen); ctx->u_fileTableLen += sizeof(romfs_fileentry) + align(file->name_len,4); return 0; @@ -371,15 +418,16 @@ int AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) u32_to_u8(entry->parentoffset,parent,LE); u32_to_u8(entry->siblingoffset,sibling,LE); - u32 uTableIndex = GetDirUTableIndex(ctx,fs); - u32_to_u8(entry->weirdoffset,ctx->dirUTable[uTableIndex],LE); - ctx->dirUTable[uTableIndex] = ctx->u_dirTableLen; + //u32 uTableIndex = GetDirUTableIndex(ctx,fs); + u32_to_u8(entry->weirdoffset,0xffffffff,LE); + //ctx->dirUTable[uTableIndex] = ctx->u_dirTableLen; u32 Currentdir = ctx->u_dirTableLen; if(Currentdir == 0) { u32_to_u8(entry->namesize,0,LE); + AddDirHashKey(ctx,parent,L"",ctx->u_dirTableLen); ctx->u_dirTableLen += sizeof(romfs_direntry); } else @@ -388,14 +436,17 @@ int AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) u8 *name_pos = (u8*)(ctx->dirTable + ctx->u_dirTableLen + sizeof(romfs_direntry)); memset(name_pos,0,(u32)align(fs->name_len,4)); memcpy(name_pos,(u8*)fs->name,fs->name_len); + AddDirHashKey(ctx,parent,fs->name,ctx->u_dirTableLen); ctx->u_dirTableLen += sizeof(romfs_direntry) + (u32)align(fs->name_len,4); } if(fs->u_file) { u32_to_u8(entry->fileoffset,ctx->u_fileTableLen,LE); + for(u32 i = 0; i < fs->u_file; i++) { + u32 file_sibling = 0; if(i >= fs->u_file-1) file_sibling = ROMFS_UNUSED_ENTRY; @@ -404,6 +455,7 @@ int AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) //wprintf(L"adding %s (0x%lx)\n",fs->file[i].name,fs->file[i].size); AddFileToRomfs(ctx,&fs->file[i],Currentdir,file_sibling); //wprintf(L"added %s (0x%lx)\n",fs->file[i].name,fs->file[i].size); + } } else @@ -418,6 +470,7 @@ int AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) for(u32 i = 0; i < fs->u_dir; i++) { u32 dir_sibling = 0; + romfs_direntry *temp_entry = (romfs_direntry*)(ctx->dirTable + ctx->u_dirTableLen); if(i >= fs->u_dir-1) dir_sibling = ROMFS_UNUSED_ENTRY; else @@ -426,6 +479,11 @@ int AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) dir_sibling = ctx->u_dirTableLen + sizeof(romfs_direntry) + (u32)align(dir[i].name_len,4); } AddDirToRomfs(ctx,&dir[i],Currentdir,dir_sibling); + if(dir_sibling != ROMFS_UNUSED_ENTRY) + { + dir_sibling = ctx->u_dirTableLen;//修复同目录文件夹偏移 + u32_to_u8(temp_entry->siblingoffset,dir_sibling,LE); + } } } else @@ -472,4 +530,17 @@ void GenIvfcHashTree(romfs_buildctx *ctx) } return; -} \ No newline at end of file +} + +u32 CalcPathHash(u32 parent,wchar_t * path,u32 start,u32 length) +{ + u32 hash = parent ^ 123456789; + u32 index = 0; + while(index < length) + { + hash = (u32)((hash >> 5) | (hash << 27));//ror + hash ^= (u16)path[start + index]; + index++; + } + return hash; +} From de12f54762bfbef96027db4b17420fcaf1a405fc Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 20 Sep 2014 17:29:52 +1000 Subject: [PATCH 065/317] makerom: romfs made fixes by enler platform independent --- makerom/dir.c | 9 +++++++-- makerom/dir.h | 3 ++- makerom/romfs_gen.c | 10 +++++----- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/makerom/dir.c b/makerom/dir.c index b0fb1e74..f44a397d 100644 --- a/makerom/dir.c +++ b/makerom/dir.c @@ -3,8 +3,6 @@ #include "utf.h" /* This is mainly a FS interface for ROMFS generation */ - - int fs_InitDir(u16 *path, u32 pathlen, fs_dir *dir); int fs_ManageDirSlot(fs_dir *dir); int fs_ManageFileSlot(fs_dir *dir); @@ -15,6 +13,13 @@ bool fs_EntryIsDirNav(fs_entry *entry); int fs_AddDir(fs_entry *entry, fs_dir *dir); int fs_AddFile(fs_entry *entry, fs_dir *dir); +int fs_u16StrLen(fs_romfs_char *str) +{ + int i; + for( i = 0; str[i] != 0x0; i++ ); + return i; +} + int fs_InitDir(u16 *path, u32 pathlen, fs_dir *dir) { dir->name_len = pathlen; diff --git a/makerom/dir.h b/makerom/dir.h index 6ee72e17..8dcb2c57 100644 --- a/makerom/dir.h +++ b/makerom/dir.h @@ -33,7 +33,7 @@ typedef struct typedef struct { - u16 *name; + fs_romfs_char *name; u32 name_len; u64 size; FILE *fp; @@ -57,6 +57,7 @@ typedef struct int fs_u8String_to_u16String(u16 **dst, u32 *dst_len, u8 *src, u32 src_len); int fs_u16String_to_u16String(u16 **dst, u32 *dst_len, u16 *src, u32 src_len); int fs_u32String_to_u16String(u16 **dst, u32 *dst_len, u32 *src, u32 src_len); +int fs_u16StrLen(fs_romfs_char *str); int fs_OpenDir(fs_char *fs_path, fs_romfs_char *path, u32 pathlen, fs_dir *dir); void fs_PrintDir(fs_dir *dir, u32 depth); diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index cc5aacc0..3d46dc70 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -329,9 +329,9 @@ u32 GetDirUTableIndex(romfs_buildctx *ctx, fs_dir *dir) return ret; } -void AddDirHashKey(romfs_buildctx *ctx,u32 parent,wchar_t * path,u32 dirOffset) +void AddDirHashKey(romfs_buildctx *ctx, u32 parent, fs_romfs_char* path, u32 dirOffset) { - u32 hash = CalcPathHash(parent,path,0,wcslen(path)); + u32 hash = CalcPathHash(parent,path,0,fs_u16StrLen(path)); u32 index = hash % ctx->m_dirUTableEntry; if(ctx->dirUTable[index] == 0xffffffff) ctx->dirUTable[index] = dirOffset; else @@ -352,9 +352,9 @@ void AddDirHashKey(romfs_buildctx *ctx,u32 parent,wchar_t * path,u32 dirOffset) } } -void AddFileHashKey(romfs_buildctx *ctx,u32 parent,wchar_t * path,u32 fileOffset) +void AddFileHashKey(romfs_buildctx *ctx,u32 parent, fs_romfs_char *path, u32 fileOffset) { - u32 hash = CalcPathHash(parent,path,0,wcslen(path)); + u32 hash = CalcPathHash(parent,path,0,fs_u16StrLen(path)); u32 index = hash % ctx->m_fileUTableEntry; if(ctx->fileUTable[index] == 0xffffffff) ctx->fileUTable[index] = fileOffset; else @@ -532,7 +532,7 @@ void GenIvfcHashTree(romfs_buildctx *ctx) return; } -u32 CalcPathHash(u32 parent,wchar_t * path,u32 start,u32 length) +u32 CalcPathHash(u32 parent, fs_romfs_char * path, u32 start, u32 length) { u32 hash = parent ^ 123456789; u32 index = 0; From ded7f036bb70e6f652296c5b5ef7c05d1ca47cbc Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 20 Sep 2014 17:32:23 +1000 Subject: [PATCH 066/317] makerom: misc --- makerom/romfs_gen.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 3d46dc70..a2157ed7 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -315,20 +315,6 @@ void BuildRomfsHeader(romfs_buildctx *ctx) return; } -u32 GetFileUTableIndex(romfs_buildctx *ctx, fs_file *file) -{ - u32 ret = ctx->u_fileUTableEntry; - ctx->u_fileUTableEntry++; - return ret; -} - -u32 GetDirUTableIndex(romfs_buildctx *ctx, fs_dir *dir) -{ - u32 ret = ctx->u_dirUTableEntry; - ctx->u_dirUTableEntry++; - return ret; -} - void AddDirHashKey(romfs_buildctx *ctx, u32 parent, fs_romfs_char* path, u32 dirOffset) { u32 hash = CalcPathHash(parent,path,0,fs_u16StrLen(path)); @@ -380,11 +366,8 @@ int AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) romfs_fileentry *entry = (romfs_fileentry*)(ctx->fileTable + ctx->u_fileTableLen); u32_to_u8(entry->parentdiroffset,parent,LE); - u32_to_u8(entry->siblingoffset,sibling,LE); - - //u32 uTableIndex = GetFileUTableIndex(ctx,file); + u32_to_u8(entry->siblingoffset,sibling,LE); u32_to_u8(entry->weirdoffset,0xffffffff,LE); - //ctx->fileUTable[uTableIndex] = ctx->u_fileTableLen; // Import Name u32_to_u8(entry->namesize,file->name_len,LE); From 1723d4fc8fa5441991f8e1b26db3c318c540fd1b Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 20 Sep 2014 22:55:48 +1000 Subject: [PATCH 067/317] makerom: build 0.13 in rsf: "Rom" renamed to "RomFs" and "HostRoot" renamed to "RootPath". Cci rsf settings "MediaSize"/"CardDevice" are automatically decided if not specified manually. Also some bug fixes. --- makerom/Makefile | 2 +- makerom/accessdesc.c | 42 +++++++- makerom/dir.h | 2 +- makerom/keyset.c | 25 +++++ makerom/keyset.h | 1 + makerom/ncch.c | 2 +- makerom/ncsd.c | 225 ++++++++++++++++++++-------------------- makerom/romfs_gen.c | 20 ++-- makerom/rsf_settings.c | 72 ++++++------- makerom/rsf_settings.h | 12 --- makerom/user_settings.c | 7 +- makerom/user_settings.h | 19 +--- 12 files changed, 229 insertions(+), 200 deletions(-) diff --git a/makerom/Makefile b/makerom/Makefile index 4cf0a1c9..669476ae 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -21,7 +21,7 @@ CC = gcc # MAKEROM Build Settings MAKEROM_BUILD_FLAGS = #-DDEBUG VER_MAJOR = 0 -VER_MINOR = 12 +VER_MINOR = 13 OUTPUT = makerom main: build diff --git a/makerom/accessdesc.c b/makerom/accessdesc.c index f02d9f10..f3b10563 100644 --- a/makerom/accessdesc.c +++ b/makerom/accessdesc.c @@ -307,6 +307,11 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)app_fw1D_prod_hdrpub; *cxiPvtk = NULL; } + if(keys->keyset == pki_GATEWAY3DS){ + *accessDescSig = (u8*)app_fw1D_prod_acexsig; + *cxiPubk = (u8*)app_fw1D_prod_hdrpub; + *cxiPvtk = (u8*)app_fw1D_prod_hdrpub; + } break; case 0x1E: if(keys->keyset == pki_PRODUCTION){ @@ -314,6 +319,11 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)app_fw1E_prod_hdrpub; *cxiPvtk = NULL; } + if(keys->keyset == pki_GATEWAY3DS){ + *accessDescSig = (u8*)app_fw1E_prod_acexsig; + *cxiPubk = (u8*)app_fw1E_prod_hdrpub; + *cxiPvtk = (u8*)app_fw1E_prod_hdrpub; + } break; case 0x20: if(keys->keyset == pki_DEVELOPMENT){ @@ -321,11 +331,16 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)app_fw20_dev_hdrpub; *cxiPvtk = NULL; } - else if(keys->keyset == pki_PRODUCTION){ + if(keys->keyset == pki_PRODUCTION){ *accessDescSig = (u8*)app_fw20_prod_acexsig; *cxiPubk = (u8*)app_fw20_prod_hdrpub; *cxiPvtk = NULL; } + if(keys->keyset == pki_GATEWAY3DS){ + *accessDescSig = (u8*)app_fw20_prod_acexsig; + *cxiPubk = (u8*)app_fw20_prod_hdrpub; + *cxiPvtk = (u8*)app_fw20_prod_hdrpub; + } break; case 0x21: if(keys->keyset == pki_DEVELOPMENT){ @@ -338,6 +353,11 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)app_fw21_prod_hdrpub; *cxiPvtk = NULL; } + if(keys->keyset == pki_GATEWAY3DS){ + *accessDescSig = (u8*)app_fw21_prod_acexsig; + *cxiPubk = (u8*)app_fw21_prod_hdrpub; + *cxiPvtk = (u8*)app_fw21_prod_hdrpub; + } break; case 0x23: if(keys->keyset == pki_DEVELOPMENT){ @@ -350,6 +370,11 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)app_fw23_prod_hdrpub; *cxiPvtk = NULL; } + if(keys->keyset == pki_GATEWAY3DS){ + *accessDescSig = (u8*)app_fw23_prod_acexsig; + *cxiPubk = (u8*)app_fw23_prod_hdrpub; + *cxiPvtk = (u8*)app_fw23_prod_hdrpub; + } break; case 0x27: if(keys->keyset == pki_PRODUCTION){ @@ -357,6 +382,11 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)app_fw27_prod_hdrpub; *cxiPvtk = NULL; } + if(keys->keyset == pki_GATEWAY3DS){ + *accessDescSig = (u8*)app_fw27_prod_acexsig; + *cxiPubk = (u8*)app_fw27_prod_hdrpub; + *cxiPvtk = (u8*)app_fw27_prod_hdrpub; + } break; } @@ -369,6 +399,11 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)ecapp_fw20_prod_hdrpub; *cxiPvtk = NULL; } + if(keys->keyset == pki_GATEWAY3DS){ + *accessDescSig = (u8*)ecapp_fw20_prod_acexsig; + *cxiPubk = (u8*)ecapp_fw20_prod_hdrpub; + *cxiPvtk = (u8*)ecapp_fw20_prod_hdrpub; + } break; case 0x23: if(keys->keyset == pki_PRODUCTION){ @@ -376,6 +411,11 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)ecapp_fw23_prod_hdrpub; *cxiPvtk = NULL; } + if(keys->keyset == pki_GATEWAY3DS){ + *accessDescSig = (u8*)ecapp_fw23_prod_acexsig; + *cxiPubk = (u8*)ecapp_fw23_prod_hdrpub; + *cxiPvtk = (u8*)ecapp_fw23_prod_hdrpub; + } break; } } diff --git a/makerom/dir.h b/makerom/dir.h index 8dcb2c57..c6e16f6b 100644 --- a/makerom/dir.h +++ b/makerom/dir.h @@ -41,7 +41,7 @@ typedef struct typedef struct { - u16 *name; + fs_romfs_char *name; u32 name_len; void *dir; // treated as type 'fs_dir'. This officially type 'void' to prevent self referencing problems diff --git a/makerom/keyset.c b/makerom/keyset.c index 66ebeff6..eaa31168 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -170,6 +170,31 @@ int LoadKeysFromResources(keys_struct *keys) SetTikCert(keys,(u8*)xsC_ppki_cert); SetTmdCert(keys,(u8*)cpB_ppki_cert); } + else if(keys->keyset == pki_GATEWAY3DS){ + keys->keysetLoaded = true; + /* AES Keys */ + // CIA + if(keys->aes.currentCommonKey > 0xff) + SetCurrentCommonKey(keys,0); + + // NCCH + SetNormalKey(keys,(u8*)dev_fixed_ncch_key[0]); + SetSystemFixedKey(keys,(u8*)dev_fixed_ncch_key[0]); + + /* RSA Keys */ + // CIA + SetTIK_RsaKey(keys,(u8*)xsC_ppki_rsa_priv,(u8*)xsC_ppki_rsa_pub); + SetTMD_RsaKey(keys,(u8*)cpB_ppki_rsa_priv,(u8*)cpB_ppki_rsa_pub); + // CCI/CFA + Set_CCI_CFA_RsaKey(keys,(u8*)prod_ncsd_cfa_priv,(u8*)prod_ncsd_cfa_pub); + // CXI + SetAccessDesc_RsaKey(keys,(u8*)prod_acex_priv,(u8*)prod_acex_pub); + + /* Certs */ + SetCaCert(keys,(u8*)ca3_ppki_cert); + SetTikCert(keys,(u8*)xsC_ppki_cert); + SetTmdCert(keys,(u8*)cpB_ppki_cert); + } return 0; } diff --git a/makerom/keyset.h b/makerom/keyset.h index 9d6c0c60..e8c92301 100644 --- a/makerom/keyset.h +++ b/makerom/keyset.h @@ -26,6 +26,7 @@ typedef enum pki_DEVELOPMENT, pki_PRODUCTION, pki_CUSTOM, + pki_GATEWAY3DS } pki_keyset; typedef enum diff --git a/makerom/ncch.c b/makerom/ncch.c index 2fc5c0ca..8f15f91d 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -175,7 +175,7 @@ int GetBasicOptions(ncch_settings *ncchset, user_settings *usrset) ncchset->options.FreeProductCode = ncchset->rsfSet->Option.FreeProductCode; ncchset->options.IsCfa = (usrset->ncch.ncchType == CFA); ncchset->options.IsBuildingCodeSection = (usrset->ncch.elfPath != NULL); - ncchset->options.UseRomFS = ((ncchset->rsfSet->Rom.HostRoot && strlen(ncchset->rsfSet->Rom.HostRoot) > 0) || usrset->ncch.romfsPath); + ncchset->options.UseRomFS = ((ncchset->rsfSet->RomFs.RootPath && strlen(ncchset->rsfSet->RomFs.RootPath) > 0) || usrset->ncch.romfsPath); ncchset->options.useSecCrypto = usrset->ncch.useSecCrypto; ncchset->options.keyXID = usrset->ncch.keyXID; diff --git a/makerom/ncsd.c b/makerom/ncsd.c index 39d70364..204587b4 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -13,6 +13,8 @@ const int NCCH0_OFFSET = 0x4000; const int CCI_BLOCK_SIZE = 0x200; +const char MEDIA_SIZE_STR[10][6] = {"128MB","256MB","512MB","1GB","2GB","4GB","8GB","16GB","32GB"}; + void ImportCciSettings(cci_settings *set, user_settings *usrset); void FreeCciSettings(cci_settings *set); int ImportCciNcch(cci_settings *set); @@ -46,13 +48,13 @@ int build_CCI(user_settings *usrset) goto finish; } - if(CheckRomConfig(set)){ - result = CCI_CONFIG_FAIL; + if(GenCardInfoHdr(set)){ + result = GEN_HDR_FAIL; goto finish; } - if(GenCardInfoHdr(set)){ - result = GEN_HDR_FAIL; + if(CheckRomConfig(set)){ + result = CCI_CONFIG_FAIL; goto finish; } @@ -165,63 +167,6 @@ bool CanCiaBeCci(u64 titleId, u16 count, tmd_content_chunk *content) return true; } -void GenRsfInputFromTmd(tmd_hdr *tmd, tmd_content_chunk *info, cci_settings *set) -{ - if(!set->rsf->CardInfo.MediaSize){ - set->rsf->CardInfo.MediaSize = calloc(20,sizeof(char)); - u64 contentSize = NCCH0_OFFSET; - u16 contentNum = GetTmdContentCount(tmd); - for(int i = 0; i < contentNum; i++) - contentSize += GetTmdContentSize(info[i]); - - if(contentSize < (u64)128*MB) - strcpy(set->rsf->CardInfo.MediaSize,"128MB"); - else if(contentSize < (u64)256*MB) - strcpy(set->rsf->CardInfo.MediaSize,"256MB"); - else if(contentSize < (u64)512*MB) - strcpy(set->rsf->CardInfo.MediaSize,"512MB"); - else if(contentSize < (u64)1*GB) - strcpy(set->rsf->CardInfo.MediaSize,"1GB"); - else if(contentSize < (u64)2*GB) - strcpy(set->rsf->CardInfo.MediaSize,"2GB"); - else if(contentSize < (u64)4*GB) - strcpy(set->rsf->CardInfo.MediaSize,"4GB"); - else if(contentSize < (u64)8*GB) - strcpy(set->rsf->CardInfo.MediaSize,"8GB"); - else{ - free(set->rsf->CardInfo.MediaSize); - set->rsf->CardInfo.MediaSize = NULL; - } - - if(set->options.verbose && set->rsf->CardInfo.MediaSize) - printf("[CCI] Auto generating RSF setting \"CardInfo/MediaSize: %s\" \n",set->rsf->CardInfo.MediaSize); - } - - if(!set->rsf->CardInfo.MediaType){ - set->rsf->CardInfo.MediaType = calloc(20,sizeof(char)); - if(set->romInfo.saveSize < (u64)1*MB) - strcpy(set->rsf->CardInfo.MediaType,"Card1"); - else - strcpy(set->rsf->CardInfo.MediaType,"Card2"); - - if(set->options.verbose) - printf("[CCI] Auto generating RSF setting \"CardInfo/MediaType: %s\" \n",set->rsf->CardInfo.MediaType); - } - - if(!set->rsf->CardInfo.CardDevice){ - set->rsf->CardInfo.CardDevice = calloc(20,sizeof(char)); - if(set->romInfo.saveSize < (u64)1*MB && set->romInfo.saveSize > 0) - strcpy(set->rsf->CardInfo.CardDevice,"NorFlash"); - else - strcpy(set->rsf->CardInfo.CardDevice,"None"); - - if(set->options.verbose) - printf("[CCI] Auto generating RSF setting \"CardInfo/CardDevice: %s\" \n",set->rsf->CardInfo.CardDevice); - } - - return; -} - int ProcessCiaForCci(cci_settings *set) { if(!IsCia(set->content.data)){ @@ -247,9 +192,7 @@ int ProcessCiaForCci(cci_settings *set) fprintf(stderr,"[CCI ERROR] This CIA cannot be converted to CCI\n"); return INCOMPAT_CIA; } - - GenRsfInputFromTmd(tmd,contentInfo,set); - + bool canDecrypt; u8 titleKey[AES_128_KEY_SIZE]; canDecrypt = GetTikTitleKey(titleKey,tik,set->keys); @@ -285,19 +228,37 @@ int ProcessCiaForCci(cci_settings *set) return 0; } -int ImportCciNcch(cci_settings *set) +void GetTitleSaveSize(cci_settings *set) { if(set->rsf->SystemControlInfo.SaveDataSize) GetSaveDataSizeFromString(&set->romInfo.saveSize,set->rsf->SystemControlInfo.SaveDataSize,"CCI"); + + // Adjusting save size + + if(set->romInfo.saveSize <= (u64)128*KB) + set->romInfo.saveSize = (u64)128*KB; + else if(set->romInfo.saveSize <= (u64)512*KB) + set->romInfo.saveSize = (u64)512*KB; + else + set->romInfo.saveSize = align(set->romInfo.saveSize,MB); +} + +int ImportCciNcch(cci_settings *set) +{ + int ret = 0; if(set->content.dataType == infile_ncch) - return ImportNcchForCci(set); + ret = ImportNcchForCci(set); else if(set->content.dataType == infile_cia) - return ProcessCiaForCci(set); - else + ret = ProcessCiaForCci(set); + else{ fprintf(stderr,"[CCI ERROR] Unrecognised input data type\n"); + return FAILED_TO_IMPORT_FILE; + } - return FAILED_TO_IMPORT_FILE; + GetTitleSaveSize(set); + + return ret; } int ProcessCverDataForCci(cci_settings *set) @@ -399,12 +360,34 @@ int ProcessNcchForCci(cci_settings *set) return 0; } +void SetCciNcchInfo(cci_hdr *hdr, cci_settings *set) +{ + u64 ncchSize,ncchOffset; + + ncchOffset = NCCH0_OFFSET; + + for(int i = 0; i < CCI_MAX_CONTENT; i++){ + if(set->content.active[i]){ + set->content.cOffset[i] = ncchOffset; + ncchSize = align(set->content.dSize[i],set->romInfo.blockSize); + + u32_to_u8(hdr->offset_sizeTable[i].offset,(ncchOffset/set->romInfo.blockSize),LE); + u32_to_u8(hdr->offset_sizeTable[i].size,(ncchSize/set->romInfo.blockSize),LE); + u64_to_u8(hdr->ncchIdTable[i],set->content.titleId[i],LE); + + ncchOffset += ncchSize; + } + } + + set->romInfo.usedSize = ncchOffset; + + return; +} + int SetMediaSize(u8 *mediaSize, cci_settings *set) { char *str = set->rsf->CardInfo.MediaSize; - if(!str) - set->romInfo.mediaSize = (u64)GB*2; - else{ + if(str){ if(strcasecmp(str,"128MB") == 0) set->romInfo.mediaSize = (u64)MB*128; else if(strcasecmp(str,"256MB") == 0) set->romInfo.mediaSize = (u64)MB*256; else if(strcasecmp(str,"512MB") == 0) set->romInfo.mediaSize = (u64)MB*512; @@ -419,6 +402,31 @@ int SetMediaSize(u8 *mediaSize, cci_settings *set) return INVALID_RSF_OPT; } } + else{ + u64 dataSize = set->romInfo.usedSize + (set->romInfo.saveSize >= MB ? set->romInfo.saveSize : 0); + if(dataSize < (u64)MB*128) + set->romInfo.mediaSize = (u64)MB*128; + else if(dataSize < (u64)MB*256) + set->romInfo.mediaSize = (u64)MB*256; + else if(dataSize < (u64)MB*512) + set->romInfo.mediaSize = (u64)MB*512; + else if(dataSize < (u64)GB*1) + set->romInfo.mediaSize = (u64)GB*1; + else if(dataSize < (u64)GB*2) + set->romInfo.mediaSize = (u64)GB*2; + else if(dataSize < (u64)GB*4) + set->romInfo.mediaSize = (u64)GB*4; + else if(dataSize < (u64)GB*8) + set->romInfo.mediaSize = (u64)GB*8; + //else if(dataSize < (u64)GB*16) + // set->romInfo.mediaSize = (u64)GB*16; + //else if(dataSize < (u64)GB*32) + // set->romInfo.mediaSize = (u64)GB*32; + else { + fprintf(stderr,"[CCI ERROR] NCCH Partitions are too large\n"); + return INVALID_RSF_OPT; + } + } u32_to_u8(mediaSize,(set->romInfo.mediaSize/set->romInfo.blockSize),LE); @@ -446,9 +454,7 @@ int SetMediaType(u8 *flag, cci_settings *set) { char *str = set->rsf->CardInfo.MediaType; - if(!str) - *flag = mediatype_CARD1; - else{ + if(str){ if(strcasecmp(str,"Card1") == 0) *flag = mediatype_CARD1; else if(strcasecmp(str,"Card2") == 0) @@ -458,6 +464,12 @@ int SetMediaType(u8 *flag, cci_settings *set) return INVALID_RSF_OPT; } } + else{ + if(set->romInfo.saveSize >= (u64)1*MB) + *flag = mediatype_CARD2; + else + *flag = mediatype_CARD1; + } return 0; } @@ -488,9 +500,7 @@ int SetCardDevice(u8 *flags, u64 saveSize, rsf_settings *rsf) /* CardDevice */ u8 cardDevice = 0; - if(!rsf->CardInfo.CardDevice) - cardDevice = carddevice_NONE; - else{ + if(rsf->CardInfo.CardDevice){ if(strcmp(rsf->CardInfo.CardDevice,"NorFlash") == 0) cardDevice = carddevice_NOR_FLASH; else if(strcmp(rsf->CardInfo.CardDevice,"None") == 0) @@ -502,6 +512,12 @@ int SetCardDevice(u8 *flags, u64 saveSize, rsf_settings *rsf) return INVALID_RSF_OPT; } } + else{ + if(saveSize == 0 || saveSize >= (u64)1*MB) + cardDevice = carddevice_NONE; + else + cardDevice = carddevice_NOR_FLASH; + } if(flags[cciflag_MEDIA_TYPE] == mediatype_CARD1){ if(saveSize != (u64)(128*KB) && saveSize != (u64)(512*KB) && cardDevice == carddevice_NOR_FLASH){ @@ -544,31 +560,6 @@ int SetCciFlags(u8 *flags, cci_settings *set) return 0; } -void SetCciNcchInfo(cci_hdr *hdr, cci_settings *set) -{ - u64 ncchSize,ncchOffset; - - ncchOffset = NCCH0_OFFSET; - - for(int i = 0; i < CCI_MAX_CONTENT; i++){ - if(set->content.active[i]){ - set->content.cOffset[i] = ncchOffset; - ncchSize = align(set->content.dSize[i],set->romInfo.blockSize); - - u32_to_u8(hdr->offset_sizeTable[i].offset,(ncchOffset/set->romInfo.blockSize),LE); - u32_to_u8(hdr->offset_sizeTable[i].size,(ncchSize/set->romInfo.blockSize),LE); - u64_to_u8(hdr->ncchIdTable[i],set->content.titleId[i],LE); - - ncchOffset += ncchSize; - } - } - - set->romInfo.usedSize = ncchOffset; - - return; -} - - int GenCciHdr(cci_settings *set) { set->headers.ccihdr.size = sizeof(cci_hdr); @@ -586,11 +577,12 @@ int GenCciHdr(cci_settings *set) u64_to_u8(hdr->titleId,set->content.titleId[0],LE); + SetCciNcchInfo(hdr,set); if(SetMediaSize(hdr->mediaSize,set)) return GEN_HDR_FAIL; if(SetCciFlags(hdr->flags,set)) return GEN_HDR_FAIL; - SetCciNcchInfo(hdr,set); + // Sign Header RsaSignVerify(&hdr->magic,sizeof(cci_hdr)-RSA_2048_KEY_SIZE,hdr->signature,set->keys->rsa.cciCfaPub,set->keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN); @@ -598,20 +590,31 @@ int GenCciHdr(cci_settings *set) return 0; } -int CheckRomConfig(cci_settings *set) +char* GetMediaSizeStr(u64 mediaSize) { + //MEDIA_SIZE_STR + switch(mediaSize){ + case (u64)MB*128: return (char*)MEDIA_SIZE_STR[0]; + case (u64)MB*256: return (char*)MEDIA_SIZE_STR[1]; + case (u64)MB*512: return (char*)MEDIA_SIZE_STR[2]; + case (u64)GB*1: return (char*)MEDIA_SIZE_STR[3]; + case (u64)GB*2: return (char*)MEDIA_SIZE_STR[4]; + case (u64)GB*4: return (char*)MEDIA_SIZE_STR[5]; + case (u64)GB*8: return (char*)MEDIA_SIZE_STR[6]; + default: return 0; + } +} + +int CheckRomConfig(cci_settings *set) +{ u64 cciUsedSize; if(set->romInfo.mediaType == mediatype_CARD2) cciUsedSize = set->romInfo.card2SaveOffset + set->romInfo.saveSize; else cciUsedSize = set->romInfo.usedSize; - if(cciUsedSize > set->romInfo.mediaSize){ - char *str = set->rsf->CardInfo.MediaSize; - if(!str) - str = "2GB"; - - fprintf(stderr,"[CCI ERROR] MediaSize '%s' is insufficient for the CCI data\n",str); + if(cciUsedSize > set->romInfo.mediaSize){ + fprintf(stderr,"[CCI ERROR] MediaSize '%s' is insufficient for the CCI data\n",GetMediaSizeStr(set->romInfo.mediaSize)); return CCI_CONFIG_FAIL; } return 0; diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index a2157ed7..04f81b67 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -5,6 +5,7 @@ const int ROMFS_BLOCK_SIZE = 0x1000; const unsigned int ROMFS_UNUSED_ENTRY = 0xffffffff; +const fs_romfs_char ROMFS_EMPTY_PATH[2] = {0x0000, 0x0000}; // Build bool IsFileWanted(fs_file *file, void *filter_criteria); @@ -18,7 +19,7 @@ int PopulateRomfs(romfs_buildctx *ctx); void BuildRomfsHeader(romfs_buildctx *ctx); void BuildIvfcHeader(romfs_buildctx *ctx); void GenIvfcHashTree(romfs_buildctx *ctx); -u32 CalcPathHash(u32 parent,wchar_t * path,u32 start,u32 length); +u32 CalcPathHash(u32 parent, fs_romfs_char* path, u32 start, u32 length); int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) @@ -30,7 +31,7 @@ int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) char *cwd = calloc(CWD_MAX_LEN,sizeof(char)); getcwd(cwd,CWD_MAX_LEN); - char *dir = ncchset->rsfSet->Rom.HostRoot; + char *dir = ncchset->rsfSet->RomFs.RootPath; fs_char *fs_path; fs_romfs_char *path; @@ -399,18 +400,15 @@ int AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) romfs_direntry *entry = (romfs_direntry*)(ctx->dirTable + ctx->u_dirTableLen); u32_to_u8(entry->parentoffset,parent,LE); - u32_to_u8(entry->siblingoffset,sibling,LE); - - //u32 uTableIndex = GetDirUTableIndex(ctx,fs); + u32_to_u8(entry->siblingoffset,sibling,LE); u32_to_u8(entry->weirdoffset,0xffffffff,LE); - //ctx->dirUTable[uTableIndex] = ctx->u_dirTableLen; u32 Currentdir = ctx->u_dirTableLen; if(Currentdir == 0) { u32_to_u8(entry->namesize,0,LE); - AddDirHashKey(ctx,parent,L"",ctx->u_dirTableLen); + AddDirHashKey(ctx,parent,(fs_romfs_char*)ROMFS_EMPTY_PATH,ctx->u_dirTableLen); ctx->u_dirTableLen += sizeof(romfs_direntry); } else @@ -464,7 +462,7 @@ int AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) AddDirToRomfs(ctx,&dir[i],Currentdir,dir_sibling); if(dir_sibling != ROMFS_UNUSED_ENTRY) { - dir_sibling = ctx->u_dirTableLen;//修复同目录文件夹偏移 + dir_sibling = ctx->u_dirTableLen;//修复同目录文件夹偏移 (Repair the same directory folder offset) u32_to_u8(temp_entry->siblingoffset,dir_sibling,LE); } } @@ -515,15 +513,13 @@ void GenIvfcHashTree(romfs_buildctx *ctx) return; } -u32 CalcPathHash(u32 parent, fs_romfs_char * path, u32 start, u32 length) +u32 CalcPathHash(u32 parent, fs_romfs_char* path, u32 start, u32 length) { u32 hash = parent ^ 123456789; - u32 index = 0; - while(index < length) + for( u32 index = 0; index < length; index++ ) { hash = (u32)((hash >> 5) | (hash << 27));//ror hash ^= (u16)path[start + index]; - index++; } return hash; } diff --git a/makerom/rsf_settings.c b/makerom/rsf_settings.c index aa7e9388..c0cdbb22 100644 --- a/makerom/rsf_settings.c +++ b/makerom/rsf_settings.c @@ -1,5 +1,16 @@ #include "lib.h" +void GET_Option(ctr_yaml_context *ctx, rsf_settings *rsf); +void GET_AccessControlInfo(ctr_yaml_context *ctx, rsf_settings *rsf); +void GET_SystemControlInfo(ctr_yaml_context *ctx, rsf_settings *rsf); +void GET_BasicInfo(ctr_yaml_context *ctx, rsf_settings *rsf); +void GET_RomFs(ctr_yaml_context *ctx, rsf_settings *rsf); +void GET_ExeFs(ctr_yaml_context *ctx, rsf_settings *rsf); +void GET_PlainRegion(ctr_yaml_context *ctx, rsf_settings *rsf); +void GET_TitleInfo(ctr_yaml_context *ctx, rsf_settings *rsf); +void GET_CardInfo(ctr_yaml_context *ctx, rsf_settings *rsf); +void GET_CommonHeaderKey(ctr_yaml_context *ctx, rsf_settings *rsf); + void EvaluateRSF(rsf_settings *rsf, ctr_yaml_context *ctx) { u32 start_level = ctx->Level-1; @@ -11,7 +22,7 @@ void EvaluateRSF(rsf_settings *rsf, ctr_yaml_context *ctx) else if(cmpYamlValue("AccessControlInfo",ctx)) {FinishEvent(ctx); GET_AccessControlInfo(ctx,rsf); goto GET_NextGroup;} else if(cmpYamlValue("SystemControlInfo",ctx)) {FinishEvent(ctx); GET_SystemControlInfo(ctx,rsf); goto GET_NextGroup;} else if(cmpYamlValue("BasicInfo",ctx)) {FinishEvent(ctx); GET_BasicInfo(ctx,rsf); goto GET_NextGroup;} - else if(cmpYamlValue("Rom",ctx)) {FinishEvent(ctx); GET_Rom(ctx,rsf); goto GET_NextGroup;} + else if(cmpYamlValue("RomFs",ctx)) {FinishEvent(ctx); GET_RomFs(ctx,rsf); goto GET_NextGroup;} else if(cmpYamlValue("ExeFs",ctx)) {FinishEvent(ctx); GET_ExeFs(ctx,rsf); goto GET_NextGroup;} else if(cmpYamlValue("PlainRegion",ctx)) {FinishEvent(ctx); GET_PlainRegion(ctx,rsf); goto GET_NextGroup;} else if(cmpYamlValue("TitleInfo",ctx)) {FinishEvent(ctx); GET_TitleInfo(ctx,rsf); goto GET_NextGroup;} @@ -202,7 +213,7 @@ void GET_BasicInfo(ctr_yaml_context *ctx, rsf_settings *rsf) FinishEvent(ctx); } -void GET_Rom(ctr_yaml_context *ctx, rsf_settings *rsf) +void GET_RomFs(ctr_yaml_context *ctx, rsf_settings *rsf) { /* Checking That Group is in a map */ if(!CheckMappingEvent(ctx)) return; @@ -213,13 +224,12 @@ void GET_Rom(ctr_yaml_context *ctx, rsf_settings *rsf) if(ctx->error || ctx->done) return; // Handle childs - if(cmpYamlValue("HostRoot",ctx)) SetSimpleYAMLValue(&rsf->Rom.HostRoot,"HostRoot",ctx,0); - //else if(cmpYamlValue("Padding",ctx)) SetSimpleYAMLValue(&rsf->Rom.Padding,"Padding",ctx,0); + if(cmpYamlValue("RootPath",ctx)) SetSimpleYAMLValue(&rsf->RomFs.RootPath,"RootPath",ctx,0); - else if(cmpYamlValue("DefaultReject",ctx)) rsf->Rom.DefaultRejectNum = SetYAMLSequence(&rsf->Rom.DefaultReject,"DefaultReject",ctx); - else if(cmpYamlValue("Reject",ctx)) rsf->Rom.RejectNum = SetYAMLSequence(&rsf->Rom.Reject,"Reject",ctx); - else if(cmpYamlValue("Include",ctx)) rsf->Rom.IncludeNum = SetYAMLSequence(&rsf->Rom.Include,"Include",ctx); - else if(cmpYamlValue("File",ctx)) rsf->Rom.FileNum = SetYAMLSequence(&rsf->Rom.File,"File",ctx); + else if(cmpYamlValue("DefaultReject",ctx)) rsf->RomFs.DefaultRejectNum = SetYAMLSequence(&rsf->RomFs.DefaultReject,"DefaultReject",ctx); + else if(cmpYamlValue("Reject",ctx)) rsf->RomFs.RejectNum = SetYAMLSequence(&rsf->RomFs.Reject,"Reject",ctx); + else if(cmpYamlValue("Include",ctx)) rsf->RomFs.IncludeNum = SetYAMLSequence(&rsf->RomFs.Include,"Include",ctx); + else if(cmpYamlValue("File",ctx)) rsf->RomFs.FileNum = SetYAMLSequence(&rsf->RomFs.File,"File",ctx); else{ fprintf(stderr,"[RSF ERROR] Unrecognised key '%s'\n",GetYamlString(ctx)); @@ -279,12 +289,10 @@ void GET_TitleInfo(ctr_yaml_context *ctx, rsf_settings *rsf) // Handle childs if(cmpYamlValue("Category",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.Category,"Category",ctx,0); - //else if(cmpYamlValue("Platform",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.Platform,"Platform",ctx,0); else if(cmpYamlValue("UniqueId",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.UniqueId,"UniqueId",ctx,0); else if(cmpYamlValue("Version",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.Version,"Version",ctx,0); else if(cmpYamlValue("ContentsIndex",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.ContentsIndex,"ContentsIndex",ctx,0); else if(cmpYamlValue("Variation",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.Variation,"Variation",ctx,0); - //else if(cmpYamlValue("Use",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.Use,"Use",ctx,0); else if(cmpYamlValue("ChildIndex",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.ChildIndex,"ChildIndex",ctx,0); else if(cmpYamlValue("DemoIndex",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.DemoIndex,"DemoIndex",ctx,0); else if(cmpYamlValue("TargetCategory",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.TargetCategory,"TargetCategory",ctx,0); @@ -377,15 +385,8 @@ void free_RsfSettings(rsf_settings *set) { //Option free(set->Option.PageSize); - /* - for(u32 i = 0; i < set->Option.AppendSystemCallNum; i++){ - free(set->Option.AppendSystemCall[i]); - } - free(set->Option.AppendSystemCall); - */ //AccessControlInfo - //free(set->AccessControlInfo.ProgramId); free(set->AccessControlInfo.IdealProcessor); free(set->AccessControlInfo.Priority); free(set->AccessControlInfo.MemoryType); @@ -401,7 +402,6 @@ void free_RsfSettings(rsf_settings *set) free(set->AccessControlInfo.SystemMode); free(set->AccessControlInfo.AffinityMask); free(set->AccessControlInfo.DescVersion); - //free(set->AccessControlInfo.CryptoKey); free(set->AccessControlInfo.ResourceLimitCategory); free(set->AccessControlInfo.ReleaseKernelMajor); free(set->AccessControlInfo.ReleaseKernelMinor); @@ -441,11 +441,6 @@ void free_RsfSettings(rsf_settings *set) free(set->AccessControlInfo.ServiceAccessControl[i]); } free(set->AccessControlInfo.ServiceAccessControl); - - for(u32 i = 0; i < set->AccessControlInfo.StorageIdNum; i++){ - free(set->AccessControlInfo.StorageId[i]); - } - free(set->AccessControlInfo.StorageId); for(u32 i = 0; i < set->AccessControlInfo.AccessibleSaveDataIdsNum; i++){ free(set->AccessControlInfo.AccessibleSaveDataIds[i]); @@ -470,32 +465,29 @@ void free_RsfSettings(rsf_settings *set) free(set->BasicInfo.ProductCode); free(set->BasicInfo.ContentType); free(set->BasicInfo.Logo); - //free(set->BasicInfo.BackupMemoryType); - //free(set->BasicInfo.InitialCode); //Rom - free(set->Rom.HostRoot); - //free(set->Rom.Padding); + free(set->RomFs.RootPath); - for(u32 i = 0; i < set->Rom.DefaultRejectNum; i++){ - free(set->Rom.DefaultReject[i]); + for(u32 i = 0; i < set->RomFs.DefaultRejectNum; i++){ + free(set->RomFs.DefaultReject[i]); } - free(set->Rom.DefaultReject); + free(set->RomFs.DefaultReject); - for(u32 i = 0; i < set->Rom.RejectNum; i++){ - free(set->Rom.Reject[i]); + for(u32 i = 0; i < set->RomFs.RejectNum; i++){ + free(set->RomFs.Reject[i]); } - free(set->Rom.Reject); + free(set->RomFs.Reject); - for(u32 i = 0; i < set->Rom.IncludeNum; i++){ - free(set->Rom.Include[i]); + for(u32 i = 0; i < set->RomFs.IncludeNum; i++){ + free(set->RomFs.Include[i]); } - free(set->Rom.Include); + free(set->RomFs.Include); - for(u32 i = 0; i < set->Rom.FileNum; i++){ - free(set->Rom.File[i]); + for(u32 i = 0; i < set->RomFs.FileNum; i++){ + free(set->RomFs.File[i]); } - free(set->Rom.File); + free(set->RomFs.File); //ExeFs for(u32 i = 0; i < set->ExeFs.TextNum; i++){ @@ -520,13 +512,11 @@ void free_RsfSettings(rsf_settings *set) free(set->PlainRegion); //TitleInfo - //free(set->TitleInfo.Platform); free(set->TitleInfo.Category); free(set->TitleInfo.UniqueId); free(set->TitleInfo.Version); free(set->TitleInfo.ContentsIndex); free(set->TitleInfo.Variation); - //free(set->TitleInfo.Use); free(set->TitleInfo.ChildIndex); free(set->TitleInfo.DemoIndex); free(set->TitleInfo.TargetCategory); diff --git a/makerom/rsf_settings.h b/makerom/rsf_settings.h index 2b355fcd..e77f3011 100644 --- a/makerom/rsf_settings.h +++ b/makerom/rsf_settings.h @@ -1,16 +1,4 @@ #pragma once void EvaluateRSF(rsf_settings *rsf, ctr_yaml_context *ctx); - -void GET_Option(ctr_yaml_context *ctx, rsf_settings *rsf); -void GET_AccessControlInfo(ctr_yaml_context *ctx, rsf_settings *rsf); -void GET_SystemControlInfo(ctr_yaml_context *ctx, rsf_settings *rsf); -void GET_BasicInfo(ctr_yaml_context *ctx, rsf_settings *rsf); -void GET_Rom(ctr_yaml_context *ctx, rsf_settings *rsf); -void GET_ExeFs(ctr_yaml_context *ctx, rsf_settings *rsf); -void GET_PlainRegion(ctr_yaml_context *ctx, rsf_settings *rsf); -void GET_TitleInfo(ctr_yaml_context *ctx, rsf_settings *rsf); -void GET_CardInfo(ctr_yaml_context *ctx, rsf_settings *rsf); -void GET_CommonHeaderKey(ctr_yaml_context *ctx, rsf_settings *rsf); - void free_RsfSettings(rsf_settings *set); \ No newline at end of file diff --git a/makerom/user_settings.c b/makerom/user_settings.c index ff987a13..1efbc0c3 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -181,8 +181,8 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) set->common.keys.keyset = pki_DEVELOPMENT; else if(strcasecmp(argv[i+1],"retail") == 0 || strcasecmp(argv[i+1],"production") == 0 || strcasecmp(argv[i+1],"p") == 0) set->common.keys.keyset = pki_PRODUCTION; - //else if(strcasecmp(argv[i+1],"custom") == 0 || strcasecmp(argv[i+1],"c") == 0) // given all known keys are here, this isn't needed - // set->common.keys.keyset = pki_CUSTOM; + else if(strcasecmp(argv[i+1],"gw") == 0 || strcasecmp(argv[i+1],"gateway3ds") == 0 || strcasecmp(argv[i+1],"g") == 0) + set->common.keys.keyset = pki_GATEWAY3DS; else{ fprintf(stderr,"[SETTING ERROR] Unrecognised target '%s'\n",argv[i+1]); return USR_BAD_ARG; @@ -866,10 +866,11 @@ void DisplayHelp(char *app_name) printf(" -v Verbose output\n"); printf(" -DNAME=VALUE Substitute values in RSF file\n"); printf("KEY OPTIONS:\n"); - printf(" -target Target for crypto, defaults to 't'\n"); + printf(" -target Target for crypto, defaults to 't'\n"); printf(" 't' Test(false) Keys & prod Certs\n"); printf(" 'd' Development Keys & Certs\n"); printf(" 'p' Production Keys & Certs\n"); + printf(" 'g' Production Keys & Certs for GW3DS only\n"); printf(" -ckeyid Override the automatic common key selection\n"); printf(" -ncchseckey Ncch keyX index ('0'=1.0+, '1'=7.0+)\n"); printf(" -showkeys Display the loaded key chain\n"); diff --git a/makerom/user_settings.h b/makerom/user_settings.h index 89d9a9ef..92838a03 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -62,10 +62,8 @@ static const char output_extention[5][5] = {".cxi",".cfa",".cci",".cia",".app"}; typedef struct { struct{ - // Booleans // Booleans bool MediaFootPadding; - //bool NoPadding; // DELETE bool AllowUnalignedSection; bool EnableCrypt; bool EnableCompress; @@ -74,10 +72,6 @@ typedef struct // Strings char *PageSize; - - // String Collections - //u32 AppendSystemCallNum; // DELETE - //char **AppendSystemCall; // DELETE } Option; struct{ @@ -96,7 +90,6 @@ typedef struct bool SpecialMemoryArrange; // Strings - //char *ProgramId; // DELETE char *IdealProcessor; char *Priority; char *MemoryType; @@ -112,7 +105,6 @@ typedef struct char *AffinityMask; // Strings From DESC char *DescVersion; - //char *CryptoKey; // DELETE char *ResourceLimitCategory; char *ReleaseKernelMajor; char *ReleaseKernelMinor; @@ -133,8 +125,6 @@ typedef struct char **SystemCallAccess; u32 ServiceAccessControlNum; char **ServiceAccessControl; - u32 StorageIdNum; // DELETE - char **StorageId; // DELETE u32 AccessibleSaveDataIdsNum; char **AccessibleSaveDataIds; } AccessControlInfo; @@ -159,14 +149,11 @@ typedef struct char *ProductCode; char *ContentType; char *Logo; - //char *BackupMemoryType;// Delete - //char *InitialCode;// Delete } BasicInfo; struct{ // Strings - char *HostRoot; - //char *Padding; // DELETE + char *RootPath; // String Collections u32 DefaultRejectNum; @@ -177,7 +164,7 @@ typedef struct char **Include; u32 FileNum; char **File; - } Rom; + } RomFs; struct{ u32 TextNum; @@ -193,13 +180,11 @@ typedef struct struct{ // Strings - //char *Platform; // DELETE char *Category; char *UniqueId; char *Version; char *ContentsIndex; char *Variation; - //char *Use; // DELETE char *ChildIndex; char *DemoIndex; char *TargetCategory; From 666ed4d3142034cf45fedd7a5273f701757e6450 Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 20 Sep 2014 22:58:37 +1000 Subject: [PATCH 068/317] makerom: fix bug removed remaining reference to wchar_t in romfs_gen.c --- makerom/romfs_gen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index a2157ed7..5f418019 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -18,7 +18,7 @@ int PopulateRomfs(romfs_buildctx *ctx); void BuildRomfsHeader(romfs_buildctx *ctx); void BuildIvfcHeader(romfs_buildctx *ctx); void GenIvfcHashTree(romfs_buildctx *ctx); -u32 CalcPathHash(u32 parent,wchar_t * path,u32 start,u32 length); +u32 CalcPathHash(u32 parent, fs_romfs_char* path, u32 start, u32 length); int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) From 63852a6ed143e30914872d4f4dcbecba0c45d98f Mon Sep 17 00:00:00 2001 From: applestash Date: Sat, 20 Sep 2014 23:37:11 +1000 Subject: [PATCH 069/317] makerom: misc --- makerom/ncsd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/makerom/ncsd.c b/makerom/ncsd.c index 204587b4..968f1552 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -235,7 +235,9 @@ void GetTitleSaveSize(cci_settings *set) // Adjusting save size - if(set->romInfo.saveSize <= (u64)128*KB) + if(set->romInfo.saveSize == 0) + set->romInfo.saveSize == 0; + else if(set->romInfo.saveSize <= (u64)128*KB) set->romInfo.saveSize = (u64)128*KB; else if(set->romInfo.saveSize <= (u64)512*KB) set->romInfo.saveSize = (u64)512*KB; From 13381800162c92854c424148a4c2952ae8f84e31 Mon Sep 17 00:00:00 2001 From: applestash Date: Sun, 21 Sep 2014 02:03:08 +1000 Subject: [PATCH 070/317] makerom: refactoring refactor exheader/rsf code --- makerom/exheader.c | 859 +++++++++++++++++++-------------------- makerom/exheader.h | 4 +- makerom/exheader_build.h | 2 +- makerom/rsf_settings.c | 9 - makerom/user_settings.h | 2 - 5 files changed, 420 insertions(+), 456 deletions(-) diff --git a/makerom/exheader.c b/makerom/exheader.c index ac902768..d18c1ac3 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -11,23 +11,25 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * int get_ExHeaderSettingsFromRsf(exheader_settings *exhdrset); int get_ExHeaderCodeSetInfo(exhdr_CodeSetInfo *CodeSetInfo, rsf_settings *rsf); -int get_ExHeaderDependencyList(u8 *DependencyList, rsf_settings *rsf); -int get_ExHeaderSystemInfo(exhdr_SystemInfo *SystemInfo, rsf_settings *rsf); -int get_ExHeaderARM11SystemLocalInfo(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf, bool useAccessDescPreset); +int get_ExHeaderDependencyList(u8 *depList, rsf_settings *rsf); +int get_ExHeaderSystemInfo(exhdr_SystemInfo *systemInfo, rsf_settings *rsf); +int get_ExHeaderARM11SystemLocalInfo(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); +int get_ExHeaderARM11SystemLocalInfoLimited(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); int SetARM11SystemLocalInfoFlags(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); -int GetAppType(int *AppType, rsf_settings *rsf); +int GetAppType(rsf_settings *rsf); int SetARM11ResLimitDesc(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); int SetARM11StorageInfo(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); -int SetARM11StorageInfoSystemSaveDataId(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); -int SetARM11StorageInfoExtSaveDataId(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); -int SetARM11StorageInfoOtherUserSaveData(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); +void SetARM11StorageInfoSystemSaveDataId(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); +int SetARM11StorageInfoFsAccessInfo(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); +void SetARM11StorageInfoExtSaveDataId(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); +void SetARM11StorageInfoOtherUserSaveData(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); bool CheckCondiditionsForNewAccessibleSaveDataIds(rsf_settings *rsf); -int SetARM11StorageInfoAccessibleSaveDataIds(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); +void SetARM11StorageInfoAccessibleSaveDataIds(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); int SetARM11ServiceAccessControl(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf); int get_ExHeaderARM11KernelInfo(exhdr_ARM11KernelCapabilities *arm11, rsf_settings *rsf); int SetARM11KernelDescSysCallControl(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf); int GetARM11SysCalls(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf); -void EnableSystemCall(ARM11KernelCapabilityDescriptor *desc, int SysCall); +void EnableSystemCall(ARM11KernelCapabilityDescriptor *desc, int sysCall); void DisableSystemCall(ARM11KernelCapabilityDescriptor *desc, int SysCall); int SetARM11KernelDescInteruptNumList(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf); int GetARM11Interupts(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf); @@ -37,17 +39,17 @@ int GetARM11IOMappings(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf) int GetARM11StaticMappings(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf); bool IsEndAddress(u32 Address); bool IsStartAddress(u32 Address); -u32 GetIOMappingDesc(u32 Address); -u32 GetStaticMappingDesc(u32 Address, bool IsReadOnly); -u32 GetMappingDesc(u32 Address, u32 PrefixVal, s32 numPrefixBits, bool IsRO); +u32 GetIOMappingDesc(u32 address); +u32 GetStaticMappingDesc(u32 address, bool IsReadOnly); +u32 GetMappingDesc(u32 address, u32 prefixVal, s32 numPrefixBits, bool IsRO); int SetARM11KernelDescOtherCapabilities(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf); int SetARM11KernelDescHandleTableSize(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf); int SetARM11KernelDescReleaseKernelVersion(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf); -void SetARM11KernelDescValue(ARM11KernelCapabilityDescriptor *desc, u16 Index, u32 Value); -void SetARM11KernelDescBitmask(ARM11KernelCapabilityDescriptor *desc, u32 Bitmask); -void AllocateARM11KernelDescMemory(ARM11KernelCapabilityDescriptor *desc, u16 Num); +void SetARM11KernelDescValue(ARM11KernelCapabilityDescriptor *desc, u16 index, u32 value); +void SetARM11KernelDescBitmask(ARM11KernelCapabilityDescriptor *desc, u32 bitmask); +void AllocateARM11KernelDescMemory(ARM11KernelCapabilityDescriptor *desc, u16 num); u32 GetDescPrefixMask(int numPrefixBits); -u32 GetDescPrefixBits(int numPrefixBits, u32 PrefixVal); +u32 GetDescPrefixBits(int numPrefixBits, u32 prefixVal); int get_ExHeaderARM9AccessControlInfo(exhdr_ARM9AccessControlInfo *arm9, rsf_settings *rsf); /* ExHeader Signature Functions */ @@ -146,7 +148,7 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * exhdrset->exHdr->codeSetInfo.flag |= infoflag_COMPRESS_EXEFS_0; if(ncchset->options.UseOnSD) exhdrset->exHdr->codeSetInfo.flag |= infoflag_SD_APPLICATION; - if(!ncchset->options.UseRomFS) // Move this later + if(!ncchset->options.UseRomFS) exhdrset->exHdr->arm11SystemLocalCapabilities.storageInfo.otherAttributes |= attribute_NOT_USE_ROMFS; return 0; @@ -155,26 +157,30 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * int get_ExHeaderSettingsFromRsf(exheader_settings *exhdrset) { int result = 0; - result = get_ExHeaderCodeSetInfo(&exhdrset->exHdr->codeSetInfo, exhdrset->rsf); - if(result) goto finish; - if(!exhdrset->useAccessDescPreset){ - result = get_ExHeaderDependencyList((u8*)&exhdrset->exHdr->dependencyList[0], exhdrset->rsf); - if(result) goto finish; + if((result = get_ExHeaderCodeSetInfo(&exhdrset->exHdr->codeSetInfo, exhdrset->rsf))) + goto finish; + if((result = get_ExHeaderDependencyList((u8*)exhdrset->exHdr->dependencyList, exhdrset->rsf))) + goto finish; + if((result = get_ExHeaderSystemInfo(&exhdrset->exHdr->systemInfo, exhdrset->rsf))) + goto finish; + if((result = get_ExHeaderARM11SystemLocalInfo(&exhdrset->exHdr->arm11SystemLocalCapabilities, exhdrset->rsf))) + goto finish; + if((result = get_ExHeaderARM11KernelInfo(&exhdrset->exHdr->arm11KernelCapabilities, exhdrset->rsf))) + goto finish; + if((result = get_ExHeaderARM9AccessControlInfo(&exhdrset->exHdr->arm9AccessControlInfo, exhdrset->rsf))) + goto finish; } - - result = get_ExHeaderSystemInfo(&exhdrset->exHdr->systemInfo, exhdrset->rsf); - if(result) goto finish; - - result = get_ExHeaderARM11SystemLocalInfo(&exhdrset->exHdr->arm11SystemLocalCapabilities, exhdrset->rsf, exhdrset->useAccessDescPreset); - if(result) goto finish; - - if(!exhdrset->useAccessDescPreset){ - result = get_ExHeaderARM11KernelInfo(&exhdrset->exHdr->arm11KernelCapabilities, exhdrset->rsf); - if(result) goto finish; + else{ + if((result = get_ExHeaderCodeSetInfo(&exhdrset->exHdr->codeSetInfo, exhdrset->rsf))) + goto finish; + if((result = get_ExHeaderSystemInfo(&exhdrset->exHdr->systemInfo, exhdrset->rsf))) + goto finish; + if((result = get_ExHeaderARM11SystemLocalInfoLimited(&exhdrset->exHdr->arm11SystemLocalCapabilities, exhdrset->rsf))) + goto finish; + if((result = get_ExHeaderARM9AccessControlInfo(&exhdrset->exHdr->arm9AccessControlInfo, exhdrset->rsf))) + goto finish; } - result = get_ExHeaderARM9AccessControlInfo(&exhdrset->exHdr->arm9AccessControlInfo, exhdrset->rsf); - if(result) goto finish; finish: return result; @@ -186,109 +192,117 @@ int get_ExHeaderCodeSetInfo(exhdr_CodeSetInfo *CodeSetInfo, rsf_settings *rsf) if(rsf->BasicInfo.Title){ //if(strlen(rsf->BasicInfo.Title) > 8){ // fprintf(stderr,"[EXHEADER ERROR] Parameter Too Long \"BasicInfo/Title\"\n"); - // return EXHDR_BAD_YAML_OPT; + // return EXHDR_BAD_RSF_OPT; //} strncpy((char*)CodeSetInfo->name,rsf->BasicInfo.Title,8); } else{ ErrorParamNotFound("BasicInfo/Title"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } + /* Stack Size */ - if(rsf->SystemControlInfo.StackSize){ - u32 StackSize = strtoul(rsf->SystemControlInfo.StackSize,NULL,0); - u32_to_u8(CodeSetInfo->stackSize,StackSize,LE); - } + if(rsf->SystemControlInfo.StackSize) + u32_to_u8(CodeSetInfo->stackSize, strtoul(rsf->SystemControlInfo.StackSize,NULL,0), LE); else{ ErrorParamNotFound("SystemControlInfo/StackSize"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } /* Remaster Version */ - if(rsf->SystemControlInfo.RemasterVersion){ - u16 RemasterVersion = strtol(rsf->SystemControlInfo.RemasterVersion,NULL,0); - u16_to_u8(CodeSetInfo->remasterVersion,RemasterVersion,LE); - } - else{ - u16_to_u8(CodeSetInfo->remasterVersion,0,LE); - } + if(rsf->SystemControlInfo.RemasterVersion) + u16_to_u8(CodeSetInfo->remasterVersion, strtol(rsf->SystemControlInfo.RemasterVersion,NULL,0), LE); + else + u16_to_u8(CodeSetInfo->remasterVersion, 0, LE); + return 0; } -int get_ExHeaderDependencyList(u8 *DependencyList, rsf_settings *rsf) +int get_ExHeaderDependencyList(u8 *depList, rsf_settings *rsf) { if(rsf->SystemControlInfo.DependencyNum > 0x30){ fprintf(stderr,"[EXHEADER ERROR] Too Many Dependency IDs\n"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } for(int i = 0; i < rsf->SystemControlInfo.DependencyNum; i++){ - u8 *pos = (DependencyList + 0x8*i); - u64 TitleID = strtoull(rsf->SystemControlInfo.Dependency[i],NULL,0); - u64_to_u8(pos,TitleID,LE); + u8 *pos = (depList + 0x8*i); + u64_to_u8(pos, strtoull(rsf->SystemControlInfo.Dependency[i],NULL,0), LE); } return 0; } -int get_ExHeaderSystemInfo(exhdr_SystemInfo *SystemInfo, rsf_settings *rsf) +int get_ExHeaderSystemInfo(exhdr_SystemInfo *systemInfo, rsf_settings *rsf) { /* SaveDataSize */ if(rsf->SystemControlInfo.SaveDataSize){ - u64 SaveDataSize = 0; - int ret = GetSaveDataSizeFromString(&SaveDataSize,rsf->SystemControlInfo.SaveDataSize,"EXHEADER"); - if(ret) return ret; - u64_to_u8(SystemInfo->savedataSize,SaveDataSize,LE); - } - else{ - u64_to_u8(SystemInfo->savedataSize,0,LE); + u64 saveSize = 0; + if(GetSaveDataSizeFromString(&saveSize,rsf->SystemControlInfo.SaveDataSize,"EXHEADER")) + return EXHDR_BAD_RSF_OPT; + u64_to_u8(systemInfo->savedataSize, saveSize, LE); } + else + u64_to_u8(systemInfo->savedataSize,0,LE); + /* Jump Id */ - if(rsf->SystemControlInfo.JumpId){ - u64 JumpId = strtoull(rsf->SystemControlInfo.JumpId,NULL,0); - u64_to_u8(SystemInfo->jumpId,JumpId,LE); - } + if(rsf->SystemControlInfo.JumpId) + u64_to_u8(systemInfo->jumpId, strtoull(rsf->SystemControlInfo.JumpId,NULL,0), LE); + else{ - u64 JumpId = 0; - int result = GetProgramID(&JumpId,rsf,false); - if(result) return result; - u64_to_u8(SystemInfo->jumpId,JumpId,LE); + u64 jumpId = 0; + if(GetProgramID(&jumpId,rsf,false)) + return EXHDR_BAD_RSF_OPT; + u64_to_u8(systemInfo->jumpId,jumpId,LE); } return 0; } -int get_ExHeaderARM11SystemLocalInfo(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf, bool useAccessDescPreset) +int get_ExHeaderARM11SystemLocalInfo(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) { /* Program Id */ - u64 ProgramId = 0; - int result = GetProgramID(&ProgramId,rsf,true); - if(result) return result; - u64_to_u8(arm11->programId,ProgramId,LE); + u64 programId = 0; + if(GetProgramID(&programId,rsf,true)) + return EXHDR_BAD_RSF_OPT; + u64_to_u8(arm11->programId,programId,LE); - if(!useAccessDescPreset){ - /* Flags */ - result = SetARM11SystemLocalInfoFlags(arm11, rsf); - if(result) return result; - - /* Resource Limit Descriptors */ - result = SetARM11ResLimitDesc(arm11, rsf); - if(result) return result; - } + /* Flags */ + if(SetARM11SystemLocalInfoFlags(arm11, rsf)) + return EXHDR_BAD_RSF_OPT; + + /* Resource Limit Descriptors */ + if(SetARM11ResLimitDesc(arm11, rsf)) + return EXHDR_BAD_RSF_OPT; /* Storage Info */ - result = SetARM11StorageInfo(arm11, rsf); - if(result) return result; - - if(!useAccessDescPreset){ - /* Service Access Control */ - result = SetARM11ServiceAccessControl(arm11, rsf); - if(result) return result; - - /* Resource Limit Category */ - if(rsf->AccessControlInfo.ResourceLimitCategory){ - if(strcasecmp(rsf->AccessControlInfo.ResourceLimitCategory,"application") == 0) arm11->resourceLimitCategory = resrc_limit_APPLICATION; - else if(strcasecmp(rsf->AccessControlInfo.ResourceLimitCategory,"sysapplet") == 0) arm11->resourceLimitCategory = resrc_limit_SYS_APPLET; - else if(strcasecmp(rsf->AccessControlInfo.ResourceLimitCategory,"libapplet") == 0) arm11->resourceLimitCategory = resrc_limit_LIB_APPLET; - else if(strcasecmp(rsf->AccessControlInfo.ResourceLimitCategory,"other") == 0) arm11->resourceLimitCategory = resrc_limit_OTHER; - } + if(SetARM11StorageInfo(arm11, rsf)) + return EXHDR_BAD_RSF_OPT; + + /* Service Access Control */ + if(SetARM11ServiceAccessControl(arm11, rsf)) + return EXHDR_BAD_RSF_OPT; + + /* Resource Limit Category */ + if(rsf->AccessControlInfo.ResourceLimitCategory){ + if(strcasecmp(rsf->AccessControlInfo.ResourceLimitCategory,"application") == 0) arm11->resourceLimitCategory = resrc_limit_APPLICATION; + else if(strcasecmp(rsf->AccessControlInfo.ResourceLimitCategory,"sysapplet") == 0) arm11->resourceLimitCategory = resrc_limit_SYS_APPLET; + else if(strcasecmp(rsf->AccessControlInfo.ResourceLimitCategory,"libapplet") == 0) arm11->resourceLimitCategory = resrc_limit_LIB_APPLET; + else if(strcasecmp(rsf->AccessControlInfo.ResourceLimitCategory,"other") == 0) arm11->resourceLimitCategory = resrc_limit_OTHER; } + + /* Finish */ + return 0; +} + +int get_ExHeaderARM11SystemLocalInfoLimited(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) +{ + /* Program Id */ + u64 programId = 0; + if(GetProgramID(&programId,rsf,true)) + return EXHDR_BAD_RSF_OPT; + u64_to_u8(arm11->programId,programId,LE); + + /* Storage Info */ + if(SetARM11StorageInfo(arm11, rsf)) + return EXHDR_BAD_RSF_OPT; + /* Finish */ return 0; } @@ -296,72 +310,69 @@ int get_ExHeaderARM11SystemLocalInfo(exhdr_ARM11SystemLocalCapabilities *arm11, int SetARM11SystemLocalInfoFlags(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) { /* Core Version */ - if(rsf->AccessControlInfo.CoreVersion){ - u32 Version = strtoul(rsf->AccessControlInfo.CoreVersion,NULL,0); - u32_to_u8(arm11->coreVersion,Version,LE); - } + if(rsf->AccessControlInfo.CoreVersion) + u32_to_u8(arm11->coreVersion,strtoul(rsf->AccessControlInfo.CoreVersion,NULL,0),LE); else{ ErrorParamNotFound("AccessControlInfo/CoreVersion"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } /* Flag */ - u8 AffinityMask = 0; - u8 IdealProcessor = 0; - u8 SystemMode = 0; + u8 affinityMask = 0; + u8 idealProcessor = 0; + u8 systemMode = 0; + if(rsf->AccessControlInfo.AffinityMask){ - AffinityMask = strtol(rsf->AccessControlInfo.AffinityMask,NULL,0); - if(AffinityMask > 1){ - fprintf(stderr,"[EXHEADER ERROR] Unexpected AffinityMask: %d. Expected range: 0x0 - 0x1\n",AffinityMask); - return EXHDR_BAD_YAML_OPT; + affinityMask = strtol(rsf->AccessControlInfo.AffinityMask,NULL,0); + if(affinityMask > 1){ + fprintf(stderr,"[EXHEADER ERROR] Unexpected AffinityMask: %d. Expected range: 0x0 - 0x1\n",affinityMask); + return EXHDR_BAD_RSF_OPT; } } if(rsf->AccessControlInfo.IdealProcessor){ - IdealProcessor = strtol(rsf->AccessControlInfo.IdealProcessor,NULL,0); - if(IdealProcessor > 1){ - fprintf(stderr,"[EXHEADER ERROR] Unexpected IdealProcessor: %d. Expected range: 0x0 - 0x1\n",IdealProcessor); - return EXHDR_BAD_YAML_OPT; + idealProcessor = strtol(rsf->AccessControlInfo.IdealProcessor,NULL,0); + if(idealProcessor > 1){ + fprintf(stderr,"[EXHEADER ERROR] Unexpected IdealProcessor: %d. Expected range: 0x0 - 0x1\n",idealProcessor); + return EXHDR_BAD_RSF_OPT; } } if(rsf->AccessControlInfo.SystemMode){ - SystemMode = strtol(rsf->AccessControlInfo.SystemMode,NULL,0); - if(SystemMode > 15){ - fprintf(stderr,"[EXHEADER ERROR] Unexpected SystemMode: 0x%x. Expected range: 0x0 - 0xf\n",SystemMode); - return EXHDR_BAD_YAML_OPT; + systemMode = strtol(rsf->AccessControlInfo.SystemMode,NULL,0); + if(systemMode > 15){ + fprintf(stderr,"[EXHEADER ERROR] Unexpected SystemMode: 0x%x. Expected range: 0x0 - 0xf\n",systemMode); + return EXHDR_BAD_RSF_OPT; } } - arm11->flag = (u8)(SystemMode << 4 | AffinityMask << 2 | IdealProcessor); + arm11->flag = (u8)(systemMode << 4 | affinityMask << 2 | idealProcessor); /* Thread Priority */ if(rsf->AccessControlInfo.Priority){ - u8 Priority = strtoul(rsf->AccessControlInfo.Priority,NULL,0); - int ProccessType = 0; - GetAppType(&ProccessType,rsf); - if(ProccessType == processtype_APPLICATION || ProccessType == processtype_DEFAULT){ - Priority += 32; - } - if(Priority > 127){ - fprintf(stderr,"[EXHEADER ERROR] Invalid Priority: %d\n",Priority); - return EXHDR_BAD_YAML_OPT; + u8 priority = strtoul(rsf->AccessControlInfo.Priority,NULL,0); + if(GetAppType(rsf) == processtype_APPLICATION) + priority += 32; + if(priority > 127){ + fprintf(stderr,"[EXHEADER ERROR] Invalid Priority: %d\n",priority); + return EXHDR_BAD_RSF_OPT; } - arm11->priority = Priority; + arm11->priority = priority; } else{ ErrorParamNotFound("AccessControlInfo/Priority"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } return 0; } -int GetAppType(int *AppType, rsf_settings *rsf) -{ - *AppType = processtype_DEFAULT; +int GetAppType(rsf_settings *rsf) +{ if(rsf->SystemControlInfo.AppType){ - if(strcasecmp(rsf->SystemControlInfo.AppType,"application") == 0) *AppType = processtype_APPLICATION; - else if(strcasecmp(rsf->SystemControlInfo.AppType,"system") == 0) *AppType = processtype_SYSTEM; + if(strcasecmp(rsf->SystemControlInfo.AppType,"application") == 0) + return processtype_APPLICATION; + else if(strcasecmp(rsf->SystemControlInfo.AppType,"system") == 0) + return processtype_SYSTEM; } - return 0; + return processtype_APPLICATION; } int SetARM11ResLimitDesc(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) @@ -381,16 +392,15 @@ int SetARM11ResLimitDesc(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings int SetARM11StorageInfo(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) { - if(rsf->AccessControlInfo.UseExtendedSaveDataAccessControl || rsf->AccessControlInfo.AccessibleSaveDataIds){ + if(rsf->AccessControlInfo.AccessibleSaveDataIds){ /* Accessible SaveData IDs */ if(!CheckCondiditionsForNewAccessibleSaveDataIds(rsf)) - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; SetARM11StorageInfoAccessibleSaveDataIds(arm11,rsf); } else{ /* Extdata Id */ - int ret = SetARM11StorageInfoExtSaveDataId(arm11,rsf); - if(ret) return ret; + SetARM11StorageInfoExtSaveDataId(arm11,rsf); /* OtherUserSaveData */ SetARM11StorageInfoOtherUserSaveData(arm11,rsf); } @@ -399,172 +409,142 @@ int SetARM11StorageInfo(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings SetARM11StorageInfoSystemSaveDataId(arm11,rsf); /* FileSystem Access Info */ - u32 AccessInfo = 0; + return SetARM11StorageInfoFsAccessInfo(arm11,rsf); +} + +int SetARM11StorageInfoFsAccessInfo(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) +{ + u32 accessInfo = 0; for(int i = 0; i < rsf->AccessControlInfo.FileSystemAccessNum; i++){ if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"CategorySystemApplication") == 0) - AccessInfo |= fsaccess_CATEGORY_SYSTEM_APPLICATION; + accessInfo |= fsaccess_CATEGORY_SYSTEM_APPLICATION; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"CategoryHardwareCheck") == 0) - AccessInfo |= fsaccess_CATEGORY_HARDWARE_CHECK; + accessInfo |= fsaccess_CATEGORY_HARDWARE_CHECK; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"CategoryFileSystemTool") == 0) - AccessInfo |= fsaccess_CATEGORY_FILE_SYSTEM_TOOL; + accessInfo |= fsaccess_CATEGORY_FILE_SYSTEM_TOOL; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"Debug") == 0) - AccessInfo |= fsaccess_DEBUG; + accessInfo |= fsaccess_DEBUG; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"TwlCardBackup") == 0) - AccessInfo |= fsaccess_TWL_CARD_BACKUP; + accessInfo |= fsaccess_TWL_CARD_BACKUP; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"TwlNandData") == 0) - AccessInfo |= fsaccess_TWL_NAND_DATA; + accessInfo |= fsaccess_TWL_NAND_DATA; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"Boss") == 0) - AccessInfo |= fsaccess_BOSS; + accessInfo |= fsaccess_BOSS; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"DirectSdmc") == 0) - AccessInfo |= fsaccess_DIRECT_SDMC; + accessInfo |= fsaccess_DIRECT_SDMC; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"Core") == 0) - AccessInfo |= fsaccess_CORE; + accessInfo |= fsaccess_CORE; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"CtrNandRo") == 0) - AccessInfo |= fsaccess_CTR_NAND_RO; + accessInfo |= fsaccess_CTR_NAND_RO; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"CtrNandRw") == 0) - AccessInfo |= fsaccess_CTR_NAND_RW; + accessInfo |= fsaccess_CTR_NAND_RW; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"CtrNandRoWrite") == 0) - AccessInfo |= fsaccess_CTR_NAND_RO_WRITE; + accessInfo |= fsaccess_CTR_NAND_RO_WRITE; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"CategorySystemSettings") == 0) - AccessInfo |= fsaccess_CATEGORY_SYSTEM_SETTINGS; + accessInfo |= fsaccess_CATEGORY_SYSTEM_SETTINGS; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"CardBoard") == 0) - AccessInfo |= fsaccess_CARD_BOARD; + accessInfo |= fsaccess_CARD_BOARD; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"ExportImportIvs") == 0) - AccessInfo |= fsaccess_EXPORT_IMPORT_IVS; + accessInfo |= fsaccess_EXPORT_IMPORT_IVS; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"DirectSdmcWrite") == 0) - AccessInfo |= fsaccess_DIRECT_SDMC_WRITE; + accessInfo |= fsaccess_DIRECT_SDMC_WRITE; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"SwitchCleanup") == 0) - AccessInfo |= fsaccess_SWITCH_CLEANUP; + accessInfo |= fsaccess_SWITCH_CLEANUP; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"SaveDataMove") == 0) - AccessInfo |= fsaccess_SAVE_DATA_MOVE; + accessInfo |= fsaccess_SAVE_DATA_MOVE; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"Shop") == 0) - AccessInfo |= fsaccess_SHOP; + accessInfo |= fsaccess_SHOP; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"Shell") == 0) - AccessInfo |= fsaccess_SHELL; + accessInfo |= fsaccess_SHELL; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"CategoryHomeMenu") == 0) - AccessInfo |= fsaccess_CATEGORY_HOME_MENU; + accessInfo |= fsaccess_CATEGORY_HOME_MENU; else{ fprintf(stderr,"[EXHEADER ERROR] Invalid FileSystemAccess Name: \"%s\"\n",rsf->AccessControlInfo.FileSystemAccess[i]); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } } - u32_to_u8(arm11->storageInfo.accessInfo,AccessInfo,LE); + u32_to_u8(arm11->storageInfo.accessInfo,accessInfo,LE); + return 0; } -int SetARM11StorageInfoSystemSaveDataId(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) +void SetARM11StorageInfoSystemSaveDataId(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) { - if(rsf->AccessControlInfo.SystemSaveDataId1){ - u32 SaveId = strtoul(rsf->AccessControlInfo.SystemSaveDataId1,NULL,0); - u32_to_u8(arm11->storageInfo.systemSavedataId,SaveId,LE); - } - if(rsf->AccessControlInfo.SystemSaveDataId2){ - u32 SaveId = strtoul(rsf->AccessControlInfo.SystemSaveDataId2,NULL,0); - u32_to_u8(&arm11->storageInfo.systemSavedataId[4],SaveId,LE); - } - return 0; + if(rsf->AccessControlInfo.SystemSaveDataId1) + u32_to_u8(arm11->storageInfo.systemSavedataId[0], strtoul(rsf->AccessControlInfo.SystemSaveDataId1,NULL,0), LE); + + if(rsf->AccessControlInfo.SystemSaveDataId2) + u32_to_u8(arm11->storageInfo.systemSavedataId[1], strtoul(rsf->AccessControlInfo.SystemSaveDataId2,NULL,0), LE); } -int SetARM11StorageInfoExtSaveDataId(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) +void SetARM11StorageInfoExtSaveDataId(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) { - if(rsf->AccessControlInfo.ExtSaveDataId){ - if(!rsf->AccessControlInfo.UseExtSaveData){ - fprintf(stderr,"[EXHEADER ERROR] Failed to set ExtSaveDataId. UseExtSaveData must be true.\n"); - return EXHDR_BAD_YAML_OPT; - } - u64 ExtdataId = strtoull(rsf->AccessControlInfo.ExtSaveDataId,NULL,0); - u64_to_u8(arm11->storageInfo.extSavedataId,ExtdataId,LE); - } - return 0; + if(rsf->AccessControlInfo.ExtSaveDataId) + u64_to_u8(arm11->storageInfo.extSavedataId, strtoull(rsf->AccessControlInfo.ExtSaveDataId,NULL,0), LE); } -int SetARM11StorageInfoOtherUserSaveData(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) +void SetARM11StorageInfoOtherUserSaveData(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) { - u64 Value = 0; + u64 value = 0; if(rsf->AccessControlInfo.OtherUserSaveDataId1) - Value = 0xffffff & strtoul(rsf->AccessControlInfo.OtherUserSaveDataId1,NULL,0); - Value = Value << 20; + value = 0xffffff & strtoul(rsf->AccessControlInfo.OtherUserSaveDataId1,NULL,0); + value = value << 20; if(rsf->AccessControlInfo.OtherUserSaveDataId2) - Value |= 0xffffff & strtoul(rsf->AccessControlInfo.OtherUserSaveDataId2,NULL,0); - Value = Value << 20; + value |= 0xffffff & strtoul(rsf->AccessControlInfo.OtherUserSaveDataId2,NULL,0); + value = value << 20; if(rsf->AccessControlInfo.OtherUserSaveDataId3) - Value |= 0xffffff & strtoul(rsf->AccessControlInfo.OtherUserSaveDataId3,NULL,0); + value |= 0xffffff & strtoul(rsf->AccessControlInfo.OtherUserSaveDataId3,NULL,0); /* UseOtherVariationSaveData Flag */ if(rsf->AccessControlInfo.UseOtherVariationSaveData) - Value |= 0x1000000000000000; + value |= 0x1000000000000000; - u64_to_u8(arm11->storageInfo.storageAccessableUniqueIds,Value,LE); - return 0; + u64_to_u8(arm11->storageInfo.storageAccessableUniqueIds,value,LE); } bool CheckCondiditionsForNewAccessibleSaveDataIds(rsf_settings *rsf) { - if(!rsf->AccessControlInfo.UseExtendedSaveDataAccessControl){ - if(rsf->AccessControlInfo.AccessibleSaveDataIds) - fprintf(stderr,"[EXHEADER ERROR] AccessibleSaveDataIds is unavailable if UseExtendedSaveDataAccessControl is false.\n"); - return false; - } - - /* - if(rsf->AccessControlInfo.AccessibleSaveDataIdsNum == 0){ - fprintf(stderr,"[EXHEADER ERROR] AccessibleSaveDataIds must be specified if UseExtendedSaveDataAccessControl is true.\n"); - return false; - } - */ - if(rsf->AccessControlInfo.AccessibleSaveDataIdsNum > 6){ fprintf(stderr,"[EXHEADER ERROR] Too many UniqueId in \"AccessibleSaveDataIds\".\n"); return false; } - - if(rsf->AccessControlInfo.UseExtSaveData){ - fprintf(stderr,"[EXHEADER ERROR] UseExtSaveData must be false if AccessibleSaveDataIds is specified.\n"); - return false; - } if (rsf->AccessControlInfo.ExtSaveDataId){ fprintf(stderr,"[EXHEADER ERROR] ExtSaveDataId is unavailable if AccessibleSaveDataIds is specified.\n"); return false; } - if (rsf->AccessControlInfo.OtherUserSaveDataId1){ - if(strtoul(rsf->AccessControlInfo.OtherUserSaveDataId1,NULL,0) > 0){ - fprintf(stderr,"[EXHEADER ERROR] OtherUserSaveDataId1 must be 0 if AccessibleSaveDataIds is specified.\n"); - return false; - } + if (rsf->AccessControlInfo.OtherUserSaveDataId1 && strtoul(rsf->AccessControlInfo.OtherUserSaveDataId1,NULL,0) > 0){ + fprintf(stderr,"[EXHEADER ERROR] OtherUserSaveDataId1 must be 0 if AccessibleSaveDataIds is specified.\n"); + return false; } - if (rsf->AccessControlInfo.OtherUserSaveDataId2){ - if(strtoul(rsf->AccessControlInfo.OtherUserSaveDataId2,NULL,0) > 0){ - fprintf(stderr,"[EXHEADER ERROR] OtherUserSaveDataId2 must be 0 if AccessibleSaveDataIds is specified.\n"); - return false; - } + if (rsf->AccessControlInfo.OtherUserSaveDataId2 && strtoul(rsf->AccessControlInfo.OtherUserSaveDataId2,NULL,0) > 0){ + fprintf(stderr,"[EXHEADER ERROR] OtherUserSaveDataId2 must be 0 if AccessibleSaveDataIds is specified.\n"); + return false; } - if (rsf->AccessControlInfo.OtherUserSaveDataId3){ - if(strtoul(rsf->AccessControlInfo.OtherUserSaveDataId3,NULL,0) > 0){ - fprintf(stderr,"[EXHEADER ERROR] OtherUserSaveDataId3 must be 0 if AccessibleSaveDataIds is specified.\n"); - return false; - } + if (rsf->AccessControlInfo.OtherUserSaveDataId3 && strtoul(rsf->AccessControlInfo.OtherUserSaveDataId3,NULL,0) > 0){ + fprintf(stderr,"[EXHEADER ERROR] OtherUserSaveDataId3 must be 0 if AccessibleSaveDataIds is specified.\n"); + return false; } return true; } -int SetARM11StorageInfoAccessibleSaveDataIds(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) +void SetARM11StorageInfoAccessibleSaveDataIds(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) { - u64 RegionExtSaveDataId = 0; - u64 RegionOtherUseSaveData = 0; + u64 region_ExtSaveDataId = 0; + u64 region_OtherUseSaveData = 0; if(rsf->AccessControlInfo.AccessibleSaveDataIdsNum > 0){ - u32 Max = rsf->AccessControlInfo.AccessibleSaveDataIdsNum < 3 ? rsf->AccessControlInfo.AccessibleSaveDataIdsNum : 3; - for(int i = 0; i < Max; i++){ - u32 UniqueID = 0xffffff & strtoul(rsf->AccessControlInfo.AccessibleSaveDataIds[i],NULL,0); - RegionOtherUseSaveData = RegionOtherUseSaveData << 20; - RegionOtherUseSaveData |= UniqueID; + u32 max = rsf->AccessControlInfo.AccessibleSaveDataIdsNum < 3 ? rsf->AccessControlInfo.AccessibleSaveDataIdsNum : 3; + for(int i = 0; i < max; i++){ + u32 uniqueID = 0xffffff & strtoul(rsf->AccessControlInfo.AccessibleSaveDataIds[i],NULL,0); + region_OtherUseSaveData = region_OtherUseSaveData << 20; + region_OtherUseSaveData |= uniqueID; } } if(rsf->AccessControlInfo.AccessibleSaveDataIdsNum > 3){ for(int i = 3; i < rsf->AccessControlInfo.AccessibleSaveDataIdsNum; i++){ - u32 UniqueID = 0xffffff & strtoul(rsf->AccessControlInfo.AccessibleSaveDataIds[i],NULL,0); - RegionExtSaveDataId = RegionExtSaveDataId << 20; - RegionExtSaveDataId |= UniqueID; + u32 uniqueID = 0xffffff & strtoul(rsf->AccessControlInfo.AccessibleSaveDataIds[i],NULL,0); + region_ExtSaveDataId = region_ExtSaveDataId << 20; + region_ExtSaveDataId |= uniqueID; } } @@ -572,11 +552,10 @@ int SetARM11StorageInfoAccessibleSaveDataIds(exhdr_ARM11SystemLocalCapabilities /* UseOtherVariationSaveData Flag */ if(rsf->AccessControlInfo.UseOtherVariationSaveData) - RegionOtherUseSaveData |= 0x1000000000000000; + region_OtherUseSaveData |= 0x1000000000000000; - u64_to_u8(arm11->storageInfo.extSavedataId,RegionExtSaveDataId,LE); - u64_to_u8(arm11->storageInfo.storageAccessableUniqueIds,RegionOtherUseSaveData,LE); - return 0; + u64_to_u8(arm11->storageInfo.extSavedataId,region_ExtSaveDataId,LE); + u64_to_u8(arm11->storageInfo.storageAccessableUniqueIds,region_OtherUseSaveData,LE); } int SetARM11ServiceAccessControl(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) @@ -584,20 +563,19 @@ int SetARM11ServiceAccessControl(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_ if(rsf->AccessControlInfo.ServiceAccessControl){ if(rsf->AccessControlInfo.ServiceAccessControlNum > 32){ fprintf(stderr,"[EXHEADER ERROR] Too Many Service Names, maximum is 32\n"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } for(int i = 0; i < rsf->AccessControlInfo.ServiceAccessControlNum; i++){ - int svc_handle_len = strlen(rsf->AccessControlInfo.ServiceAccessControl[i]); - if(svc_handle_len > 8){ + if(strlen(rsf->AccessControlInfo.ServiceAccessControl[i]) > 8){ fprintf(stderr,"[EXHEADER ERROR] Service Name: \"%s\" is too long\n",rsf->AccessControlInfo.ServiceAccessControl[i]); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } - memcpy(arm11->serviceAccessControl[i],rsf->AccessControlInfo.ServiceAccessControl[i],svc_handle_len); + strncpy((char*)arm11->serviceAccessControl[i],rsf->AccessControlInfo.ServiceAccessControl[i],8); } } else{ ErrorParamNotFound("AccessControlInfo/ServiceAccessControl"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } return 0; } @@ -605,87 +583,89 @@ int SetARM11ServiceAccessControl(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_ int get_ExHeaderARM11KernelInfo(exhdr_ARM11KernelCapabilities *arm11, rsf_settings *rsf) { int result = 0; + u16 totalDesc, descIndex; ARM11KernelCapabilityDescriptor desc[6]; - memset(&desc,0,sizeof(ARM11KernelCapabilityDescriptor)*6); + clrmem(&desc,sizeof(ARM11KernelCapabilityDescriptor)*6); /* Get Descriptors */ - result = SetARM11KernelDescSysCallControl(&desc[0],rsf); - if(result) goto finish; - result = SetARM11KernelDescInteruptNumList(&desc[1],rsf); - if(result) goto finish; - result = SetARM11KernelDescAddressMapping(&desc[2],rsf); - if(result) goto finish; - result = SetARM11KernelDescOtherCapabilities(&desc[3],rsf); - if(result) goto finish; - result = SetARM11KernelDescHandleTableSize(&desc[4],rsf); - if(result) goto finish; - result = SetARM11KernelDescReleaseKernelVersion(&desc[5],rsf); + if((result = SetARM11KernelDescSysCallControl(&desc[0],rsf))) + goto finish; + if((result = SetARM11KernelDescInteruptNumList(&desc[1],rsf))) + goto finish; + if((result = SetARM11KernelDescAddressMapping(&desc[2],rsf))) + goto finish; + if((result = SetARM11KernelDescOtherCapabilities(&desc[3],rsf))) + goto finish; + if((result = SetARM11KernelDescHandleTableSize(&desc[4],rsf))) + goto finish; + if((result = SetARM11KernelDescReleaseKernelVersion(&desc[5],rsf))) + goto finish; /* Write Descriptors To Exheader */ - u16 TotalDesc = 0; - for(int i = 0; i < 6; i++){ - TotalDesc += desc[i].num; - } - if(TotalDesc >= 28){ + totalDesc = 0; + for(int i = 0; i < 6; i++) + totalDesc += desc[i].num; + + if(totalDesc >= 28){ fprintf(stderr,"[EXHEADER ERROR] Too many Kernel Capabilities.\n"); - result = EXHDR_BAD_YAML_OPT; + result = EXHDR_BAD_RSF_OPT; goto finish; } - u16 DescIndex = 0; + + descIndex = 0; for(int i = 0; i < 6; i++){ for(int j = 0; j < desc[i].num; j++){ - u32_to_u8(arm11->descriptors[DescIndex],desc[i].Data[j],LE); - DescIndex++; + u32_to_u8(arm11->descriptors[descIndex],desc[i].data[j],LE); + descIndex++; } } /* Fill Remaining Descriptors with 0xffffffff */ - for(int i = DescIndex; i < 28; i++){ + for(int i = descIndex; i < 28; i++) u32_to_u8(arm11->descriptors[i],0xffffffff,LE); - } finish: - for(int i = 0; i < 6; i++){ - free(desc[i].Data); - } + for(int i = 0; i < 6; i++) + free(desc[i].data); return result; } int SetARM11KernelDescSysCallControl(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf) { int ret = 0; + u16 activeSysCallDesc, sysCallDescPos; // Create Temporary Descriptor ARM11KernelCapabilityDescriptor tmp; - memset(&tmp,0,sizeof(ARM11KernelCapabilityDescriptor)); + clrmem(&tmp,sizeof(ARM11KernelCapabilityDescriptor)); AllocateARM11KernelDescMemory(&tmp,8); for(int i = 0; i < 8; i++) SetARM11KernelDescValue(&tmp,i,desc_SysCallControl | (i << 24)); // Get SysCalls - ret = GetARM11SysCalls(&tmp,rsf); - if(ret) goto finish; + if((ret = GetARM11SysCalls(&tmp,rsf))) + goto finish; // Count Active Syscall Descs - u16 ActiveSysCallDesc = 0; + activeSysCallDesc = 0; for(int i = 0; i < 8; i++) - if((tmp.Data[i] & 0x00ffffff) != 0) - ActiveSysCallDesc++; + if((tmp.data[i] & 0x00ffffff) != 0) + activeSysCallDesc++; // Transfer Active Syscall Descs to out Descriptor - AllocateARM11KernelDescMemory(desc,ActiveSysCallDesc); - u16 SysCallDescPos = 0; + AllocateARM11KernelDescMemory(desc,activeSysCallDesc); + sysCallDescPos = 0; for(int i = 0; i < 8; i++){ - if((tmp.Data[i] & 0x00ffffff) != 0) { - SetARM11KernelDescValue(desc,SysCallDescPos,tmp.Data[i]); - SysCallDescPos++; + if((tmp.data[i] & 0x00ffffff) != 0) { + SetARM11KernelDescValue(desc,sysCallDescPos,tmp.data[i]); + sysCallDescPos++; } } finish: // Free data in Temporary Descriptor - free(tmp.Data); + free(tmp.data); return ret; } @@ -693,38 +673,39 @@ int GetARM11SysCalls(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf) { if(!rsf->AccessControlInfo.SystemCallAccess){ ErrorParamNotFound("AccessControlInfo/SystemCallAccess"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } for(int i = 0; i < rsf->AccessControlInfo.SystemCallAccessNum; i++){ - int SysCall = strtoul(rsf->AccessControlInfo.SystemCallAccess[i],NULL,0); - if(SysCall > 184){ - fprintf(stderr,"[EXHEADER ERROR] Unexpected Syscall: 0x%02x. Expected Range: 0x00 - 0xB8\n",SysCall); - return EXHDR_BAD_YAML_OPT; + int sysCall = strtoul(rsf->AccessControlInfo.SystemCallAccess[i],NULL,0); + if(sysCall > 184){ + fprintf(stderr,"[EXHEADER ERROR] Unexpected Syscall: 0x%02x. Expected Range: 0x00 - 0xB8\n",sysCall); + return EXHDR_BAD_RSF_OPT; } - EnableSystemCall(desc,SysCall); + EnableSystemCall(desc,sysCall); } return 0; } -void EnableSystemCall(ARM11KernelCapabilityDescriptor *desc, int SysCall) +void EnableSystemCall(ARM11KernelCapabilityDescriptor *desc, int sysCall) { - int num = SysCall / 24; - int num1 = SysCall % 24; - desc->Data[num] |= 1 << (num1 & 31); + int num = sysCall / 24; + int num1 = sysCall % 24; + desc->data[num] |= 1 << (num1 & 31); } -void DisableSystemCall(ARM11KernelCapabilityDescriptor *desc, int SysCall) +void DisableSystemCall(ARM11KernelCapabilityDescriptor *desc, int sysCall) { - int num = SysCall / 24; - int num1 = SysCall % 24; - desc->Data[num] = desc->Data[num] & ~(1 << (num1 & 31)); + int num = sysCall / 24; + int num1 = sysCall % 24; + desc->data[num] = desc->data[num] & ~(1 << (num1 & 31)); } int SetARM11KernelDescInteruptNumList(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf) { int ret = 0; - + u16 activeInteruptDesc, interuptDescPos; + // Create Temporary Descriptor ARM11KernelCapabilityDescriptor tmp; memset(&tmp,0,sizeof(ARM11KernelCapabilityDescriptor)); @@ -736,43 +717,43 @@ int SetARM11KernelDescInteruptNumList(ARM11KernelCapabilityDescriptor *desc, rsf if(ret) goto finish; // Count Active Interupt Descs - u16 ActiveInteruptDesc = 0; + activeInteruptDesc = 0; for(int i = 0; i < 8; i++) - if(tmp.Data[i]) - ActiveInteruptDesc++; + if(tmp.data[i]) + activeInteruptDesc++; // Transfer Active Interupt Descs to output Descriptor - AllocateARM11KernelDescMemory(desc,ActiveInteruptDesc); - u16 InteruptDescPos = 0; + AllocateARM11KernelDescMemory(desc,activeInteruptDesc); + interuptDescPos = 0; for(int i = 0; i < 8; i++){ - if(tmp.Data[i]) { - SetARM11KernelDescValue(desc,InteruptDescPos,(tmp.Data[i] & 0x0fffffff) | desc_InteruptNumList); - InteruptDescPos++; + if(tmp.data[i]) { + SetARM11KernelDescValue(desc,interuptDescPos,(tmp.data[i] & 0x0fffffff) | desc_InteruptNumList); + interuptDescPos++; } } finish: // Free data in Temporary Descriptor - free(tmp.Data); + free(tmp.data); return ret; } int GetARM11Interupts(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf) { - if(!rsf->AccessControlInfo.InterruptNumbers){ + if(!rsf->AccessControlInfo.InterruptNumbers) return 0; - } + if(rsf->AccessControlInfo.InterruptNumbersNum > 32){ fprintf(stderr,"[EXHEADER ERROR] Too many Interupts. Maximum is 32\n"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } for(int i = 0; i < rsf->AccessControlInfo.InterruptNumbersNum; i++){ - int Interrupt = strtoul(rsf->AccessControlInfo.InterruptNumbers[i],NULL,0); - if(Interrupt > 0x7f){ - fprintf(stderr,"[EXHEADER ERROR] Unexpected Interupt: 0x%02x. Expected Range: 0x00 - 0x7f\n",Interrupt); - return EXHDR_BAD_YAML_OPT; + int interrupt = strtoul(rsf->AccessControlInfo.InterruptNumbers[i],NULL,0); + if(interrupt > 0x7f){ + fprintf(stderr,"[EXHEADER ERROR] Unexpected Interupt: 0x%02x. Expected Range: 0x00 - 0x7f\n",interrupt); + return EXHDR_BAD_RSF_OPT; } - EnableInterupt(desc,Interrupt,i); + EnableInterupt(desc,interrupt,i); } return 0; @@ -781,44 +762,46 @@ int GetARM11Interupts(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf) void EnableInterupt(ARM11KernelCapabilityDescriptor *desc, int Interrupt, int i) { int num = i / 4; - if(num*4 == i) desc->Data[num] |= 0xffffffff; - desc->Data[num] = desc->Data[num] << 7; - desc->Data[num] |= Interrupt; + if(num*4 == i) desc->data[num] |= 0xffffffff; + desc->data[num] = desc->data[num] << 7; + desc->data[num] |= Interrupt; } int SetARM11KernelDescAddressMapping(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf) { int ret = 0; + u16 memMapDescPos; + // Create Temporary Descriptors ARM11KernelCapabilityDescriptor io_tmp; - memset(&io_tmp,0,sizeof(ARM11KernelCapabilityDescriptor)); + clrmem(&io_tmp,sizeof(ARM11KernelCapabilityDescriptor)); ARM11KernelCapabilityDescriptor static_tmp; - memset(&static_tmp,0,sizeof(ARM11KernelCapabilityDescriptor)); + clrmem(&static_tmp,sizeof(ARM11KernelCapabilityDescriptor)); // Getting IO Mapping - ret = GetARM11IOMappings(&io_tmp,rsf); - if(ret) goto finish; + if((ret = GetARM11IOMappings(&io_tmp,rsf))) + goto finish; // Getting Static Mapping - ret = GetARM11StaticMappings(&static_tmp,rsf); - if(ret) goto finish; + if((ret = GetARM11StaticMappings(&static_tmp,rsf))) + goto finish; // Creating Output Descriptor and Combining the two MemMap Descriptors AllocateARM11KernelDescMemory(desc,io_tmp.num+static_tmp.num); - u16 MemMapDescPos = 0; + memMapDescPos = 0; for(int i = 0; i < io_tmp.num; i++){ - SetARM11KernelDescValue(desc,MemMapDescPos,io_tmp.Data[i]); - MemMapDescPos++; + SetARM11KernelDescValue(desc,memMapDescPos,io_tmp.data[i]); + memMapDescPos++; } for(int i = 0; i < static_tmp.num; i++){ - SetARM11KernelDescValue(desc,MemMapDescPos,static_tmp.Data[i]); - MemMapDescPos++; + SetARM11KernelDescValue(desc,memMapDescPos,static_tmp.data[i]); + memMapDescPos++; } finish: - free(io_tmp.Data); - free(static_tmp.Data); + free(io_tmp.data); + free(static_tmp.data); return ret; } @@ -826,9 +809,10 @@ int GetARM11IOMappings(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf) { if(!rsf->AccessControlInfo.IORegisterMapping) return 0; - + AllocateARM11KernelDescMemory(desc,rsf->AccessControlInfo.IORegisterMappingNum*2); - u16 DescUsed = 0; + u16 descUsed = 0; + for(int i = 0; i < rsf->AccessControlInfo.IORegisterMappingNum; i++){ if(strlen(rsf->AccessControlInfo.IORegisterMapping[i])){ // Parse Address String @@ -845,38 +829,36 @@ int GetARM11IOMappings(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf) u32 AddressStart = strtoul(AddressStartStr,NULL,16); if(!IsStartAddress(AddressStart)){ fprintf(stderr,"[EXHEADER ERROR] Address 0x%x is not valid mapping start address.\n",AddressStart); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } if(!AddressEndStr){ // No End Addr Was Specified - SetARM11KernelDescValue(desc,DescUsed,GetIOMappingDesc(AddressStart)); - DescUsed++; - goto skip; + SetARM11KernelDescValue(desc,descUsed,GetIOMappingDesc(AddressStart)); + descUsed++; + continue; } u32 AddressEnd = strtoul(AddressEndStr,NULL,16); if(!IsEndAddress(AddressEnd)){ fprintf(stderr,"[EXHEADER ERROR] Address 0x%x is not valid mapping end address.\n",AddressEnd); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } u32 DescStartAddr = GetStaticMappingDesc(AddressStart,false); u32 DescEndAddr = GetStaticMappingDesc(AddressEnd+0x1000,false); if(DescStartAddr != DescEndAddr){ - SetARM11KernelDescValue(desc,DescUsed,DescStartAddr); - SetARM11KernelDescValue(desc,DescUsed+1,DescEndAddr); - DescUsed += 2; - goto skip; + SetARM11KernelDescValue(desc,descUsed,DescStartAddr); + SetARM11KernelDescValue(desc,descUsed+1,DescEndAddr); + descUsed += 2; + continue; } else{ - SetARM11KernelDescValue(desc,DescUsed,GetIOMappingDesc(AddressStart)); - DescUsed++; - goto skip; + SetARM11KernelDescValue(desc,descUsed,GetIOMappingDesc(AddressStart)); + descUsed++; + continue; } } - - skip: ; } - desc->num = DescUsed; + desc->num = descUsed; return 0; } @@ -886,7 +868,7 @@ int GetARM11StaticMappings(ARM11KernelCapabilityDescriptor *desc, rsf_settings * return 0; AllocateARM11KernelDescMemory(desc,rsf->AccessControlInfo.MemoryMappingNum*2); - u16 DescUsed = 0; + u16 descUsed = 0; for(int i = 0; i < rsf->AccessControlInfo.MemoryMappingNum; i++){ if(strlen(rsf->AccessControlInfo.MemoryMapping[i])){ char *AddressStartStr = rsf->AccessControlInfo.MemoryMapping[i]; @@ -908,115 +890,113 @@ int GetARM11StaticMappings(ARM11KernelCapabilityDescriptor *desc, rsf_settings * u32 AddressStart = strtoul(AddressStartStr,NULL,16); if(!IsStartAddress(AddressStart)){ fprintf(stderr,"[EXHEADER ERROR] Address 0x%x (%s) is not valid mapping start address.\n",AddressStart,AddressStartStr); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } if(!AddressEndStr){ // No End Addr Was Specified - SetARM11KernelDescValue(desc,DescUsed,GetStaticMappingDesc(AddressStart,IsRO)); - SetARM11KernelDescValue(desc,DescUsed+1,GetStaticMappingDesc(AddressStart+0x1000, true)); - DescUsed += 2; - goto skip; + SetARM11KernelDescValue(desc,descUsed,GetStaticMappingDesc(AddressStart,IsRO)); + SetARM11KernelDescValue(desc,descUsed+1,GetStaticMappingDesc(AddressStart+0x1000, true)); + descUsed += 2; + continue; } u32 AddressEnd = strtoul(AddressEndStr,NULL,16); if(!IsEndAddress(AddressEnd)){ fprintf(stderr,"[EXHEADER ERROR] Address 0x%x (%s) is not valid mapping end address.\n",AddressEnd,AddressEndStr); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } u32 DescStartAddr = GetStaticMappingDesc(AddressStart,IsRO); u32 DescEndAddr = GetStaticMappingDesc(AddressEnd+0x1000,true); if(DescStartAddr != DescEndAddr){ - SetARM11KernelDescValue(desc,DescUsed,DescStartAddr); - SetARM11KernelDescValue(desc,DescUsed+1,DescEndAddr); - DescUsed += 2; - goto skip; + SetARM11KernelDescValue(desc,descUsed,DescStartAddr); + SetARM11KernelDescValue(desc,descUsed+1,DescEndAddr); + descUsed += 2; + continue; } else{ - SetARM11KernelDescValue(desc,DescUsed,GetStaticMappingDesc(AddressStart,IsRO)); - SetARM11KernelDescValue(desc,DescUsed+1,GetStaticMappingDesc(AddressStart+0x1000, true)); - DescUsed += 2; - goto skip; + SetARM11KernelDescValue(desc,descUsed,GetStaticMappingDesc(AddressStart,IsRO)); + SetARM11KernelDescValue(desc,descUsed+1,GetStaticMappingDesc(AddressStart+0x1000, true)); + descUsed += 2; + continue; } } - - skip: ; } - desc->num = DescUsed; + desc->num = descUsed; return 0; } -bool IsEndAddress(u32 Address) +bool IsEndAddress(u32 address) { - return (Address & 0x0fff) == 0x0fff; + return (address & 0x0fff) == 0x0fff; } -bool IsStartAddress(u32 Address) +bool IsStartAddress(u32 address) { - return (Address & 0x0fff) == 0; + return (address & 0x0fff) == 0; } -u32 GetIOMappingDesc(u32 Address) +u32 GetIOMappingDesc(u32 address) { - return GetMappingDesc(Address,0xFFE,0xC,false); + return GetMappingDesc(address,0xFFE,0xC,false); } -u32 GetStaticMappingDesc(u32 Address, bool IsReadOnly) +u32 GetStaticMappingDesc(u32 address, bool IsReadOnly) { - return GetMappingDesc(Address,0x7FC,0xB,IsReadOnly); + return GetMappingDesc(address,0x7FC,0xB,IsReadOnly); } -u32 GetMappingDesc(u32 Address, u32 PrefixVal, s32 numPrefixBits, bool IsRO) +u32 GetMappingDesc(u32 address, u32 prefixVal, s32 numPrefixBits, bool IsRO) { - u32 PrefixMask = GetDescPrefixMask(numPrefixBits); - u32 PrefixBits = GetDescPrefixBits(numPrefixBits,PrefixVal); - u32 Desc = (Address >> 12 & ~PrefixMask) | PrefixBits; + u32 prefixMask = GetDescPrefixMask(numPrefixBits); + u32 prefixBits = GetDescPrefixBits(numPrefixBits,prefixVal); + u32 desc = (address >> 12 & ~prefixMask) | prefixBits; if (IsRO) - Desc |= 0x100000; - return Desc; + desc |= 0x100000; + return desc; } int SetARM11KernelDescOtherCapabilities(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf) { - u32 OtherCapabilities = 0; + u32 otherCapabilities = 0; + u32 memType = 0; if(!rsf->AccessControlInfo.DisableDebug) - OtherCapabilities |= othcap_PERMIT_DEBUG; + otherCapabilities |= othcap_PERMIT_DEBUG; if(rsf->AccessControlInfo.EnableForceDebug) - OtherCapabilities |= othcap_FORCE_DEBUG; + otherCapabilities |= othcap_FORCE_DEBUG; if(rsf->AccessControlInfo.CanUseNonAlphabetAndNumber) - OtherCapabilities |= othcap_CAN_USE_NON_ALPHABET_AND_NUMBER; + otherCapabilities |= othcap_CAN_USE_NON_ALPHABET_AND_NUMBER; if(rsf->AccessControlInfo.CanWriteSharedPage) - OtherCapabilities |= othcap_CAN_WRITE_SHARED_PAGE; + otherCapabilities |= othcap_CAN_WRITE_SHARED_PAGE; if(rsf->AccessControlInfo.CanUsePrivilegedPriority) - OtherCapabilities |= othcap_CAN_USE_PRIVILEGE_PRIORITY; + otherCapabilities |= othcap_CAN_USE_PRIVILEGE_PRIORITY; if(rsf->AccessControlInfo.PermitMainFunctionArgument) - OtherCapabilities |= othcap_PERMIT_MAIN_FUNCTION_ARGUMENT; + otherCapabilities |= othcap_PERMIT_MAIN_FUNCTION_ARGUMENT; if(rsf->AccessControlInfo.CanShareDeviceMemory) - OtherCapabilities |= othcap_CAN_SHARE_DEVICE_MEMORY; + otherCapabilities |= othcap_CAN_SHARE_DEVICE_MEMORY; if(rsf->AccessControlInfo.RunnableOnSleep) - OtherCapabilities |= othcap_RUNNABLE_ON_SLEEP; + otherCapabilities |= othcap_RUNNABLE_ON_SLEEP; if(rsf->AccessControlInfo.SpecialMemoryArrange) - OtherCapabilities |= othcap_SPECIAL_MEMORY_ARRANGE; + otherCapabilities |= othcap_SPECIAL_MEMORY_ARRANGE; if(rsf->AccessControlInfo.MemoryType){ - u32 MemType = 0; if(strcasecmp(rsf->AccessControlInfo.MemoryType,"application") == 0) - MemType = memtype_APPLICATION; + memType = memtype_APPLICATION; else if(strcasecmp(rsf->AccessControlInfo.MemoryType,"system") == 0) - MemType = memtype_SYSTEM; + memType = memtype_SYSTEM; else if(strcasecmp(rsf->AccessControlInfo.MemoryType,"base") == 0) - MemType = memtype_BASE; + memType = memtype_BASE; else{ fprintf(stderr,"[EXHEADER ERROR] Invalid memory type: \"%s\"\n",rsf->AccessControlInfo.MemoryType); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } - OtherCapabilities = (OtherCapabilities & 0xffffff0f) | MemType << 8; + otherCapabilities = (otherCapabilities & 0xffffff0f) | memType << 8; } - if(OtherCapabilities){ + if(otherCapabilities){ AllocateARM11KernelDescMemory(desc,1); SetARM11KernelDescBitmask(desc,desc_OtherCapabilities); - SetARM11KernelDescValue(desc,0,OtherCapabilities); + SetARM11KernelDescValue(desc,0,otherCapabilities); } return 0; } @@ -1024,18 +1004,18 @@ int SetARM11KernelDescOtherCapabilities(ARM11KernelCapabilityDescriptor *desc, r int SetARM11KernelDescHandleTableSize(ARM11KernelCapabilityDescriptor *desc, rsf_settings *rsf) { if(rsf->AccessControlInfo.HandleTableSize){ - u16 HandleTableSize = strtoul(rsf->AccessControlInfo.HandleTableSize,NULL,0); - if(HandleTableSize > 1023){ + u16 handleTableSize = strtoul(rsf->AccessControlInfo.HandleTableSize,NULL,0); + if(handleTableSize > 1023){ fprintf(stderr,"[EXHEADER ERROR] Too large handle table size\n"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } AllocateARM11KernelDescMemory(desc,1); SetARM11KernelDescBitmask(desc,desc_HandleTableSize); - SetARM11KernelDescValue(desc,0,HandleTableSize); + SetARM11KernelDescValue(desc,0,handleTableSize); } else{ ErrorParamNotFound("AccessControlInfo/HandleTableSize"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } return 0; } @@ -1055,24 +1035,24 @@ int SetARM11KernelDescReleaseKernelVersion(ARM11KernelCapabilityDescriptor *desc return 0; } -void SetARM11KernelDescValue(ARM11KernelCapabilityDescriptor *desc, u16 Index, u32 Value) +void SetARM11KernelDescValue(ARM11KernelCapabilityDescriptor *desc, u16 index, u32 value) { - if(Index >= desc->num) return; - desc->Data[Index] |= Value; + if(index >= desc->num) return; + desc->data[index] |= value; } -void SetARM11KernelDescBitmask(ARM11KernelCapabilityDescriptor *desc, u32 Bitmask) +void SetARM11KernelDescBitmask(ARM11KernelCapabilityDescriptor *desc, u32 bitmask) { for(int i = 0; i < desc->num; i++) - SetARM11KernelDescValue(desc,i,Bitmask); + SetARM11KernelDescValue(desc,i,bitmask); } -void AllocateARM11KernelDescMemory(ARM11KernelCapabilityDescriptor *desc, u16 Num) +void AllocateARM11KernelDescMemory(ARM11KernelCapabilityDescriptor *desc, u16 num) { - if(Num == 0) return; - desc->num = Num; - desc->Data = malloc(sizeof(u32)*Num); - memset(desc->Data,0,sizeof(u32)*Num); + if(num == 0) return; + desc->num = num; + desc->data = malloc(sizeof(u32)*num); + clrmem(desc->data,sizeof(u32)*num); return; } @@ -1081,63 +1061,58 @@ u32 GetDescPrefixMask(int numPrefixBits) return (u32)(~((1 << (32 - (numPrefixBits & 31))) - 1)); } -u32 GetDescPrefixBits(int numPrefixBits, u32 PrefixVal) +u32 GetDescPrefixBits(int numPrefixBits, u32 prefixVal) { - return PrefixVal << (32 - (numPrefixBits & 31)); + return prefixVal << (32 - (numPrefixBits & 31)); } int get_ExHeaderARM9AccessControlInfo(exhdr_ARM9AccessControlInfo *arm9, rsf_settings *rsf) { - u32 Arm9AccessControl = 0; + u32 arm9AccessControl = 0; for(int i = 0; i < rsf->AccessControlInfo.IoAccessControlNum; i++){ if(strcmp(rsf->AccessControlInfo.IoAccessControl[i],"FsMountNand") == 0) - Arm9AccessControl |= arm9cap_FS_MOUNT_NAND; + arm9AccessControl |= arm9cap_FS_MOUNT_NAND; else if(strcmp(rsf->AccessControlInfo.IoAccessControl[i],"FsMountNandRoWrite") == 0) - Arm9AccessControl |= arm9cap_FS_MOUNT_NAND_RO_WRITE; + arm9AccessControl |= arm9cap_FS_MOUNT_NAND_RO_WRITE; else if(strcmp(rsf->AccessControlInfo.IoAccessControl[i],"FsMountTwln") == 0) - Arm9AccessControl |= arm9cap_FS_MOUNT_TWLN; + arm9AccessControl |= arm9cap_FS_MOUNT_TWLN; else if(strcmp(rsf->AccessControlInfo.IoAccessControl[i],"FsMountWnand") == 0) - Arm9AccessControl |= arm9cap_FS_MOUNT_WNAND; + arm9AccessControl |= arm9cap_FS_MOUNT_WNAND; else if(strcmp(rsf->AccessControlInfo.IoAccessControl[i],"FsMountCardSpi") == 0) - Arm9AccessControl |= arm9cap_FS_MOUNT_CARD_SPI; + arm9AccessControl |= arm9cap_FS_MOUNT_CARD_SPI; else if(strcmp(rsf->AccessControlInfo.IoAccessControl[i],"UseSdif3") == 0) - Arm9AccessControl |= arm9cap_USE_SDIF3; + arm9AccessControl |= arm9cap_USE_SDIF3; else if(strcmp(rsf->AccessControlInfo.IoAccessControl[i],"CreateSeed") == 0) - Arm9AccessControl |= arm9cap_CREATE_SEED; + arm9AccessControl |= arm9cap_CREATE_SEED; else if(strcmp(rsf->AccessControlInfo.IoAccessControl[i],"UseCardSpi") == 0) - Arm9AccessControl |= arm9cap_USE_CARD_SPI; + arm9AccessControl |= arm9cap_USE_CARD_SPI; else{ fprintf(stderr,"[EXHEADER ERROR] Invalid IoAccessControl Name: \"%s\"\n",rsf->AccessControlInfo.IoAccessControl[i]); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } } for(int i = 0; i < rsf->AccessControlInfo.FileSystemAccessNum; i++){ if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"DirectSdmc") == 0) - Arm9AccessControl |= arm9cap_USE_DIRECT_SDMC; + arm9AccessControl |= arm9cap_USE_DIRECT_SDMC; } if(rsf->Option.UseOnSD) - Arm9AccessControl |= arm9cap_SD_APPLICATION; + arm9AccessControl |= arm9cap_SD_APPLICATION; - u32_to_u8(arm9->descriptors,Arm9AccessControl,LE); + u32_to_u8(arm9->descriptors,arm9AccessControl,LE); - if(rsf->AccessControlInfo.DescVersion){ + if(rsf->AccessControlInfo.DescVersion) arm9->descriptors[15] = strtol(rsf->AccessControlInfo.DescVersion,NULL,0); - } else{ //ErrorParamNotFound("AccessControlInfo/DescVersion"); - //return EXHDR_BAD_YAML_OPT; + //return EXHDR_BAD_RSF_OPT; arm9->descriptors[15] = 2; // Makerom generates a desc version 2 anyway, so if not specified, it will be set to 2 } return 0; } - - - - /* Generic Exheader Errors */ void ErrorParamNotFound(char *string) { @@ -1222,14 +1197,14 @@ int GetSaveDataSizeFromString(u64 *out, char *string, char *moduleName) fprintf(stderr,"[%s ERROR] Invalid save data size format.\n",moduleName); else fprintf(stderr,"[ERROR] Invalid save data size format.\n"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } if((SaveDataSize & 65536) != 0){ if(moduleName) fprintf(stderr,"[%s ERROR] Save data size must be aligned to 64K.\n",moduleName); else fprintf(stderr,"[ERROR] Save data size must be aligned to 64K.\n"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } *out = SaveDataSize; return 0; @@ -1260,11 +1235,11 @@ int GetSaveDataSize_rsf(u64 *SaveDataSize, user_settings *usrset) } else{ fprintf(stderr,"[EXHEADER ERROR] Invalid save data size format.\n"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } if((*SaveDataSize & 65536) != 0){ fprintf(stderr,"[EXHEADER ERROR] Save data size must be aligned to 64K.\n"); - return EXHDR_BAD_YAML_OPT; + return EXHDR_BAD_RSF_OPT; } } else{ diff --git a/makerom/exheader.h b/makerom/exheader.h index ee1e2d45..d33639c6 100644 --- a/makerom/exheader.h +++ b/makerom/exheader.h @@ -117,7 +117,7 @@ typedef struct typedef struct { u8 extSavedataId[8]; - u8 systemSavedataId[8]; + u8 systemSavedataId[2][4]; u8 storageAccessableUniqueIds[8]; u8 accessInfo[7]; u8 otherAttributes; @@ -140,7 +140,7 @@ typedef struct typedef struct { u16 num; - u32 *Data; + u32 *data; } ARM11KernelCapabilityDescriptor; typedef enum diff --git a/makerom/exheader_build.h b/makerom/exheader_build.h index f7fd894c..4645c075 100644 --- a/makerom/exheader_build.h +++ b/makerom/exheader_build.h @@ -4,7 +4,7 @@ typedef enum { COMMON_HEADER_KEY_NOT_FOUND = -10, - EXHDR_BAD_YAML_OPT = -11, + EXHDR_BAD_RSF_OPT = -11, CANNOT_SIGN_ACCESSDESC = -12 } exheader_errors; diff --git a/makerom/rsf_settings.c b/makerom/rsf_settings.c index c0cdbb22..fdfa291e 100644 --- a/makerom/rsf_settings.c +++ b/makerom/rsf_settings.c @@ -64,13 +64,11 @@ void GET_Option(ctr_yaml_context *ctx, rsf_settings *rsf) // Handle childs if(cmpYamlValue("AllowUnalignedSection",ctx)) SetBoolYAMLValue(&rsf->Option.AllowUnalignedSection,"AllowUnalignedSection",ctx); else if(cmpYamlValue("MediaFootPadding",ctx)) SetBoolYAMLValue(&rsf->Option.MediaFootPadding,"MediaFootPadding",ctx); - //else if(cmpYamlValue("NoPadding",ctx)) SetBoolYAMLValue(&rsf->Option.NoPadding,"NoPadding",ctx); else if(cmpYamlValue("EnableCrypt",ctx)) SetBoolYAMLValue(&rsf->Option.EnableCrypt,"EnableCrypt",ctx); else if(cmpYamlValue("EnableCompress",ctx)) SetBoolYAMLValue(&rsf->Option.EnableCompress,"EnableCompress",ctx); else if(cmpYamlValue("FreeProductCode",ctx)) SetBoolYAMLValue(&rsf->Option.FreeProductCode,"FreeProductCode",ctx); else if(cmpYamlValue("UseOnSD",ctx)) SetBoolYAMLValue(&rsf->Option.UseOnSD,"UseOnSD",ctx); else if(cmpYamlValue("PageSize",ctx)) SetSimpleYAMLValue(&rsf->Option.PageSize,"PageSize",ctx,0); - //else if(cmpYamlValue("AppendSystemCall",ctx)) rsf->Option.AppendSystemCallNum = SetYAMLSequence(&rsf->Option.AppendSystemCall,"AppendSystemCall",ctx); else{ fprintf(stderr,"[RSF ERROR] Unrecognised key '%s'\n",GetYamlString(ctx)); ctx->error = YAML_UNKNOWN_KEY; @@ -102,13 +100,10 @@ void GET_AccessControlInfo(ctr_yaml_context *ctx, rsf_settings *rsf) else if(cmpYamlValue("PermitMainFunctionArgument",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.PermitMainFunctionArgument,"PermitMainFunctionArgument",ctx); else if(cmpYamlValue("CanShareDeviceMemory",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.CanShareDeviceMemory,"CanShareDeviceMemory",ctx); else if(cmpYamlValue("UseOtherVariationSaveData",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.UseOtherVariationSaveData,"UseOtherVariationSaveData",ctx); - else if(cmpYamlValue("UseExtSaveData",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.UseExtSaveData,"UseExtSaveData",ctx); - else if(cmpYamlValue("UseExtendedSaveDataAccessControl",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.UseExtendedSaveDataAccessControl,"UseExtendedSaveDataAccessControl",ctx); else if(cmpYamlValue("RunnableOnSleep",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.RunnableOnSleep,"RunnableOnSleep",ctx); else if(cmpYamlValue("SpecialMemoryArrange",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.SpecialMemoryArrange,"SpecialMemoryArrange",ctx); - //else if(cmpYamlValue("ProgramId",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.ProgramId,"ProgramId",ctx,0); else if(cmpYamlValue("IdealProcessor",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.IdealProcessor,"IdealProcessor",ctx,0); else if(cmpYamlValue("Priority",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.Priority,"Priority",ctx,0); else if(cmpYamlValue("MemoryType",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.MemoryType,"MemoryType",ctx,0); @@ -123,7 +118,6 @@ void GET_AccessControlInfo(ctr_yaml_context *ctx, rsf_settings *rsf) else if(cmpYamlValue("ExtSaveDataId",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.ExtSaveDataId,"ExtSaveDataId",ctx,0); else if(cmpYamlValue("AffinityMask",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.AffinityMask,"AffinityMask",ctx,0); else if(cmpYamlValue("DescVersion",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.DescVersion,"DescVersion",ctx,0); - //else if(cmpYamlValue("CryptoKey",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.CryptoKey,"CryptoKey",ctx,0); else if(cmpYamlValue("ResourceLimitCategory",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.ResourceLimitCategory,"ResourceLimitCategory",ctx,0); else if(cmpYamlValue("ReleaseKernelMajor",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.ReleaseKernelMajor,"ReleaseKernelMajor",ctx,0); else if(cmpYamlValue("ReleaseKernelMinor",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.ReleaseKernelMinor,"ReleaseKernelMinor",ctx,0); @@ -137,7 +131,6 @@ void GET_AccessControlInfo(ctr_yaml_context *ctx, rsf_settings *rsf) else if(cmpYamlValue("InterruptNumbers",ctx)) rsf->AccessControlInfo.InterruptNumbersNum = SetYAMLSequence(&rsf->AccessControlInfo.InterruptNumbers,"InterruptNumbers",ctx); else if(cmpYamlValue("SystemCallAccess",ctx)) rsf->AccessControlInfo.SystemCallAccessNum = SetYAMLSequenceFromMapping(&rsf->AccessControlInfo.SystemCallAccess,"SystemCallAccess",ctx,false); else if(cmpYamlValue("ServiceAccessControl",ctx)) rsf->AccessControlInfo.ServiceAccessControlNum = SetYAMLSequence(&rsf->AccessControlInfo.ServiceAccessControl,"ServiceAccessControl",ctx); - //else if(cmpYamlValue("StorageId",ctx)) rsf->AccessControlInfo.StorageIdNum = SetYAMLSequence(&rsf->AccessControlInfo.StorageId,"StorageId",ctx); else if(cmpYamlValue("AccessibleSaveDataIds",ctx)) rsf->AccessControlInfo.AccessibleSaveDataIdsNum = SetYAMLSequence(&rsf->AccessControlInfo.AccessibleSaveDataIds,"AccessibleSaveDataIds",ctx); else{ @@ -198,8 +191,6 @@ void GET_BasicInfo(ctr_yaml_context *ctx, rsf_settings *rsf) else if(cmpYamlValue("ProductCode",ctx)) SetSimpleYAMLValue(&rsf->BasicInfo.ProductCode,"ProductCode",ctx,0); else if(cmpYamlValue("ContentType",ctx)) SetSimpleYAMLValue(&rsf->BasicInfo.ContentType,"ContentType",ctx,0); else if(cmpYamlValue("Logo",ctx)) SetSimpleYAMLValue(&rsf->BasicInfo.Logo,"Logo",ctx,0); - //else if(cmpYamlValue("BackupMemoryType",ctx)) SetSimpleYAMLValue(&rsf->BasicInfo.BackupMemoryType,"BackupMemoryType",ctx,0); - //else if(cmpYamlValue("InitialCode",ctx)) SetSimpleYAMLValue(&rsf->BasicInfo.InitialCode,"InitialCode",ctx,0); else{ fprintf(stderr,"[RSF ERROR] Unrecognised key '%s'\n",GetYamlString(ctx)); ctx->error = YAML_UNKNOWN_KEY; diff --git a/makerom/user_settings.h b/makerom/user_settings.h index 92838a03..561429a5 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -84,8 +84,6 @@ typedef struct bool PermitMainFunctionArgument; bool CanShareDeviceMemory; bool UseOtherVariationSaveData; - bool UseExtSaveData; - bool UseExtendedSaveDataAccessControl; bool RunnableOnSleep; bool SpecialMemoryArrange; From 0545c49530734bf0d313eb56b6ccf92c3c9e448d Mon Sep 17 00:00:00 2001 From: applestash Date: Sun, 21 Sep 2014 02:34:39 +1000 Subject: [PATCH 071/317] makerom: bug fix --- makerom/exheader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/exheader.c b/makerom/exheader.c index d18c1ac3..7db6f31a 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -990,7 +990,7 @@ int SetARM11KernelDescOtherCapabilities(ARM11KernelCapabilityDescriptor *desc, r fprintf(stderr,"[EXHEADER ERROR] Invalid memory type: \"%s\"\n",rsf->AccessControlInfo.MemoryType); return EXHDR_BAD_RSF_OPT; } - otherCapabilities = (otherCapabilities & 0xffffff0f) | memType << 8; + otherCapabilities = (otherCapabilities & 0xfffff0ff) | (memType & 7) << 8; } if(otherCapabilities){ From 0fa5af0af065ee4f5b406f28317c9403bd2c95b6 Mon Sep 17 00:00:00 2001 From: applestash Date: Sun, 21 Sep 2014 02:39:12 +1000 Subject: [PATCH 072/317] makerom: misc --- makerom/ncsd.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/makerom/ncsd.c b/makerom/ncsd.c index 968f1552..23d7dfe1 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -181,12 +181,6 @@ int ProcessCiaForCci(cci_settings *set) u16 contentCount = GetTmdContentCount(tmd); set->romInfo.saveSize = GetTmdSaveSize(tmd); - if(set->romInfo.saveSize > 0 && set->romInfo.saveSize < (u64)(128*KB)) - set->romInfo.saveSize = (u64)(128*KB); - else if(set->romInfo.saveSize > (u64)(128*KB) && set->romInfo.saveSize < (u64)(512*KB)) - set->romInfo.saveSize = (u64)(512*KB); - else if(set->romInfo.saveSize > (u64)(512*KB)) - set->romInfo.saveSize = align(set->romInfo.saveSize,MB); if(!CanCiaBeCci(GetTmdTitleId(tmd),contentCount,contentInfo)){ fprintf(stderr,"[CCI ERROR] This CIA cannot be converted to CCI\n"); @@ -234,14 +228,11 @@ void GetTitleSaveSize(cci_settings *set) GetSaveDataSizeFromString(&set->romInfo.saveSize,set->rsf->SystemControlInfo.SaveDataSize,"CCI"); // Adjusting save size - - if(set->romInfo.saveSize == 0) - set->romInfo.saveSize == 0; - else if(set->romInfo.saveSize <= (u64)128*KB) - set->romInfo.saveSize = (u64)128*KB; - else if(set->romInfo.saveSize <= (u64)512*KB) - set->romInfo.saveSize = (u64)512*KB; - else + if(set->romInfo.saveSize > 0 && set->romInfo.saveSize < (u64)(128*KB)) + set->romInfo.saveSize = (u64)(128*KB); + else if(set->romInfo.saveSize > (u64)(128*KB) && set->romInfo.saveSize < (u64)(512*KB)) + set->romInfo.saveSize = (u64)(512*KB); + else if(set->romInfo.saveSize > (u64)(512*KB)) set->romInfo.saveSize = align(set->romInfo.saveSize,MB); } From 0e950c42740d654fbc1b5d508dcc6ab5fd56712d Mon Sep 17 00:00:00 2001 From: applestash Date: Sun, 21 Sep 2014 02:40:05 +1000 Subject: [PATCH 073/317] makerom: misc --- makerom/exheader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/exheader.c b/makerom/exheader.c index 7db6f31a..87d78f77 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -990,7 +990,7 @@ int SetARM11KernelDescOtherCapabilities(ARM11KernelCapabilityDescriptor *desc, r fprintf(stderr,"[EXHEADER ERROR] Invalid memory type: \"%s\"\n",rsf->AccessControlInfo.MemoryType); return EXHDR_BAD_RSF_OPT; } - otherCapabilities = (otherCapabilities & 0xfffff0ff) | (memType & 7) << 8; + otherCapabilities = (otherCapabilities & 0xfffff0ff) | (memType & 0xf) << 8; } if(otherCapabilities){ From b240ea80459ee57c8678e68ece948ff32b0dd0b5 Mon Sep 17 00:00:00 2001 From: applestash Date: Mon, 22 Sep 2014 20:54:26 +1000 Subject: [PATCH 074/317] makerom: misc --- makerom/cardinfo.c | 8 ++++++-- makerom/crypto.c | 12 ++++-------- makerom/crypto.h | 6 ++++++ makerom/romfs_gen.c | 23 ++++++++++++----------- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/makerom/cardinfo.c b/makerom/cardinfo.c index f2ac692f..e9b80a41 100644 --- a/makerom/cardinfo.c +++ b/makerom/cardinfo.c @@ -139,8 +139,12 @@ int SetCardInfoBitmask(cardinfo_hdr *hdr, cci_settings *set) } str = set->rsf->CardInfo.CryptoType; - if(!str) - bitmask |= 0;//(3*0x40); + if(!str) { + if(set->options.useExternalSdkCardInfo) + bitmask |= (3*0x40); + else + bitmask |= 0; + } else{ int val = strtol(str,NULL,10); if(val < 0 || val > 3) { diff --git a/makerom/crypto.c b/makerom/crypto.c index 9fd99ee7..e83b9f71 100644 --- a/makerom/crypto.c +++ b/makerom/crypto.c @@ -153,17 +153,13 @@ u32 GetSigHashLen(u32 sig_type) { switch(sig_type){ case RSA_4096_SHA1: - return 0x14; - case RSA_4096_SHA256: - return 0x20; case RSA_2048_SHA1: - return 0x14; - case RSA_2048_SHA256: - return 0x20; case ECC_SHA1: - return 0x14; + return SHA_1_LEN; + case RSA_4096_SHA256: + case RSA_2048_SHA256: case ECC_SHA256: - return 0x20; + return SHA_256_LEN; } return 0; } diff --git a/makerom/crypto.h b/makerom/crypto.h index 617fdc84..95b1ea5f 100644 --- a/makerom/crypto.h +++ b/makerom/crypto.h @@ -36,6 +36,12 @@ typedef enum CTR_SHA_256, } ctr_sha_modes; +typedef enum +{ + SHA_1_LEN = 0x14, + SHA_256_LEN = 0x20, +} sha_hash_len; + typedef enum { RSA_4096_PUBK = 0, diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 04f81b67..3ad516c0 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -202,9 +202,9 @@ int CalcRomfsSize(romfs_buildctx *ctx) //printf("predict level sizes\n"); ctx->level[3].size = romfsHdrSize + ctx->m_dataLen; // data - ctx->level[2].size = align(ctx->level[3].size,ROMFS_BLOCK_SIZE) / ROMFS_BLOCK_SIZE * 0x20 ; - ctx->level[1].size = align(ctx->level[2].size,ROMFS_BLOCK_SIZE) / ROMFS_BLOCK_SIZE * 0x20 ; - ctx->level[0].size = align(ctx->level[1].size,ROMFS_BLOCK_SIZE) / ROMFS_BLOCK_SIZE * 0x20 + align(sizeof(ivfc_hdr),0x10); // hdr + ctx->level[2].size = align(ctx->level[3].size,ROMFS_BLOCK_SIZE) / ROMFS_BLOCK_SIZE * SHA_256_LEN ; + ctx->level[1].size = align(ctx->level[2].size,ROMFS_BLOCK_SIZE) / ROMFS_BLOCK_SIZE * SHA_256_LEN ; + ctx->level[0].size = align(ctx->level[1].size,ROMFS_BLOCK_SIZE) / ROMFS_BLOCK_SIZE * SHA_256_LEN + align(sizeof(ivfc_hdr),0x10); // hdr ctx->romfsHeaderSize = ctx->level[0].size; @@ -320,13 +320,13 @@ void AddDirHashKey(romfs_buildctx *ctx, u32 parent, fs_romfs_char* path, u32 dir { u32 hash = CalcPathHash(parent,path,0,fs_u16StrLen(path)); u32 index = hash % ctx->m_dirUTableEntry; - if(ctx->dirUTable[index] == 0xffffffff) ctx->dirUTable[index] = dirOffset; + if(ctx->dirUTable[index] == ROMFS_UNUSED_ENTRY) ctx->dirUTable[index] = dirOffset; else { romfs_direntry * curdir = (romfs_direntry*)(ctx->dirTable + ctx->dirUTable[index]); while(1) { - if(*(u32*)curdir->weirdoffset == 0xffffffff) + if(*(u32*)curdir->weirdoffset == ROMFS_UNUSED_ENTRY) { *(u32*)curdir->weirdoffset = dirOffset; break; @@ -343,13 +343,13 @@ void AddFileHashKey(romfs_buildctx *ctx,u32 parent, fs_romfs_char *path, u32 fil { u32 hash = CalcPathHash(parent,path,0,fs_u16StrLen(path)); u32 index = hash % ctx->m_fileUTableEntry; - if(ctx->fileUTable[index] == 0xffffffff) ctx->fileUTable[index] = fileOffset; + if(ctx->fileUTable[index] == ROMFS_UNUSED_ENTRY) ctx->fileUTable[index] = fileOffset; else { romfs_fileentry * curfile = (romfs_fileentry*)(ctx->fileTable + ctx->fileUTable[index]); while(1) { - if(*(u32*)curfile->weirdoffset == 0xffffffff) + if(*(u32*)curfile->weirdoffset == ROMFS_UNUSED_ENTRY) { *(u32*)curfile->weirdoffset = fileOffset; break; @@ -368,7 +368,7 @@ int AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) u32_to_u8(entry->parentdiroffset,parent,LE); u32_to_u8(entry->siblingoffset,sibling,LE); - u32_to_u8(entry->weirdoffset,0xffffffff,LE); + u32_to_u8(entry->weirdoffset,ROMFS_UNUSED_ENTRY,LE); // Import Name u32_to_u8(entry->namesize,file->name_len,LE); @@ -388,6 +388,7 @@ int AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) } else u64_to_u8(entry->dataoffset,0x40,LE); + AddFileHashKey(ctx,parent,file->name,ctx->u_fileTableLen); ctx->u_fileTableLen += sizeof(romfs_fileentry) + align(file->name_len,4); @@ -401,7 +402,7 @@ int AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) u32_to_u8(entry->parentoffset,parent,LE); u32_to_u8(entry->siblingoffset,sibling,LE); - u32_to_u8(entry->weirdoffset,0xffffffff,LE); + u32_to_u8(entry->weirdoffset,ROMFS_UNUSED_ENTRY,LE); u32 Currentdir = ctx->u_dirTableLen; @@ -485,7 +486,7 @@ void BuildIvfcHeader(romfs_buildctx *ctx) memcpy(ctx->ivfcHdr->magic,"IVFC",4); u32_to_u8(ctx->ivfcHdr->id,0x10000,LE); - u32 masterHashSize = ( align(ctx->level[1].size,ROMFS_BLOCK_SIZE) / ROMFS_BLOCK_SIZE ) * 0x20 ; + u32 masterHashSize = ( align(ctx->level[1].size,ROMFS_BLOCK_SIZE) / ROMFS_BLOCK_SIZE ) * SHA_256_LEN ; u32_to_u8(ctx->ivfcHdr->masterHashSize,masterHashSize,LE); for(int i = 1; i < 4; i++){ @@ -505,7 +506,7 @@ void GenIvfcHashTree(romfs_buildctx *ctx) u32 numHashes = align(ctx->level[i+1].size,ROMFS_BLOCK_SIZE) / ROMFS_BLOCK_SIZE; for(u32 j = 0; j < numHashes; j++){ u8 *datapos = (u8*)(ctx->level[i+1].pos + ROMFS_BLOCK_SIZE * j); - u8 *hashpos = (u8*)(ctx->level[i].pos + 0x20 * j); + u8 *hashpos = (u8*)(ctx->level[i].pos + SHA_256_LEN * j); ShaCalc(datapos, ROMFS_BLOCK_SIZE, hashpos, CTR_SHA_256); } } From c7f8b1833e7d352d628bcf0971eaec4d8cd31bc5 Mon Sep 17 00:00:00 2001 From: applestash Date: Mon, 22 Sep 2014 21:42:33 +1000 Subject: [PATCH 075/317] ctrtool: tmd savesize detection & smarter exheader pre check --- ctrtool/exheader.c | 18 ++++++++++++++++++ ctrtool/exheader.h | 3 +++ ctrtool/ncch.c | 3 ++- ctrtool/tmd.c | 10 +++++++++- ctrtool/tmd.h | 8 ++++++-- 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index afb19279..6f064d97 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -42,6 +42,11 @@ void exheader_set_programid(exheader_context* ctx, u8 programid[8]) memcpy(ctx->programid, programid, 8); } +void exheader_set_hash(exheader_context* ctx, u8 hash[32]) +{ + memcpy(ctx->hash, hash, 32); +} + void exheader_set_counter(exheader_context* ctx, u8 counter[16]) { memcpy(ctx->counter, counter, 16); @@ -94,6 +99,19 @@ void exheader_read(exheader_context* ctx, u32 actions) } } +int exheader_hash_valid(exheader_context* ctx) +{ + u8 hash[32]; + ctr_sha_256((u8*)&ctx->header, 0x400, hash); + + if(memcmp(ctx->hash,hash,0x20)){ + fprintf(stderr, "Error, exheader hash mismatch. Wrong key?\n"); + return 0; + } + + return 1; +} + int exheader_programid_valid(exheader_context* ctx) { if (!settings_get_ignore_programid(ctx->usersettings)) diff --git a/ctrtool/exheader.h b/ctrtool/exheader.h index 6cebdeba..436a8d72 100644 --- a/ctrtool/exheader.h +++ b/ctrtool/exheader.h @@ -109,6 +109,7 @@ typedef struct settings* usersettings; u8 partitionid[8]; u8 programid[8]; + u8 hash[32]; u8 counter[16]; u8 key[16]; u32 offset; @@ -134,6 +135,7 @@ 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_hash(exheader_context* ctx, u8 hash[32]); 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); @@ -143,6 +145,7 @@ int exheader_process(exheader_context* ctx, u32 actions); const char* exheader_getvalidstring(int valid); void exheader_print(exheader_context* ctx); void exheader_verify(exheader_context* ctx); +int exheader_hash_valid(exheader_context* ctx); int exheader_programid_valid(exheader_context* ctx); void exheader_determine_key(exheader_context* ctx, u32 actions); diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index de2328d3..f8e8dc2d 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -328,6 +328,7 @@ void ncch_process(ncch_context* ctx, u32 actions) 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_hash(&ctx->exheader, ctx->header.extendedheaderhash); exheader_set_counter(&ctx->exheader, exheadercounter); exheader_set_key(&ctx->exheader, ctx->key); exheader_set_encrypted(&ctx->exheader, ctx->encrypted); @@ -361,7 +362,7 @@ void ncch_process(ncch_context* ctx, u32 actions) if (result && ncch_get_exheader_size(ctx)) { - if (!exheader_programid_valid(&ctx->exheader)) + if (!exheader_hash_valid(&ctx->exheader)) return; result = exheader_process(&ctx->exheader, actions); diff --git a/ctrtool/tmd.c b/ctrtool/tmd.c index e6a2b15e..8ba2dd24 100644 --- a/ctrtool/tmd.c +++ b/ctrtool/tmd.c @@ -86,6 +86,7 @@ void tmd_print(tmd_context* ctx) ctr_tmd_header_2048* header2048 = 0; ctr_tmd_body* body = 0; unsigned int contentcount = 0; + unsigned int savesize = 0; unsigned int i; if (type == TMD_RSA_2048_SHA256 || type == TMD_RSA_2048_SHA1) @@ -104,7 +105,8 @@ void tmd_print(tmd_context* ctx) body = tmd_get_body(ctx); contentcount = getbe16(body->contentcount); - + savesize = getle32(body->savedatasize); + fprintf(stdout, "\nTMD header:\n"); fprintf(stdout, "Signature type: %s\n", tmd_get_type_string(type)); fprintf(stdout, "Issuer: %s\n", body->issuer); @@ -115,6 +117,12 @@ void tmd_print(tmd_context* ctx) 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)); + if(savesize < sizeKB) + fprintf(stdout, "Save Size: %08x\n", savesize); + else if(savesize < sizeMB) + fprintf(stdout, "Save Size: %dKB (%08x)\n", savesize/sizeKB, savesize); + else + fprintf(stdout, "Save Size: %dMB (%08x)\n", savesize/sizeMB, savesize); 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)); diff --git a/ctrtool/tmd.h b/ctrtool/tmd.h index 5d9f475e..6272429d 100644 --- a/ctrtool/tmd.h +++ b/ctrtool/tmd.h @@ -27,12 +27,16 @@ typedef struct unsigned char titleid[8]; unsigned char titletype[4]; unsigned char groupid[2]; - unsigned char padding3[62]; + unsigned char savedatasize[4]; + unsigned char privsavedatasize[4]; + unsigned char padding3[4]; + unsigned char twlflag; + unsigned char padding4[0x31]; unsigned char accessrights[4]; unsigned char titleversion[2]; unsigned char contentcount[2]; unsigned char bootcontent[2]; - unsigned char padding4[2]; + unsigned char padding5[2]; unsigned char hash[32]; unsigned char contentinfo[36*64]; } ctr_tmd_body; From 2e48695ef6abd417e142d85cfc0dcaee3e9fe375 Mon Sep 17 00:00:00 2001 From: 3DSGuy Date: Tue, 23 Sep 2014 13:15:07 +0800 Subject: [PATCH 076/317] reversed (un)intentional vandalism I should have known better than to trust someone who hadn't REd the format from scratch. --- makerom/accessdesc.c | 40 ---------------------------------------- makerom/keyset.c | 25 ------------------------- makerom/keyset.h | 1 - makerom/user_settings.c | 9 +++++---- 4 files changed, 5 insertions(+), 70 deletions(-) diff --git a/makerom/accessdesc.c b/makerom/accessdesc.c index f3b10563..317f759e 100644 --- a/makerom/accessdesc.c +++ b/makerom/accessdesc.c @@ -307,11 +307,6 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)app_fw1D_prod_hdrpub; *cxiPvtk = NULL; } - if(keys->keyset == pki_GATEWAY3DS){ - *accessDescSig = (u8*)app_fw1D_prod_acexsig; - *cxiPubk = (u8*)app_fw1D_prod_hdrpub; - *cxiPvtk = (u8*)app_fw1D_prod_hdrpub; - } break; case 0x1E: if(keys->keyset == pki_PRODUCTION){ @@ -319,11 +314,6 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)app_fw1E_prod_hdrpub; *cxiPvtk = NULL; } - if(keys->keyset == pki_GATEWAY3DS){ - *accessDescSig = (u8*)app_fw1E_prod_acexsig; - *cxiPubk = (u8*)app_fw1E_prod_hdrpub; - *cxiPvtk = (u8*)app_fw1E_prod_hdrpub; - } break; case 0x20: if(keys->keyset == pki_DEVELOPMENT){ @@ -336,11 +326,6 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)app_fw20_prod_hdrpub; *cxiPvtk = NULL; } - if(keys->keyset == pki_GATEWAY3DS){ - *accessDescSig = (u8*)app_fw20_prod_acexsig; - *cxiPubk = (u8*)app_fw20_prod_hdrpub; - *cxiPvtk = (u8*)app_fw20_prod_hdrpub; - } break; case 0x21: if(keys->keyset == pki_DEVELOPMENT){ @@ -353,11 +338,6 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)app_fw21_prod_hdrpub; *cxiPvtk = NULL; } - if(keys->keyset == pki_GATEWAY3DS){ - *accessDescSig = (u8*)app_fw21_prod_acexsig; - *cxiPubk = (u8*)app_fw21_prod_hdrpub; - *cxiPvtk = (u8*)app_fw21_prod_hdrpub; - } break; case 0x23: if(keys->keyset == pki_DEVELOPMENT){ @@ -370,11 +350,6 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)app_fw23_prod_hdrpub; *cxiPvtk = NULL; } - if(keys->keyset == pki_GATEWAY3DS){ - *accessDescSig = (u8*)app_fw23_prod_acexsig; - *cxiPubk = (u8*)app_fw23_prod_hdrpub; - *cxiPvtk = (u8*)app_fw23_prod_hdrpub; - } break; case 0x27: if(keys->keyset == pki_PRODUCTION){ @@ -382,11 +357,6 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)app_fw27_prod_hdrpub; *cxiPvtk = NULL; } - if(keys->keyset == pki_GATEWAY3DS){ - *accessDescSig = (u8*)app_fw27_prod_acexsig; - *cxiPubk = (u8*)app_fw27_prod_hdrpub; - *cxiPvtk = (u8*)app_fw27_prod_hdrpub; - } break; } @@ -399,11 +369,6 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)ecapp_fw20_prod_hdrpub; *cxiPvtk = NULL; } - if(keys->keyset == pki_GATEWAY3DS){ - *accessDescSig = (u8*)ecapp_fw20_prod_acexsig; - *cxiPubk = (u8*)ecapp_fw20_prod_hdrpub; - *cxiPvtk = (u8*)ecapp_fw20_prod_hdrpub; - } break; case 0x23: if(keys->keyset == pki_PRODUCTION){ @@ -411,11 +376,6 @@ void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, *cxiPubk = (u8*)ecapp_fw23_prod_hdrpub; *cxiPvtk = NULL; } - if(keys->keyset == pki_GATEWAY3DS){ - *accessDescSig = (u8*)ecapp_fw23_prod_acexsig; - *cxiPubk = (u8*)ecapp_fw23_prod_hdrpub; - *cxiPvtk = (u8*)ecapp_fw23_prod_hdrpub; - } break; } } diff --git a/makerom/keyset.c b/makerom/keyset.c index eaa31168..66ebeff6 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -170,31 +170,6 @@ int LoadKeysFromResources(keys_struct *keys) SetTikCert(keys,(u8*)xsC_ppki_cert); SetTmdCert(keys,(u8*)cpB_ppki_cert); } - else if(keys->keyset == pki_GATEWAY3DS){ - keys->keysetLoaded = true; - /* AES Keys */ - // CIA - if(keys->aes.currentCommonKey > 0xff) - SetCurrentCommonKey(keys,0); - - // NCCH - SetNormalKey(keys,(u8*)dev_fixed_ncch_key[0]); - SetSystemFixedKey(keys,(u8*)dev_fixed_ncch_key[0]); - - /* RSA Keys */ - // CIA - SetTIK_RsaKey(keys,(u8*)xsC_ppki_rsa_priv,(u8*)xsC_ppki_rsa_pub); - SetTMD_RsaKey(keys,(u8*)cpB_ppki_rsa_priv,(u8*)cpB_ppki_rsa_pub); - // CCI/CFA - Set_CCI_CFA_RsaKey(keys,(u8*)prod_ncsd_cfa_priv,(u8*)prod_ncsd_cfa_pub); - // CXI - SetAccessDesc_RsaKey(keys,(u8*)prod_acex_priv,(u8*)prod_acex_pub); - - /* Certs */ - SetCaCert(keys,(u8*)ca3_ppki_cert); - SetTikCert(keys,(u8*)xsC_ppki_cert); - SetTmdCert(keys,(u8*)cpB_ppki_cert); - } return 0; } diff --git a/makerom/keyset.h b/makerom/keyset.h index e8c92301..9d6c0c60 100644 --- a/makerom/keyset.h +++ b/makerom/keyset.h @@ -26,7 +26,6 @@ typedef enum pki_DEVELOPMENT, pki_PRODUCTION, pki_CUSTOM, - pki_GATEWAY3DS } pki_keyset; typedef enum diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 1efbc0c3..792611a3 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -177,12 +177,14 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } if(strcasecmp(argv[i+1],"test") == 0 || strcasecmp(argv[i+1],"t") == 0) set->common.keys.keyset = pki_TEST; + //else if(strcasecmp(argv[i+1],"beta") == 0 || strcasecmp(argv[i+1],"b") == 0) + // set->common.keys.keyset = pki_BETA; else if(strcasecmp(argv[i+1],"debug") == 0 || strcasecmp(argv[i+1],"development") == 0 || strcasecmp(argv[i+1],"d") == 0) set->common.keys.keyset = pki_DEVELOPMENT; else if(strcasecmp(argv[i+1],"retail") == 0 || strcasecmp(argv[i+1],"production") == 0 || strcasecmp(argv[i+1],"p") == 0) set->common.keys.keyset = pki_PRODUCTION; - else if(strcasecmp(argv[i+1],"gw") == 0 || strcasecmp(argv[i+1],"gateway3ds") == 0 || strcasecmp(argv[i+1],"g") == 0) - set->common.keys.keyset = pki_GATEWAY3DS; + //else if(strcasecmp(argv[i+1],"custom") == 0 || strcasecmp(argv[i+1],"c") == 0) + // set->common.keys.keyset = pki_CUSTOM; else{ fprintf(stderr,"[SETTING ERROR] Unrecognised target '%s'\n",argv[i+1]); return USR_BAD_ARG; @@ -866,11 +868,10 @@ void DisplayHelp(char *app_name) printf(" -v Verbose output\n"); printf(" -DNAME=VALUE Substitute values in RSF file\n"); printf("KEY OPTIONS:\n"); - printf(" -target Target for crypto, defaults to 't'\n"); + printf(" -target Target for crypto, defaults to 't'\n"); printf(" 't' Test(false) Keys & prod Certs\n"); printf(" 'd' Development Keys & Certs\n"); printf(" 'p' Production Keys & Certs\n"); - printf(" 'g' Production Keys & Certs for GW3DS only\n"); printf(" -ckeyid Override the automatic common key selection\n"); printf(" -ncchseckey Ncch keyX index ('0'=1.0+, '1'=7.0+)\n"); printf(" -showkeys Display the loaded key chain\n"); From e6f84e1b323d368a230cd05c40b5aa6eb08bc2cb Mon Sep 17 00:00:00 2001 From: profi200 Date: Wed, 12 Nov 2014 01:11:23 +0100 Subject: [PATCH 077/317] Don't free twice --- makerom/rsf_settings.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/makerom/rsf_settings.c b/makerom/rsf_settings.c index fdfa291e..47696b49 100644 --- a/makerom/rsf_settings.c +++ b/makerom/rsf_settings.c @@ -389,8 +389,7 @@ void free_RsfSettings(rsf_settings *set) free(set->AccessControlInfo.OtherUserSaveDataId1); free(set->AccessControlInfo.OtherUserSaveDataId2); free(set->AccessControlInfo.OtherUserSaveDataId3); - free(set->AccessControlInfo.ExtSaveDataId); - free(set->AccessControlInfo.SystemMode); + free(set->AccessControlInfo.ExtSaveDataId); free(set->AccessControlInfo.AffinityMask); free(set->AccessControlInfo.DescVersion); free(set->AccessControlInfo.ResourceLimitCategory); From 038aca268ac82d43a9142db7cfc6b6746a142698 Mon Sep 17 00:00:00 2001 From: profi200 Date: Wed, 24 Dec 2014 04:17:56 +0100 Subject: [PATCH 078/317] Added detection for secure3 (New 3DS) crypto. --- ctrtool/ncch.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index f8e8dc2d..4a781be2 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -589,6 +589,8 @@ void ncch_print(ncch_context* ctx) fprintf(stdout, " > Crypto key: %s\n", programid_is_system(header->programid)? "Fixed":"Zeros"); else if (header->flags[3] & 1) fprintf(stdout, " > Crypto key: Secure2\n"); + else if (header->flags[3] & 10) + fprintf(stdout, " > Crypto key: secure3 (New 3DS)\n"); else fprintf(stdout, " > Crypto key: Secure\n"); fprintf(stdout, " > Form type: %s\n", formtypetostring(header->flags[5])); From f8993e198a8439ad3e6883414cc3c7cfb5cdd9d0 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 14 Sep 2015 21:52:04 +0800 Subject: [PATCH 079/317] ctrtool: Updated exheader validation and ncch spec/flag recognition --- ctrtool/ctrtool.vcxproj | 6 +- ctrtool/exheader.c | 213 ++++++++++++++++++++++------------------ ctrtool/exheader.h | 44 ++++++++- ctrtool/ncch.c | 21 ++-- ctrtool/ncch.h | 2 +- 5 files changed, 172 insertions(+), 114 deletions(-) diff --git a/ctrtool/ctrtool.vcxproj b/ctrtool/ctrtool.vcxproj index 75b72886..628d52fe 100644 --- a/ctrtool/ctrtool.vcxproj +++ b/ctrtool/ctrtool.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -18,13 +18,13 @@ Application - v110 + v140 Unicode true Application - v110 + v140 Unicode diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index 6f064d97..92db1a65 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -126,6 +126,58 @@ int exheader_programid_valid(exheader_context* ctx) return 1; } +void exheader_deserialise_arm11localcaps_permissions(exheader_arm11systemlocalcaps_deserialised *caps, const exheader_arm11systemlocalcaps *arm11) +{ + int i; + + memset(caps, 0, sizeof(exheader_arm11systemlocalcaps_deserialised)); + + memcpy(caps->program_id, arm11->programid, 8); + caps->core_version = getle32(arm11->coreversion); + + caps->enable_l2_cache = (arm11->flag[0] >> 0) & 1; + caps->use_additional_cores = (arm11->flag[0] >> 1) & 1; + caps->new3ds_systemmode = (arm11->flag[1] >> 0) & 15; + + caps->ideal_processor = (arm11->flag[2] >> 0) & 3; + caps->affinity_mask = (arm11->flag[2] >> 2) & 3; + caps->old3ds_systemmode = (arm11->flag[2] >> 4) & 15; + + caps->priority = (s8)arm11->flag[3]; + + // storage info + if (arm11->storageinfo.otherattributes & 2) { + caps->extdata_id = 0; + for (i = 0; i < 3; i++) + caps->other_user_saveid[i] = 0; + caps->use_other_variation_savedata = 0; + + for (i = 0; i < 3; i++) + caps->accessible_saveid[i] = 0xfffff & (getle64(arm11->storageinfo.accessibleuniqueids) >> 20 * (2 - i)); + for (i = 0; i < 3; i++) + caps->accessible_saveid[i+3] = 0xfffff & (getle64(arm11->storageinfo.extsavedataid) >> 20 * (2 - i)); + } + else { + caps->extdata_id = getle64(arm11->storageinfo.extsavedataid); + for (i = 0; i < 3; i++) + caps->other_user_saveid[i] = 0xfffff & (getle64(arm11->storageinfo.accessibleuniqueids) >> 20 * (2 - i)); + caps->use_other_variation_savedata = (getle64(arm11->storageinfo.accessibleuniqueids) >> 60) & 1; + + for (i = 0; i < 6; i++) + caps->accessible_saveid[i] = 0; + } + + caps->system_saveid[0] = getle32(arm11->storageinfo.systemsavedataid); + caps->system_saveid[1] = getle32(arm11->storageinfo.systemsavedataid + 4); + caps->accessinfo = getle64(arm11->storageinfo.accessinfo) & ~((u64)0xff00000000000000); + + // Service Access Control + for (i = 0; i < 34; i++) + strncpy(caps->service_access_control[i], (char*)arm11->serviceaccesscontrol[i], 8); + + caps->resource_limit_category = arm11->resourcelimitcategory; +} + int exheader_process(exheader_context* ctx, u32 actions) { exheader_determine_key(ctx, actions); @@ -135,13 +187,13 @@ int exheader_process(exheader_context* ctx, u32 actions) if (ctx->header.codesetinfo.flags.flag & 1) ctx->compressedflag = 1; + exheader_deserialise_arm11localcaps_permissions(&ctx->system_local_caps, &ctx->header.arm11systemlocalcaps); + if (actions & VerifyFlag) exheader_verify(ctx); if (actions & InfoFlag) - { - exheader_print(ctx); - } + exheader_print(ctx); return 1; } @@ -397,11 +449,10 @@ void exheader_print_arm11accessinfo(exheader_context* ctx) { char str[100]; u64 i, bit; - u64 accessinfo = 0xffffffffffffff & getle64(ctx->header.arm11systemlocalcaps.storageinfo.accessinfo); for(i = 0; i < 56; i++) { bit = ((u64)1 << i); - if((accessinfo & bit) == bit) + if((ctx->system_local_caps.accessinfo & bit) == bit) fprintf(stdout, " > %s\n",exheader_print_accessinfobit((u32)i,str)); } } @@ -410,72 +461,21 @@ void exheader_print_arm11storageinfo(exheader_context* ctx) { u32 i; - // Storage Info - u32 systemsaveID[2]; - u64 extdataID; - u32 otherusersaveID[3]; - u32 accessiblesaveID[6]; - - u8 otherattibutes = ctx->header.arm11systemlocalcaps.storageinfo.otherattributes; - u8 accessOtherVariationSavedata = (getle64(ctx->header.arm11systemlocalcaps.storageinfo.accessibleuniqueids) & 0x1000000000000000) == 0x1000000000000000; - - systemsaveID[0] = getle32(ctx->header.arm11systemlocalcaps.storageinfo.systemsavedataid); - systemsaveID[1] = getle32(ctx->header.arm11systemlocalcaps.storageinfo.systemsavedataid+4); - - extdataID = getle64(ctx->header.arm11systemlocalcaps.storageinfo.extsavedataid); - - for(i = 0; i < 3; i++) - { - accessiblesaveID[i] = 0xfffff & (getle64(ctx->header.arm11systemlocalcaps.storageinfo.accessibleuniqueids) >> 20*(2-i)); - otherusersaveID[i] = 0xfffff & (getle64(ctx->header.arm11systemlocalcaps.storageinfo.accessibleuniqueids) >> 20*(2-i)); - } - - for(i = 0; i < 3; i++) - { - accessiblesaveID[i+3] = 0xfffff & (getle64(ctx->header.arm11systemlocalcaps.storageinfo.extsavedataid) >> 20*(2-i)); - } - - if(otherattibutes & 2) - { - extdataID = 0; - for(i = 0; i < 3; i++) - otherusersaveID[i] = 0; - } - else - { - for(i = 0; i < 6; i++) - accessiblesaveID[i] = 0; - } - - fprintf(stdout, "Ext savedata id: 0x%08"PRIx64"\n",extdataID); + fprintf(stdout, "Ext savedata id: 0x%"PRIx64"\n",ctx->system_local_caps.extdata_id); for(i = 0; i < 2; i++) - fprintf(stdout, "System savedata id %d: 0x%08x %s\n",i+1,systemsaveID[i],exheader_getvalidstring(ctx->validsystemsaveID[i])); + fprintf(stdout, "System savedata id %d: 0x%x %s\n",i+1, ctx->system_local_caps.system_saveid[i],exheader_getvalidstring(ctx->validsystemsaveID[i])); for(i = 0; i < 3; i++) - fprintf(stdout, "OtherUserSaveDataId%d: 0x%05x\n",i+1,otherusersaveID[i]); + fprintf(stdout, "OtherUserSaveDataId%d: 0x%x\n",i+1, ctx->system_local_caps.other_user_saveid[i]); fprintf(stdout, "Accessible Savedata Ids:\n"); for(i = 0; i < 6; i++) { - if(accessiblesaveID[i] != 0x00000) - fprintf(stdout, " > 0x%05x\n",accessiblesaveID[i]); + if(ctx->system_local_caps.accessible_saveid[i] != 0x00000) + fprintf(stdout, " > 0x%05x\n", ctx->system_local_caps.accessible_saveid[i]); } - fprintf(stdout, "Other Variation Saves: %s\n", accessOtherVariationSavedata ? "Accessible" : "Inaccessible"); - if(ctx->validaccessinfo == Unchecked) - memdump(stdout, "Access info: ", ctx->header.arm11systemlocalcaps.storageinfo.accessinfo, 7); - else if(ctx->validaccessinfo == Good) - memdump(stdout, "Access info (GOOD): ", ctx->header.arm11systemlocalcaps.storageinfo.accessinfo, 7); - else - memdump(stdout, "Access info (FAIL): ", ctx->header.arm11systemlocalcaps.storageinfo.accessinfo, 7); - exheader_print_arm11accessinfo(ctx); - - fprintf(stdout, "Other attributes: %02X", ctx->header.arm11systemlocalcaps.storageinfo.otherattributes); - /* - if(otherattibutes & 1) - fprintf(stdout," [no use romfs]"); - if(otherattibutes & 2) - fprintf(stdout," [use extended savedata access control]"); - */ - printf("\n"); + fprintf(stdout, "Other Variation Saves: %s\n", ctx->system_local_caps.use_other_variation_savedata ? "Accessible" : "Inaccessible"); + fprintf(stdout, "Access info: 0x%"PRIx64" %s\n", ctx->system_local_caps.accessinfo,exheader_getvalidstring(ctx->validaccessinfo)); + exheader_print_arm11accessinfo(ctx); } int exheader_signature_verify(exheader_context* ctx, rsakey2048* key) @@ -486,61 +486,78 @@ int exheader_signature_verify(exheader_context* ctx, rsakey2048* key) return ctr_rsa_verify_hash(ctx->header.accessdesc.signature, hash, key); } - void exheader_verify(exheader_context* ctx) { - unsigned int i; - u8 exheaderflag6[3]; - u8 descflag6[3]; + unsigned int i, j; + exheader_arm11systemlocalcaps_deserialised accessdesc; + + exheader_deserialise_arm11localcaps_permissions(&accessdesc, &ctx->header.accessdesc.arm11systemlocalcaps); ctx->validsystemsaveID[0] = Good; ctx->validsystemsaveID[1] = Good; ctx->validaccessinfo = Good; + ctx->validcoreversion = Good; ctx->validprogramid = Good; ctx->validpriority = Good; ctx->validaffinitymask = Good; ctx->valididealprocessor = Good; + ctx->validold3dssystemmode = Good; + ctx->validnew3dssystemmode = Good; + ctx->validenablel2cache = Good; + ctx->validuseadditionalcores = Good; + ctx->validservicecontrol = Good; for(i=0; i<8; i++) { - if (0 == (ctx->header.arm11systemlocalcaps.programid[i] & ~ctx->header.accessdesc.arm11systemlocalcaps.programid[i])) + if (ctx->system_local_caps.program_id[i] == accessdesc.program_id[i] || accessdesc.program_id[i] == 0xFF) continue; ctx->validprogramid = Fail; break; } - // Ideal Proccessor - exheaderflag6[0] = (ctx->header.arm11systemlocalcaps.flag>>0)&0x3; - descflag6[0] = (ctx->header.accessdesc.arm11systemlocalcaps.flag>>0)&0x3; - // Affinity Mask - exheaderflag6[1] = (ctx->header.arm11systemlocalcaps.flag>>2)&0x3; - descflag6[1] = (ctx->header.accessdesc.arm11systemlocalcaps.flag>>2)&0x3; - // System Mode - //exheaderflag6[2] = (ctx->header.arm11systemlocalcaps.flag>>4)&0xf; - //descflag6[2] = (ctx->header.accessdesc.arm11systemlocalcaps.flag>>4)&0xf; - - if (ctx->header.accessdesc.arm11systemlocalcaps.priority > ctx->header.arm11systemlocalcaps.priority || ctx->header.arm11systemlocalcaps.priority > 127) + if (ctx->system_local_caps.core_version != accessdesc.core_version) + ctx->validcoreversion = Fail; + + if (ctx->system_local_caps.priority < accessdesc.priority) ctx->validpriority = Fail; - if((1<system_local_caps.ideal_processor & accessdesc.ideal_processor) == 0) ctx->valididealprocessor = Fail; - if (exheaderflag6[1] & ~descflag6[1]) + if (ctx->system_local_caps.affinity_mask & ~accessdesc.affinity_mask) ctx->validaffinitymask = Fail; + if (ctx->system_local_caps.old3ds_systemmode > accessdesc.old3ds_systemmode) + ctx->validold3dssystemmode = Fail; + + if (ctx->system_local_caps.new3ds_systemmode > accessdesc.new3ds_systemmode) + ctx->validnew3dssystemmode = Fail; + // Storage Info Verify - if(0 != (getle32(ctx->header.arm11systemlocalcaps.storageinfo.systemsavedataid) & ~getle32(ctx->header.accessdesc.arm11systemlocalcaps.storageinfo.systemsavedataid))) + if(ctx->system_local_caps.system_saveid[0] & ~accessdesc.system_saveid[0]) ctx->validsystemsaveID[0] = Fail; - if(0 != (getle32(ctx->header.arm11systemlocalcaps.storageinfo.systemsavedataid+4) & ~getle32(ctx->header.accessdesc.arm11systemlocalcaps.storageinfo.systemsavedataid+4))) + if(ctx->system_local_caps.system_saveid[1] & ~accessdesc.system_saveid[1]) ctx->validsystemsaveID[1] = Fail; - for(i=0; i<7; i++) - { - if(0 == (ctx->header.arm11systemlocalcaps.storageinfo.accessinfo[i] & ~ctx->header.accessdesc.arm11systemlocalcaps.storageinfo.accessinfo[i])) - continue; + + if (ctx->system_local_caps.accessinfo & ~accessdesc.accessinfo) ctx->validaccessinfo = Fail; - break; + + // Service Access Control + for (i = 0; i < 34; i++) { + if (strlen(ctx->system_local_caps.service_access_control[i]) == 0) + continue; + + for (j = 0; j < 34; j++) { + if (strcmp(ctx->system_local_caps.service_access_control[i], accessdesc.service_access_control[j]) == 0) + break; + } + + if (strcmp(ctx->system_local_caps.service_access_control[i], accessdesc.service_access_control[j]) == 0) + continue; + + ctx->validservicecontrol = Fail; } if (ctx->usersettings) @@ -609,21 +626,21 @@ void exheader_print(exheader_context* ctx) fprintf(stdout, "Program id: %016"PRIx64" %s\n", getle64(ctx->header.arm11systemlocalcaps.programid), exheader_getvalidstring(ctx->validprogramid)); fprintf(stdout, "Core version: 0x%X\n", getle32(ctx->header.arm11systemlocalcaps.coreversion)); - fprintf(stdout, "System mode: 0x%X\n", (ctx->header.arm11systemlocalcaps.flag>>4)&0xF); - fprintf(stdout, "Ideal processor: %d %s\n", (ctx->header.arm11systemlocalcaps.flag>>0)&0x3, exheader_getvalidstring(ctx->valididealprocessor)); - fprintf(stdout, "Affinity mask: %d %s\n", (ctx->header.arm11systemlocalcaps.flag>>2)&0x3, exheader_getvalidstring(ctx->validaffinitymask)); - fprintf(stdout, "Main thread priority: %d %s\n", ctx->header.arm11systemlocalcaps.priority, exheader_getvalidstring(ctx->validpriority)); + fprintf(stdout, "System mode: %d %s\n", ctx->system_local_caps.old3ds_systemmode, exheader_getvalidstring(ctx->validold3dssystemmode)); + fprintf(stdout, "System mode (New3DS): %d %s\n", ctx->system_local_caps.new3ds_systemmode, exheader_getvalidstring(ctx->validnew3dssystemmode)); + fprintf(stdout, "Ideal processor: %d %s\n", ctx->system_local_caps.ideal_processor, exheader_getvalidstring(ctx->valididealprocessor)); + fprintf(stdout, "Affinity mask: %d %s\n", ctx->system_local_caps.affinity_mask, exheader_getvalidstring(ctx->validaffinitymask)); + fprintf(stdout, "Main thread priority: %d %s\n", ctx->system_local_caps.priority, exheader_getvalidstring(ctx->validpriority)); // print resource limit descriptor too? currently mostly zeroes... exheader_print_arm11storageinfo(ctx); exheader_print_arm11kernelcapabilities(ctx); exheader_print_arm9accesscontrol(ctx); - - - for(i=0; i<0x20; i++) + fprintf(stdout, "Service access: %s\n", exheader_getvalidstring(ctx->validservicecontrol)); + for(i=0; i<34; i++) { - if (getle64(ctx->header.arm11systemlocalcaps.serviceaccesscontrol[i]) != 0x0000000000000000UL) - fprintf(stdout, "Service access: %.8s\n", ctx->header.arm11systemlocalcaps.serviceaccesscontrol[i]); + if (strlen(ctx->system_local_caps.service_access_control[i]) > 0) + fprintf(stdout, " > %s\n", ctx->system_local_caps.service_access_control[i]); } fprintf(stdout, "Reslimit category: %02X\n", ctx->header.arm11systemlocalcaps.resourcelimitcategory); } diff --git a/ctrtool/exheader.h b/ctrtool/exheader.h index 436a8d72..a4432c26 100644 --- a/ctrtool/exheader.h +++ b/ctrtool/exheader.h @@ -57,16 +57,41 @@ typedef struct { u8 programid[8]; u8 coreversion[4]; - u8 reserved0[2]; - u8 flag; - u8 priority; + u8 flag[4]; u8 resourcelimitdescriptor[0x10][2]; exheader_storageinfo storageinfo; - u8 serviceaccesscontrol[0x20][8]; - u8 reserved[0x1f]; + u8 serviceaccesscontrol[34][8]; + u8 reserved[0xf]; u8 resourcelimitcategory; } exheader_arm11systemlocalcaps; +typedef struct +{ + u8 program_id[8]; + u32 core_version; + + // flag + u8 enable_l2_cache; + u8 use_additional_cores; + u8 new3ds_systemmode; + u8 ideal_processor; + u8 affinity_mask; + u8 old3ds_systemmode; + s8 priority; + + // storageinfo + u64 extdata_id; + u32 other_user_saveid[3]; + u8 use_other_variation_savedata; + u32 accessible_saveid[3]; + u32 system_saveid[2]; + u64 accessinfo; + + + char service_access_control[34][10]; + u8 resource_limit_category; +} exheader_arm11systemlocalcaps_deserialised; + typedef struct { u8 descriptors[28][4]; @@ -115,6 +140,9 @@ typedef struct u32 offset; u32 size; exheader_header header; + + exheader_arm11systemlocalcaps_deserialised system_local_caps; + ctr_aes_context aes; ctr_rsa_context rsa; int compressedflag; @@ -123,8 +151,14 @@ typedef struct int validpriority; int validaffinitymask; int valididealprocessor; + int validold3dssystemmode; + int validnew3dssystemmode; + int validenablel2cache; + int validuseadditionalcores; + int validcoreversion; int validsystemsaveID[2]; int validaccessinfo; + int validservicecontrol; int validsignature; } exheader_context; diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 4a781be2..07f8755d 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -540,6 +540,17 @@ static const char* contenttypetostring(unsigned char flags) case 2: return "Manual"; case 3: return "Child"; case 4: return "Trial"; + case 5: return "Extended System Update"; + default: return "Unknown"; + } +} + +static const char* contentplatformtostring(unsigned char platform) +{ + switch (platform) + { + case 1: return "CTR"; + case 2: return "SNAKE"; default: return "Unknown"; } } @@ -566,6 +577,7 @@ void ncch_print(ncch_context* ctx) fprintf(stdout, "Partition id: %016"PRIx64"\n", getle64(header->partitionid)); fprintf(stdout, "Maker code: %04x\n", getle16(header->makercode)); fprintf(stdout, "Version: %04x\n", getle16(header->version)); + fprintf(stdout, "Title seed check: %08x\n", getle32(header->seedcheck)); fprintf(stdout, "Program id: %016"PRIx64"\n", getle64(header->programid)); if(ctx->logohashcheck == Unchecked) memdump(stdout, "Logo hash: ", header->logohash, 0x20); @@ -587,16 +599,11 @@ void ncch_print(ncch_context* ctx) 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 if (header->flags[3] & 1) - fprintf(stdout, " > Crypto key: Secure2\n"); - else if (header->flags[3] & 10) - fprintf(stdout, " > Crypto key: secure3 (New 3DS)\n"); else - fprintf(stdout, " > Crypto key: Secure\n"); + fprintf(stdout, " > Crypto key: Secure (%d)%s\n", header->flags[3], header->flags[7] & 32? " (KeyY seeded)" : ""); 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"); + fprintf(stdout, " > Content platform: %s\n", contentplatformtostring(header->flags[4])); if (header->flags[7] & 2) fprintf(stdout, " > No RomFS mount\n"); diff --git a/ctrtool/ncch.h b/ctrtool/ncch.h index b6539e06..f453bb58 100644 --- a/ctrtool/ncch.h +++ b/ctrtool/ncch.h @@ -26,7 +26,7 @@ typedef struct u8 partitionid[8]; u8 makercode[2]; u8 version[2]; - u8 reserved0[4]; + u8 seedcheck[4]; u8 programid[8]; u8 reserved1[0x10]; u8 logohash[0x20]; From e636db7397484413372b28325fb5c822eaa2c9bb Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 14 Sep 2015 23:48:59 +0800 Subject: [PATCH 080/317] makerom: updated to latest SDK specs. --- makerom/accessdesc.c | 4 +- makerom/exheader.c | 52 ++- makerom/exheader.h | 14 +- makerom/makerom.vcxproj | 272 +++++++++++++++ makerom/makerom.vcxproj.filters | 584 ++++++++++++++++++++++++++++++++ makerom/ncch.c | 44 ++- makerom/ncch.h | 26 +- makerom/readme.txt | 29 ++ makerom/rsf_settings.c | 12 +- makerom/user_settings.h | 7 +- 10 files changed, 1008 insertions(+), 36 deletions(-) create mode 100644 makerom/makerom.vcxproj create mode 100644 makerom/makerom.vcxproj.filters create mode 100644 makerom/readme.txt diff --git a/makerom/accessdesc.c b/makerom/accessdesc.c index 317f759e..fcb17be3 100644 --- a/makerom/accessdesc.c +++ b/makerom/accessdesc.c @@ -44,12 +44,12 @@ int accessdesc_SignWithKey(exheader_settings *exhdrset) memcpy(&exhdrset->acexDesc->arm9AccessControlInfo,&exhdrset->exHdr->arm9AccessControlInfo,sizeof(exhdr_ARM9AccessControlInfo)); /* Adjust Data */ - u8 *flag = &exhdrset->acexDesc->arm11SystemLocalCapabilities.flag; + u8 *flag = &exhdrset->acexDesc->arm11SystemLocalCapabilities.flag[2]; u8 SystemMode = (*flag>>4)&0xF; u8 AffinityMask = (*flag>>2)&0x3; u8 IdealProcessor = 1<<((*flag>>0)&0x3); *flag = (u8)(SystemMode << 4 | AffinityMask << 2 | IdealProcessor); - exhdrset->acexDesc->arm11SystemLocalCapabilities.priority /= 2; + exhdrset->acexDesc->arm11SystemLocalCapabilities.flag[3] /= 2; /* Sign AccessDesc */ return SignAccessDesc(exhdrset->acexDesc,exhdrset->keys); diff --git a/makerom/exheader.c b/makerom/exheader.c index 87d78f77..e159ebe8 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -317,7 +317,34 @@ int SetARM11SystemLocalInfoFlags(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_ return EXHDR_BAD_RSF_OPT; } - /* Flag */ + /* Flag[0] */ + arm11->flag[0] |= rsf->AccessControlInfo.EnableL2Cache; + + if (rsf->AccessControlInfo.CpuSpeed) { + if(strcasecmp(rsf->AccessControlInfo.CpuSpeed, "256mhz") == 0) + arm11->flag[0] |= cpuspeed_268MHz << 1; + else if(strcasecmp(rsf->AccessControlInfo.CpuSpeed, "804mhz") == 0) + arm11->flag[0] |= cpuspeed_804MHz << 1; + else { + fprintf(stderr, "[EXHEADER ERROR] Invalid cpu speed: 0x%s\n", rsf->AccessControlInfo.CpuSpeed); + return EXHDR_BAD_RSF_OPT; + } + } + else + arm11->flag[0] |= cpuspeed_268MHz << 1; + + /* Flag[1] (SystemModeExt) */ + u8 systemModeExt = 0; + if (rsf->AccessControlInfo.SystemModeExt) { + systemModeExt = strtol(rsf->AccessControlInfo.SystemModeExt, NULL, 0); + if (systemModeExt > 15) { + fprintf(stderr, "[EXHEADER ERROR] Unexpected SystemModeExt: 0x%x. Expected range: 0x0 - 0xf\n", systemModeExt); + return EXHDR_BAD_RSF_OPT; + } + arm11->flag[1] = systemModeExt & 0xf; + } + + /* Flag[2] */ u8 affinityMask = 0; u8 idealProcessor = 0; u8 systemMode = 0; @@ -343,9 +370,9 @@ int SetARM11SystemLocalInfoFlags(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_ return EXHDR_BAD_RSF_OPT; } } - arm11->flag = (u8)(systemMode << 4 | affinityMask << 2 | idealProcessor); + arm11->flag[2] = (u8)(systemMode << 4 | affinityMask << 2 | idealProcessor); - /* Thread Priority */ + /* flag[3] (Thread Priority) */ if(rsf->AccessControlInfo.Priority){ u8 priority = strtoul(rsf->AccessControlInfo.Priority,NULL,0); if(GetAppType(rsf) == processtype_APPLICATION) @@ -354,7 +381,7 @@ int SetARM11SystemLocalInfoFlags(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_ fprintf(stderr,"[EXHEADER ERROR] Invalid Priority: %d\n",priority); return EXHDR_BAD_RSF_OPT; } - arm11->priority = priority; + arm11->flag[3] = priority; } else{ ErrorParamNotFound("AccessControlInfo/Priority"); @@ -479,8 +506,13 @@ void SetARM11StorageInfoSystemSaveDataId(exhdr_ARM11SystemLocalCapabilities *arm void SetARM11StorageInfoExtSaveDataId(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) { - if(rsf->AccessControlInfo.ExtSaveDataId) - u64_to_u8(arm11->storageInfo.extSavedataId, strtoull(rsf->AccessControlInfo.ExtSaveDataId,NULL,0), LE); + if (rsf->AccessControlInfo.UseExtSaveData || rsf->AccessControlInfo.ExtSaveDataId) { + if (rsf->AccessControlInfo.ExtSaveDataId) + u64_to_u8(arm11->storageInfo.extSavedataId, strtoull(rsf->AccessControlInfo.ExtSaveDataId, NULL, 0), LE); + else + u32_to_u8(arm11->storageInfo.extSavedataId, GetTidUniqueId(u8_to_u64(arm11->programId,LE)), LE); + } + } void SetARM11StorageInfoOtherUserSaveData(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) @@ -508,6 +540,10 @@ bool CheckCondiditionsForNewAccessibleSaveDataIds(rsf_settings *rsf) fprintf(stderr,"[EXHEADER ERROR] Too many UniqueId in \"AccessibleSaveDataIds\".\n"); return false; } + if (rsf->AccessControlInfo.UseExtSaveData) { + fprintf(stderr, "[EXHEADER ERROR] UseExtSaveData must be false if AccessibleSaveDataIds is specified.\n"); + return false; + } if (rsf->AccessControlInfo.ExtSaveDataId){ fprintf(stderr,"[EXHEADER ERROR] ExtSaveDataId is unavailable if AccessibleSaveDataIds is specified.\n"); return false; @@ -561,8 +597,8 @@ void SetARM11StorageInfoAccessibleSaveDataIds(exhdr_ARM11SystemLocalCapabilities int SetARM11ServiceAccessControl(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_settings *rsf) { if(rsf->AccessControlInfo.ServiceAccessControl){ - if(rsf->AccessControlInfo.ServiceAccessControlNum > 32){ - fprintf(stderr,"[EXHEADER ERROR] Too Many Service Names, maximum is 32\n"); + if(rsf->AccessControlInfo.ServiceAccessControlNum > 34){ + fprintf(stderr,"[EXHEADER ERROR] Too Many Service Names, maximum is 34\n"); return EXHDR_BAD_RSF_OPT; } for(int i = 0; i < rsf->AccessControlInfo.ServiceAccessControlNum; i++){ diff --git a/makerom/exheader.h b/makerom/exheader.h index d33639c6..8e6ef3ce 100644 --- a/makerom/exheader.h +++ b/makerom/exheader.h @@ -28,6 +28,12 @@ typedef enum resrc_limit_OTHER } resource_limit_category; +typedef enum +{ + cpuspeed_268MHz, + cpuspeed_804MHz +}; + typedef enum { othcap_PERMIT_DEBUG = (1 << 0), @@ -127,13 +133,11 @@ typedef struct { u8 programId[8]; u8 coreVersion[4]; - u8 padding0[2]; - u8 flag; - u8 priority; + u8 flag[4]; u8 resourceLimitDescriptor[16][2]; exhdr_StorageInfo storageInfo; - u8 serviceAccessControl[32][8]; // Those char[8] server names - u8 padding1[0x1f]; + u8 serviceAccessControl[34][8]; // Those char[8] server names + u8 padding1[0xf]; u8 resourceLimitCategory; } exhdr_ARM11SystemLocalCapabilities; diff --git a/makerom/makerom.vcxproj b/makerom/makerom.vcxproj new file mode 100644 index 00000000..aa4d41d6 --- /dev/null +++ b/makerom/makerom.vcxproj @@ -0,0 +1,272 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {21926330-F5A5-4643-AD32-D4F167CE226B} + MakeFileProj + + + + Makefile + true + v140 + + + Makefile + false + v140 + + + Application + true + v140 + + + Application + false + v140 + + + + + + + + + + + + + + + + + + + + + make + makerom.exe + make clean + make rebuild + WIN32;_DEBUG;$(NMakePreprocessorDefinitions) + + + make + makerom.exe + make clean + make rebuild + WIN32;NDEBUG;$(NMakePreprocessorDefinitions) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/makerom/makerom.vcxproj.filters b/makerom/makerom.vcxproj.filters new file mode 100644 index 00000000..0f4ba0ba --- /dev/null +++ b/makerom/makerom.vcxproj.filters @@ -0,0 +1,584 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {bab0486d-d6e9-48e4-b4a5-ab1c9a917a15} + + + {e3b6ff03-546a-4f9c-8246-ee2a5a6b5c20} + + + {2bf08da5-c0b2-4b6f-a07a-0f3a03b79f14} + + + {7545c89e-a9ce-4c04-989e-ae726a518efd} + + + {49964d4d-b429-41e6-a85f-e4d361de0faf} + + + {a0455bf4-2a1e-4ced-9d42-88d7ce131c22} + + + + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\polarssl + + + Header Files\libyaml + + + Header Files\libyaml + + + Resource Files\PKI + + + Resource Files\PKI + + + Resource Files\PKI + + + Resource Files\PKI + + + Resource Files\PKI + + + Resource Files\DESC + + + Resource Files\DESC + + + Resource Files\DESC + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\libyaml + + + Source Files\libyaml + + + Source Files\libyaml + + + Source Files\libyaml + + + Source Files\libyaml + + + Source Files\libyaml + + + Source Files\libyaml + + + Source Files\libyaml + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + Source Files\polarssl + + + + + Resource Files + + + \ No newline at end of file diff --git a/makerom/ncch.c b/makerom/ncch.c index 8f15f91d..438940bf 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -626,28 +626,48 @@ int SetCommonHeaderBasicData(ncch_settings *set, ncch_hdr *hdr) hdr->flags[ncchflag_CONTENT_BLOCK_SIZE] = GetCtrBlockSizeFlag(set->options.blockSize); /* Setting ContentPlatform */ - hdr->flags[ncchflag_CONTENT_PLATFORM] = 1; // CTR + if(set->rsfSet->TitleInfo.Platform){ + if(strcasecmp(set->rsfSet->TitleInfo.Platform, "ctr") == 0) + hdr->flags[ncchflag_CONTENT_PLATFORM] = platform_CTR; + else if (strcasecmp(set->rsfSet->TitleInfo.Platform, "snake") == 0) + hdr->flags[ncchflag_CONTENT_PLATFORM] = platform_SNAKE; + else{ + fprintf(stderr, "[NCCH ERROR] Invalid Platform '%s'\n", set->rsfSet->TitleInfo.Platform); + return NCCH_BAD_RSF_SET; + } + } + else + hdr->flags[ncchflag_CONTENT_PLATFORM] = platform_CTR; /* Setting OtherFlag */ if(!set->options.UseRomFS) hdr->flags[ncchflag_OTHER_FLAG] |= otherflag_NoMountRomFs; + /* Setting FormType */ + hdr->flags[ncchflag_CONTENT_TYPE] = form_Unassigned; + if(set->options.IsCfa) + hdr->flags[ncchflag_CONTENT_TYPE] = form_SimpleContent; + else if (set->options.UseRomFS) + hdr->flags[ncchflag_CONTENT_TYPE] = form_Executable; + else + hdr->flags[ncchflag_CONTENT_TYPE] = form_ExecutableWithoutRomfs; + /* Setting ContentType */ - hdr->flags[ncchflag_CONTENT_TYPE] = 0; - if(set->options.UseRomFS) hdr->flags[ncchflag_CONTENT_TYPE] |= content_Data; - if(!set->options.IsCfa) hdr->flags[ncchflag_CONTENT_TYPE] |= content_Executable; if(set->rsfSet->BasicInfo.ContentType){ - if(strcmp(set->rsfSet->BasicInfo.ContentType,"Application") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= 0; - else if(strcmp(set->rsfSet->BasicInfo.ContentType,"SystemUpdate") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= content_SystemUpdate; - else if(strcmp(set->rsfSet->BasicInfo.ContentType,"Manual") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= content_Manual; - else if(strcmp(set->rsfSet->BasicInfo.ContentType,"Child") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= content_Child; - else if(strcmp(set->rsfSet->BasicInfo.ContentType,"Trial") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= content_Trial; + if(strcmp(set->rsfSet->BasicInfo.ContentType,"Application") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= (content_Application << 2); + else if(strcmp(set->rsfSet->BasicInfo.ContentType,"SystemUpdate") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= (content_SystemUpdate << 2); + else if(strcmp(set->rsfSet->BasicInfo.ContentType,"Manual") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= (content_Manual << 2); + else if(strcmp(set->rsfSet->BasicInfo.ContentType,"Child") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= (content_Child << 2); + else if(strcmp(set->rsfSet->BasicInfo.ContentType,"Trial") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= (content_Trial << 2); + else if (strcmp(set->rsfSet->BasicInfo.ContentType, "ExtendedSystemUpdate") == 0) hdr->flags[ncchflag_CONTENT_TYPE] |= (content_ExtendedSystemUpdate << 2); else{ fprintf(stderr,"[NCCH ERROR] Invalid ContentType '%s'\n",set->rsfSet->BasicInfo.ContentType); return NCCH_BAD_RSF_SET; } } + else + hdr->flags[ncchflag_CONTENT_TYPE] |= (content_Application << 2); return 0; } @@ -663,7 +683,7 @@ bool IsValidProductCode(char *ProductCode, bool FreeProductCode) if(strlen(ProductCode) < 10) return false; - if(strncmp(ProductCode,"CTR",3) != 0) + if(strncmp(ProductCode,"CTR",3) != 0 && strncmp(ProductCode, "KTR", 3) != 0) return false; for(int i = 3; i < 10; i++){ @@ -946,12 +966,12 @@ bool IsNcch(FILE *fp, u8 *buf) bool IsCfa(ncch_hdr* hdr) { - return (((hdr->flags[ncchflag_CONTENT_TYPE] & content_Data) == content_Data) && ((hdr->flags[ncchflag_CONTENT_TYPE] & content_Executable) != content_Executable)); + return (hdr->flags[ncchflag_CONTENT_TYPE] & 3) == form_SimpleContent; } bool IsUpdateCfa(ncch_hdr* hdr) { - return (((hdr->flags[ncchflag_CONTENT_TYPE] & content_SystemUpdate) == content_SystemUpdate) && ((hdr->flags[ncchflag_CONTENT_TYPE] & content_Child) != content_Child) && IsCfa(hdr)); + return (hdr->flags[ncchflag_CONTENT_TYPE] >> 2) == content_SystemUpdate || (hdr->flags[ncchflag_CONTENT_TYPE] >> 2) == content_ExtendedSystemUpdate; } u32 GetNcchBlockSize(ncch_hdr* hdr) diff --git a/makerom/ncch.h b/makerom/ncch.h index f2918830..baf220f5 100644 --- a/makerom/ncch.h +++ b/makerom/ncch.h @@ -50,14 +50,28 @@ typedef enum typedef enum { - content_Data = 0x1, - content_Executable = 0x2, - content_SystemUpdate = 0x4, - content_Manual = 0x8, - content_Child = (0x4|0x8), - content_Trial = 0x10 + form_Unassigned, + form_SimpleContent, + form_ExecutableWithoutRomfs, + form_Executable +}; + +typedef enum +{ + content_Application, + content_SystemUpdate, + content_Manual, + content_Child, + content_Trial, + content_ExtendedSystemUpdate } ncch_content_bitmask; +typedef enum +{ + platform_CTR = 0x1, + platform_SNAKE = 0x2 +} ncch_platform; + typedef struct { u16 formatVersion; diff --git a/makerom/readme.txt b/makerom/readme.txt new file mode 100644 index 00000000..8b7b09e3 --- /dev/null +++ b/makerom/readme.txt @@ -0,0 +1,29 @@ +======================================================================== + MAKEFILE PROJECT : makerom Project Overview +======================================================================== + +AppWizard has created this makerom project for you. + +This file contains a summary of what you will find in each of the files that +make up your makerom project. + + +makerom.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +makerom.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +This project allows you to build/clean/rebuild from within Visual Studio by calling the commands you have input +in the wizard. The build command can be nmake or any other tool you use. + +This project does not contain any files, so there are none displayed in Solution Explorer. + +///////////////////////////////////////////////////////////////////////////// diff --git a/makerom/rsf_settings.c b/makerom/rsf_settings.c index 47696b49..8240b456 100644 --- a/makerom/rsf_settings.c +++ b/makerom/rsf_settings.c @@ -102,12 +102,16 @@ void GET_AccessControlInfo(ctr_yaml_context *ctx, rsf_settings *rsf) else if(cmpYamlValue("UseOtherVariationSaveData",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.UseOtherVariationSaveData,"UseOtherVariationSaveData",ctx); else if(cmpYamlValue("RunnableOnSleep",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.RunnableOnSleep,"RunnableOnSleep",ctx); else if(cmpYamlValue("SpecialMemoryArrange",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.SpecialMemoryArrange,"SpecialMemoryArrange",ctx); - + else if(cmpYamlValue("UseExtSaveData", ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.UseExtSaveData, "UseExtSaveData", ctx); + else if(cmpYamlValue("EnableL2Cache", ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.EnableL2Cache, "EnableL2Cache", ctx); + else if(cmpYamlValue("IdealProcessor",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.IdealProcessor,"IdealProcessor",ctx,0); else if(cmpYamlValue("Priority",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.Priority,"Priority",ctx,0); else if(cmpYamlValue("MemoryType",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.MemoryType,"MemoryType",ctx,0); else if(cmpYamlValue("SystemMode",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.SystemMode,"SystemMode",ctx,0); + else if(cmpYamlValue("SystemModeExt", ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.SystemModeExt, "SystemModeExt", ctx, 0); + else if(cmpYamlValue("CpuSpeed", ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.CpuSpeed, "CpuSpeed", ctx, 0); else if(cmpYamlValue("CoreVersion",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.CoreVersion,"CoreVersion",ctx,0); else if(cmpYamlValue("HandleTableSize",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.HandleTableSize,"HandleTableSize",ctx,0); else if(cmpYamlValue("SystemSaveDataId1",ctx)) SetSimpleYAMLValue(&rsf->AccessControlInfo.SystemSaveDataId1,"SystemSaveDataId1",ctx,0); @@ -279,7 +283,8 @@ void GET_TitleInfo(ctr_yaml_context *ctx, rsf_settings *rsf) if(ctx->error || ctx->done) return; // Handle childs - if(cmpYamlValue("Category",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.Category,"Category",ctx,0); + if (cmpYamlValue("Platform", ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.Platform, "Platform", ctx, 0); + else if(cmpYamlValue("Category",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.Category,"Category",ctx,0); else if(cmpYamlValue("UniqueId",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.UniqueId,"UniqueId",ctx,0); else if(cmpYamlValue("Version",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.Version,"Version",ctx,0); else if(cmpYamlValue("ContentsIndex",ctx)) SetSimpleYAMLValue(&rsf->TitleInfo.ContentsIndex,"ContentsIndex",ctx,0); @@ -382,6 +387,8 @@ void free_RsfSettings(rsf_settings *set) free(set->AccessControlInfo.Priority); free(set->AccessControlInfo.MemoryType); free(set->AccessControlInfo.SystemMode); + free(set->AccessControlInfo.SystemModeExt); + free(set->AccessControlInfo.CpuSpeed); free(set->AccessControlInfo.CoreVersion); free(set->AccessControlInfo.HandleTableSize); free(set->AccessControlInfo.SystemSaveDataId1); @@ -502,6 +509,7 @@ void free_RsfSettings(rsf_settings *set) free(set->PlainRegion); //TitleInfo + free(set->TitleInfo.Platform); free(set->TitleInfo.Category); free(set->TitleInfo.UniqueId); free(set->TitleInfo.Version); diff --git a/makerom/user_settings.h b/makerom/user_settings.h index 561429a5..299f5ef9 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -86,12 +86,16 @@ typedef struct bool UseOtherVariationSaveData; bool RunnableOnSleep; bool SpecialMemoryArrange; - + bool UseExtSaveData; + bool EnableL2Cache; + // Strings char *IdealProcessor; char *Priority; char *MemoryType; char *SystemMode; + char *SystemModeExt; + char *CpuSpeed; char *CoreVersion; char *HandleTableSize; char *SystemSaveDataId1; @@ -178,6 +182,7 @@ typedef struct struct{ // Strings + char *Platform; char *Category; char *UniqueId; char *Version; From 2abefe56355ccbbf4b7780ebf9cc76535377577e Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 15 Sep 2015 22:05:00 +0800 Subject: [PATCH 081/317] makerom: misc, gave names to enums --- makerom/exheader.h | 2 +- makerom/ncch.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/makerom/exheader.h b/makerom/exheader.h index 8e6ef3ce..4bac931d 100644 --- a/makerom/exheader.h +++ b/makerom/exheader.h @@ -32,7 +32,7 @@ typedef enum { cpuspeed_268MHz, cpuspeed_804MHz -}; +} cpu_speed; typedef enum { diff --git a/makerom/ncch.h b/makerom/ncch.h index baf220f5..93c56860 100644 --- a/makerom/ncch.h +++ b/makerom/ncch.h @@ -54,7 +54,7 @@ typedef enum form_SimpleContent, form_ExecutableWithoutRomfs, form_Executable -}; +} ncch_form_type; typedef enum { From 3a9d4b700b4586d975576bf7615466e94d7f69ee Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 15 Sep 2015 22:06:43 +0800 Subject: [PATCH 082/317] makerom: fixed romfs generation --- makerom/makerom.vcxproj | 1 + makerom/romfs.h | 27 ++--- makerom/romfs_gen.c | 232 +++++++++++++++++++++------------------- 3 files changed, 129 insertions(+), 131 deletions(-) diff --git a/makerom/makerom.vcxproj b/makerom/makerom.vcxproj index aa4d41d6..97ca195f 100644 --- a/makerom/makerom.vcxproj +++ b/makerom/makerom.vcxproj @@ -67,6 +67,7 @@ make clean make rebuild WIN32;_DEBUG;$(NMakePreprocessorDefinitions) + C:\Program Files\mingw-w64\x86_64-5.2.0-win32-seh-rt_v4-rev0\mingw64\x86_64-w64-mingw32\include;$(IncludePath) make diff --git a/makerom/romfs.h b/makerom/romfs.h index fecb2da7..54bc218e 100644 --- a/makerom/romfs.h +++ b/makerom/romfs.h @@ -54,7 +54,7 @@ typedef struct 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 hashoffset[4]; u8 namesize[4]; //u8 name[ROMFS_MAXNAMESIZE]; } romfs_direntry; //sizeof(romfs_direntry) = 0x18 @@ -65,7 +65,7 @@ typedef struct 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 hashoffset[4]; u8 namesize[4]; //u8 name[ROMFS_MAXNAMESIZE]; } romfs_fileentry; //sizeof(romfs_fileentry) = 0x20 @@ -86,18 +86,18 @@ typedef struct fs_dir *fs; - u32 *dirUTable; - u32 m_dirUTableEntry; - u32 u_dirUTableEntry; + u32 *dirHashTable; + u32 m_dirHashTable; + u32 u_dirHashTable; u8 *dirTable; u32 dirNum; u32 m_dirTableLen; u32 u_dirTableLen; - u32 *fileUTable; - u32 m_fileUTableEntry; - u32 u_fileUTableEntry; + u32 *fileHashTable; + u32 m_fileHashTable; + u32 u_fileHashTable; u8 *fileTable; u32 fileNum; @@ -112,16 +112,5 @@ typedef struct ivfc_level level[4]; } romfs_buildctx; -/* -typedef struct -{ - u8 *output; - u64 romfsSize; - u64 romfsHeaderSize; - - bool ImportRomfsBinary; - FILE *romfsBinary; -} romfs_buildctx; -*/ int SetupRomFs(ncch_settings *ncchset, romfs_buildctx *ctx); int BuildRomFs(romfs_buildctx *ctx); \ No newline at end of file diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 3ad516c0..ed45daa5 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -11,11 +11,13 @@ const fs_romfs_char ROMFS_EMPTY_PATH[2] = {0x0000, 0x0000}; bool IsFileWanted(fs_file *file, void *filter_criteria); bool IsDirWanted(fs_dir *dir, void *filter_criteria); void CalcDirSize(romfs_buildctx *ctx, fs_dir *fs); -int CalcRomfsSize(romfs_buildctx *ctx); -int AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling); -int AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling); +void CalcRomfsSize(romfs_buildctx *ctx); int FilterRomFS(fs_dir *fs_raw, fs_dir *fs_filtered, void *filter_criteria); -int PopulateRomfs(romfs_buildctx *ctx); +void AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling); +void AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling); +void AddDirChildrenToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 dir); +void PopulateHashTable(romfs_buildctx *ctx); +void PopulateRomfs(romfs_buildctx *ctx); void BuildRomfsHeader(romfs_buildctx *ctx); void BuildIvfcHeader(romfs_buildctx *ctx); void GenIvfcHashTree(romfs_buildctx *ctx); @@ -69,7 +71,9 @@ int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) free(fs_raw); //printf("leave if no ROMFS needs to be made\n"); - if(ctx->fs->u_file == 0){ + // If no wanted directory or file exists in root, don't make romfs + // a directory can be wanted if a wanted file exists somewhere under it + if(ctx->fs->u_file == 0 && ctx->fs->u_dir == 0){ ctx->romfsSize = 0; goto finish; } @@ -116,8 +120,7 @@ int BuildRomFsBinary(romfs_buildctx *ctx) // Build Romfs ctx->romfsHdr = (romfs_infoheader*)(ctx->level[3].pos); BuildRomfsHeader(ctx); - if(PopulateRomfs(ctx) != 0) - return -1; + PopulateRomfs(ctx); // Finalise by building IVFC hash tree @@ -180,25 +183,35 @@ void CalcDirSize(romfs_buildctx *ctx, fs_dir *fs) ctx->dirNum += fs->u_dir; } -int CalcRomfsSize(romfs_buildctx *ctx) +u32 GetHashTableCount(u32 num) +{ + u32 count = num; + if (num < 3) + count = 3; + else if (count < 19) + count |= 1; + else { + while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 || count % 11 == 0 || count % 13 == 0 || count % 17 == 0) + count++; + } + return count; +} + +void CalcRomfsSize(romfs_buildctx *ctx) { ctx->dirNum = 1; // root dir //printf("Recursively get FS sizes\n"); CalcDirSize(ctx,ctx->fs); //printf("check U tables\n"); - ctx->u_dirUTableEntry = 0; - ctx->m_dirUTableEntry = 3; - if(ctx->dirNum > 3) - ctx->m_dirUTableEntry += align(ctx->dirNum-3,2); + ctx->u_dirHashTable = 0; + ctx->m_dirHashTable = GetHashTableCount(ctx->dirNum); - ctx->u_fileUTableEntry = 0; - ctx->m_fileUTableEntry = 3; - if(ctx->fileNum > 3) - ctx->m_fileUTableEntry += align(ctx->fileNum-3,2); + ctx->u_fileHashTable = 0; + ctx->m_fileHashTable = GetHashTableCount(ctx->fileNum); //printf("calc romfs header size\n"); - u32 romfsHdrSize = align(sizeof(romfs_infoheader) + ctx->m_dirUTableEntry*sizeof(u32) + ctx->m_dirTableLen + ctx->m_fileUTableEntry*sizeof(u32) + ctx->m_fileTableLen,0x10); + u32 romfsHdrSize = align(sizeof(romfs_infoheader) + ctx->m_dirHashTable*sizeof(u32) + ctx->m_dirTableLen + ctx->m_fileHashTable*sizeof(u32) + ctx->m_fileTableLen,0x10); //printf("predict level sizes\n"); ctx->level[3].size = romfsHdrSize + ctx->m_dataLen; // data @@ -214,7 +227,6 @@ int CalcRomfsSize(romfs_buildctx *ctx) ctx->romfsSize += align(ctx->level[i].size,ROMFS_BLOCK_SIZE); //printf("return from CalcRomfsSize();\n"); - return 0; } int FilterRomFS(fs_dir *fs_raw, fs_dir *fs_filtered, void *filter_criteria) @@ -278,10 +290,10 @@ void BuildRomfsHeader(romfs_buildctx *ctx) for(int i = 0; i < 4; i++){ if(i == 0){ - ctx->dirUTable = (u32*)(ctx->level[3].pos + level3_pos); + ctx->dirHashTable = (u32*)(ctx->level[3].pos + level3_pos); u32_to_u8(ctx->romfsHdr->section[i].offset,level3_pos,LE); - u32_to_u8(ctx->romfsHdr->section[i].size,ctx->m_dirUTableEntry*sizeof(u32),LE); - level3_pos += ctx->m_dirUTableEntry*sizeof(u32); + u32_to_u8(ctx->romfsHdr->section[i].size,ctx->m_dirHashTable*sizeof(u32),LE); + level3_pos += ctx->m_dirHashTable*sizeof(u32); } else if(i == 1 && ctx->m_dirTableLen){ ctx->dirTable = ctx->level[3].pos + level3_pos; @@ -290,10 +302,10 @@ void BuildRomfsHeader(romfs_buildctx *ctx) level3_pos += ctx->m_dirTableLen; } else if(i == 2){ - ctx->fileUTable = (u32*)(ctx->level[3].pos + level3_pos); + ctx->fileHashTable = (u32*)(ctx->level[3].pos + level3_pos); u32_to_u8(ctx->romfsHdr->section[i].offset,level3_pos,LE); - u32_to_u8(ctx->romfsHdr->section[i].size,ctx->m_fileUTableEntry*sizeof(u32),LE); - level3_pos += ctx->m_fileUTableEntry*sizeof(u32); + u32_to_u8(ctx->romfsHdr->section[i].size,ctx->m_fileHashTable*sizeof(u32),LE); + level3_pos += ctx->m_fileHashTable*sizeof(u32); } else if(i == 3 && ctx->m_fileTableLen){ ctx->fileTable = ctx->level[3].pos + level3_pos; @@ -310,71 +322,45 @@ void BuildRomfsHeader(romfs_buildctx *ctx) ctx->data = ctx->level[3].pos + align(level3_pos,0x10); u32_to_u8(ctx->romfsHdr->dataoffset,align(level3_pos,0x10),LE); - memset(ctx->dirUTable,0xff,ctx->m_dirUTableEntry*sizeof(u32)); - memset(ctx->fileUTable,0xff,ctx->m_fileUTableEntry*sizeof(u32)); - - return; + for (u32 i = 0; i < ctx->m_dirHashTable; i++) { + ctx->dirHashTable[i] = ROMFS_UNUSED_ENTRY; + } + + for (u32 i = 0; i < ctx->m_fileHashTable; i++) { + ctx->fileHashTable[i] = ROMFS_UNUSED_ENTRY; + } } -void AddDirHashKey(romfs_buildctx *ctx, u32 parent, fs_romfs_char* path, u32 dirOffset) +u32 GetFileHashTableIndex(romfs_buildctx *ctx, u32 parent, fs_romfs_char *path) { - u32 hash = CalcPathHash(parent,path,0,fs_u16StrLen(path)); - u32 index = hash % ctx->m_dirUTableEntry; - if(ctx->dirUTable[index] == ROMFS_UNUSED_ENTRY) ctx->dirUTable[index] = dirOffset; - else - { - romfs_direntry * curdir = (romfs_direntry*)(ctx->dirTable + ctx->dirUTable[index]); - while(1) - { - if(*(u32*)curdir->weirdoffset == ROMFS_UNUSED_ENTRY) - { - *(u32*)curdir->weirdoffset = dirOffset; - break; - } - else - { - curdir = (romfs_direntry*)(ctx->dirTable + *(u32*)curdir->weirdoffset); - } - } - } + u32 hash = CalcPathHash(parent, path, 0, fs_u16StrLen(path)); + return hash % ctx->m_fileHashTable; } -void AddFileHashKey(romfs_buildctx *ctx,u32 parent, fs_romfs_char *path, u32 fileOffset) +u32 GetDirHashTableIndex(romfs_buildctx *ctx, u32 parent, fs_romfs_char* path) { - u32 hash = CalcPathHash(parent,path,0,fs_u16StrLen(path)); - u32 index = hash % ctx->m_fileUTableEntry; - if(ctx->fileUTable[index] == ROMFS_UNUSED_ENTRY) ctx->fileUTable[index] = fileOffset; - else - { - romfs_fileentry * curfile = (romfs_fileentry*)(ctx->fileTable + ctx->fileUTable[index]); - while(1) - { - if(*(u32*)curfile->weirdoffset == ROMFS_UNUSED_ENTRY) - { - *(u32*)curfile->weirdoffset = fileOffset; - break; - } - else - { - curfile = (romfs_fileentry*)(ctx->fileTable + *(u32*)curfile->weirdoffset); - } - } - } + u32 hash = CalcPathHash(parent, path, 0, fs_u16StrLen(path)); + return hash % ctx->m_dirHashTable; } -int AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) +void AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) { romfs_fileentry *entry = (romfs_fileentry*)(ctx->fileTable + ctx->u_fileTableLen); u32_to_u8(entry->parentdiroffset,parent,LE); u32_to_u8(entry->siblingoffset,sibling,LE); - u32_to_u8(entry->weirdoffset,ROMFS_UNUSED_ENTRY,LE); // Import Name u32_to_u8(entry->namesize,file->name_len,LE); u8 *name_pos = (u8*)(ctx->fileTable + ctx->u_fileTableLen + sizeof(romfs_fileentry)); memset(name_pos,0,align(file->name_len,4)); memcpy(name_pos,(u8*)file->name,file->name_len); + + // Set Hash Data + u32 hashindex = GetFileHashTableIndex(ctx, parent, file->name); + u32_to_u8(entry->hashoffset, ctx->fileHashTable[hashindex], LE); + ctx->fileHashTable[hashindex] = ctx->u_fileTableLen; + // Import Data if(file->size) @@ -387,29 +373,31 @@ int AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) ctx->u_dataLen += file->size; // adding file size } else - u64_to_u8(entry->dataoffset,0x40,LE); + u64_to_u8(entry->dataoffset,0x00,LE); - AddFileHashKey(ctx,parent,file->name,ctx->u_fileTableLen); + //AddFileHashKey(ctx,parent,file->name,ctx->u_fileTableLen); ctx->u_fileTableLen += sizeof(romfs_fileentry) + align(file->name_len,4); - - return 0; } -int AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) +void AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) { //wprintf(L"adding %s \n",fs->name); - romfs_direntry *entry = (romfs_direntry*)(ctx->dirTable + ctx->u_dirTableLen); + u32 offset = ctx->u_dirTableLen; + romfs_direntry *entry = (romfs_direntry*)(ctx->dirTable + offset); u32_to_u8(entry->parentoffset,parent,LE); u32_to_u8(entry->siblingoffset,sibling,LE); - u32_to_u8(entry->weirdoffset,ROMFS_UNUSED_ENTRY,LE); - - u32 Currentdir = ctx->u_dirTableLen; + u32_to_u8(entry->childoffset, ROMFS_UNUSED_ENTRY, LE); + u32_to_u8(entry->fileoffset, ROMFS_UNUSED_ENTRY, LE); - if(Currentdir == 0) + if(offset == 0) { u32_to_u8(entry->namesize,0,LE); - AddDirHashKey(ctx,parent,(fs_romfs_char*)ROMFS_EMPTY_PATH,ctx->u_dirTableLen); + + u32 index = GetFileHashTableIndex(ctx, parent, (fs_romfs_char*)ROMFS_EMPTY_PATH); + u32_to_u8(entry->hashoffset, ctx->dirHashTable[index], LE); + ctx->dirHashTable[index] = ctx->u_dirTableLen; + ctx->u_dirTableLen += sizeof(romfs_direntry); } else @@ -418,67 +406,87 @@ int AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) u8 *name_pos = (u8*)(ctx->dirTable + ctx->u_dirTableLen + sizeof(romfs_direntry)); memset(name_pos,0,(u32)align(fs->name_len,4)); memcpy(name_pos,(u8*)fs->name,fs->name_len); - AddDirHashKey(ctx,parent,fs->name,ctx->u_dirTableLen); + + u32 index = GetFileHashTableIndex(ctx, parent, fs->name); + u32_to_u8(entry->hashoffset, ctx->dirHashTable[index], LE); + ctx->dirHashTable[index] = ctx->u_dirTableLen; + ctx->u_dirTableLen += sizeof(romfs_direntry) + (u32)align(fs->name_len,4); } + + //wprintf(L"added %s \n",fs->name); +} + +void AddDirChildrenToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 dir) +{ + //wprintf(L"adding %s \n",fs->name); + romfs_direntry *entry = (romfs_direntry*)(ctx->dirTable + dir); - if(fs->u_file) + if (fs->u_file) { - u32_to_u8(entry->fileoffset,ctx->u_fileTableLen,LE); - - for(u32 i = 0; i < fs->u_file; i++) + u32_to_u8(entry->fileoffset, ctx->u_fileTableLen, LE); + + for (u32 i = 0; i < fs->u_file; i++) { - + u32 file_sibling = 0; - if(i >= fs->u_file-1) + if (i >= fs->u_file - 1) file_sibling = ROMFS_UNUSED_ENTRY; else - file_sibling = ctx->u_fileTableLen + sizeof(romfs_fileentry) + (u32)align(fs->file[i].name_len,4); + file_sibling = ctx->u_fileTableLen + sizeof(romfs_fileentry) + (u32)align(fs->file[i].name_len, 4); //wprintf(L"adding %s (0x%lx)\n",fs->file[i].name,fs->file[i].size); - AddFileToRomfs(ctx,&fs->file[i],Currentdir,file_sibling); + AddFileToRomfs(ctx, &fs->file[i], dir, file_sibling); //wprintf(L"added %s (0x%lx)\n",fs->file[i].name,fs->file[i].size); } } - else - u32_to_u8(entry->fileoffset,ROMFS_UNUSED_ENTRY,LE); - + //printf("Checking if to add dirs\n"); - if(fs->u_dir) + if (fs->u_dir) { + u32 *childs = calloc(fs->u_dir, sizeof(u32)); + u32 dir_sibling; + romfs_direntry *temp_entry; + //printf(" is adding dirs \n"); - u32_to_u8(entry->childoffset,ctx->u_dirTableLen,LE); - fs_dir *dir = (fs_dir*)fs->dir; - for(u32 i = 0; i < fs->u_dir; i++) + u32_to_u8(entry->childoffset, ctx->u_dirTableLen, LE); + fs_dir *subdir = (fs_dir*)fs->dir; + for (u32 i = 0; i < fs->u_dir; i++) { - u32 dir_sibling = 0; - romfs_direntry *temp_entry = (romfs_direntry*)(ctx->dirTable + ctx->u_dirTableLen); - if(i >= fs->u_dir-1) + childs[i] = ctx->u_dirTableLen; + dir_sibling = 0; + temp_entry = (romfs_direntry*)(ctx->dirTable + ctx->u_dirTableLen); + + if (i >= fs->u_dir - 1) dir_sibling = ROMFS_UNUSED_ENTRY; else - { - //printf(" dir has sibling\n"); - dir_sibling = ctx->u_dirTableLen + sizeof(romfs_direntry) + (u32)align(dir[i].name_len,4); - } - AddDirToRomfs(ctx,&dir[i],Currentdir,dir_sibling); - if(dir_sibling != ROMFS_UNUSED_ENTRY) + dir_sibling = ctx->u_dirTableLen + sizeof(romfs_direntry) + (u32)align(subdir[i].name_len, 4); + AddDirToRomfs(ctx, &subdir[i], dir, dir_sibling); + /* unnecessisary as the above prediction is correct + if (dir_sibling != ROMFS_UNUSED_ENTRY) { dir_sibling = ctx->u_dirTableLen;//修复同目录文件夹偏移 (Repair the same directory folder offset) - u32_to_u8(temp_entry->siblingoffset,dir_sibling,LE); + u32_to_u8(temp_entry->siblingoffset, dir_sibling, LE); } + */ + } + for (u32 i = 0; i < fs->u_dir; i++) + { + AddDirChildrenToRomfs(ctx, &subdir[i], dir, childs[i]); } + + free(childs); } - else - u32_to_u8(entry->childoffset,ROMFS_UNUSED_ENTRY,LE); + //printf(" finished adding dirs \n"); //wprintf(L"added %s \n",fs->name); - return 0; } -int PopulateRomfs(romfs_buildctx *ctx) +void PopulateRomfs(romfs_buildctx *ctx) { - return AddDirToRomfs(ctx,ctx->fs,0x0,ROMFS_UNUSED_ENTRY); + AddDirToRomfs(ctx, ctx->fs, 0x0, ROMFS_UNUSED_ENTRY); + AddDirChildrenToRomfs(ctx, ctx->fs, 0x0, 0); } void BuildIvfcHeader(romfs_buildctx *ctx) From 4f47d71d9def0612b20495d3c6b0163b338b34b1 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 15 Sep 2015 22:28:49 +0800 Subject: [PATCH 083/317] makerom: cleaned romfs_gen.c --- makerom/romfs_gen.c | 113 +++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 69 deletions(-) diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index ed45daa5..5a99ba4a 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -26,9 +26,7 @@ u32 CalcPathHash(u32 parent, fs_romfs_char* path, u32 start, u32 length); int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) { - // Input Path - //printf("Get input path\n"); - + /* Input Path */ const int CWD_MAX_LEN = 1024; char *cwd = calloc(CWD_MAX_LEN,sizeof(char)); getcwd(cwd,CWD_MAX_LEN); @@ -46,47 +44,33 @@ int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) fs_path = dir; #endif - // FS Structures + /* FS Structures */ void *filter_criteria = NULL; - //printf("calloc fs_raw\n"); fs_dir *fs_raw = calloc(1,sizeof(fs_dir)); - //printf("calloc ctx->fs\n"); ctx->fs = calloc(1,sizeof(fs_dir)); - //memdump(stdout,"ctx->fs: ",(u8*)ctx->fs,sizeof(fs_dir)); - //printf("ctx->fs = 0x%x\n",ctx->fs); - // Import FS and process - //printf("open fs into fs_raw\n"); + /* Import FS and process */ fs_OpenDir(fs_path,path,path_len,fs_raw); - //printf("filter fs_raw into ctx->fs\n"); FilterRomFS(fs_raw,ctx->fs,filter_criteria); - // free unfiltered FS - //fs_PrintDir(fs_raw,0); - //printf("free discarded file ptrs\n"); + /* free unfiltered FS */ fs_FreeFiles(fs_raw); // All important FPs have been moved with FilterRomFS, so only un-wanted FPs are closed here - //printf("free structs in fs_raw\n"); fs_FreeDir(fs_raw); - //printf("free fs_raw\n"); free(fs_raw); - //printf("leave if no ROMFS needs to be made\n"); - // If no wanted directory or file exists in root, don't make romfs - // a directory can be wanted if a wanted file exists somewhere under it + /* Abort romfs making, if no wanted files/directories were found */ if(ctx->fs->u_file == 0 && ctx->fs->u_dir == 0){ ctx->romfsSize = 0; goto finish; } - // Print Filtered FS - //printf("print filtered FS\n"); + /* Print Filtered FS */ if(ncchset->options.verbose){ printf("[ROMFS] File System:\n"); fs_PrintDir(ctx->fs,0); } - //printf("predict romfs size\n"); CalcRomfsSize(ctx); finish: @@ -96,13 +80,13 @@ int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) int BuildRomFsBinary(romfs_buildctx *ctx) { - // Decide IVFC Level Actual Offsets + /* Decide IVFC Level Actual Offsets */ ctx->level[0].offset = 0; ctx->level[3].offset = ctx->level[0].offset + align(ctx->level[0].size, ROMFS_BLOCK_SIZE); ctx->level[1].offset = ctx->level[3].offset + align(ctx->level[3].size, ROMFS_BLOCK_SIZE); ctx->level[2].offset = ctx->level[1].offset + align(ctx->level[1].size, ROMFS_BLOCK_SIZE); - // Decide IVFC Level Logical Offsets + /* Decide IVFC Level Logical Offsets */ for(int i = 1; i < 4; i++){ if(i == 1) ctx->level[i].logicalOffset = 0; @@ -110,20 +94,20 @@ int BuildRomFsBinary(romfs_buildctx *ctx) ctx->level[i].logicalOffset = align(ctx->level[i-1].logicalOffset + ctx->level[i-1].size,ROMFS_BLOCK_SIZE); } - // Setup IVFC Level Ptrs + /* Setup IVFC Level Ptrs */ for(int i = 0; i < 4; i++){ ctx->level[i].pos = (ctx->output + ctx->level[i].offset); if(i == 0) ctx->level[i].pos += align(sizeof(ivfc_hdr),0x10); } - // Build Romfs + /* Build Romfs */ ctx->romfsHdr = (romfs_infoheader*)(ctx->level[3].pos); BuildRomfsHeader(ctx); PopulateRomfs(ctx); - // Finalise by building IVFC hash tree + /* Finalise by building IVFC hash tree */ ctx->ivfcHdr = (ivfc_hdr*)(ctx->output + ctx->level[0].offset); BuildIvfcHeader(ctx); GenIvfcHashTree(ctx); @@ -200,20 +184,16 @@ u32 GetHashTableCount(u32 num) void CalcRomfsSize(romfs_buildctx *ctx) { ctx->dirNum = 1; // root dir - //printf("Recursively get FS sizes\n"); CalcDirSize(ctx,ctx->fs); - //printf("check U tables\n"); ctx->u_dirHashTable = 0; ctx->m_dirHashTable = GetHashTableCount(ctx->dirNum); ctx->u_fileHashTable = 0; ctx->m_fileHashTable = GetHashTableCount(ctx->fileNum); - //printf("calc romfs header size\n"); u32 romfsHdrSize = align(sizeof(romfs_infoheader) + ctx->m_dirHashTable*sizeof(u32) + ctx->m_dirTableLen + ctx->m_fileHashTable*sizeof(u32) + ctx->m_fileTableLen,0x10); - //printf("predict level sizes\n"); ctx->level[3].size = romfsHdrSize + ctx->m_dataLen; // data ctx->level[2].size = align(ctx->level[3].size,ROMFS_BLOCK_SIZE) / ROMFS_BLOCK_SIZE * SHA_256_LEN ; ctx->level[1].size = align(ctx->level[2].size,ROMFS_BLOCK_SIZE) / ROMFS_BLOCK_SIZE * SHA_256_LEN ; @@ -221,12 +201,9 @@ void CalcRomfsSize(romfs_buildctx *ctx) ctx->romfsHeaderSize = ctx->level[0].size; - //printf("calc total ROMFS size\n"); ctx->romfsSize = 0; for(int i = 0; i < 4; i++) - ctx->romfsSize += align(ctx->level[i].size,ROMFS_BLOCK_SIZE); - - //printf("return from CalcRomfsSize();\n"); + ctx->romfsSize += align(ctx->level[i].size,ROMFS_BLOCK_SIZE); } int FilterRomFS(fs_dir *fs_raw, fs_dir *fs_filtered, void *filter_criteria) @@ -350,19 +327,19 @@ void AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) u32_to_u8(entry->parentdiroffset,parent,LE); u32_to_u8(entry->siblingoffset,sibling,LE); - // Import Name + /* Import name */ u32_to_u8(entry->namesize,file->name_len,LE); u8 *name_pos = (u8*)(ctx->fileTable + ctx->u_fileTableLen + sizeof(romfs_fileentry)); memset(name_pos,0,align(file->name_len,4)); memcpy(name_pos,(u8*)file->name,file->name_len); - // Set Hash Data + /* Set hash data */ u32 hashindex = GetFileHashTableIndex(ctx, parent, file->name); u32_to_u8(entry->hashoffset, ctx->fileHashTable[hashindex], LE); ctx->fileHashTable[hashindex] = ctx->u_fileTableLen; - // Import Data + /* Import data */ if(file->size) { ctx->u_dataLen = align(ctx->u_dataLen,0x10); // Padding @@ -375,101 +352,103 @@ void AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) else u64_to_u8(entry->dataoffset,0x00,LE); - //AddFileHashKey(ctx,parent,file->name,ctx->u_fileTableLen); + /* Increment used file table length */ ctx->u_fileTableLen += sizeof(romfs_fileentry) + align(file->name_len,4); } void AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) { - //wprintf(L"adding %s \n",fs->name); u32 offset = ctx->u_dirTableLen; + u32 hashindex; romfs_direntry *entry = (romfs_direntry*)(ctx->dirTable + offset); + /* Set entry data */ u32_to_u8(entry->parentoffset,parent,LE); u32_to_u8(entry->siblingoffset,sibling,LE); u32_to_u8(entry->childoffset, ROMFS_UNUSED_ENTRY, LE); u32_to_u8(entry->fileoffset, ROMFS_UNUSED_ENTRY, LE); + + /* If root dir ... */ if(offset == 0) { + /* Import name (root dir has no name) */ u32_to_u8(entry->namesize,0,LE); - u32 index = GetFileHashTableIndex(ctx, parent, (fs_romfs_char*)ROMFS_EMPTY_PATH); - u32_to_u8(entry->hashoffset, ctx->dirHashTable[index], LE); - ctx->dirHashTable[index] = ctx->u_dirTableLen; + /* Get hash table index */ + hashindex = GetFileHashTableIndex(ctx, parent, (fs_romfs_char*)ROMFS_EMPTY_PATH); + /* Increment used dir table length */ ctx->u_dirTableLen += sizeof(romfs_direntry); } else { + /* Import name */ u32_to_u8(entry->namesize,fs->name_len,LE); u8 *name_pos = (u8*)(ctx->dirTable + ctx->u_dirTableLen + sizeof(romfs_direntry)); memset(name_pos,0,(u32)align(fs->name_len,4)); memcpy(name_pos,(u8*)fs->name,fs->name_len); - u32 index = GetFileHashTableIndex(ctx, parent, fs->name); - u32_to_u8(entry->hashoffset, ctx->dirHashTable[index], LE); - ctx->dirHashTable[index] = ctx->u_dirTableLen; + /* Get hash table index */ + hashindex = GetFileHashTableIndex(ctx, parent, fs->name); + /* Increment used dir table length */ ctx->u_dirTableLen += sizeof(romfs_direntry) + (u32)align(fs->name_len,4); } - //wprintf(L"added %s \n",fs->name); + /* Set hash data */ + u32_to_u8(entry->hashoffset, ctx->dirHashTable[hashindex], LE); + ctx->dirHashTable[hashindex] = offset; } void AddDirChildrenToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 dir) { - //wprintf(L"adding %s \n",fs->name); romfs_direntry *entry = (romfs_direntry*)(ctx->dirTable + dir); if (fs->u_file) { u32_to_u8(entry->fileoffset, ctx->u_fileTableLen, LE); + /* Create file entries*/ for (u32 i = 0; i < fs->u_file; i++) { - + /* If is the last file, no more siblings */ u32 file_sibling = 0; if (i >= fs->u_file - 1) file_sibling = ROMFS_UNUSED_ENTRY; else file_sibling = ctx->u_fileTableLen + sizeof(romfs_fileentry) + (u32)align(fs->file[i].name_len, 4); - //wprintf(L"adding %s (0x%lx)\n",fs->file[i].name,fs->file[i].size); - AddFileToRomfs(ctx, &fs->file[i], dir, file_sibling); - //wprintf(L"added %s (0x%lx)\n",fs->file[i].name,fs->file[i].size); + /* Create file entry */ + AddFileToRomfs(ctx, &fs->file[i], dir, file_sibling); } } - //printf("Checking if to add dirs\n"); if (fs->u_dir) { + /* Prepare to store child addresses */ u32 *childs = calloc(fs->u_dir, sizeof(u32)); - u32 dir_sibling; - romfs_direntry *temp_entry; - //printf(" is adding dirs \n"); + /* Create child directory entries*/ u32_to_u8(entry->childoffset, ctx->u_dirTableLen, LE); fs_dir *subdir = (fs_dir*)fs->dir; for (u32 i = 0; i < fs->u_dir; i++) { + /* Store address fo child */ childs[i] = ctx->u_dirTableLen; - dir_sibling = 0; - temp_entry = (romfs_direntry*)(ctx->dirTable + ctx->u_dirTableLen); + + u32 dir_sibling = 0; if (i >= fs->u_dir - 1) dir_sibling = ROMFS_UNUSED_ENTRY; else dir_sibling = ctx->u_dirTableLen + sizeof(romfs_direntry) + (u32)align(subdir[i].name_len, 4); + + /* Create child directory entry */ AddDirToRomfs(ctx, &subdir[i], dir, dir_sibling); - /* unnecessisary as the above prediction is correct - if (dir_sibling != ROMFS_UNUSED_ENTRY) - { - dir_sibling = ctx->u_dirTableLen;//修复同目录文件夹偏移 (Repair the same directory folder offset) - u32_to_u8(temp_entry->siblingoffset, dir_sibling, LE); - } - */ } + + /* Populate child's childs */ for (u32 i = 0; i < fs->u_dir; i++) { AddDirChildrenToRomfs(ctx, &subdir[i], dir, childs[i]); @@ -477,10 +456,6 @@ void AddDirChildrenToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 dir) free(childs); } - - //printf(" finished adding dirs \n"); - - //wprintf(L"added %s \n",fs->name); } void PopulateRomfs(romfs_buildctx *ctx) From 66203c674bce6f1a76d9c615b6d5d9f216858017 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 15 Sep 2015 23:05:17 +0800 Subject: [PATCH 084/317] makerom: removed unnecessary polarssl files & updated makefile --- makerom/Makefile | 26 +- makerom/polarssl/arc4.c | 173 -- makerom/polarssl/asn1parse.c | 260 -- makerom/polarssl/asn1write.c | 241 -- makerom/polarssl/blowfish.c | 632 ----- makerom/polarssl/camellia.c | 1035 -------- makerom/polarssl/certs.c | 196 -- makerom/polarssl/cipher.c | 602 ----- makerom/polarssl/cipher_wrap.c | 711 ------ makerom/polarssl/ctr_drbg.c | 562 ----- makerom/polarssl/debug.c | 238 -- makerom/polarssl/des.c | 997 -------- makerom/polarssl/dhm.c | 302 --- makerom/polarssl/entropy.c | 204 -- makerom/polarssl/entropy_poll.c | 136 -- makerom/polarssl/error.c | 612 ----- makerom/polarssl/gcm.c | 621 ----- makerom/polarssl/havege.c | 231 -- makerom/polarssl/md2.c | 368 --- makerom/polarssl/md4.c | 464 ---- makerom/polarssl/net.c | 374 --- makerom/polarssl/padlock.c | 162 -- makerom/polarssl/pbkdf2.c | 60 - makerom/polarssl/pem.c | 355 --- makerom/polarssl/pkcs11.c | 238 -- makerom/polarssl/pkcs12.c | 330 --- makerom/polarssl/pkcs5.c | 415 ---- makerom/polarssl/ssl_cache.c | 221 -- makerom/polarssl/ssl_cli.c | 1386 ----------- makerom/polarssl/ssl_srv.c | 1623 ------------- makerom/polarssl/ssl_tls.c | 3991 ------------------------------- makerom/polarssl/timing.c | 312 --- makerom/polarssl/version.c | 50 - makerom/polarssl/x509parse.c | 3809 ----------------------------- makerom/polarssl/x509write.c | 285 --- makerom/polarssl/xtea.c | 251 -- 36 files changed, 8 insertions(+), 22465 deletions(-) delete mode 100644 makerom/polarssl/arc4.c delete mode 100644 makerom/polarssl/asn1parse.c delete mode 100644 makerom/polarssl/asn1write.c delete mode 100644 makerom/polarssl/blowfish.c delete mode 100644 makerom/polarssl/camellia.c delete mode 100644 makerom/polarssl/certs.c delete mode 100644 makerom/polarssl/cipher.c delete mode 100644 makerom/polarssl/cipher_wrap.c delete mode 100644 makerom/polarssl/ctr_drbg.c delete mode 100644 makerom/polarssl/debug.c delete mode 100644 makerom/polarssl/des.c delete mode 100644 makerom/polarssl/dhm.c delete mode 100644 makerom/polarssl/entropy.c delete mode 100644 makerom/polarssl/entropy_poll.c delete mode 100644 makerom/polarssl/error.c delete mode 100644 makerom/polarssl/gcm.c delete mode 100644 makerom/polarssl/havege.c delete mode 100644 makerom/polarssl/md2.c delete mode 100644 makerom/polarssl/md4.c delete mode 100644 makerom/polarssl/net.c delete mode 100644 makerom/polarssl/padlock.c delete mode 100644 makerom/polarssl/pbkdf2.c delete mode 100644 makerom/polarssl/pem.c delete mode 100644 makerom/polarssl/pkcs11.c delete mode 100644 makerom/polarssl/pkcs12.c delete mode 100644 makerom/polarssl/pkcs5.c delete mode 100644 makerom/polarssl/ssl_cache.c delete mode 100644 makerom/polarssl/ssl_cli.c delete mode 100644 makerom/polarssl/ssl_srv.c delete mode 100644 makerom/polarssl/ssl_tls.c delete mode 100644 makerom/polarssl/timing.c delete mode 100644 makerom/polarssl/version.c delete mode 100644 makerom/polarssl/x509parse.c delete mode 100644 makerom/polarssl/x509write.c delete mode 100644 makerom/polarssl/xtea.c diff --git a/makerom/Makefile b/makerom/Makefile index 669476ae..ef47794a 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -1,19 +1,9 @@ -# Makerom Sources -UTILS_OBJS = utils.o ctr_utils.o dir.o utf.o keyset.o titleid.o -CIA_OBJS = cia.o certs.o tik.o tmd.o -NCCH_OBJS = ncch.o exheader.o accessdesc.o exefs.o elf.o romfs.o romfs_import.o romfs_gen.o -NCSD_OBJS = ncsd.o cardinfo.o -SETTINGS_OBJS = user_settings.o rsf_settings.o -LIB_API_OBJS = crypto.o yaml_parser.o blz.o - -OBJS = makerom.o $(UTILS_OBJS) $(LIB_API_OBJS) $(SETTINGS_OBJS) $(NCSD_OBJS) $(NCCH_OBJS) $(CIA_OBJS) - -# Libraries -POLAR_OBJS = polarssl/aes.o polarssl/rsa.o polarssl/sha1.o polarssl/sha2.o polarssl/base64.o polarssl/bignum.o polarssl/padlock.o polarssl/md.o polarssl/md_wrap.o polarssl/md5.o polarssl/sha4.o -YAML_OBJS = libyaml/api.o libyaml/dumper.o libyaml/emitter.o libyaml/loader.o libyaml/parser.o libyaml/reader.o libyaml/scanner.o libyaml/writer.o +# Sources +SRC_DIR = . polarssl libyaml +OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) # Compiler Settings -LIBS = -static-libgcc -static-libstdc++ +LIBS = -static-libgcc CXXFLAGS = -I. CFLAGS = --std=c99 -Wall -Wno-unused-but-set-variable -Wno-unused-value -I. -DMAKEROM_VER_MAJOR=$(VER_MAJOR) -DMAKEROM_VER_MINOR=$(VER_MINOR) $(MAKEROM_BUILD_FLAGS) -m64 CC = gcc @@ -21,15 +11,15 @@ CC = gcc # MAKEROM Build Settings MAKEROM_BUILD_FLAGS = #-DDEBUG VER_MAJOR = 0 -VER_MINOR = 13 +VER_MINOR = 14 OUTPUT = makerom main: build rebuild: clean build -build: $(OBJS) $(POLAR_OBJS) $(YAML_OBJS) - g++ -o $(OUTPUT) $(LIBS) $(OBJS) $(POLAR_OBJS) $(YAML_OBJS) -m64 +build: $(OBJS) + g++ -o $(OUTPUT) $(LIBS) $(OBJS) -m64 clean: - rm -rf $(OUTPUT) $(OBJS) $(POLAR_OBJS) $(YAML_OBJS) *.cci *.cia *.cxi *.cfa \ No newline at end of file + rm -rf $(OUTPUT) $(OBJS) *.cci *.cia *.cxi *.cfa \ No newline at end of file diff --git a/makerom/polarssl/arc4.c b/makerom/polarssl/arc4.c deleted file mode 100644 index 85b78f5b..00000000 --- a/makerom/polarssl/arc4.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * An implementation of the ARCFOUR algorithm - * - * Copyright (C) 2006-2013, 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 ARCFOUR algorithm was publicly disclosed on 94/09. - * - * http://groups.google.com/group/sci.crypt/msg/10a300c9d21afca0 - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_ARC4_C) - -#include "polarssl/arc4.h" - -#if !defined(POLARSSL_ARC4_ALT) - -/* - * ARC4 key schedule - */ -void arc4_setup( arc4_context *ctx, const unsigned char *key, unsigned int keylen ) -{ - int i, j, a; - unsigned int k; - unsigned char *m; - - ctx->x = 0; - ctx->y = 0; - m = ctx->m; - - for( i = 0; i < 256; i++ ) - m[i] = (unsigned char) i; - - j = k = 0; - - for( i = 0; i < 256; i++, k++ ) - { - if( k >= keylen ) k = 0; - - a = m[i]; - j = ( j + a + key[k] ) & 0xFF; - m[i] = m[j]; - m[j] = (unsigned char) a; - } -} - -/* - * ARC4 cipher function - */ -int arc4_crypt( arc4_context *ctx, size_t length, const unsigned char *input, - unsigned char *output ) -{ - int x, y, a, b; - size_t i; - unsigned char *m; - - x = ctx->x; - y = ctx->y; - m = ctx->m; - - for( i = 0; i < length; i++ ) - { - x = ( x + 1 ) & 0xFF; a = m[x]; - y = ( y + a ) & 0xFF; b = m[y]; - - m[x] = (unsigned char) b; - m[y] = (unsigned char) a; - - output[i] = (unsigned char) - ( input[i] ^ m[(unsigned char)( a + b )] ); - } - - ctx->x = x; - ctx->y = y; - - return( 0 ); -} - -#endif /* !POLARSSL_ARC4_ALT */ - -#if defined(POLARSSL_SELF_TEST) - -#include -#include - -/* - * ARC4 tests vectors as posted by Eric Rescorla in sep. 1994: - * - * http://groups.google.com/group/comp.security.misc/msg/10a300c9d21afca0 - */ -static const unsigned char arc4_test_key[3][8] = -{ - { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }, - { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } -}; - -static const unsigned char arc4_test_pt[3][8] = -{ - { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } -}; - -static const unsigned char arc4_test_ct[3][8] = -{ - { 0x75, 0xB7, 0x87, 0x80, 0x99, 0xE0, 0xC5, 0x96 }, - { 0x74, 0x94, 0xC2, 0xE7, 0x10, 0x4B, 0x08, 0x79 }, - { 0xDE, 0x18, 0x89, 0x41, 0xA3, 0x37, 0x5D, 0x3A } -}; - -/* - * Checkup routine - */ -int arc4_self_test( int verbose ) -{ - int i; - unsigned char ibuf[8]; - unsigned char obuf[8]; - arc4_context ctx; - - for( i = 0; i < 3; i++ ) - { - if( verbose != 0 ) - printf( " ARC4 test #%d: ", i + 1 ); - - memcpy( ibuf, arc4_test_pt[i], 8 ); - - arc4_setup( &ctx, arc4_test_key[i], 8 ); - arc4_crypt( &ctx, 8, ibuf, obuf ); - - if( memcmp( obuf, arc4_test_ct[i], 8 ) != 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/makerom/polarssl/asn1parse.c b/makerom/polarssl/asn1parse.c deleted file mode 100644 index 2584774a..00000000 --- a/makerom/polarssl/asn1parse.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Generic ASN.1 parsing - * - * Copyright (C) 2006-2011, 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_ASN1_PARSE_C) - -#include "polarssl/asn1.h" - -#if defined(POLARSSL_BIGNUM_C) -#include "polarssl/bignum.h" -#endif - -#include -#include -#include - -/* - * ASN.1 DER decoding routines - */ -int asn1_get_len( unsigned char **p, - const unsigned char *end, - size_t *len ) -{ - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - if( ( **p & 0x80 ) == 0 ) - *len = *(*p)++; - else - { - switch( **p & 0x7F ) - { - case 1: - if( ( end - *p ) < 2 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - *len = (*p)[1]; - (*p) += 2; - break; - - case 2: - if( ( end - *p ) < 3 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - *len = ( (*p)[1] << 8 ) | (*p)[2]; - (*p) += 3; - break; - - case 3: - if( ( end - *p ) < 4 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - *len = ( (*p)[1] << 16 ) | ( (*p)[2] << 8 ) | (*p)[3]; - (*p) += 4; - break; - - case 4: - if( ( end - *p ) < 5 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - *len = ( (*p)[1] << 24 ) | ( (*p)[2] << 16 ) | ( (*p)[3] << 8 ) | (*p)[4]; - (*p) += 5; - break; - - default: - return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); - } - } - - if( *len > (size_t) ( end - *p ) ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - return( 0 ); -} - -int asn1_get_tag( unsigned char **p, - const unsigned char *end, - size_t *len, int tag ) -{ - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - if( **p != tag ) - return( POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); - - (*p)++; - - return( asn1_get_len( p, end, len ) ); -} - -int asn1_get_bool( unsigned char **p, - const unsigned char *end, - int *val ) -{ - int ret; - size_t len; - - if( ( ret = asn1_get_tag( p, end, &len, ASN1_BOOLEAN ) ) != 0 ) - return( ret ); - - if( len != 1 ) - return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); - - *val = ( **p != 0 ) ? 1 : 0; - (*p)++; - - return( 0 ); -} - -int asn1_get_int( unsigned char **p, - const unsigned char *end, - int *val ) -{ - int ret; - size_t len; - - if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 ) - return( ret ); - - if( len > sizeof( int ) || ( **p & 0x80 ) != 0 ) - return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); - - *val = 0; - - while( len-- > 0 ) - { - *val = ( *val << 8 ) | **p; - (*p)++; - } - - return( 0 ); -} - -#if defined(POLARSSL_BIGNUM_C) -int asn1_get_mpi( unsigned char **p, - const unsigned char *end, - mpi *X ) -{ - int ret; - size_t len; - - if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 ) - return( ret ); - - ret = mpi_read_binary( X, *p, len ); - - *p += len; - - return( ret ); -} -#endif /* POLARSSL_BIGNUM_C */ - -int asn1_get_bitstring( unsigned char **p, const unsigned char *end, - asn1_bitstring *bs) -{ - int ret; - - /* Certificate type is a single byte bitstring */ - if( ( ret = asn1_get_tag( p, end, &bs->len, ASN1_BIT_STRING ) ) != 0 ) - return( ret ); - - /* Check length, subtract one for actual bit string length */ - if ( bs->len < 1 ) - return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); - bs->len -= 1; - - /* Get number of unused bits, ensure unused bits <= 7 */ - bs->unused_bits = **p; - if( bs->unused_bits > 7 ) - return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); - (*p)++; - - /* Get actual bitstring */ - bs->p = *p; - *p += bs->len; - - if( *p != end ) - return( POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return 0; -} - - -/* - * Parses and splits an ASN.1 "SEQUENCE OF " - */ -int asn1_get_sequence_of( unsigned char **p, - const unsigned char *end, - asn1_sequence *cur, - int tag) -{ - int ret; - size_t len; - asn1_buf *buf; - - /* Get main sequence tag */ - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - return( ret ); - - if( *p + len != end ) - return( POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - while( *p < end ) - { - buf = &(cur->buf); - buf->tag = **p; - - if( ( ret = asn1_get_tag( p, end, &buf->len, tag ) ) != 0 ) - return( ret ); - - buf->p = *p; - *p += buf->len; - - /* Allocate and assign next pointer */ - if (*p < end) - { - cur->next = (asn1_sequence *) malloc( - sizeof( asn1_sequence ) ); - - if( cur->next == NULL ) - return( POLARSSL_ERR_ASN1_MALLOC_FAILED ); - - cur = cur->next; - } - } - - /* Set final sequence entry's next pointer to NULL */ - cur->next = NULL; - - if( *p != end ) - return( POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); -} - -#endif diff --git a/makerom/polarssl/asn1write.c b/makerom/polarssl/asn1write.c deleted file mode 100644 index e50c17c5..00000000 --- a/makerom/polarssl/asn1write.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * ASN.1 buffer writing functionality - * - * Copyright (C) 2006-2012, 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_ASN1_WRITE_C) - -#include "polarssl/asn1write.h" - -int asn1_write_len( unsigned char **p, unsigned char *start, size_t len ) -{ - if( len < 0x80 ) - { - if( *p - start < 1 ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - *--(*p) = len; - return( 1 ); - } - - if( len <= 0xFF ) - { - if( *p - start < 2 ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - *--(*p) = len; - *--(*p) = 0x81; - return( 2 ); - } - - if( *p - start < 3 ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - // We assume we never have lengths larger than 65535 bytes - // - *--(*p) = len % 256; - *--(*p) = ( len / 256 ) % 256; - *--(*p) = 0x82; - - return( 3 ); -} - -int asn1_write_tag( unsigned char **p, unsigned char *start, unsigned char tag ) -{ - if( *p - start < 1 ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - *--(*p) = tag; - - return( 1 ); -} - -int asn1_write_mpi( unsigned char **p, unsigned char *start, mpi *X ) -{ - int ret; - size_t len = 0; - - // Write the MPI - // - len = mpi_size( X ); - - if( *p - start < (int) len ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - (*p) -= len; - mpi_write_binary( X, *p, len ); - - // DER format assumes 2s complement for numbers, so the leftmost bit - // should be 0 for positive numbers and 1 for negative numbers. - // - if ( X->s ==1 && **p & 0x80 ) - { - if( *p - start < 1 ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - *--(*p) = 0x00; - len += 1; - } - - ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_INTEGER ) ); - - return( len ); -} - -int asn1_write_null( unsigned char **p, unsigned char *start ) -{ - int ret; - size_t len = 0; - - // Write NULL - // - ASN1_CHK_ADD( len, asn1_write_len( p, start, 0) ); - ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_NULL ) ); - - return( len ); -} - -int asn1_write_oid( unsigned char **p, unsigned char *start, char *oid ) -{ - int ret; - size_t len = 0; - - // Write OID - // - len = strlen( oid ); - - if( *p - start < (int) len ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - (*p) -= len; - memcpy( *p, oid, len ); - - ASN1_CHK_ADD( len , asn1_write_len( p, start, len ) ); - ASN1_CHK_ADD( len , asn1_write_tag( p, start, ASN1_OID ) ); - - return( len ); -} - -int asn1_write_algorithm_identifier( unsigned char **p, unsigned char *start, - char *algorithm_oid ) -{ - int ret; - size_t null_len = 0; - size_t oid_len = 0; - size_t len = 0; - - // Write NULL - // - ASN1_CHK_ADD( null_len, asn1_write_null( p, start ) ); - - // Write OID - // - ASN1_CHK_ADD( oid_len, asn1_write_oid( p, start, algorithm_oid ) ); - - len = oid_len + null_len; - ASN1_CHK_ADD( len, asn1_write_len( p, start, oid_len + null_len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( p, start, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ); - - return( len ); -} - -int asn1_write_int( unsigned char **p, unsigned char *start, int val ) -{ - int ret; - size_t len = 0; - - // TODO negative values and values larger than 128 - // DER format assumes 2s complement for numbers, so the leftmost bit - // should be 0 for positive numbers and 1 for negative numbers. - // - if( *p - start < 1 ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - len += 1; - *--(*p) = val; - - if ( val > 0 && **p & 0x80 ) - { - if( *p - start < 1 ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - *--(*p) = 0x00; - len += 1; - } - - ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_INTEGER ) ); - - return( len ); -} - -int asn1_write_printable_string( unsigned char **p, unsigned char *start, - char *text ) -{ - int ret; - size_t len = 0; - - // Write string - // - len = strlen( text ); - - if( *p - start < (int) len ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - (*p) -= len; - memcpy( *p, text, len ); - - ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_PRINTABLE_STRING ) ); - - return( len ); -} - -int asn1_write_ia5_string( unsigned char **p, unsigned char *start, - char *text ) -{ - int ret; - size_t len = 0; - - // Write string - // - len = strlen( text ); - - if( *p - start < (int) len ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - (*p) -= len; - memcpy( *p, text, len ); - - ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_IA5_STRING ) ); - - return( len ); -} - - -#endif diff --git a/makerom/polarssl/blowfish.c b/makerom/polarssl/blowfish.c deleted file mode 100644 index 719aea61..00000000 --- a/makerom/polarssl/blowfish.c +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Blowfish implementation - * - * Copyright (C) 2012-2013, 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 Blowfish block cipher was designed by Bruce Schneier in 1993. - * http://www.schneier.com/blowfish.html - * http://en.wikipedia.org/wiki/Blowfish_%28cipher%29 - * - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_BLOWFISH_C) - -#include "polarssl/blowfish.h" - -#if !defined(POLARSSL_BLOWFISH_ALT) - -/* - * 32-bit integer manipulation macros (big endian) - */ -#ifndef GET_UINT32_BE -#define GET_UINT32_BE(n,b,i) \ -{ \ - (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ - | ( (uint32_t) (b)[(i) + 1] << 16 ) \ - | ( (uint32_t) (b)[(i) + 2] << 8 ) \ - | ( (uint32_t) (b)[(i) + 3] ); \ -} -#endif - -#ifndef PUT_UINT32_BE -#define PUT_UINT32_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 - -static const uint32_t P[BLOWFISH_ROUNDS + 2] = { - 0x243F6A88L, 0x85A308D3L, 0x13198A2EL, 0x03707344L, - 0xA4093822L, 0x299F31D0L, 0x082EFA98L, 0xEC4E6C89L, - 0x452821E6L, 0x38D01377L, 0xBE5466CFL, 0x34E90C6CL, - 0xC0AC29B7L, 0xC97C50DDL, 0x3F84D5B5L, 0xB5470917L, - 0x9216D5D9L, 0x8979FB1BL -}; - -/* declarations of data at the end of this file */ -static const uint32_t S[4][256]; - -static uint32_t F(blowfish_context *ctx, uint32_t x) -{ - unsigned short a, b, c, d; - uint32_t y; - - d = (unsigned short)(x & 0xFF); - x >>= 8; - c = (unsigned short)(x & 0xFF); - x >>= 8; - b = (unsigned short)(x & 0xFF); - x >>= 8; - a = (unsigned short)(x & 0xFF); - y = ctx->S[0][a] + ctx->S[1][b]; - y = y ^ ctx->S[2][c]; - y = y + ctx->S[3][d]; - - return y; -} - -static void blowfish_enc(blowfish_context *ctx, uint32_t *xl, uint32_t *xr) -{ - uint32_t Xl, Xr, temp; - short i; - - Xl = *xl; - Xr = *xr; - - for (i = 0; i < BLOWFISH_ROUNDS; ++i) - { - Xl = Xl ^ ctx->P[i]; - Xr = F(ctx, Xl) ^ Xr; - - temp = Xl; - Xl = Xr; - Xr = temp; - } - - temp = Xl; - Xl = Xr; - Xr = temp; - - Xr = Xr ^ ctx->P[BLOWFISH_ROUNDS]; - Xl = Xl ^ ctx->P[BLOWFISH_ROUNDS + 1]; - - *xl = Xl; - *xr = Xr; -} - -static void blowfish_dec(blowfish_context *ctx, uint32_t *xl, uint32_t *xr) -{ - uint32_t Xl, Xr, temp; - short i; - - Xl = *xl; - Xr = *xr; - - for (i = BLOWFISH_ROUNDS + 1; i > 1; --i) - { - Xl = Xl ^ ctx->P[i]; - Xr = F(ctx, Xl) ^ Xr; - - temp = Xl; - Xl = Xr; - Xr = temp; - } - - temp = Xl; - Xl = Xr; - Xr = temp; - - Xr = Xr ^ ctx->P[1]; - Xl = Xl ^ ctx->P[0]; - - *xl = Xl; - *xr = Xr; -} - -/* - * Blowfish key schedule - */ -int blowfish_setkey( blowfish_context *ctx, const unsigned char *key, unsigned int keysize ) -{ - unsigned int i, j, k; - uint32_t data, datal, datar; - - if( keysize < BLOWFISH_MIN_KEY || keysize > BLOWFISH_MAX_KEY || - ( keysize % 8 ) ) - { - return POLARSSL_ERR_BLOWFISH_INVALID_KEY_LENGTH; - } - - keysize >>= 3; - - for( i = 0; i < 4; i++ ) - { - for( j = 0; j < 256; j++ ) - ctx->S[i][j] = S[i][j]; - } - - j = 0; - for( i = 0; i < BLOWFISH_ROUNDS + 2; ++i ) - { - data = 0x00000000; - for( k = 0; k < 4; ++k ) - { - data = ( data << 8 ) | key[j++]; - if( j >= keysize ) - j = 0; - } - ctx->P[i] = P[i] ^ data; - } - - datal = 0x00000000; - datar = 0x00000000; - - for( i = 0; i < BLOWFISH_ROUNDS + 2; i += 2 ) - { - blowfish_enc( ctx, &datal, &datar ); - ctx->P[i] = datal; - ctx->P[i + 1] = datar; - } - - for( i = 0; i < 4; i++ ) - { - for( j = 0; j < 256; j += 2 ) - { - blowfish_enc( ctx, &datal, &datar ); - ctx->S[i][j] = datal; - ctx->S[i][j + 1] = datar; - } - } - return( 0 ); -} - -/* - * Blowfish-ECB block encryption/decryption - */ -int blowfish_crypt_ecb( blowfish_context *ctx, - int mode, - const unsigned char input[BLOWFISH_BLOCKSIZE], - unsigned char output[BLOWFISH_BLOCKSIZE] ) -{ - uint32_t X0, X1; - - GET_UINT32_BE( X0, input, 0 ); - GET_UINT32_BE( X1, input, 4 ); - - if( mode == BLOWFISH_DECRYPT ) - { - blowfish_dec(ctx, &X0, &X1); - } - else /* BLOWFISH_ENCRYPT */ - { - blowfish_enc(ctx, &X0, &X1); - } - - PUT_UINT32_BE( X0, output, 0 ); - PUT_UINT32_BE( X1, output, 4 ); - - return( 0 ); -} - -/* - * Blowfish-CBC buffer encryption/decryption - */ -int blowfish_crypt_cbc( blowfish_context *ctx, - int mode, - size_t length, - unsigned char iv[BLOWFISH_BLOCKSIZE], - const unsigned char *input, - unsigned char *output ) -{ - int i; - unsigned char temp[BLOWFISH_BLOCKSIZE]; - - if( length % BLOWFISH_BLOCKSIZE ) - return( POLARSSL_ERR_BLOWFISH_INVALID_INPUT_LENGTH ); - - if( mode == BLOWFISH_DECRYPT ) - { - while( length > 0 ) - { - memcpy( temp, input, BLOWFISH_BLOCKSIZE ); - blowfish_crypt_ecb( ctx, mode, input, output ); - - for( i = 0; i < BLOWFISH_BLOCKSIZE;i++ ) - output[i] = (unsigned char)( output[i] ^ iv[i] ); - - memcpy( iv, temp, BLOWFISH_BLOCKSIZE ); - - input += BLOWFISH_BLOCKSIZE; - output += BLOWFISH_BLOCKSIZE; - length -= BLOWFISH_BLOCKSIZE; - } - } - else - { - while( length > 0 ) - { - for( i = 0; i < BLOWFISH_BLOCKSIZE; i++ ) - output[i] = (unsigned char)( input[i] ^ iv[i] ); - - blowfish_crypt_ecb( ctx, mode, output, output ); - memcpy( iv, output, BLOWFISH_BLOCKSIZE ); - - input += BLOWFISH_BLOCKSIZE; - output += BLOWFISH_BLOCKSIZE; - length -= BLOWFISH_BLOCKSIZE; - } - } - - return( 0 ); -} - -#if defined(POLARSSL_CIPHER_MODE_CFB) -/* - * Blowfish CFB buffer encryption/decryption - */ -int blowfish_crypt_cfb64( blowfish_context *ctx, - int mode, - size_t length, - size_t *iv_off, - unsigned char iv[BLOWFISH_BLOCKSIZE], - const unsigned char *input, - unsigned char *output ) -{ - int c; - size_t n = *iv_off; - - if( mode == BLOWFISH_DECRYPT ) - { - while( length-- ) - { - if( n == 0 ) - blowfish_crypt_ecb( ctx, BLOWFISH_ENCRYPT, iv, iv ); - - c = *input++; - *output++ = (unsigned char)( c ^ iv[n] ); - iv[n] = (unsigned char) c; - - n = (n + 1) % BLOWFISH_BLOCKSIZE; - } - } - else - { - while( length-- ) - { - if( n == 0 ) - blowfish_crypt_ecb( ctx, BLOWFISH_ENCRYPT, iv, iv ); - - iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); - - n = (n + 1) % BLOWFISH_BLOCKSIZE; - } - } - - *iv_off = n; - - return( 0 ); -} -#endif /*POLARSSL_CIPHER_MODE_CFB */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) -/* - * Blowfish CTR buffer encryption/decryption - */ -int blowfish_crypt_ctr( blowfish_context *ctx, - size_t length, - size_t *nc_off, - unsigned char nonce_counter[BLOWFISH_BLOCKSIZE], - unsigned char stream_block[BLOWFISH_BLOCKSIZE], - const unsigned char *input, - unsigned char *output ) -{ - int c, i; - size_t n = *nc_off; - - while( length-- ) - { - if( n == 0 ) { - blowfish_crypt_ecb( ctx, BLOWFISH_ENCRYPT, nonce_counter, stream_block ); - - for( i = BLOWFISH_BLOCKSIZE; i > 0; i-- ) - if( ++nonce_counter[i - 1] != 0 ) - break; - } - c = *input++; - *output++ = (unsigned char)( c ^ stream_block[n] ); - - n = (n + 1) % BLOWFISH_BLOCKSIZE; - } - - *nc_off = n; - - return( 0 ); -} -#endif /* POLARSSL_CIPHER_MODE_CTR */ - -static const uint32_t S[4][256] = { - { 0xD1310BA6L, 0x98DFB5ACL, 0x2FFD72DBL, 0xD01ADFB7L, - 0xB8E1AFEDL, 0x6A267E96L, 0xBA7C9045L, 0xF12C7F99L, - 0x24A19947L, 0xB3916CF7L, 0x0801F2E2L, 0x858EFC16L, - 0x636920D8L, 0x71574E69L, 0xA458FEA3L, 0xF4933D7EL, - 0x0D95748FL, 0x728EB658L, 0x718BCD58L, 0x82154AEEL, - 0x7B54A41DL, 0xC25A59B5L, 0x9C30D539L, 0x2AF26013L, - 0xC5D1B023L, 0x286085F0L, 0xCA417918L, 0xB8DB38EFL, - 0x8E79DCB0L, 0x603A180EL, 0x6C9E0E8BL, 0xB01E8A3EL, - 0xD71577C1L, 0xBD314B27L, 0x78AF2FDAL, 0x55605C60L, - 0xE65525F3L, 0xAA55AB94L, 0x57489862L, 0x63E81440L, - 0x55CA396AL, 0x2AAB10B6L, 0xB4CC5C34L, 0x1141E8CEL, - 0xA15486AFL, 0x7C72E993L, 0xB3EE1411L, 0x636FBC2AL, - 0x2BA9C55DL, 0x741831F6L, 0xCE5C3E16L, 0x9B87931EL, - 0xAFD6BA33L, 0x6C24CF5CL, 0x7A325381L, 0x28958677L, - 0x3B8F4898L, 0x6B4BB9AFL, 0xC4BFE81BL, 0x66282193L, - 0x61D809CCL, 0xFB21A991L, 0x487CAC60L, 0x5DEC8032L, - 0xEF845D5DL, 0xE98575B1L, 0xDC262302L, 0xEB651B88L, - 0x23893E81L, 0xD396ACC5L, 0x0F6D6FF3L, 0x83F44239L, - 0x2E0B4482L, 0xA4842004L, 0x69C8F04AL, 0x9E1F9B5EL, - 0x21C66842L, 0xF6E96C9AL, 0x670C9C61L, 0xABD388F0L, - 0x6A51A0D2L, 0xD8542F68L, 0x960FA728L, 0xAB5133A3L, - 0x6EEF0B6CL, 0x137A3BE4L, 0xBA3BF050L, 0x7EFB2A98L, - 0xA1F1651DL, 0x39AF0176L, 0x66CA593EL, 0x82430E88L, - 0x8CEE8619L, 0x456F9FB4L, 0x7D84A5C3L, 0x3B8B5EBEL, - 0xE06F75D8L, 0x85C12073L, 0x401A449FL, 0x56C16AA6L, - 0x4ED3AA62L, 0x363F7706L, 0x1BFEDF72L, 0x429B023DL, - 0x37D0D724L, 0xD00A1248L, 0xDB0FEAD3L, 0x49F1C09BL, - 0x075372C9L, 0x80991B7BL, 0x25D479D8L, 0xF6E8DEF7L, - 0xE3FE501AL, 0xB6794C3BL, 0x976CE0BDL, 0x04C006BAL, - 0xC1A94FB6L, 0x409F60C4L, 0x5E5C9EC2L, 0x196A2463L, - 0x68FB6FAFL, 0x3E6C53B5L, 0x1339B2EBL, 0x3B52EC6FL, - 0x6DFC511FL, 0x9B30952CL, 0xCC814544L, 0xAF5EBD09L, - 0xBEE3D004L, 0xDE334AFDL, 0x660F2807L, 0x192E4BB3L, - 0xC0CBA857L, 0x45C8740FL, 0xD20B5F39L, 0xB9D3FBDBL, - 0x5579C0BDL, 0x1A60320AL, 0xD6A100C6L, 0x402C7279L, - 0x679F25FEL, 0xFB1FA3CCL, 0x8EA5E9F8L, 0xDB3222F8L, - 0x3C7516DFL, 0xFD616B15L, 0x2F501EC8L, 0xAD0552ABL, - 0x323DB5FAL, 0xFD238760L, 0x53317B48L, 0x3E00DF82L, - 0x9E5C57BBL, 0xCA6F8CA0L, 0x1A87562EL, 0xDF1769DBL, - 0xD542A8F6L, 0x287EFFC3L, 0xAC6732C6L, 0x8C4F5573L, - 0x695B27B0L, 0xBBCA58C8L, 0xE1FFA35DL, 0xB8F011A0L, - 0x10FA3D98L, 0xFD2183B8L, 0x4AFCB56CL, 0x2DD1D35BL, - 0x9A53E479L, 0xB6F84565L, 0xD28E49BCL, 0x4BFB9790L, - 0xE1DDF2DAL, 0xA4CB7E33L, 0x62FB1341L, 0xCEE4C6E8L, - 0xEF20CADAL, 0x36774C01L, 0xD07E9EFEL, 0x2BF11FB4L, - 0x95DBDA4DL, 0xAE909198L, 0xEAAD8E71L, 0x6B93D5A0L, - 0xD08ED1D0L, 0xAFC725E0L, 0x8E3C5B2FL, 0x8E7594B7L, - 0x8FF6E2FBL, 0xF2122B64L, 0x8888B812L, 0x900DF01CL, - 0x4FAD5EA0L, 0x688FC31CL, 0xD1CFF191L, 0xB3A8C1ADL, - 0x2F2F2218L, 0xBE0E1777L, 0xEA752DFEL, 0x8B021FA1L, - 0xE5A0CC0FL, 0xB56F74E8L, 0x18ACF3D6L, 0xCE89E299L, - 0xB4A84FE0L, 0xFD13E0B7L, 0x7CC43B81L, 0xD2ADA8D9L, - 0x165FA266L, 0x80957705L, 0x93CC7314L, 0x211A1477L, - 0xE6AD2065L, 0x77B5FA86L, 0xC75442F5L, 0xFB9D35CFL, - 0xEBCDAF0CL, 0x7B3E89A0L, 0xD6411BD3L, 0xAE1E7E49L, - 0x00250E2DL, 0x2071B35EL, 0x226800BBL, 0x57B8E0AFL, - 0x2464369BL, 0xF009B91EL, 0x5563911DL, 0x59DFA6AAL, - 0x78C14389L, 0xD95A537FL, 0x207D5BA2L, 0x02E5B9C5L, - 0x83260376L, 0x6295CFA9L, 0x11C81968L, 0x4E734A41L, - 0xB3472DCAL, 0x7B14A94AL, 0x1B510052L, 0x9A532915L, - 0xD60F573FL, 0xBC9BC6E4L, 0x2B60A476L, 0x81E67400L, - 0x08BA6FB5L, 0x571BE91FL, 0xF296EC6BL, 0x2A0DD915L, - 0xB6636521L, 0xE7B9F9B6L, 0xFF34052EL, 0xC5855664L, - 0x53B02D5DL, 0xA99F8FA1L, 0x08BA4799L, 0x6E85076AL }, - { 0x4B7A70E9L, 0xB5B32944L, 0xDB75092EL, 0xC4192623L, - 0xAD6EA6B0L, 0x49A7DF7DL, 0x9CEE60B8L, 0x8FEDB266L, - 0xECAA8C71L, 0x699A17FFL, 0x5664526CL, 0xC2B19EE1L, - 0x193602A5L, 0x75094C29L, 0xA0591340L, 0xE4183A3EL, - 0x3F54989AL, 0x5B429D65L, 0x6B8FE4D6L, 0x99F73FD6L, - 0xA1D29C07L, 0xEFE830F5L, 0x4D2D38E6L, 0xF0255DC1L, - 0x4CDD2086L, 0x8470EB26L, 0x6382E9C6L, 0x021ECC5EL, - 0x09686B3FL, 0x3EBAEFC9L, 0x3C971814L, 0x6B6A70A1L, - 0x687F3584L, 0x52A0E286L, 0xB79C5305L, 0xAA500737L, - 0x3E07841CL, 0x7FDEAE5CL, 0x8E7D44ECL, 0x5716F2B8L, - 0xB03ADA37L, 0xF0500C0DL, 0xF01C1F04L, 0x0200B3FFL, - 0xAE0CF51AL, 0x3CB574B2L, 0x25837A58L, 0xDC0921BDL, - 0xD19113F9L, 0x7CA92FF6L, 0x94324773L, 0x22F54701L, - 0x3AE5E581L, 0x37C2DADCL, 0xC8B57634L, 0x9AF3DDA7L, - 0xA9446146L, 0x0FD0030EL, 0xECC8C73EL, 0xA4751E41L, - 0xE238CD99L, 0x3BEA0E2FL, 0x3280BBA1L, 0x183EB331L, - 0x4E548B38L, 0x4F6DB908L, 0x6F420D03L, 0xF60A04BFL, - 0x2CB81290L, 0x24977C79L, 0x5679B072L, 0xBCAF89AFL, - 0xDE9A771FL, 0xD9930810L, 0xB38BAE12L, 0xDCCF3F2EL, - 0x5512721FL, 0x2E6B7124L, 0x501ADDE6L, 0x9F84CD87L, - 0x7A584718L, 0x7408DA17L, 0xBC9F9ABCL, 0xE94B7D8CL, - 0xEC7AEC3AL, 0xDB851DFAL, 0x63094366L, 0xC464C3D2L, - 0xEF1C1847L, 0x3215D908L, 0xDD433B37L, 0x24C2BA16L, - 0x12A14D43L, 0x2A65C451L, 0x50940002L, 0x133AE4DDL, - 0x71DFF89EL, 0x10314E55L, 0x81AC77D6L, 0x5F11199BL, - 0x043556F1L, 0xD7A3C76BL, 0x3C11183BL, 0x5924A509L, - 0xF28FE6EDL, 0x97F1FBFAL, 0x9EBABF2CL, 0x1E153C6EL, - 0x86E34570L, 0xEAE96FB1L, 0x860E5E0AL, 0x5A3E2AB3L, - 0x771FE71CL, 0x4E3D06FAL, 0x2965DCB9L, 0x99E71D0FL, - 0x803E89D6L, 0x5266C825L, 0x2E4CC978L, 0x9C10B36AL, - 0xC6150EBAL, 0x94E2EA78L, 0xA5FC3C53L, 0x1E0A2DF4L, - 0xF2F74EA7L, 0x361D2B3DL, 0x1939260FL, 0x19C27960L, - 0x5223A708L, 0xF71312B6L, 0xEBADFE6EL, 0xEAC31F66L, - 0xE3BC4595L, 0xA67BC883L, 0xB17F37D1L, 0x018CFF28L, - 0xC332DDEFL, 0xBE6C5AA5L, 0x65582185L, 0x68AB9802L, - 0xEECEA50FL, 0xDB2F953BL, 0x2AEF7DADL, 0x5B6E2F84L, - 0x1521B628L, 0x29076170L, 0xECDD4775L, 0x619F1510L, - 0x13CCA830L, 0xEB61BD96L, 0x0334FE1EL, 0xAA0363CFL, - 0xB5735C90L, 0x4C70A239L, 0xD59E9E0BL, 0xCBAADE14L, - 0xEECC86BCL, 0x60622CA7L, 0x9CAB5CABL, 0xB2F3846EL, - 0x648B1EAFL, 0x19BDF0CAL, 0xA02369B9L, 0x655ABB50L, - 0x40685A32L, 0x3C2AB4B3L, 0x319EE9D5L, 0xC021B8F7L, - 0x9B540B19L, 0x875FA099L, 0x95F7997EL, 0x623D7DA8L, - 0xF837889AL, 0x97E32D77L, 0x11ED935FL, 0x16681281L, - 0x0E358829L, 0xC7E61FD6L, 0x96DEDFA1L, 0x7858BA99L, - 0x57F584A5L, 0x1B227263L, 0x9B83C3FFL, 0x1AC24696L, - 0xCDB30AEBL, 0x532E3054L, 0x8FD948E4L, 0x6DBC3128L, - 0x58EBF2EFL, 0x34C6FFEAL, 0xFE28ED61L, 0xEE7C3C73L, - 0x5D4A14D9L, 0xE864B7E3L, 0x42105D14L, 0x203E13E0L, - 0x45EEE2B6L, 0xA3AAABEAL, 0xDB6C4F15L, 0xFACB4FD0L, - 0xC742F442L, 0xEF6ABBB5L, 0x654F3B1DL, 0x41CD2105L, - 0xD81E799EL, 0x86854DC7L, 0xE44B476AL, 0x3D816250L, - 0xCF62A1F2L, 0x5B8D2646L, 0xFC8883A0L, 0xC1C7B6A3L, - 0x7F1524C3L, 0x69CB7492L, 0x47848A0BL, 0x5692B285L, - 0x095BBF00L, 0xAD19489DL, 0x1462B174L, 0x23820E00L, - 0x58428D2AL, 0x0C55F5EAL, 0x1DADF43EL, 0x233F7061L, - 0x3372F092L, 0x8D937E41L, 0xD65FECF1L, 0x6C223BDBL, - 0x7CDE3759L, 0xCBEE7460L, 0x4085F2A7L, 0xCE77326EL, - 0xA6078084L, 0x19F8509EL, 0xE8EFD855L, 0x61D99735L, - 0xA969A7AAL, 0xC50C06C2L, 0x5A04ABFCL, 0x800BCADCL, - 0x9E447A2EL, 0xC3453484L, 0xFDD56705L, 0x0E1E9EC9L, - 0xDB73DBD3L, 0x105588CDL, 0x675FDA79L, 0xE3674340L, - 0xC5C43465L, 0x713E38D8L, 0x3D28F89EL, 0xF16DFF20L, - 0x153E21E7L, 0x8FB03D4AL, 0xE6E39F2BL, 0xDB83ADF7L }, - { 0xE93D5A68L, 0x948140F7L, 0xF64C261CL, 0x94692934L, - 0x411520F7L, 0x7602D4F7L, 0xBCF46B2EL, 0xD4A20068L, - 0xD4082471L, 0x3320F46AL, 0x43B7D4B7L, 0x500061AFL, - 0x1E39F62EL, 0x97244546L, 0x14214F74L, 0xBF8B8840L, - 0x4D95FC1DL, 0x96B591AFL, 0x70F4DDD3L, 0x66A02F45L, - 0xBFBC09ECL, 0x03BD9785L, 0x7FAC6DD0L, 0x31CB8504L, - 0x96EB27B3L, 0x55FD3941L, 0xDA2547E6L, 0xABCA0A9AL, - 0x28507825L, 0x530429F4L, 0x0A2C86DAL, 0xE9B66DFBL, - 0x68DC1462L, 0xD7486900L, 0x680EC0A4L, 0x27A18DEEL, - 0x4F3FFEA2L, 0xE887AD8CL, 0xB58CE006L, 0x7AF4D6B6L, - 0xAACE1E7CL, 0xD3375FECL, 0xCE78A399L, 0x406B2A42L, - 0x20FE9E35L, 0xD9F385B9L, 0xEE39D7ABL, 0x3B124E8BL, - 0x1DC9FAF7L, 0x4B6D1856L, 0x26A36631L, 0xEAE397B2L, - 0x3A6EFA74L, 0xDD5B4332L, 0x6841E7F7L, 0xCA7820FBL, - 0xFB0AF54EL, 0xD8FEB397L, 0x454056ACL, 0xBA489527L, - 0x55533A3AL, 0x20838D87L, 0xFE6BA9B7L, 0xD096954BL, - 0x55A867BCL, 0xA1159A58L, 0xCCA92963L, 0x99E1DB33L, - 0xA62A4A56L, 0x3F3125F9L, 0x5EF47E1CL, 0x9029317CL, - 0xFDF8E802L, 0x04272F70L, 0x80BB155CL, 0x05282CE3L, - 0x95C11548L, 0xE4C66D22L, 0x48C1133FL, 0xC70F86DCL, - 0x07F9C9EEL, 0x41041F0FL, 0x404779A4L, 0x5D886E17L, - 0x325F51EBL, 0xD59BC0D1L, 0xF2BCC18FL, 0x41113564L, - 0x257B7834L, 0x602A9C60L, 0xDFF8E8A3L, 0x1F636C1BL, - 0x0E12B4C2L, 0x02E1329EL, 0xAF664FD1L, 0xCAD18115L, - 0x6B2395E0L, 0x333E92E1L, 0x3B240B62L, 0xEEBEB922L, - 0x85B2A20EL, 0xE6BA0D99L, 0xDE720C8CL, 0x2DA2F728L, - 0xD0127845L, 0x95B794FDL, 0x647D0862L, 0xE7CCF5F0L, - 0x5449A36FL, 0x877D48FAL, 0xC39DFD27L, 0xF33E8D1EL, - 0x0A476341L, 0x992EFF74L, 0x3A6F6EABL, 0xF4F8FD37L, - 0xA812DC60L, 0xA1EBDDF8L, 0x991BE14CL, 0xDB6E6B0DL, - 0xC67B5510L, 0x6D672C37L, 0x2765D43BL, 0xDCD0E804L, - 0xF1290DC7L, 0xCC00FFA3L, 0xB5390F92L, 0x690FED0BL, - 0x667B9FFBL, 0xCEDB7D9CL, 0xA091CF0BL, 0xD9155EA3L, - 0xBB132F88L, 0x515BAD24L, 0x7B9479BFL, 0x763BD6EBL, - 0x37392EB3L, 0xCC115979L, 0x8026E297L, 0xF42E312DL, - 0x6842ADA7L, 0xC66A2B3BL, 0x12754CCCL, 0x782EF11CL, - 0x6A124237L, 0xB79251E7L, 0x06A1BBE6L, 0x4BFB6350L, - 0x1A6B1018L, 0x11CAEDFAL, 0x3D25BDD8L, 0xE2E1C3C9L, - 0x44421659L, 0x0A121386L, 0xD90CEC6EL, 0xD5ABEA2AL, - 0x64AF674EL, 0xDA86A85FL, 0xBEBFE988L, 0x64E4C3FEL, - 0x9DBC8057L, 0xF0F7C086L, 0x60787BF8L, 0x6003604DL, - 0xD1FD8346L, 0xF6381FB0L, 0x7745AE04L, 0xD736FCCCL, - 0x83426B33L, 0xF01EAB71L, 0xB0804187L, 0x3C005E5FL, - 0x77A057BEL, 0xBDE8AE24L, 0x55464299L, 0xBF582E61L, - 0x4E58F48FL, 0xF2DDFDA2L, 0xF474EF38L, 0x8789BDC2L, - 0x5366F9C3L, 0xC8B38E74L, 0xB475F255L, 0x46FCD9B9L, - 0x7AEB2661L, 0x8B1DDF84L, 0x846A0E79L, 0x915F95E2L, - 0x466E598EL, 0x20B45770L, 0x8CD55591L, 0xC902DE4CL, - 0xB90BACE1L, 0xBB8205D0L, 0x11A86248L, 0x7574A99EL, - 0xB77F19B6L, 0xE0A9DC09L, 0x662D09A1L, 0xC4324633L, - 0xE85A1F02L, 0x09F0BE8CL, 0x4A99A025L, 0x1D6EFE10L, - 0x1AB93D1DL, 0x0BA5A4DFL, 0xA186F20FL, 0x2868F169L, - 0xDCB7DA83L, 0x573906FEL, 0xA1E2CE9BL, 0x4FCD7F52L, - 0x50115E01L, 0xA70683FAL, 0xA002B5C4L, 0x0DE6D027L, - 0x9AF88C27L, 0x773F8641L, 0xC3604C06L, 0x61A806B5L, - 0xF0177A28L, 0xC0F586E0L, 0x006058AAL, 0x30DC7D62L, - 0x11E69ED7L, 0x2338EA63L, 0x53C2DD94L, 0xC2C21634L, - 0xBBCBEE56L, 0x90BCB6DEL, 0xEBFC7DA1L, 0xCE591D76L, - 0x6F05E409L, 0x4B7C0188L, 0x39720A3DL, 0x7C927C24L, - 0x86E3725FL, 0x724D9DB9L, 0x1AC15BB4L, 0xD39EB8FCL, - 0xED545578L, 0x08FCA5B5L, 0xD83D7CD3L, 0x4DAD0FC4L, - 0x1E50EF5EL, 0xB161E6F8L, 0xA28514D9L, 0x6C51133CL, - 0x6FD5C7E7L, 0x56E14EC4L, 0x362ABFCEL, 0xDDC6C837L, - 0xD79A3234L, 0x92638212L, 0x670EFA8EL, 0x406000E0L }, - { 0x3A39CE37L, 0xD3FAF5CFL, 0xABC27737L, 0x5AC52D1BL, - 0x5CB0679EL, 0x4FA33742L, 0xD3822740L, 0x99BC9BBEL, - 0xD5118E9DL, 0xBF0F7315L, 0xD62D1C7EL, 0xC700C47BL, - 0xB78C1B6BL, 0x21A19045L, 0xB26EB1BEL, 0x6A366EB4L, - 0x5748AB2FL, 0xBC946E79L, 0xC6A376D2L, 0x6549C2C8L, - 0x530FF8EEL, 0x468DDE7DL, 0xD5730A1DL, 0x4CD04DC6L, - 0x2939BBDBL, 0xA9BA4650L, 0xAC9526E8L, 0xBE5EE304L, - 0xA1FAD5F0L, 0x6A2D519AL, 0x63EF8CE2L, 0x9A86EE22L, - 0xC089C2B8L, 0x43242EF6L, 0xA51E03AAL, 0x9CF2D0A4L, - 0x83C061BAL, 0x9BE96A4DL, 0x8FE51550L, 0xBA645BD6L, - 0x2826A2F9L, 0xA73A3AE1L, 0x4BA99586L, 0xEF5562E9L, - 0xC72FEFD3L, 0xF752F7DAL, 0x3F046F69L, 0x77FA0A59L, - 0x80E4A915L, 0x87B08601L, 0x9B09E6ADL, 0x3B3EE593L, - 0xE990FD5AL, 0x9E34D797L, 0x2CF0B7D9L, 0x022B8B51L, - 0x96D5AC3AL, 0x017DA67DL, 0xD1CF3ED6L, 0x7C7D2D28L, - 0x1F9F25CFL, 0xADF2B89BL, 0x5AD6B472L, 0x5A88F54CL, - 0xE029AC71L, 0xE019A5E6L, 0x47B0ACFDL, 0xED93FA9BL, - 0xE8D3C48DL, 0x283B57CCL, 0xF8D56629L, 0x79132E28L, - 0x785F0191L, 0xED756055L, 0xF7960E44L, 0xE3D35E8CL, - 0x15056DD4L, 0x88F46DBAL, 0x03A16125L, 0x0564F0BDL, - 0xC3EB9E15L, 0x3C9057A2L, 0x97271AECL, 0xA93A072AL, - 0x1B3F6D9BL, 0x1E6321F5L, 0xF59C66FBL, 0x26DCF319L, - 0x7533D928L, 0xB155FDF5L, 0x03563482L, 0x8ABA3CBBL, - 0x28517711L, 0xC20AD9F8L, 0xABCC5167L, 0xCCAD925FL, - 0x4DE81751L, 0x3830DC8EL, 0x379D5862L, 0x9320F991L, - 0xEA7A90C2L, 0xFB3E7BCEL, 0x5121CE64L, 0x774FBE32L, - 0xA8B6E37EL, 0xC3293D46L, 0x48DE5369L, 0x6413E680L, - 0xA2AE0810L, 0xDD6DB224L, 0x69852DFDL, 0x09072166L, - 0xB39A460AL, 0x6445C0DDL, 0x586CDECFL, 0x1C20C8AEL, - 0x5BBEF7DDL, 0x1B588D40L, 0xCCD2017FL, 0x6BB4E3BBL, - 0xDDA26A7EL, 0x3A59FF45L, 0x3E350A44L, 0xBCB4CDD5L, - 0x72EACEA8L, 0xFA6484BBL, 0x8D6612AEL, 0xBF3C6F47L, - 0xD29BE463L, 0x542F5D9EL, 0xAEC2771BL, 0xF64E6370L, - 0x740E0D8DL, 0xE75B1357L, 0xF8721671L, 0xAF537D5DL, - 0x4040CB08L, 0x4EB4E2CCL, 0x34D2466AL, 0x0115AF84L, - 0xE1B00428L, 0x95983A1DL, 0x06B89FB4L, 0xCE6EA048L, - 0x6F3F3B82L, 0x3520AB82L, 0x011A1D4BL, 0x277227F8L, - 0x611560B1L, 0xE7933FDCL, 0xBB3A792BL, 0x344525BDL, - 0xA08839E1L, 0x51CE794BL, 0x2F32C9B7L, 0xA01FBAC9L, - 0xE01CC87EL, 0xBCC7D1F6L, 0xCF0111C3L, 0xA1E8AAC7L, - 0x1A908749L, 0xD44FBD9AL, 0xD0DADECBL, 0xD50ADA38L, - 0x0339C32AL, 0xC6913667L, 0x8DF9317CL, 0xE0B12B4FL, - 0xF79E59B7L, 0x43F5BB3AL, 0xF2D519FFL, 0x27D9459CL, - 0xBF97222CL, 0x15E6FC2AL, 0x0F91FC71L, 0x9B941525L, - 0xFAE59361L, 0xCEB69CEBL, 0xC2A86459L, 0x12BAA8D1L, - 0xB6C1075EL, 0xE3056A0CL, 0x10D25065L, 0xCB03A442L, - 0xE0EC6E0EL, 0x1698DB3BL, 0x4C98A0BEL, 0x3278E964L, - 0x9F1F9532L, 0xE0D392DFL, 0xD3A0342BL, 0x8971F21EL, - 0x1B0A7441L, 0x4BA3348CL, 0xC5BE7120L, 0xC37632D8L, - 0xDF359F8DL, 0x9B992F2EL, 0xE60B6F47L, 0x0FE3F11DL, - 0xE54CDA54L, 0x1EDAD891L, 0xCE6279CFL, 0xCD3E7E6FL, - 0x1618B166L, 0xFD2C1D05L, 0x848FD2C5L, 0xF6FB2299L, - 0xF523F357L, 0xA6327623L, 0x93A83531L, 0x56CCCD02L, - 0xACF08162L, 0x5A75EBB5L, 0x6E163697L, 0x88D273CCL, - 0xDE966292L, 0x81B949D0L, 0x4C50901BL, 0x71C65614L, - 0xE6C6C7BDL, 0x327A140AL, 0x45E1D006L, 0xC3F27B9AL, - 0xC9AA53FDL, 0x62A80F00L, 0xBB25BFE2L, 0x35BDD2F6L, - 0x71126905L, 0xB2040222L, 0xB6CBCF7CL, 0xCD769C2BL, - 0x53113EC0L, 0x1640E3D3L, 0x38ABBD60L, 0x2547ADF0L, - 0xBA38209CL, 0xF746CE76L, 0x77AFA1C5L, 0x20756060L, - 0x85CBFE4EL, 0x8AE88DD8L, 0x7AAAF9B0L, 0x4CF9AA7EL, - 0x1948C25CL, 0x02FB8A8CL, 0x01C36AE4L, 0xD6EBE1F9L, - 0x90D4F869L, 0xA65CDEA0L, 0x3F09252DL, 0xC208E69FL, - 0xB74E6132L, 0xCE77E25BL, 0x578FDFE3L, 0x3AC372E6L } -}; - -#endif /* !POLARSSL_BLOWFISH_ALT */ -#endif /* POLARSSL_BLOWFISH_C */ diff --git a/makerom/polarssl/camellia.c b/makerom/polarssl/camellia.c deleted file mode 100644 index bb878750..00000000 --- a/makerom/polarssl/camellia.c +++ /dev/null @@ -1,1035 +0,0 @@ -/* - * Camellia implementation - * - * Copyright (C) 2006-2013, 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 Camellia block cipher was designed by NTT and Mitsubishi Electric - * Corporation. - * - * http://info.isl.ntt.co.jp/crypt/eng/camellia/dl/01espec.pdf - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_CAMELLIA_C) - -#include "polarssl/camellia.h" - -#if !defined(POLARSSL_CAMELLIA_ALT) - -/* - * 32-bit integer manipulation macros (big endian) - */ -#ifndef GET_UINT32_BE -#define GET_UINT32_BE(n,b,i) \ -{ \ - (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ - | ( (uint32_t) (b)[(i) + 1] << 16 ) \ - | ( (uint32_t) (b)[(i) + 2] << 8 ) \ - | ( (uint32_t) (b)[(i) + 3] ); \ -} -#endif - -#ifndef PUT_UINT32_BE -#define PUT_UINT32_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 - -static const unsigned char SIGMA_CHARS[6][8] = -{ - { 0xa0, 0x9e, 0x66, 0x7f, 0x3b, 0xcc, 0x90, 0x8b }, - { 0xb6, 0x7a, 0xe8, 0x58, 0x4c, 0xaa, 0x73, 0xb2 }, - { 0xc6, 0xef, 0x37, 0x2f, 0xe9, 0x4f, 0x82, 0xbe }, - { 0x54, 0xff, 0x53, 0xa5, 0xf1, 0xd3, 0x6f, 0x1c }, - { 0x10, 0xe5, 0x27, 0xfa, 0xde, 0x68, 0x2d, 0x1d }, - { 0xb0, 0x56, 0x88, 0xc2, 0xb3, 0xe6, 0xc1, 0xfd } -}; - -#if defined(POLARSSL_CAMELLIA_SMALL_MEMORY) - -static const unsigned char FSb[256] = -{ - 112,130, 44,236,179, 39,192,229,228,133, 87, 53,234, 12,174, 65, - 35,239,107,147, 69, 25,165, 33,237, 14, 79, 78, 29,101,146,189, - 134,184,175,143,124,235, 31,206, 62, 48,220, 95, 94,197, 11, 26, - 166,225, 57,202,213, 71, 93, 61,217, 1, 90,214, 81, 86,108, 77, - 139, 13,154,102,251,204,176, 45,116, 18, 43, 32,240,177,132,153, - 223, 76,203,194, 52,126,118, 5,109,183,169, 49,209, 23, 4,215, - 20, 88, 58, 97,222, 27, 17, 28, 50, 15,156, 22, 83, 24,242, 34, - 254, 68,207,178,195,181,122,145, 36, 8,232,168, 96,252,105, 80, - 170,208,160,125,161,137, 98,151, 84, 91, 30,149,224,255,100,210, - 16,196, 0, 72,163,247,117,219,138, 3,230,218, 9, 63,221,148, - 135, 92,131, 2,205, 74,144, 51,115,103,246,243,157,127,191,226, - 82,155,216, 38,200, 55,198, 59,129,150,111, 75, 19,190, 99, 46, - 233,121,167,140,159,110,188,142, 41,245,249,182, 47,253,180, 89, - 120,152, 6,106,231, 70,113,186,212, 37,171, 66,136,162,141,250, - 114, 7,185, 85,248,238,172, 10, 54, 73, 42,104, 60, 56,241,164, - 64, 40,211,123,187,201, 67,193, 21,227,173,244,119,199,128,158 -}; - -#define SBOX1(n) FSb[(n)] -#define SBOX2(n) (unsigned char)((FSb[(n)] >> 7 ^ FSb[(n)] << 1) & 0xff) -#define SBOX3(n) (unsigned char)((FSb[(n)] >> 1 ^ FSb[(n)] << 7) & 0xff) -#define SBOX4(n) FSb[((n) << 1 ^ (n) >> 7) &0xff] - -#else - -static const unsigned char FSb[256] = -{ - 112, 130, 44, 236, 179, 39, 192, 229, 228, 133, 87, 53, 234, 12, 174, 65, - 35, 239, 107, 147, 69, 25, 165, 33, 237, 14, 79, 78, 29, 101, 146, 189, - 134, 184, 175, 143, 124, 235, 31, 206, 62, 48, 220, 95, 94, 197, 11, 26, - 166, 225, 57, 202, 213, 71, 93, 61, 217, 1, 90, 214, 81, 86, 108, 77, - 139, 13, 154, 102, 251, 204, 176, 45, 116, 18, 43, 32, 240, 177, 132, 153, - 223, 76, 203, 194, 52, 126, 118, 5, 109, 183, 169, 49, 209, 23, 4, 215, - 20, 88, 58, 97, 222, 27, 17, 28, 50, 15, 156, 22, 83, 24, 242, 34, - 254, 68, 207, 178, 195, 181, 122, 145, 36, 8, 232, 168, 96, 252, 105, 80, - 170, 208, 160, 125, 161, 137, 98, 151, 84, 91, 30, 149, 224, 255, 100, 210, - 16, 196, 0, 72, 163, 247, 117, 219, 138, 3, 230, 218, 9, 63, 221, 148, - 135, 92, 131, 2, 205, 74, 144, 51, 115, 103, 246, 243, 157, 127, 191, 226, - 82, 155, 216, 38, 200, 55, 198, 59, 129, 150, 111, 75, 19, 190, 99, 46, - 233, 121, 167, 140, 159, 110, 188, 142, 41, 245, 249, 182, 47, 253, 180, 89, - 120, 152, 6, 106, 231, 70, 113, 186, 212, 37, 171, 66, 136, 162, 141, 250, - 114, 7, 185, 85, 248, 238, 172, 10, 54, 73, 42, 104, 60, 56, 241, 164, - 64, 40, 211, 123, 187, 201, 67, 193, 21, 227, 173, 244, 119, 199, 128, 158 -}; - -static const unsigned char FSb2[256] = -{ - 224, 5, 88, 217, 103, 78, 129, 203, 201, 11, 174, 106, 213, 24, 93, 130, - 70, 223, 214, 39, 138, 50, 75, 66, 219, 28, 158, 156, 58, 202, 37, 123, - 13, 113, 95, 31, 248, 215, 62, 157, 124, 96, 185, 190, 188, 139, 22, 52, - 77, 195, 114, 149, 171, 142, 186, 122, 179, 2, 180, 173, 162, 172, 216, 154, - 23, 26, 53, 204, 247, 153, 97, 90, 232, 36, 86, 64, 225, 99, 9, 51, - 191, 152, 151, 133, 104, 252, 236, 10, 218, 111, 83, 98, 163, 46, 8, 175, - 40, 176, 116, 194, 189, 54, 34, 56, 100, 30, 57, 44, 166, 48, 229, 68, - 253, 136, 159, 101, 135, 107, 244, 35, 72, 16, 209, 81, 192, 249, 210, 160, - 85, 161, 65, 250, 67, 19, 196, 47, 168, 182, 60, 43, 193, 255, 200, 165, - 32, 137, 0, 144, 71, 239, 234, 183, 21, 6, 205, 181, 18, 126, 187, 41, - 15, 184, 7, 4, 155, 148, 33, 102, 230, 206, 237, 231, 59, 254, 127, 197, - 164, 55, 177, 76, 145, 110, 141, 118, 3, 45, 222, 150, 38, 125, 198, 92, - 211, 242, 79, 25, 63, 220, 121, 29, 82, 235, 243, 109, 94, 251, 105, 178, - 240, 49, 12, 212, 207, 140, 226, 117, 169, 74, 87, 132, 17, 69, 27, 245, - 228, 14, 115, 170, 241, 221, 89, 20, 108, 146, 84, 208, 120, 112, 227, 73, - 128, 80, 167, 246, 119, 147, 134, 131, 42, 199, 91, 233, 238, 143, 1, 61 -}; - -static const unsigned char FSb3[256] = -{ - 56, 65, 22, 118, 217, 147, 96, 242, 114, 194, 171, 154, 117, 6, 87, 160, - 145, 247, 181, 201, 162, 140, 210, 144, 246, 7, 167, 39, 142, 178, 73, 222, - 67, 92, 215, 199, 62, 245, 143, 103, 31, 24, 110, 175, 47, 226, 133, 13, - 83, 240, 156, 101, 234, 163, 174, 158, 236, 128, 45, 107, 168, 43, 54, 166, - 197, 134, 77, 51, 253, 102, 88, 150, 58, 9, 149, 16, 120, 216, 66, 204, - 239, 38, 229, 97, 26, 63, 59, 130, 182, 219, 212, 152, 232, 139, 2, 235, - 10, 44, 29, 176, 111, 141, 136, 14, 25, 135, 78, 11, 169, 12, 121, 17, - 127, 34, 231, 89, 225, 218, 61, 200, 18, 4, 116, 84, 48, 126, 180, 40, - 85, 104, 80, 190, 208, 196, 49, 203, 42, 173, 15, 202, 112, 255, 50, 105, - 8, 98, 0, 36, 209, 251, 186, 237, 69, 129, 115, 109, 132, 159, 238, 74, - 195, 46, 193, 1, 230, 37, 72, 153, 185, 179, 123, 249, 206, 191, 223, 113, - 41, 205, 108, 19, 100, 155, 99, 157, 192, 75, 183, 165, 137, 95, 177, 23, - 244, 188, 211, 70, 207, 55, 94, 71, 148, 250, 252, 91, 151, 254, 90, 172, - 60, 76, 3, 53, 243, 35, 184, 93, 106, 146, 213, 33, 68, 81, 198, 125, - 57, 131, 220, 170, 124, 119, 86, 5, 27, 164, 21, 52, 30, 28, 248, 82, - 32, 20, 233, 189, 221, 228, 161, 224, 138, 241, 214, 122, 187, 227, 64, 79 -}; - -static const unsigned char FSb4[256] = -{ - 112, 44, 179, 192, 228, 87, 234, 174, 35, 107, 69, 165, 237, 79, 29, 146, - 134, 175, 124, 31, 62, 220, 94, 11, 166, 57, 213, 93, 217, 90, 81, 108, - 139, 154, 251, 176, 116, 43, 240, 132, 223, 203, 52, 118, 109, 169, 209, 4, - 20, 58, 222, 17, 50, 156, 83, 242, 254, 207, 195, 122, 36, 232, 96, 105, - 170, 160, 161, 98, 84, 30, 224, 100, 16, 0, 163, 117, 138, 230, 9, 221, - 135, 131, 205, 144, 115, 246, 157, 191, 82, 216, 200, 198, 129, 111, 19, 99, - 233, 167, 159, 188, 41, 249, 47, 180, 120, 6, 231, 113, 212, 171, 136, 141, - 114, 185, 248, 172, 54, 42, 60, 241, 64, 211, 187, 67, 21, 173, 119, 128, - 130, 236, 39, 229, 133, 53, 12, 65, 239, 147, 25, 33, 14, 78, 101, 189, - 184, 143, 235, 206, 48, 95, 197, 26, 225, 202, 71, 61, 1, 214, 86, 77, - 13, 102, 204, 45, 18, 32, 177, 153, 76, 194, 126, 5, 183, 49, 23, 215, - 88, 97, 27, 28, 15, 22, 24, 34, 68, 178, 181, 145, 8, 168, 252, 80, - 208, 125, 137, 151, 91, 149, 255, 210, 196, 72, 247, 219, 3, 218, 63, 148, - 92, 2, 74, 51, 103, 243, 127, 226, 155, 38, 55, 59, 150, 75, 190, 46, - 121, 140, 110, 142, 245, 182, 253, 89, 152, 106, 70, 186, 37, 66, 162, 250, - 7, 85, 238, 10, 73, 104, 56, 164, 40, 123, 201, 193, 227, 244, 199, 158 -}; - -#define SBOX1(n) FSb[(n)] -#define SBOX2(n) FSb2[(n)] -#define SBOX3(n) FSb3[(n)] -#define SBOX4(n) FSb4[(n)] - -#endif - -static const unsigned char shifts[2][4][4] = -{ - { - { 1, 1, 1, 1 }, /* KL */ - { 0, 0, 0, 0 }, /* KR */ - { 1, 1, 1, 1 }, /* KA */ - { 0, 0, 0, 0 } /* KB */ - }, - { - { 1, 0, 1, 1 }, /* KL */ - { 1, 1, 0, 1 }, /* KR */ - { 1, 1, 1, 0 }, /* KA */ - { 1, 1, 0, 1 } /* KB */ - } -}; - -static const signed char indexes[2][4][20] = -{ - { - { 0, 1, 2, 3, 8, 9, 10, 11, 38, 39, - 36, 37, 23, 20, 21, 22, 27, -1, -1, 26 }, /* KL -> RK */ - { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /* KR -> RK */ - { 4, 5, 6, 7, 12, 13, 14, 15, 16, 17, - 18, 19, -1, 24, 25, -1, 31, 28, 29, 30 }, /* KA -> RK */ - { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } /* KB -> RK */ - }, - { - { 0, 1, 2, 3, 61, 62, 63, 60, -1, -1, - -1, -1, 27, 24, 25, 26, 35, 32, 33, 34 }, /* KL -> RK */ - { -1, -1, -1, -1, 8, 9, 10, 11, 16, 17, - 18, 19, -1, -1, -1, -1, 39, 36, 37, 38 }, /* KR -> RK */ - { -1, -1, -1, -1, 12, 13, 14, 15, 58, 59, - 56, 57, 31, 28, 29, 30, -1, -1, -1, -1 }, /* KA -> RK */ - { 4, 5, 6, 7, 65, 66, 67, 64, 20, 21, - 22, 23, -1, -1, -1, -1, 43, 40, 41, 42 } /* KB -> RK */ - } -}; - -static const signed char transposes[2][20] = -{ - { - 21, 22, 23, 20, - -1, -1, -1, -1, - 18, 19, 16, 17, - 11, 8, 9, 10, - 15, 12, 13, 14 - }, - { - 25, 26, 27, 24, - 29, 30, 31, 28, - 18, 19, 16, 17, - -1, -1, -1, -1, - -1, -1, -1, -1 - } -}; - -/* Shift macro for 128 bit strings with rotation smaller than 32 bits (!) */ -#define ROTL(DEST, SRC, SHIFT) \ -{ \ - (DEST)[0] = (SRC)[0] << (SHIFT) ^ (SRC)[1] >> (32 - (SHIFT)); \ - (DEST)[1] = (SRC)[1] << (SHIFT) ^ (SRC)[2] >> (32 - (SHIFT)); \ - (DEST)[2] = (SRC)[2] << (SHIFT) ^ (SRC)[3] >> (32 - (SHIFT)); \ - (DEST)[3] = (SRC)[3] << (SHIFT) ^ (SRC)[0] >> (32 - (SHIFT)); \ -} - -#define FL(XL, XR, KL, KR) \ -{ \ - (XR) = ((((XL) & (KL)) << 1) | (((XL) & (KL)) >> 31)) ^ (XR); \ - (XL) = ((XR) | (KR)) ^ (XL); \ -} - -#define FLInv(YL, YR, KL, KR) \ -{ \ - (YL) = ((YR) | (KR)) ^ (YL); \ - (YR) = ((((YL) & (KL)) << 1) | (((YL) & (KL)) >> 31)) ^ (YR); \ -} - -#define SHIFT_AND_PLACE(INDEX, OFFSET) \ -{ \ - TK[0] = KC[(OFFSET) * 4 + 0]; \ - TK[1] = KC[(OFFSET) * 4 + 1]; \ - TK[2] = KC[(OFFSET) * 4 + 2]; \ - TK[3] = KC[(OFFSET) * 4 + 3]; \ - \ - for ( i = 1; i <= 4; i++ ) \ - if (shifts[(INDEX)][(OFFSET)][i -1]) \ - ROTL(TK + i * 4, TK, (15 * i) % 32); \ - \ - for ( i = 0; i < 20; i++ ) \ - if (indexes[(INDEX)][(OFFSET)][i] != -1) { \ - RK[indexes[(INDEX)][(OFFSET)][i]] = TK[ i ]; \ - } \ -} - -static void camellia_feistel(const uint32_t x[2], const uint32_t k[2], uint32_t z[2]) -{ - uint32_t I0, I1; - I0 = x[0] ^ k[0]; - I1 = x[1] ^ k[1]; - - I0 = (SBOX1((I0 >> 24) & 0xFF) << 24) | - (SBOX2((I0 >> 16) & 0xFF) << 16) | - (SBOX3((I0 >> 8) & 0xFF) << 8) | - (SBOX4((I0 ) & 0xFF) ); - I1 = (SBOX2((I1 >> 24) & 0xFF) << 24) | - (SBOX3((I1 >> 16) & 0xFF) << 16) | - (SBOX4((I1 >> 8) & 0xFF) << 8) | - (SBOX1((I1 ) & 0xFF) ); - - I0 ^= (I1 << 8) | (I1 >> 24); - I1 ^= (I0 << 16) | (I0 >> 16); - I0 ^= (I1 >> 8) | (I1 << 24); - I1 ^= (I0 >> 8) | (I0 << 24); - - z[0] ^= I1; - z[1] ^= I0; -} - -/* - * Camellia key schedule (encryption) - */ -int camellia_setkey_enc( camellia_context *ctx, const unsigned char *key, unsigned int keysize ) -{ - int idx; - size_t i; - uint32_t *RK; - unsigned char t[64]; - uint32_t SIGMA[6][2]; - uint32_t KC[16]; - uint32_t TK[20]; - - RK = ctx->rk; - - memset(t, 0, 64); - memset(RK, 0, sizeof(ctx->rk)); - - switch( keysize ) - { - case 128: ctx->nr = 3; idx = 0; break; - case 192: - case 256: ctx->nr = 4; idx = 1; break; - default : return( POLARSSL_ERR_CAMELLIA_INVALID_KEY_LENGTH ); - } - - for( i = 0; i < keysize / 8; ++i) - t[i] = key[i]; - - if (keysize == 192) { - for (i = 0; i < 8; i++) - t[24 + i] = ~t[16 + i]; - } - - /* - * Prepare SIGMA values - */ - for (i = 0; i < 6; i++) { - GET_UINT32_BE(SIGMA[i][0], SIGMA_CHARS[i], 0); - GET_UINT32_BE(SIGMA[i][1], SIGMA_CHARS[i], 4); - } - - /* - * Key storage in KC - * Order: KL, KR, KA, KB - */ - memset(KC, 0, sizeof(KC)); - - /* Store KL, KR */ - for (i = 0; i < 8; i++) - GET_UINT32_BE(KC[i], t, i * 4); - - /* Generate KA */ - for( i = 0; i < 4; ++i) - KC[8 + i] = KC[i] ^ KC[4 + i]; - - camellia_feistel(KC + 8, SIGMA[0], KC + 10); - camellia_feistel(KC + 10, SIGMA[1], KC + 8); - - for( i = 0; i < 4; ++i) - KC[8 + i] ^= KC[i]; - - camellia_feistel(KC + 8, SIGMA[2], KC + 10); - camellia_feistel(KC + 10, SIGMA[3], KC + 8); - - if (keysize > 128) { - /* Generate KB */ - for( i = 0; i < 4; ++i) - KC[12 + i] = KC[4 + i] ^ KC[8 + i]; - - camellia_feistel(KC + 12, SIGMA[4], KC + 14); - camellia_feistel(KC + 14, SIGMA[5], KC + 12); - } - - /* - * Generating subkeys - */ - - /* Manipulating KL */ - SHIFT_AND_PLACE(idx, 0); - - /* Manipulating KR */ - if (keysize > 128) { - SHIFT_AND_PLACE(idx, 1); - } - - /* Manipulating KA */ - SHIFT_AND_PLACE(idx, 2); - - /* Manipulating KB */ - if (keysize > 128) { - SHIFT_AND_PLACE(idx, 3); - } - - /* Do transpositions */ - for ( i = 0; i < 20; i++ ) { - if (transposes[idx][i] != -1) { - RK[32 + 12 * idx + i] = RK[transposes[idx][i]]; - } - } - - return( 0 ); -} - -/* - * Camellia key schedule (decryption) - */ -int camellia_setkey_dec( camellia_context *ctx, const unsigned char *key, unsigned int keysize ) -{ - int idx; - size_t i; - camellia_context cty; - uint32_t *RK; - uint32_t *SK; - int ret; - - switch( keysize ) - { - case 128: ctx->nr = 3; idx = 0; break; - case 192: - case 256: ctx->nr = 4; idx = 1; break; - default : return( POLARSSL_ERR_CAMELLIA_INVALID_KEY_LENGTH ); - } - - RK = ctx->rk; - - ret = camellia_setkey_enc(&cty, key, keysize); - if( ret != 0 ) - return( ret ); - - SK = cty.rk + 24 * 2 + 8 * idx * 2; - - *RK++ = *SK++; - *RK++ = *SK++; - *RK++ = *SK++; - *RK++ = *SK++; - - for (i = 22 + 8 * idx, SK -= 6; i > 0; i--, SK -= 4) - { - *RK++ = *SK++; - *RK++ = *SK++; - } - - SK -= 2; - - *RK++ = *SK++; - *RK++ = *SK++; - *RK++ = *SK++; - *RK++ = *SK++; - - memset( &cty, 0, sizeof( camellia_context ) ); - - return( 0 ); -} - -/* - * Camellia-ECB block encryption/decryption - */ -int camellia_crypt_ecb( camellia_context *ctx, - int mode, - const unsigned char input[16], - unsigned char output[16] ) -{ - int NR; - uint32_t *RK, X[4]; - - ( (void) mode ); - - NR = ctx->nr; - RK = ctx->rk; - - GET_UINT32_BE( X[0], input, 0 ); - GET_UINT32_BE( X[1], input, 4 ); - GET_UINT32_BE( X[2], input, 8 ); - GET_UINT32_BE( X[3], input, 12 ); - - X[0] ^= *RK++; - X[1] ^= *RK++; - X[2] ^= *RK++; - X[3] ^= *RK++; - - while (NR) { - --NR; - camellia_feistel(X, RK, X + 2); - RK += 2; - camellia_feistel(X + 2, RK, X); - RK += 2; - camellia_feistel(X, RK, X + 2); - RK += 2; - camellia_feistel(X + 2, RK, X); - RK += 2; - camellia_feistel(X, RK, X + 2); - RK += 2; - camellia_feistel(X + 2, RK, X); - RK += 2; - - if (NR) { - FL(X[0], X[1], RK[0], RK[1]); - RK += 2; - FLInv(X[2], X[3], RK[0], RK[1]); - RK += 2; - } - } - - X[2] ^= *RK++; - X[3] ^= *RK++; - X[0] ^= *RK++; - X[1] ^= *RK++; - - PUT_UINT32_BE( X[2], output, 0 ); - PUT_UINT32_BE( X[3], output, 4 ); - PUT_UINT32_BE( X[0], output, 8 ); - PUT_UINT32_BE( X[1], output, 12 ); - - return( 0 ); -} - -/* - * Camellia-CBC buffer encryption/decryption - */ -int camellia_crypt_cbc( camellia_context *ctx, - int mode, - size_t length, - unsigned char iv[16], - const unsigned char *input, - unsigned char *output ) -{ - int i; - unsigned char temp[16]; - - if( length % 16 ) - return( POLARSSL_ERR_CAMELLIA_INVALID_INPUT_LENGTH ); - - if( mode == CAMELLIA_DECRYPT ) - { - while( length > 0 ) - { - memcpy( temp, input, 16 ); - camellia_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] ); - - camellia_crypt_ecb( ctx, mode, output, output ); - memcpy( iv, output, 16 ); - - input += 16; - output += 16; - length -= 16; - } - } - - return( 0 ); -} - -#if defined(POLARSSL_CIPHER_MODE_CFB) -/* - * Camellia-CFB128 buffer encryption/decryption - */ -int camellia_crypt_cfb128( camellia_context *ctx, - int mode, - size_t length, - size_t *iv_off, - unsigned char iv[16], - const unsigned char *input, - unsigned char *output ) -{ - int c; - size_t n = *iv_off; - - if( mode == CAMELLIA_DECRYPT ) - { - while( length-- ) - { - if( n == 0 ) - camellia_crypt_ecb( ctx, CAMELLIA_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 ) - camellia_crypt_ecb( ctx, CAMELLIA_ENCRYPT, iv, iv ); - - iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); - - n = (n + 1) & 0x0F; - } - } - - *iv_off = n; - - return( 0 ); -} -#endif /* POLARSSL_CIPHER_MODE_CFB */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) -/* - * Camellia-CTR buffer encryption/decryption - */ -int camellia_crypt_ctr( camellia_context *ctx, - size_t length, - size_t *nc_off, - unsigned char nonce_counter[16], - unsigned char stream_block[16], - const unsigned char *input, - unsigned char *output ) -{ - int c, i; - size_t n = *nc_off; - - while( length-- ) - { - if( n == 0 ) { - camellia_crypt_ecb( ctx, CAMELLIA_ENCRYPT, nonce_counter, stream_block ); - - for( i = 16; i > 0; i-- ) - if( ++nonce_counter[i - 1] != 0 ) - break; - } - c = *input++; - *output++ = (unsigned char)( c ^ stream_block[n] ); - - n = (n + 1) & 0x0F; - } - - *nc_off = n; - - return( 0 ); -} -#endif /* POLARSSL_CIPHER_MODE_CTR */ -#endif /* !POLARSSL_CAMELLIA_ALT */ - -#if defined(POLARSSL_SELF_TEST) - -#include - -/* - * Camellia test vectors from: - * - * http://info.isl.ntt.co.jp/crypt/eng/camellia/technology.html: - * http://info.isl.ntt.co.jp/crypt/eng/camellia/dl/cryptrec/intermediate.txt - * http://info.isl.ntt.co.jp/crypt/eng/camellia/dl/cryptrec/t_camellia.txt - * (For each bitlength: Key 0, Nr 39) - */ -#define CAMELLIA_TESTS_ECB 2 - -static const unsigned char camellia_test_ecb_key[3][CAMELLIA_TESTS_ECB][32] = -{ - { - { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } - }, - { - { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } - }, - { - { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } - }, -}; - -static const unsigned char camellia_test_ecb_plain[CAMELLIA_TESTS_ECB][16] = -{ - { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }, - { 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } -}; - -static const unsigned char camellia_test_ecb_cipher[3][CAMELLIA_TESTS_ECB][16] = -{ - { - { 0x67, 0x67, 0x31, 0x38, 0x54, 0x96, 0x69, 0x73, - 0x08, 0x57, 0x06, 0x56, 0x48, 0xea, 0xbe, 0x43 }, - { 0x38, 0x3C, 0x6C, 0x2A, 0xAB, 0xEF, 0x7F, 0xDE, - 0x25, 0xCD, 0x47, 0x0B, 0xF7, 0x74, 0xA3, 0x31 } - }, - { - { 0xb4, 0x99, 0x34, 0x01, 0xb3, 0xe9, 0x96, 0xf8, - 0x4e, 0xe5, 0xce, 0xe7, 0xd7, 0x9b, 0x09, 0xb9 }, - { 0xD1, 0x76, 0x3F, 0xC0, 0x19, 0xD7, 0x7C, 0xC9, - 0x30, 0xBF, 0xF2, 0xA5, 0x6F, 0x7C, 0x93, 0x64 } - }, - { - { 0x9a, 0xcc, 0x23, 0x7d, 0xff, 0x16, 0xd7, 0x6c, - 0x20, 0xef, 0x7c, 0x91, 0x9e, 0x3a, 0x75, 0x09 }, - { 0x05, 0x03, 0xFB, 0x10, 0xAB, 0x24, 0x1E, 0x7C, - 0xF4, 0x5D, 0x8C, 0xDE, 0xEE, 0x47, 0x43, 0x35 } - } -}; - -#define CAMELLIA_TESTS_CBC 3 - -static const unsigned char camellia_test_cbc_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 camellia_test_cbc_iv[16] = - - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F } -; - -static const unsigned char camellia_test_cbc_plain[CAMELLIA_TESTS_CBC][16] = -{ - { 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 } - -}; - -static const unsigned char camellia_test_cbc_cipher[3][CAMELLIA_TESTS_CBC][16] = -{ - { - { 0x16, 0x07, 0xCF, 0x49, 0x4B, 0x36, 0xBB, 0xF0, - 0x0D, 0xAE, 0xB0, 0xB5, 0x03, 0xC8, 0x31, 0xAB }, - { 0xA2, 0xF2, 0xCF, 0x67, 0x16, 0x29, 0xEF, 0x78, - 0x40, 0xC5, 0xA5, 0xDF, 0xB5, 0x07, 0x48, 0x87 }, - { 0x0F, 0x06, 0x16, 0x50, 0x08, 0xCF, 0x8B, 0x8B, - 0x5A, 0x63, 0x58, 0x63, 0x62, 0x54, 0x3E, 0x54 } - }, - { - { 0x2A, 0x48, 0x30, 0xAB, 0x5A, 0xC4, 0xA1, 0xA2, - 0x40, 0x59, 0x55, 0xFD, 0x21, 0x95, 0xCF, 0x93 }, - { 0x5D, 0x5A, 0x86, 0x9B, 0xD1, 0x4C, 0xE5, 0x42, - 0x64, 0xF8, 0x92, 0xA6, 0xDD, 0x2E, 0xC3, 0xD5 }, - { 0x37, 0xD3, 0x59, 0xC3, 0x34, 0x98, 0x36, 0xD8, - 0x84, 0xE3, 0x10, 0xAD, 0xDF, 0x68, 0xC4, 0x49 } - }, - { - { 0xE6, 0xCF, 0xA3, 0x5F, 0xC0, 0x2B, 0x13, 0x4A, - 0x4D, 0x2C, 0x0B, 0x67, 0x37, 0xAC, 0x3E, 0xDA }, - { 0x36, 0xCB, 0xEB, 0x73, 0xBD, 0x50, 0x4B, 0x40, - 0x70, 0xB1, 0xB7, 0xDE, 0x2B, 0x21, 0xEB, 0x50 }, - { 0xE3, 0x1A, 0x60, 0x55, 0x29, 0x7D, 0x96, 0xCA, - 0x33, 0x30, 0xCD, 0xF1, 0xB1, 0x86, 0x0A, 0x83 } - } -}; - -#if defined(POLARSSL_CIPHER_MODE_CTR) -/* - * Camellia-CTR test vectors from: - * - * http://www.faqs.org/rfcs/rfc5528.html - */ - -static const unsigned char camellia_test_ctr_key[3][16] = -{ - { 0xAE, 0x68, 0x52, 0xF8, 0x12, 0x10, 0x67, 0xCC, - 0x4B, 0xF7, 0xA5, 0x76, 0x55, 0x77, 0xF3, 0x9E }, - { 0x7E, 0x24, 0x06, 0x78, 0x17, 0xFA, 0xE0, 0xD7, - 0x43, 0xD6, 0xCE, 0x1F, 0x32, 0x53, 0x91, 0x63 }, - { 0x76, 0x91, 0xBE, 0x03, 0x5E, 0x50, 0x20, 0xA8, - 0xAC, 0x6E, 0x61, 0x85, 0x29, 0xF9, 0xA0, 0xDC } -}; - -static const unsigned char camellia_test_ctr_nonce_counter[3][16] = -{ - { 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, - { 0x00, 0x6C, 0xB6, 0xDB, 0xC0, 0x54, 0x3B, 0x59, - 0xDA, 0x48, 0xD9, 0x0B, 0x00, 0x00, 0x00, 0x01 }, - { 0x00, 0xE0, 0x01, 0x7B, 0x27, 0x77, 0x7F, 0x3F, - 0x4A, 0x17, 0x86, 0xF0, 0x00, 0x00, 0x00, 0x01 } -}; - -static const unsigned char camellia_test_ctr_pt[3][48] = -{ - { 0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x62, - 0x6C, 0x6F, 0x63, 0x6B, 0x20, 0x6D, 0x73, 0x67 }, - - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }, - - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, - 0x20, 0x21, 0x22, 0x23 } -}; - -static const unsigned char camellia_test_ctr_ct[3][48] = -{ - { 0xD0, 0x9D, 0xC2, 0x9A, 0x82, 0x14, 0x61, 0x9A, - 0x20, 0x87, 0x7C, 0x76, 0xDB, 0x1F, 0x0B, 0x3F }, - { 0xDB, 0xF3, 0xC7, 0x8D, 0xC0, 0x83, 0x96, 0xD4, - 0xDA, 0x7C, 0x90, 0x77, 0x65, 0xBB, 0xCB, 0x44, - 0x2B, 0x8E, 0x8E, 0x0F, 0x31, 0xF0, 0xDC, 0xA7, - 0x2C, 0x74, 0x17, 0xE3, 0x53, 0x60, 0xE0, 0x48 }, - { 0xB1, 0x9D, 0x1F, 0xCD, 0xCB, 0x75, 0xEB, 0x88, - 0x2F, 0x84, 0x9C, 0xE2, 0x4D, 0x85, 0xCF, 0x73, - 0x9C, 0xE6, 0x4B, 0x2B, 0x5C, 0x9D, 0x73, 0xF1, - 0x4F, 0x2D, 0x5D, 0x9D, 0xCE, 0x98, 0x89, 0xCD, - 0xDF, 0x50, 0x86, 0x96 } -}; - -static const int camellia_test_ctr_len[3] = - { 16, 32, 36 }; -#endif /* POLARSSL_CIPHER_MODE_CTR */ - -/* - * Checkup routine - */ -int camellia_self_test( int verbose ) -{ - int i, j, u, v; - unsigned char key[32]; - unsigned char buf[64]; - unsigned char src[16]; - unsigned char dst[16]; - unsigned char iv[16]; -#if defined(POLARSSL_CIPHER_MODE_CTR) - size_t offset, len; - unsigned char nonce_counter[16]; - unsigned char stream_block[16]; -#endif - - camellia_context ctx; - - memset( key, 0, 32 ); - - for (j = 0; j < 6; j++) { - u = j >> 1; - v = j & 1; - - if( verbose != 0 ) - printf( " CAMELLIA-ECB-%3d (%s): ", 128 + u * 64, - (v == CAMELLIA_DECRYPT) ? "dec" : "enc"); - - for (i = 0; i < CAMELLIA_TESTS_ECB; i++ ) { - memcpy( key, camellia_test_ecb_key[u][i], 16 + 8 * u); - - if (v == CAMELLIA_DECRYPT) { - camellia_setkey_dec(&ctx, key, 128 + u * 64); - memcpy(src, camellia_test_ecb_cipher[u][i], 16); - memcpy(dst, camellia_test_ecb_plain[i], 16); - } else { /* CAMELLIA_ENCRYPT */ - camellia_setkey_enc(&ctx, key, 128 + u * 64); - memcpy(src, camellia_test_ecb_plain[i], 16); - memcpy(dst, camellia_test_ecb_cipher[u][i], 16); - } - - camellia_crypt_ecb(&ctx, v, src, buf); - - if( memcmp( buf, dst, 16 ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - } - - if( verbose != 0 ) - printf( "passed\n" ); - } - - if( verbose != 0 ) - printf( "\n" ); - - /* - * CBC mode - */ - for( j = 0; j < 6; j++ ) - { - u = j >> 1; - v = j & 1; - - if( verbose != 0 ) - printf( " CAMELLIA-CBC-%3d (%s): ", 128 + u * 64, - ( v == CAMELLIA_DECRYPT ) ? "dec" : "enc" ); - - memcpy( src, camellia_test_cbc_iv, 16); - memcpy( dst, camellia_test_cbc_iv, 16); - memcpy( key, camellia_test_cbc_key[u], 16 + 8 * u); - - if (v == CAMELLIA_DECRYPT) { - camellia_setkey_dec(&ctx, key, 128 + u * 64); - } else { - camellia_setkey_enc(&ctx, key, 128 + u * 64); - } - - for (i = 0; i < CAMELLIA_TESTS_CBC; i++ ) { - - if (v == CAMELLIA_DECRYPT) { - memcpy( iv , src, 16 ); - memcpy(src, camellia_test_cbc_cipher[u][i], 16); - memcpy(dst, camellia_test_cbc_plain[i], 16); - } else { /* CAMELLIA_ENCRYPT */ - memcpy( iv , dst, 16 ); - memcpy(src, camellia_test_cbc_plain[i], 16); - memcpy(dst, camellia_test_cbc_cipher[u][i], 16); - } - - camellia_crypt_cbc(&ctx, v, 16, iv, src, buf); - - if( memcmp( buf, dst, 16 ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - } - - if( verbose != 0 ) - printf( "passed\n" ); - } - - if( verbose != 0 ) - printf( "\n" ); - -#if defined(POLARSSL_CIPHER_MODE_CTR) - /* - * CTR mode - */ - for( i = 0; i < 6; i++ ) - { - u = i >> 1; - v = i & 1; - - if( verbose != 0 ) - printf( " CAMELLIA-CTR-128 (%s): ", - ( v == CAMELLIA_DECRYPT ) ? "dec" : "enc" ); - - memcpy( nonce_counter, camellia_test_ctr_nonce_counter[u], 16 ); - memcpy( key, camellia_test_ctr_key[u], 16 ); - - offset = 0; - camellia_setkey_enc( &ctx, key, 128 ); - - if( v == CAMELLIA_DECRYPT ) - { - len = camellia_test_ctr_len[u]; - memcpy( buf, camellia_test_ctr_ct[u], len ); - - camellia_crypt_ctr( &ctx, len, &offset, nonce_counter, stream_block, buf, buf ); - - if( memcmp( buf, camellia_test_ctr_pt[u], len ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - } - else - { - len = camellia_test_ctr_len[u]; - memcpy( buf, camellia_test_ctr_pt[u], len ); - - camellia_crypt_ctr( &ctx, len, &offset, nonce_counter, stream_block, buf, buf ); - - if( memcmp( buf, camellia_test_ctr_ct[u], len ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - } - - if( verbose != 0 ) - printf( "passed\n" ); - } - - if( verbose != 0 ) - printf( "\n" ); -#endif /* POLARSSL_CIPHER_MODE_CTR */ - - return ( 0 ); -} - -#endif - -#endif diff --git a/makerom/polarssl/certs.c b/makerom/polarssl/certs.c deleted file mode 100644 index e2d07f76..00000000 --- a/makerom/polarssl/certs.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * X.509 test certificates - * - * 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_CERTS_C) - -const char test_ca_crt[] = -"-----BEGIN CERTIFICATE-----\r\n" -"MIIDhzCCAm+gAwIBAgIBADANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJOTDER\r\n" -"MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN\r\n" -"MTEwMjEyMTQ0NDAwWhcNMjEwMjEyMTQ0NDAwWjA7MQswCQYDVQQGEwJOTDERMA8G\r\n" -"A1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwggEiMA0G\r\n" -"CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDA3zf8F7vglp0/ht6WMn1EpRagzSHx\r\n" -"mdTs6st8GFgIlKXsm8WL3xoemTiZhx57wI053zhdcHgH057Zk+i5clHFzqMwUqny\r\n" -"50BwFMtEonILwuVA+T7lpg6z+exKY8C4KQB0nFc7qKUEkHHxvYPZP9al4jwqj+8n\r\n" -"YMPGn8u67GB9t+aEMr5P+1gmIgNb1LTV+/Xjli5wwOQuvfwu7uJBVcA0Ln0kcmnL\r\n" -"R7EUQIN9Z/SG9jGr8XmksrUuEvmEF/Bibyc+E1ixVA0hmnM3oTDPb5Lc9un8rNsu\r\n" -"KNF+AksjoBXyOGVkCeoMbo4bF6BxyLObyavpw/LPh5aPgAIynplYb6LVAgMBAAGj\r\n" -"gZUwgZIwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQUtFrkpbPe0lL2udWmlQ/rPrzH\r\n" -"/f8wYwYDVR0jBFwwWoAUtFrkpbPe0lL2udWmlQ/rPrzH/f+hP6Q9MDsxCzAJBgNV\r\n" -"BAYTAk5MMREwDwYDVQQKEwhQb2xhclNTTDEZMBcGA1UEAxMQUG9sYXJTU0wgVGVz\r\n" -"dCBDQYIBADANBgkqhkiG9w0BAQUFAAOCAQEAuP1U2ABUkIslsCfdlc2i94QHHYeJ\r\n" -"SsR4EdgHtdciUI5I62J6Mom+Y0dT/7a+8S6MVMCZP6C5NyNyXw1GWY/YR82XTJ8H\r\n" -"DBJiCTok5DbZ6SzaONBzdWHXwWwmi5vg1dxn7YxrM9d0IjxM27WNKs4sDQhZBQkF\r\n" -"pjmfs2cb4oPl4Y9T9meTx/lvdkRYEug61Jfn6cA+qHpyPYdTH+UshITnmp5/Ztkf\r\n" -"m/UTSLBNFNHesiTZeH31NcxYGdHSme9Nc/gfidRa0FLOCfWxRlFqAI47zG9jAQCZ\r\n" -"7Z2mCGDNMhjQc+BYcdnl0lPXjdDK6V0qCg1dVewhUBcW5gZKzV7e9+DpVA==\r\n" -"-----END CERTIFICATE-----\r\n"; - -const char test_ca_key[] = -"-----BEGIN RSA PRIVATE KEY-----\r\n" -"Proc-Type: 4,ENCRYPTED\r\n" -"DEK-Info: DES-EDE3-CBC,A8A95B05D5B7206B\r\n" -"\r\n" -"9Qd9GeArejl1GDVh2lLV1bHt0cPtfbh5h/5zVpAVaFpqtSPMrElp50Rntn9et+JA\r\n" -"7VOyboR+Iy2t/HU4WvA687k3Bppe9GwKHjHhtl//8xFKwZr3Xb5yO5JUP8AUctQq\r\n" -"Nb8CLlZyuUC+52REAAthdWgsX+7dJO4yabzUcQ22Tp9JSD0hiL43BlkWYUNK3dAo\r\n" -"PZlmiptjnzVTjg1MxsBSydZinWOLBV8/JQgxSPo2yD4uEfig28qbvQ2wNIn0pnAb\r\n" -"GxnSAOazkongEGfvcjIIs+LZN9gXFhxcOh6kc4Q/c99B7QWETwLLkYgZ+z1a9VY9\r\n" -"gEU7CwCxYCD+h9hY6FPmsK0/lC4O7aeRKpYq00rPPxs6i7phiexg6ax6yTMmArQq\r\n" -"QmK3TAsJm8V/J5AWpLEV6jAFgRGymGGHnof0DXzVWZidrcZJWTNuGEX90nB3ee2w\r\n" -"PXJEFWKoD3K3aFcSLdHYr3mLGxP7H9ThQai9VsycxZKS5kwvBKQ//YMrmFfwPk8x\r\n" -"vTeY4KZMaUrveEel5tWZC94RSMKgxR6cyE1nBXyTQnDOGbfpNNgBKxyKbINWoOJU\r\n" -"WJZAwlsQn+QzCDwpri7+sV1mS3gBE6UY7aQmnmiiaC2V3Hbphxct/en5QsfDOt1X\r\n" -"JczSfpRWLlbPznZg8OQh/VgCMA58N5DjOzTIK7sJJ5r+94ZBTCpgAMbF588f0NTR\r\n" -"KCe4yrxGJR7X02M4nvD4IwOlpsQ8xQxZtOSgXv4LkxvdU9XJJKWZ/XNKJeWztxSe\r\n" -"Z1vdTc2YfsDBA2SEv33vxHx2g1vqtw8SjDRT2RaQSS0QuSaMJimdOX6mTOCBKk1J\r\n" -"9Q5mXTrER+/LnK0jEmXsBXWA5bqqVZIyahXSx4VYZ7l7w/PHiUDtDgyRhMMKi4n2\r\n" -"iQvQcWSQTjrpnlJbca1/DkpRt3YwrvJwdqb8asZU2VrNETh5x0QVefDRLFiVpif/\r\n" -"tUaeAe/P1F8OkS7OIZDs1SUbv/sD2vMbhNkUoCms3/PvNtdnvgL4F0zhaDpKCmlT\r\n" -"P8vx49E7v5CyRNmED9zZg4o3wmMqrQO93PtTug3Eu9oVx1zPQM1NVMyBa2+f29DL\r\n" -"1nuTCeXdo9+ni45xx+jAI4DCwrRdhJ9uzZyC6962H37H6D+5naNvClFR1s6li1Gb\r\n" -"nqPoiy/OBsEx9CaDGcqQBp5Wme/3XW+6z1ISOx+igwNTVCT14mHdBMbya0eIKft5\r\n" -"X+GnwtgEMyCYyyWuUct8g4RzErcY9+yW9Om5Hzpx4zOuW4NPZgPDTgK+t2RSL/Yq\r\n" -"rE1njrgeGYcVeG3f+OftH4s6fPbq7t1A5ZgUscbLMBqr9tK+OqygR4EgKBPsH6Cz\r\n" -"L6zlv/2RV0qAHvVuDJcIDIgwY5rJtINEm32rhOeFNJwZS5MNIC1czXZx5//ugX7l\r\n" -"I4sy5nbVhwSjtAk8Xg5dZbdTZ6mIrb7xqH+fdakZor1khG7bC2uIwibD3cSl2XkR\r\n" -"wN48lslbHnqqagr6Xm1nNOSVl8C/6kbJEsMpLhAezfRtGwvOucoaE+WbeUNolGde\r\n" -"P/eQiddSf0brnpiLJRh7qZrl9XuqYdpUqnoEdMAfotDOID8OtV7gt8a48ad8VPW2\r\n" -"-----END RSA PRIVATE KEY-----\r\n"; - -const char test_ca_pwd[] = "PolarSSLTest"; - -const char test_srv_crt[] = -"-----BEGIN CERTIFICATE-----\r\n" -"MIIDPzCCAiegAwIBAgIBATANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJOTDER\r\n" -"MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN\r\n" -"MTEwMjEyMTQ0NDA2WhcNMjEwMjEyMTQ0NDA2WjA8MQswCQYDVQQGEwJOTDERMA8G\r\n" -"A1UEChMIUG9sYXJTU0wxGjAYBgNVBAMTEVBvbGFyU1NMIFNlcnZlciAxMIIBIjAN\r\n" -"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQIfPUBq1VVTi/027oJlLhVhXom/\r\n" -"uOhFkNvuiBZS0/FDUEeWEllkh2v9K+BG+XO+3c+S4ZFb7Wagb4kpeUWA0INq1UFD\r\n" -"d185fAkER4KwVzlw7aPsFRkeqDMIR8EFQqn9TMO0390GH00QUUBncxMPQPhtgSVf\r\n" -"CrFTxjB+FTms+Vruf5KepgVb5xOXhbUjktnUJAbVCSWJdQfdphqPPwkZvq1lLGTr\r\n" -"lZvc/kFeF6babFtpzAK6FCwWJJxK3M3Q91Jnc/EtoCP9fvQxyi1wyokLBNsupk9w\r\n" -"bp7OvViJ4lNZnm5akmXiiD8MlBmj3eXonZUT7Snbq3AS3FrKaxerUoJUsQIDAQAB\r\n" -"o00wSzAJBgNVHRMEAjAAMB0GA1UdDgQWBBQfdNY/KcF0dEU7BRIsPai9Q1kCpjAf\r\n" -"BgNVHSMEGDAWgBS0WuSls97SUva51aaVD+s+vMf9/zANBgkqhkiG9w0BAQUFAAOC\r\n" -"AQEAvc+WwZUemsJu2IiI2Cp6liA+UAvIx98dQe3kZs2zAoF9VwQbXcYzWQ/BILkj\r\n" -"NImKbPL9x0g2jIDn4ZvGYFywMwIO/d++YbwYiQw42/v7RiMy94zBPnzeHi86dy/0\r\n" -"jpOOJUx3IXRsGLdyjb/1T11klcFqGnARiK+8VYolMPP6afKvLXX7K4kiUpsFQhUp\r\n" -"E5VeM5pV1Mci2ETOJau2cO40FJvI/C9W/wR+GAArMaw2fxG77E3laaa0LAOlexM6\r\n" -"A4KOb5f5cGTM5Ih6tEF5FVq3/9vzNIYMa1FqzacBLZF8zSHYLEimXBdzjBoN4qDU\r\n" -"/WzRyYRBRjAI49mzHX6raleqnw==\r\n" -"-----END CERTIFICATE-----\r\n"; - -const char test_srv_key[] = -"-----BEGIN RSA PRIVATE KEY-----\r\n" -"MIIEogIBAAKCAQEAqQIfPUBq1VVTi/027oJlLhVhXom/uOhFkNvuiBZS0/FDUEeW\r\n" -"Ellkh2v9K+BG+XO+3c+S4ZFb7Wagb4kpeUWA0INq1UFDd185fAkER4KwVzlw7aPs\r\n" -"FRkeqDMIR8EFQqn9TMO0390GH00QUUBncxMPQPhtgSVfCrFTxjB+FTms+Vruf5Ke\r\n" -"pgVb5xOXhbUjktnUJAbVCSWJdQfdphqPPwkZvq1lLGTrlZvc/kFeF6babFtpzAK6\r\n" -"FCwWJJxK3M3Q91Jnc/EtoCP9fvQxyi1wyokLBNsupk9wbp7OvViJ4lNZnm5akmXi\r\n" -"iD8MlBmj3eXonZUT7Snbq3AS3FrKaxerUoJUsQIDAQABAoIBABaJ9eiRQq4Ypv+w\r\n" -"UTcVpLC0oTueWzcpor1i1zjG4Vzqe/Ok2FqyGToGKMlFK7Hwwa+LEyeJ3xyV5yd4\r\n" -"v1Mw9bDZFdJC1eCBjoUAHtX6k9HOE0Vd6woVQ4Vi6OPI1g7B5Mnr/58rNrnN6TMs\r\n" -"x58NF6euecwTU811QJrZtLbX7j2Cr28yB2Vs8qyYlHwVw5jbDOv43D7vU5gmlIDN\r\n" -"0JQRuWAnOuPzZNoJr4SfJKqHNGxYYY6pHZ1s0dOTLIDb/B8KQWapA2kRmZyid2EH\r\n" -"nwzgLbAsHJCf+bQnhXjXuxtUsrcIL8noZLazlOMxwNEammglVWW23Ud/QRnFgJg5\r\n" -"UgcAcRECgYEA19uYetht5qmwdJ+12oC6zeO+vXLcyD9gon23T5J6w2YThld7/OW0\r\n" -"oArQJGgkAdaq0pcTyOIjtTQVMFygdVmCEJmxh/3RutPcTeydqW9fphKDMej32J8e\r\n" -"GniGmNGiclbcfNOS8E5TGp445yZb9P1+7AHng16bGg3Ykj5EA4G+HCcCgYEAyHAl\r\n" -"//ekk8YjQElm+8izLtFkymIK0aCtEe9C/RIRhFYBeFaotC5dStNhBOncn4ovMAPD\r\n" -"lX/92yDi9OP8PPLN3a4B9XpW3k/SS5GrbT5cwOivBHNllZSmu/2qz5WPGcjVCOrB\r\n" -"LYl3YWr2h3EGKICT03kEoTkiDBvCeOpW7cCGl2cCgYBD5whoXHz1+ptPlI4YVjZt\r\n" -"Xh86aU+ajpVPiEyJ84I6xXmO4SZXv8q6LaycR0ZMbcL+zBelMb4Z2nBv7jNrtuR7\r\n" -"ZF28cdPv+YVr3esaybZE/73VjXup4SQPH6r3l7qKTVi+y6+FeJ4b2Xn8/MwgnT23\r\n" -"8EFrye7wmzpthrjOgZnUMQKBgE9Lhsz/5J0Nis6Y+2Pqn3CLKEukg9Ewtqdct2y0\r\n" -"5Dcta0F3TyCRIxlCDKTL/BslqMtfAdY4H268UO0+8IAQMn9boqzBrHIgs/pvc5kx\r\n" -"TbKHmw2wtWR6vYersBKVgVpbCGSRssDYHGFu1n74qM4HJ/RGcR1zI9QUe1gopSFD\r\n" -"xDtLAoGAVAdWvrqDwgoL2hHW3scGpxdE/ygJDOwHnf+1B9goKAOP5lf2FJaiAxf3\r\n" -"ectoPOgZbCmm/iiDmigu703ld3O+VoCLDD4qx3R+KyALL78gtVJYzSRiKhzgCZ3g\r\n" -"mKsIVRBq4IfwiwyMNG2BYZQAwbSDjjPtn/kPBduPzPj7eriByhI=\r\n" -"-----END RSA PRIVATE KEY-----\r\n"; - -const char test_cli_crt[] = -"-----BEGIN CERTIFICATE-----\r\n" -"MIIDPzCCAiegAwIBAgIBBDANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJOTDER\r\n" -"MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN\r\n" -"MTEwMjEyMTQ0NDA3WhcNMjEwMjEyMTQ0NDA3WjA8MQswCQYDVQQGEwJOTDERMA8G\r\n" -"A1UEChMIUG9sYXJTU0wxGjAYBgNVBAMTEVBvbGFyU1NMIENsaWVudCAyMIIBIjAN\r\n" -"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyHTEzLn5tXnpRdkUYLB9u5Pyax6f\r\n" -"M60Nj4o8VmXl3ETZzGaFB9X4J7BKNdBjngpuG7fa8H6r7gwQk4ZJGDTzqCrSV/Uu\r\n" -"1C93KYRhTYJQj6eVSHD1bk2y1RPD0hrt5kPqQhTrdOrA7R/UV06p86jt0uDBMHEw\r\n" -"MjDV0/YI0FZPRo7yX/k9Z5GIMC5Cst99++UMd//sMcB4j7/Cf8qtbCHWjdmLao5v\r\n" -"4Jv4EFbMs44TFeY0BGbH7vk2DmqV9gmaBmf0ZXH4yqSxJeD+PIs1BGe64E92hfx/\r\n" -"/DZrtenNLQNiTrM9AM+vdqBpVoNq0qjU51Bx5rU2BXcFbXvI5MT9TNUhXwIDAQAB\r\n" -"o00wSzAJBgNVHRMEAjAAMB0GA1UdDgQWBBRxoQBzckAvVHZeM/xSj7zx3WtGITAf\r\n" -"BgNVHSMEGDAWgBS0WuSls97SUva51aaVD+s+vMf9/zANBgkqhkiG9w0BAQUFAAOC\r\n" -"AQEAAn86isAM8X+mVwJqeItt6E9slhEQbAofyk+diH1Lh8Y9iLlWQSKbw/UXYjx5\r\n" -"LLPZcniovxIcARC/BjyZR9g3UwTHNGNm+rwrqa15viuNOFBchykX/Orsk02EH7NR\r\n" -"Alw5WLPorYjED6cdVQgBl9ot93HdJogRiXCxErM7NC8/eP511mjq+uLDjLKH8ZPQ\r\n" -"8I4ekHJnroLsDkIwXKGIsvIBHQy2ac/NwHLCQOK6mfum1pRx52V4Utu5dLLjD5bM\r\n" -"xOBC7KU4xZKuMXXZM6/93Yb51K/J4ahf1TxJlTWXtnzDr9saEYdNy2SKY/6ZiDNH\r\n" -"D+stpAKiQLAWaAusIWKYEyw9MQ==\r\n" -"-----END CERTIFICATE-----\r\n"; - -const char test_cli_key[] = -"-----BEGIN RSA PRIVATE KEY-----\r\n" -"MIIEpAIBAAKCAQEAyHTEzLn5tXnpRdkUYLB9u5Pyax6fM60Nj4o8VmXl3ETZzGaF\r\n" -"B9X4J7BKNdBjngpuG7fa8H6r7gwQk4ZJGDTzqCrSV/Uu1C93KYRhTYJQj6eVSHD1\r\n" -"bk2y1RPD0hrt5kPqQhTrdOrA7R/UV06p86jt0uDBMHEwMjDV0/YI0FZPRo7yX/k9\r\n" -"Z5GIMC5Cst99++UMd//sMcB4j7/Cf8qtbCHWjdmLao5v4Jv4EFbMs44TFeY0BGbH\r\n" -"7vk2DmqV9gmaBmf0ZXH4yqSxJeD+PIs1BGe64E92hfx//DZrtenNLQNiTrM9AM+v\r\n" -"dqBpVoNq0qjU51Bx5rU2BXcFbXvI5MT9TNUhXwIDAQABAoIBAGdNtfYDiap6bzst\r\n" -"yhCiI8m9TtrhZw4MisaEaN/ll3XSjaOG2dvV6xMZCMV+5TeXDHOAZnY18Yi18vzz\r\n" -"4Ut2TnNFzizCECYNaA2fST3WgInnxUkV3YXAyP6CNxJaCmv2aA0yFr2kFVSeaKGt\r\n" -"ymvljNp2NVkvm7Th8fBQBO7I7AXhz43k0mR7XmPgewe8ApZOG3hstkOaMvbWAvWA\r\n" -"zCZupdDjZYjOJqlA4eEA4H8/w7F83r5CugeBE8LgEREjLPiyejrU5H1fubEY+h0d\r\n" -"l5HZBJ68ybTXfQ5U9o/QKA3dd0toBEhhdRUDGzWtjvwkEQfqF1reGWj/tod/gCpf\r\n" -"DFi6X0ECgYEA4wOv/pjSC3ty6TuOvKX2rOUiBrLXXv2JSxZnMoMiWI5ipLQt+RYT\r\n" -"VPafL/m7Dn6MbwjayOkcZhBwk5CNz5A6Q4lJ64Mq/lqHznRCQQ2Mc1G8eyDF/fYL\r\n" -"Ze2pLvwP9VD5jTc2miDfw+MnvJhywRRLcemDFP8k4hQVtm8PMp3ZmNECgYEA4gz7\r\n" -"wzObR4gn8ibe617uQPZjWzUj9dUHYd+in1gwBCIrtNnaRn9I9U/Q6tegRYpii4ys\r\n" -"c176NmU+umy6XmuSKV5qD9bSpZWG2nLFnslrN15Lm3fhZxoeMNhBaEDTnLT26yoi\r\n" -"33gp0mSSWy94ZEqipms+ULF6sY1ZtFW6tpGFoy8CgYAQHhnnvJflIs2ky4q10B60\r\n" -"ZcxFp3rtDpkp0JxhFLhiizFrujMtZSjYNm5U7KkgPVHhLELEUvCmOnKTt4ap/vZ0\r\n" -"BxJNe1GZH3pW6SAvGDQpl9sG7uu/vTFP+lCxukmzxB0DrrDcvorEkKMom7ZCCRvW\r\n" -"KZsZ6YeH2Z81BauRj218kQKBgQCUV/DgKP2985xDTT79N08jUo3hTP5MVYCCuj/+\r\n" -"UeEw1TvZcx3LJby7P6Xad6a1/BqveaGyFKIfEFIaBUBItk801sDDpDaYc4gL00Xc\r\n" -"7lFuBHOZkxJYlss5QrGpuOEl9ZwUt5IrFLBdYaKqNHzNVC1pCPfb/JyH6Dr2HUxq\r\n" -"gxUwAQKBgQCcU6G2L8AG9d9c0UpOyL1tMvFe5Ttw0KjlQVdsh1MP6yigYo9DYuwu\r\n" -"bHFVW2r0dBTqegP2/KTOxKzaHfC1qf0RGDsUoJCNJrd1cwoCLG8P2EF4w3OBrKqv\r\n" -"8u4ytY0F+Vlanj5lm3TaoHSVF1+NWPyOTiwevIECGKwSxvlki4fDAA==\r\n" -"-----END RSA PRIVATE KEY-----\r\n"; - -const char test_dhm_params[] = -"-----BEGIN DH PARAMETERS-----\r\n" -"MIGHAoGBAJ419DBEOgmQTzo5qXl5fQcN9TN455wkOL7052HzxxRVMyhYmwQcgJvh\r\n" -"1sa18fyfR9OiVEMYglOpkqVoGLN7qd5aQNNi5W7/C+VBdHTBJcGZJyyP5B3qcz32\r\n" -"9mLJKudlVudV0Qxk5qUJaPZ/xupz0NyoVpviuiBOI1gNi8ovSXWzAgEC\r\n" -"-----END DH PARAMETERS-----\r\n"; - -#endif diff --git a/makerom/polarssl/cipher.c b/makerom/polarssl/cipher.c deleted file mode 100644 index 09c38073..00000000 --- a/makerom/polarssl/cipher.c +++ /dev/null @@ -1,602 +0,0 @@ -/** - * \file cipher.c - * - * \brief Generic cipher wrapper for PolarSSL - * - * \author Adriaan de Jong - * - * Copyright (C) 2006-2012, 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_CIPHER_C) - -#include "polarssl/cipher.h" -#include "polarssl/cipher_wrap.h" - -#include -#include - -#if defined _MSC_VER && !defined strcasecmp -#define strcasecmp _stricmp -#endif - -static const int supported_ciphers[] = { - -#if defined(POLARSSL_AES_C) - POLARSSL_CIPHER_AES_128_CBC, - POLARSSL_CIPHER_AES_192_CBC, - POLARSSL_CIPHER_AES_256_CBC, - -#if defined(POLARSSL_CIPHER_MODE_CFB) - POLARSSL_CIPHER_AES_128_CFB128, - POLARSSL_CIPHER_AES_192_CFB128, - POLARSSL_CIPHER_AES_256_CFB128, -#endif /* defined(POLARSSL_CIPHER_MODE_CFB) */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) - POLARSSL_CIPHER_AES_128_CTR, - POLARSSL_CIPHER_AES_192_CTR, - POLARSSL_CIPHER_AES_256_CTR, -#endif /* defined(POLARSSL_CIPHER_MODE_CTR) */ - -#endif /* defined(POLARSSL_AES_C) */ - -#if defined(POLARSSL_CAMELLIA_C) - POLARSSL_CIPHER_CAMELLIA_128_CBC, - POLARSSL_CIPHER_CAMELLIA_192_CBC, - POLARSSL_CIPHER_CAMELLIA_256_CBC, - -#if defined(POLARSSL_CIPHER_MODE_CFB) - POLARSSL_CIPHER_CAMELLIA_128_CFB128, - POLARSSL_CIPHER_CAMELLIA_192_CFB128, - POLARSSL_CIPHER_CAMELLIA_256_CFB128, -#endif /* defined(POLARSSL_CIPHER_MODE_CFB) */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) - POLARSSL_CIPHER_CAMELLIA_128_CTR, - POLARSSL_CIPHER_CAMELLIA_192_CTR, - POLARSSL_CIPHER_CAMELLIA_256_CTR, -#endif /* defined(POLARSSL_CIPHER_MODE_CTR) */ - -#endif /* defined(POLARSSL_CAMELLIA_C) */ - -#if defined(POLARSSL_DES_C) - POLARSSL_CIPHER_DES_CBC, - POLARSSL_CIPHER_DES_EDE_CBC, - POLARSSL_CIPHER_DES_EDE3_CBC, -#endif /* defined(POLARSSL_DES_C) */ - -#if defined(POLARSSL_BLOWFISH_C) - POLARSSL_CIPHER_BLOWFISH_CBC, - -#if defined(POLARSSL_CIPHER_MODE_CFB) - POLARSSL_CIPHER_BLOWFISH_CFB64, -#endif /* defined(POLARSSL_CIPHER_MODE_CFB) */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) - POLARSSL_CIPHER_BLOWFISH_CTR, -#endif /* defined(POLARSSL_CIPHER_MODE_CTR) */ - -#endif /* defined(POLARSSL_BLOWFISH_C) */ - -#if defined(POLARSSL_CIPHER_NULL_CIPHER) - POLARSSL_CIPHER_NULL, -#endif /* defined(POLARSSL_CIPHER_NULL_CIPHER) */ - - 0 -}; - -const int *cipher_list( void ) -{ - return supported_ciphers; -} - -const cipher_info_t *cipher_info_from_type( const cipher_type_t cipher_type ) -{ - /* Find static cipher information */ - switch ( cipher_type ) - { -#if defined(POLARSSL_AES_C) - case POLARSSL_CIPHER_AES_128_CBC: - return &aes_128_cbc_info; - case POLARSSL_CIPHER_AES_192_CBC: - return &aes_192_cbc_info; - case POLARSSL_CIPHER_AES_256_CBC: - return &aes_256_cbc_info; - -#if defined(POLARSSL_CIPHER_MODE_CFB) - case POLARSSL_CIPHER_AES_128_CFB128: - return &aes_128_cfb128_info; - case POLARSSL_CIPHER_AES_192_CFB128: - return &aes_192_cfb128_info; - case POLARSSL_CIPHER_AES_256_CFB128: - return &aes_256_cfb128_info; -#endif /* defined(POLARSSL_CIPHER_MODE_CFB) */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) - case POLARSSL_CIPHER_AES_128_CTR: - return &aes_128_ctr_info; - case POLARSSL_CIPHER_AES_192_CTR: - return &aes_192_ctr_info; - case POLARSSL_CIPHER_AES_256_CTR: - return &aes_256_ctr_info; -#endif /* defined(POLARSSL_CIPHER_MODE_CTR) */ - -#endif - -#if defined(POLARSSL_CAMELLIA_C) - case POLARSSL_CIPHER_CAMELLIA_128_CBC: - return &camellia_128_cbc_info; - case POLARSSL_CIPHER_CAMELLIA_192_CBC: - return &camellia_192_cbc_info; - case POLARSSL_CIPHER_CAMELLIA_256_CBC: - return &camellia_256_cbc_info; - -#if defined(POLARSSL_CIPHER_MODE_CFB) - case POLARSSL_CIPHER_CAMELLIA_128_CFB128: - return &camellia_128_cfb128_info; - case POLARSSL_CIPHER_CAMELLIA_192_CFB128: - return &camellia_192_cfb128_info; - case POLARSSL_CIPHER_CAMELLIA_256_CFB128: - return &camellia_256_cfb128_info; -#endif /* defined(POLARSSL_CIPHER_MODE_CFB) */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) - case POLARSSL_CIPHER_CAMELLIA_128_CTR: - return &camellia_128_ctr_info; - case POLARSSL_CIPHER_CAMELLIA_192_CTR: - return &camellia_192_ctr_info; - case POLARSSL_CIPHER_CAMELLIA_256_CTR: - return &camellia_256_ctr_info; -#endif /* defined(POLARSSL_CIPHER_MODE_CTR) */ - -#endif - -#if defined(POLARSSL_DES_C) - case POLARSSL_CIPHER_DES_CBC: - return &des_cbc_info; - case POLARSSL_CIPHER_DES_EDE_CBC: - return &des_ede_cbc_info; - case POLARSSL_CIPHER_DES_EDE3_CBC: - return &des_ede3_cbc_info; -#endif - -#if defined(POLARSSL_BLOWFISH_C) - case POLARSSL_CIPHER_BLOWFISH_CBC: - return &blowfish_cbc_info; - -#if defined(POLARSSL_CIPHER_MODE_CFB) - case POLARSSL_CIPHER_BLOWFISH_CFB64: - return &blowfish_cfb64_info; -#endif /* defined(POLARSSL_CIPHER_MODE_CFB) */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) - case POLARSSL_CIPHER_BLOWFISH_CTR: - return &blowfish_ctr_info; -#endif /* defined(POLARSSL_CIPHER_MODE_CTR) */ - -#endif - -#if defined(POLARSSL_CIPHER_NULL_CIPHER) - case POLARSSL_CIPHER_NULL: - return &null_cipher_info; -#endif /* defined(POLARSSL_CIPHER_NULL_CIPHER) */ - - default: - return NULL; - } -} - -const cipher_info_t *cipher_info_from_string( const char *cipher_name ) -{ - if( NULL == cipher_name ) - return NULL; - - /* Get the appropriate cipher information */ -#if defined(POLARSSL_CAMELLIA_C) - if( !strcasecmp( "CAMELLIA-128-CBC", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_CAMELLIA_128_CBC ); - if( !strcasecmp( "CAMELLIA-192-CBC", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_CAMELLIA_192_CBC ); - if( !strcasecmp( "CAMELLIA-256-CBC", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_CAMELLIA_256_CBC ); - -#if defined(POLARSSL_CIPHER_MODE_CFB) - if( !strcasecmp( "CAMELLIA-128-CFB128", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_CAMELLIA_128_CFB128 ); - if( !strcasecmp( "CAMELLIA-192-CFB128", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_CAMELLIA_192_CFB128 ); - if( !strcasecmp( "CAMELLIA-256-CFB128", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_CAMELLIA_256_CFB128 ); -#endif /* defined(POLARSSL_CIPHER_MODE_CFB) */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) - if( !strcasecmp( "CAMELLIA-128-CTR", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_CAMELLIA_128_CTR ); - if( !strcasecmp( "CAMELLIA-192-CTR", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_CAMELLIA_192_CTR ); - if( !strcasecmp( "CAMELLIA-256-CTR", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_CAMELLIA_256_CTR ); -#endif /* defined(POLARSSL_CIPHER_MODE_CTR) */ -#endif - -#if defined(POLARSSL_AES_C) - if( !strcasecmp( "AES-128-CBC", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_AES_128_CBC ); - if( !strcasecmp( "AES-192-CBC", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_AES_192_CBC ); - if( !strcasecmp( "AES-256-CBC", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_AES_256_CBC ); - -#if defined(POLARSSL_CIPHER_MODE_CFB) - if( !strcasecmp( "AES-128-CFB128", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_AES_128_CFB128 ); - if( !strcasecmp( "AES-192-CFB128", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_AES_192_CFB128 ); - if( !strcasecmp( "AES-256-CFB128", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_AES_256_CFB128 ); -#endif /* defined(POLARSSL_CIPHER_MODE_CFB) */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) - if( !strcasecmp( "AES-128-CTR", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_AES_128_CTR ); - if( !strcasecmp( "AES-192-CTR", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_AES_192_CTR ); - if( !strcasecmp( "AES-256-CTR", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_AES_256_CTR ); -#endif /* defined(POLARSSL_CIPHER_MODE_CTR) */ -#endif - -#if defined(POLARSSL_DES_C) - if( !strcasecmp( "DES-CBC", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_DES_CBC ); - if( !strcasecmp( "DES-EDE-CBC", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_DES_EDE_CBC ); - if( !strcasecmp( "DES-EDE3-CBC", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_DES_EDE3_CBC ); -#endif - -#if defined(POLARSSL_BLOWFISH_C) - if( !strcasecmp( "BLOWFISH-CBC", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_BLOWFISH_CBC ); - -#if defined(POLARSSL_CIPHER_MODE_CFB) - if( !strcasecmp( "BLOWFISH-CFB64", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_BLOWFISH_CFB64 ); -#endif /* defined(POLARSSL_CIPHER_MODE_CFB) */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) - if( !strcasecmp( "BLOWFISH-CTR", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_BLOWFISH_CTR ); -#endif /* defined(POLARSSL_CIPHER_MODE_CTR) */ -#endif - -#if defined(POLARSSL_CIPHER_NULL_CIPHER) - if( !strcasecmp( "NULL", cipher_name ) ) - return cipher_info_from_type( POLARSSL_CIPHER_NULL ); -#endif /* defined(POLARSSL_CIPHER_NULL_CIPHER) */ - - return NULL; -} - -int cipher_init_ctx( cipher_context_t *ctx, const cipher_info_t *cipher_info ) -{ - if( NULL == cipher_info || NULL == ctx ) - return POLARSSL_ERR_CIPHER_BAD_INPUT_DATA; - - memset( ctx, 0, sizeof( cipher_context_t ) ); - - if( NULL == ( ctx->cipher_ctx = cipher_info->base->ctx_alloc_func() ) ) - return POLARSSL_ERR_CIPHER_ALLOC_FAILED; - - ctx->cipher_info = cipher_info; - - return 0; -} - -int cipher_free_ctx( cipher_context_t *ctx ) -{ - if( ctx == NULL || ctx->cipher_info == NULL ) - return POLARSSL_ERR_CIPHER_BAD_INPUT_DATA; - - ctx->cipher_info->base->ctx_free_func( ctx->cipher_ctx ); - - return 0; -} - -int cipher_setkey( cipher_context_t *ctx, const unsigned char *key, - int key_length, const operation_t operation ) -{ - if( NULL == ctx || NULL == ctx->cipher_info ) - return POLARSSL_ERR_CIPHER_BAD_INPUT_DATA; - - ctx->key_length = key_length; - ctx->operation = operation; - -#if defined(POLARSSL_CIPHER_NULL_CIPHER) - if( ctx->cipher_info->mode == POLARSSL_MODE_NULL ) - return 0; -#endif /* defined(POLARSSL_CIPHER_NULL_CIPHER) */ - - /* - * For CFB and CTR mode always use the encryption key schedule - */ - if( POLARSSL_ENCRYPT == operation || - POLARSSL_MODE_CFB == ctx->cipher_info->mode || - POLARSSL_MODE_CTR == ctx->cipher_info->mode ) - { - return ctx->cipher_info->base->setkey_enc_func( ctx->cipher_ctx, key, - ctx->key_length ); - } - - if( POLARSSL_DECRYPT == operation ) - return ctx->cipher_info->base->setkey_dec_func( ctx->cipher_ctx, key, - ctx->key_length ); - - return POLARSSL_ERR_CIPHER_BAD_INPUT_DATA; -} - -int cipher_reset( cipher_context_t *ctx, const unsigned char *iv ) -{ - if( NULL == ctx || NULL == ctx->cipher_info || NULL == iv ) - return POLARSSL_ERR_CIPHER_BAD_INPUT_DATA; - - ctx->unprocessed_len = 0; - - memcpy( ctx->iv, iv, cipher_get_iv_size( ctx ) ); - - return 0; -} - -int cipher_update( cipher_context_t *ctx, const unsigned char *input, size_t ilen, - unsigned char *output, size_t *olen ) -{ - int ret; - size_t copy_len = 0; - - if( NULL == ctx || NULL == ctx->cipher_info || NULL == olen || - input == output ) - { - return POLARSSL_ERR_CIPHER_BAD_INPUT_DATA; - } - - *olen = 0; - -#if defined(POLARSSL_CIPHER_NULL_CIPHER) - if( ctx->cipher_info->mode == POLARSSL_MODE_NULL ) - { - memcpy( output, input, ilen ); - *olen = ilen; - return 0; - } -#endif /* defined(POLARSSL_CIPHER_NULL_CIPHER) */ - - if( ctx->cipher_info->mode == POLARSSL_MODE_CBC ) - { - /* - * If there is not enough data for a full block, cache it. - */ - if( ( ctx->operation == POLARSSL_DECRYPT && - ilen + ctx->unprocessed_len <= cipher_get_block_size( ctx ) ) || - ( ctx->operation == POLARSSL_ENCRYPT && - ilen + ctx->unprocessed_len < cipher_get_block_size( ctx ) ) ) - { - memcpy( &( ctx->unprocessed_data[ctx->unprocessed_len] ), input, - ilen ); - - ctx->unprocessed_len += ilen; - return 0; - } - - /* - * Process cached data first - */ - if( ctx->unprocessed_len != 0 ) - { - copy_len = cipher_get_block_size( ctx ) - ctx->unprocessed_len; - - memcpy( &( ctx->unprocessed_data[ctx->unprocessed_len] ), input, - copy_len ); - - if( 0 != ( ret = ctx->cipher_info->base->cbc_func( ctx->cipher_ctx, - ctx->operation, cipher_get_block_size( ctx ), ctx->iv, - ctx->unprocessed_data, output ) ) ) - { - return ret; - } - - *olen += cipher_get_block_size( ctx ); - output += cipher_get_block_size( ctx ); - ctx->unprocessed_len = 0; - - input += copy_len; - ilen -= copy_len; - } - - /* - * Cache final, incomplete block - */ - if( 0 != ilen ) - { - copy_len = ilen % cipher_get_block_size( ctx ); - if( copy_len == 0 && ctx->operation == POLARSSL_DECRYPT ) - copy_len = cipher_get_block_size(ctx); - - memcpy( ctx->unprocessed_data, &( input[ilen - copy_len] ), - copy_len ); - - ctx->unprocessed_len += copy_len; - ilen -= copy_len; - } - - /* - * Process remaining full blocks - */ - if( ilen ) - { - if( 0 != ( ret = ctx->cipher_info->base->cbc_func( ctx->cipher_ctx, - ctx->operation, ilen, ctx->iv, input, output ) ) ) - { - return ret; - } - *olen += ilen; - } - - return 0; - } - - if( ctx->cipher_info->mode == POLARSSL_MODE_CFB ) - { - if( 0 != ( ret = ctx->cipher_info->base->cfb_func( ctx->cipher_ctx, - ctx->operation, ilen, &ctx->unprocessed_len, ctx->iv, - input, output ) ) ) - { - return ret; - } - - *olen = ilen; - - return 0; - } - - if( ctx->cipher_info->mode == POLARSSL_MODE_CTR ) - { - if( 0 != ( ret = ctx->cipher_info->base->ctr_func( ctx->cipher_ctx, - ilen, &ctx->unprocessed_len, ctx->iv, - ctx->unprocessed_data, input, output ) ) ) - { - return ret; - } - - *olen = ilen; - - return 0; - } - - return POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE; -} - -static void add_pkcs_padding( unsigned char *output, size_t output_len, - size_t data_len ) -{ - size_t padding_len = output_len - data_len; - unsigned char i = 0; - - for( i = 0; i < padding_len; i++ ) - output[data_len + i] = (unsigned char) padding_len; -} - -static int get_pkcs_padding( unsigned char *input, unsigned int input_len, - size_t *data_len) -{ - unsigned int i, padding_len = 0; - - if( NULL == input || NULL == data_len ) - return POLARSSL_ERR_CIPHER_BAD_INPUT_DATA; - - padding_len = input[input_len - 1]; - - if( padding_len > input_len ) - return POLARSSL_ERR_CIPHER_INVALID_PADDING; - - for( i = input_len - padding_len; i < input_len; i++ ) - if( input[i] != padding_len ) - return POLARSSL_ERR_CIPHER_INVALID_PADDING; - - *data_len = input_len - padding_len; - - return 0; -} - -int cipher_finish( cipher_context_t *ctx, unsigned char *output, size_t *olen) -{ - int ret = 0; - - if( NULL == ctx || NULL == ctx->cipher_info || NULL == olen ) - return POLARSSL_ERR_CIPHER_BAD_INPUT_DATA; - - *olen = 0; - - if( POLARSSL_MODE_CFB == ctx->cipher_info->mode || - POLARSSL_MODE_CTR == ctx->cipher_info->mode || - POLARSSL_MODE_NULL == ctx->cipher_info->mode ) - { - return 0; - } - - if( POLARSSL_MODE_CBC == ctx->cipher_info->mode ) - { - if( POLARSSL_ENCRYPT == ctx->operation ) - { - add_pkcs_padding( ctx->unprocessed_data, cipher_get_iv_size( ctx ), - ctx->unprocessed_len ); - } - else if ( cipher_get_block_size( ctx ) != ctx->unprocessed_len ) - { - /* For decrypt operations, expect a full block */ - return POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED; - } - - /* cipher block */ - if( 0 != ( ret = ctx->cipher_info->base->cbc_func( ctx->cipher_ctx, - ctx->operation, cipher_get_block_size( ctx ), ctx->iv, - ctx->unprocessed_data, output ) ) ) - { - return ret; - } - - /* Set output size for decryption */ - if( POLARSSL_DECRYPT == ctx->operation ) - return get_pkcs_padding( output, cipher_get_block_size( ctx ), olen ); - - /* Set output size for encryption */ - *olen = cipher_get_block_size( ctx ); - return 0; - } - - return POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE; -} - -#if defined(POLARSSL_SELF_TEST) - -#include - -#define ASSERT(x) if (!(x)) { \ - printf( "failed with %i at %s\n", value, (#x) ); \ - return( 1 ); \ -} -/* - * Checkup routine - */ - -int cipher_self_test( int verbose ) -{ - ((void) verbose); - - return( 0 ); -} - -#endif - -#endif diff --git a/makerom/polarssl/cipher_wrap.c b/makerom/polarssl/cipher_wrap.c deleted file mode 100644 index 94372127..00000000 --- a/makerom/polarssl/cipher_wrap.c +++ /dev/null @@ -1,711 +0,0 @@ -/** - * \file md_wrap.c - * - * \brief Generic cipher wrapper for PolarSSL - * - * \author Adriaan de Jong - * - * Copyright (C) 2006-2012, 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_CIPHER_C) - -#include "polarssl/cipher_wrap.h" - -#if defined(POLARSSL_AES_C) -#include "polarssl/aes.h" -#endif - -#if defined(POLARSSL_CAMELLIA_C) -#include "polarssl/camellia.h" -#endif - -#if defined(POLARSSL_DES_C) -#include "polarssl/des.h" -#endif - -#if defined(POLARSSL_BLOWFISH_C) -#include "polarssl/blowfish.h" -#endif - -#include - -#if defined(POLARSSL_AES_C) - -int aes_crypt_cbc_wrap( void *ctx, operation_t operation, size_t length, - unsigned char *iv, const unsigned char *input, unsigned char *output ) -{ - return aes_crypt_cbc( (aes_context *) ctx, operation, length, iv, input, output ); -} - -int aes_crypt_cfb128_wrap( void *ctx, operation_t operation, size_t length, - size_t *iv_off, unsigned char *iv, const unsigned char *input, unsigned char *output ) -{ -#if defined(POLARSSL_CIPHER_MODE_CFB) - return aes_crypt_cfb128( (aes_context *) ctx, operation, length, iv_off, iv, input, output ); -#else - ((void) ctx); - ((void) operation); - ((void) length); - ((void) iv_off); - ((void) iv); - ((void) input); - ((void) output); - - return POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE; -#endif -} - -int aes_crypt_ctr_wrap( void *ctx, size_t length, - size_t *nc_off, unsigned char *nonce_counter, unsigned char *stream_block, - const unsigned char *input, unsigned char *output ) -{ -#if defined(POLARSSL_CIPHER_MODE_CTR) - return aes_crypt_ctr( (aes_context *) ctx, length, nc_off, nonce_counter, - stream_block, input, output ); -#else - ((void) ctx); - ((void) length); - ((void) nc_off); - ((void) nonce_counter); - ((void) stream_block); - ((void) input); - ((void) output); - - return POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE; -#endif -} - -int aes_setkey_dec_wrap( void *ctx, const unsigned char *key, unsigned int key_length ) -{ - return aes_setkey_dec( (aes_context *) ctx, key, key_length ); -} - -int aes_setkey_enc_wrap( void *ctx, const unsigned char *key, unsigned int key_length ) -{ - return aes_setkey_enc( (aes_context *) ctx, key, key_length ); -} - -static void * aes_ctx_alloc( void ) -{ - return malloc( sizeof( aes_context ) ); -} - -static void aes_ctx_free( void *ctx ) -{ - free( ctx ); -} - -const cipher_base_t aes_info = { - POLARSSL_CIPHER_ID_AES, - aes_crypt_cbc_wrap, - aes_crypt_cfb128_wrap, - aes_crypt_ctr_wrap, - aes_setkey_enc_wrap, - aes_setkey_dec_wrap, - aes_ctx_alloc, - aes_ctx_free -}; - -const cipher_info_t aes_128_cbc_info = { - POLARSSL_CIPHER_AES_128_CBC, - POLARSSL_MODE_CBC, - 128, - "AES-128-CBC", - 16, - 16, - &aes_info -}; - -const cipher_info_t aes_192_cbc_info = { - POLARSSL_CIPHER_AES_192_CBC, - POLARSSL_MODE_CBC, - 192, - "AES-192-CBC", - 16, - 16, - &aes_info -}; - -const cipher_info_t aes_256_cbc_info = { - POLARSSL_CIPHER_AES_256_CBC, - POLARSSL_MODE_CBC, - 256, - "AES-256-CBC", - 16, - 16, - &aes_info -}; - -#if defined(POLARSSL_CIPHER_MODE_CFB) -const cipher_info_t aes_128_cfb128_info = { - POLARSSL_CIPHER_AES_128_CFB128, - POLARSSL_MODE_CFB, - 128, - "AES-128-CFB128", - 16, - 16, - &aes_info -}; - -const cipher_info_t aes_192_cfb128_info = { - POLARSSL_CIPHER_AES_192_CFB128, - POLARSSL_MODE_CFB, - 192, - "AES-192-CFB128", - 16, - 16, - &aes_info -}; - -const cipher_info_t aes_256_cfb128_info = { - POLARSSL_CIPHER_AES_256_CFB128, - POLARSSL_MODE_CFB, - 256, - "AES-256-CFB128", - 16, - 16, - &aes_info -}; -#endif /* POLARSSL_CIPHER_MODE_CFB */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) -const cipher_info_t aes_128_ctr_info = { - POLARSSL_CIPHER_AES_128_CTR, - POLARSSL_MODE_CTR, - 128, - "AES-128-CTR", - 16, - 16, - &aes_info -}; - -const cipher_info_t aes_192_ctr_info = { - POLARSSL_CIPHER_AES_192_CTR, - POLARSSL_MODE_CTR, - 192, - "AES-192-CTR", - 16, - 16, - &aes_info -}; - -const cipher_info_t aes_256_ctr_info = { - POLARSSL_CIPHER_AES_256_CTR, - POLARSSL_MODE_CTR, - 256, - "AES-256-CTR", - 16, - 16, - &aes_info -}; -#endif /* POLARSSL_CIPHER_MODE_CTR */ - -#endif - -#if defined(POLARSSL_CAMELLIA_C) - -int camellia_crypt_cbc_wrap( void *ctx, operation_t operation, size_t length, - unsigned char *iv, const unsigned char *input, unsigned char *output ) -{ - return camellia_crypt_cbc( (camellia_context *) ctx, operation, length, iv, input, output ); -} - -int camellia_crypt_cfb128_wrap( void *ctx, operation_t operation, size_t length, - size_t *iv_off, unsigned char *iv, const unsigned char *input, unsigned char *output ) -{ -#if defined(POLARSSL_CIPHER_MODE_CFB) - return camellia_crypt_cfb128( (camellia_context *) ctx, operation, length, iv_off, iv, input, output ); -#else - ((void) ctx); - ((void) operation); - ((void) length); - ((void) iv_off); - ((void) iv); - ((void) input); - ((void) output); - - return POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE; -#endif -} - -int camellia_crypt_ctr_wrap( void *ctx, size_t length, - size_t *nc_off, unsigned char *nonce_counter, unsigned char *stream_block, - const unsigned char *input, unsigned char *output ) -{ -#if defined(POLARSSL_CIPHER_MODE_CTR) - return camellia_crypt_ctr( (camellia_context *) ctx, length, nc_off, nonce_counter, - stream_block, input, output ); -#else - ((void) ctx); - ((void) length); - ((void) nc_off); - ((void) nonce_counter); - ((void) stream_block); - ((void) input); - ((void) output); - - return POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE; -#endif -} - -int camellia_setkey_dec_wrap( void *ctx, const unsigned char *key, unsigned int key_length ) -{ - return camellia_setkey_dec( (camellia_context *) ctx, key, key_length ); -} - -int camellia_setkey_enc_wrap( void *ctx, const unsigned char *key, unsigned int key_length ) -{ - return camellia_setkey_enc( (camellia_context *) ctx, key, key_length ); -} - -static void * camellia_ctx_alloc( void ) -{ - return malloc( sizeof( camellia_context ) ); -} - -static void camellia_ctx_free( void *ctx ) -{ - free( ctx ); -} - -const cipher_base_t camellia_info = { - POLARSSL_CIPHER_ID_CAMELLIA, - camellia_crypt_cbc_wrap, - camellia_crypt_cfb128_wrap, - camellia_crypt_ctr_wrap, - camellia_setkey_enc_wrap, - camellia_setkey_dec_wrap, - camellia_ctx_alloc, - camellia_ctx_free -}; - -const cipher_info_t camellia_128_cbc_info = { - POLARSSL_CIPHER_CAMELLIA_128_CBC, - POLARSSL_MODE_CBC, - 128, - "CAMELLIA-128-CBC", - 16, - 16, - &camellia_info -}; - -const cipher_info_t camellia_192_cbc_info = { - POLARSSL_CIPHER_CAMELLIA_192_CBC, - POLARSSL_MODE_CBC, - 192, - "CAMELLIA-192-CBC", - 16, - 16, - &camellia_info -}; - -const cipher_info_t camellia_256_cbc_info = { - POLARSSL_CIPHER_CAMELLIA_256_CBC, - POLARSSL_MODE_CBC, - 256, - "CAMELLIA-256-CBC", - 16, - 16, - &camellia_info -}; - -#if defined(POLARSSL_CIPHER_MODE_CFB) -const cipher_info_t camellia_128_cfb128_info = { - POLARSSL_CIPHER_CAMELLIA_128_CFB128, - POLARSSL_MODE_CFB, - 128, - "CAMELLIA-128-CFB128", - 16, - 16, - &camellia_info -}; - -const cipher_info_t camellia_192_cfb128_info = { - POLARSSL_CIPHER_CAMELLIA_192_CFB128, - POLARSSL_MODE_CFB, - 192, - "CAMELLIA-192-CFB128", - 16, - 16, - &camellia_info -}; - -const cipher_info_t camellia_256_cfb128_info = { - POLARSSL_CIPHER_CAMELLIA_256_CFB128, - POLARSSL_MODE_CFB, - 256, - "CAMELLIA-256-CFB128", - 16, - 16, - &camellia_info -}; -#endif /* POLARSSL_CIPHER_MODE_CFB */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) -const cipher_info_t camellia_128_ctr_info = { - POLARSSL_CIPHER_CAMELLIA_128_CTR, - POLARSSL_MODE_CTR, - 128, - "CAMELLIA-128-CTR", - 16, - 16, - &camellia_info -}; - -const cipher_info_t camellia_192_ctr_info = { - POLARSSL_CIPHER_CAMELLIA_192_CTR, - POLARSSL_MODE_CTR, - 192, - "CAMELLIA-192-CTR", - 16, - 16, - &camellia_info -}; - -const cipher_info_t camellia_256_ctr_info = { - POLARSSL_CIPHER_CAMELLIA_256_CTR, - POLARSSL_MODE_CTR, - 256, - "CAMELLIA-256-CTR", - 16, - 16, - &camellia_info -}; -#endif /* POLARSSL_CIPHER_MODE_CTR */ - -#endif - -#if defined(POLARSSL_DES_C) - -int des_crypt_cbc_wrap( void *ctx, operation_t operation, size_t length, - unsigned char *iv, const unsigned char *input, unsigned char *output ) -{ - return des_crypt_cbc( (des_context *) ctx, operation, length, iv, input, output ); -} - -int des3_crypt_cbc_wrap( void *ctx, operation_t operation, size_t length, - unsigned char *iv, const unsigned char *input, unsigned char *output ) -{ - return des3_crypt_cbc( (des3_context *) ctx, operation, length, iv, input, output ); -} - -int des_crypt_cfb128_wrap( void *ctx, operation_t operation, size_t length, - size_t *iv_off, unsigned char *iv, const unsigned char *input, unsigned char *output ) -{ - ((void) ctx); - ((void) operation); - ((void) length); - ((void) iv_off); - ((void) iv); - ((void) input); - ((void) output); - - return POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE; -} - -int des_crypt_ctr_wrap( void *ctx, size_t length, - size_t *nc_off, unsigned char *nonce_counter, unsigned char *stream_block, - const unsigned char *input, unsigned char *output ) -{ - ((void) ctx); - ((void) length); - ((void) nc_off); - ((void) nonce_counter); - ((void) stream_block); - ((void) input); - ((void) output); - - return POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE; -} - - -int des_setkey_dec_wrap( void *ctx, const unsigned char *key, unsigned int key_length ) -{ - ((void) key_length); - - return des_setkey_dec( (des_context *) ctx, key ); -} - -int des_setkey_enc_wrap( void *ctx, const unsigned char *key, unsigned int key_length ) -{ - ((void) key_length); - - return des_setkey_enc( (des_context *) ctx, key ); -} - -int des3_set2key_dec_wrap( void *ctx, const unsigned char *key, unsigned int key_length ) -{ - ((void) key_length); - - return des3_set2key_dec( (des3_context *) ctx, key ); -} - -int des3_set2key_enc_wrap( void *ctx, const unsigned char *key, unsigned int key_length ) -{ - ((void) key_length); - - return des3_set2key_enc( (des3_context *) ctx, key ); -} - -int des3_set3key_dec_wrap( void *ctx, const unsigned char *key, unsigned int key_length ) -{ - ((void) key_length); - - return des3_set3key_dec( (des3_context *) ctx, key ); -} - -int des3_set3key_enc_wrap( void *ctx, const unsigned char *key, unsigned int key_length ) -{ - ((void) key_length); - - return des3_set3key_enc( (des3_context *) ctx, key ); -} - -static void * des_ctx_alloc( void ) -{ - return malloc( sizeof( des_context ) ); -} - -static void * des3_ctx_alloc( void ) -{ - return malloc( sizeof( des3_context ) ); -} - -static void des_ctx_free( void *ctx ) -{ - free( ctx ); -} - -const cipher_base_t des_info = { - POLARSSL_CIPHER_ID_DES, - des_crypt_cbc_wrap, - des_crypt_cfb128_wrap, - des_crypt_ctr_wrap, - des_setkey_enc_wrap, - des_setkey_dec_wrap, - des_ctx_alloc, - des_ctx_free -}; - -const cipher_info_t des_cbc_info = { - POLARSSL_CIPHER_DES_CBC, - POLARSSL_MODE_CBC, - POLARSSL_KEY_LENGTH_DES, - "DES-CBC", - 8, - 8, - &des_info -}; - -const cipher_base_t des_ede_info = { - POLARSSL_CIPHER_ID_DES, - des3_crypt_cbc_wrap, - des_crypt_cfb128_wrap, - des_crypt_ctr_wrap, - des3_set2key_enc_wrap, - des3_set2key_dec_wrap, - des3_ctx_alloc, - des_ctx_free -}; - -const cipher_info_t des_ede_cbc_info = { - POLARSSL_CIPHER_DES_EDE_CBC, - POLARSSL_MODE_CBC, - POLARSSL_KEY_LENGTH_DES_EDE, - "DES-EDE-CBC", - 8, - 8, - &des_ede_info -}; - -const cipher_base_t des_ede3_info = { - POLARSSL_CIPHER_ID_DES, - des3_crypt_cbc_wrap, - des_crypt_cfb128_wrap, - des_crypt_ctr_wrap, - des3_set3key_enc_wrap, - des3_set3key_dec_wrap, - des3_ctx_alloc, - des_ctx_free -}; - -const cipher_info_t des_ede3_cbc_info = { - POLARSSL_CIPHER_DES_EDE3_CBC, - POLARSSL_MODE_CBC, - POLARSSL_KEY_LENGTH_DES_EDE3, - "DES-EDE3-CBC", - 8, - 8, - &des_ede3_info -}; -#endif - -#if defined(POLARSSL_BLOWFISH_C) - -int blowfish_crypt_cbc_wrap( void *ctx, operation_t operation, size_t length, - unsigned char *iv, const unsigned char *input, unsigned char *output ) -{ - return blowfish_crypt_cbc( (blowfish_context *) ctx, operation, length, iv, input, output ); -} - -int blowfish_crypt_cfb64_wrap( void *ctx, operation_t operation, size_t length, - size_t *iv_off, unsigned char *iv, const unsigned char *input, unsigned char *output ) -{ -#if defined(POLARSSL_CIPHER_MODE_CFB) - return blowfish_crypt_cfb64( (blowfish_context *) ctx, operation, length, iv_off, iv, input, output ); -#else - ((void) ctx); - ((void) operation); - ((void) length); - ((void) iv_off); - ((void) iv); - ((void) input); - ((void) output); - - return POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE; -#endif -} - -int blowfish_crypt_ctr_wrap( void *ctx, size_t length, - size_t *nc_off, unsigned char *nonce_counter, unsigned char *stream_block, - const unsigned char *input, unsigned char *output ) -{ -#if defined(POLARSSL_CIPHER_MODE_CTR) - return blowfish_crypt_ctr( (blowfish_context *) ctx, length, nc_off, nonce_counter, - stream_block, input, output ); -#else - ((void) ctx); - ((void) length); - ((void) nc_off); - ((void) nonce_counter); - ((void) stream_block); - ((void) input); - ((void) output); - - return POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE; -#endif -} - -int blowfish_setkey_dec_wrap( void *ctx, const unsigned char *key, unsigned int key_length ) -{ - return blowfish_setkey( (blowfish_context *) ctx, key, key_length ); -} - -int blowfish_setkey_enc_wrap( void *ctx, const unsigned char *key, unsigned int key_length ) -{ - return blowfish_setkey( (blowfish_context *) ctx, key, key_length ); -} - -static void * blowfish_ctx_alloc( void ) -{ - return malloc( sizeof( blowfish_context ) ); -} - -static void blowfish_ctx_free( void *ctx ) -{ - free( ctx ); -} - -const cipher_base_t blowfish_info = { - POLARSSL_CIPHER_ID_BLOWFISH, - blowfish_crypt_cbc_wrap, - blowfish_crypt_cfb64_wrap, - blowfish_crypt_ctr_wrap, - blowfish_setkey_enc_wrap, - blowfish_setkey_dec_wrap, - blowfish_ctx_alloc, - blowfish_ctx_free -}; - -const cipher_info_t blowfish_cbc_info = { - POLARSSL_CIPHER_BLOWFISH_CBC, - POLARSSL_MODE_CBC, - 128, - "BLOWFISH-CBC", - 8, - 8, - &blowfish_info -}; - -#if defined(POLARSSL_CIPHER_MODE_CFB) -const cipher_info_t blowfish_cfb64_info = { - POLARSSL_CIPHER_BLOWFISH_CFB64, - POLARSSL_MODE_CFB, - 128, - "BLOWFISH-CFB64", - 8, - 8, - &blowfish_info -}; -#endif /* POLARSSL_CIPHER_MODE_CFB */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) -const cipher_info_t blowfish_ctr_info = { - POLARSSL_CIPHER_BLOWFISH_CTR, - POLARSSL_MODE_CTR, - 128, - "BLOWFISH-CTR", - 8, - 8, - &blowfish_info -}; -#endif /* POLARSSL_CIPHER_MODE_CTR */ -#endif /* POLARSSL_BLOWFISH_C */ - -#if defined(POLARSSL_CIPHER_NULL_CIPHER) -static void * null_ctx_alloc( void ) -{ - return (void *) 1; -} - - -static void null_ctx_free( void *ctx ) -{ - ((void) ctx); -} - -const cipher_base_t null_base_info = { - POLARSSL_CIPHER_ID_NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - null_ctx_alloc, - null_ctx_free -}; - -const cipher_info_t null_cipher_info = { - POLARSSL_CIPHER_NULL, - POLARSSL_MODE_NULL, - 0, - "NULL", - 1, - 1, - &null_base_info -}; -#endif /* defined(POLARSSL_CIPHER_NULL_CIPHER) */ - -#endif diff --git a/makerom/polarssl/ctr_drbg.c b/makerom/polarssl/ctr_drbg.c deleted file mode 100644 index 8cf03712..00000000 --- a/makerom/polarssl/ctr_drbg.c +++ /dev/null @@ -1,562 +0,0 @@ -/* - * CTR_DRBG implementation based on AES-256 (NIST SP 800-90) - * - * Copyright (C) 2006-2011, 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 NIST SP 800-90 DRBGs are described in the following publucation. - * - * http://csrc.nist.gov/publications/nistpubs/800-90/SP800-90revised_March2007.pdf - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_CTR_DRBG_C) - -#include "polarssl/ctr_drbg.h" - -#if defined(POLARSSL_FS_IO) -#include -#endif - -/* - * Non-public function wrapped by ctr_crbg_init(). Necessary to allow NIST - * tests to succeed (which require known length fixed entropy) - */ -int ctr_drbg_init_entropy_len( - ctr_drbg_context *ctx, - int (*f_entropy)(void *, unsigned char *, size_t), - void *p_entropy, - const unsigned char *custom, - size_t len, - size_t entropy_len ) -{ - int ret; - unsigned char key[CTR_DRBG_KEYSIZE]; - - memset( ctx, 0, sizeof(ctr_drbg_context) ); - memset( key, 0, CTR_DRBG_KEYSIZE ); - - ctx->f_entropy = f_entropy; - ctx->p_entropy = p_entropy; - - ctx->entropy_len = entropy_len; - ctx->reseed_interval = CTR_DRBG_RESEED_INTERVAL; - - /* - * Initialize with an empty key - */ - aes_setkey_enc( &ctx->aes_ctx, key, CTR_DRBG_KEYBITS ); - - if( ( ret = ctr_drbg_reseed( ctx, custom, len ) ) != 0 ) - return( ret ); - - return( 0 ); -} - -int ctr_drbg_init( ctr_drbg_context *ctx, - int (*f_entropy)(void *, unsigned char *, size_t), - void *p_entropy, - const unsigned char *custom, - size_t len ) -{ - return( ctr_drbg_init_entropy_len( ctx, f_entropy, p_entropy, custom, len, - CTR_DRBG_ENTROPY_LEN ) ); -} - -void ctr_drbg_set_prediction_resistance( ctr_drbg_context *ctx, int resistance ) -{ - ctx->prediction_resistance = resistance; -} - -void ctr_drbg_set_entropy_len( ctr_drbg_context *ctx, size_t len ) -{ - ctx->entropy_len = len; -} - -void ctr_drbg_set_reseed_interval( ctr_drbg_context *ctx, int interval ) -{ - ctx->reseed_interval = interval; -} - -int block_cipher_df( unsigned char *output, - const unsigned char *data, size_t data_len ) -{ - unsigned char buf[CTR_DRBG_MAX_SEED_INPUT + CTR_DRBG_BLOCKSIZE + 16]; - unsigned char tmp[CTR_DRBG_SEEDLEN]; - unsigned char key[CTR_DRBG_KEYSIZE]; - unsigned char chain[CTR_DRBG_BLOCKSIZE]; - unsigned char *p = buf, *iv; - aes_context aes_ctx; - - int i, j, buf_len, use_len; - - memset( buf, 0, CTR_DRBG_MAX_SEED_INPUT + CTR_DRBG_BLOCKSIZE + 16 ); - - /* - * Construct IV (16 bytes) and S in buffer - * IV = Counter (in 32-bits) padded to 16 with zeroes - * S = Length input string (in 32-bits) || Length of output (in 32-bits) || - * data || 0x80 - * (Total is padded to a multiple of 16-bytes with zeroes) - */ - p = buf + CTR_DRBG_BLOCKSIZE; - *p++ = ( data_len >> 24 ) & 0xff; - *p++ = ( data_len >> 16 ) & 0xff; - *p++ = ( data_len >> 8 ) & 0xff; - *p++ = ( data_len ) & 0xff; - p += 3; - *p++ = CTR_DRBG_SEEDLEN; - memcpy( p, data, data_len ); - p[data_len] = 0x80; - - buf_len = CTR_DRBG_BLOCKSIZE + 8 + data_len + 1; - - for( i = 0; i < CTR_DRBG_KEYSIZE; i++ ) - key[i] = i; - - aes_setkey_enc( &aes_ctx, key, CTR_DRBG_KEYBITS ); - - /* - * Reduce data to POLARSSL_CTR_DRBG_SEEDLEN bytes of data - */ - for( j = 0; j < CTR_DRBG_SEEDLEN; j += CTR_DRBG_BLOCKSIZE ) - { - p = buf; - memset( chain, 0, CTR_DRBG_BLOCKSIZE ); - use_len = buf_len; - - while( use_len > 0 ) - { - for( i = 0; i < CTR_DRBG_BLOCKSIZE; i++ ) - chain[i] ^= p[i]; - p += CTR_DRBG_BLOCKSIZE; - use_len -= CTR_DRBG_BLOCKSIZE; - - aes_crypt_ecb( &aes_ctx, AES_ENCRYPT, chain, chain ); - } - - memcpy( tmp + j, chain, CTR_DRBG_BLOCKSIZE ); - - /* - * Update IV - */ - buf[3]++; - } - - /* - * Do final encryption with reduced data - */ - aes_setkey_enc( &aes_ctx, tmp, CTR_DRBG_KEYBITS ); - iv = tmp + CTR_DRBG_KEYSIZE; - p = output; - - for( j = 0; j < CTR_DRBG_SEEDLEN; j += CTR_DRBG_BLOCKSIZE ) - { - aes_crypt_ecb( &aes_ctx, AES_ENCRYPT, iv, iv ); - memcpy( p, iv, CTR_DRBG_BLOCKSIZE ); - p += CTR_DRBG_BLOCKSIZE; - } - - return( 0 ); -} - -int ctr_drbg_update_internal( ctr_drbg_context *ctx, - const unsigned char data[CTR_DRBG_SEEDLEN] ) -{ - unsigned char tmp[CTR_DRBG_SEEDLEN]; - unsigned char *p = tmp; - int i, j; - - memset( tmp, 0, CTR_DRBG_SEEDLEN ); - - for( j = 0; j < CTR_DRBG_SEEDLEN; j += CTR_DRBG_BLOCKSIZE ) - { - /* - * Increase counter - */ - for( i = CTR_DRBG_BLOCKSIZE; i > 0; i-- ) - if( ++ctx->counter[i - 1] != 0 ) - break; - - /* - * Crypt counter block - */ - aes_crypt_ecb( &ctx->aes_ctx, AES_ENCRYPT, ctx->counter, p ); - - p += CTR_DRBG_BLOCKSIZE; - } - - for( i = 0; i < CTR_DRBG_SEEDLEN; i++ ) - tmp[i] ^= data[i]; - - /* - * Update key and counter - */ - aes_setkey_enc( &ctx->aes_ctx, tmp, CTR_DRBG_KEYBITS ); - memcpy( ctx->counter, tmp + CTR_DRBG_KEYSIZE, CTR_DRBG_BLOCKSIZE ); - - return( 0 ); -} - -void ctr_drbg_update( ctr_drbg_context *ctx, - const unsigned char *additional, size_t add_len ) -{ - unsigned char add_input[CTR_DRBG_SEEDLEN]; - - if( add_len > 0 ) - { - block_cipher_df( add_input, additional, add_len ); - ctr_drbg_update_internal( ctx, add_input ); - } -} - -int ctr_drbg_reseed( ctr_drbg_context *ctx, - const unsigned char *additional, size_t len ) -{ - unsigned char seed[CTR_DRBG_MAX_SEED_INPUT]; - size_t seedlen = 0; - - if( ctx->entropy_len + len > CTR_DRBG_MAX_SEED_INPUT ) - return( POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG ); - - memset( seed, 0, CTR_DRBG_MAX_SEED_INPUT ); - - /* - * Gather enropy_len bytes of entropy to seed state - */ - if( 0 != ctx->f_entropy( ctx->p_entropy, seed, - ctx->entropy_len ) ) - { - return( POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED ); - } - - seedlen += ctx->entropy_len; - - /* - * Add additional data - */ - if( additional && len ) - { - memcpy( seed + seedlen, additional, len ); - seedlen += len; - } - - /* - * Reduce to 384 bits - */ - block_cipher_df( seed, seed, seedlen ); - - /* - * Update state - */ - ctr_drbg_update_internal( ctx, seed ); - ctx->reseed_counter = 1; - - return( 0 ); -} - -int ctr_drbg_random_with_add( void *p_rng, - unsigned char *output, size_t output_len, - const unsigned char *additional, size_t add_len ) -{ - int ret = 0; - ctr_drbg_context *ctx = (ctr_drbg_context *) p_rng; - unsigned char add_input[CTR_DRBG_SEEDLEN]; - unsigned char *p = output; - unsigned char tmp[CTR_DRBG_BLOCKSIZE]; - int i; - size_t use_len; - - if( output_len > CTR_DRBG_MAX_REQUEST ) - return( POLARSSL_ERR_CTR_DRBG_REQUEST_TOO_BIG ); - - if( add_len > CTR_DRBG_MAX_INPUT ) - return( POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG ); - - memset( add_input, 0, CTR_DRBG_SEEDLEN ); - - if( ctx->reseed_counter > ctx->reseed_interval || - ctx->prediction_resistance ) - { - if( ( ret = ctr_drbg_reseed( ctx, additional, add_len ) ) != 0 ) - return( ret ); - - add_len = 0; - } - - if( add_len > 0 ) - { - block_cipher_df( add_input, additional, add_len ); - ctr_drbg_update_internal( ctx, add_input ); - } - - while( output_len > 0 ) - { - /* - * Increase counter - */ - for( i = CTR_DRBG_BLOCKSIZE; i > 0; i-- ) - if( ++ctx->counter[i - 1] != 0 ) - break; - - /* - * Crypt counter block - */ - aes_crypt_ecb( &ctx->aes_ctx, AES_ENCRYPT, ctx->counter, tmp ); - - use_len = (output_len > CTR_DRBG_BLOCKSIZE ) ? CTR_DRBG_BLOCKSIZE : output_len; - /* - * Copy random block to destination - */ - memcpy( p, tmp, use_len ); - p += use_len; - output_len -= use_len; - } - - ctr_drbg_update_internal( ctx, add_input ); - - ctx->reseed_counter++; - - return( 0 ); -} - -int ctr_drbg_random( void *p_rng, unsigned char *output, size_t output_len ) -{ - return ctr_drbg_random_with_add( p_rng, output, output_len, NULL, 0 ); -} - -#if defined(POLARSSL_FS_IO) -int ctr_drbg_write_seed_file( ctr_drbg_context *ctx, const char *path ) -{ - int ret; - FILE *f; - unsigned char buf[ CTR_DRBG_MAX_INPUT ]; - - if( ( f = fopen( path, "wb" ) ) == NULL ) - return( POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR ); - - if( ( ret = ctr_drbg_random( ctx, buf, CTR_DRBG_MAX_INPUT ) ) != 0 ) - return( ret ); - - if( fwrite( buf, 1, CTR_DRBG_MAX_INPUT, f ) != CTR_DRBG_MAX_INPUT ) - { - fclose( f ); - return( POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR ); - } - - fclose( f ); - return( 0 ); -} - -int ctr_drbg_update_seed_file( ctr_drbg_context *ctx, const char *path ) -{ - FILE *f; - size_t n; - unsigned char buf[ CTR_DRBG_MAX_INPUT ]; - - if( ( f = fopen( path, "rb" ) ) == NULL ) - return( POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR ); - - fseek( f, 0, SEEK_END ); - n = (size_t) ftell( f ); - fseek( f, 0, SEEK_SET ); - - if( n > CTR_DRBG_MAX_INPUT ) - return( POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG ); - - if( fread( buf, 1, n, f ) != n ) - { - fclose( f ); - return( POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR ); - } - - ctr_drbg_update( ctx, buf, n ); - - fclose( f ); - - return( ctr_drbg_write_seed_file( ctx, path ) ); -} -#endif /* POLARSSL_FS_IO */ - -#if defined(POLARSSL_SELF_TEST) - -#include - -unsigned char entropy_source_pr[96] = - { 0xc1, 0x80, 0x81, 0xa6, 0x5d, 0x44, 0x02, 0x16, - 0x19, 0xb3, 0xf1, 0x80, 0xb1, 0xc9, 0x20, 0x02, - 0x6a, 0x54, 0x6f, 0x0c, 0x70, 0x81, 0x49, 0x8b, - 0x6e, 0xa6, 0x62, 0x52, 0x6d, 0x51, 0xb1, 0xcb, - 0x58, 0x3b, 0xfa, 0xd5, 0x37, 0x5f, 0xfb, 0xc9, - 0xff, 0x46, 0xd2, 0x19, 0xc7, 0x22, 0x3e, 0x95, - 0x45, 0x9d, 0x82, 0xe1, 0xe7, 0x22, 0x9f, 0x63, - 0x31, 0x69, 0xd2, 0x6b, 0x57, 0x47, 0x4f, 0xa3, - 0x37, 0xc9, 0x98, 0x1c, 0x0b, 0xfb, 0x91, 0x31, - 0x4d, 0x55, 0xb9, 0xe9, 0x1c, 0x5a, 0x5e, 0xe4, - 0x93, 0x92, 0xcf, 0xc5, 0x23, 0x12, 0xd5, 0x56, - 0x2c, 0x4a, 0x6e, 0xff, 0xdc, 0x10, 0xd0, 0x68 }; - -unsigned char entropy_source_nopr[64] = - { 0x5a, 0x19, 0x4d, 0x5e, 0x2b, 0x31, 0x58, 0x14, - 0x54, 0xde, 0xf6, 0x75, 0xfb, 0x79, 0x58, 0xfe, - 0xc7, 0xdb, 0x87, 0x3e, 0x56, 0x89, 0xfc, 0x9d, - 0x03, 0x21, 0x7c, 0x68, 0xd8, 0x03, 0x38, 0x20, - 0xf9, 0xe6, 0x5e, 0x04, 0xd8, 0x56, 0xf3, 0xa9, - 0xc4, 0x4a, 0x4c, 0xbd, 0xc1, 0xd0, 0x08, 0x46, - 0xf5, 0x98, 0x3d, 0x77, 0x1c, 0x1b, 0x13, 0x7e, - 0x4e, 0x0f, 0x9d, 0x8e, 0xf4, 0x09, 0xf9, 0x2e }; - -unsigned char nonce_pers_pr[16] = - { 0xd2, 0x54, 0xfc, 0xff, 0x02, 0x1e, 0x69, 0xd2, - 0x29, 0xc9, 0xcf, 0xad, 0x85, 0xfa, 0x48, 0x6c }; - -unsigned char nonce_pers_nopr[16] = - { 0x1b, 0x54, 0xb8, 0xff, 0x06, 0x42, 0xbf, 0xf5, - 0x21, 0xf1, 0x5c, 0x1c, 0x0b, 0x66, 0x5f, 0x3f }; - -unsigned char result_pr[16] = - { 0x34, 0x01, 0x16, 0x56, 0xb4, 0x29, 0x00, 0x8f, - 0x35, 0x63, 0xec, 0xb5, 0xf2, 0x59, 0x07, 0x23 }; - -unsigned char result_nopr[16] = - { 0xa0, 0x54, 0x30, 0x3d, 0x8a, 0x7e, 0xa9, 0x88, - 0x9d, 0x90, 0x3e, 0x07, 0x7c, 0x6f, 0x21, 0x8f }; - -int test_offset; -int ctr_drbg_self_test_entropy( void *data, unsigned char *buf, size_t len ) -{ - unsigned char *p = data; - memcpy( buf, p + test_offset, len ); - test_offset += 32; - return( 0 ); -} - -/* - * Checkup routine - */ -int ctr_drbg_self_test( int verbose ) -{ - ctr_drbg_context ctx; - unsigned char buf[16]; - - /* - * Based on a NIST CTR_DRBG test vector (PR = True) - */ - if( verbose != 0 ) - printf( " CTR_DRBG (PR = TRUE) : " ); - - test_offset = 0; - if( ctr_drbg_init_entropy_len( &ctx, ctr_drbg_self_test_entropy, entropy_source_pr, nonce_pers_pr, 16, 32 ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - ctr_drbg_set_prediction_resistance( &ctx, CTR_DRBG_PR_ON ); - - if( ctr_drbg_random( &ctx, buf, CTR_DRBG_BLOCKSIZE ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( ctr_drbg_random( &ctx, buf, CTR_DRBG_BLOCKSIZE ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( memcmp( buf, result_pr, CTR_DRBG_BLOCKSIZE ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( verbose != 0 ) - printf( "passed\n" ); - - /* - * Based on a NIST CTR_DRBG test vector (PR = FALSE) - */ - if( verbose != 0 ) - printf( " CTR_DRBG (PR = FALSE): " ); - - test_offset = 0; - if( ctr_drbg_init_entropy_len( &ctx, ctr_drbg_self_test_entropy, entropy_source_nopr, nonce_pers_nopr, 16, 32 ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( ctr_drbg_random( &ctx, buf, 16 ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( ctr_drbg_reseed( &ctx, NULL, 0 ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( ctr_drbg_random( &ctx, buf, 16 ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( memcmp( buf, result_nopr, 16 ) != 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/makerom/polarssl/debug.c b/makerom/polarssl/debug.c deleted file mode 100644 index 81ee6490..00000000 --- a/makerom/polarssl/debug.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Debugging routines - * - * 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_DEBUG_C) - -#include "polarssl/debug.h" - -#include -#include - -#if defined _MSC_VER && !defined snprintf -#define snprintf _snprintf -#endif - -#if defined _MSC_VER && !defined vsnprintf -#define vsnprintf _vsnprintf -#endif - -char *debug_fmt( const char *format, ... ) -{ - va_list argp; - static char str[512]; - int maxlen = sizeof( str ) - 1; - - va_start( argp, format ); - vsnprintf( str, maxlen, format, argp ); - va_end( argp ); - - str[maxlen] = '\0'; - return( str ); -} - -void debug_print_msg( const ssl_context *ssl, int level, - const char *file, int line, const char *text ) -{ - char str[512]; - int maxlen = sizeof( str ) - 1; - - if( ssl->f_dbg == NULL ) - return; - - snprintf( str, maxlen, "%s(%04d): %s\n", file, line, text ); - str[maxlen] = '\0'; - ssl->f_dbg( ssl->p_dbg, level, str ); -} - -void debug_print_ret( const ssl_context *ssl, int level, - const char *file, int line, - const char *text, int ret ) -{ - char str[512]; - int maxlen = sizeof( str ) - 1; - - if( ssl->f_dbg == NULL ) - return; - - snprintf( str, maxlen, "%s(%04d): %s() returned %d (0x%x)\n", - file, line, text, ret, ret ); - - str[maxlen] = '\0'; - ssl->f_dbg( ssl->p_dbg, level, str ); -} - -void debug_print_buf( const ssl_context *ssl, int level, - const char *file, int line, const char *text, - unsigned char *buf, size_t len ) -{ - char str[512]; - size_t i, maxlen = sizeof( str ) - 1; - - if( ssl->f_dbg == NULL ) - return; - - snprintf( str, maxlen, "%s(%04d): dumping '%s' (%d bytes)\n", - file, line, text, (unsigned int) len ); - - str[maxlen] = '\0'; - ssl->f_dbg( ssl->p_dbg, level, str ); - - for( i = 0; i < len; i++ ) - { - if( i >= 4096 ) - break; - - if( i % 16 == 0 ) - { - if( i > 0 ) - ssl->f_dbg( ssl->p_dbg, level, "\n" ); - - snprintf( str, maxlen, "%s(%04d): %04x: ", file, line, - (unsigned int) i ); - - str[maxlen] = '\0'; - ssl->f_dbg( ssl->p_dbg, level, str ); - } - - snprintf( str, maxlen, " %02x", (unsigned int) buf[i] ); - - str[maxlen] = '\0'; - ssl->f_dbg( ssl->p_dbg, level, str ); - } - - if( len > 0 ) - ssl->f_dbg( ssl->p_dbg, level, "\n" ); -} - -void debug_print_mpi( const ssl_context *ssl, int level, - const char *file, int line, - const char *text, const mpi *X ) -{ - char str[512]; - int j, k, maxlen = sizeof( str ) - 1, zeros = 1; - size_t i, n; - - if( ssl->f_dbg == NULL || X == NULL ) - return; - - for( n = X->n - 1; n > 0; n-- ) - if( X->p[n] != 0 ) - break; - - for( j = ( sizeof(t_uint) << 3 ) - 1; j >= 0; j-- ) - if( ( ( X->p[n] >> j ) & 1 ) != 0 ) - break; - - snprintf( str, maxlen, "%s(%04d): value of '%s' (%d bits) is:\n", - file, line, text, - (int) ( ( n * ( sizeof(t_uint) << 3 ) ) + j + 1 ) ); - - str[maxlen] = '\0'; - ssl->f_dbg( ssl->p_dbg, level, str ); - - for( i = n + 1, j = 0; i > 0; i-- ) - { - if( zeros && X->p[i - 1] == 0 ) - continue; - - for( k = sizeof( t_uint ) - 1; k >= 0; k-- ) - { - if( zeros && ( ( X->p[i - 1] >> (k << 3) ) & 0xFF ) == 0 ) - continue; - else - zeros = 0; - - if( j % 16 == 0 ) - { - if( j > 0 ) - ssl->f_dbg( ssl->p_dbg, level, "\n" ); - - snprintf( str, maxlen, "%s(%04d): ", file, line ); - - str[maxlen] = '\0'; - ssl->f_dbg( ssl->p_dbg, level, str ); - } - - snprintf( str, maxlen, " %02x", (unsigned int) - ( X->p[i - 1] >> (k << 3) ) & 0xFF ); - - str[maxlen] = '\0'; - ssl->f_dbg( ssl->p_dbg, level, str ); - - j++; - } - - } - - if( zeros == 1 ) - { - snprintf( str, maxlen, "%s(%04d): ", file, line ); - - str[maxlen] = '\0'; - ssl->f_dbg( ssl->p_dbg, level, str ); - ssl->f_dbg( ssl->p_dbg, level, " 00" ); - } - - ssl->f_dbg( ssl->p_dbg, level, "\n" ); -} - -void debug_print_crt( const ssl_context *ssl, int level, - const char *file, int line, - const char *text, const x509_cert *crt ) -{ - char str[1024], prefix[64]; - int i = 0, maxlen = sizeof( prefix ) - 1; - - if( ssl->f_dbg == NULL || crt == NULL ) - return; - - snprintf( prefix, maxlen, "%s(%04d): ", file, line ); - prefix[maxlen] = '\0'; - maxlen = sizeof( str ) - 1; - - while( crt != NULL ) - { - char buf[1024]; - x509parse_cert_info( buf, sizeof( buf ) - 1, prefix, crt ); - - snprintf( str, maxlen, "%s(%04d): %s #%d:\n%s", - file, line, text, ++i, buf ); - - str[maxlen] = '\0'; - ssl->f_dbg( ssl->p_dbg, level, str ); - - debug_print_mpi( ssl, level, file, line, - "crt->rsa.N", &crt->rsa.N ); - - debug_print_mpi( ssl, level, file, line, - "crt->rsa.E", &crt->rsa.E ); - - crt = crt->next; - } -} - -#endif diff --git a/makerom/polarssl/des.c b/makerom/polarssl/des.c deleted file mode 100644 index 0cf4b3d5..00000000 --- a/makerom/polarssl/des.c +++ /dev/null @@ -1,997 +0,0 @@ -/* - * FIPS-46-3 compliant Triple-DES implementation - * - * Copyright (C) 2006-2013, 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. - */ -/* - * DES, on which TDES is based, was originally designed by Horst Feistel - * at IBM in 1974, and was adopted as a standard by NIST (formerly NBS). - * - * http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_DES_C) - -#include "polarssl/des.h" - -#if !defined(POLARSSL_DES_ALT) - -/* - * 32-bit integer manipulation macros (big endian) - */ -#ifndef GET_UINT32_BE -#define GET_UINT32_BE(n,b,i) \ -{ \ - (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ - | ( (uint32_t) (b)[(i) + 1] << 16 ) \ - | ( (uint32_t) (b)[(i) + 2] << 8 ) \ - | ( (uint32_t) (b)[(i) + 3] ); \ -} -#endif - -#ifndef PUT_UINT32_BE -#define PUT_UINT32_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 - -/* - * Expanded DES S-boxes - */ -static const uint32_t SB1[64] = -{ - 0x01010400, 0x00000000, 0x00010000, 0x01010404, - 0x01010004, 0x00010404, 0x00000004, 0x00010000, - 0x00000400, 0x01010400, 0x01010404, 0x00000400, - 0x01000404, 0x01010004, 0x01000000, 0x00000004, - 0x00000404, 0x01000400, 0x01000400, 0x00010400, - 0x00010400, 0x01010000, 0x01010000, 0x01000404, - 0x00010004, 0x01000004, 0x01000004, 0x00010004, - 0x00000000, 0x00000404, 0x00010404, 0x01000000, - 0x00010000, 0x01010404, 0x00000004, 0x01010000, - 0x01010400, 0x01000000, 0x01000000, 0x00000400, - 0x01010004, 0x00010000, 0x00010400, 0x01000004, - 0x00000400, 0x00000004, 0x01000404, 0x00010404, - 0x01010404, 0x00010004, 0x01010000, 0x01000404, - 0x01000004, 0x00000404, 0x00010404, 0x01010400, - 0x00000404, 0x01000400, 0x01000400, 0x00000000, - 0x00010004, 0x00010400, 0x00000000, 0x01010004 -}; - -static const uint32_t SB2[64] = -{ - 0x80108020, 0x80008000, 0x00008000, 0x00108020, - 0x00100000, 0x00000020, 0x80100020, 0x80008020, - 0x80000020, 0x80108020, 0x80108000, 0x80000000, - 0x80008000, 0x00100000, 0x00000020, 0x80100020, - 0x00108000, 0x00100020, 0x80008020, 0x00000000, - 0x80000000, 0x00008000, 0x00108020, 0x80100000, - 0x00100020, 0x80000020, 0x00000000, 0x00108000, - 0x00008020, 0x80108000, 0x80100000, 0x00008020, - 0x00000000, 0x00108020, 0x80100020, 0x00100000, - 0x80008020, 0x80100000, 0x80108000, 0x00008000, - 0x80100000, 0x80008000, 0x00000020, 0x80108020, - 0x00108020, 0x00000020, 0x00008000, 0x80000000, - 0x00008020, 0x80108000, 0x00100000, 0x80000020, - 0x00100020, 0x80008020, 0x80000020, 0x00100020, - 0x00108000, 0x00000000, 0x80008000, 0x00008020, - 0x80000000, 0x80100020, 0x80108020, 0x00108000 -}; - -static const uint32_t SB3[64] = -{ - 0x00000208, 0x08020200, 0x00000000, 0x08020008, - 0x08000200, 0x00000000, 0x00020208, 0x08000200, - 0x00020008, 0x08000008, 0x08000008, 0x00020000, - 0x08020208, 0x00020008, 0x08020000, 0x00000208, - 0x08000000, 0x00000008, 0x08020200, 0x00000200, - 0x00020200, 0x08020000, 0x08020008, 0x00020208, - 0x08000208, 0x00020200, 0x00020000, 0x08000208, - 0x00000008, 0x08020208, 0x00000200, 0x08000000, - 0x08020200, 0x08000000, 0x00020008, 0x00000208, - 0x00020000, 0x08020200, 0x08000200, 0x00000000, - 0x00000200, 0x00020008, 0x08020208, 0x08000200, - 0x08000008, 0x00000200, 0x00000000, 0x08020008, - 0x08000208, 0x00020000, 0x08000000, 0x08020208, - 0x00000008, 0x00020208, 0x00020200, 0x08000008, - 0x08020000, 0x08000208, 0x00000208, 0x08020000, - 0x00020208, 0x00000008, 0x08020008, 0x00020200 -}; - -static const uint32_t SB4[64] = -{ - 0x00802001, 0x00002081, 0x00002081, 0x00000080, - 0x00802080, 0x00800081, 0x00800001, 0x00002001, - 0x00000000, 0x00802000, 0x00802000, 0x00802081, - 0x00000081, 0x00000000, 0x00800080, 0x00800001, - 0x00000001, 0x00002000, 0x00800000, 0x00802001, - 0x00000080, 0x00800000, 0x00002001, 0x00002080, - 0x00800081, 0x00000001, 0x00002080, 0x00800080, - 0x00002000, 0x00802080, 0x00802081, 0x00000081, - 0x00800080, 0x00800001, 0x00802000, 0x00802081, - 0x00000081, 0x00000000, 0x00000000, 0x00802000, - 0x00002080, 0x00800080, 0x00800081, 0x00000001, - 0x00802001, 0x00002081, 0x00002081, 0x00000080, - 0x00802081, 0x00000081, 0x00000001, 0x00002000, - 0x00800001, 0x00002001, 0x00802080, 0x00800081, - 0x00002001, 0x00002080, 0x00800000, 0x00802001, - 0x00000080, 0x00800000, 0x00002000, 0x00802080 -}; - -static const uint32_t SB5[64] = -{ - 0x00000100, 0x02080100, 0x02080000, 0x42000100, - 0x00080000, 0x00000100, 0x40000000, 0x02080000, - 0x40080100, 0x00080000, 0x02000100, 0x40080100, - 0x42000100, 0x42080000, 0x00080100, 0x40000000, - 0x02000000, 0x40080000, 0x40080000, 0x00000000, - 0x40000100, 0x42080100, 0x42080100, 0x02000100, - 0x42080000, 0x40000100, 0x00000000, 0x42000000, - 0x02080100, 0x02000000, 0x42000000, 0x00080100, - 0x00080000, 0x42000100, 0x00000100, 0x02000000, - 0x40000000, 0x02080000, 0x42000100, 0x40080100, - 0x02000100, 0x40000000, 0x42080000, 0x02080100, - 0x40080100, 0x00000100, 0x02000000, 0x42080000, - 0x42080100, 0x00080100, 0x42000000, 0x42080100, - 0x02080000, 0x00000000, 0x40080000, 0x42000000, - 0x00080100, 0x02000100, 0x40000100, 0x00080000, - 0x00000000, 0x40080000, 0x02080100, 0x40000100 -}; - -static const uint32_t SB6[64] = -{ - 0x20000010, 0x20400000, 0x00004000, 0x20404010, - 0x20400000, 0x00000010, 0x20404010, 0x00400000, - 0x20004000, 0x00404010, 0x00400000, 0x20000010, - 0x00400010, 0x20004000, 0x20000000, 0x00004010, - 0x00000000, 0x00400010, 0x20004010, 0x00004000, - 0x00404000, 0x20004010, 0x00000010, 0x20400010, - 0x20400010, 0x00000000, 0x00404010, 0x20404000, - 0x00004010, 0x00404000, 0x20404000, 0x20000000, - 0x20004000, 0x00000010, 0x20400010, 0x00404000, - 0x20404010, 0x00400000, 0x00004010, 0x20000010, - 0x00400000, 0x20004000, 0x20000000, 0x00004010, - 0x20000010, 0x20404010, 0x00404000, 0x20400000, - 0x00404010, 0x20404000, 0x00000000, 0x20400010, - 0x00000010, 0x00004000, 0x20400000, 0x00404010, - 0x00004000, 0x00400010, 0x20004010, 0x00000000, - 0x20404000, 0x20000000, 0x00400010, 0x20004010 -}; - -static const uint32_t SB7[64] = -{ - 0x00200000, 0x04200002, 0x04000802, 0x00000000, - 0x00000800, 0x04000802, 0x00200802, 0x04200800, - 0x04200802, 0x00200000, 0x00000000, 0x04000002, - 0x00000002, 0x04000000, 0x04200002, 0x00000802, - 0x04000800, 0x00200802, 0x00200002, 0x04000800, - 0x04000002, 0x04200000, 0x04200800, 0x00200002, - 0x04200000, 0x00000800, 0x00000802, 0x04200802, - 0x00200800, 0x00000002, 0x04000000, 0x00200800, - 0x04000000, 0x00200800, 0x00200000, 0x04000802, - 0x04000802, 0x04200002, 0x04200002, 0x00000002, - 0x00200002, 0x04000000, 0x04000800, 0x00200000, - 0x04200800, 0x00000802, 0x00200802, 0x04200800, - 0x00000802, 0x04000002, 0x04200802, 0x04200000, - 0x00200800, 0x00000000, 0x00000002, 0x04200802, - 0x00000000, 0x00200802, 0x04200000, 0x00000800, - 0x04000002, 0x04000800, 0x00000800, 0x00200002 -}; - -static const uint32_t SB8[64] = -{ - 0x10001040, 0x00001000, 0x00040000, 0x10041040, - 0x10000000, 0x10001040, 0x00000040, 0x10000000, - 0x00040040, 0x10040000, 0x10041040, 0x00041000, - 0x10041000, 0x00041040, 0x00001000, 0x00000040, - 0x10040000, 0x10000040, 0x10001000, 0x00001040, - 0x00041000, 0x00040040, 0x10040040, 0x10041000, - 0x00001040, 0x00000000, 0x00000000, 0x10040040, - 0x10000040, 0x10001000, 0x00041040, 0x00040000, - 0x00041040, 0x00040000, 0x10041000, 0x00001000, - 0x00000040, 0x10040040, 0x00001000, 0x00041040, - 0x10001000, 0x00000040, 0x10000040, 0x10040000, - 0x10040040, 0x10000000, 0x00040000, 0x10001040, - 0x00000000, 0x10041040, 0x00040040, 0x10000040, - 0x10040000, 0x10001000, 0x10001040, 0x00000000, - 0x10041040, 0x00041000, 0x00041000, 0x00001040, - 0x00001040, 0x00040040, 0x10000000, 0x10041000 -}; - -/* - * PC1: left and right halves bit-swap - */ -static const uint32_t LHs[16] = -{ - 0x00000000, 0x00000001, 0x00000100, 0x00000101, - 0x00010000, 0x00010001, 0x00010100, 0x00010101, - 0x01000000, 0x01000001, 0x01000100, 0x01000101, - 0x01010000, 0x01010001, 0x01010100, 0x01010101 -}; - -static const uint32_t RHs[16] = -{ - 0x00000000, 0x01000000, 0x00010000, 0x01010000, - 0x00000100, 0x01000100, 0x00010100, 0x01010100, - 0x00000001, 0x01000001, 0x00010001, 0x01010001, - 0x00000101, 0x01000101, 0x00010101, 0x01010101, -}; - -/* - * Initial Permutation macro - */ -#define DES_IP(X,Y) \ -{ \ - T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ - T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ - T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ - T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ - Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ - T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ - X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ -} - -/* - * Final Permutation macro - */ -#define DES_FP(X,Y) \ -{ \ - X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ - T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ - Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ - T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ - T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ - T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ - T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ -} - -/* - * DES round macro - */ -#define DES_ROUND(X,Y) \ -{ \ - T = *SK++ ^ X; \ - Y ^= SB8[ (T ) & 0x3F ] ^ \ - SB6[ (T >> 8) & 0x3F ] ^ \ - SB4[ (T >> 16) & 0x3F ] ^ \ - SB2[ (T >> 24) & 0x3F ]; \ - \ - T = *SK++ ^ ((X << 28) | (X >> 4)); \ - Y ^= SB7[ (T ) & 0x3F ] ^ \ - SB5[ (T >> 8) & 0x3F ] ^ \ - SB3[ (T >> 16) & 0x3F ] ^ \ - SB1[ (T >> 24) & 0x3F ]; \ -} - -#define SWAP(a,b) { uint32_t t = a; a = b; b = t; t = 0; } - -static const unsigned char odd_parity_table[128] = { 1, 2, 4, 7, 8, - 11, 13, 14, 16, 19, 21, 22, 25, 26, 28, 31, 32, 35, 37, 38, 41, 42, 44, - 47, 49, 50, 52, 55, 56, 59, 61, 62, 64, 67, 69, 70, 73, 74, 76, 79, 81, - 82, 84, 87, 88, 91, 93, 94, 97, 98, 100, 103, 104, 107, 109, 110, 112, - 115, 117, 118, 121, 122, 124, 127, 128, 131, 133, 134, 137, 138, 140, - 143, 145, 146, 148, 151, 152, 155, 157, 158, 161, 162, 164, 167, 168, - 171, 173, 174, 176, 179, 181, 182, 185, 186, 188, 191, 193, 194, 196, - 199, 200, 203, 205, 206, 208, 211, 213, 214, 217, 218, 220, 223, 224, - 227, 229, 230, 233, 234, 236, 239, 241, 242, 244, 247, 248, 251, 253, - 254 }; - -void des_key_set_parity( unsigned char key[DES_KEY_SIZE] ) -{ - int i; - - for( i = 0; i < DES_KEY_SIZE; i++ ) - key[i] = odd_parity_table[key[i] / 2]; -} - -/* - * Check the given key's parity, returns 1 on failure, 0 on SUCCESS - */ -int des_key_check_key_parity( const unsigned char key[DES_KEY_SIZE] ) -{ - int i; - - for( i = 0; i < DES_KEY_SIZE; i++ ) - if ( key[i] != odd_parity_table[key[i] / 2] ) - return( 1 ); - - return( 0 ); -} - -/* - * Table of weak and semi-weak keys - * - * Source: http://en.wikipedia.org/wiki/Weak_key - * - * Weak: - * Alternating ones + zeros (0x0101010101010101) - * Alternating 'F' + 'E' (0xFEFEFEFEFEFEFEFE) - * '0xE0E0E0E0F1F1F1F1' - * '0x1F1F1F1F0E0E0E0E' - * - * Semi-weak: - * 0x011F011F010E010E and 0x1F011F010E010E01 - * 0x01E001E001F101F1 and 0xE001E001F101F101 - * 0x01FE01FE01FE01FE and 0xFE01FE01FE01FE01 - * 0x1FE01FE00EF10EF1 and 0xE01FE01FF10EF10E - * 0x1FFE1FFE0EFE0EFE and 0xFE1FFE1FFE0EFE0E - * 0xE0FEE0FEF1FEF1FE and 0xFEE0FEE0FEF1FEF1 - * - */ - -#define WEAK_KEY_COUNT 16 - -static const unsigned char weak_key_table[WEAK_KEY_COUNT][DES_KEY_SIZE] = -{ - { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, - { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE }, - { 0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E }, - { 0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1 }, - - { 0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E }, - { 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01 }, - { 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1 }, - { 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01 }, - { 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE }, - { 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01 }, - { 0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1 }, - { 0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E }, - { 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE }, - { 0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E }, - { 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE }, - { 0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1 } -}; - -int des_key_check_weak( const unsigned char key[DES_KEY_SIZE] ) -{ - int i; - - for( i = 0; i < WEAK_KEY_COUNT; i++ ) - if( memcmp( weak_key_table[i], key, DES_KEY_SIZE) == 0) - return( 1 ); - - return( 0 ); -} - -static void des_setkey( uint32_t SK[32], const unsigned char key[DES_KEY_SIZE] ) -{ - int i; - uint32_t X, Y, T; - - GET_UINT32_BE( X, key, 0 ); - GET_UINT32_BE( Y, key, 4 ); - - /* - * Permuted Choice 1 - */ - T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); - T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T ); - - X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2) - | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] ) - | (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6) - | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4); - - Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2) - | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] ) - | (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6) - | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4); - - X &= 0x0FFFFFFF; - Y &= 0x0FFFFFFF; - - /* - * calculate subkeys - */ - for( i = 0; i < 16; i++ ) - { - if( i < 2 || i == 8 || i == 15 ) - { - X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; - Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; - } - else - { - X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; - Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; - } - - *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) - | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) - | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) - | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) - | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) - | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) - | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) - | ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100) - | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) - | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) - | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); - - *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) - | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) - | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) - | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) - | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) - | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) - | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) - | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) - | ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100) - | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) - | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); - } -} - -/* - * DES key schedule (56-bit, encryption) - */ -int des_setkey_enc( des_context *ctx, const unsigned char key[DES_KEY_SIZE] ) -{ - des_setkey( ctx->sk, key ); - - return( 0 ); -} - -/* - * DES key schedule (56-bit, decryption) - */ -int des_setkey_dec( des_context *ctx, const unsigned char key[DES_KEY_SIZE] ) -{ - int i; - - des_setkey( ctx->sk, key ); - - for( i = 0; i < 16; i += 2 ) - { - SWAP( ctx->sk[i ], ctx->sk[30 - i] ); - SWAP( ctx->sk[i + 1], ctx->sk[31 - i] ); - } - - return( 0 ); -} - -static void des3_set2key( uint32_t esk[96], - uint32_t dsk[96], - const unsigned char key[DES_KEY_SIZE*2] ) -{ - int i; - - des_setkey( esk, key ); - des_setkey( dsk + 32, key + 8 ); - - for( i = 0; i < 32; i += 2 ) - { - dsk[i ] = esk[30 - i]; - dsk[i + 1] = esk[31 - i]; - - esk[i + 32] = dsk[62 - i]; - esk[i + 33] = dsk[63 - i]; - - esk[i + 64] = esk[i ]; - esk[i + 65] = esk[i + 1]; - - dsk[i + 64] = dsk[i ]; - dsk[i + 65] = dsk[i + 1]; - } -} - -/* - * Triple-DES key schedule (112-bit, encryption) - */ -int des3_set2key_enc( des3_context *ctx, const unsigned char key[DES_KEY_SIZE * 2] ) -{ - uint32_t sk[96]; - - des3_set2key( ctx->sk, sk, key ); - memset( sk, 0, sizeof( sk ) ); - - return( 0 ); -} - -/* - * Triple-DES key schedule (112-bit, decryption) - */ -int des3_set2key_dec( des3_context *ctx, const unsigned char key[DES_KEY_SIZE * 2] ) -{ - uint32_t sk[96]; - - des3_set2key( sk, ctx->sk, key ); - memset( sk, 0, sizeof( sk ) ); - - return( 0 ); -} - -static void des3_set3key( uint32_t esk[96], - uint32_t dsk[96], - const unsigned char key[24] ) -{ - int i; - - des_setkey( esk, key ); - des_setkey( dsk + 32, key + 8 ); - des_setkey( esk + 64, key + 16 ); - - for( i = 0; i < 32; i += 2 ) - { - dsk[i ] = esk[94 - i]; - dsk[i + 1] = esk[95 - i]; - - esk[i + 32] = dsk[62 - i]; - esk[i + 33] = dsk[63 - i]; - - dsk[i + 64] = esk[30 - i]; - dsk[i + 65] = esk[31 - i]; - } -} - -/* - * Triple-DES key schedule (168-bit, encryption) - */ -int des3_set3key_enc( des3_context *ctx, const unsigned char key[DES_KEY_SIZE * 3] ) -{ - uint32_t sk[96]; - - des3_set3key( ctx->sk, sk, key ); - memset( sk, 0, sizeof( sk ) ); - - return( 0 ); -} - -/* - * Triple-DES key schedule (168-bit, decryption) - */ -int des3_set3key_dec( des3_context *ctx, const unsigned char key[DES_KEY_SIZE * 3] ) -{ - uint32_t sk[96]; - - des3_set3key( sk, ctx->sk, key ); - memset( sk, 0, sizeof( sk ) ); - - return( 0 ); -} - -/* - * DES-ECB block encryption/decryption - */ -int des_crypt_ecb( des_context *ctx, - const unsigned char input[8], - unsigned char output[8] ) -{ - int i; - uint32_t X, Y, T, *SK; - - SK = ctx->sk; - - GET_UINT32_BE( X, input, 0 ); - GET_UINT32_BE( Y, input, 4 ); - - DES_IP( X, Y ); - - for( i = 0; i < 8; i++ ) - { - DES_ROUND( Y, X ); - DES_ROUND( X, Y ); - } - - DES_FP( Y, X ); - - PUT_UINT32_BE( Y, output, 0 ); - PUT_UINT32_BE( X, output, 4 ); - - return( 0 ); -} - -/* - * DES-CBC buffer encryption/decryption - */ -int des_crypt_cbc( des_context *ctx, - int mode, - size_t length, - unsigned char iv[8], - const unsigned char *input, - unsigned char *output ) -{ - int i; - unsigned char temp[8]; - - if( length % 8 ) - return( POLARSSL_ERR_DES_INVALID_INPUT_LENGTH ); - - if( mode == DES_ENCRYPT ) - { - while( length > 0 ) - { - for( i = 0; i < 8; i++ ) - output[i] = (unsigned char)( input[i] ^ iv[i] ); - - des_crypt_ecb( ctx, output, output ); - memcpy( iv, output, 8 ); - - input += 8; - output += 8; - length -= 8; - } - } - else /* DES_DECRYPT */ - { - while( length > 0 ) - { - memcpy( temp, input, 8 ); - des_crypt_ecb( ctx, input, output ); - - for( i = 0; i < 8; i++ ) - output[i] = (unsigned char)( output[i] ^ iv[i] ); - - memcpy( iv, temp, 8 ); - - input += 8; - output += 8; - length -= 8; - } - } - - return( 0 ); -} - -/* - * 3DES-ECB block encryption/decryption - */ -int des3_crypt_ecb( des3_context *ctx, - const unsigned char input[8], - unsigned char output[8] ) -{ - int i; - uint32_t X, Y, T, *SK; - - SK = ctx->sk; - - GET_UINT32_BE( X, input, 0 ); - GET_UINT32_BE( Y, input, 4 ); - - DES_IP( X, Y ); - - for( i = 0; i < 8; i++ ) - { - DES_ROUND( Y, X ); - DES_ROUND( X, Y ); - } - - for( i = 0; i < 8; i++ ) - { - DES_ROUND( X, Y ); - DES_ROUND( Y, X ); - } - - for( i = 0; i < 8; i++ ) - { - DES_ROUND( Y, X ); - DES_ROUND( X, Y ); - } - - DES_FP( Y, X ); - - PUT_UINT32_BE( Y, output, 0 ); - PUT_UINT32_BE( X, output, 4 ); - - return( 0 ); -} - -/* - * 3DES-CBC buffer encryption/decryption - */ -int des3_crypt_cbc( des3_context *ctx, - int mode, - size_t length, - unsigned char iv[8], - const unsigned char *input, - unsigned char *output ) -{ - int i; - unsigned char temp[8]; - - if( length % 8 ) - return( POLARSSL_ERR_DES_INVALID_INPUT_LENGTH ); - - if( mode == DES_ENCRYPT ) - { - while( length > 0 ) - { - for( i = 0; i < 8; i++ ) - output[i] = (unsigned char)( input[i] ^ iv[i] ); - - des3_crypt_ecb( ctx, output, output ); - memcpy( iv, output, 8 ); - - input += 8; - output += 8; - length -= 8; - } - } - else /* DES_DECRYPT */ - { - while( length > 0 ) - { - memcpy( temp, input, 8 ); - des3_crypt_ecb( ctx, input, output ); - - for( i = 0; i < 8; i++ ) - output[i] = (unsigned char)( output[i] ^ iv[i] ); - - memcpy( iv, temp, 8 ); - - input += 8; - output += 8; - length -= 8; - } - } - - return( 0 ); -} - -#endif /* !POLARSSL_DES_ALT */ - -#if defined(POLARSSL_SELF_TEST) - -#include - -/* - * DES and 3DES test vectors from: - * - * http://csrc.nist.gov/groups/STM/cavp/documents/des/tripledes-vectors.zip - */ -static const unsigned char des3_test_keys[24] = -{ - 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, - 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, - 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 -}; - -static const unsigned char des3_test_iv[8] = -{ - 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, -}; - -static const unsigned char des3_test_buf[8] = -{ - 0x4E, 0x6F, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74 -}; - -static const unsigned char des3_test_ecb_dec[3][8] = -{ - { 0xCD, 0xD6, 0x4F, 0x2F, 0x94, 0x27, 0xC1, 0x5D }, - { 0x69, 0x96, 0xC8, 0xFA, 0x47, 0xA2, 0xAB, 0xEB }, - { 0x83, 0x25, 0x39, 0x76, 0x44, 0x09, 0x1A, 0x0A } -}; - -static const unsigned char des3_test_ecb_enc[3][8] = -{ - { 0x6A, 0x2A, 0x19, 0xF4, 0x1E, 0xCA, 0x85, 0x4B }, - { 0x03, 0xE6, 0x9F, 0x5B, 0xFA, 0x58, 0xEB, 0x42 }, - { 0xDD, 0x17, 0xE8, 0xB8, 0xB4, 0x37, 0xD2, 0x32 } -}; - -static const unsigned char des3_test_cbc_dec[3][8] = -{ - { 0x12, 0x9F, 0x40, 0xB9, 0xD2, 0x00, 0x56, 0xB3 }, - { 0x47, 0x0E, 0xFC, 0x9A, 0x6B, 0x8E, 0xE3, 0x93 }, - { 0xC5, 0xCE, 0xCF, 0x63, 0xEC, 0xEC, 0x51, 0x4C } -}; - -static const unsigned char des3_test_cbc_enc[3][8] = -{ - { 0x54, 0xF1, 0x5A, 0xF6, 0xEB, 0xE3, 0xA4, 0xB4 }, - { 0x35, 0x76, 0x11, 0x56, 0x5F, 0xA1, 0x8E, 0x4D }, - { 0xCB, 0x19, 0x1F, 0x85, 0xD1, 0xED, 0x84, 0x39 } -}; - -/* - * Checkup routine - */ -int des_self_test( int verbose ) -{ - int i, j, u, v; - des_context ctx; - des3_context ctx3; - unsigned char key[24]; - unsigned char buf[8]; - unsigned char prv[8]; - unsigned char iv[8]; - - memset( key, 0, 24 ); - - /* - * ECB mode - */ - for( i = 0; i < 6; i++ ) - { - u = i >> 1; - v = i & 1; - - if( verbose != 0 ) - printf( " DES%c-ECB-%3d (%s): ", - ( u == 0 ) ? ' ' : '3', 56 + u * 56, - ( v == DES_DECRYPT ) ? "dec" : "enc" ); - - memcpy( buf, des3_test_buf, 8 ); - - switch( i ) - { - case 0: - des_setkey_dec( &ctx, des3_test_keys ); - break; - - case 1: - des_setkey_enc( &ctx, des3_test_keys ); - break; - - case 2: - des3_set2key_dec( &ctx3, des3_test_keys ); - break; - - case 3: - des3_set2key_enc( &ctx3, des3_test_keys ); - break; - - case 4: - des3_set3key_dec( &ctx3, des3_test_keys ); - break; - - case 5: - des3_set3key_enc( &ctx3, des3_test_keys ); - break; - - default: - return( 1 ); - } - - for( j = 0; j < 10000; j++ ) - { - if( u == 0 ) - des_crypt_ecb( &ctx, buf, buf ); - else - des3_crypt_ecb( &ctx3, buf, buf ); - } - - if( ( v == DES_DECRYPT && - memcmp( buf, des3_test_ecb_dec[u], 8 ) != 0 ) || - ( v != DES_DECRYPT && - memcmp( buf, des3_test_ecb_enc[u], 8 ) != 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( " DES%c-CBC-%3d (%s): ", - ( u == 0 ) ? ' ' : '3', 56 + u * 56, - ( v == DES_DECRYPT ) ? "dec" : "enc" ); - - memcpy( iv, des3_test_iv, 8 ); - memcpy( prv, des3_test_iv, 8 ); - memcpy( buf, des3_test_buf, 8 ); - - switch( i ) - { - case 0: - des_setkey_dec( &ctx, des3_test_keys ); - break; - - case 1: - des_setkey_enc( &ctx, des3_test_keys ); - break; - - case 2: - des3_set2key_dec( &ctx3, des3_test_keys ); - break; - - case 3: - des3_set2key_enc( &ctx3, des3_test_keys ); - break; - - case 4: - des3_set3key_dec( &ctx3, des3_test_keys ); - break; - - case 5: - des3_set3key_enc( &ctx3, des3_test_keys ); - break; - - default: - return( 1 ); - } - - if( v == DES_DECRYPT ) - { - for( j = 0; j < 10000; j++ ) - { - if( u == 0 ) - des_crypt_cbc( &ctx, v, 8, iv, buf, buf ); - else - des3_crypt_cbc( &ctx3, v, 8, iv, buf, buf ); - } - } - else - { - for( j = 0; j < 10000; j++ ) - { - unsigned char tmp[8]; - - if( u == 0 ) - des_crypt_cbc( &ctx, v, 8, iv, buf, buf ); - else - des3_crypt_cbc( &ctx3, v, 8, iv, buf, buf ); - - memcpy( tmp, prv, 8 ); - memcpy( prv, buf, 8 ); - memcpy( buf, tmp, 8 ); - } - - memcpy( buf, prv, 8 ); - } - - if( ( v == DES_DECRYPT && - memcmp( buf, des3_test_cbc_dec[u], 8 ) != 0 ) || - ( v != DES_DECRYPT && - memcmp( buf, des3_test_cbc_enc[u], 8 ) != 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/makerom/polarssl/dhm.c b/makerom/polarssl/dhm.c deleted file mode 100644 index 90e5b4b1..00000000 --- a/makerom/polarssl/dhm.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Diffie-Hellman-Merkle key exchange - * - * 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. - */ -/* - * Reference: - * - * http://www.cacr.math.uwaterloo.ca/hac/ (chapter 12) - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_DHM_C) - -#include "polarssl/dhm.h" - -/* - * helper to validate the mpi size and import it - */ -static int dhm_read_bignum( mpi *X, - unsigned char **p, - const unsigned char *end ) -{ - int ret, n; - - if( end - *p < 2 ) - return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); - - n = ( (*p)[0] << 8 ) | (*p)[1]; - (*p) += 2; - - if( (int)( end - *p ) < n ) - return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); - - if( ( ret = mpi_read_binary( X, *p, n ) ) != 0 ) - return( POLARSSL_ERR_DHM_READ_PARAMS_FAILED + ret ); - - (*p) += n; - - return( 0 ); -} - -/* - * Verify sanity of parameter with regards to P - * - * Parameter should be: 2 <= public_param <= P - 2 - * - * For more information on the attack, see: - * http://www.cl.cam.ac.uk/~rja14/Papers/psandqs.pdf - * http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2005-2643 - */ -static int dhm_check_range( const mpi *param, const mpi *P ) -{ - mpi L, U; - int ret = POLARSSL_ERR_DHM_BAD_INPUT_DATA; - - mpi_init( &L ); mpi_init( &U ); - mpi_lset( &L, 2 ); - mpi_sub_int( &U, P, 2 ); - - if( mpi_cmp_mpi( param, &L ) >= 0 && - mpi_cmp_mpi( param, &U ) <= 0 ) - { - ret = 0; - } - - mpi_free( &L ); mpi_free( &U ); - - return( ret ); -} - -/* - * Parse the ServerKeyExchange parameters - */ -int dhm_read_params( dhm_context *ctx, - unsigned char **p, - const unsigned char *end ) -{ - int ret; - - memset( ctx, 0, sizeof( dhm_context ) ); - - if( ( ret = dhm_read_bignum( &ctx->P, p, end ) ) != 0 || - ( ret = dhm_read_bignum( &ctx->G, p, end ) ) != 0 || - ( ret = dhm_read_bignum( &ctx->GY, p, end ) ) != 0 ) - return( ret ); - - if( ( ret = dhm_check_range( &ctx->GY, &ctx->P ) ) != 0 ) - return( ret ); - - ctx->len = mpi_size( &ctx->P ); - - if( end - *p < 2 ) - return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); - - return( 0 ); -} - -/* - * Setup and write the ServerKeyExchange parameters - */ -int dhm_make_params( dhm_context *ctx, int x_size, - unsigned char *output, size_t *olen, - int (*f_rng)(void *, unsigned char *, size_t), - void *p_rng ) -{ - int ret, count = 0; - size_t n1, n2, n3; - unsigned char *p; - - if( mpi_cmp_int( &ctx->P, 0 ) == 0 ) - return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); - - /* - * Generate X as large as possible ( < P ) - */ - do - { - mpi_fill_random( &ctx->X, x_size, f_rng, p_rng ); - - while( mpi_cmp_mpi( &ctx->X, &ctx->P ) >= 0 ) - mpi_shift_r( &ctx->X, 1 ); - - if( count++ > 10 ) - return( POLARSSL_ERR_DHM_MAKE_PARAMS_FAILED ); - } - while( dhm_check_range( &ctx->X, &ctx->P ) != 0 ); - - /* - * Calculate GX = G^X mod P - */ - MPI_CHK( mpi_exp_mod( &ctx->GX, &ctx->G, &ctx->X, - &ctx->P , &ctx->RP ) ); - - if( ( ret = dhm_check_range( &ctx->GX, &ctx->P ) ) != 0 ) - return( ret ); - - /* - * export P, G, GX - */ -#define DHM_MPI_EXPORT(X,n) \ - MPI_CHK( mpi_write_binary( X, p + 2, n ) ); \ - *p++ = (unsigned char)( n >> 8 ); \ - *p++ = (unsigned char)( n ); p += n; - - n1 = mpi_size( &ctx->P ); - n2 = mpi_size( &ctx->G ); - n3 = mpi_size( &ctx->GX ); - - p = output; - DHM_MPI_EXPORT( &ctx->P , n1 ); - DHM_MPI_EXPORT( &ctx->G , n2 ); - DHM_MPI_EXPORT( &ctx->GX, n3 ); - - *olen = p - output; - - ctx->len = n1; - -cleanup: - - if( ret != 0 ) - return( POLARSSL_ERR_DHM_MAKE_PARAMS_FAILED + ret ); - - return( 0 ); -} - -/* - * Import the peer's public value G^Y - */ -int dhm_read_public( dhm_context *ctx, - const unsigned char *input, size_t ilen ) -{ - int ret; - - if( ctx == NULL || ilen < 1 || ilen > ctx->len ) - return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); - - if( ( ret = mpi_read_binary( &ctx->GY, input, ilen ) ) != 0 ) - return( POLARSSL_ERR_DHM_READ_PUBLIC_FAILED + ret ); - - return( 0 ); -} - -/* - * Create own private value X and export G^X - */ -int dhm_make_public( dhm_context *ctx, int x_size, - unsigned char *output, size_t olen, - int (*f_rng)(void *, unsigned char *, size_t), - void *p_rng ) -{ - int ret, count = 0; - - if( ctx == NULL || olen < 1 || olen > ctx->len ) - return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); - - if( mpi_cmp_int( &ctx->P, 0 ) == 0 ) - return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); - - /* - * generate X and calculate GX = G^X mod P - */ - do - { - mpi_fill_random( &ctx->X, x_size, f_rng, p_rng ); - - while( mpi_cmp_mpi( &ctx->X, &ctx->P ) >= 0 ) - mpi_shift_r( &ctx->X, 1 ); - - if( count++ > 10 ) - return( POLARSSL_ERR_DHM_MAKE_PUBLIC_FAILED ); - } - while( dhm_check_range( &ctx->X, &ctx->P ) != 0 ); - - MPI_CHK( mpi_exp_mod( &ctx->GX, &ctx->G, &ctx->X, - &ctx->P , &ctx->RP ) ); - - if( ( ret = dhm_check_range( &ctx->GX, &ctx->P ) ) != 0 ) - return( ret ); - - MPI_CHK( mpi_write_binary( &ctx->GX, output, olen ) ); - -cleanup: - - if( ret != 0 ) - return( POLARSSL_ERR_DHM_MAKE_PUBLIC_FAILED + ret ); - - return( 0 ); -} - -/* - * Derive and export the shared secret (G^Y)^X mod P - */ -int dhm_calc_secret( dhm_context *ctx, - unsigned char *output, size_t *olen ) -{ - int ret; - - if( ctx == NULL || *olen < ctx->len ) - return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); - - MPI_CHK( mpi_exp_mod( &ctx->K, &ctx->GY, &ctx->X, - &ctx->P, &ctx->RP ) ); - - if( ( ret = dhm_check_range( &ctx->GY, &ctx->P ) ) != 0 ) - return( ret ); - - *olen = mpi_size( &ctx->K ); - - MPI_CHK( mpi_write_binary( &ctx->K, output, *olen ) ); - -cleanup: - - if( ret != 0 ) - return( POLARSSL_ERR_DHM_CALC_SECRET_FAILED + ret ); - - return( 0 ); -} - -/* - * Free the components of a DHM key - */ -void dhm_free( dhm_context *ctx ) -{ - mpi_free( &ctx->RP ); mpi_free( &ctx->K ); mpi_free( &ctx->GY ); - mpi_free( &ctx->GX ); mpi_free( &ctx->X ); mpi_free( &ctx->G ); - mpi_free( &ctx->P ); -} - -#if defined(POLARSSL_SELF_TEST) - -/* - * Checkup routine - */ -int dhm_self_test( int verbose ) -{ - return( verbose++ ); -} - -#endif - -#endif diff --git a/makerom/polarssl/entropy.c b/makerom/polarssl/entropy.c deleted file mode 100644 index 96624547..00000000 --- a/makerom/polarssl/entropy.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Entropy accumulator implementation - * - * Copyright (C) 2006-2011, 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_ENTROPY_C) - -#include "polarssl/entropy.h" -#include "polarssl/entropy_poll.h" - -#if defined(POLARSSL_HAVEGE_C) -#include "polarssl/havege.h" -#endif - -#define ENTROPY_MAX_LOOP 256 /**< Maximum amount to loop before error */ - -void entropy_init( entropy_context *ctx ) -{ - memset( ctx, 0, sizeof(entropy_context) ); - - sha4_starts( &ctx->accumulator, 0 ); -#if defined(POLARSSL_HAVEGE_C) - havege_init( &ctx->havege_data ); -#endif - -#if !defined(POLARSSL_NO_DEFAULT_ENTROPY_SOURCES) -#if !defined(POLARSSL_NO_PLATFORM_ENTROPY) - entropy_add_source( ctx, platform_entropy_poll, NULL, - ENTROPY_MIN_PLATFORM ); -#endif -#if defined(POLARSSL_TIMING_C) - entropy_add_source( ctx, hardclock_poll, NULL, ENTROPY_MIN_HARDCLOCK ); -#endif -#if defined(POLARSSL_HAVEGE_C) - entropy_add_source( ctx, havege_poll, &ctx->havege_data, - ENTROPY_MIN_HAVEGE ); -#endif -#endif /* POLARSSL_NO_DEFAULT_ENTROPY_SOURCES */ -} - -int entropy_add_source( entropy_context *ctx, - f_source_ptr f_source, void *p_source, - size_t threshold ) -{ - int index = ctx->source_count; - - if( index >= ENTROPY_MAX_SOURCES ) - return( POLARSSL_ERR_ENTROPY_MAX_SOURCES ); - - ctx->source[index].f_source = f_source; - ctx->source[index].p_source = p_source; - ctx->source[index].threshold = threshold; - - ctx->source_count++; - - return( 0 ); -} - -/* - * Entropy accumulator update - */ -int entropy_update( entropy_context *ctx, unsigned char source_id, - const unsigned char *data, size_t len ) -{ - unsigned char header[2]; - unsigned char tmp[ENTROPY_BLOCK_SIZE]; - size_t use_len = len; - const unsigned char *p = data; - - if( use_len > ENTROPY_BLOCK_SIZE ) - { - sha4( data, len, tmp, 0 ); - - p = tmp; - use_len = ENTROPY_BLOCK_SIZE; - } - - header[0] = source_id; - header[1] = use_len & 0xFF; - - sha4_update( &ctx->accumulator, header, 2 ); - sha4_update( &ctx->accumulator, p, use_len ); - - return( 0 ); -} - -int entropy_update_manual( entropy_context *ctx, - const unsigned char *data, size_t len ) -{ - return entropy_update( ctx, ENTROPY_SOURCE_MANUAL, data, len ); -} - -/* - * Run through the different sources to add entropy to our accumulator - */ -int entropy_gather( entropy_context *ctx ) -{ - int ret, i; - unsigned char buf[ENTROPY_MAX_GATHER]; - size_t olen; - - if( ctx->source_count == 0 ) - return( POLARSSL_ERR_ENTROPY_NO_SOURCES_DEFINED ); - - /* - * Run through our entropy sources - */ - for( i = 0; i < ctx->source_count; i++ ) - { - olen = 0; - if ( ( ret = ctx->source[i].f_source( ctx->source[i].p_source, - buf, ENTROPY_MAX_GATHER, &olen ) ) != 0 ) - { - return( ret ); - } - - /* - * Add if we actually gathered something - */ - if( olen > 0 ) - { - entropy_update( ctx, (unsigned char) i, buf, olen ); - ctx->source[i].size += olen; - } - } - - return( 0 ); -} - -int entropy_func( void *data, unsigned char *output, size_t len ) -{ - int ret, count = 0, i, reached; - entropy_context *ctx = (entropy_context *) data; - unsigned char buf[ENTROPY_BLOCK_SIZE]; - - if( len > ENTROPY_BLOCK_SIZE ) - return( POLARSSL_ERR_ENTROPY_SOURCE_FAILED ); - - /* - * Always gather extra entropy before a call - */ - do - { - if( count++ > ENTROPY_MAX_LOOP ) - return( POLARSSL_ERR_ENTROPY_SOURCE_FAILED ); - - if( ( ret = entropy_gather( ctx ) ) != 0 ) - return( ret ); - - reached = 0; - - for( i = 0; i < ctx->source_count; i++ ) - if( ctx->source[i].size >= ctx->source[i].threshold ) - reached++; - } - while( reached != ctx->source_count ); - - memset( buf, 0, ENTROPY_BLOCK_SIZE ); - - sha4_finish( &ctx->accumulator, buf ); - - /* - * Perform second SHA-512 on entropy - */ - sha4( buf, ENTROPY_BLOCK_SIZE, buf, 0 ); - - /* - * Reset accumulator and counters and recycle existing entropy - */ - memset( &ctx->accumulator, 0, sizeof( sha4_context ) ); - sha4_starts( &ctx->accumulator, 0 ); - sha4_update( &ctx->accumulator, buf, ENTROPY_BLOCK_SIZE ); - - for( i = 0; i < ctx->source_count; i++ ) - ctx->source[i].size = 0; - - memcpy( output, buf, len ); - - return( 0 ); -} - -#endif diff --git a/makerom/polarssl/entropy_poll.c b/makerom/polarssl/entropy_poll.c deleted file mode 100644 index b5d9f787..00000000 --- a/makerom/polarssl/entropy_poll.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Platform-specific and custom entropy polling functions - * - * Copyright (C) 2006-2011, 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_ENTROPY_C) - -#include "polarssl/entropy.h" -#include "polarssl/entropy_poll.h" - -#if defined(POLARSSL_TIMING_C) -#include "polarssl/timing.h" -#endif -#if defined(POLARSSL_HAVEGE_C) -#include "polarssl/havege.h" -#endif - -#if !defined(POLARSSL_NO_PLATFORM_ENTROPY) -#if defined(_WIN32) - -#if !defined(_WIN32_WINNT) -#define _WIN32_WINNT 0x0400 -#endif -#include -#include - -int platform_entropy_poll( void *data, unsigned char *output, size_t len, - size_t *olen ) -{ - HCRYPTPROV provider; - ((void) data); - *olen = 0; - - if( CryptAcquireContext( &provider, NULL, NULL, - PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) == FALSE ) - { - return POLARSSL_ERR_ENTROPY_SOURCE_FAILED; - } - - if( CryptGenRandom( provider, len, output ) == FALSE ) - return POLARSSL_ERR_ENTROPY_SOURCE_FAILED; - - CryptReleaseContext( provider, 0 ); - *olen = len; - - return( 0 ); -} -#else - -#include - -int platform_entropy_poll( void *data, - unsigned char *output, size_t len, size_t *olen ) -{ - FILE *file; - size_t ret; - ((void) data); - - *olen = 0; - - file = fopen( "/dev/urandom", "rb" ); - if( file == NULL ) - return POLARSSL_ERR_ENTROPY_SOURCE_FAILED; - - ret = fread( output, 1, len, file ); - if( ret != len ) - { - fclose( file ); - return POLARSSL_ERR_ENTROPY_SOURCE_FAILED; - } - - fclose( file ); - *olen = len; - - return( 0 ); -} -#endif -#endif - -#if defined(POLARSSL_TIMING_C) -int hardclock_poll( void *data, - unsigned char *output, size_t len, size_t *olen ) -{ - unsigned long timer = hardclock(); - ((void) data); - *olen = 0; - - if( len < sizeof(unsigned long) ) - return( 0 ); - - memcpy( output, &timer, sizeof(unsigned long) ); - *olen = sizeof(unsigned long); - - return( 0 ); -} -#endif - -#if defined(POLARSSL_HAVEGE_C) -int havege_poll( void *data, - unsigned char *output, size_t len, size_t *olen ) -{ - havege_state *hs = (havege_state *) data; - *olen = 0; - - if( havege_random( hs, output, len ) != 0 ) - return POLARSSL_ERR_ENTROPY_SOURCE_FAILED; - - *olen = len; - - return( 0 ); -} -#endif - -#endif /* POLARSSL_ENTROPY_C */ diff --git a/makerom/polarssl/error.c b/makerom/polarssl/error.c deleted file mode 100644 index 036b834f..00000000 --- a/makerom/polarssl/error.c +++ /dev/null @@ -1,612 +0,0 @@ -/* - * Error message information - * - * Copyright (C) 2006-2012, 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_ERROR_C) - -#include "polarssl/error.h" - -#if defined(POLARSSL_AES_C) -#include "polarssl/aes.h" -#endif - -#if defined(POLARSSL_BASE64_C) -#include "polarssl/base64.h" -#endif - -#if defined(POLARSSL_BIGNUM_C) -#include "polarssl/bignum.h" -#endif - -#if defined(POLARSSL_BLOWFISH_C) -#include "polarssl/blowfish.h" -#endif - -#if defined(POLARSSL_CAMELLIA_C) -#include "polarssl/camellia.h" -#endif - -#if defined(POLARSSL_CIPHER_C) -#include "polarssl/cipher.h" -#endif - -#if defined(POLARSSL_CTR_DRBG_C) -#include "polarssl/ctr_drbg.h" -#endif - -#if defined(POLARSSL_DES_C) -#include "polarssl/des.h" -#endif - -#if defined(POLARSSL_DHM_C) -#include "polarssl/dhm.h" -#endif - -#if defined(POLARSSL_ENTROPY_C) -#include "polarssl/entropy.h" -#endif - -#if defined(POLARSSL_GCM_C) -#include "polarssl/gcm.h" -#endif - -#if defined(POLARSSL_MD_C) -#include "polarssl/md.h" -#endif - -#if defined(POLARSSL_MD2_C) -#include "polarssl/md2.h" -#endif - -#if defined(POLARSSL_MD4_C) -#include "polarssl/md4.h" -#endif - -#if defined(POLARSSL_MD5_C) -#include "polarssl/md5.h" -#endif - -#if defined(POLARSSL_NET_C) -#include "polarssl/net.h" -#endif - -#if defined(POLARSSL_PADLOCK_C) -#include "polarssl/padlock.h" -#endif - -#if defined(POLARSSL_PBKDF2_C) -#include "polarssl/pbkdf2.h" -#endif - -#if defined(POLARSSL_PEM_C) -#include "polarssl/pem.h" -#endif - -#if defined(POLARSSL_PKCS12_C) -#include "polarssl/pkcs12.h" -#endif - -#if defined(POLARSSL_PKCS5_C) -#include "polarssl/pkcs5.h" -#endif - -#if defined(POLARSSL_RSA_C) -#include "polarssl/rsa.h" -#endif - -#if defined(POLARSSL_SHA1_C) -#include "polarssl/sha1.h" -#endif - -#if defined(POLARSSL_SHA2_C) -#include "polarssl/sha2.h" -#endif - -#if defined(POLARSSL_SHA4_C) -#include "polarssl/sha4.h" -#endif - -#if defined(POLARSSL_SSL_TLS_C) -#include "polarssl/ssl.h" -#endif - -#if defined(POLARSSL_X509_PARSE_C) -#include "polarssl/x509.h" -#endif - -#if defined(POLARSSL_XTEA_C) -#include "polarssl/xtea.h" -#endif - - -#include - -#if defined _MSC_VER && !defined snprintf -#define snprintf _snprintf -#endif - -void error_strerror( int ret, char *buf, size_t buflen ) -{ - size_t len; - int use_ret; - - memset( buf, 0x00, buflen ); - - if( ret < 0 ) - ret = -ret; - - if( ret & 0xFF80 ) - { - use_ret = ret & 0xFF80; - - // High level error codes - // -#if defined(POLARSSL_CIPHER_C) - if( use_ret == -(POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE) ) - snprintf( buf, buflen, "CIPHER - The selected feature is not available" ); - if( use_ret == -(POLARSSL_ERR_CIPHER_BAD_INPUT_DATA) ) - snprintf( buf, buflen, "CIPHER - Bad input parameters to function" ); - if( use_ret == -(POLARSSL_ERR_CIPHER_ALLOC_FAILED) ) - snprintf( buf, buflen, "CIPHER - Failed to allocate memory" ); - if( use_ret == -(POLARSSL_ERR_CIPHER_INVALID_PADDING) ) - snprintf( buf, buflen, "CIPHER - Input data contains invalid padding and is rejected" ); - if( use_ret == -(POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED) ) - snprintf( buf, buflen, "CIPHER - Decryption of block requires a full block" ); -#endif /* POLARSSL_CIPHER_C */ - -#if defined(POLARSSL_DHM_C) - if( use_ret == -(POLARSSL_ERR_DHM_BAD_INPUT_DATA) ) - snprintf( buf, buflen, "DHM - Bad input parameters to function" ); - if( use_ret == -(POLARSSL_ERR_DHM_READ_PARAMS_FAILED) ) - snprintf( buf, buflen, "DHM - Reading of the DHM parameters failed" ); - if( use_ret == -(POLARSSL_ERR_DHM_MAKE_PARAMS_FAILED) ) - snprintf( buf, buflen, "DHM - Making of the DHM parameters failed" ); - if( use_ret == -(POLARSSL_ERR_DHM_READ_PUBLIC_FAILED) ) - snprintf( buf, buflen, "DHM - Reading of the public values failed" ); - if( use_ret == -(POLARSSL_ERR_DHM_MAKE_PUBLIC_FAILED) ) - snprintf( buf, buflen, "DHM - Making of the public value failed" ); - if( use_ret == -(POLARSSL_ERR_DHM_CALC_SECRET_FAILED) ) - snprintf( buf, buflen, "DHM - Calculation of the DHM secret failed" ); -#endif /* POLARSSL_DHM_C */ - -#if defined(POLARSSL_MD_C) - if( use_ret == -(POLARSSL_ERR_MD_FEATURE_UNAVAILABLE) ) - snprintf( buf, buflen, "MD - The selected feature is not available" ); - if( use_ret == -(POLARSSL_ERR_MD_BAD_INPUT_DATA) ) - snprintf( buf, buflen, "MD - Bad input parameters to function" ); - if( use_ret == -(POLARSSL_ERR_MD_ALLOC_FAILED) ) - snprintf( buf, buflen, "MD - Failed to allocate memory" ); - if( use_ret == -(POLARSSL_ERR_MD_FILE_IO_ERROR) ) - snprintf( buf, buflen, "MD - Opening or reading of file failed" ); -#endif /* POLARSSL_MD_C */ - -#if defined(POLARSSL_PEM_C) - if( use_ret == -(POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT) ) - snprintf( buf, buflen, "PEM - No PEM header or footer found" ); - if( use_ret == -(POLARSSL_ERR_PEM_INVALID_DATA) ) - snprintf( buf, buflen, "PEM - PEM string is not as expected" ); - if( use_ret == -(POLARSSL_ERR_PEM_MALLOC_FAILED) ) - snprintf( buf, buflen, "PEM - Failed to allocate memory" ); - if( use_ret == -(POLARSSL_ERR_PEM_INVALID_ENC_IV) ) - snprintf( buf, buflen, "PEM - RSA IV is not in hex-format" ); - if( use_ret == -(POLARSSL_ERR_PEM_UNKNOWN_ENC_ALG) ) - snprintf( buf, buflen, "PEM - Unsupported key encryption algorithm" ); - if( use_ret == -(POLARSSL_ERR_PEM_PASSWORD_REQUIRED) ) - snprintf( buf, buflen, "PEM - Private key password can't be empty" ); - if( use_ret == -(POLARSSL_ERR_PEM_PASSWORD_MISMATCH) ) - snprintf( buf, buflen, "PEM - Given private key password does not allow for correct decryption" ); - if( use_ret == -(POLARSSL_ERR_PEM_FEATURE_UNAVAILABLE) ) - snprintf( buf, buflen, "PEM - Unavailable feature, e.g. hashing/encryption combination" ); - if( use_ret == -(POLARSSL_ERR_PEM_BAD_INPUT_DATA) ) - snprintf( buf, buflen, "PEM - Bad input parameters to function" ); -#endif /* POLARSSL_PEM_C */ - -#if defined(POLARSSL_PKCS12_C) - if( use_ret == -(POLARSSL_ERR_PKCS12_BAD_INPUT_DATA) ) - snprintf( buf, buflen, "PKCS12 - Bad input parameters to function" ); - if( use_ret == -(POLARSSL_ERR_PKCS12_FEATURE_UNAVAILABLE) ) - snprintf( buf, buflen, "PKCS12 - Feature not available, e.g. unsupported encryption scheme" ); - if( use_ret == -(POLARSSL_ERR_PKCS12_PBE_INVALID_FORMAT) ) - snprintf( buf, buflen, "PKCS12 - PBE ASN.1 data not as expected" ); - if( use_ret == -(POLARSSL_ERR_PKCS12_PASSWORD_MISMATCH) ) - snprintf( buf, buflen, "PKCS12 - Given private key password does not allow for correct decryption" ); -#endif /* POLARSSL_PKCS12_C */ - -#if defined(POLARSSL_PKCS5_C) - if( use_ret == -(POLARSSL_ERR_PKCS5_BAD_INPUT_DATA) ) - snprintf( buf, buflen, "PKCS5 - Bad input parameters to function" ); - if( use_ret == -(POLARSSL_ERR_PKCS5_INVALID_FORMAT) ) - snprintf( buf, buflen, "PKCS5 - Unexpected ASN.1 data" ); - if( use_ret == -(POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE) ) - snprintf( buf, buflen, "PKCS5 - Requested encryption or digest alg not available" ); - if( use_ret == -(POLARSSL_ERR_PKCS5_PASSWORD_MISMATCH) ) - snprintf( buf, buflen, "PKCS5 - Given private key password does not allow for correct decryption" ); -#endif /* POLARSSL_PKCS5_C */ - -#if defined(POLARSSL_RSA_C) - if( use_ret == -(POLARSSL_ERR_RSA_BAD_INPUT_DATA) ) - snprintf( buf, buflen, "RSA - Bad input parameters to function" ); - if( use_ret == -(POLARSSL_ERR_RSA_INVALID_PADDING) ) - snprintf( buf, buflen, "RSA - Input data contains invalid padding and is rejected" ); - if( use_ret == -(POLARSSL_ERR_RSA_KEY_GEN_FAILED) ) - snprintf( buf, buflen, "RSA - Something failed during generation of a key" ); - if( use_ret == -(POLARSSL_ERR_RSA_KEY_CHECK_FAILED) ) - snprintf( buf, buflen, "RSA - Key failed to pass the libraries validity check" ); - if( use_ret == -(POLARSSL_ERR_RSA_PUBLIC_FAILED) ) - snprintf( buf, buflen, "RSA - The public key operation failed" ); - if( use_ret == -(POLARSSL_ERR_RSA_PRIVATE_FAILED) ) - snprintf( buf, buflen, "RSA - The private key operation failed" ); - if( use_ret == -(POLARSSL_ERR_RSA_VERIFY_FAILED) ) - snprintf( buf, buflen, "RSA - The PKCS#1 verification failed" ); - if( use_ret == -(POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE) ) - snprintf( buf, buflen, "RSA - The output buffer for decryption is not large enough" ); - if( use_ret == -(POLARSSL_ERR_RSA_RNG_FAILED) ) - snprintf( buf, buflen, "RSA - The random generator failed to generate non-zeros" ); -#endif /* POLARSSL_RSA_C */ - -#if defined(POLARSSL_SSL_TLS_C) - if( use_ret == -(POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE) ) - snprintf( buf, buflen, "SSL - The requested feature is not available" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_INPUT_DATA) ) - snprintf( buf, buflen, "SSL - Bad input parameters to function" ); - if( use_ret == -(POLARSSL_ERR_SSL_INVALID_MAC) ) - snprintf( buf, buflen, "SSL - Verification of the message MAC failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_INVALID_RECORD) ) - snprintf( buf, buflen, "SSL - An invalid SSL record was received" ); - if( use_ret == -(POLARSSL_ERR_SSL_CONN_EOF) ) - snprintf( buf, buflen, "SSL - The connection indicated an EOF" ); - if( use_ret == -(POLARSSL_ERR_SSL_UNKNOWN_CIPHER) ) - snprintf( buf, buflen, "SSL - An unknown cipher was received" ); - if( use_ret == -(POLARSSL_ERR_SSL_NO_CIPHER_CHOSEN) ) - snprintf( buf, buflen, "SSL - The server has no ciphersuites in common with the client" ); - if( use_ret == -(POLARSSL_ERR_SSL_NO_SESSION_FOUND) ) - snprintf( buf, buflen, "SSL - No session to recover was found" ); - if( use_ret == -(POLARSSL_ERR_SSL_NO_CLIENT_CERTIFICATE) ) - snprintf( buf, buflen, "SSL - No client certification received from the client, but required by the authentication mode" ); - if( use_ret == -(POLARSSL_ERR_SSL_CERTIFICATE_TOO_LARGE) ) - snprintf( buf, buflen, "SSL - DESCRIPTION MISSING" ); - if( use_ret == -(POLARSSL_ERR_SSL_CERTIFICATE_REQUIRED) ) - snprintf( buf, buflen, "SSL - The own certificate is not set, but needed by the server" ); - if( use_ret == -(POLARSSL_ERR_SSL_PRIVATE_KEY_REQUIRED) ) - snprintf( buf, buflen, "SSL - The own private key is not set, but needed" ); - if( use_ret == -(POLARSSL_ERR_SSL_CA_CHAIN_REQUIRED) ) - snprintf( buf, buflen, "SSL - No CA Chain is set, but required to operate" ); - if( use_ret == -(POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE) ) - snprintf( buf, buflen, "SSL - An unexpected message was received from our peer" ); - if( use_ret == -(POLARSSL_ERR_SSL_FATAL_ALERT_MESSAGE) ) - { - snprintf( buf, buflen, "SSL - A fatal alert message was received from our peer" ); - return; - } - if( use_ret == -(POLARSSL_ERR_SSL_PEER_VERIFY_FAILED) ) - snprintf( buf, buflen, "SSL - Verification of our peer failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) ) - snprintf( buf, buflen, "SSL - The peer notified us that the connection is going to be closed" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO) ) - snprintf( buf, buflen, "SSL - Processing of the ClientHello handshake message failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO) ) - snprintf( buf, buflen, "SSL - Processing of the ServerHello handshake message failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE) ) - snprintf( buf, buflen, "SSL - Processing of the Certificate handshake message failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST) ) - snprintf( buf, buflen, "SSL - Processing of the CertificateRequest handshake message failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE) ) - snprintf( buf, buflen, "SSL - Processing of the ServerKeyExchange handshake message failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO_DONE) ) - snprintf( buf, buflen, "SSL - Processing of the ServerHelloDone handshake message failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE) ) - snprintf( buf, buflen, "SSL - Processing of the ClientKeyExchange handshake message failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_DHM_RP) ) - snprintf( buf, buflen, "SSL - Processing of the ClientKeyExchange handshake message failed in DHM Read Public" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_DHM_CS) ) - snprintf( buf, buflen, "SSL - Processing of the ClientKeyExchange handshake message failed in DHM Calculate Secret" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY) ) - snprintf( buf, buflen, "SSL - Processing of the CertificateVerify handshake message failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC) ) - snprintf( buf, buflen, "SSL - Processing of the ChangeCipherSpec handshake message failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_FINISHED) ) - snprintf( buf, buflen, "SSL - Processing of the Finished handshake message failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_MALLOC_FAILED) ) - snprintf( buf, buflen, "SSL - Memory allocation failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_HW_ACCEL_FAILED) ) - snprintf( buf, buflen, "SSL - Hardware acceleration function returned with error" ); - if( use_ret == -(POLARSSL_ERR_SSL_HW_ACCEL_FALLTHROUGH) ) - snprintf( buf, buflen, "SSL - Hardware acceleration function skipped / left alone data" ); - if( use_ret == -(POLARSSL_ERR_SSL_COMPRESSION_FAILED) ) - snprintf( buf, buflen, "SSL - Processing of the compression / decompression failed" ); - if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION) ) - snprintf( buf, buflen, "SSL - Handshake protocol not within min/max boundaries" ); -#endif /* POLARSSL_SSL_TLS_C */ - -#if defined(POLARSSL_X509_PARSE_C) - if( use_ret == -(POLARSSL_ERR_X509_FEATURE_UNAVAILABLE) ) - snprintf( buf, buflen, "X509 - Unavailable feature, e.g. RSA hashing/encryption combination" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_INVALID_PEM) ) - snprintf( buf, buflen, "X509 - The PEM-encoded certificate contains invalid elements, e.g. invalid character" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_INVALID_FORMAT) ) - snprintf( buf, buflen, "X509 - The certificate format is invalid, e.g. different type expected" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_INVALID_VERSION) ) - snprintf( buf, buflen, "X509 - The certificate version element is invalid" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_INVALID_SERIAL) ) - snprintf( buf, buflen, "X509 - The serial tag or value is invalid" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_INVALID_ALG) ) - snprintf( buf, buflen, "X509 - The algorithm tag or value is invalid" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_INVALID_NAME) ) - snprintf( buf, buflen, "X509 - The name tag or value is invalid" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_INVALID_DATE) ) - snprintf( buf, buflen, "X509 - The date tag or value is invalid" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_INVALID_PUBKEY) ) - snprintf( buf, buflen, "X509 - The pubkey tag or value is invalid (only RSA is supported)" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE) ) - snprintf( buf, buflen, "X509 - The signature tag or value invalid" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS) ) - snprintf( buf, buflen, "X509 - The extension tag or value is invalid" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION) ) - snprintf( buf, buflen, "X509 - Certificate or CRL has an unsupported version number" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG) ) - snprintf( buf, buflen, "X509 - Signature algorithm (oid) is unsupported" ); - if( use_ret == -(POLARSSL_ERR_X509_UNKNOWN_PK_ALG) ) - snprintf( buf, buflen, "X509 - Key algorithm is unsupported (only RSA is supported)" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_SIG_MISMATCH) ) - snprintf( buf, buflen, "X509 - Certificate signature algorithms do not match. (see \\c ::x509_cert sig_oid)" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_VERIFY_FAILED) ) - snprintf( buf, buflen, "X509 - Certificate verification failed, e.g. CRL, CA or signature check failed" ); - if( use_ret == -(POLARSSL_ERR_X509_KEY_INVALID_VERSION) ) - snprintf( buf, buflen, "X509 - Unsupported RSA key version" ); - if( use_ret == -(POLARSSL_ERR_X509_KEY_INVALID_FORMAT) ) - snprintf( buf, buflen, "X509 - Invalid RSA key tag or value" ); - if( use_ret == -(POLARSSL_ERR_X509_CERT_UNKNOWN_FORMAT) ) - snprintf( buf, buflen, "X509 - Format not recognized as DER or PEM" ); - if( use_ret == -(POLARSSL_ERR_X509_INVALID_INPUT) ) - snprintf( buf, buflen, "X509 - Input invalid" ); - if( use_ret == -(POLARSSL_ERR_X509_MALLOC_FAILED) ) - snprintf( buf, buflen, "X509 - Allocation of memory failed" ); - if( use_ret == -(POLARSSL_ERR_X509_FILE_IO_ERROR) ) - snprintf( buf, buflen, "X509 - Read/write of file failed" ); - if( use_ret == -(POLARSSL_ERR_X509_PASSWORD_REQUIRED) ) - snprintf( buf, buflen, "X509 - Private key password can't be empty" ); - if( use_ret == -(POLARSSL_ERR_X509_PASSWORD_MISMATCH) ) - snprintf( buf, buflen, "X509 - Given private key password does not allow for correct decryption" ); -#endif /* POLARSSL_X509_PARSE_C */ - - if( strlen( buf ) == 0 ) - snprintf( buf, buflen, "UNKNOWN ERROR CODE (%04X)", use_ret ); - } - - use_ret = ret & ~0xFF80; - - if( use_ret == 0 ) - return; - - // If high level code is present, make a concatenation between both - // error strings. - // - len = strlen( buf ); - - if( len > 0 ) - { - if( buflen - len < 5 ) - return; - - snprintf( buf + len, buflen - len, " : " ); - - buf += len + 3; - buflen -= len + 3; - } - - // Low level error codes - // -#if defined(POLARSSL_AES_C) - if( use_ret == -(POLARSSL_ERR_AES_INVALID_KEY_LENGTH) ) - snprintf( buf, buflen, "AES - Invalid key length" ); - if( use_ret == -(POLARSSL_ERR_AES_INVALID_INPUT_LENGTH) ) - snprintf( buf, buflen, "AES - Invalid data input length" ); -#endif /* POLARSSL_AES_C */ - -#if defined(POLARSSL_ASN1_PARSE_C) - if( use_ret == -(POLARSSL_ERR_ASN1_OUT_OF_DATA) ) - snprintf( buf, buflen, "ASN1 - Out of data when parsing an ASN1 data structure" ); - if( use_ret == -(POLARSSL_ERR_ASN1_UNEXPECTED_TAG) ) - snprintf( buf, buflen, "ASN1 - ASN1 tag was of an unexpected value" ); - if( use_ret == -(POLARSSL_ERR_ASN1_INVALID_LENGTH) ) - snprintf( buf, buflen, "ASN1 - Error when trying to determine the length or invalid length" ); - if( use_ret == -(POLARSSL_ERR_ASN1_LENGTH_MISMATCH) ) - snprintf( buf, buflen, "ASN1 - Actual length differs from expected length" ); - if( use_ret == -(POLARSSL_ERR_ASN1_INVALID_DATA) ) - snprintf( buf, buflen, "ASN1 - Data is invalid. (not used)" ); - if( use_ret == -(POLARSSL_ERR_ASN1_MALLOC_FAILED) ) - snprintf( buf, buflen, "ASN1 - Memory allocation failed" ); - if( use_ret == -(POLARSSL_ERR_ASN1_BUF_TOO_SMALL) ) - snprintf( buf, buflen, "ASN1 - Buffer too small when writing ASN.1 data structure" ); -#endif /* POLARSSL_ASN1_PARSE_C */ - -#if defined(POLARSSL_BASE64_C) - if( use_ret == -(POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL) ) - snprintf( buf, buflen, "BASE64 - Output buffer too small" ); - if( use_ret == -(POLARSSL_ERR_BASE64_INVALID_CHARACTER) ) - snprintf( buf, buflen, "BASE64 - Invalid character in input" ); -#endif /* POLARSSL_BASE64_C */ - -#if defined(POLARSSL_BIGNUM_C) - if( use_ret == -(POLARSSL_ERR_MPI_FILE_IO_ERROR) ) - snprintf( buf, buflen, "BIGNUM - An error occurred while reading from or writing to a file" ); - if( use_ret == -(POLARSSL_ERR_MPI_BAD_INPUT_DATA) ) - snprintf( buf, buflen, "BIGNUM - Bad input parameters to function" ); - if( use_ret == -(POLARSSL_ERR_MPI_INVALID_CHARACTER) ) - snprintf( buf, buflen, "BIGNUM - There is an invalid character in the digit string" ); - if( use_ret == -(POLARSSL_ERR_MPI_BUFFER_TOO_SMALL) ) - snprintf( buf, buflen, "BIGNUM - The buffer is too small to write to" ); - if( use_ret == -(POLARSSL_ERR_MPI_NEGATIVE_VALUE) ) - snprintf( buf, buflen, "BIGNUM - The input arguments are negative or result in illegal output" ); - if( use_ret == -(POLARSSL_ERR_MPI_DIVISION_BY_ZERO) ) - snprintf( buf, buflen, "BIGNUM - The input argument for division is zero, which is not allowed" ); - if( use_ret == -(POLARSSL_ERR_MPI_NOT_ACCEPTABLE) ) - snprintf( buf, buflen, "BIGNUM - The input arguments are not acceptable" ); - if( use_ret == -(POLARSSL_ERR_MPI_MALLOC_FAILED) ) - snprintf( buf, buflen, "BIGNUM - Memory allocation failed" ); -#endif /* POLARSSL_BIGNUM_C */ - -#if defined(POLARSSL_BLOWFISH_C) - if( use_ret == -(POLARSSL_ERR_BLOWFISH_INVALID_KEY_LENGTH) ) - snprintf( buf, buflen, "BLOWFISH - Invalid key length" ); - if( use_ret == -(POLARSSL_ERR_BLOWFISH_INVALID_INPUT_LENGTH) ) - snprintf( buf, buflen, "BLOWFISH - Invalid data input length" ); -#endif /* POLARSSL_BLOWFISH_C */ - -#if defined(POLARSSL_CAMELLIA_C) - if( use_ret == -(POLARSSL_ERR_CAMELLIA_INVALID_KEY_LENGTH) ) - snprintf( buf, buflen, "CAMELLIA - Invalid key length" ); - if( use_ret == -(POLARSSL_ERR_CAMELLIA_INVALID_INPUT_LENGTH) ) - snprintf( buf, buflen, "CAMELLIA - Invalid data input length" ); -#endif /* POLARSSL_CAMELLIA_C */ - -#if defined(POLARSSL_CTR_DRBG_C) - if( use_ret == -(POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED) ) - snprintf( buf, buflen, "CTR_DRBG - The entropy source failed" ); - if( use_ret == -(POLARSSL_ERR_CTR_DRBG_REQUEST_TOO_BIG) ) - snprintf( buf, buflen, "CTR_DRBG - Too many random requested in single call" ); - if( use_ret == -(POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG) ) - snprintf( buf, buflen, "CTR_DRBG - Input too large (Entropy + additional)" ); - if( use_ret == -(POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR) ) - snprintf( buf, buflen, "CTR_DRBG - Read/write error in file" ); -#endif /* POLARSSL_CTR_DRBG_C */ - -#if defined(POLARSSL_DES_C) - if( use_ret == -(POLARSSL_ERR_DES_INVALID_INPUT_LENGTH) ) - snprintf( buf, buflen, "DES - The data input has an invalid length" ); -#endif /* POLARSSL_DES_C */ - -#if defined(POLARSSL_ENTROPY_C) - if( use_ret == -(POLARSSL_ERR_ENTROPY_SOURCE_FAILED) ) - snprintf( buf, buflen, "ENTROPY - Critical entropy source failure" ); - if( use_ret == -(POLARSSL_ERR_ENTROPY_MAX_SOURCES) ) - snprintf( buf, buflen, "ENTROPY - No more sources can be added" ); - if( use_ret == -(POLARSSL_ERR_ENTROPY_NO_SOURCES_DEFINED) ) - snprintf( buf, buflen, "ENTROPY - No sources have been added to poll" ); -#endif /* POLARSSL_ENTROPY_C */ - -#if defined(POLARSSL_GCM_C) - if( use_ret == -(POLARSSL_ERR_GCM_AUTH_FAILED) ) - snprintf( buf, buflen, "GCM - Authenticated decryption failed" ); - if( use_ret == -(POLARSSL_ERR_GCM_BAD_INPUT) ) - snprintf( buf, buflen, "GCM - Bad input parameters to function" ); -#endif /* POLARSSL_GCM_C */ - -#if defined(POLARSSL_MD2_C) - if( use_ret == -(POLARSSL_ERR_MD2_FILE_IO_ERROR) ) - snprintf( buf, buflen, "MD2 - Read/write error in file" ); -#endif /* POLARSSL_MD2_C */ - -#if defined(POLARSSL_MD4_C) - if( use_ret == -(POLARSSL_ERR_MD4_FILE_IO_ERROR) ) - snprintf( buf, buflen, "MD4 - Read/write error in file" ); -#endif /* POLARSSL_MD4_C */ - -#if defined(POLARSSL_MD5_C) - if( use_ret == -(POLARSSL_ERR_MD5_FILE_IO_ERROR) ) - snprintf( buf, buflen, "MD5 - Read/write error in file" ); -#endif /* POLARSSL_MD5_C */ - -#if defined(POLARSSL_NET_C) - if( use_ret == -(POLARSSL_ERR_NET_UNKNOWN_HOST) ) - snprintf( buf, buflen, "NET - Failed to get an IP address for the given hostname" ); - if( use_ret == -(POLARSSL_ERR_NET_SOCKET_FAILED) ) - snprintf( buf, buflen, "NET - Failed to open a socket" ); - if( use_ret == -(POLARSSL_ERR_NET_CONNECT_FAILED) ) - snprintf( buf, buflen, "NET - The connection to the given server / port failed" ); - if( use_ret == -(POLARSSL_ERR_NET_BIND_FAILED) ) - snprintf( buf, buflen, "NET - Binding of the socket failed" ); - if( use_ret == -(POLARSSL_ERR_NET_LISTEN_FAILED) ) - snprintf( buf, buflen, "NET - Could not listen on the socket" ); - if( use_ret == -(POLARSSL_ERR_NET_ACCEPT_FAILED) ) - snprintf( buf, buflen, "NET - Could not accept the incoming connection" ); - if( use_ret == -(POLARSSL_ERR_NET_RECV_FAILED) ) - snprintf( buf, buflen, "NET - Reading information from the socket failed" ); - if( use_ret == -(POLARSSL_ERR_NET_SEND_FAILED) ) - snprintf( buf, buflen, "NET - Sending information through the socket failed" ); - if( use_ret == -(POLARSSL_ERR_NET_CONN_RESET) ) - snprintf( buf, buflen, "NET - Connection was reset by peer" ); - if( use_ret == -(POLARSSL_ERR_NET_WANT_READ) ) - snprintf( buf, buflen, "NET - Connection requires a read call" ); - if( use_ret == -(POLARSSL_ERR_NET_WANT_WRITE) ) - snprintf( buf, buflen, "NET - Connection requires a write call" ); -#endif /* POLARSSL_NET_C */ - -#if defined(POLARSSL_PADLOCK_C) - if( use_ret == -(POLARSSL_ERR_PADLOCK_DATA_MISALIGNED) ) - snprintf( buf, buflen, "PADLOCK - Input data should be aligned" ); -#endif /* POLARSSL_PADLOCK_C */ - -#if defined(POLARSSL_PBKDF2_C) - if( use_ret == -(POLARSSL_ERR_PBKDF2_BAD_INPUT_DATA) ) - snprintf( buf, buflen, "PBKDF2 - Bad input parameters to function" ); -#endif /* POLARSSL_PBKDF2_C */ - -#if defined(POLARSSL_SHA1_C) - if( use_ret == -(POLARSSL_ERR_SHA1_FILE_IO_ERROR) ) - snprintf( buf, buflen, "SHA1 - Read/write error in file" ); -#endif /* POLARSSL_SHA1_C */ - -#if defined(POLARSSL_SHA2_C) - if( use_ret == -(POLARSSL_ERR_SHA2_FILE_IO_ERROR) ) - snprintf( buf, buflen, "SHA2 - Read/write error in file" ); -#endif /* POLARSSL_SHA2_C */ - -#if defined(POLARSSL_SHA4_C) - if( use_ret == -(POLARSSL_ERR_SHA4_FILE_IO_ERROR) ) - snprintf( buf, buflen, "SHA4 - Read/write error in file" ); -#endif /* POLARSSL_SHA4_C */ - -#if defined(POLARSSL_XTEA_C) - if( use_ret == -(POLARSSL_ERR_XTEA_INVALID_INPUT_LENGTH) ) - snprintf( buf, buflen, "XTEA - The data input has an invalid length" ); -#endif /* POLARSSL_XTEA_C */ - - if( strlen( buf ) != 0 ) - return; - - snprintf( buf, buflen, "UNKNOWN ERROR CODE (%04X)", use_ret ); -} - -#else /* POLARSSL_ERROR_C */ - -#if defined(POLARSSL_ERROR_STRERROR_DUMMY) - -#include - -/* - * Provide an non-function in case POLARSSL_ERROR_C is not defined - */ -void error_strerror( int ret, char *buf, size_t buflen ) -{ - ((void) ret); - - if( buflen > 0 ) - buf[0] = '\0'; -} - -#endif /* POLARSSL_ERROR_STRERROR_DUMMY */ -#endif /* POLARSSL_ERROR_C */ diff --git a/makerom/polarssl/gcm.c b/makerom/polarssl/gcm.c deleted file mode 100644 index 60dc0cd0..00000000 --- a/makerom/polarssl/gcm.c +++ /dev/null @@ -1,621 +0,0 @@ -/* - * NIST SP800-38D compliant GCM implementation - * - * Copyright (C) 2006-2012, 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. - */ -/* - * http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf - */ -#include "polarssl/config.h" - -#if defined(POLARSSL_GCM_C) - -#include "polarssl/gcm.h" - -/* - * 32-bit integer manipulation macros (big endian) - */ -#ifndef GET_UINT32_BE -#define GET_UINT32_BE(n,b,i) \ -{ \ - (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ - | ( (uint32_t) (b)[(i) + 1] << 16 ) \ - | ( (uint32_t) (b)[(i) + 2] << 8 ) \ - | ( (uint32_t) (b)[(i) + 3] ); \ -} -#endif - -#ifndef PUT_UINT32_BE -#define PUT_UINT32_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 - -static void gcm_gen_table( gcm_context *ctx ) -{ - int i, j; - uint64_t hi, lo; - uint64_t vl, vh; - unsigned char h[16]; - - memset( h, 0, 16 ); - aes_crypt_ecb( &ctx->aes_ctx, AES_ENCRYPT, h, h ); - - ctx->HH[0] = 0; - ctx->HL[0] = 0; - - GET_UINT32_BE( hi, h, 0 ); - GET_UINT32_BE( lo, h, 4 ); - vh = (uint64_t) hi << 32 | lo; - - GET_UINT32_BE( hi, h, 8 ); - GET_UINT32_BE( lo, h, 12 ); - vl = (uint64_t) hi << 32 | lo; - - ctx->HL[8] = vl; - ctx->HH[8] = vh; - - for( i = 4; i > 0; i >>= 1 ) - { - uint32_t T = ( vl & 1 ) * 0xe1000000U; - vl = ( vh << 63 ) | ( vl >> 1 ); - vh = ( vh >> 1 ) ^ ( (uint64_t) T << 32); - - ctx->HL[i] = vl; - ctx->HH[i] = vh; - } - - for (i = 2; i < 16; i <<= 1 ) - { - uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i; - vh = *HiH; - vl = *HiL; - for( j = 1; j < i; j++ ) - { - HiH[j] = vh ^ ctx->HH[j]; - HiL[j] = vl ^ ctx->HL[j]; - } - } - -} - -int gcm_init( gcm_context *ctx, const unsigned char *key, unsigned int keysize ) -{ - int ret; - - memset( ctx, 0, sizeof(gcm_context) ); - - if( ( ret = aes_setkey_enc( &ctx->aes_ctx, key, keysize ) ) != 0 ) - return( ret ); - - gcm_gen_table( ctx ); - - return( 0 ); -} - -static const uint64_t last4[16] = -{ - 0x0000, 0x1c20, 0x3840, 0x2460, - 0x7080, 0x6ca0, 0x48c0, 0x54e0, - 0xe100, 0xfd20, 0xd940, 0xc560, - 0x9180, 0x8da0, 0xa9c0, 0xb5e0 -}; - -void gcm_mult( gcm_context *ctx, const unsigned char x[16], unsigned char output[16] ) -{ - int i = 0; - unsigned char z[16]; - unsigned char lo, hi, rem; - uint64_t zh, zl; - - memset( z, 0x00, 16 ); - - lo = x[15] & 0xf; - hi = x[15] >> 4; - - zh = ctx->HH[lo]; - zl = ctx->HL[lo]; - - for( i = 15; i >= 0; i-- ) - { - lo = x[i] & 0xf; - hi = x[i] >> 4; - - if( i != 15 ) - { - rem = (unsigned char) zl & 0xf; - zl = ( zh << 60 ) | ( zl >> 4 ); - zh = ( zh >> 4 ); - zh ^= (uint64_t) last4[rem] << 48; - zh ^= ctx->HH[lo]; - zl ^= ctx->HL[lo]; - - } - - rem = (unsigned char) zl & 0xf; - zl = ( zh << 60 ) | ( zl >> 4 ); - zh = ( zh >> 4 ); - zh ^= (uint64_t) last4[rem] << 48; - zh ^= ctx->HH[hi]; - zl ^= ctx->HL[hi]; - } - - PUT_UINT32_BE( zh >> 32, output, 0 ); - PUT_UINT32_BE( zh, output, 4 ); - PUT_UINT32_BE( zl >> 32, output, 8 ); - PUT_UINT32_BE( zl, output, 12 ); -} - -int gcm_crypt_and_tag( gcm_context *ctx, - int mode, - size_t length, - const unsigned char *iv, - size_t iv_len, - const unsigned char *add, - size_t add_len, - const unsigned char *input, - unsigned char *output, - size_t tag_len, - unsigned char *tag ) -{ - unsigned char y[16]; - unsigned char ectr[16]; - unsigned char buf[16]; - unsigned char work_buf[16]; - size_t i; - const unsigned char *p; - unsigned char *out_p = output; - size_t use_len; - uint64_t orig_len = length * 8; - uint64_t orig_add_len = add_len * 8; - - memset( y, 0x00, 16 ); - memset( work_buf, 0x00, 16 ); - memset( tag, 0x00, tag_len ); - memset( buf, 0x00, 16 ); - - if( ( mode == GCM_DECRYPT && output <= input && ( input - output ) < 8 ) || - ( output > input && (size_t) ( output - input ) < length ) ) - { - return( POLARSSL_ERR_GCM_BAD_INPUT ); - } - - if( iv_len == 12 ) - { - memcpy( y, iv, iv_len ); - y[15] = 1; - } - else - { - memset( work_buf, 0x00, 16 ); - PUT_UINT32_BE( iv_len * 8, work_buf, 12 ); - - p = iv; - while( iv_len > 0 ) - { - use_len = ( iv_len < 16 ) ? iv_len : 16; - - for( i = 0; i < use_len; i++ ) - y[i] ^= p[i]; - - gcm_mult( ctx, y, y ); - - iv_len -= use_len; - p += use_len; - } - - for( i = 0; i < 16; i++ ) - y[i] ^= work_buf[i]; - - gcm_mult( ctx, y, y ); - } - - aes_crypt_ecb( &ctx->aes_ctx, AES_ENCRYPT, y, ectr ); - memcpy( tag, ectr, tag_len ); - - p = add; - while( add_len > 0 ) - { - use_len = ( add_len < 16 ) ? add_len : 16; - - for( i = 0; i < use_len; i++ ) - buf[i] ^= p[i]; - - gcm_mult( ctx, buf, buf ); - - add_len -= use_len; - p += use_len; - } - - p = input; - while( length > 0 ) - { - use_len = ( length < 16 ) ? length : 16; - - for( i = 16; i > 12; i-- ) - if( ++y[i - 1] != 0 ) - break; - - aes_crypt_ecb( &ctx->aes_ctx, AES_ENCRYPT, y, ectr ); - - for( i = 0; i < use_len; i++ ) - { - out_p[i] = ectr[i] ^ p[i]; - if( mode == GCM_ENCRYPT ) - buf[i] ^= out_p[i]; - else - buf[i] ^= p[i]; - } - - gcm_mult( ctx, buf, buf ); - - length -= use_len; - p += use_len; - out_p += use_len; - } - - if( orig_len || orig_add_len ) - { - memset( work_buf, 0x00, 16 ); - - PUT_UINT32_BE( ( orig_add_len >> 32 ), work_buf, 0 ); - PUT_UINT32_BE( ( orig_add_len ), work_buf, 4 ); - PUT_UINT32_BE( ( orig_len >> 32 ), work_buf, 8 ); - PUT_UINT32_BE( ( orig_len ), work_buf, 12 ); - - for( i = 0; i < 16; i++ ) - buf[i] ^= work_buf[i]; - - gcm_mult( ctx, buf, buf ); - - for( i = 0; i < tag_len; i++ ) - tag[i] ^= buf[i]; - } - - return( 0 ); -} - -int gcm_auth_decrypt( gcm_context *ctx, - size_t length, - const unsigned char *iv, - size_t iv_len, - const unsigned char *add, - size_t add_len, - const unsigned char *tag, - size_t tag_len, - const unsigned char *input, - unsigned char *output ) -{ - unsigned char check_tag[16]; - - gcm_crypt_and_tag( ctx, GCM_DECRYPT, length, iv, iv_len, add, add_len, input, output, tag_len, check_tag ); - - if( memcmp( check_tag, tag, tag_len ) == 0 ) - return( 0 ); - - memset( output, 0, length ); - - return( POLARSSL_ERR_GCM_AUTH_FAILED ); -} - -#if defined(POLARSSL_SELF_TEST) - -#include - -/* - * GCM test vectors from: - * - * http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip - */ -#define MAX_TESTS 6 - -int key_index[MAX_TESTS] = - { 0, 0, 1, 1, 1, 1 }; - -unsigned char key[MAX_TESTS][32] = -{ - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, - 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, - 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, - 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 }, -}; - -size_t iv_len[MAX_TESTS] = - { 12, 12, 12, 12, 8, 60 }; - -int iv_index[MAX_TESTS] = - { 0, 0, 1, 1, 1, 2 }; - -unsigned char iv[MAX_TESTS][64] = -{ - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 }, - { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, - 0xde, 0xca, 0xf8, 0x88 }, - { 0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5, - 0x55, 0x90, 0x9c, 0x5a, 0xff, 0x52, 0x69, 0xaa, - 0x6a, 0x7a, 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1, - 0xe4, 0xc3, 0x03, 0xd2, 0xa3, 0x18, 0xa7, 0x28, - 0xc3, 0xc0, 0xc9, 0x51, 0x56, 0x80, 0x95, 0x39, - 0xfc, 0xf0, 0xe2, 0x42, 0x9a, 0x6b, 0x52, 0x54, - 0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde, 0x6a, 0x57, - 0xa6, 0x37, 0xb3, 0x9b }, -}; - -size_t add_len[MAX_TESTS] = - { 0, 0, 0, 20, 20, 20 }; - -int add_index[MAX_TESTS] = - { 0, 0, 0, 1, 1, 1 }; - -unsigned char additional[MAX_TESTS][64] = -{ - { 0x00 }, - { 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, - 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, - 0xab, 0xad, 0xda, 0xd2 }, -}; - -size_t pt_len[MAX_TESTS] = - { 0, 16, 64, 60, 60, 60 }; - -int pt_index[MAX_TESTS] = - { 0, 0, 1, 1, 1, 1 }; - -unsigned char pt[MAX_TESTS][64] = -{ - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, - 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, - 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, - 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, - 0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, - 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, - 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, - 0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55 }, -}; - -unsigned char ct[MAX_TESTS * 3][64] = -{ - { 0x00 }, - { 0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, - 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78 }, - { 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, - 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, - 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, - 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, - 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, - 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, - 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, - 0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59, 0x85 }, - { 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, - 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, - 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, - 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, - 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, - 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, - 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, - 0x3d, 0x58, 0xe0, 0x91 }, - { 0x61, 0x35, 0x3b, 0x4c, 0x28, 0x06, 0x93, 0x4a, - 0x77, 0x7f, 0xf5, 0x1f, 0xa2, 0x2a, 0x47, 0x55, - 0x69, 0x9b, 0x2a, 0x71, 0x4f, 0xcd, 0xc6, 0xf8, - 0x37, 0x66, 0xe5, 0xf9, 0x7b, 0x6c, 0x74, 0x23, - 0x73, 0x80, 0x69, 0x00, 0xe4, 0x9f, 0x24, 0xb2, - 0x2b, 0x09, 0x75, 0x44, 0xd4, 0x89, 0x6b, 0x42, - 0x49, 0x89, 0xb5, 0xe1, 0xeb, 0xac, 0x0f, 0x07, - 0xc2, 0x3f, 0x45, 0x98 }, - { 0x8c, 0xe2, 0x49, 0x98, 0x62, 0x56, 0x15, 0xb6, - 0x03, 0xa0, 0x33, 0xac, 0xa1, 0x3f, 0xb8, 0x94, - 0xbe, 0x91, 0x12, 0xa5, 0xc3, 0xa2, 0x11, 0xa8, - 0xba, 0x26, 0x2a, 0x3c, 0xca, 0x7e, 0x2c, 0xa7, - 0x01, 0xe4, 0xa9, 0xa4, 0xfb, 0xa4, 0x3c, 0x90, - 0xcc, 0xdc, 0xb2, 0x81, 0xd4, 0x8c, 0x7c, 0x6f, - 0xd6, 0x28, 0x75, 0xd2, 0xac, 0xa4, 0x17, 0x03, - 0x4c, 0x34, 0xae, 0xe5 }, - { 0x00 }, - { 0x98, 0xe7, 0x24, 0x7c, 0x07, 0xf0, 0xfe, 0x41, - 0x1c, 0x26, 0x7e, 0x43, 0x84, 0xb0, 0xf6, 0x00 }, - { 0x39, 0x80, 0xca, 0x0b, 0x3c, 0x00, 0xe8, 0x41, - 0xeb, 0x06, 0xfa, 0xc4, 0x87, 0x2a, 0x27, 0x57, - 0x85, 0x9e, 0x1c, 0xea, 0xa6, 0xef, 0xd9, 0x84, - 0x62, 0x85, 0x93, 0xb4, 0x0c, 0xa1, 0xe1, 0x9c, - 0x7d, 0x77, 0x3d, 0x00, 0xc1, 0x44, 0xc5, 0x25, - 0xac, 0x61, 0x9d, 0x18, 0xc8, 0x4a, 0x3f, 0x47, - 0x18, 0xe2, 0x44, 0x8b, 0x2f, 0xe3, 0x24, 0xd9, - 0xcc, 0xda, 0x27, 0x10, 0xac, 0xad, 0xe2, 0x56 }, - { 0x39, 0x80, 0xca, 0x0b, 0x3c, 0x00, 0xe8, 0x41, - 0xeb, 0x06, 0xfa, 0xc4, 0x87, 0x2a, 0x27, 0x57, - 0x85, 0x9e, 0x1c, 0xea, 0xa6, 0xef, 0xd9, 0x84, - 0x62, 0x85, 0x93, 0xb4, 0x0c, 0xa1, 0xe1, 0x9c, - 0x7d, 0x77, 0x3d, 0x00, 0xc1, 0x44, 0xc5, 0x25, - 0xac, 0x61, 0x9d, 0x18, 0xc8, 0x4a, 0x3f, 0x47, - 0x18, 0xe2, 0x44, 0x8b, 0x2f, 0xe3, 0x24, 0xd9, - 0xcc, 0xda, 0x27, 0x10 }, - { 0x0f, 0x10, 0xf5, 0x99, 0xae, 0x14, 0xa1, 0x54, - 0xed, 0x24, 0xb3, 0x6e, 0x25, 0x32, 0x4d, 0xb8, - 0xc5, 0x66, 0x63, 0x2e, 0xf2, 0xbb, 0xb3, 0x4f, - 0x83, 0x47, 0x28, 0x0f, 0xc4, 0x50, 0x70, 0x57, - 0xfd, 0xdc, 0x29, 0xdf, 0x9a, 0x47, 0x1f, 0x75, - 0xc6, 0x65, 0x41, 0xd4, 0xd4, 0xda, 0xd1, 0xc9, - 0xe9, 0x3a, 0x19, 0xa5, 0x8e, 0x8b, 0x47, 0x3f, - 0xa0, 0xf0, 0x62, 0xf7 }, - { 0xd2, 0x7e, 0x88, 0x68, 0x1c, 0xe3, 0x24, 0x3c, - 0x48, 0x30, 0x16, 0x5a, 0x8f, 0xdc, 0xf9, 0xff, - 0x1d, 0xe9, 0xa1, 0xd8, 0xe6, 0xb4, 0x47, 0xef, - 0x6e, 0xf7, 0xb7, 0x98, 0x28, 0x66, 0x6e, 0x45, - 0x81, 0xe7, 0x90, 0x12, 0xaf, 0x34, 0xdd, 0xd9, - 0xe2, 0xf0, 0x37, 0x58, 0x9b, 0x29, 0x2d, 0xb3, - 0xe6, 0x7c, 0x03, 0x67, 0x45, 0xfa, 0x22, 0xe7, - 0xe9, 0xb7, 0x37, 0x3b }, - { 0x00 }, - { 0xce, 0xa7, 0x40, 0x3d, 0x4d, 0x60, 0x6b, 0x6e, - 0x07, 0x4e, 0xc5, 0xd3, 0xba, 0xf3, 0x9d, 0x18 }, - { 0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07, - 0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d, - 0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9, - 0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa, - 0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d, - 0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38, - 0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a, - 0xbc, 0xc9, 0xf6, 0x62, 0x89, 0x80, 0x15, 0xad }, - { 0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07, - 0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d, - 0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9, - 0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa, - 0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d, - 0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38, - 0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a, - 0xbc, 0xc9, 0xf6, 0x62 }, - { 0xc3, 0x76, 0x2d, 0xf1, 0xca, 0x78, 0x7d, 0x32, - 0xae, 0x47, 0xc1, 0x3b, 0xf1, 0x98, 0x44, 0xcb, - 0xaf, 0x1a, 0xe1, 0x4d, 0x0b, 0x97, 0x6a, 0xfa, - 0xc5, 0x2f, 0xf7, 0xd7, 0x9b, 0xba, 0x9d, 0xe0, - 0xfe, 0xb5, 0x82, 0xd3, 0x39, 0x34, 0xa4, 0xf0, - 0x95, 0x4c, 0xc2, 0x36, 0x3b, 0xc7, 0x3f, 0x78, - 0x62, 0xac, 0x43, 0x0e, 0x64, 0xab, 0xe4, 0x99, - 0xf4, 0x7c, 0x9b, 0x1f }, - { 0x5a, 0x8d, 0xef, 0x2f, 0x0c, 0x9e, 0x53, 0xf1, - 0xf7, 0x5d, 0x78, 0x53, 0x65, 0x9e, 0x2a, 0x20, - 0xee, 0xb2, 0xb2, 0x2a, 0xaf, 0xde, 0x64, 0x19, - 0xa0, 0x58, 0xab, 0x4f, 0x6f, 0x74, 0x6b, 0xf4, - 0x0f, 0xc0, 0xc3, 0xb7, 0x80, 0xf2, 0x44, 0x45, - 0x2d, 0xa3, 0xeb, 0xf1, 0xc5, 0xd8, 0x2c, 0xde, - 0xa2, 0x41, 0x89, 0x97, 0x20, 0x0e, 0xf8, 0x2e, - 0x44, 0xae, 0x7e, 0x3f }, -}; - -unsigned char tag[MAX_TESTS * 3][16] = -{ - { 0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61, - 0x36, 0x7f, 0x1d, 0x57, 0xa4, 0xe7, 0x45, 0x5a }, - { 0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, - 0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf }, - { 0x4d, 0x5c, 0x2a, 0xf3, 0x27, 0xcd, 0x64, 0xa6, - 0x2c, 0xf3, 0x5a, 0xbd, 0x2b, 0xa6, 0xfa, 0xb4 }, - { 0x5b, 0xc9, 0x4f, 0xbc, 0x32, 0x21, 0xa5, 0xdb, - 0x94, 0xfa, 0xe9, 0x5a, 0xe7, 0x12, 0x1a, 0x47 }, - { 0x36, 0x12, 0xd2, 0xe7, 0x9e, 0x3b, 0x07, 0x85, - 0x56, 0x1b, 0xe1, 0x4a, 0xac, 0xa2, 0xfc, 0xcb }, - { 0x61, 0x9c, 0xc5, 0xae, 0xff, 0xfe, 0x0b, 0xfa, - 0x46, 0x2a, 0xf4, 0x3c, 0x16, 0x99, 0xd0, 0x50 }, - { 0xcd, 0x33, 0xb2, 0x8a, 0xc7, 0x73, 0xf7, 0x4b, - 0xa0, 0x0e, 0xd1, 0xf3, 0x12, 0x57, 0x24, 0x35 }, - { 0x2f, 0xf5, 0x8d, 0x80, 0x03, 0x39, 0x27, 0xab, - 0x8e, 0xf4, 0xd4, 0x58, 0x75, 0x14, 0xf0, 0xfb }, - { 0x99, 0x24, 0xa7, 0xc8, 0x58, 0x73, 0x36, 0xbf, - 0xb1, 0x18, 0x02, 0x4d, 0xb8, 0x67, 0x4a, 0x14 }, - { 0x25, 0x19, 0x49, 0x8e, 0x80, 0xf1, 0x47, 0x8f, - 0x37, 0xba, 0x55, 0xbd, 0x6d, 0x27, 0x61, 0x8c }, - { 0x65, 0xdc, 0xc5, 0x7f, 0xcf, 0x62, 0x3a, 0x24, - 0x09, 0x4f, 0xcc, 0xa4, 0x0d, 0x35, 0x33, 0xf8 }, - { 0xdc, 0xf5, 0x66, 0xff, 0x29, 0x1c, 0x25, 0xbb, - 0xb8, 0x56, 0x8f, 0xc3, 0xd3, 0x76, 0xa6, 0xd9 }, - { 0x53, 0x0f, 0x8a, 0xfb, 0xc7, 0x45, 0x36, 0xb9, - 0xa9, 0x63, 0xb4, 0xf1, 0xc4, 0xcb, 0x73, 0x8b }, - { 0xd0, 0xd1, 0xc8, 0xa7, 0x99, 0x99, 0x6b, 0xf0, - 0x26, 0x5b, 0x98, 0xb5, 0xd4, 0x8a, 0xb9, 0x19 }, - { 0xb0, 0x94, 0xda, 0xc5, 0xd9, 0x34, 0x71, 0xbd, - 0xec, 0x1a, 0x50, 0x22, 0x70, 0xe3, 0xcc, 0x6c }, - { 0x76, 0xfc, 0x6e, 0xce, 0x0f, 0x4e, 0x17, 0x68, - 0xcd, 0xdf, 0x88, 0x53, 0xbb, 0x2d, 0x55, 0x1b }, - { 0x3a, 0x33, 0x7d, 0xbf, 0x46, 0xa7, 0x92, 0xc4, - 0x5e, 0x45, 0x49, 0x13, 0xfe, 0x2e, 0xa8, 0xf2 }, - { 0xa4, 0x4a, 0x82, 0x66, 0xee, 0x1c, 0x8e, 0xb0, - 0xc8, 0xb5, 0xd4, 0xcf, 0x5a, 0xe9, 0xf1, 0x9a }, -}; - -int gcm_self_test( int verbose ) -{ - gcm_context ctx; - unsigned char buf[64]; - unsigned char tag_buf[16]; - int i, j, ret; - - for( j = 0; j < 3; j++ ) - { - int key_len = 128 + 64 * j; - - for( i = 0; i < MAX_TESTS; i++ ) - { - printf( " AES-GCM-%3d #%d (%s): ", key_len, i, "enc" ); - gcm_init( &ctx, key[key_index[i]], key_len ); - - ret = gcm_crypt_and_tag( &ctx, GCM_ENCRYPT, - pt_len[i], - iv[iv_index[i]], iv_len[i], - additional[add_index[i]], add_len[i], - pt[pt_index[i]], buf, 16, tag_buf ); - - if( ret != 0 || - memcmp( buf, ct[j * 6 + i], pt_len[i] ) != 0 || - memcmp( tag_buf, tag[j * 6 + i], 16 ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( verbose != 0 ) - printf( "passed\n" ); - - printf( " AES-GCM-%3d #%d (%s): ", key_len, i, "dec" ); - gcm_init( &ctx, key[key_index[i]], key_len ); - - ret = gcm_crypt_and_tag( &ctx, GCM_DECRYPT, - pt_len[i], - iv[iv_index[i]], iv_len[i], - additional[add_index[i]], add_len[i], - ct[j * 6 + i], buf, 16, tag_buf ); - - if( ret != 0 || - memcmp( buf, pt[pt_index[i]], pt_len[i] ) != 0 || - memcmp( tag_buf, tag[j * 6 + i], 16 ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( verbose != 0 ) - printf( "passed\n" ); - } - } - - printf( "\n" ); - - return( 0 ); -} - -#endif - -#endif diff --git a/makerom/polarssl/havege.c b/makerom/polarssl/havege.c deleted file mode 100644 index ff302c57..00000000 --- a/makerom/polarssl/havege.c +++ /dev/null @@ -1,231 +0,0 @@ -/** - * \brief HAVEGE: HArdware Volatile Entropy Gathering and Expansion - * - * 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 HAVEGE RNG was designed by Andre Seznec in 2002. - * - * http://www.irisa.fr/caps/projects/hipsor/publi.php - * - * Contact: seznec(at)irisa_dot_fr - orocheco(at)irisa_dot_fr - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_HAVEGE_C) - -#include "polarssl/havege.h" -#include "polarssl/timing.h" - -#include -#include - -/* ------------------------------------------------------------------------ - * On average, one iteration accesses two 8-word blocks in the havege WALK - * table, and generates 16 words in the RES array. - * - * The data read in the WALK table is updated and permuted after each use. - * The result of the hardware clock counter read is used for this update. - * - * 25 conditional tests are present. The conditional tests are grouped in - * two nested groups of 12 conditional tests and 1 test that controls the - * permutation; on average, there should be 6 tests executed and 3 of them - * should be mispredicted. - * ------------------------------------------------------------------------ - */ - -#define SWAP(X,Y) { int *T = X; X = Y; Y = T; } - -#define TST1_ENTER if( PTEST & 1 ) { PTEST ^= 3; PTEST >>= 1; -#define TST2_ENTER if( PTEST & 1 ) { PTEST ^= 3; PTEST >>= 1; - -#define TST1_LEAVE U1++; } -#define TST2_LEAVE U2++; } - -#define ONE_ITERATION \ - \ - PTEST = PT1 >> 20; \ - \ - TST1_ENTER TST1_ENTER TST1_ENTER TST1_ENTER \ - TST1_ENTER TST1_ENTER TST1_ENTER TST1_ENTER \ - TST1_ENTER TST1_ENTER TST1_ENTER TST1_ENTER \ - \ - TST1_LEAVE TST1_LEAVE TST1_LEAVE TST1_LEAVE \ - TST1_LEAVE TST1_LEAVE TST1_LEAVE TST1_LEAVE \ - TST1_LEAVE TST1_LEAVE TST1_LEAVE TST1_LEAVE \ - \ - PTX = (PT1 >> 18) & 7; \ - PT1 &= 0x1FFF; \ - PT2 &= 0x1FFF; \ - CLK = (int) hardclock(); \ - \ - i = 0; \ - A = &WALK[PT1 ]; RES[i++] ^= *A; \ - B = &WALK[PT2 ]; RES[i++] ^= *B; \ - C = &WALK[PT1 ^ 1]; RES[i++] ^= *C; \ - D = &WALK[PT2 ^ 4]; RES[i++] ^= *D; \ - \ - IN = (*A >> (1)) ^ (*A << (31)) ^ CLK; \ - *A = (*B >> (2)) ^ (*B << (30)) ^ CLK; \ - *B = IN ^ U1; \ - *C = (*C >> (3)) ^ (*C << (29)) ^ CLK; \ - *D = (*D >> (4)) ^ (*D << (28)) ^ CLK; \ - \ - A = &WALK[PT1 ^ 2]; RES[i++] ^= *A; \ - B = &WALK[PT2 ^ 2]; RES[i++] ^= *B; \ - C = &WALK[PT1 ^ 3]; RES[i++] ^= *C; \ - D = &WALK[PT2 ^ 6]; RES[i++] ^= *D; \ - \ - if( PTEST & 1 ) SWAP( A, C ); \ - \ - IN = (*A >> (5)) ^ (*A << (27)) ^ CLK; \ - *A = (*B >> (6)) ^ (*B << (26)) ^ CLK; \ - *B = IN; CLK = (int) hardclock(); \ - *C = (*C >> (7)) ^ (*C << (25)) ^ CLK; \ - *D = (*D >> (8)) ^ (*D << (24)) ^ CLK; \ - \ - A = &WALK[PT1 ^ 4]; \ - B = &WALK[PT2 ^ 1]; \ - \ - PTEST = PT2 >> 1; \ - \ - PT2 = (RES[(i - 8) ^ PTY] ^ WALK[PT2 ^ PTY ^ 7]); \ - PT2 = ((PT2 & 0x1FFF) & (~8)) ^ ((PT1 ^ 8) & 0x8); \ - PTY = (PT2 >> 10) & 7; \ - \ - TST2_ENTER TST2_ENTER TST2_ENTER TST2_ENTER \ - TST2_ENTER TST2_ENTER TST2_ENTER TST2_ENTER \ - TST2_ENTER TST2_ENTER TST2_ENTER TST2_ENTER \ - \ - TST2_LEAVE TST2_LEAVE TST2_LEAVE TST2_LEAVE \ - TST2_LEAVE TST2_LEAVE TST2_LEAVE TST2_LEAVE \ - TST2_LEAVE TST2_LEAVE TST2_LEAVE TST2_LEAVE \ - \ - C = &WALK[PT1 ^ 5]; \ - D = &WALK[PT2 ^ 5]; \ - \ - RES[i++] ^= *A; \ - RES[i++] ^= *B; \ - RES[i++] ^= *C; \ - RES[i++] ^= *D; \ - \ - IN = (*A >> ( 9)) ^ (*A << (23)) ^ CLK; \ - *A = (*B >> (10)) ^ (*B << (22)) ^ CLK; \ - *B = IN ^ U2; \ - *C = (*C >> (11)) ^ (*C << (21)) ^ CLK; \ - *D = (*D >> (12)) ^ (*D << (20)) ^ CLK; \ - \ - A = &WALK[PT1 ^ 6]; RES[i++] ^= *A; \ - B = &WALK[PT2 ^ 3]; RES[i++] ^= *B; \ - C = &WALK[PT1 ^ 7]; RES[i++] ^= *C; \ - D = &WALK[PT2 ^ 7]; RES[i++] ^= *D; \ - \ - IN = (*A >> (13)) ^ (*A << (19)) ^ CLK; \ - *A = (*B >> (14)) ^ (*B << (18)) ^ CLK; \ - *B = IN; \ - *C = (*C >> (15)) ^ (*C << (17)) ^ CLK; \ - *D = (*D >> (16)) ^ (*D << (16)) ^ CLK; \ - \ - PT1 = ( RES[(i - 8) ^ PTX] ^ \ - WALK[PT1 ^ PTX ^ 7] ) & (~1); \ - PT1 ^= (PT2 ^ 0x10) & 0x10; \ - \ - for( n++, i = 0; i < 16; i++ ) \ - hs->pool[n % COLLECT_SIZE] ^= RES[i]; - -/* - * Entropy gathering function - */ -static void havege_fill( havege_state *hs ) -{ - int i, n = 0; - int U1, U2, *A, *B, *C, *D; - int PT1, PT2, *WALK, RES[16]; - int PTX, PTY, CLK, PTEST, IN; - - WALK = hs->WALK; - PT1 = hs->PT1; - PT2 = hs->PT2; - - PTX = U1 = 0; - PTY = U2 = 0; - - memset( RES, 0, sizeof( RES ) ); - - while( n < COLLECT_SIZE * 4 ) - { - ONE_ITERATION - ONE_ITERATION - ONE_ITERATION - ONE_ITERATION - } - - hs->PT1 = PT1; - hs->PT2 = PT2; - - hs->offset[0] = 0; - hs->offset[1] = COLLECT_SIZE / 2; -} - -/* - * HAVEGE initialization - */ -void havege_init( havege_state *hs ) -{ - memset( hs, 0, sizeof( havege_state ) ); - - havege_fill( hs ); -} - -/* - * HAVEGE rand function - */ -int havege_random( void *p_rng, unsigned char *buf, size_t len ) -{ - int val; - size_t use_len; - havege_state *hs = (havege_state *) p_rng; - unsigned char *p = buf; - - while( len > 0 ) - { - use_len = len; - if( use_len > sizeof(int) ) - use_len = sizeof(int); - - if( hs->offset[1] >= COLLECT_SIZE ) - havege_fill( hs ); - - val = hs->pool[hs->offset[0]++]; - val ^= hs->pool[hs->offset[1]++]; - - memcpy( p, &val, use_len ); - - len -= use_len; - p += use_len; - } - - return( 0 ); -} - -#endif diff --git a/makerom/polarssl/md2.c b/makerom/polarssl/md2.c deleted file mode 100644 index 2c8754a8..00000000 --- a/makerom/polarssl/md2.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * RFC 1115/1319 compliant MD2 implementation - * - * Copyright (C) 2006-2013, 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 MD2 algorithm was designed by Ron Rivest in 1989. - * - * http://www.ietf.org/rfc/rfc1115.txt - * http://www.ietf.org/rfc/rfc1319.txt - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_MD2_C) - -#include "polarssl/md2.h" - -#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) -#include -#endif - -#if !defined(POLARSSL_MD2_ALT) - -static const unsigned char PI_SUBST[256] = -{ - 0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36, - 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13, 0x62, 0xA7, 0x05, 0xF3, - 0xC0, 0xC7, 0x73, 0x8C, 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, - 0x82, 0xCA, 0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16, - 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12, 0xBE, 0x4E, - 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, 0xA0, 0xFB, 0xF5, 0x8E, - 0xBB, 0x2F, 0xEE, 0x7A, 0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, - 0x07, 0x3F, 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21, - 0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, 0x35, 0x3E, - 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03, 0xFF, 0x19, 0x30, 0xB3, - 0x48, 0xA5, 0xB5, 0xD1, 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, - 0xAA, 0xC6, 0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6, - 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1, 0x45, 0x9D, - 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5B, 0xCF, 0x65, - 0xE6, 0x2D, 0xA8, 0x02, 0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, - 0xB9, 0xF6, 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F, - 0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, 0xC3, 0x5C, - 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26, 0x2C, 0x53, 0x0D, 0x6E, - 0x85, 0x28, 0x84, 0x09, 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, - 0x4D, 0x52, 0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA, - 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A, 0x78, 0x88, - 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, 0xE9, 0xCB, 0xD5, 0xFE, - 0x3B, 0x00, 0x1D, 0x39, 0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, - 0xD0, 0xE4, 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A, - 0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, 0xDB, 0x99, - 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14 -}; - -/* - * MD2 context setup - */ -void md2_starts( md2_context *ctx ) -{ - memset( ctx->cksum, 0, 16 ); - memset( ctx->state, 0, 46 ); - memset( ctx->buffer, 0, 16 ); - ctx->left = 0; -} - -static void md2_process( md2_context *ctx ) -{ - int i, j; - unsigned char t = 0; - - for( i = 0; i < 16; i++ ) - { - ctx->state[i + 16] = ctx->buffer[i]; - ctx->state[i + 32] = - (unsigned char)( ctx->buffer[i] ^ ctx->state[i]); - } - - for( i = 0; i < 18; i++ ) - { - for( j = 0; j < 48; j++ ) - { - ctx->state[j] = (unsigned char) - ( ctx->state[j] ^ PI_SUBST[t] ); - t = ctx->state[j]; - } - - t = (unsigned char)( t + i ); - } - - t = ctx->cksum[15]; - - for( i = 0; i < 16; i++ ) - { - ctx->cksum[i] = (unsigned char) - ( ctx->cksum[i] ^ PI_SUBST[ctx->buffer[i] ^ t] ); - t = ctx->cksum[i]; - } -} - -/* - * MD2 process buffer - */ -void md2_update( md2_context *ctx, const unsigned char *input, size_t ilen ) -{ - size_t fill; - - while( ilen > 0 ) - { - if( ctx->left + ilen > 16 ) - fill = 16 - ctx->left; - else - fill = ilen; - - memcpy( ctx->buffer + ctx->left, input, fill ); - - ctx->left += fill; - input += fill; - ilen -= fill; - - if( ctx->left == 16 ) - { - ctx->left = 0; - md2_process( ctx ); - } - } -} - -/* - * MD2 final digest - */ -void md2_finish( md2_context *ctx, unsigned char output[16] ) -{ - size_t i; - unsigned char x; - - x = (unsigned char)( 16 - ctx->left ); - - for( i = ctx->left; i < 16; i++ ) - ctx->buffer[i] = x; - - md2_process( ctx ); - - memcpy( ctx->buffer, ctx->cksum, 16 ); - md2_process( ctx ); - - memcpy( output, ctx->state, 16 ); -} - -#endif /* !POLARSSL_MD2_ALT */ - -/* - * output = MD2( input buffer ) - */ -void md2( const unsigned char *input, size_t ilen, unsigned char output[16] ) -{ - md2_context ctx; - - md2_starts( &ctx ); - md2_update( &ctx, input, ilen ); - md2_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( md2_context ) ); -} - -#if defined(POLARSSL_FS_IO) -/* - * output = MD2( file contents ) - */ -int md2_file( const char *path, unsigned char output[16] ) -{ - FILE *f; - size_t n; - md2_context ctx; - unsigned char buf[1024]; - - if( ( f = fopen( path, "rb" ) ) == NULL ) - return( POLARSSL_ERR_MD2_FILE_IO_ERROR ); - - md2_starts( &ctx ); - - while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) - md2_update( &ctx, buf, n ); - - md2_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( md2_context ) ); - - if( ferror( f ) != 0 ) - { - fclose( f ); - return( POLARSSL_ERR_MD2_FILE_IO_ERROR ); - } - - fclose( f ); - return( 0 ); -} -#endif /* POLARSSL_FS_IO */ - -/* - * MD2 HMAC context setup - */ -void md2_hmac_starts( md2_context *ctx, const unsigned char *key, size_t keylen ) -{ - size_t i; - unsigned char sum[16]; - - if( keylen > 16 ) - { - md2( key, keylen, sum ); - keylen = 16; - key = sum; - } - - memset( ctx->ipad, 0x36, 16 ); - memset( ctx->opad, 0x5C, 16 ); - - 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] ); - } - - md2_starts( ctx ); - md2_update( ctx, ctx->ipad, 16 ); - - memset( sum, 0, sizeof( sum ) ); -} - -/* - * MD2 HMAC process buffer - */ -void md2_hmac_update( md2_context *ctx, const unsigned char *input, size_t ilen ) -{ - md2_update( ctx, input, ilen ); -} - -/* - * MD2 HMAC final digest - */ -void md2_hmac_finish( md2_context *ctx, unsigned char output[16] ) -{ - unsigned char tmpbuf[16]; - - md2_finish( ctx, tmpbuf ); - md2_starts( ctx ); - md2_update( ctx, ctx->opad, 16 ); - md2_update( ctx, tmpbuf, 16 ); - md2_finish( ctx, output ); - - memset( tmpbuf, 0, sizeof( tmpbuf ) ); -} - -/* - * MD2 HMAC context reset - */ -void md2_hmac_reset( md2_context *ctx ) -{ - md2_starts( ctx ); - md2_update( ctx, ctx->ipad, 16 ); -} - -/* - * output = HMAC-MD2( hmac key, input buffer ) - */ -void md2_hmac( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char output[16] ) -{ - md2_context ctx; - - md2_hmac_starts( &ctx, key, keylen ); - md2_hmac_update( &ctx, input, ilen ); - md2_hmac_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( md2_context ) ); -} - -#if defined(POLARSSL_SELF_TEST) - -/* - * RFC 1319 test vectors - */ -static const char md2_test_str[7][81] = -{ - { "" }, - { "a" }, - { "abc" }, - { "message digest" }, - { "abcdefghijklmnopqrstuvwxyz" }, - { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, - { "12345678901234567890123456789012345678901234567890123456789012" \ - "345678901234567890" } -}; - -static const unsigned char md2_test_sum[7][16] = -{ - { 0x83, 0x50, 0xE5, 0xA3, 0xE2, 0x4C, 0x15, 0x3D, - 0xF2, 0x27, 0x5C, 0x9F, 0x80, 0x69, 0x27, 0x73 }, - { 0x32, 0xEC, 0x01, 0xEC, 0x4A, 0x6D, 0xAC, 0x72, - 0xC0, 0xAB, 0x96, 0xFB, 0x34, 0xC0, 0xB5, 0xD1 }, - { 0xDA, 0x85, 0x3B, 0x0D, 0x3F, 0x88, 0xD9, 0x9B, - 0x30, 0x28, 0x3A, 0x69, 0xE6, 0xDE, 0xD6, 0xBB }, - { 0xAB, 0x4F, 0x49, 0x6B, 0xFB, 0x2A, 0x53, 0x0B, - 0x21, 0x9F, 0xF3, 0x30, 0x31, 0xFE, 0x06, 0xB0 }, - { 0x4E, 0x8D, 0xDF, 0xF3, 0x65, 0x02, 0x92, 0xAB, - 0x5A, 0x41, 0x08, 0xC3, 0xAA, 0x47, 0x94, 0x0B }, - { 0xDA, 0x33, 0xDE, 0xF2, 0xA4, 0x2D, 0xF1, 0x39, - 0x75, 0x35, 0x28, 0x46, 0xC3, 0x03, 0x38, 0xCD }, - { 0xD5, 0x97, 0x6F, 0x79, 0xD8, 0x3D, 0x3A, 0x0D, - 0xC9, 0x80, 0x6C, 0x3C, 0x66, 0xF3, 0xEF, 0xD8 } -}; - -/* - * Checkup routine - */ -int md2_self_test( int verbose ) -{ - int i; - unsigned char md2sum[16]; - - for( i = 0; i < 7; i++ ) - { - if( verbose != 0 ) - printf( " MD2 test #%d: ", i + 1 ); - - md2( (unsigned char *) md2_test_str[i], - strlen( md2_test_str[i] ), md2sum ); - - if( memcmp( md2sum, md2_test_sum[i], 16 ) != 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/makerom/polarssl/md4.c b/makerom/polarssl/md4.c deleted file mode 100644 index 980f5e46..00000000 --- a/makerom/polarssl/md4.c +++ /dev/null @@ -1,464 +0,0 @@ -/* - * RFC 1186/1320 compliant MD4 implementation - * - * Copyright (C) 2006-2013, 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 MD4 algorithm was designed by Ron Rivest in 1990. - * - * http://www.ietf.org/rfc/rfc1186.txt - * http://www.ietf.org/rfc/rfc1320.txt - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_MD4_C) - -#include "polarssl/md4.h" - -#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) -#include -#endif - -#if !defined(POLARSSL_MD4_ALT) - -/* - * 32-bit integer manipulation macros (little endian) - */ -#ifndef GET_UINT32_LE -#define GET_UINT32_LE(n,b,i) \ -{ \ - (n) = ( (uint32_t) (b)[(i) ] ) \ - | ( (uint32_t) (b)[(i) + 1] << 8 ) \ - | ( (uint32_t) (b)[(i) + 2] << 16 ) \ - | ( (uint32_t) (b)[(i) + 3] << 24 ); \ -} -#endif - -#ifndef PUT_UINT32_LE -#define PUT_UINT32_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 - -/* - * MD4 context setup - */ -void md4_starts( md4_context *ctx ) -{ - ctx->total[0] = 0; - ctx->total[1] = 0; - - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xEFCDAB89; - ctx->state[2] = 0x98BADCFE; - ctx->state[3] = 0x10325476; -} - -static void md4_process( md4_context *ctx, const unsigned char data[64] ) -{ - uint32_t X[16], A, B, C, D; - - GET_UINT32_LE( X[ 0], data, 0 ); - GET_UINT32_LE( X[ 1], data, 4 ); - GET_UINT32_LE( X[ 2], data, 8 ); - GET_UINT32_LE( X[ 3], data, 12 ); - GET_UINT32_LE( X[ 4], data, 16 ); - GET_UINT32_LE( X[ 5], data, 20 ); - GET_UINT32_LE( X[ 6], data, 24 ); - GET_UINT32_LE( X[ 7], data, 28 ); - GET_UINT32_LE( X[ 8], data, 32 ); - GET_UINT32_LE( X[ 9], data, 36 ); - GET_UINT32_LE( X[10], data, 40 ); - GET_UINT32_LE( X[11], data, 44 ); - GET_UINT32_LE( X[12], data, 48 ); - GET_UINT32_LE( X[13], data, 52 ); - GET_UINT32_LE( X[14], data, 56 ); - GET_UINT32_LE( X[15], data, 60 ); - -#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) - - A = ctx->state[0]; - B = ctx->state[1]; - C = ctx->state[2]; - D = ctx->state[3]; - -#define F(x, y, z) ((x & y) | ((~x) & z)) -#define P(a,b,c,d,x,s) { a += F(b,c,d) + x; a = S(a,s); } - - P( A, B, C, D, X[ 0], 3 ); - P( D, A, B, C, X[ 1], 7 ); - P( C, D, A, B, X[ 2], 11 ); - P( B, C, D, A, X[ 3], 19 ); - P( A, B, C, D, X[ 4], 3 ); - P( D, A, B, C, X[ 5], 7 ); - P( C, D, A, B, X[ 6], 11 ); - P( B, C, D, A, X[ 7], 19 ); - P( A, B, C, D, X[ 8], 3 ); - P( D, A, B, C, X[ 9], 7 ); - P( C, D, A, B, X[10], 11 ); - P( B, C, D, A, X[11], 19 ); - P( A, B, C, D, X[12], 3 ); - P( D, A, B, C, X[13], 7 ); - P( C, D, A, B, X[14], 11 ); - P( B, C, D, A, X[15], 19 ); - -#undef P -#undef F - -#define F(x,y,z) ((x & y) | (x & z) | (y & z)) -#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x5A827999; a = S(a,s); } - - P( A, B, C, D, X[ 0], 3 ); - P( D, A, B, C, X[ 4], 5 ); - P( C, D, A, B, X[ 8], 9 ); - P( B, C, D, A, X[12], 13 ); - P( A, B, C, D, X[ 1], 3 ); - P( D, A, B, C, X[ 5], 5 ); - P( C, D, A, B, X[ 9], 9 ); - P( B, C, D, A, X[13], 13 ); - P( A, B, C, D, X[ 2], 3 ); - P( D, A, B, C, X[ 6], 5 ); - P( C, D, A, B, X[10], 9 ); - P( B, C, D, A, X[14], 13 ); - P( A, B, C, D, X[ 3], 3 ); - P( D, A, B, C, X[ 7], 5 ); - P( C, D, A, B, X[11], 9 ); - P( B, C, D, A, X[15], 13 ); - -#undef P -#undef F - -#define F(x,y,z) (x ^ y ^ z) -#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x6ED9EBA1; a = S(a,s); } - - P( A, B, C, D, X[ 0], 3 ); - P( D, A, B, C, X[ 8], 9 ); - P( C, D, A, B, X[ 4], 11 ); - P( B, C, D, A, X[12], 15 ); - P( A, B, C, D, X[ 2], 3 ); - P( D, A, B, C, X[10], 9 ); - P( C, D, A, B, X[ 6], 11 ); - P( B, C, D, A, X[14], 15 ); - P( A, B, C, D, X[ 1], 3 ); - P( D, A, B, C, X[ 9], 9 ); - P( C, D, A, B, X[ 5], 11 ); - P( B, C, D, A, X[13], 15 ); - P( A, B, C, D, X[ 3], 3 ); - P( D, A, B, C, X[11], 9 ); - P( C, D, A, B, X[ 7], 11 ); - P( B, C, D, A, X[15], 15 ); - -#undef F -#undef P - - ctx->state[0] += A; - ctx->state[1] += B; - ctx->state[2] += C; - ctx->state[3] += D; -} - -/* - * MD4 process buffer - */ -void md4_update( md4_context *ctx, const unsigned char *input, size_t ilen ) -{ - size_t fill; - uint32_t left; - - if( ilen <= 0 ) - return; - - left = ctx->total[0] & 0x3F; - fill = 64 - left; - - ctx->total[0] += (uint32_t) ilen; - ctx->total[0] &= 0xFFFFFFFF; - - if( ctx->total[0] < (uint32_t) ilen ) - ctx->total[1]++; - - if( left && ilen >= fill ) - { - memcpy( (void *) (ctx->buffer + left), - (void *) input, fill ); - md4_process( ctx, ctx->buffer ); - input += fill; - ilen -= fill; - left = 0; - } - - while( ilen >= 64 ) - { - md4_process( ctx, input ); - input += 64; - ilen -= 64; - } - - if( ilen > 0 ) - { - memcpy( (void *) (ctx->buffer + left), - (void *) input, ilen ); - } -} - -static const unsigned char md4_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 -}; - -/* - * MD4 final digest - */ -void md4_finish( md4_context *ctx, unsigned char output[16] ) -{ - uint32_t last, padn; - uint32_t high, low; - unsigned char msglen[8]; - - high = ( ctx->total[0] >> 29 ) - | ( ctx->total[1] << 3 ); - low = ( ctx->total[0] << 3 ); - - PUT_UINT32_LE( low, msglen, 0 ); - PUT_UINT32_LE( high, msglen, 4 ); - - last = ctx->total[0] & 0x3F; - padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); - - md4_update( ctx, (unsigned char *) md4_padding, padn ); - md4_update( ctx, msglen, 8 ); - - PUT_UINT32_LE( ctx->state[0], output, 0 ); - PUT_UINT32_LE( ctx->state[1], output, 4 ); - PUT_UINT32_LE( ctx->state[2], output, 8 ); - PUT_UINT32_LE( ctx->state[3], output, 12 ); -} - -#endif /* !POLARSSL_MD4_ALT */ - -/* - * output = MD4( input buffer ) - */ -void md4( const unsigned char *input, size_t ilen, unsigned char output[16] ) -{ - md4_context ctx; - - md4_starts( &ctx ); - md4_update( &ctx, input, ilen ); - md4_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( md4_context ) ); -} - -#if defined(POLARSSL_FS_IO) -/* - * output = MD4( file contents ) - */ -int md4_file( const char *path, unsigned char output[16] ) -{ - FILE *f; - size_t n; - md4_context ctx; - unsigned char buf[1024]; - - if( ( f = fopen( path, "rb" ) ) == NULL ) - return( POLARSSL_ERR_MD4_FILE_IO_ERROR ); - - md4_starts( &ctx ); - - while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) - md4_update( &ctx, buf, n ); - - md4_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( md4_context ) ); - - if( ferror( f ) != 0 ) - { - fclose( f ); - return( POLARSSL_ERR_MD4_FILE_IO_ERROR ); - } - - fclose( f ); - return( 0 ); -} -#endif /* POLARSSL_FS_IO */ - -/* - * MD4 HMAC context setup - */ -void md4_hmac_starts( md4_context *ctx, const unsigned char *key, size_t keylen ) -{ - size_t i; - unsigned char sum[16]; - - if( keylen > 64 ) - { - md4( key, keylen, sum ); - keylen = 16; - 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] ); - } - - md4_starts( ctx ); - md4_update( ctx, ctx->ipad, 64 ); - - memset( sum, 0, sizeof( sum ) ); -} - -/* - * MD4 HMAC process buffer - */ -void md4_hmac_update( md4_context *ctx, const unsigned char *input, size_t ilen ) -{ - md4_update( ctx, input, ilen ); -} - -/* - * MD4 HMAC final digest - */ -void md4_hmac_finish( md4_context *ctx, unsigned char output[16] ) -{ - unsigned char tmpbuf[16]; - - md4_finish( ctx, tmpbuf ); - md4_starts( ctx ); - md4_update( ctx, ctx->opad, 64 ); - md4_update( ctx, tmpbuf, 16 ); - md4_finish( ctx, output ); - - memset( tmpbuf, 0, sizeof( tmpbuf ) ); -} - -/* - * MD4 HMAC context reset - */ -void md4_hmac_reset( md4_context *ctx ) -{ - md4_starts( ctx ); - md4_update( ctx, ctx->ipad, 64 ); -} - -/* - * output = HMAC-MD4( hmac key, input buffer ) - */ -void md4_hmac( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char output[16] ) -{ - md4_context ctx; - - md4_hmac_starts( &ctx, key, keylen ); - md4_hmac_update( &ctx, input, ilen ); - md4_hmac_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( md4_context ) ); -} - -#if defined(POLARSSL_SELF_TEST) - -/* - * RFC 1320 test vectors - */ -static const char md4_test_str[7][81] = -{ - { "" }, - { "a" }, - { "abc" }, - { "message digest" }, - { "abcdefghijklmnopqrstuvwxyz" }, - { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, - { "12345678901234567890123456789012345678901234567890123456789012" \ - "345678901234567890" } -}; - -static const unsigned char md4_test_sum[7][16] = -{ - { 0x31, 0xD6, 0xCF, 0xE0, 0xD1, 0x6A, 0xE9, 0x31, - 0xB7, 0x3C, 0x59, 0xD7, 0xE0, 0xC0, 0x89, 0xC0 }, - { 0xBD, 0xE5, 0x2C, 0xB3, 0x1D, 0xE3, 0x3E, 0x46, - 0x24, 0x5E, 0x05, 0xFB, 0xDB, 0xD6, 0xFB, 0x24 }, - { 0xA4, 0x48, 0x01, 0x7A, 0xAF, 0x21, 0xD8, 0x52, - 0x5F, 0xC1, 0x0A, 0xE8, 0x7A, 0xA6, 0x72, 0x9D }, - { 0xD9, 0x13, 0x0A, 0x81, 0x64, 0x54, 0x9F, 0xE8, - 0x18, 0x87, 0x48, 0x06, 0xE1, 0xC7, 0x01, 0x4B }, - { 0xD7, 0x9E, 0x1C, 0x30, 0x8A, 0xA5, 0xBB, 0xCD, - 0xEE, 0xA8, 0xED, 0x63, 0xDF, 0x41, 0x2D, 0xA9 }, - { 0x04, 0x3F, 0x85, 0x82, 0xF2, 0x41, 0xDB, 0x35, - 0x1C, 0xE6, 0x27, 0xE1, 0x53, 0xE7, 0xF0, 0xE4 }, - { 0xE3, 0x3B, 0x4D, 0xDC, 0x9C, 0x38, 0xF2, 0x19, - 0x9C, 0x3E, 0x7B, 0x16, 0x4F, 0xCC, 0x05, 0x36 } -}; - -/* - * Checkup routine - */ -int md4_self_test( int verbose ) -{ - int i; - unsigned char md4sum[16]; - - for( i = 0; i < 7; i++ ) - { - if( verbose != 0 ) - printf( " MD4 test #%d: ", i + 1 ); - - md4( (unsigned char *) md4_test_str[i], - strlen( md4_test_str[i] ), md4sum ); - - if( memcmp( md4sum, md4_test_sum[i], 16 ) != 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/makerom/polarssl/net.c b/makerom/polarssl/net.c deleted file mode 100644 index 7a1818df..00000000 --- a/makerom/polarssl/net.c +++ /dev/null @@ -1,374 +0,0 @@ -/* - * TCP networking functions - * - * 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_NET_C) - -#include "polarssl/net.h" - -#if defined(_WIN32) || defined(_WIN32_WCE) - -#include -#include - -#if defined(_WIN32_WCE) -#pragma comment( lib, "ws2.lib" ) -#else -#pragma comment( lib, "ws2_32.lib" ) -#endif - -#define read(fd,buf,len) recv(fd,(char*)buf,(int) len,0) -#define write(fd,buf,len) send(fd,(char*)buf,(int) len,0) -#define close(fd) closesocket(fd) - -static int wsa_init_done = 0; - -#else - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || \ - defined(__DragonflyBSD__) -#include -#elif defined(__APPLE__) -#include -#elif defined(sun) -#include -#else -#include -#endif - -#endif - -#include -#include -#include - -#ifdef _MSC_VER -#include -typedef UINT32 uint32_t; -#else -#include -#endif - -/* - * htons() is not always available. - * By default go for LITTLE_ENDIAN variant. Otherwise hope for _BYTE_ORDER and __BIG_ENDIAN - * to help determine endianess. - */ -#if defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN -#define POLARSSL_HTONS(n) (n) -#define POLARSSL_HTONL(n) (n) -#else -#define POLARSSL_HTONS(n) ((((unsigned short)(n) & 0xFF ) << 8 ) | \ - (((unsigned short)(n) & 0xFF00 ) >> 8 )) -#define POLARSSL_HTONL(n) ((((unsigned long )(n) & 0xFF ) << 24) | \ - (((unsigned long )(n) & 0xFF00 ) << 8 ) | \ - (((unsigned long )(n) & 0xFF0000 ) >> 8 ) | \ - (((unsigned long )(n) & 0xFF000000) >> 24)) -#endif - -unsigned short net_htons(unsigned short n); -unsigned long net_htonl(unsigned long n); -#define net_htons(n) POLARSSL_HTONS(n) -#define net_htonl(n) POLARSSL_HTONL(n) - -/* - * Initiate a TCP connection with host:port - */ -int net_connect( int *fd, const char *host, int port ) -{ - struct sockaddr_in server_addr; - struct hostent *server_host; - -#if defined(_WIN32) || defined(_WIN32_WCE) - WSADATA wsaData; - - if( wsa_init_done == 0 ) - { - if( WSAStartup( MAKEWORD(2,0), &wsaData ) == SOCKET_ERROR ) - return( POLARSSL_ERR_NET_SOCKET_FAILED ); - - wsa_init_done = 1; - } -#else - signal( SIGPIPE, SIG_IGN ); -#endif - - if( ( server_host = gethostbyname( host ) ) == NULL ) - return( POLARSSL_ERR_NET_UNKNOWN_HOST ); - - if( ( *fd = socket( AF_INET, SOCK_STREAM, IPPROTO_IP ) ) < 0 ) - return( POLARSSL_ERR_NET_SOCKET_FAILED ); - - memcpy( (void *) &server_addr.sin_addr, - (void *) server_host->h_addr, - server_host->h_length ); - - server_addr.sin_family = AF_INET; - server_addr.sin_port = net_htons( port ); - - if( connect( *fd, (struct sockaddr *) &server_addr, - sizeof( server_addr ) ) < 0 ) - { - close( *fd ); - return( POLARSSL_ERR_NET_CONNECT_FAILED ); - } - - return( 0 ); -} - -/* - * Create a listening socket on bind_ip:port - */ -int net_bind( int *fd, const char *bind_ip, int port ) -{ - int n, c[4]; - struct sockaddr_in server_addr; - -#if defined(_WIN32) || defined(_WIN32_WCE) - WSADATA wsaData; - - if( wsa_init_done == 0 ) - { - if( WSAStartup( MAKEWORD(2,0), &wsaData ) == SOCKET_ERROR ) - return( POLARSSL_ERR_NET_SOCKET_FAILED ); - - wsa_init_done = 1; - } -#else - signal( SIGPIPE, SIG_IGN ); -#endif - - if( ( *fd = socket( AF_INET, SOCK_STREAM, IPPROTO_IP ) ) < 0 ) - return( POLARSSL_ERR_NET_SOCKET_FAILED ); - - n = 1; - setsockopt( *fd, SOL_SOCKET, SO_REUSEADDR, - (const char *) &n, sizeof( n ) ); - - server_addr.sin_addr.s_addr = net_htonl( INADDR_ANY ); - server_addr.sin_family = AF_INET; - server_addr.sin_port = net_htons( port ); - - if( bind_ip != NULL ) - { - memset( c, 0, sizeof( c ) ); - sscanf( bind_ip, "%d.%d.%d.%d", &c[0], &c[1], &c[2], &c[3] ); - - for( n = 0; n < 4; n++ ) - if( c[n] < 0 || c[n] > 255 ) - break; - - if( n == 4 ) - server_addr.sin_addr.s_addr = net_htonl( - ( (uint32_t) c[0] << 24 ) | - ( (uint32_t) c[1] << 16 ) | - ( (uint32_t) c[2] << 8 ) | - ( (uint32_t) c[3] ) ); - } - - if( bind( *fd, (struct sockaddr *) &server_addr, - sizeof( server_addr ) ) < 0 ) - { - close( *fd ); - return( POLARSSL_ERR_NET_BIND_FAILED ); - } - - if( listen( *fd, POLARSSL_NET_LISTEN_BACKLOG ) != 0 ) - { - close( *fd ); - return( POLARSSL_ERR_NET_LISTEN_FAILED ); - } - - return( 0 ); -} - -/* - * Check if the current operation is blocking - */ -static int net_is_blocking( void ) -{ -#if defined(_WIN32) || defined(_WIN32_WCE) - return( WSAGetLastError() == WSAEWOULDBLOCK ); -#else - switch( errno ) - { -#if defined EAGAIN - case EAGAIN: -#endif -#if defined EWOULDBLOCK && EWOULDBLOCK != EAGAIN - case EWOULDBLOCK: -#endif - return( 1 ); - } - return( 0 ); -#endif -} - -/* - * Accept a connection from a remote client - */ -int net_accept( int bind_fd, int *client_fd, void *client_ip ) -{ - struct sockaddr_in client_addr; - -#if defined(__socklen_t_defined) || defined(_SOCKLEN_T) || \ - defined(_SOCKLEN_T_DECLARED) - socklen_t n = (socklen_t) sizeof( client_addr ); -#else - int n = (int) sizeof( client_addr ); -#endif - - *client_fd = accept( bind_fd, (struct sockaddr *) - &client_addr, &n ); - - if( *client_fd < 0 ) - { - if( net_is_blocking() != 0 ) - return( POLARSSL_ERR_NET_WANT_READ ); - - return( POLARSSL_ERR_NET_ACCEPT_FAILED ); - } - - if( client_ip != NULL ) - memcpy( client_ip, &client_addr.sin_addr.s_addr, - sizeof( client_addr.sin_addr.s_addr ) ); - - return( 0 ); -} - -/* - * Set the socket blocking or non-blocking - */ -int net_set_block( int fd ) -{ -#if defined(_WIN32) || defined(_WIN32_WCE) - u_long n = 0; - return( ioctlsocket( fd, FIONBIO, &n ) ); -#else - return( fcntl( fd, F_SETFL, fcntl( fd, F_GETFL ) & ~O_NONBLOCK ) ); -#endif -} - -int net_set_nonblock( int fd ) -{ -#if defined(_WIN32) || defined(_WIN32_WCE) - u_long n = 1; - return( ioctlsocket( fd, FIONBIO, &n ) ); -#else - return( fcntl( fd, F_SETFL, fcntl( fd, F_GETFL ) | O_NONBLOCK ) ); -#endif -} - -/* - * Portable usleep helper - */ -void net_usleep( unsigned long usec ) -{ - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = usec; - select( 0, NULL, NULL, NULL, &tv ); -} - -/* - * Read at most 'len' characters - */ -int net_recv( void *ctx, unsigned char *buf, size_t len ) -{ - int ret = read( *((int *) ctx), buf, len ); - - if( ret < 0 ) - { - if( net_is_blocking() != 0 ) - return( POLARSSL_ERR_NET_WANT_READ ); - -#if defined(_WIN32) || defined(_WIN32_WCE) - if( WSAGetLastError() == WSAECONNRESET ) - return( POLARSSL_ERR_NET_CONN_RESET ); -#else - if( errno == EPIPE || errno == ECONNRESET ) - return( POLARSSL_ERR_NET_CONN_RESET ); - - if( errno == EINTR ) - return( POLARSSL_ERR_NET_WANT_READ ); -#endif - - return( POLARSSL_ERR_NET_RECV_FAILED ); - } - - return( ret ); -} - -/* - * Write at most 'len' characters - */ -int net_send( void *ctx, const unsigned char *buf, size_t len ) -{ - int ret = write( *((int *) ctx), buf, len ); - - if( ret < 0 ) - { - if( net_is_blocking() != 0 ) - return( POLARSSL_ERR_NET_WANT_WRITE ); - -#if defined(_WIN32) || defined(_WIN32_WCE) - if( WSAGetLastError() == WSAECONNRESET ) - return( POLARSSL_ERR_NET_CONN_RESET ); -#else - if( errno == EPIPE || errno == ECONNRESET ) - return( POLARSSL_ERR_NET_CONN_RESET ); - - if( errno == EINTR ) - return( POLARSSL_ERR_NET_WANT_WRITE ); -#endif - - return( POLARSSL_ERR_NET_SEND_FAILED ); - } - - return( ret ); -} - -/* - * Gracefully close the connection - */ -void net_close( int fd ) -{ - shutdown( fd, 2 ); - close( fd ); -} - -#endif diff --git a/makerom/polarssl/padlock.c b/makerom/polarssl/padlock.c deleted file mode 100644 index 9ddac15e..00000000 --- a/makerom/polarssl/padlock.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * VIA PadLock support functions - * - * 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 implementation is based on the VIA PadLock Programming Guide: - * - * http://www.via.com.tw/en/downloads/whitepapers/initiatives/padlock/ - * programming_guide.pdf - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_PADLOCK_C) - -#include "polarssl/padlock.h" - -#if defined(POLARSSL_HAVE_X86) - -/* - * PadLock detection routine - */ -int padlock_supports( int feature ) -{ - static int flags = -1; - int ebx, edx; - - if( flags == -1 ) - { - __asm__( "movl %%ebx, %0 \n" \ - "movl $0xC0000000, %%eax \n" \ - "cpuid \n" \ - "cmpl $0xC0000001, %%eax \n" \ - "movl $0, %%edx \n" \ - "jb unsupported \n" \ - "movl $0xC0000001, %%eax \n" \ - "cpuid \n" \ - "unsupported: \n" \ - "movl %%edx, %1 \n" \ - "movl %2, %%ebx \n" - : "=m" (ebx), "=m" (edx) - : "m" (ebx) - : "eax", "ecx", "edx" ); - - flags = edx; - } - - return( flags & feature ); -} - -/* - * PadLock AES-ECB block en(de)cryption - */ -int padlock_xcryptecb( aes_context *ctx, - int mode, - const unsigned char input[16], - unsigned char output[16] ) -{ - int ebx; - uint32_t *rk; - uint32_t *blk; - uint32_t *ctrl; - unsigned char buf[256]; - - rk = ctx->rk; - blk = PADLOCK_ALIGN16( buf ); - memcpy( blk, input, 16 ); - - ctrl = blk + 4; - *ctrl = 0x80 | ctx->nr | ( ( ctx->nr + ( mode^1 ) - 10 ) << 9 ); - - __asm__( "pushfl; popfl \n" \ - "movl %%ebx, %0 \n" \ - "movl $1, %%ecx \n" \ - "movl %2, %%edx \n" \ - "movl %3, %%ebx \n" \ - "movl %4, %%esi \n" \ - "movl %4, %%edi \n" \ - ".byte 0xf3,0x0f,0xa7,0xc8\n" \ - "movl %1, %%ebx \n" - : "=m" (ebx) - : "m" (ebx), "m" (ctrl), "m" (rk), "m" (blk) - : "ecx", "edx", "esi", "edi" ); - - memcpy( output, blk, 16 ); - - return( 0 ); -} - -/* - * PadLock AES-CBC buffer en(de)cryption - */ -int padlock_xcryptcbc( aes_context *ctx, - int mode, - size_t length, - unsigned char iv[16], - const unsigned char *input, - unsigned char *output ) -{ - int ebx; - size_t count; - uint32_t *rk; - uint32_t *iw; - uint32_t *ctrl; - unsigned char buf[256]; - - if( ( (long) input & 15 ) != 0 || - ( (long) output & 15 ) != 0 ) - return( POLARSSL_ERR_PADLOCK_DATA_MISALIGNED ); - - rk = ctx->rk; - iw = PADLOCK_ALIGN16( buf ); - memcpy( iw, iv, 16 ); - - ctrl = iw + 4; - *ctrl = 0x80 | ctx->nr | ( ( ctx->nr + (mode^1) - 10 ) << 9 ); - - count = (length + 15) >> 4; - - __asm__( "pushfl; popfl \n" \ - "movl %%ebx, %0 \n" \ - "movl %2, %%ecx \n" \ - "movl %3, %%edx \n" \ - "movl %4, %%ebx \n" \ - "movl %5, %%esi \n" \ - "movl %6, %%edi \n" \ - "movl %7, %%eax \n" \ - ".byte 0xf3,0x0f,0xa7,0xd0\n" \ - "movl %1, %%ebx \n" - : "=m" (ebx) - : "m" (ebx), "m" (count), "m" (ctrl), - "m" (rk), "m" (input), "m" (output), "m" (iw) - : "eax", "ecx", "edx", "esi", "edi" ); - - memcpy( iv, iw, 16 ); - - return( 0 ); -} - -#endif - -#endif diff --git a/makerom/polarssl/pbkdf2.c b/makerom/polarssl/pbkdf2.c deleted file mode 100644 index 09e56dfa..00000000 --- a/makerom/polarssl/pbkdf2.c +++ /dev/null @@ -1,60 +0,0 @@ -/** - * \file pbkdf2.c - * - * \brief Password-Based Key Derivation Function 2 (from PKCS#5) - * DEPRECATED: Use pkcs5.c instead - * - * \author Mathias Olsson - * - * Copyright (C) 2006-2012, 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. - */ -/* - * PBKDF2 is part of PKCS#5 - * - * http://tools.ietf.org/html/rfc2898 (Specification) - * http://tools.ietf.org/html/rfc6070 (Test vectors) - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_PBKDF2_C) - -#include "polarssl/pbkdf2.h" -#include "polarssl/pkcs5.h" - -int pbkdf2_hmac( md_context_t *ctx, const unsigned char *password, size_t plen, - const unsigned char *salt, size_t slen, - unsigned int iteration_count, - uint32_t key_length, unsigned char *output ) -{ - return pkcs5_pbkdf2_hmac( ctx, password, plen, salt, slen, iteration_count, - key_length, output ); -} - -#if defined(POLARSSL_SELF_TEST) -int pbkdf2_self_test( int verbose ) -{ - return pkcs5_self_test( verbose ); -} -#endif /* POLARSSL_SELF_TEST */ - -#endif /* POLARSSL_PBKDF2_C */ diff --git a/makerom/polarssl/pem.c b/makerom/polarssl/pem.c deleted file mode 100644 index e2e39980..00000000 --- a/makerom/polarssl/pem.c +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Privacy Enhanced Mail (PEM) decoding - * - * Copyright (C) 2006-2013, 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_PEM_C) - -#include "polarssl/pem.h" -#include "polarssl/base64.h" -#include "polarssl/des.h" -#include "polarssl/aes.h" -#include "polarssl/md5.h" -#include "polarssl/cipher.h" - -#include - -void pem_init( pem_context *ctx ) -{ - memset( ctx, 0, sizeof( pem_context ) ); -} - -#if defined(POLARSSL_MD5_C) && (defined(POLARSSL_DES_C) || defined(POLARSSL_AES_C)) -/* - * Read a 16-byte hex string and convert it to binary - */ -static int pem_get_iv( const unsigned char *s, unsigned char *iv, size_t iv_len ) -{ - size_t i, j, k; - - memset( iv, 0, iv_len ); - - for( i = 0; i < iv_len * 2; i++, s++ ) - { - if( *s >= '0' && *s <= '9' ) j = *s - '0'; else - if( *s >= 'A' && *s <= 'F' ) j = *s - '7'; else - if( *s >= 'a' && *s <= 'f' ) j = *s - 'W'; else - return( POLARSSL_ERR_PEM_INVALID_ENC_IV ); - - k = ( ( i & 1 ) != 0 ) ? j : j << 4; - - iv[i >> 1] = (unsigned char)( iv[i >> 1] | k ); - } - - return( 0 ); -} - -static void pem_pbkdf1( unsigned char *key, size_t keylen, - unsigned char *iv, - const unsigned char *pwd, size_t pwdlen ) -{ - md5_context md5_ctx; - unsigned char md5sum[16]; - size_t use_len; - - /* - * key[ 0..15] = MD5(pwd || IV) - */ - md5_starts( &md5_ctx ); - md5_update( &md5_ctx, pwd, pwdlen ); - md5_update( &md5_ctx, iv, 8 ); - md5_finish( &md5_ctx, md5sum ); - - if( keylen <= 16 ) - { - memcpy( key, md5sum, keylen ); - - memset( &md5_ctx, 0, sizeof( md5_ctx ) ); - memset( md5sum, 0, 16 ); - return; - } - - memcpy( key, md5sum, 16 ); - - /* - * key[16..23] = MD5(key[ 0..15] || pwd || IV]) - */ - md5_starts( &md5_ctx ); - md5_update( &md5_ctx, md5sum, 16 ); - md5_update( &md5_ctx, pwd, pwdlen ); - md5_update( &md5_ctx, iv, 8 ); - md5_finish( &md5_ctx, md5sum ); - - use_len = 16; - if( keylen < 32 ) - use_len = keylen - 16; - - memcpy( key + 16, md5sum, use_len ); - - memset( &md5_ctx, 0, sizeof( md5_ctx ) ); - memset( md5sum, 0, 16 ); -} - -#if defined(POLARSSL_DES_C) -/* - * Decrypt with DES-CBC, using PBKDF1 for key derivation - */ -static void pem_des_decrypt( unsigned char des_iv[8], - unsigned char *buf, size_t buflen, - const unsigned char *pwd, size_t pwdlen ) -{ - des_context des_ctx; - unsigned char des_key[8]; - - pem_pbkdf1( des_key, 8, des_iv, pwd, pwdlen ); - - des_setkey_dec( &des_ctx, des_key ); - des_crypt_cbc( &des_ctx, DES_DECRYPT, buflen, - des_iv, buf, buf ); - - memset( &des_ctx, 0, sizeof( des_ctx ) ); - memset( des_key, 0, 8 ); -} - -/* - * Decrypt with 3DES-CBC, using PBKDF1 for key derivation - */ -static void pem_des3_decrypt( unsigned char des3_iv[8], - unsigned char *buf, size_t buflen, - const unsigned char *pwd, size_t pwdlen ) -{ - des3_context des3_ctx; - unsigned char des3_key[24]; - - pem_pbkdf1( des3_key, 24, des3_iv, pwd, pwdlen ); - - des3_set3key_dec( &des3_ctx, des3_key ); - des3_crypt_cbc( &des3_ctx, DES_DECRYPT, buflen, - des3_iv, buf, buf ); - - memset( &des3_ctx, 0, sizeof( des3_ctx ) ); - memset( des3_key, 0, 24 ); -} -#endif /* POLARSSL_DES_C */ - -#if defined(POLARSSL_AES_C) -/* - * Decrypt with AES-XXX-CBC, using PBKDF1 for key derivation - */ -static void pem_aes_decrypt( unsigned char aes_iv[16], unsigned int keylen, - unsigned char *buf, size_t buflen, - const unsigned char *pwd, size_t pwdlen ) -{ - aes_context aes_ctx; - unsigned char aes_key[32]; - - pem_pbkdf1( aes_key, keylen, aes_iv, pwd, pwdlen ); - - aes_setkey_dec( &aes_ctx, aes_key, keylen * 8 ); - aes_crypt_cbc( &aes_ctx, AES_DECRYPT, buflen, - aes_iv, buf, buf ); - - memset( &aes_ctx, 0, sizeof( aes_ctx ) ); - memset( aes_key, 0, keylen ); -} -#endif /* POLARSSL_AES_C */ - -#endif /* POLARSSL_MD5_C && (POLARSSL_AES_C || POLARSSL_DES_C) */ - -int pem_read_buffer( pem_context *ctx, char *header, char *footer, const unsigned char *data, const unsigned char *pwd, size_t pwdlen, size_t *use_len ) -{ - int ret, enc; - size_t len; - unsigned char *buf; - const unsigned char *s1, *s2, *end; -#if defined(POLARSSL_MD5_C) && (defined(POLARSSL_DES_C) || defined(POLARSSL_AES_C)) - unsigned char pem_iv[16]; - cipher_type_t enc_alg = POLARSSL_CIPHER_NONE; -#else - ((void) pwd); - ((void) pwdlen); -#endif /* POLARSSL_MD5_C && (POLARSSL_AES_C || POLARSSL_DES_C) */ - - if( ctx == NULL ) - return( POLARSSL_ERR_PEM_BAD_INPUT_DATA ); - - s1 = (unsigned char *) strstr( (const char *) data, header ); - - if( s1 == NULL ) - return( POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ); - - s2 = (unsigned char *) strstr( (const char *) data, footer ); - - if( s2 == NULL || s2 <= s1 ) - return( POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ); - - s1 += strlen( header ); - if( *s1 == '\r' ) s1++; - if( *s1 == '\n' ) s1++; - else return( POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ); - - end = s2; - end += strlen( footer ); - if( *end == '\r' ) end++; - if( *end == '\n' ) end++; - *use_len = end - data; - - enc = 0; - - if( memcmp( s1, "Proc-Type: 4,ENCRYPTED", 22 ) == 0 ) - { -#if defined(POLARSSL_MD5_C) && (defined(POLARSSL_DES_C) || defined(POLARSSL_AES_C)) - enc++; - - s1 += 22; - if( *s1 == '\r' ) s1++; - if( *s1 == '\n' ) s1++; - else return( POLARSSL_ERR_PEM_INVALID_DATA ); - - -#if defined(POLARSSL_DES_C) - if( memcmp( s1, "DEK-Info: DES-EDE3-CBC,", 23 ) == 0 ) - { - enc_alg = POLARSSL_CIPHER_DES_EDE3_CBC; - - s1 += 23; - if( pem_get_iv( s1, pem_iv, 8 ) != 0 ) - return( POLARSSL_ERR_PEM_INVALID_ENC_IV ); - - s1 += 16; - } - else if( memcmp( s1, "DEK-Info: DES-CBC,", 18 ) == 0 ) - { - enc_alg = POLARSSL_CIPHER_DES_CBC; - - s1 += 18; - if( pem_get_iv( s1, pem_iv, 8) != 0 ) - return( POLARSSL_ERR_PEM_INVALID_ENC_IV ); - - s1 += 16; - } -#endif /* POLARSSL_DES_C */ - -#if defined(POLARSSL_AES_C) - if( memcmp( s1, "DEK-Info: AES-", 14 ) == 0 ) - { - if( memcmp( s1, "DEK-Info: AES-128-CBC,", 22 ) == 0 ) - enc_alg = POLARSSL_CIPHER_AES_128_CBC; - else if( memcmp( s1, "DEK-Info: AES-192-CBC,", 22 ) == 0 ) - enc_alg = POLARSSL_CIPHER_AES_192_CBC; - else if( memcmp( s1, "DEK-Info: AES-256-CBC,", 22 ) == 0 ) - enc_alg = POLARSSL_CIPHER_AES_256_CBC; - else - return( POLARSSL_ERR_PEM_UNKNOWN_ENC_ALG ); - - s1 += 22; - if( pem_get_iv( s1, pem_iv, 16 ) != 0 ) - return( POLARSSL_ERR_PEM_INVALID_ENC_IV ); - - s1 += 32; - } -#endif /* POLARSSL_AES_C */ - - if( enc_alg == POLARSSL_CIPHER_NONE ) - return( POLARSSL_ERR_PEM_UNKNOWN_ENC_ALG ); - - if( *s1 == '\r' ) s1++; - if( *s1 == '\n' ) s1++; - else return( POLARSSL_ERR_PEM_INVALID_DATA ); -#else - return( POLARSSL_ERR_PEM_FEATURE_UNAVAILABLE ); -#endif /* POLARSSL_MD5_C && (POLARSSL_AES_C || POLARSSL_DES_C) */ - } - - len = 0; - ret = base64_decode( NULL, &len, s1, s2 - s1 ); - - if( ret == POLARSSL_ERR_BASE64_INVALID_CHARACTER ) - return( POLARSSL_ERR_PEM_INVALID_DATA + ret ); - - if( ( buf = (unsigned char *) malloc( len ) ) == NULL ) - return( POLARSSL_ERR_PEM_MALLOC_FAILED ); - - if( ( ret = base64_decode( buf, &len, s1, s2 - s1 ) ) != 0 ) - { - free( buf ); - return( POLARSSL_ERR_PEM_INVALID_DATA + ret ); - } - - if( enc != 0 ) - { -#if defined(POLARSSL_MD5_C) && (defined(POLARSSL_DES_C) || defined(POLARSSL_AES_C)) - if( pwd == NULL ) - { - free( buf ); - return( POLARSSL_ERR_PEM_PASSWORD_REQUIRED ); - } - -#if defined(POLARSSL_DES_C) - if( enc_alg == POLARSSL_CIPHER_DES_EDE3_CBC ) - pem_des3_decrypt( pem_iv, buf, len, pwd, pwdlen ); - else if( enc_alg == POLARSSL_CIPHER_DES_CBC ) - pem_des_decrypt( pem_iv, buf, len, pwd, pwdlen ); -#endif /* POLARSSL_DES_C */ - -#if defined(POLARSSL_AES_C) - if( enc_alg == POLARSSL_CIPHER_AES_128_CBC ) - pem_aes_decrypt( pem_iv, 16, buf, len, pwd, pwdlen ); - else if( enc_alg == POLARSSL_CIPHER_AES_192_CBC ) - pem_aes_decrypt( pem_iv, 24, buf, len, pwd, pwdlen ); - else if( enc_alg == POLARSSL_CIPHER_AES_256_CBC ) - pem_aes_decrypt( pem_iv, 32, buf, len, pwd, pwdlen ); -#endif /* POLARSSL_AES_C */ - - if( buf[0] != 0x30 || buf[1] != 0x82 || - buf[4] != 0x02 || buf[5] != 0x01 ) - { - free( buf ); - return( POLARSSL_ERR_PEM_PASSWORD_MISMATCH ); - } -#else - free( buf ); - return( POLARSSL_ERR_PEM_FEATURE_UNAVAILABLE ); -#endif - } - - ctx->buf = buf; - ctx->buflen = len; - - return( 0 ); -} - -void pem_free( pem_context *ctx ) -{ - if( ctx->buf ) - free( ctx->buf ); - - if( ctx->info ) - free( ctx->info ); - - memset( ctx, 0, sizeof( pem_context ) ); -} - -#endif diff --git a/makerom/polarssl/pkcs11.c b/makerom/polarssl/pkcs11.c deleted file mode 100644 index b68d6881..00000000 --- a/makerom/polarssl/pkcs11.c +++ /dev/null @@ -1,238 +0,0 @@ -/** - * \file pkcs11.c - * - * \brief Wrapper for PKCS#11 library libpkcs11-helper - * - * \author Adriaan de Jong - * - * 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. - */ - -#include "polarssl/pkcs11.h" - -#if defined(POLARSSL_PKCS11_C) - -#include - -int pkcs11_x509_cert_init( x509_cert *cert, pkcs11h_certificate_t pkcs11_cert ) -{ - int ret = 1; - unsigned char *cert_blob = NULL; - size_t cert_blob_size = 0; - - if( cert == NULL ) - { - ret = 2; - goto cleanup; - } - - if( pkcs11h_certificate_getCertificateBlob( pkcs11_cert, NULL, &cert_blob_size ) != CKR_OK ) - { - ret = 3; - goto cleanup; - } - - cert_blob = malloc( cert_blob_size ); - if( NULL == cert_blob ) - { - ret = 4; - goto cleanup; - } - - if( pkcs11h_certificate_getCertificateBlob( pkcs11_cert, cert_blob, &cert_blob_size ) != CKR_OK ) - { - ret = 5; - goto cleanup; - } - - if( 0 != x509parse_crt(cert, cert_blob, cert_blob_size ) ) - { - ret = 6; - goto cleanup; - } - - ret = 0; - -cleanup: - if( NULL != cert_blob ) - free( cert_blob ); - - return ret; -} - - -int pkcs11_priv_key_init( pkcs11_context *priv_key, - pkcs11h_certificate_t pkcs11_cert ) -{ - int ret = 1; - x509_cert cert; - - memset( &cert, 0, sizeof( cert ) ); - - if( priv_key == NULL ) - goto cleanup; - - if( 0 != pkcs11_x509_cert_init( &cert, pkcs11_cert ) ) - goto cleanup; - - priv_key->len = cert.rsa.len; - priv_key->pkcs11h_cert = pkcs11_cert; - - ret = 0; - -cleanup: - x509_free( &cert ); - - return ret; -} - -void pkcs11_priv_key_free( pkcs11_context *priv_key ) -{ - if( NULL != priv_key ) - pkcs11h_certificate_freeCertificate( priv_key->pkcs11h_cert ); -} - -int pkcs11_decrypt( pkcs11_context *ctx, - int mode, size_t *olen, - const unsigned char *input, - unsigned char *output, - size_t output_max_len ) -{ - size_t input_len, output_len; - - if( NULL == ctx ) - return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); - - if( RSA_PUBLIC == mode ) - return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); - - output_len = input_len = ctx->len; - - if( input_len < 16 || input_len > output_max_len ) - return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); - - /* Determine size of output buffer */ - if( pkcs11h_certificate_decryptAny( ctx->pkcs11h_cert, CKM_RSA_PKCS, input, - input_len, NULL, &output_len ) != CKR_OK ) - { - return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); - } - - if( output_len > output_max_len ) - return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); - - if( pkcs11h_certificate_decryptAny( ctx->pkcs11h_cert, CKM_RSA_PKCS, input, - input_len, output, &output_len ) != CKR_OK ) - { - return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); - } - *olen = output_len; - return( 0 ); -} - -int pkcs11_sign( pkcs11_context *ctx, - int mode, - int hash_id, - unsigned int hashlen, - const unsigned char *hash, - unsigned char *sig ) -{ - size_t olen, asn_len; - unsigned char *p = sig; - - if( NULL == ctx ) - return POLARSSL_ERR_RSA_BAD_INPUT_DATA; - - if( RSA_PUBLIC == mode ) - return POLARSSL_ERR_RSA_BAD_INPUT_DATA; - - olen = ctx->len; - - switch( hash_id ) - { - case SIG_RSA_RAW: - asn_len = 0; - memcpy( p, hash, hashlen ); - break; - - case SIG_RSA_MD2: - asn_len = OID_SIZE(ASN1_HASH_MDX); - memcpy( p, ASN1_HASH_MDX, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[13] = 2; break; - - case SIG_RSA_MD4: - asn_len = OID_SIZE(ASN1_HASH_MDX); - memcpy( p, ASN1_HASH_MDX, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[13] = 4; break; - - case SIG_RSA_MD5: - asn_len = OID_SIZE(ASN1_HASH_MDX); - memcpy( p, ASN1_HASH_MDX, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[13] = 5; break; - - case SIG_RSA_SHA1: - asn_len = OID_SIZE(ASN1_HASH_SHA1); - memcpy( p, ASN1_HASH_SHA1, asn_len ); - memcpy( p + 15, hash, hashlen ); - break; - - case SIG_RSA_SHA224: - asn_len = OID_SIZE(ASN1_HASH_SHA2X); - memcpy( p, ASN1_HASH_SHA2X, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[1] += hashlen; p[14] = 4; p[18] += hashlen; break; - - case SIG_RSA_SHA256: - asn_len = OID_SIZE(ASN1_HASH_SHA2X); - memcpy( p, ASN1_HASH_SHA2X, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[1] += hashlen; p[14] = 1; p[18] += hashlen; break; - - case SIG_RSA_SHA384: - asn_len = OID_SIZE(ASN1_HASH_SHA2X); - memcpy( p, ASN1_HASH_SHA2X, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[1] += hashlen; p[14] = 2; p[18] += hashlen; break; - - case SIG_RSA_SHA512: - asn_len = OID_SIZE(ASN1_HASH_SHA2X); - memcpy( p, ASN1_HASH_SHA2X, asn_len ); - memcpy( p + asn_len, hash, hashlen ); - p[1] += hashlen; p[14] = 3; p[18] += hashlen; break; - - default: - return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); - } - - if( pkcs11h_certificate_signAny( ctx->pkcs11h_cert, CKM_RSA_PKCS, sig, - asn_len + hashlen, sig, &olen ) != CKR_OK ) - { - return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); - } - - return( 0 ); -} - -#endif /* defined(POLARSSL_PKCS11_C) */ diff --git a/makerom/polarssl/pkcs12.c b/makerom/polarssl/pkcs12.c deleted file mode 100644 index 2ee9c5ea..00000000 --- a/makerom/polarssl/pkcs12.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * PKCS#12 Personal Information Exchange Syntax - * - * Copyright (C) 2006-2013, 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 PKCS #12 Personal Information Exchange Syntax Standard v1.1 - * - * http://www.rsa.com/rsalabs/pkcs/files/h11301-wp-pkcs-12v1-1-personal-information-exchange-syntax.pdf - * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-12/pkcs-12v1-1.asn - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_PKCS12_C) - -#include "polarssl/pkcs12.h" -#include "polarssl/asn1.h" -#include "polarssl/cipher.h" - -#if defined(POLARSSL_ARC4_C) -#include "polarssl/arc4.h" -#endif - -#if defined(POLARSSL_DES_C) -#include "polarssl/des.h" -#endif - -static int pkcs12_parse_pbe_params( unsigned char **p, - const unsigned char *end, - asn1_buf *salt, int *iterations ) -{ - int ret; - size_t len = 0; - - /* - * pkcs-12PbeParams ::= SEQUENCE { - * salt OCTET STRING, - * iterations INTEGER - * } - * - */ - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - return( POLARSSL_ERR_PKCS12_PBE_INVALID_FORMAT + ret ); - } - - end = *p + len; - - if( ( ret = asn1_get_tag( p, end, &salt->len, ASN1_OCTET_STRING ) ) != 0 ) - return( POLARSSL_ERR_PKCS12_PBE_INVALID_FORMAT + ret ); - - salt->p = *p; - *p += salt->len; - - if( ( ret = asn1_get_int( p, end, iterations ) ) != 0 ) - return( POLARSSL_ERR_PKCS12_PBE_INVALID_FORMAT + ret ); - - if( *p != end ) - return( POLARSSL_ERR_PKCS12_PBE_INVALID_FORMAT + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); -} - -static int pkcs12_pbe_derive_key_iv( asn1_buf *pbe_params, md_type_t md_type, - const unsigned char *pwd, size_t pwdlen, - unsigned char *key, size_t keylen, - unsigned char *iv, size_t ivlen ) -{ - int ret, iterations; - asn1_buf salt; - size_t i; - unsigned char *p, *end; - unsigned char unipwd[258]; - - memset(&salt, 0, sizeof(asn1_buf)); - memset(&unipwd, 0, sizeof(unipwd)); - - p = pbe_params->p; - end = p + pbe_params->len; - - if( ( ret = pkcs12_parse_pbe_params( &p, end, &salt, &iterations ) ) != 0 ) - return( ret ); - - for(i = 0; i < pwdlen; i++) - unipwd[i * 2 + 1] = pwd[i]; - - if( ( ret = pkcs12_derivation( key, keylen, unipwd, pwdlen * 2 + 2, - salt.p, salt.len, md_type, - PKCS12_DERIVE_KEY, iterations ) ) != 0 ) - { - return( ret ); - } - - if( iv == NULL || ivlen == 0 ) - return( 0 ); - - if( ( ret = pkcs12_derivation( iv, ivlen, unipwd, pwdlen * 2 + 2, - salt.p, salt.len, md_type, - PKCS12_DERIVE_IV, iterations ) ) != 0 ) - { - return( ret ); - } - return( 0 ); -} - -int pkcs12_pbe_sha1_rc4_128( asn1_buf *pbe_params, int mode, - const unsigned char *pwd, size_t pwdlen, - const unsigned char *data, size_t len, - unsigned char *output ) -{ -#if !defined(POLARSSL_ARC4_C) - ((void) pbe_params); - ((void) mode); - ((void) pwd); - ((void) pwdlen); - ((void) data); - ((void) len); - ((void) output); - return( POLARSSL_ERR_PKCS12_FEATURE_UNAVAILABLE ); -#else - int ret; - unsigned char key[16]; - arc4_context ctx; - ((void) mode); - - if( ( ret = pkcs12_pbe_derive_key_iv( pbe_params, POLARSSL_MD_SHA1, - pwd, pwdlen, - key, 16, NULL, 0 ) ) != 0 ) - { - return( ret ); - } - - arc4_setup( &ctx, key, 16 ); - if( ( ret = arc4_crypt( &ctx, len, data, output ) ) != 0 ) - return( ret ); - - return( 0 ); -#endif /* POLARSSL_ARC4_C */ -} - -int pkcs12_pbe( asn1_buf *pbe_params, int mode, - cipher_type_t cipher_type, md_type_t md_type, - const unsigned char *pwd, size_t pwdlen, - const unsigned char *data, size_t len, - unsigned char *output ) -{ - int ret, keylen = 0; - unsigned char key[32]; - unsigned char iv[16]; - const cipher_info_t *cipher_info; - cipher_context_t cipher_ctx; - size_t olen = 0; - - cipher_info = cipher_info_from_type( cipher_type ); - if( cipher_info == NULL ) - return( POLARSSL_ERR_PKCS12_FEATURE_UNAVAILABLE ); - - keylen = cipher_info->key_length / 8; - - if( ( ret = pkcs12_pbe_derive_key_iv( pbe_params, md_type, pwd, pwdlen, - key, keylen, - iv, cipher_info->iv_size ) ) != 0 ) - { - return( ret ); - } - - if( ( ret = cipher_init_ctx( &cipher_ctx, cipher_info ) ) != 0 ) - return( ret ); - - if( ( ret = cipher_setkey( &cipher_ctx, key, keylen, mode ) ) != 0 ) - return( ret ); - - if( ( ret = cipher_reset( &cipher_ctx, iv ) ) != 0 ) - return( ret ); - - if( ( ret = cipher_update( &cipher_ctx, data, len, - output, &olen ) ) != 0 ) - { - return( ret ); - } - - if( ( ret = cipher_finish( &cipher_ctx, output + olen, &olen ) ) != 0 ) - return( POLARSSL_ERR_PKCS12_PASSWORD_MISMATCH ); - - return( 0 ); -} - -static void pkcs12_fill_buffer( unsigned char *data, size_t data_len, - const unsigned char *filler, size_t fill_len ) -{ - unsigned char *p = data; - size_t use_len; - - while( data_len > 0 ) - { - use_len = ( data_len > fill_len ) ? fill_len : data_len; - memcpy( p, filler, use_len ); - p += use_len; - data_len -= use_len; - } -} - -int pkcs12_derivation( unsigned char *data, size_t datalen, - const unsigned char *pwd, size_t pwdlen, - const unsigned char *salt, size_t saltlen, - md_type_t md_type, int id, int iterations ) -{ - int ret, i; - unsigned int j; - - unsigned char diversifier[128]; - unsigned char salt_block[128], pwd_block[128], hash_block[128]; - unsigned char hash_output[POLARSSL_MD_MAX_SIZE]; - unsigned char *p; - unsigned char c; - - size_t hlen, use_len, v; - - const md_info_t *md_info; - md_context_t md_ctx; - - // This version only allows max of 64 bytes of password or salt - if( datalen > 128 || pwdlen > 64 || saltlen > 64 ) - return( POLARSSL_ERR_PKCS12_BAD_INPUT_DATA ); - - md_info = md_info_from_type( md_type ); - if( md_info == NULL ) - return( POLARSSL_ERR_PKCS12_FEATURE_UNAVAILABLE ); - - if ( ( ret = md_init_ctx( &md_ctx, md_info ) ) != 0 ) - return( ret ); - hlen = md_get_size( md_info ); - - if( hlen <= 32 ) - v = 64; - else - v = 128; - - memset( diversifier, (unsigned char) id, v ); - - pkcs12_fill_buffer( salt_block, v, salt, saltlen ); - pkcs12_fill_buffer( pwd_block, v, pwd, pwdlen ); - - p = data; - while( datalen > 0 ) - { - // Calculate hash( diversifier || salt_block || pwd_block ) - if( ( ret = md_starts( &md_ctx ) ) != 0 ) - return( ret ); - - if( ( ret = md_update( &md_ctx, diversifier, v ) ) != 0 ) - return( ret ); - - if( ( ret = md_update( &md_ctx, salt_block, v ) ) != 0 ) - return( ret ); - - if( ( ret = md_update( &md_ctx, pwd_block, v ) ) != 0 ) - return( ret ); - - if( ( ret = md_finish( &md_ctx, hash_output ) ) != 0 ) - return( ret ); - - // Perform remaining ( iterations - 1 ) recursive hash calculations - for( i = 1; i < iterations; i++ ) - { - if( ( ret = md( md_info, hash_output, hlen, hash_output ) ) != 0 ) - return( ret ); - } - - use_len = ( datalen > hlen ) ? hlen : datalen; - memcpy( p, hash_output, use_len ); - datalen -= use_len; - p += use_len; - - if( datalen == 0 ) - break; - - // Concatenating copies of hash_output into hash_block (B) - pkcs12_fill_buffer( hash_block, v, hash_output, hlen ); - - // B += 1 - for( i = v; i > 0; i-- ) - if( ++hash_block[i - 1] != 0 ) - break; - - // salt_block += B - c = 0; - for( i = v; i > 0; i-- ) - { - j = salt_block[i - 1] + hash_block[i - 1] + c; - c = (unsigned char) (j >> 8); - salt_block[i - 1] = j & 0xFF; - } - - // pwd_block += B - c = 0; - for( i = v; i > 0; i-- ) - { - j = pwd_block[i - 1] + hash_block[i - 1] + c; - c = (unsigned char) (j >> 8); - pwd_block[i - 1] = j & 0xFF; - } - } - - return( 0 ); -} - -#endif /* POLARSSL_PKCS12_C */ diff --git a/makerom/polarssl/pkcs5.c b/makerom/polarssl/pkcs5.c deleted file mode 100644 index 8e55415b..00000000 --- a/makerom/polarssl/pkcs5.c +++ /dev/null @@ -1,415 +0,0 @@ -/** - * \file pkcs5.c - * - * \brief PKCS#5 functions - * - * \author Mathias Olsson - * - * Copyright (C) 2006-2013, 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. - */ -/* - * PKCS#5 includes PBKDF2 and more - * - * http://tools.ietf.org/html/rfc2898 (Specification) - * http://tools.ietf.org/html/rfc6070 (Test vectors) - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_PKCS5_C) - -#include "polarssl/pkcs5.h" -#include "polarssl/asn1.h" -#include "polarssl/cipher.h" - -#define OID_CMP(oid_str, oid_buf) \ - ( ( OID_SIZE(oid_str) == (oid_buf)->len ) && \ - memcmp( (oid_str), (oid_buf)->p, (oid_buf)->len) == 0) - -static int pkcs5_parse_pbkdf2_params( unsigned char **p, - const unsigned char *end, - asn1_buf *salt, int *iterations, - int *keylen, md_type_t *md_type ) -{ - int ret; - size_t len = 0; - asn1_buf prf_alg_oid; - - /* - * PBKDF2-params ::= SEQUENCE { - * salt OCTET STRING, - * iterationCount INTEGER, - * keyLength INTEGER OPTIONAL - * prf AlgorithmIdentifier DEFAULT algid-hmacWithSHA1 - * } - * - */ - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); - } - - end = *p + len; - - if( ( ret = asn1_get_tag( p, end, &salt->len, ASN1_OCTET_STRING ) ) != 0 ) - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); - - salt->p = *p; - *p += salt->len; - - if( ( ret = asn1_get_int( p, end, iterations ) ) != 0 ) - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); - - if( *p == end ) - return( 0 ); - - if( ( ret = asn1_get_int( p, end, keylen ) ) != 0 ) - { - if( ret != POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); - } - - if( *p == end ) - return( 0 ); - - if( ( ret = asn1_get_tag( p, end, &prf_alg_oid.len, ASN1_OID ) ) != 0 ) - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); - - if( !OID_CMP( OID_HMAC_SHA1, &prf_alg_oid ) ) - return( POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE ); - - *md_type = POLARSSL_MD_SHA1; - - if( *p != end ) - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); -} - -int pkcs5_pbes2( asn1_buf *pbe_params, int mode, - const unsigned char *pwd, size_t pwdlen, - const unsigned char *data, size_t datalen, - unsigned char *output ) -{ - int ret, iterations = 0, keylen = 0; - unsigned char *p, *end, *end2; - asn1_buf kdf_alg_oid, enc_scheme_oid, salt; - md_type_t md_type = POLARSSL_MD_SHA1; - unsigned char key[32], iv[32]; - size_t len = 0, olen = 0; - const md_info_t *md_info; - const cipher_info_t *cipher_info; - md_context_t md_ctx; - cipher_context_t cipher_ctx; - - p = pbe_params->p; - end = p + pbe_params->len; - - /* - * PBES2-params ::= SEQUENCE { - * keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}}, - * encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} - * } - */ - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); - } - - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); - } - - end2 = p + len; - - if( ( ret = asn1_get_tag( &p, end2, &kdf_alg_oid.len, ASN1_OID ) ) != 0 ) - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); - - kdf_alg_oid.p = p; - p += kdf_alg_oid.len; - - // Only PBKDF2 supported at the moment - // - if( !OID_CMP( OID_PKCS5_PBKDF2, &kdf_alg_oid ) ) - return( POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE ); - - if( ( ret = pkcs5_parse_pbkdf2_params( &p, end2, - &salt, &iterations, &keylen, - &md_type ) ) != 0 ) - { - return( ret ); - } - - md_info = md_info_from_type( md_type ); - if( md_info == NULL ) - return( POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE ); - - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); - } - - end2 = p + len; - - if( ( ret = asn1_get_tag( &p, end2, &enc_scheme_oid.len, ASN1_OID ) ) != 0 ) - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); - - enc_scheme_oid.p = p; - p += enc_scheme_oid.len; - -#if defined(POLARSSL_DES_C) - // Only DES-CBC and DES-EDE3-CBC supported at the moment - // - if( OID_CMP( OID_DES_EDE3_CBC, &enc_scheme_oid ) ) - { - cipher_info = cipher_info_from_type( POLARSSL_CIPHER_DES_EDE3_CBC ); - } - else if( OID_CMP( OID_DES_CBC, &enc_scheme_oid ) ) - { - cipher_info = cipher_info_from_type( POLARSSL_CIPHER_DES_CBC ); - } - else -#endif /* POLARSSL_DES_C */ - return( POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE ); - - if( cipher_info == NULL ) - return( POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE ); - - keylen = cipher_info->key_length / 8; - - if( ( ret = asn1_get_tag( &p, end2, &len, ASN1_OCTET_STRING ) ) != 0 ) - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); - - if( len != cipher_info->iv_size ) - return( POLARSSL_ERR_PKCS5_INVALID_FORMAT ); - - memcpy( iv, p, len ); - - if( ( ret = md_init_ctx( &md_ctx, md_info ) ) != 0 ) - return( ret ); - - if( ( ret = cipher_init_ctx( &cipher_ctx, cipher_info ) ) != 0 ) - return( ret ); - - if ( ( ret = pkcs5_pbkdf2_hmac( &md_ctx, pwd, pwdlen, salt.p, salt.len, - iterations, keylen, key ) ) != 0 ) - { - return( ret ); - } - - if( ( ret = cipher_setkey( &cipher_ctx, key, keylen, mode ) ) != 0 ) - return( ret ); - - if( ( ret = cipher_reset( &cipher_ctx, iv ) ) != 0 ) - return( ret ); - - if( ( ret = cipher_update( &cipher_ctx, data, datalen, - output, &olen ) ) != 0 ) - { - return( ret ); - } - - if( ( ret = cipher_finish( &cipher_ctx, output + olen, &olen ) ) != 0 ) - return( POLARSSL_ERR_PKCS5_PASSWORD_MISMATCH ); - - return( 0 ); -} - -int pkcs5_pbkdf2_hmac( md_context_t *ctx, const unsigned char *password, - size_t plen, const unsigned char *salt, size_t slen, - unsigned int iteration_count, - uint32_t key_length, unsigned char *output ) -{ - int ret, j; - unsigned int i; - unsigned char md1[POLARSSL_MD_MAX_SIZE]; - unsigned char work[POLARSSL_MD_MAX_SIZE]; - unsigned char md_size = md_get_size( ctx->md_info ); - size_t use_len; - unsigned char *out_p = output; - unsigned char counter[4]; - - memset( counter, 0, 4 ); - counter[3] = 1; - - if( iteration_count > 0xFFFFFFFF ) - return( POLARSSL_ERR_PKCS5_BAD_INPUT_DATA ); - - while( key_length ) - { - // U1 ends up in work - // - if( ( ret = md_hmac_starts( ctx, password, plen ) ) != 0 ) - return( ret ); - - if( ( ret = md_hmac_update( ctx, salt, slen ) ) != 0 ) - return( ret ); - - if( ( ret = md_hmac_update( ctx, counter, 4 ) ) != 0 ) - return( ret ); - - if( ( ret = md_hmac_finish( ctx, work ) ) != 0 ) - return( ret ); - - memcpy( md1, work, md_size ); - - for ( i = 1; i < iteration_count; i++ ) - { - // U2 ends up in md1 - // - if( ( ret = md_hmac_starts( ctx, password, plen ) ) != 0 ) - return( ret ); - - if( ( ret = md_hmac_update( ctx, md1, md_size ) ) != 0 ) - return( ret ); - - if( ( ret = md_hmac_finish( ctx, md1 ) ) != 0 ) - return( ret ); - - // U1 xor U2 - // - for( j = 0; j < md_size; j++ ) - work[j] ^= md1[j]; - } - - use_len = ( key_length < md_size ) ? key_length : md_size; - memcpy( out_p, work, use_len ); - - key_length -= use_len; - out_p += use_len; - - for( i = 4; i > 0; i-- ) - if( ++counter[i - 1] != 0 ) - break; - } - - return( 0 ); -} - -#if defined(POLARSSL_SELF_TEST) - -#include - -#define MAX_TESTS 6 - -size_t plen[MAX_TESTS] = - { 8, 8, 8, 8, 24, 9 }; - -unsigned char password[MAX_TESTS][32] = -{ - "password", - "password", - "password", - "password", - "passwordPASSWORDpassword", - "pass\0word", -}; - -size_t slen[MAX_TESTS] = - { 4, 4, 4, 4, 36, 5 }; - -unsigned char salt[MAX_TESTS][40] = -{ - "salt", - "salt", - "salt", - "salt", - "saltSALTsaltSALTsaltSALTsaltSALTsalt", - "sa\0lt", -}; - -uint32_t it_cnt[MAX_TESTS] = - { 1, 2, 4096, 16777216, 4096, 4096 }; - -uint32_t key_len[MAX_TESTS] = - { 20, 20, 20, 20, 25, 16 }; - - -unsigned char result_key[MAX_TESTS][32] = -{ - { 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, - 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06, - 0x2f, 0xe0, 0x37, 0xa6 }, - { 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, - 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0, - 0xd8, 0xde, 0x89, 0x57 }, - { 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a, - 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0, - 0x65, 0xa4, 0x29, 0xc1 }, - { 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4, - 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c, - 0x26, 0x34, 0xe9, 0x84 }, - { 0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b, - 0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a, - 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, - 0x38 }, - { 0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d, - 0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3 }, -}; - -int pkcs5_self_test( int verbose ) -{ - md_context_t sha1_ctx; - const md_info_t *info_sha1; - int ret, i; - unsigned char key[64]; - - info_sha1 = md_info_from_type( POLARSSL_MD_SHA1 ); - if( info_sha1 == NULL ) - return( 1 ); - - if( ( ret = md_init_ctx( &sha1_ctx, info_sha1 ) ) != 0 ) - return( 1 ); - - for( i = 0; i < MAX_TESTS; i++ ) - { - printf( " PBKDF2 (SHA1) #%d: ", i ); - - ret = pkcs5_pbkdf2_hmac( &sha1_ctx, password[i], plen[i], salt[i], - slen[i], it_cnt[i], key_len[i], key ); - if( ret != 0 || - memcmp( result_key[i], key, key_len[i] ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( verbose != 0 ) - printf( "passed\n" ); - } - - printf( "\n" ); - - return( 0 ); -} - -#endif /* POLARSSL_SELF_TEST */ - -#endif /* POLARSSL_PKCS5_C */ diff --git a/makerom/polarssl/ssl_cache.c b/makerom/polarssl/ssl_cache.c deleted file mode 100644 index f5686be0..00000000 --- a/makerom/polarssl/ssl_cache.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * SSL session cache implementation - * - * Copyright (C) 2006-2012, 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. - */ -/* - * These session callbacks use a simple chained list - * to store and retrieve the session information. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_SSL_CACHE_C) - -#include "polarssl/ssl_cache.h" - -#include - -void ssl_cache_init( ssl_cache_context *cache ) -{ - memset( cache, 0, sizeof( ssl_cache_context ) ); - - cache->timeout = SSL_CACHE_DEFAULT_TIMEOUT; - cache->max_entries = SSL_CACHE_DEFAULT_MAX_ENTRIES; -} - -int ssl_cache_get( void *data, ssl_session *session ) -{ - time_t t = time( NULL ); - ssl_cache_context *cache = (ssl_cache_context *) data; - ssl_cache_entry *cur, *entry; - - cur = cache->chain; - entry = NULL; - - while( cur != NULL ) - { - entry = cur; - cur = cur->next; - - if( cache->timeout != 0 && - (int) ( t - entry->timestamp ) > cache->timeout ) - continue; - - if( session->ciphersuite != entry->session.ciphersuite || - session->compression != entry->session.compression || - session->length != entry->session.length ) - continue; - - if( memcmp( session->id, entry->session.id, - entry->session.length ) != 0 ) - continue; - - memcpy( session->master, entry->session.master, 48 ); - - /* - * Restore peer certificate (without rest of the original chain) - */ - if( entry->peer_cert.p != NULL ) - { - session->peer_cert = (x509_cert *) malloc( sizeof(x509_cert) ); - if( session->peer_cert == NULL ) - return( 1 ); - - memset( session->peer_cert, 0, sizeof(x509_cert) ); - if( x509parse_crt( session->peer_cert, entry->peer_cert.p, - entry->peer_cert.len ) != 0 ) - { - free( session->peer_cert ); - session->peer_cert = NULL; - return( 1 ); - } - } - - return( 0 ); - } - - return( 1 ); -} - -int ssl_cache_set( void *data, const ssl_session *session ) -{ - time_t t = time( NULL ), oldest = 0; - ssl_cache_context *cache = (ssl_cache_context *) data; - ssl_cache_entry *cur, *prv, *old = NULL; - int count = 0; - - cur = cache->chain; - prv = NULL; - - while( cur != NULL ) - { - count++; - - if( cache->timeout != 0 && - (int) ( t - cur->timestamp ) > cache->timeout ) - { - cur->timestamp = t; - break; /* expired, reuse this slot, update timestamp */ - } - - if( memcmp( session->id, cur->session.id, cur->session.length ) == 0 ) - break; /* client reconnected, keep timestamp for session id */ - - if( oldest == 0 || cur->timestamp < oldest ) - { - oldest = cur->timestamp; - old = cur; - } - - prv = cur; - cur = cur->next; - } - - if( cur == NULL ) - { - /* - * Reuse oldest entry if max_entries reached - */ - if( old != NULL && count >= cache->max_entries ) - { - cur = old; - memset( &cur->session, 0, sizeof(ssl_session) ); - if( cur->peer_cert.p != NULL ) - { - free( cur->peer_cert.p ); - memset( &cur->peer_cert, 0, sizeof(x509_buf) ); - } - } - else - { - cur = (ssl_cache_entry *) malloc( sizeof(ssl_cache_entry) ); - if( cur == NULL ) - return( 1 ); - - memset( cur, 0, sizeof(ssl_cache_entry) ); - - if( prv == NULL ) - cache->chain = cur; - else - prv->next = cur; - } - - cur->timestamp = t; - } - - memcpy( &cur->session, session, sizeof( ssl_session ) ); - - /* - * Store peer certificate - */ - if( session->peer_cert != NULL ) - { - cur->peer_cert.p = (unsigned char *) malloc( session->peer_cert->raw.len ); - if( cur->peer_cert.p == NULL ) - return( 1 ); - - memcpy( cur->peer_cert.p, session->peer_cert->raw.p, - session->peer_cert->raw.len ); - cur->peer_cert.len = session->peer_cert->raw.len; - - cur->session.peer_cert = NULL; - } - - return( 0 ); -} - -void ssl_cache_set_timeout( ssl_cache_context *cache, int timeout ) -{ - if( timeout < 0 ) timeout = 0; - - cache->timeout = timeout; -} - -void ssl_cache_set_max_entries( ssl_cache_context *cache, int max ) -{ - if( max < 0 ) max = 0; - - cache->max_entries = max; -} - -void ssl_cache_free( ssl_cache_context *cache ) -{ - ssl_cache_entry *cur, *prv; - - cur = cache->chain; - - while( cur != NULL ) - { - prv = cur; - cur = cur->next; - - ssl_session_free( &prv->session ); - - if( prv->peer_cert.p != NULL ) - free( prv->peer_cert.p ); - - free( prv ); - } -} - -#endif /* POLARSSL_SSL_CACHE_C */ diff --git a/makerom/polarssl/ssl_cli.c b/makerom/polarssl/ssl_cli.c deleted file mode 100644 index e4a102b9..00000000 --- a/makerom/polarssl/ssl_cli.c +++ /dev/null @@ -1,1386 +0,0 @@ -/* - * SSLv3/TLSv1 client-side functions - * - * Copyright (C) 2006-2012, 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_SSL_CLI_C) - -#include "polarssl/debug.h" -#include "polarssl/ssl.h" - -#include -#include -#include - -#if defined(POLARSSL_SHA4_C) -#include "polarssl/sha4.h" -#endif - -static int ssl_write_client_hello( ssl_context *ssl ) -{ - int ret; - size_t i, n, ext_len = 0; - unsigned char *buf; - unsigned char *p; - time_t t; - unsigned char sig_alg_list[20]; - size_t sig_alg_len = 0; - - SSL_DEBUG_MSG( 2, ( "=> write client hello" ) ); - - if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE ) - { - ssl->major_ver = ssl->min_major_ver; - ssl->minor_ver = ssl->min_minor_ver; - } - - if( ssl->max_major_ver == 0 && ssl->max_minor_ver == 0 ) - { - ssl->max_major_ver = SSL_MAJOR_VERSION_3; - ssl->max_minor_ver = SSL_MINOR_VERSION_3; - } - - /* - * 0 . 0 handshake type - * 1 . 3 handshake length - * 4 . 5 highest version supported - * 6 . 9 current UNIX time - * 10 . 37 random bytes - */ - buf = ssl->out_msg; - p = buf + 4; - - *p++ = (unsigned char) ssl->max_major_ver; - *p++ = (unsigned char) ssl->max_minor_ver; - - SSL_DEBUG_MSG( 3, ( "client hello, max version: [%d:%d]", - buf[4], buf[5] ) ); - - t = time( NULL ); - *p++ = (unsigned char)( t >> 24 ); - *p++ = (unsigned char)( t >> 16 ); - *p++ = (unsigned char)( t >> 8 ); - *p++ = (unsigned char)( t ); - - SSL_DEBUG_MSG( 3, ( "client hello, current time: %lu", t ) ); - - if( ( ret = ssl->f_rng( ssl->p_rng, p, 28 ) ) != 0 ) - return( ret ); - - p += 28; - - memcpy( ssl->handshake->randbytes, buf + 6, 32 ); - - SSL_DEBUG_BUF( 3, "client hello, random bytes", buf + 6, 32 ); - - /* - * 38 . 38 session id length - * 39 . 39+n session id - * 40+n . 41+n ciphersuitelist length - * 42+n . .. ciphersuitelist - * .. . .. compression methods length - * .. . .. compression methods - * .. . .. extensions length - * .. . .. extensions - */ - n = ssl->session_negotiate->length; - - if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE || n < 16 || n > 32 || - ssl->handshake->resume == 0 ) - n = 0; - - *p++ = (unsigned char) n; - - for( i = 0; i < n; i++ ) - *p++ = ssl->session_negotiate->id[i]; - - SSL_DEBUG_MSG( 3, ( "client hello, session id len.: %d", n ) ); - SSL_DEBUG_BUF( 3, "client hello, session id", buf + 39, n ); - - for( n = 0; ssl->ciphersuites[ssl->minor_ver][n] != 0; n++ ); - if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE ) n++; - *p++ = (unsigned char)( n >> 7 ); - *p++ = (unsigned char)( n << 1 ); - - /* - * Add TLS_EMPTY_RENEGOTIATION_INFO_SCSV - */ - if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE ) - { - *p++ = (unsigned char)( SSL_EMPTY_RENEGOTIATION_INFO >> 8 ); - *p++ = (unsigned char)( SSL_EMPTY_RENEGOTIATION_INFO ); - n--; - } - - SSL_DEBUG_MSG( 3, ( "client hello, got %d ciphersuites", n ) ); - - for( i = 0; i < n; i++ ) - { - SSL_DEBUG_MSG( 3, ( "client hello, add ciphersuite: %2d", - ssl->ciphersuites[ssl->minor_ver][i] ) ); - - *p++ = (unsigned char)( ssl->ciphersuites[ssl->minor_ver][i] >> 8 ); - *p++ = (unsigned char)( ssl->ciphersuites[ssl->minor_ver][i] ); - } - -#if defined(POLARSSL_ZLIB_SUPPORT) - SSL_DEBUG_MSG( 3, ( "client hello, compress len.: %d", 2 ) ); - SSL_DEBUG_MSG( 3, ( "client hello, compress alg.: %d %d", - SSL_COMPRESS_DEFLATE, SSL_COMPRESS_NULL ) ); - - *p++ = 2; - *p++ = SSL_COMPRESS_DEFLATE; - *p++ = SSL_COMPRESS_NULL; -#else - SSL_DEBUG_MSG( 3, ( "client hello, compress len.: %d", 1 ) ); - SSL_DEBUG_MSG( 3, ( "client hello, compress alg.: %d", SSL_COMPRESS_NULL ) ); - - *p++ = 1; - *p++ = SSL_COMPRESS_NULL; -#endif - - if ( ssl->hostname != NULL ) - { - SSL_DEBUG_MSG( 3, ( "client hello, prepping for server name extension: %s", - ssl->hostname ) ); - - ext_len += ssl->hostname_len + 9; - } - - if( ssl->renegotiation == SSL_RENEGOTIATION ) - { - SSL_DEBUG_MSG( 3, ( "client hello, prepping for renegotiation extension" ) ); - ext_len += 5 + ssl->verify_data_len; - } - - /* - * Prepare signature_algorithms extension (TLS 1.2) - */ - if( ssl->max_minor_ver == SSL_MINOR_VERSION_3 ) - { -#if defined(POLARSSL_SHA4_C) - sig_alg_list[sig_alg_len++] = SSL_HASH_SHA512; - sig_alg_list[sig_alg_len++] = SSL_SIG_RSA; - sig_alg_list[sig_alg_len++] = SSL_HASH_SHA384; - sig_alg_list[sig_alg_len++] = SSL_SIG_RSA; -#endif -#if defined(POLARSSL_SHA2_C) - sig_alg_list[sig_alg_len++] = SSL_HASH_SHA256; - sig_alg_list[sig_alg_len++] = SSL_SIG_RSA; - sig_alg_list[sig_alg_len++] = SSL_HASH_SHA224; - sig_alg_list[sig_alg_len++] = SSL_SIG_RSA; -#endif -#if defined(POLARSSL_SHA1_C) - sig_alg_list[sig_alg_len++] = SSL_HASH_SHA1; - sig_alg_list[sig_alg_len++] = SSL_SIG_RSA; -#endif -#if defined(POLARSSL_MD5_C) - sig_alg_list[sig_alg_len++] = SSL_HASH_MD5; - sig_alg_list[sig_alg_len++] = SSL_SIG_RSA; -#endif - ext_len += 6 + sig_alg_len; - } - - SSL_DEBUG_MSG( 3, ( "client hello, total extension length: %d", - ext_len ) ); - - *p++ = (unsigned char)( ( ext_len >> 8 ) & 0xFF ); - *p++ = (unsigned char)( ( ext_len ) & 0xFF ); - - if ( ssl->hostname != NULL ) - { - /* - * struct { - * NameType name_type; - * select (name_type) { - * case host_name: HostName; - * } name; - * } ServerName; - * - * enum { - * host_name(0), (255) - * } NameType; - * - * opaque HostName<1..2^16-1>; - * - * struct { - * ServerName server_name_list<1..2^16-1> - * } ServerNameList; - */ - SSL_DEBUG_MSG( 3, ( "client hello, adding server name extension: %s", - ssl->hostname ) ); - - *p++ = (unsigned char)( ( TLS_EXT_SERVERNAME >> 8 ) & 0xFF ); - *p++ = (unsigned char)( ( TLS_EXT_SERVERNAME ) & 0xFF ); - - *p++ = (unsigned char)( ( (ssl->hostname_len + 5) >> 8 ) & 0xFF ); - *p++ = (unsigned char)( ( (ssl->hostname_len + 5) ) & 0xFF ); - - *p++ = (unsigned char)( ( (ssl->hostname_len + 3) >> 8 ) & 0xFF ); - *p++ = (unsigned char)( ( (ssl->hostname_len + 3) ) & 0xFF ); - - *p++ = (unsigned char)( ( TLS_EXT_SERVERNAME_HOSTNAME ) & 0xFF ); - *p++ = (unsigned char)( ( ssl->hostname_len >> 8 ) & 0xFF ); - *p++ = (unsigned char)( ( ssl->hostname_len ) & 0xFF ); - - memcpy( p, ssl->hostname, ssl->hostname_len ); - p += ssl->hostname_len; - } - - if( ssl->renegotiation == SSL_RENEGOTIATION ) - { - /* - * Secure renegotiation - */ - SSL_DEBUG_MSG( 3, ( "client hello, renegotiation info extension" ) ); - - *p++ = (unsigned char)( ( TLS_EXT_RENEGOTIATION_INFO >> 8 ) & 0xFF ); - *p++ = (unsigned char)( ( TLS_EXT_RENEGOTIATION_INFO ) & 0xFF ); - - *p++ = 0x00; - *p++ = ( ssl->verify_data_len + 1 ) & 0xFF; - *p++ = ssl->verify_data_len & 0xFF; - - memcpy( p, ssl->own_verify_data, ssl->verify_data_len ); - p += ssl->verify_data_len; - } - - if( ssl->max_minor_ver == SSL_MINOR_VERSION_3 ) - { - /* - * enum { - * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), - * sha512(6), (255) - * } HashAlgorithm; - * - * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } - * SignatureAlgorithm; - * - * struct { - * HashAlgorithm hash; - * SignatureAlgorithm signature; - * } SignatureAndHashAlgorithm; - * - * SignatureAndHashAlgorithm - * supported_signature_algorithms<2..2^16-2>; - */ - SSL_DEBUG_MSG( 3, ( "client hello, adding signature_algorithms extension" ) ); - - *p++ = (unsigned char)( ( TLS_EXT_SIG_ALG >> 8 ) & 0xFF ); - *p++ = (unsigned char)( ( TLS_EXT_SIG_ALG ) & 0xFF ); - - *p++ = (unsigned char)( ( ( sig_alg_len + 2 ) >> 8 ) & 0xFF ); - *p++ = (unsigned char)( ( ( sig_alg_len + 2 ) ) & 0xFF ); - - *p++ = (unsigned char)( ( sig_alg_len >> 8 ) & 0xFF ); - *p++ = (unsigned char)( ( sig_alg_len ) & 0xFF ); - - memcpy( p, sig_alg_list, sig_alg_len ); - - p += sig_alg_len; - } - - ssl->out_msglen = p - buf; - ssl->out_msgtype = SSL_MSG_HANDSHAKE; - ssl->out_msg[0] = SSL_HS_CLIENT_HELLO; - - ssl->state++; - - if( ( ret = ssl_write_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_write_record", ret ); - return( ret ); - } - - SSL_DEBUG_MSG( 2, ( "<= write client hello" ) ); - - return( 0 ); -} - -static int ssl_parse_renegotiation_info( ssl_context *ssl, - unsigned char *buf, - size_t len ) -{ - int ret; - - if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE ) - { - if( len != 1 || buf[0] != 0x0 ) - { - SSL_DEBUG_MSG( 1, ( "non-zero length renegotiated connection field" ) ); - - if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) - return( ret ); - - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); - } - - ssl->secure_renegotiation = SSL_SECURE_RENEGOTIATION; - } - else - { - if( len != 1 + ssl->verify_data_len * 2 || - buf[0] != ssl->verify_data_len * 2 || - memcmp( buf + 1, ssl->own_verify_data, ssl->verify_data_len ) != 0 || - memcmp( buf + 1 + ssl->verify_data_len, - ssl->peer_verify_data, ssl->verify_data_len ) != 0 ) - { - SSL_DEBUG_MSG( 1, ( "non-matching renegotiated connection field" ) ); - - if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) - return( ret ); - - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); - } - } - - return( 0 ); -} - -static int ssl_parse_server_hello( ssl_context *ssl ) -{ -#if defined(POLARSSL_DEBUG_C) - time_t t; -#endif - int ret, i, comp; - size_t n; - size_t ext_len = 0; - unsigned char *buf, *ext; - int renegotiation_info_seen = 0; - int handshake_failure = 0; - - SSL_DEBUG_MSG( 2, ( "=> parse server hello" ) ); - - /* - * 0 . 0 handshake type - * 1 . 3 handshake length - * 4 . 5 protocol version - * 6 . 9 UNIX time() - * 10 . 37 random bytes - */ - buf = ssl->in_msg; - - if( ( ret = ssl_read_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_read_record", ret ); - return( ret ); - } - - if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); - return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); - } - - SSL_DEBUG_MSG( 3, ( "server hello, chosen version: [%d:%d]", - buf[4], buf[5] ) ); - - if( ssl->in_hslen < 42 || - buf[0] != SSL_HS_SERVER_HELLO || - buf[4] != SSL_MAJOR_VERSION_3 ) - { - SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); - } - - if( buf[5] > ssl->max_minor_ver ) - { - SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); - } - - ssl->minor_ver = buf[5]; - - if( ssl->minor_ver < ssl->min_minor_ver ) - { - SSL_DEBUG_MSG( 1, ( "server only supports ssl smaller than minimum" - " [%d:%d] < [%d:%d]", ssl->major_ver, ssl->minor_ver, - buf[4], buf[5] ) ); - - ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, - SSL_ALERT_MSG_PROTOCOL_VERSION ); - - return( POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION ); - } - -#if defined(POLARSSL_DEBUG_C) - t = ( (time_t) buf[6] << 24 ) - | ( (time_t) buf[7] << 16 ) - | ( (time_t) buf[8] << 8 ) - | ( (time_t) buf[9] ); -#endif - - memcpy( ssl->handshake->randbytes + 32, buf + 6, 32 ); - - n = buf[38]; - - SSL_DEBUG_MSG( 3, ( "server hello, current time: %lu", t ) ); - SSL_DEBUG_BUF( 3, "server hello, random bytes", buf + 6, 32 ); - - if( n > 32 ) - { - SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); - } - - /* - * 38 . 38 session id length - * 39 . 38+n session id - * 39+n . 40+n chosen ciphersuite - * 41+n . 41+n chosen compression alg. - * 42+n . 43+n extensions length - * 44+n . 44+n+m extensions - */ - if( ssl->in_hslen > 42 + n ) - { - ext_len = ( ( buf[42 + n] << 8 ) - | ( buf[43 + n] ) ); - - if( ( ext_len > 0 && ext_len < 4 ) || - ssl->in_hslen != 44 + n + ext_len ) - { - SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); - } - } - - i = ( buf[39 + n] << 8 ) | buf[40 + n]; - comp = buf[41 + n]; - - /* - * Initialize update checksum functions - */ - ssl_optimize_checksum( ssl, i ); - - SSL_DEBUG_MSG( 3, ( "server hello, session id len.: %d", n ) ); - SSL_DEBUG_BUF( 3, "server hello, session id", buf + 39, n ); - - /* - * Check if the session can be resumed - */ - if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE || - ssl->handshake->resume == 0 || n == 0 || - ssl->session_negotiate->ciphersuite != i || - ssl->session_negotiate->compression != comp || - ssl->session_negotiate->length != n || - memcmp( ssl->session_negotiate->id, buf + 39, n ) != 0 ) - { - ssl->state++; - ssl->handshake->resume = 0; - ssl->session_negotiate->start = time( NULL ); - ssl->session_negotiate->ciphersuite = i; - ssl->session_negotiate->compression = comp; - ssl->session_negotiate->length = n; - memcpy( ssl->session_negotiate->id, buf + 39, n ); - } - else - { - ssl->state = SSL_SERVER_CHANGE_CIPHER_SPEC; - - if( ( ret = ssl_derive_keys( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_derive_keys", ret ); - return( ret ); - } - } - - SSL_DEBUG_MSG( 3, ( "%s session has been resumed", - ssl->handshake->resume ? "a" : "no" ) ); - - SSL_DEBUG_MSG( 3, ( "server hello, chosen ciphersuite: %d", i ) ); - SSL_DEBUG_MSG( 3, ( "server hello, compress alg.: %d", buf[41 + n] ) ); - - i = 0; - while( 1 ) - { - if( ssl->ciphersuites[ssl->minor_ver][i] == 0 ) - { - SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); - } - - if( ssl->ciphersuites[ssl->minor_ver][i++] == ssl->session_negotiate->ciphersuite ) - break; - } - - if( comp != SSL_COMPRESS_NULL -#if defined(POLARSSL_ZLIB_SUPPORT) - && comp != SSL_COMPRESS_DEFLATE -#endif - ) - { - SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); - } - ssl->session_negotiate->compression = comp; - - ext = buf + 44 + n; - - while( ext_len ) - { - unsigned int ext_id = ( ( ext[0] << 8 ) - | ( ext[1] ) ); - unsigned int ext_size = ( ( ext[2] << 8 ) - | ( ext[3] ) ); - - if( ext_size + 4 > ext_len ) - { - SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); - } - - switch( ext_id ) - { - case TLS_EXT_RENEGOTIATION_INFO: - SSL_DEBUG_MSG( 3, ( "found renegotiation extension" ) ); - renegotiation_info_seen = 1; - - if( ( ret = ssl_parse_renegotiation_info( ssl, ext + 4, ext_size ) ) != 0 ) - return( ret ); - - break; - - default: - SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)", - ext_id ) ); - } - - ext_len -= 4 + ext_size; - ext += 4 + ext_size; - - if( ext_len > 0 && ext_len < 4 ) - { - SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); - } - } - - /* - * Renegotiation security checks - */ - if( ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && - ssl->allow_legacy_renegotiation == SSL_LEGACY_BREAK_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "legacy renegotiation, breaking off handshake" ) ); - handshake_failure = 1; - } - else if( ssl->renegotiation == SSL_RENEGOTIATION && - ssl->secure_renegotiation == SSL_SECURE_RENEGOTIATION && - renegotiation_info_seen == 0 ) - { - SSL_DEBUG_MSG( 1, ( "renegotiation_info extension missing (secure)" ) ); - handshake_failure = 1; - } - else if( ssl->renegotiation == SSL_RENEGOTIATION && - ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && - ssl->allow_legacy_renegotiation == SSL_LEGACY_NO_RENEGOTIATION ) - { - SSL_DEBUG_MSG( 1, ( "legacy renegotiation not allowed" ) ); - handshake_failure = 1; - } - else if( ssl->renegotiation == SSL_RENEGOTIATION && - ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && - renegotiation_info_seen == 1 ) - { - SSL_DEBUG_MSG( 1, ( "renegotiation_info extension present (legacy)" ) ); - handshake_failure = 1; - } - - if( handshake_failure == 1 ) - { - if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) - return( ret ); - - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); - } - - SSL_DEBUG_MSG( 2, ( "<= parse server hello" ) ); - - return( 0 ); -} - -static int ssl_parse_server_key_exchange( ssl_context *ssl ) -{ -#if defined(POLARSSL_DHM_C) - int ret; - size_t n; - unsigned char *p, *end; - unsigned char hash[64]; - md5_context md5; - sha1_context sha1; - int hash_id = SIG_RSA_RAW; - unsigned int hashlen = 0; -#endif - - SSL_DEBUG_MSG( 2, ( "=> parse server key exchange" ) ); - - if( ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_DES_CBC_SHA && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ) - { - SSL_DEBUG_MSG( 2, ( "<= skip parse server key exchange" ) ); - ssl->state++; - return( 0 ); - } - -#if !defined(POLARSSL_DHM_C) - SSL_DEBUG_MSG( 1, ( "support for dhm in not available" ) ); - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); -#else - if( ( ret = ssl_read_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_read_record", ret ); - return( ret ); - } - - if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); - return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); - } - - if( ssl->in_msg[0] != SSL_HS_SERVER_KEY_EXCHANGE ) - { - SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); - } - - SSL_DEBUG_BUF( 3, "server key exchange", ssl->in_msg + 4, ssl->in_hslen - 4 ); - - /* - * Ephemeral DH parameters: - * - * struct { - * opaque dh_p<1..2^16-1>; - * opaque dh_g<1..2^16-1>; - * opaque dh_Ys<1..2^16-1>; - * } ServerDHParams; - */ - p = ssl->in_msg + 4; - end = ssl->in_msg + ssl->in_hslen; - - if( ( ret = dhm_read_params( &ssl->handshake->dhm_ctx, &p, end ) ) != 0 ) - { - SSL_DEBUG_MSG( 2, ( "DHM Read Params returned -0x%x", -ret ) ); - SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); - } - - if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) - { - if( p[1] != SSL_SIG_RSA ) - { - SSL_DEBUG_MSG( 2, ( "server used unsupported SignatureAlgorithm %d", p[1] ) ); - SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); - } - - switch( p[0] ) - { -#if defined(POLARSSL_MD5_C) - case SSL_HASH_MD5: - hash_id = SIG_RSA_MD5; - break; -#endif -#if defined(POLARSSL_SHA1_C) - case SSL_HASH_SHA1: - hash_id = SIG_RSA_SHA1; - break; -#endif -#if defined(POLARSSL_SHA2_C) - case SSL_HASH_SHA224: - hash_id = SIG_RSA_SHA224; - break; - case SSL_HASH_SHA256: - hash_id = SIG_RSA_SHA256; - break; -#endif -#if defined(POLARSSL_SHA4_C) - case SSL_HASH_SHA384: - hash_id = SIG_RSA_SHA384; - break; - case SSL_HASH_SHA512: - hash_id = SIG_RSA_SHA512; - break; -#endif - default: - SSL_DEBUG_MSG( 2, ( "Server used unsupported HashAlgorithm %d", p[0] ) ); - SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); - } - - SSL_DEBUG_MSG( 2, ( "Server used SignatureAlgorithm %d", p[1] ) ); - SSL_DEBUG_MSG( 2, ( "Server used HashAlgorithm %d", p[0] ) ); - p += 2; - } - - n = ( p[0] << 8 ) | p[1]; - p += 2; - - if( end != p + n ) - { - SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); - } - - if( (unsigned int)( end - p ) != - ssl->session_negotiate->peer_cert->rsa.len ) - { - SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); - } - - if( ssl->handshake->dhm_ctx.len < 64 || ssl->handshake->dhm_ctx.len > 512 ) - { - SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); - } - - SSL_DEBUG_MPI( 3, "DHM: P ", &ssl->handshake->dhm_ctx.P ); - SSL_DEBUG_MPI( 3, "DHM: G ", &ssl->handshake->dhm_ctx.G ); - SSL_DEBUG_MPI( 3, "DHM: GY", &ssl->handshake->dhm_ctx.GY ); - - if( ssl->minor_ver != SSL_MINOR_VERSION_3 ) - { - /* - * digitally-signed struct { - * opaque md5_hash[16]; - * opaque sha_hash[20]; - * }; - * - * md5_hash - * MD5(ClientHello.random + ServerHello.random - * + ServerParams); - * sha_hash - * SHA(ClientHello.random + ServerHello.random - * + ServerParams); - */ - n = ssl->in_hslen - ( end - p ) - 6; - - md5_starts( &md5 ); - md5_update( &md5, ssl->handshake->randbytes, 64 ); - md5_update( &md5, ssl->in_msg + 4, n ); - md5_finish( &md5, hash ); - - sha1_starts( &sha1 ); - sha1_update( &sha1, ssl->handshake->randbytes, 64 ); - sha1_update( &sha1, ssl->in_msg + 4, n ); - sha1_finish( &sha1, hash + 16 ); - - hash_id = SIG_RSA_RAW; - hashlen = 36; - } - else - { - sha2_context sha2; -#if defined(POLARSSL_SHA4_C) - sha4_context sha4; -#endif - - n = ssl->in_hslen - ( end - p ) - 8; - - /* - * digitally-signed struct { - * opaque client_random[32]; - * opaque server_random[32]; - * ServerDHParams params; - * }; - */ - switch( hash_id ) - { -#if defined(POLARSSL_MD5_C) - case SIG_RSA_MD5: - md5_starts( &md5 ); - md5_update( &md5, ssl->handshake->randbytes, 64 ); - md5_update( &md5, ssl->in_msg + 4, n ); - md5_finish( &md5, hash ); - hashlen = 16; - break; -#endif -#if defined(POLARSSL_SHA1_C) - case SIG_RSA_SHA1: - sha1_starts( &sha1 ); - sha1_update( &sha1, ssl->handshake->randbytes, 64 ); - sha1_update( &sha1, ssl->in_msg + 4, n ); - sha1_finish( &sha1, hash ); - hashlen = 20; - break; -#endif -#if defined(POLARSSL_SHA2_C) - case SIG_RSA_SHA224: - sha2_starts( &sha2, 1 ); - sha2_update( &sha2, ssl->handshake->randbytes, 64 ); - sha2_update( &sha2, ssl->in_msg + 4, n ); - sha2_finish( &sha2, hash ); - hashlen = 28; - break; - case SIG_RSA_SHA256: - sha2_starts( &sha2, 0 ); - sha2_update( &sha2, ssl->handshake->randbytes, 64 ); - sha2_update( &sha2, ssl->in_msg + 4, n ); - sha2_finish( &sha2, hash ); - hashlen = 32; - break; -#endif -#if defined(POLARSSL_SHA4_C) - case SIG_RSA_SHA384: - sha4_starts( &sha4, 1 ); - sha4_update( &sha4, ssl->handshake->randbytes, 64 ); - sha4_update( &sha4, ssl->in_msg + 4, n ); - sha4_finish( &sha4, hash ); - hashlen = 48; - break; - case SIG_RSA_SHA512: - sha4_starts( &sha4, 0 ); - sha4_update( &sha4, ssl->handshake->randbytes, 64 ); - sha4_update( &sha4, ssl->in_msg + 4, n ); - sha4_finish( &sha4, hash ); - hashlen = 64; - break; -#endif - } - } - - SSL_DEBUG_BUF( 3, "parameters hash", hash, hashlen ); - - if( ( ret = rsa_pkcs1_verify( &ssl->session_negotiate->peer_cert->rsa, - RSA_PUBLIC, - hash_id, hashlen, hash, p ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "rsa_pkcs1_verify", ret ); - return( ret ); - } - - ssl->state++; - - SSL_DEBUG_MSG( 2, ( "<= parse server key exchange" ) ); - - return( 0 ); -#endif -} - -static int ssl_parse_certificate_request( ssl_context *ssl ) -{ - int ret; - unsigned char *buf, *p; - size_t n = 0, m = 0; - size_t cert_type_len = 0, sig_alg_len = 0, dn_len = 0; - - SSL_DEBUG_MSG( 2, ( "=> parse certificate request" ) ); - - /* - * 0 . 0 handshake type - * 1 . 3 handshake length - * 4 . 4 cert type count - * 5 .. m-1 cert types - * m .. m+1 sig alg length (TLS 1.2 only) - * m+1 .. n-1 SignatureAndHashAlgorithms (TLS 1.2 only) - * n .. n+1 length of all DNs - * n+2 .. n+3 length of DN 1 - * n+4 .. ... Distinguished Name #1 - * ... .. ... length of DN 2, etc. - */ - if( ( ret = ssl_read_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_read_record", ret ); - return( ret ); - } - - if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) ); - return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); - } - - ssl->client_auth = 0; - ssl->state++; - - if( ssl->in_msg[0] == SSL_HS_CERTIFICATE_REQUEST ) - ssl->client_auth++; - - SSL_DEBUG_MSG( 3, ( "got %s certificate request", - ssl->client_auth ? "a" : "no" ) ); - - if( ssl->client_auth == 0 ) - goto exit; - - // TODO: handshake_failure alert for an anonymous server to request - // client authentication - - buf = ssl->in_msg; - - // Retrieve cert types - // - cert_type_len = buf[4]; - n = cert_type_len; - - if( ssl->in_hslen < 6 + n ) - { - SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST ); - } - - p = buf + 5; - while( cert_type_len > 0 ) - { - if( *p == SSL_CERT_TYPE_RSA_SIGN ) - { - ssl->handshake->cert_type = SSL_CERT_TYPE_RSA_SIGN; - break; - } - - cert_type_len--; - p++; - } - - if( ssl->handshake->cert_type == 0 ) - { - SSL_DEBUG_MSG( 1, ( "no known cert_type provided" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST ); - } - - if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) - { - sig_alg_len = ( ( buf[5 + n] << 8 ) - | ( buf[6 + n] ) ); - - p = buf + 7 + n; - m += 2; - n += sig_alg_len; - - if( ssl->in_hslen < 6 + n ) - { - SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST ); - } - } - - dn_len = ( ( buf[5 + m + n] << 8 ) - | ( buf[6 + m + n] ) ); - - n += dn_len; - if( ssl->in_hslen != 7 + m + n ) - { - SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST ); - } - -exit: - SSL_DEBUG_MSG( 2, ( "<= parse certificate request" ) ); - - return( 0 ); -} - -static int ssl_parse_server_hello_done( ssl_context *ssl ) -{ - int ret; - - SSL_DEBUG_MSG( 2, ( "=> parse server hello done" ) ); - - if( ssl->client_auth != 0 ) - { - if( ( ret = ssl_read_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_read_record", ret ); - return( ret ); - } - - if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "bad server hello done message" ) ); - return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); - } - } - - if( ssl->in_hslen != 4 || - ssl->in_msg[0] != SSL_HS_SERVER_HELLO_DONE ) - { - SSL_DEBUG_MSG( 1, ( "bad server hello done message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO_DONE ); - } - - ssl->state++; - - SSL_DEBUG_MSG( 2, ( "<= parse server hello done" ) ); - - return( 0 ); -} - -static int ssl_write_client_key_exchange( ssl_context *ssl ) -{ - int ret; - size_t i, n; - - SSL_DEBUG_MSG( 2, ( "=> write client key exchange" ) ); - - if( ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_DES_CBC_SHA || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_128_CBC_SHA || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_256_CBC_SHA || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ) - { -#if !defined(POLARSSL_DHM_C) - SSL_DEBUG_MSG( 1, ( "support for dhm in not available" ) ); - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); -#else - /* - * DHM key exchange -- send G^X mod P - */ - n = ssl->handshake->dhm_ctx.len; - - ssl->out_msg[4] = (unsigned char)( n >> 8 ); - ssl->out_msg[5] = (unsigned char)( n ); - i = 6; - - ret = dhm_make_public( &ssl->handshake->dhm_ctx, - mpi_size( &ssl->handshake->dhm_ctx.P ), - &ssl->out_msg[i], n, - ssl->f_rng, ssl->p_rng ); - if( ret != 0 ) - { - SSL_DEBUG_RET( 1, "dhm_make_public", ret ); - return( ret ); - } - - SSL_DEBUG_MPI( 3, "DHM: X ", &ssl->handshake->dhm_ctx.X ); - SSL_DEBUG_MPI( 3, "DHM: GX", &ssl->handshake->dhm_ctx.GX ); - - ssl->handshake->pmslen = ssl->handshake->dhm_ctx.len; - - if( ( ret = dhm_calc_secret( &ssl->handshake->dhm_ctx, - ssl->handshake->premaster, - &ssl->handshake->pmslen ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "dhm_calc_secret", ret ); - return( ret ); - } - - SSL_DEBUG_MPI( 3, "DHM: K ", &ssl->handshake->dhm_ctx.K ); -#endif - } - else - { - /* - * RSA key exchange -- send rsa_public(pkcs1 v1.5(premaster)) - */ - ssl->handshake->premaster[0] = (unsigned char) ssl->max_major_ver; - ssl->handshake->premaster[1] = (unsigned char) ssl->max_minor_ver; - ssl->handshake->pmslen = 48; - - ret = ssl->f_rng( ssl->p_rng, ssl->handshake->premaster + 2, - ssl->handshake->pmslen - 2 ); - if( ret != 0 ) - return( ret ); - - i = 4; - n = ssl->session_negotiate->peer_cert->rsa.len; - - if( ssl->minor_ver != SSL_MINOR_VERSION_0 ) - { - i += 2; - ssl->out_msg[4] = (unsigned char)( n >> 8 ); - ssl->out_msg[5] = (unsigned char)( n ); - } - - ret = rsa_pkcs1_encrypt( &ssl->session_negotiate->peer_cert->rsa, - ssl->f_rng, ssl->p_rng, - RSA_PUBLIC, - ssl->handshake->pmslen, - ssl->handshake->premaster, - ssl->out_msg + i ); - if( ret != 0 ) - { - SSL_DEBUG_RET( 1, "rsa_pkcs1_encrypt", ret ); - return( ret ); - } - } - - if( ( ret = ssl_derive_keys( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_derive_keys", ret ); - return( ret ); - } - - ssl->out_msglen = i + n; - ssl->out_msgtype = SSL_MSG_HANDSHAKE; - ssl->out_msg[0] = SSL_HS_CLIENT_KEY_EXCHANGE; - - ssl->state++; - - if( ( ret = ssl_write_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_write_record", ret ); - return( ret ); - } - - SSL_DEBUG_MSG( 2, ( "<= write client key exchange" ) ); - - return( 0 ); -} - -static int ssl_write_certificate_verify( ssl_context *ssl ) -{ - int ret = 0; - size_t n = 0, offset = 0; - unsigned char hash[48]; - int hash_id = SIG_RSA_RAW; - unsigned int hashlen = 36; - - SSL_DEBUG_MSG( 2, ( "=> write certificate verify" ) ); - - if( ssl->client_auth == 0 || ssl->own_cert == NULL ) - { - SSL_DEBUG_MSG( 2, ( "<= skip write certificate verify" ) ); - ssl->state++; - return( 0 ); - } - - if( ssl->rsa_key == NULL ) - { - SSL_DEBUG_MSG( 1, ( "got no private key" ) ); - return( POLARSSL_ERR_SSL_PRIVATE_KEY_REQUIRED ); - } - - /* - * Make an RSA signature of the handshake digests - */ - ssl->handshake->calc_verify( ssl, hash ); - - if( ssl->minor_ver != SSL_MINOR_VERSION_3 ) - { - /* - * digitally-signed struct { - * opaque md5_hash[16]; - * opaque sha_hash[20]; - * }; - * - * md5_hash - * MD5(handshake_messages); - * - * sha_hash - * SHA(handshake_messages); - */ - hashlen = 36; - hash_id = SIG_RSA_RAW; - } - else - { - /* - * digitally-signed struct { - * opaque handshake_messages[handshake_messages_length]; - * }; - * - * Taking shortcut here. We assume that the server always allows the - * PRF Hash function and has sent it in the allowed signature - * algorithms list received in the Certificate Request message. - * - * Until we encounter a server that does not, we will take this - * shortcut. - * - * Reason: Otherwise we should have running hashes for SHA512 and SHA224 - * in order to satisfy 'weird' needs from the server side. - */ - if( ssl->session_negotiate->ciphersuite == TLS_RSA_WITH_AES_256_GCM_SHA384 || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ) - { - hash_id = SIG_RSA_SHA384; - hashlen = 48; - ssl->out_msg[4] = SSL_HASH_SHA384; - ssl->out_msg[5] = SSL_SIG_RSA; - } - else - { - hash_id = SIG_RSA_SHA256; - hashlen = 32; - ssl->out_msg[4] = SSL_HASH_SHA256; - ssl->out_msg[5] = SSL_SIG_RSA; - } - - offset = 2; - } - - if ( ssl->rsa_key ) - n = ssl->rsa_key_len ( ssl->rsa_key ); - - ssl->out_msg[4 + offset] = (unsigned char)( n >> 8 ); - ssl->out_msg[5 + offset] = (unsigned char)( n ); - - if( ssl->rsa_key ) - { - ret = ssl->rsa_sign( ssl->rsa_key, ssl->f_rng, ssl->p_rng, - RSA_PRIVATE, hash_id, - hashlen, hash, ssl->out_msg + 6 + offset ); - } - - if (ret != 0) - { - SSL_DEBUG_RET( 1, "pkcs1_sign", ret ); - return( ret ); - } - - ssl->out_msglen = 6 + n + offset; - ssl->out_msgtype = SSL_MSG_HANDSHAKE; - ssl->out_msg[0] = SSL_HS_CERTIFICATE_VERIFY; - - ssl->state++; - - if( ( ret = ssl_write_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_write_record", ret ); - return( ret ); - } - - SSL_DEBUG_MSG( 2, ( "<= write certificate verify" ) ); - - return( 0 ); -} - -/* - * SSL handshake -- client side -- single step - */ -int ssl_handshake_client_step( ssl_context *ssl ) -{ - int ret = 0; - - if( ssl->state == SSL_HANDSHAKE_OVER ) - return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - - SSL_DEBUG_MSG( 2, ( "client state: %d", ssl->state ) ); - - if( ( ret = ssl_flush_output( ssl ) ) != 0 ) - return( ret ); - - switch( ssl->state ) - { - case SSL_HELLO_REQUEST: - ssl->state = SSL_CLIENT_HELLO; - break; - - /* - * ==> ClientHello - */ - case SSL_CLIENT_HELLO: - ret = ssl_write_client_hello( ssl ); - break; - - /* - * <== ServerHello - * Certificate - * ( ServerKeyExchange ) - * ( CertificateRequest ) - * ServerHelloDone - */ - case SSL_SERVER_HELLO: - ret = ssl_parse_server_hello( ssl ); - break; - - case SSL_SERVER_CERTIFICATE: - ret = ssl_parse_certificate( ssl ); - break; - - case SSL_SERVER_KEY_EXCHANGE: - ret = ssl_parse_server_key_exchange( ssl ); - break; - - case SSL_CERTIFICATE_REQUEST: - ret = ssl_parse_certificate_request( ssl ); - break; - - case SSL_SERVER_HELLO_DONE: - ret = ssl_parse_server_hello_done( ssl ); - break; - - /* - * ==> ( Certificate/Alert ) - * ClientKeyExchange - * ( CertificateVerify ) - * ChangeCipherSpec - * Finished - */ - case SSL_CLIENT_CERTIFICATE: - ret = ssl_write_certificate( ssl ); - break; - - case SSL_CLIENT_KEY_EXCHANGE: - ret = ssl_write_client_key_exchange( ssl ); - break; - - case SSL_CERTIFICATE_VERIFY: - ret = ssl_write_certificate_verify( ssl ); - break; - - case SSL_CLIENT_CHANGE_CIPHER_SPEC: - ret = ssl_write_change_cipher_spec( ssl ); - break; - - case SSL_CLIENT_FINISHED: - ret = ssl_write_finished( ssl ); - break; - - /* - * <== ChangeCipherSpec - * Finished - */ - case SSL_SERVER_CHANGE_CIPHER_SPEC: - ret = ssl_parse_change_cipher_spec( ssl ); - break; - - case SSL_SERVER_FINISHED: - ret = ssl_parse_finished( ssl ); - break; - - case SSL_FLUSH_BUFFERS: - SSL_DEBUG_MSG( 2, ( "handshake: done" ) ); - ssl->state = SSL_HANDSHAKE_WRAPUP; - break; - - case SSL_HANDSHAKE_WRAPUP: - ssl_handshake_wrapup( ssl ); - break; - - default: - SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) ); - return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - } - - return( ret ); -} -#endif diff --git a/makerom/polarssl/ssl_srv.c b/makerom/polarssl/ssl_srv.c deleted file mode 100644 index 9ba22949..00000000 --- a/makerom/polarssl/ssl_srv.c +++ /dev/null @@ -1,1623 +0,0 @@ -/* - * SSLv3/TLSv1 server-side functions - * - * Copyright (C) 2006-2012, 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_SSL_SRV_C) - -#include "polarssl/debug.h" -#include "polarssl/ssl.h" - -#include -#include -#include - -static int ssl_parse_servername_ext( ssl_context *ssl, - const unsigned char *buf, - size_t len ) -{ - int ret; - size_t servername_list_size, hostname_len; - const unsigned char *p; - - servername_list_size = ( ( buf[0] << 8 ) | ( buf[1] ) ); - if( servername_list_size + 2 != len ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - p = buf + 2; - while( servername_list_size > 0 ) - { - hostname_len = ( ( p[1] << 8 ) | p[2] ); - if( hostname_len + 3 > servername_list_size ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - if( p[0] == TLS_EXT_SERVERNAME_HOSTNAME ) - { - ret = ssl->f_sni( ssl->p_sni, ssl, p + 3, hostname_len ); - if( ret != 0 ) - { - ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, - SSL_ALERT_MSG_UNRECOGNIZED_NAME ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - return( 0 ); - } - - servername_list_size -= hostname_len + 3; - p += hostname_len + 3; - } - - if( servername_list_size != 0 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - return( 0 ); -} - -static int ssl_parse_renegotiation_info( ssl_context *ssl, - const unsigned char *buf, - size_t len ) -{ - int ret; - - if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE ) - { - if( len != 1 || buf[0] != 0x0 ) - { - SSL_DEBUG_MSG( 1, ( "non-zero length renegotiated connection field" ) ); - - if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) - return( ret ); - - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - ssl->secure_renegotiation = SSL_SECURE_RENEGOTIATION; - } - else - { - if( len != 1 + ssl->verify_data_len || - buf[0] != ssl->verify_data_len || - memcmp( buf + 1, ssl->peer_verify_data, ssl->verify_data_len ) != 0 ) - { - SSL_DEBUG_MSG( 1, ( "non-matching renegotiated connection field" ) ); - - if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) - return( ret ); - - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - } - - return( 0 ); -} - -static int ssl_parse_signature_algorithms_ext( ssl_context *ssl, - const unsigned char *buf, - size_t len ) -{ - size_t sig_alg_list_size; - const unsigned char *p; - - sig_alg_list_size = ( ( buf[0] << 8 ) | ( buf[1] ) ); - if( sig_alg_list_size + 2 != len || - sig_alg_list_size %2 != 0 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - p = buf + 2; - while( sig_alg_list_size > 0 ) - { - if( p[1] != SSL_SIG_RSA ) - { - sig_alg_list_size -= 2; - p += 2; - continue; - } -#if defined(POLARSSL_SHA4_C) - if( p[0] == SSL_HASH_SHA512 ) - { - ssl->handshake->sig_alg = SSL_HASH_SHA512; - break; - } - if( p[0] == SSL_HASH_SHA384 ) - { - ssl->handshake->sig_alg = SSL_HASH_SHA384; - break; - } -#endif -#if defined(POLARSSL_SHA2_C) - if( p[0] == SSL_HASH_SHA256 ) - { - ssl->handshake->sig_alg = SSL_HASH_SHA256; - break; - } - if( p[0] == SSL_HASH_SHA224 ) - { - ssl->handshake->sig_alg = SSL_HASH_SHA224; - break; - } -#endif - if( p[0] == SSL_HASH_SHA1 ) - { - ssl->handshake->sig_alg = SSL_HASH_SHA1; - break; - } - if( p[0] == SSL_HASH_MD5 ) - { - ssl->handshake->sig_alg = SSL_HASH_MD5; - break; - } - - sig_alg_list_size -= 2; - p += 2; - } - - SSL_DEBUG_MSG( 3, ( "client hello v3, signature_algorithm ext: %d", - ssl->handshake->sig_alg ) ); - - return( 0 ); -} - -#if defined(POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO) -static int ssl_parse_client_hello_v2( ssl_context *ssl ) -{ - int ret; - unsigned int i, j; - size_t n; - unsigned int ciph_len, sess_len, chal_len; - unsigned char *buf, *p; - - SSL_DEBUG_MSG( 2, ( "=> parse client hello v2" ) ); - - if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "client hello v2 illegal for renegotiation" ) ); - - if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) - return( ret ); - - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - buf = ssl->in_hdr; - - SSL_DEBUG_BUF( 4, "record header", buf, 5 ); - - SSL_DEBUG_MSG( 3, ( "client hello v2, message type: %d", - buf[2] ) ); - SSL_DEBUG_MSG( 3, ( "client hello v2, message len.: %d", - ( ( buf[0] & 0x7F ) << 8 ) | buf[1] ) ); - SSL_DEBUG_MSG( 3, ( "client hello v2, max. version: [%d:%d]", - buf[3], buf[4] ) ); - - /* - * SSLv2 Client Hello - * - * Record layer: - * 0 . 1 message length - * - * SSL layer: - * 2 . 2 message type - * 3 . 4 protocol version - */ - if( buf[2] != SSL_HS_CLIENT_HELLO || - buf[3] != SSL_MAJOR_VERSION_3 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - n = ( ( buf[0] << 8 ) | buf[1] ) & 0x7FFF; - - if( n < 17 || n > 512 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - ssl->major_ver = SSL_MAJOR_VERSION_3; - ssl->minor_ver = ( buf[4] <= SSL_MINOR_VERSION_3 ) - ? buf[4] : SSL_MINOR_VERSION_3; - - if( ssl->minor_ver < ssl->min_minor_ver ) - { - SSL_DEBUG_MSG( 1, ( "client only supports ssl smaller than minimum" - " [%d:%d] < [%d:%d]", ssl->major_ver, ssl->minor_ver, - ssl->min_major_ver, ssl->min_minor_ver ) ); - - ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, - SSL_ALERT_MSG_PROTOCOL_VERSION ); - return( POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION ); - } - - ssl->max_major_ver = buf[3]; - ssl->max_minor_ver = buf[4]; - - if( ( ret = ssl_fetch_input( ssl, 2 + n ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_fetch_input", ret ); - return( ret ); - } - - ssl->handshake->update_checksum( ssl, buf + 2, n ); - - buf = ssl->in_msg; - n = ssl->in_left - 5; - - /* - * 0 . 1 ciphersuitelist length - * 2 . 3 session id length - * 4 . 5 challenge length - * 6 . .. ciphersuitelist - * .. . .. session id - * .. . .. challenge - */ - SSL_DEBUG_BUF( 4, "record contents", buf, n ); - - ciph_len = ( buf[0] << 8 ) | buf[1]; - sess_len = ( buf[2] << 8 ) | buf[3]; - chal_len = ( buf[4] << 8 ) | buf[5]; - - SSL_DEBUG_MSG( 3, ( "ciph_len: %d, sess_len: %d, chal_len: %d", - ciph_len, sess_len, chal_len ) ); - - /* - * Make sure each parameter length is valid - */ - if( ciph_len < 3 || ( ciph_len % 3 ) != 0 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - if( sess_len > 32 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - if( chal_len < 8 || chal_len > 32 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - if( n != 6 + ciph_len + sess_len + chal_len ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - SSL_DEBUG_BUF( 3, "client hello, ciphersuitelist", - buf + 6, ciph_len ); - SSL_DEBUG_BUF( 3, "client hello, session id", - buf + 6 + ciph_len, sess_len ); - SSL_DEBUG_BUF( 3, "client hello, challenge", - buf + 6 + ciph_len + sess_len, chal_len ); - - p = buf + 6 + ciph_len; - ssl->session_negotiate->length = sess_len; - memset( ssl->session_negotiate->id, 0, sizeof( ssl->session_negotiate->id ) ); - memcpy( ssl->session_negotiate->id, p, ssl->session_negotiate->length ); - - p += sess_len; - memset( ssl->handshake->randbytes, 0, 64 ); - memcpy( ssl->handshake->randbytes + 32 - chal_len, p, chal_len ); - - /* - * Check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV - */ - for( i = 0, p = buf + 6; i < ciph_len; i += 3, p += 3 ) - { - if( p[0] == 0 && p[1] == 0 && p[2] == SSL_EMPTY_RENEGOTIATION_INFO ) - { - SSL_DEBUG_MSG( 3, ( "received TLS_EMPTY_RENEGOTIATION_INFO " ) ); - if( ssl->renegotiation == SSL_RENEGOTIATION ) - { - SSL_DEBUG_MSG( 1, ( "received RENEGOTIATION SCSV during renegotiation" ) ); - - if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) - return( ret ); - - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - ssl->secure_renegotiation = SSL_SECURE_RENEGOTIATION; - break; - } - } - - for( i = 0; ssl->ciphersuites[ssl->minor_ver][i] != 0; i++ ) - { - for( j = 0, p = buf + 6; j < ciph_len; j += 3, p += 3 ) - { - if( p[0] == 0 && - p[1] == 0 && - p[2] == ssl->ciphersuites[ssl->minor_ver][i] ) - goto have_ciphersuite_v2; - } - } - - SSL_DEBUG_MSG( 1, ( "got no ciphersuites in common" ) ); - - return( POLARSSL_ERR_SSL_NO_CIPHER_CHOSEN ); - -have_ciphersuite_v2: - ssl->session_negotiate->ciphersuite = ssl->ciphersuites[ssl->minor_ver][i]; - ssl_optimize_checksum( ssl, ssl->session_negotiate->ciphersuite ); - - /* - * SSLv2 Client Hello relevant renegotiation security checks - */ - if( ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && - ssl->allow_legacy_renegotiation == SSL_LEGACY_BREAK_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "legacy renegotiation, breaking off handshake" ) ); - - if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) - return( ret ); - - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - ssl->in_left = 0; - ssl->state++; - - SSL_DEBUG_MSG( 2, ( "<= parse client hello v2" ) ); - - return( 0 ); -} -#endif /* POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO */ - -static int ssl_parse_client_hello( ssl_context *ssl ) -{ - int ret; - unsigned int i, j; - size_t n; - unsigned int ciph_len, sess_len; - unsigned int comp_len; - unsigned int ext_len = 0; - unsigned char *buf, *p, *ext; - int renegotiation_info_seen = 0; - int handshake_failure = 0; - - SSL_DEBUG_MSG( 2, ( "=> parse client hello" ) ); - - if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE && - ( ret = ssl_fetch_input( ssl, 5 ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_fetch_input", ret ); - return( ret ); - } - - buf = ssl->in_hdr; - -#if defined(POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO) - if( ( buf[0] & 0x80 ) != 0 ) - return ssl_parse_client_hello_v2( ssl ); -#endif - - SSL_DEBUG_BUF( 4, "record header", buf, 5 ); - - SSL_DEBUG_MSG( 3, ( "client hello v3, message type: %d", - buf[0] ) ); - SSL_DEBUG_MSG( 3, ( "client hello v3, message len.: %d", - ( buf[3] << 8 ) | buf[4] ) ); - SSL_DEBUG_MSG( 3, ( "client hello v3, protocol ver: [%d:%d]", - buf[1], buf[2] ) ); - - /* - * SSLv3 Client Hello - * - * Record layer: - * 0 . 0 message type - * 1 . 2 protocol version - * 3 . 4 message length - */ - if( buf[0] != SSL_MSG_HANDSHAKE || - buf[1] != SSL_MAJOR_VERSION_3 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - n = ( buf[3] << 8 ) | buf[4]; - - if( n < 45 || n > 512 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE && - ( ret = ssl_fetch_input( ssl, 5 + n ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_fetch_input", ret ); - return( ret ); - } - - buf = ssl->in_msg; - if( !ssl->renegotiation ) - n = ssl->in_left - 5; - else - n = ssl->in_msglen; - - ssl->handshake->update_checksum( ssl, buf, n ); - - /* - * SSL layer: - * 0 . 0 handshake type - * 1 . 3 handshake length - * 4 . 5 protocol version - * 6 . 9 UNIX time() - * 10 . 37 random bytes - * 38 . 38 session id length - * 39 . 38+x session id - * 39+x . 40+x ciphersuitelist length - * 41+x . .. ciphersuitelist - * .. . .. compression alg. - * .. . .. extensions - */ - SSL_DEBUG_BUF( 4, "record contents", buf, n ); - - SSL_DEBUG_MSG( 3, ( "client hello v3, handshake type: %d", - buf[0] ) ); - SSL_DEBUG_MSG( 3, ( "client hello v3, handshake len.: %d", - ( buf[1] << 16 ) | ( buf[2] << 8 ) | buf[3] ) ); - SSL_DEBUG_MSG( 3, ( "client hello v3, max. version: [%d:%d]", - buf[4], buf[5] ) ); - - /* - * Check the handshake type and protocol version - */ - if( buf[0] != SSL_HS_CLIENT_HELLO || - buf[4] != SSL_MAJOR_VERSION_3 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - ssl->major_ver = SSL_MAJOR_VERSION_3; - ssl->minor_ver = ( buf[5] <= SSL_MINOR_VERSION_3 ) - ? buf[5] : SSL_MINOR_VERSION_3; - - if( ssl->minor_ver < ssl->min_minor_ver ) - { - SSL_DEBUG_MSG( 1, ( "client only supports ssl smaller than minimum" - " [%d:%d] < [%d:%d]", ssl->major_ver, ssl->minor_ver, - ssl->min_major_ver, ssl->min_minor_ver ) ); - - ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, - SSL_ALERT_MSG_PROTOCOL_VERSION ); - - return( POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION ); - } - - ssl->max_major_ver = buf[4]; - ssl->max_minor_ver = buf[5]; - - memcpy( ssl->handshake->randbytes, buf + 6, 32 ); - - /* - * Check the handshake message length - */ - if( buf[1] != 0 || n != (unsigned int) 4 + ( ( buf[2] << 8 ) | buf[3] ) ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - /* - * Check the session length - */ - sess_len = buf[38]; - - if( sess_len > 32 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - ssl->session_negotiate->length = sess_len; - memset( ssl->session_negotiate->id, 0, - sizeof( ssl->session_negotiate->id ) ); - memcpy( ssl->session_negotiate->id, buf + 39, - ssl->session_negotiate->length ); - - /* - * Check the ciphersuitelist length - */ - ciph_len = ( buf[39 + sess_len] << 8 ) - | ( buf[40 + sess_len] ); - - if( ciph_len < 2 || ciph_len > 256 || ( ciph_len % 2 ) != 0 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - /* - * Check the compression algorithms length - */ - comp_len = buf[41 + sess_len + ciph_len]; - - if( comp_len < 1 || comp_len > 16 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - /* - * Check the extension length - */ - if( n > 42 + sess_len + ciph_len + comp_len ) - { - ext_len = ( buf[42 + sess_len + ciph_len + comp_len] << 8 ) - | ( buf[43 + sess_len + ciph_len + comp_len] ); - - if( ( ext_len > 0 && ext_len < 4 ) || - n != 44 + sess_len + ciph_len + comp_len + ext_len ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - SSL_DEBUG_BUF( 3, "Ext", buf + 44 + sess_len + ciph_len + comp_len, ext_len); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - } - - ssl->session_negotiate->compression = SSL_COMPRESS_NULL; -#if defined(POLARSSL_ZLIB_SUPPORT) - for( i = 0; i < comp_len; ++i ) - { - if( buf[42 + sess_len + ciph_len + i] == SSL_COMPRESS_DEFLATE ) - { - ssl->session_negotiate->compression = SSL_COMPRESS_DEFLATE; - break; - } - } -#endif - - SSL_DEBUG_BUF( 3, "client hello, random bytes", - buf + 6, 32 ); - SSL_DEBUG_BUF( 3, "client hello, session id", - buf + 38, sess_len ); - SSL_DEBUG_BUF( 3, "client hello, ciphersuitelist", - buf + 41 + sess_len, ciph_len ); - SSL_DEBUG_BUF( 3, "client hello, compression", - buf + 42 + sess_len + ciph_len, comp_len ); - - /* - * Check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV - */ - for( i = 0, p = buf + 41 + sess_len; i < ciph_len; i += 2, p += 2 ) - { - if( p[0] == 0 && p[1] == SSL_EMPTY_RENEGOTIATION_INFO ) - { - SSL_DEBUG_MSG( 3, ( "received TLS_EMPTY_RENEGOTIATION_INFO " ) ); - if( ssl->renegotiation == SSL_RENEGOTIATION ) - { - SSL_DEBUG_MSG( 1, ( "received RENEGOTIATION SCSV during renegotiation" ) ); - - if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) - return( ret ); - - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - ssl->secure_renegotiation = SSL_SECURE_RENEGOTIATION; - break; - } - } - - /* - * Search for a matching ciphersuite - */ - for( i = 0; ssl->ciphersuites[ssl->minor_ver][i] != 0; i++ ) - { - for( j = 0, p = buf + 41 + sess_len; j < ciph_len; - j += 2, p += 2 ) - { - if( p[0] == 0 && p[1] == ssl->ciphersuites[ssl->minor_ver][i] ) - goto have_ciphersuite; - } - } - - SSL_DEBUG_MSG( 1, ( "got no ciphersuites in common" ) ); - - return( POLARSSL_ERR_SSL_NO_CIPHER_CHOSEN ); - -have_ciphersuite: - ssl->session_negotiate->ciphersuite = ssl->ciphersuites[ssl->minor_ver][i]; - ssl_optimize_checksum( ssl, ssl->session_negotiate->ciphersuite ); - - ext = buf + 44 + sess_len + ciph_len + comp_len; - - while( ext_len ) - { - unsigned int ext_id = ( ( ext[0] << 8 ) - | ( ext[1] ) ); - unsigned int ext_size = ( ( ext[2] << 8 ) - | ( ext[3] ) ); - - if( ext_size + 4 > ext_len ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - switch( ext_id ) - { - case TLS_EXT_SERVERNAME: - SSL_DEBUG_MSG( 3, ( "found ServerName extension" ) ); - if( ssl->f_sni == NULL ) - break; - - ret = ssl_parse_servername_ext( ssl, ext + 4, ext_size ); - if( ret != 0 ) - return( ret ); - break; - - case TLS_EXT_RENEGOTIATION_INFO: - SSL_DEBUG_MSG( 3, ( "found renegotiation extension" ) ); - renegotiation_info_seen = 1; - - ret = ssl_parse_renegotiation_info( ssl, ext + 4, ext_size ); - if( ret != 0 ) - return( ret ); - break; - - case TLS_EXT_SIG_ALG: - SSL_DEBUG_MSG( 3, ( "found signature_algorithms extension" ) ); - if( ssl->renegotiation == SSL_RENEGOTIATION ) - break; - - ret = ssl_parse_signature_algorithms_ext( ssl, ext + 4, ext_size ); - if( ret != 0 ) - return( ret ); - break; - - default: - SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)", - ext_id ) ); - } - - ext_len -= 4 + ext_size; - ext += 4 + ext_size; - - if( ext_len > 0 && ext_len < 4 ) - { - SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - } - - /* - * Renegotiation security checks - */ - if( ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && - ssl->allow_legacy_renegotiation == SSL_LEGACY_BREAK_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "legacy renegotiation, breaking off handshake" ) ); - handshake_failure = 1; - } - else if( ssl->renegotiation == SSL_RENEGOTIATION && - ssl->secure_renegotiation == SSL_SECURE_RENEGOTIATION && - renegotiation_info_seen == 0 ) - { - SSL_DEBUG_MSG( 1, ( "renegotiation_info extension missing (secure)" ) ); - handshake_failure = 1; - } - else if( ssl->renegotiation == SSL_RENEGOTIATION && - ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && - ssl->allow_legacy_renegotiation == SSL_LEGACY_NO_RENEGOTIATION ) - { - SSL_DEBUG_MSG( 1, ( "legacy renegotiation not allowed" ) ); - handshake_failure = 1; - } - else if( ssl->renegotiation == SSL_RENEGOTIATION && - ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && - renegotiation_info_seen == 1 ) - { - SSL_DEBUG_MSG( 1, ( "renegotiation_info extension present (legacy)" ) ); - handshake_failure = 1; - } - - if( handshake_failure == 1 ) - { - if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) - return( ret ); - - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); - } - - ssl->in_left = 0; - ssl->state++; - - SSL_DEBUG_MSG( 2, ( "<= parse client hello" ) ); - - return( 0 ); -} - -static int ssl_write_server_hello( ssl_context *ssl ) -{ - time_t t; - int ret, n; - size_t ext_len = 0; - unsigned char *buf, *p; - - SSL_DEBUG_MSG( 2, ( "=> write server hello" ) ); - - /* - * 0 . 0 handshake type - * 1 . 3 handshake length - * 4 . 5 protocol version - * 6 . 9 UNIX time() - * 10 . 37 random bytes - */ - buf = ssl->out_msg; - p = buf + 4; - - *p++ = (unsigned char) ssl->major_ver; - *p++ = (unsigned char) ssl->minor_ver; - - SSL_DEBUG_MSG( 3, ( "server hello, chosen version: [%d:%d]", - buf[4], buf[5] ) ); - - t = time( NULL ); - *p++ = (unsigned char)( t >> 24 ); - *p++ = (unsigned char)( t >> 16 ); - *p++ = (unsigned char)( t >> 8 ); - *p++ = (unsigned char)( t ); - - SSL_DEBUG_MSG( 3, ( "server hello, current time: %lu", t ) ); - - if( ( ret = ssl->f_rng( ssl->p_rng, p, 28 ) ) != 0 ) - return( ret ); - - p += 28; - - memcpy( ssl->handshake->randbytes + 32, buf + 6, 32 ); - - SSL_DEBUG_BUF( 3, "server hello, random bytes", buf + 6, 32 ); - - /* - * 38 . 38 session id length - * 39 . 38+n session id - * 39+n . 40+n chosen ciphersuite - * 41+n . 41+n chosen compression alg. - */ - ssl->session_negotiate->length = n = 32; - *p++ = (unsigned char) ssl->session_negotiate->length; - - if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE || - ssl->f_get_cache == NULL || - ssl->f_get_cache( ssl->p_get_cache, ssl->session_negotiate ) != 0 ) - { - /* - * Not found, create a new session id - */ - ssl->handshake->resume = 0; - ssl->state++; - - if( ( ret = ssl->f_rng( ssl->p_rng, ssl->session_negotiate->id, - n ) ) != 0 ) - return( ret ); - } - else - { - /* - * Found a matching session, resuming it - */ - ssl->handshake->resume = 1; - ssl->state = SSL_SERVER_CHANGE_CIPHER_SPEC; - - if( ( ret = ssl_derive_keys( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_derive_keys", ret ); - return( ret ); - } - } - - memcpy( p, ssl->session_negotiate->id, ssl->session_negotiate->length ); - p += ssl->session_negotiate->length; - - SSL_DEBUG_MSG( 3, ( "server hello, session id len.: %d", n ) ); - SSL_DEBUG_BUF( 3, "server hello, session id", buf + 39, n ); - SSL_DEBUG_MSG( 3, ( "%s session has been resumed", - ssl->handshake->resume ? "a" : "no" ) ); - - *p++ = (unsigned char)( ssl->session_negotiate->ciphersuite >> 8 ); - *p++ = (unsigned char)( ssl->session_negotiate->ciphersuite ); - *p++ = (unsigned char)( ssl->session_negotiate->compression ); - - SSL_DEBUG_MSG( 3, ( "server hello, chosen ciphersuite: %d", - ssl->session_negotiate->ciphersuite ) ); - SSL_DEBUG_MSG( 3, ( "server hello, compress alg.: %d", - ssl->session_negotiate->compression ) ); - - if( ssl->secure_renegotiation == SSL_SECURE_RENEGOTIATION ) - { - SSL_DEBUG_MSG( 3, ( "server hello, prepping for secure renegotiation extension" ) ); - ext_len += 5 + ssl->verify_data_len * 2; - - SSL_DEBUG_MSG( 3, ( "server hello, total extension length: %d", - ext_len ) ); - - *p++ = (unsigned char)( ( ext_len >> 8 ) & 0xFF ); - *p++ = (unsigned char)( ( ext_len ) & 0xFF ); - - /* - * Secure renegotiation - */ - SSL_DEBUG_MSG( 3, ( "client hello, secure renegotiation extension" ) ); - - *p++ = (unsigned char)( ( TLS_EXT_RENEGOTIATION_INFO >> 8 ) & 0xFF ); - *p++ = (unsigned char)( ( TLS_EXT_RENEGOTIATION_INFO ) & 0xFF ); - - *p++ = 0x00; - *p++ = ( ssl->verify_data_len * 2 + 1 ) & 0xFF; - *p++ = ssl->verify_data_len * 2 & 0xFF; - - memcpy( p, ssl->peer_verify_data, ssl->verify_data_len ); - p += ssl->verify_data_len; - memcpy( p, ssl->own_verify_data, ssl->verify_data_len ); - p += ssl->verify_data_len; - } - - ssl->out_msglen = p - buf; - ssl->out_msgtype = SSL_MSG_HANDSHAKE; - ssl->out_msg[0] = SSL_HS_SERVER_HELLO; - - ret = ssl_write_record( ssl ); - - SSL_DEBUG_MSG( 2, ( "<= write server hello" ) ); - - return( ret ); -} - -static int ssl_write_certificate_request( ssl_context *ssl ) -{ - int ret; - size_t n = 0, dn_size, total_dn_size; - unsigned char *buf, *p; - const x509_cert *crt; - - SSL_DEBUG_MSG( 2, ( "=> write certificate request" ) ); - - ssl->state++; - - if( ssl->authmode == SSL_VERIFY_NONE ) - { - SSL_DEBUG_MSG( 2, ( "<= skip write certificate request" ) ); - return( 0 ); - } - - /* - * 0 . 0 handshake type - * 1 . 3 handshake length - * 4 . 4 cert type count - * 5 .. m-1 cert types - * m .. m+1 sig alg length (TLS 1.2 only) - * m+1 .. n-1 SignatureAndHashAlgorithms (TLS 1.2 only) - * n .. n+1 length of all DNs - * n+2 .. n+3 length of DN 1 - * n+4 .. ... Distinguished Name #1 - * ... .. ... length of DN 2, etc. - */ - buf = ssl->out_msg; - p = buf + 4; - - /* - * At the moment, only RSA certificates are supported - */ - *p++ = 1; - *p++ = SSL_CERT_TYPE_RSA_SIGN; - - /* - * Add signature_algorithms for verify (TLS 1.2) - * Only add current running algorithm that is already required for - * requested ciphersuite. - * - * Length is always 2 - */ - if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) - { - ssl->handshake->verify_sig_alg = SSL_HASH_SHA256; - - *p++ = 0; - *p++ = 2; - - if( ssl->session_negotiate->ciphersuite == TLS_RSA_WITH_AES_256_GCM_SHA384 || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ) - { - ssl->handshake->verify_sig_alg = SSL_HASH_SHA384; - } - - *p++ = ssl->handshake->verify_sig_alg; - *p++ = SSL_SIG_RSA; - - n += 4; - } - - p += 2; - crt = ssl->ca_chain; - - total_dn_size = 0; - while( crt != NULL && crt->version != 0) - { - if( p - buf > 4096 ) - break; - - dn_size = crt->subject_raw.len; - *p++ = (unsigned char)( dn_size >> 8 ); - *p++ = (unsigned char)( dn_size ); - memcpy( p, crt->subject_raw.p, dn_size ); - p += dn_size; - - SSL_DEBUG_BUF( 3, "requested DN", p, dn_size ); - - total_dn_size += 2 + dn_size; - crt = crt->next; - } - - ssl->out_msglen = p - buf; - ssl->out_msgtype = SSL_MSG_HANDSHAKE; - ssl->out_msg[0] = SSL_HS_CERTIFICATE_REQUEST; - ssl->out_msg[6 + n] = (unsigned char)( total_dn_size >> 8 ); - ssl->out_msg[7 + n] = (unsigned char)( total_dn_size ); - - ret = ssl_write_record( ssl ); - - SSL_DEBUG_MSG( 2, ( "<= write certificate request" ) ); - - return( ret ); -} - -static int ssl_write_server_key_exchange( ssl_context *ssl ) -{ -#if defined(POLARSSL_DHM_C) - int ret; - size_t n, rsa_key_len = 0; - unsigned char hash[64]; - int hash_id = 0; - unsigned int hashlen = 0; -#endif - - SSL_DEBUG_MSG( 2, ( "=> write server key exchange" ) ); - - if( ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_DES_CBC_SHA && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 && - ssl->session_negotiate->ciphersuite != TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ) - { - SSL_DEBUG_MSG( 2, ( "<= skip write server key exchange" ) ); - ssl->state++; - return( 0 ); - } - -#if !defined(POLARSSL_DHM_C) - SSL_DEBUG_MSG( 1, ( "support for dhm is not available" ) ); - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); -#else - - if( ssl->rsa_key == NULL ) - { - SSL_DEBUG_MSG( 1, ( "got no private key" ) ); - return( POLARSSL_ERR_SSL_PRIVATE_KEY_REQUIRED ); - } - - /* - * Ephemeral DH parameters: - * - * struct { - * opaque dh_p<1..2^16-1>; - * opaque dh_g<1..2^16-1>; - * opaque dh_Ys<1..2^16-1>; - * } ServerDHParams; - */ - if( ( ret = mpi_copy( &ssl->handshake->dhm_ctx.P, &ssl->dhm_P ) ) != 0 || - ( ret = mpi_copy( &ssl->handshake->dhm_ctx.G, &ssl->dhm_G ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "mpi_copy", ret ); - return( ret ); - } - - if( ( ret = dhm_make_params( &ssl->handshake->dhm_ctx, - mpi_size( &ssl->handshake->dhm_ctx.P ), - ssl->out_msg + 4, - &n, ssl->f_rng, ssl->p_rng ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "dhm_make_params", ret ); - return( ret ); - } - - SSL_DEBUG_MPI( 3, "DHM: X ", &ssl->handshake->dhm_ctx.X ); - SSL_DEBUG_MPI( 3, "DHM: P ", &ssl->handshake->dhm_ctx.P ); - SSL_DEBUG_MPI( 3, "DHM: G ", &ssl->handshake->dhm_ctx.G ); - SSL_DEBUG_MPI( 3, "DHM: GX", &ssl->handshake->dhm_ctx.GX ); - - if( ssl->minor_ver != SSL_MINOR_VERSION_3 ) - { - md5_context md5; - sha1_context sha1; - - /* - * digitally-signed struct { - * opaque md5_hash[16]; - * opaque sha_hash[20]; - * }; - * - * md5_hash - * MD5(ClientHello.random + ServerHello.random - * + ServerParams); - * sha_hash - * SHA(ClientHello.random + ServerHello.random - * + ServerParams); - */ - md5_starts( &md5 ); - md5_update( &md5, ssl->handshake->randbytes, 64 ); - md5_update( &md5, ssl->out_msg + 4, n ); - md5_finish( &md5, hash ); - - sha1_starts( &sha1 ); - sha1_update( &sha1, ssl->handshake->randbytes, 64 ); - sha1_update( &sha1, ssl->out_msg + 4, n ); - sha1_finish( &sha1, hash + 16 ); - - hashlen = 36; - hash_id = SIG_RSA_RAW; - } - else - { - /* - * digitally-signed struct { - * opaque client_random[32]; - * opaque server_random[32]; - * ServerDHParams params; - * }; - */ -#if defined(POLARSSL_SHA4_C) - if( ssl->handshake->sig_alg == SSL_HASH_SHA512 ) - { - sha4_context sha4; - - sha4_starts( &sha4, 0 ); - sha4_update( &sha4, ssl->handshake->randbytes, 64 ); - sha4_update( &sha4, ssl->out_msg + 4, n ); - sha4_finish( &sha4, hash ); - - hashlen = 64; - hash_id = SIG_RSA_SHA512; - } - else if( ssl->handshake->sig_alg == SSL_HASH_SHA384 ) - { - sha4_context sha4; - - sha4_starts( &sha4, 1 ); - sha4_update( &sha4, ssl->handshake->randbytes, 64 ); - sha4_update( &sha4, ssl->out_msg + 4, n ); - sha4_finish( &sha4, hash ); - - hashlen = 48; - hash_id = SIG_RSA_SHA384; - } - else -#endif -#if defined(POLARSSL_SHA2_C) - if( ssl->handshake->sig_alg == SSL_HASH_SHA256 ) - { - sha2_context sha2; - - sha2_starts( &sha2, 0 ); - sha2_update( &sha2, ssl->handshake->randbytes, 64 ); - sha2_update( &sha2, ssl->out_msg + 4, n ); - sha2_finish( &sha2, hash ); - - hashlen = 32; - hash_id = SIG_RSA_SHA256; - } - else if( ssl->handshake->sig_alg == SSL_HASH_SHA224 ) - { - sha2_context sha2; - - sha2_starts( &sha2, 1 ); - sha2_update( &sha2, ssl->handshake->randbytes, 64 ); - sha2_update( &sha2, ssl->out_msg + 4, n ); - sha2_finish( &sha2, hash ); - - hashlen = 24; - hash_id = SIG_RSA_SHA224; - } - else -#endif - if( ssl->handshake->sig_alg == SSL_HASH_SHA1 ) - { - sha1_context sha1; - - sha1_starts( &sha1 ); - sha1_update( &sha1, ssl->handshake->randbytes, 64 ); - sha1_update( &sha1, ssl->out_msg + 4, n ); - sha1_finish( &sha1, hash ); - - hashlen = 20; - hash_id = SIG_RSA_SHA1; - } - else if( ssl->handshake->sig_alg == SSL_HASH_MD5 ) - { - md5_context md5; - - md5_starts( &md5 ); - md5_update( &md5, ssl->handshake->randbytes, 64 ); - md5_update( &md5, ssl->out_msg + 4, n ); - md5_finish( &md5, hash ); - - hashlen = 16; - hash_id = SIG_RSA_MD5; - } - } - - SSL_DEBUG_BUF( 3, "parameters hash", hash, hashlen ); - - if ( ssl->rsa_key ) - rsa_key_len = ssl->rsa_key_len( ssl->rsa_key ); - - if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) - { - ssl->out_msg[4 + n] = ssl->handshake->sig_alg; - ssl->out_msg[5 + n] = SSL_SIG_RSA; - - n += 2; - } - - ssl->out_msg[4 + n] = (unsigned char)( rsa_key_len >> 8 ); - ssl->out_msg[5 + n] = (unsigned char)( rsa_key_len ); - - if ( ssl->rsa_key ) - { - ret = ssl->rsa_sign( ssl->rsa_key, ssl->f_rng, ssl->p_rng, - RSA_PRIVATE, - hash_id, hashlen, hash, - ssl->out_msg + 6 + n ); - } - - if( ret != 0 ) - { - SSL_DEBUG_RET( 1, "pkcs1_sign", ret ); - return( ret ); - } - - SSL_DEBUG_BUF( 3, "my RSA sig", ssl->out_msg + 6 + n, rsa_key_len ); - - ssl->out_msglen = 6 + n + rsa_key_len; - ssl->out_msgtype = SSL_MSG_HANDSHAKE; - ssl->out_msg[0] = SSL_HS_SERVER_KEY_EXCHANGE; - - ssl->state++; - - if( ( ret = ssl_write_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_write_record", ret ); - return( ret ); - } - - SSL_DEBUG_MSG( 2, ( "<= write server key exchange" ) ); - - return( 0 ); -#endif -} - -static int ssl_write_server_hello_done( ssl_context *ssl ) -{ - int ret; - - SSL_DEBUG_MSG( 2, ( "=> write server hello done" ) ); - - ssl->out_msglen = 4; - ssl->out_msgtype = SSL_MSG_HANDSHAKE; - ssl->out_msg[0] = SSL_HS_SERVER_HELLO_DONE; - - ssl->state++; - - if( ( ret = ssl_write_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_write_record", ret ); - return( ret ); - } - - SSL_DEBUG_MSG( 2, ( "<= write server hello done" ) ); - - return( 0 ); -} - -static int ssl_parse_client_key_exchange( ssl_context *ssl ) -{ - int ret; - size_t i, n = 0; - - SSL_DEBUG_MSG( 2, ( "=> parse client key exchange" ) ); - - if( ( ret = ssl_read_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_read_record", ret ); - return( ret ); - } - - if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); - } - - if( ssl->in_msg[0] != SSL_HS_CLIENT_KEY_EXCHANGE ) - { - SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); - } - - if( ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_DES_CBC_SHA || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_128_CBC_SHA || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_256_CBC_SHA || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 || - ssl->session_negotiate->ciphersuite == TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ) - { -#if !defined(POLARSSL_DHM_C) - SSL_DEBUG_MSG( 1, ( "support for dhm is not available" ) ); - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); -#else - /* - * Receive G^Y mod P, premaster = (G^Y)^X mod P - */ - n = ( ssl->in_msg[4] << 8 ) | ssl->in_msg[5]; - - if( n < 1 || n > ssl->handshake->dhm_ctx.len || - n + 6 != ssl->in_hslen ) - { - SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); - } - - if( ( ret = dhm_read_public( &ssl->handshake->dhm_ctx, - ssl->in_msg + 6, n ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "dhm_read_public", ret ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_DHM_RP ); - } - - SSL_DEBUG_MPI( 3, "DHM: GY", &ssl->handshake->dhm_ctx.GY ); - - ssl->handshake->pmslen = ssl->handshake->dhm_ctx.len; - - if( ( ret = dhm_calc_secret( &ssl->handshake->dhm_ctx, - ssl->handshake->premaster, - &ssl->handshake->pmslen ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "dhm_calc_secret", ret ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_DHM_CS ); - } - - SSL_DEBUG_MPI( 3, "DHM: K ", &ssl->handshake->dhm_ctx.K ); -#endif - } - else - { - if( ssl->rsa_key == NULL ) - { - SSL_DEBUG_MSG( 1, ( "got no private key" ) ); - return( POLARSSL_ERR_SSL_PRIVATE_KEY_REQUIRED ); - } - - /* - * Decrypt the premaster using own private RSA key - */ - i = 4; - if( ssl->rsa_key ) - n = ssl->rsa_key_len( ssl->rsa_key ); - ssl->handshake->pmslen = 48; - - if( ssl->minor_ver != SSL_MINOR_VERSION_0 ) - { - i += 2; - if( ssl->in_msg[4] != ( ( n >> 8 ) & 0xFF ) || - ssl->in_msg[5] != ( ( n ) & 0xFF ) ) - { - SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); - } - } - - if( ssl->in_hslen != i + n ) - { - SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); - } - - if( ssl->rsa_key ) { - ret = ssl->rsa_decrypt( ssl->rsa_key, RSA_PRIVATE, - &ssl->handshake->pmslen, - ssl->in_msg + i, - ssl->handshake->premaster, - sizeof(ssl->handshake->premaster) ); - } - - if( ret != 0 || ssl->handshake->pmslen != 48 || - ssl->handshake->premaster[0] != ssl->max_major_ver || - ssl->handshake->premaster[1] != ssl->max_minor_ver ) - { - SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); - - /* - * Protection against Bleichenbacher's attack: - * invalid PKCS#1 v1.5 padding must not cause - * the connection to end immediately; instead, - * send a bad_record_mac later in the handshake. - */ - ssl->handshake->pmslen = 48; - - ret = ssl->f_rng( ssl->p_rng, ssl->handshake->premaster, - ssl->handshake->pmslen ); - if( ret != 0 ) - return( ret ); - } - } - - if( ( ret = ssl_derive_keys( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_derive_keys", ret ); - return( ret ); - } - - ssl->state++; - - SSL_DEBUG_MSG( 2, ( "<= parse client key exchange" ) ); - - return( 0 ); -} - -static int ssl_parse_certificate_verify( ssl_context *ssl ) -{ - int ret; - size_t n = 0, n1, n2; - unsigned char hash[48]; - int hash_id; - unsigned int hashlen; - - SSL_DEBUG_MSG( 2, ( "=> parse certificate verify" ) ); - - if( ssl->session_negotiate->peer_cert == NULL ) - { - SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) ); - ssl->state++; - return( 0 ); - } - - ssl->handshake->calc_verify( ssl, hash ); - - if( ( ret = ssl_read_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_read_record", ret ); - return( ret ); - } - - ssl->state++; - - if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "bad certificate verify message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); - } - - if( ssl->in_msg[0] != SSL_HS_CERTIFICATE_VERIFY ) - { - SSL_DEBUG_MSG( 1, ( "bad certificate verify message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); - } - - if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) - { - /* - * As server we know we either have SSL_HASH_SHA384 or - * SSL_HASH_SHA256 - */ - if( ssl->in_msg[4] != ssl->handshake->verify_sig_alg || - ssl->in_msg[5] != SSL_SIG_RSA ) - { - SSL_DEBUG_MSG( 1, ( "peer not adhering to requested sig_alg for verify message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); - } - - if( ssl->handshake->verify_sig_alg == SSL_HASH_SHA384 ) - { - hashlen = 48; - hash_id = SIG_RSA_SHA384; - } - else - { - hashlen = 32; - hash_id = SIG_RSA_SHA256; - } - - n += 2; - } - else - { - hashlen = 36; - hash_id = SIG_RSA_RAW; - } - - n1 = ssl->session_negotiate->peer_cert->rsa.len; - n2 = ( ssl->in_msg[4 + n] << 8 ) | ssl->in_msg[5 + n]; - - if( n + n1 + 6 != ssl->in_hslen || n1 != n2 ) - { - SSL_DEBUG_MSG( 1, ( "bad certificate verify message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); - } - - ret = rsa_pkcs1_verify( &ssl->session_negotiate->peer_cert->rsa, RSA_PUBLIC, - hash_id, hashlen, hash, ssl->in_msg + 6 + n ); - if( ret != 0 ) - { - SSL_DEBUG_RET( 1, "rsa_pkcs1_verify", ret ); - return( ret ); - } - - SSL_DEBUG_MSG( 2, ( "<= parse certificate verify" ) ); - - return( 0 ); -} - -/* - * SSL handshake -- server side -- single step - */ -int ssl_handshake_server_step( ssl_context *ssl ) -{ - int ret = 0; - - if( ssl->state == SSL_HANDSHAKE_OVER ) - return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - - SSL_DEBUG_MSG( 2, ( "server state: %d", ssl->state ) ); - - if( ( ret = ssl_flush_output( ssl ) ) != 0 ) - return( ret ); - - switch( ssl->state ) - { - case SSL_HELLO_REQUEST: - ssl->state = SSL_CLIENT_HELLO; - break; - - /* - * <== ClientHello - */ - case SSL_CLIENT_HELLO: - ret = ssl_parse_client_hello( ssl ); - break; - - /* - * ==> ServerHello - * Certificate - * ( ServerKeyExchange ) - * ( CertificateRequest ) - * ServerHelloDone - */ - case SSL_SERVER_HELLO: - ret = ssl_write_server_hello( ssl ); - break; - - case SSL_SERVER_CERTIFICATE: - ret = ssl_write_certificate( ssl ); - break; - - case SSL_SERVER_KEY_EXCHANGE: - ret = ssl_write_server_key_exchange( ssl ); - break; - - case SSL_CERTIFICATE_REQUEST: - ret = ssl_write_certificate_request( ssl ); - break; - - case SSL_SERVER_HELLO_DONE: - ret = ssl_write_server_hello_done( ssl ); - break; - - /* - * <== ( Certificate/Alert ) - * ClientKeyExchange - * ( CertificateVerify ) - * ChangeCipherSpec - * Finished - */ - case SSL_CLIENT_CERTIFICATE: - ret = ssl_parse_certificate( ssl ); - break; - - case SSL_CLIENT_KEY_EXCHANGE: - ret = ssl_parse_client_key_exchange( ssl ); - break; - - case SSL_CERTIFICATE_VERIFY: - ret = ssl_parse_certificate_verify( ssl ); - break; - - case SSL_CLIENT_CHANGE_CIPHER_SPEC: - ret = ssl_parse_change_cipher_spec( ssl ); - break; - - case SSL_CLIENT_FINISHED: - ret = ssl_parse_finished( ssl ); - break; - - /* - * ==> ChangeCipherSpec - * Finished - */ - case SSL_SERVER_CHANGE_CIPHER_SPEC: - ret = ssl_write_change_cipher_spec( ssl ); - break; - - case SSL_SERVER_FINISHED: - ret = ssl_write_finished( ssl ); - break; - - case SSL_FLUSH_BUFFERS: - SSL_DEBUG_MSG( 2, ( "handshake: done" ) ); - ssl->state = SSL_HANDSHAKE_WRAPUP; - break; - - case SSL_HANDSHAKE_WRAPUP: - ssl_handshake_wrapup( ssl ); - break; - - default: - SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) ); - return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - } - - return( ret ); -} -#endif diff --git a/makerom/polarssl/ssl_tls.c b/makerom/polarssl/ssl_tls.c deleted file mode 100644 index cde6795f..00000000 --- a/makerom/polarssl/ssl_tls.c +++ /dev/null @@ -1,3991 +0,0 @@ -/* - * SSLv3/TLSv1 shared functions - * - * Copyright (C) 2006-2012, 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 SSL 3.0 specification was drafted by Netscape in 1996, - * and became an IETF standard in 1999. - * - * http://wp.netscape.com/eng/ssl3/ - * http://www.ietf.org/rfc/rfc2246.txt - * http://www.ietf.org/rfc/rfc4346.txt - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_SSL_TLS_C) - -#include "polarssl/aes.h" -#include "polarssl/arc4.h" -#include "polarssl/camellia.h" -#include "polarssl/des.h" -#include "polarssl/debug.h" -#include "polarssl/ssl.h" -#include "polarssl/sha2.h" - -#if defined(POLARSSL_GCM_C) -#include "polarssl/gcm.h" -#endif - -#include -#include - -#if defined _MSC_VER && !defined strcasecmp -#define strcasecmp _stricmp -#endif - -#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) -int (*ssl_hw_record_init)(ssl_context *ssl, - const unsigned char *key_enc, const unsigned char *key_dec, - const unsigned char *iv_enc, const unsigned char *iv_dec, - const unsigned char *mac_enc, const unsigned char *mac_dec) = NULL; -int (*ssl_hw_record_reset)(ssl_context *ssl) = NULL; -int (*ssl_hw_record_write)(ssl_context *ssl) = NULL; -int (*ssl_hw_record_read)(ssl_context *ssl) = NULL; -int (*ssl_hw_record_finish)(ssl_context *ssl) = NULL; -#endif - -static int ssl_rsa_decrypt( void *ctx, int mode, size_t *olen, - const unsigned char *input, unsigned char *output, - size_t output_max_len ) -{ - return rsa_pkcs1_decrypt( (rsa_context *) ctx, mode, olen, input, output, - output_max_len ); -} - -static int ssl_rsa_sign( void *ctx, - int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, - int mode, int hash_id, unsigned int hashlen, - const unsigned char *hash, unsigned char *sig ) -{ - return rsa_pkcs1_sign( (rsa_context *) ctx, f_rng, p_rng, mode, hash_id, - hashlen, hash, sig ); -} - -static size_t ssl_rsa_key_len( void *ctx ) -{ - return ( (rsa_context *) ctx )->len; -} - -/* - * Key material generation - */ -static int ssl3_prf( unsigned char *secret, size_t slen, char *label, - unsigned char *random, size_t rlen, - unsigned char *dstbuf, size_t dlen ) -{ - size_t i; - md5_context md5; - sha1_context sha1; - unsigned char padding[16]; - unsigned char sha1sum[20]; - ((void)label); - - /* - * SSLv3: - * block = - * MD5( secret + SHA1( 'A' + secret + random ) ) + - * MD5( secret + SHA1( 'BB' + secret + random ) ) + - * MD5( secret + SHA1( 'CCC' + secret + random ) ) + - * ... - */ - for( i = 0; i < dlen / 16; i++ ) - { - memset( padding, 'A' + i, 1 + i ); - - sha1_starts( &sha1 ); - sha1_update( &sha1, padding, 1 + i ); - sha1_update( &sha1, secret, slen ); - sha1_update( &sha1, random, rlen ); - sha1_finish( &sha1, sha1sum ); - - md5_starts( &md5 ); - md5_update( &md5, secret, slen ); - md5_update( &md5, sha1sum, 20 ); - md5_finish( &md5, dstbuf + i * 16 ); - } - - memset( &md5, 0, sizeof( md5 ) ); - memset( &sha1, 0, sizeof( sha1 ) ); - - memset( padding, 0, sizeof( padding ) ); - memset( sha1sum, 0, sizeof( sha1sum ) ); - - return( 0 ); -} - -static int tls1_prf( unsigned char *secret, size_t slen, char *label, - unsigned char *random, size_t rlen, - unsigned char *dstbuf, size_t dlen ) -{ - size_t nb, hs; - size_t i, j, k; - unsigned char *S1, *S2; - unsigned char tmp[128]; - unsigned char h_i[20]; - - if( sizeof( tmp ) < 20 + strlen( label ) + rlen ) - return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - - hs = ( slen + 1 ) / 2; - S1 = secret; - S2 = secret + slen - hs; - - nb = strlen( label ); - memcpy( tmp + 20, label, nb ); - memcpy( tmp + 20 + nb, random, rlen ); - nb += rlen; - - /* - * First compute P_md5(secret,label+random)[0..dlen] - */ - md5_hmac( S1, hs, tmp + 20, nb, 4 + tmp ); - - for( i = 0; i < dlen; i += 16 ) - { - md5_hmac( S1, hs, 4 + tmp, 16 + nb, h_i ); - md5_hmac( S1, hs, 4 + tmp, 16, 4 + tmp ); - - k = ( i + 16 > dlen ) ? dlen % 16 : 16; - - for( j = 0; j < k; j++ ) - dstbuf[i + j] = h_i[j]; - } - - /* - * XOR out with P_sha1(secret,label+random)[0..dlen] - */ - sha1_hmac( S2, hs, tmp + 20, nb, tmp ); - - for( i = 0; i < dlen; i += 20 ) - { - sha1_hmac( S2, hs, tmp, 20 + nb, h_i ); - sha1_hmac( S2, hs, tmp, 20, tmp ); - - k = ( i + 20 > dlen ) ? dlen % 20 : 20; - - for( j = 0; j < k; j++ ) - dstbuf[i + j] = (unsigned char)( dstbuf[i + j] ^ h_i[j] ); - } - - memset( tmp, 0, sizeof( tmp ) ); - memset( h_i, 0, sizeof( h_i ) ); - - return( 0 ); -} - -static int tls_prf_sha256( unsigned char *secret, size_t slen, char *label, - unsigned char *random, size_t rlen, - unsigned char *dstbuf, size_t dlen ) -{ - size_t nb; - size_t i, j, k; - unsigned char tmp[128]; - unsigned char h_i[32]; - - if( sizeof( tmp ) < 32 + strlen( label ) + rlen ) - return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - - nb = strlen( label ); - memcpy( tmp + 32, label, nb ); - memcpy( tmp + 32 + nb, random, rlen ); - nb += rlen; - - /* - * Compute P_(secret, label + random)[0..dlen] - */ - sha2_hmac( secret, slen, tmp + 32, nb, tmp, 0 ); - - for( i = 0; i < dlen; i += 32 ) - { - sha2_hmac( secret, slen, tmp, 32 + nb, h_i, 0 ); - sha2_hmac( secret, slen, tmp, 32, tmp, 0 ); - - k = ( i + 32 > dlen ) ? dlen % 32 : 32; - - for( j = 0; j < k; j++ ) - dstbuf[i + j] = h_i[j]; - } - - memset( tmp, 0, sizeof( tmp ) ); - memset( h_i, 0, sizeof( h_i ) ); - - return( 0 ); -} - -#if defined(POLARSSL_SHA4_C) -static int tls_prf_sha384( unsigned char *secret, size_t slen, char *label, - unsigned char *random, size_t rlen, - unsigned char *dstbuf, size_t dlen ) -{ - size_t nb; - size_t i, j, k; - unsigned char tmp[128]; - unsigned char h_i[48]; - - if( sizeof( tmp ) < 48 + strlen( label ) + rlen ) - return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - - nb = strlen( label ); - memcpy( tmp + 48, label, nb ); - memcpy( tmp + 48 + nb, random, rlen ); - nb += rlen; - - /* - * Compute P_(secret, label + random)[0..dlen] - */ - sha4_hmac( secret, slen, tmp + 48, nb, tmp, 1 ); - - for( i = 0; i < dlen; i += 48 ) - { - sha4_hmac( secret, slen, tmp, 48 + nb, h_i, 1 ); - sha4_hmac( secret, slen, tmp, 48, tmp, 1 ); - - k = ( i + 48 > dlen ) ? dlen % 48 : 48; - - for( j = 0; j < k; j++ ) - dstbuf[i + j] = h_i[j]; - } - - memset( tmp, 0, sizeof( tmp ) ); - memset( h_i, 0, sizeof( h_i ) ); - - return( 0 ); -} -#endif - -static void ssl_update_checksum_start(ssl_context *, unsigned char *, size_t); -static void ssl_update_checksum_md5sha1(ssl_context *, unsigned char *, size_t); -static void ssl_update_checksum_sha256(ssl_context *, unsigned char *, size_t); - -static void ssl_calc_verify_ssl(ssl_context *,unsigned char *); -static void ssl_calc_verify_tls(ssl_context *,unsigned char *); -static void ssl_calc_verify_tls_sha256(ssl_context *,unsigned char *); - -static void ssl_calc_finished_ssl(ssl_context *,unsigned char *,int); -static void ssl_calc_finished_tls(ssl_context *,unsigned char *,int); -static void ssl_calc_finished_tls_sha256(ssl_context *,unsigned char *,int); - -#if defined(POLARSSL_SHA4_C) -static void ssl_update_checksum_sha384(ssl_context *, unsigned char *, size_t); -static void ssl_calc_verify_tls_sha384(ssl_context *,unsigned char *); -static void ssl_calc_finished_tls_sha384(ssl_context *,unsigned char *,int); -#endif - -int ssl_derive_keys( ssl_context *ssl ) -{ - unsigned char tmp[64]; - unsigned char keyblk[256]; - unsigned char *key1; - unsigned char *key2; - unsigned int iv_copy_len; - ssl_session *session = ssl->session_negotiate; - ssl_transform *transform = ssl->transform_negotiate; - ssl_handshake_params *handshake = ssl->handshake; - - SSL_DEBUG_MSG( 2, ( "=> derive keys" ) ); - - /* - * Set appropriate PRF function and other SSL / TLS / TLS1.2 functions - */ - if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) - { - handshake->tls_prf = ssl3_prf; - handshake->calc_verify = ssl_calc_verify_ssl; - handshake->calc_finished = ssl_calc_finished_ssl; - } - else if( ssl->minor_ver < SSL_MINOR_VERSION_3 ) - { - handshake->tls_prf = tls1_prf; - handshake->calc_verify = ssl_calc_verify_tls; - handshake->calc_finished = ssl_calc_finished_tls; - } -#if defined(POLARSSL_SHA4_C) - else if( session->ciphersuite == TLS_RSA_WITH_AES_256_GCM_SHA384 || - session->ciphersuite == TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ) - { - handshake->tls_prf = tls_prf_sha384; - handshake->calc_verify = ssl_calc_verify_tls_sha384; - handshake->calc_finished = ssl_calc_finished_tls_sha384; - } -#endif - else - { - handshake->tls_prf = tls_prf_sha256; - handshake->calc_verify = ssl_calc_verify_tls_sha256; - handshake->calc_finished = ssl_calc_finished_tls_sha256; - } - - /* - * SSLv3: - * master = - * MD5( premaster + SHA1( 'A' + premaster + randbytes ) ) + - * MD5( premaster + SHA1( 'BB' + premaster + randbytes ) ) + - * MD5( premaster + SHA1( 'CCC' + premaster + randbytes ) ) - * - * TLSv1: - * master = PRF( premaster, "master secret", randbytes )[0..47] - */ - if( handshake->resume == 0 ) - { - SSL_DEBUG_BUF( 3, "premaster secret", handshake->premaster, - handshake->pmslen ); - - handshake->tls_prf( handshake->premaster, handshake->pmslen, - "master secret", - handshake->randbytes, 64, session->master, 48 ); - - memset( handshake->premaster, 0, sizeof( handshake->premaster ) ); - } - else - SSL_DEBUG_MSG( 3, ( "no premaster (session resumed)" ) ); - - /* - * Swap the client and server random values. - */ - memcpy( tmp, handshake->randbytes, 64 ); - memcpy( handshake->randbytes, tmp + 32, 32 ); - memcpy( handshake->randbytes + 32, tmp, 32 ); - memset( tmp, 0, sizeof( tmp ) ); - - /* - * SSLv3: - * key block = - * MD5( master + SHA1( 'A' + master + randbytes ) ) + - * MD5( master + SHA1( 'BB' + master + randbytes ) ) + - * MD5( master + SHA1( 'CCC' + master + randbytes ) ) + - * MD5( master + SHA1( 'DDDD' + master + randbytes ) ) + - * ... - * - * TLSv1: - * key block = PRF( master, "key expansion", randbytes ) - */ - handshake->tls_prf( session->master, 48, "key expansion", - handshake->randbytes, 64, keyblk, 256 ); - - SSL_DEBUG_MSG( 3, ( "ciphersuite = %s", - ssl_get_ciphersuite_name( session->ciphersuite ) ) ); - SSL_DEBUG_BUF( 3, "master secret", session->master, 48 ); - SSL_DEBUG_BUF( 4, "random bytes", handshake->randbytes, 64 ); - SSL_DEBUG_BUF( 4, "key block", keyblk, 256 ); - - memset( handshake->randbytes, 0, sizeof( handshake->randbytes ) ); - - /* - * Determine the appropriate key, IV and MAC length. - */ - switch( session->ciphersuite ) - { -#if defined(POLARSSL_ARC4_C) - case TLS_RSA_WITH_RC4_128_MD5: - transform->keylen = 16; transform->minlen = 16; - transform->ivlen = 0; transform->maclen = 16; - break; - - case TLS_RSA_WITH_RC4_128_SHA: - transform->keylen = 16; transform->minlen = 20; - transform->ivlen = 0; transform->maclen = 20; - break; -#endif - -#if defined(POLARSSL_DES_C) - case TLS_RSA_WITH_3DES_EDE_CBC_SHA: - case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: - transform->keylen = 24; transform->minlen = 24; - transform->ivlen = 8; transform->maclen = 20; - break; -#endif - -#if defined(POLARSSL_AES_C) - case TLS_RSA_WITH_AES_128_CBC_SHA: - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: - transform->keylen = 16; transform->minlen = 32; - transform->ivlen = 16; transform->maclen = 20; - break; - - case TLS_RSA_WITH_AES_256_CBC_SHA: - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: - transform->keylen = 32; transform->minlen = 32; - transform->ivlen = 16; transform->maclen = 20; - break; - -#if defined(POLARSSL_SHA2_C) - case TLS_RSA_WITH_AES_128_CBC_SHA256: - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: - transform->keylen = 16; transform->minlen = 32; - transform->ivlen = 16; transform->maclen = 32; - break; - - case TLS_RSA_WITH_AES_256_CBC_SHA256: - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: - transform->keylen = 32; transform->minlen = 32; - transform->ivlen = 16; transform->maclen = 32; - break; -#endif -#if defined(POLARSSL_GCM_C) - case TLS_RSA_WITH_AES_128_GCM_SHA256: - case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: - transform->keylen = 16; transform->minlen = 1; - transform->ivlen = 12; transform->maclen = 0; - transform->fixed_ivlen = 4; - break; - - case TLS_RSA_WITH_AES_256_GCM_SHA384: - case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: - transform->keylen = 32; transform->minlen = 1; - transform->ivlen = 12; transform->maclen = 0; - transform->fixed_ivlen = 4; - break; -#endif -#endif - -#if defined(POLARSSL_CAMELLIA_C) - case TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: - case TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: - transform->keylen = 16; transform->minlen = 32; - transform->ivlen = 16; transform->maclen = 20; - break; - - case TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: - case TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: - transform->keylen = 32; transform->minlen = 32; - transform->ivlen = 16; transform->maclen = 20; - break; - -#if defined(POLARSSL_SHA2_C) - case TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: - case TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: - transform->keylen = 16; transform->minlen = 32; - transform->ivlen = 16; transform->maclen = 32; - break; - - case TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: - case TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: - transform->keylen = 32; transform->minlen = 32; - transform->ivlen = 16; transform->maclen = 32; - break; -#endif -#endif - -#if defined(POLARSSL_ENABLE_WEAK_CIPHERSUITES) -#if defined(POLARSSL_CIPHER_NULL_CIPHER) - case TLS_RSA_WITH_NULL_MD5: - transform->keylen = 0; transform->minlen = 0; - transform->ivlen = 0; transform->maclen = 16; - break; - - case TLS_RSA_WITH_NULL_SHA: - transform->keylen = 0; transform->minlen = 0; - transform->ivlen = 0; transform->maclen = 20; - break; - - case TLS_RSA_WITH_NULL_SHA256: - transform->keylen = 0; transform->minlen = 0; - transform->ivlen = 0; transform->maclen = 32; - break; -#endif /* defined(POLARSSL_CIPHER_NULL_CIPHER) */ - -#if defined(POLARSSL_DES_C) - case TLS_RSA_WITH_DES_CBC_SHA: - case TLS_DHE_RSA_WITH_DES_CBC_SHA: - transform->keylen = 8; transform->minlen = 8; - transform->ivlen = 8; transform->maclen = 20; - break; -#endif -#endif /* defined(POLARSSL_ENABLE_WEAK_CIPHERSUITES) */ - - default: - SSL_DEBUG_MSG( 1, ( "ciphersuite %s is not available", - ssl_get_ciphersuite_name( session->ciphersuite ) ) ); - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); - } - - SSL_DEBUG_MSG( 3, ( "keylen: %d, minlen: %d, ivlen: %d, maclen: %d", - transform->keylen, transform->minlen, transform->ivlen, - transform->maclen ) ); - - /* - * Finally setup the cipher contexts, IVs and MAC secrets. - */ - if( ssl->endpoint == SSL_IS_CLIENT ) - { - key1 = keyblk + transform->maclen * 2; - key2 = keyblk + transform->maclen * 2 + transform->keylen; - - memcpy( transform->mac_enc, keyblk, transform->maclen ); - memcpy( transform->mac_dec, keyblk + transform->maclen, - transform->maclen ); - - /* - * This is not used in TLS v1.1. - */ - iv_copy_len = ( transform->fixed_ivlen ) ? - transform->fixed_ivlen : transform->ivlen; - memcpy( transform->iv_enc, key2 + transform->keylen, iv_copy_len ); - memcpy( transform->iv_dec, key2 + transform->keylen + iv_copy_len, - iv_copy_len ); - } - else - { - key1 = keyblk + transform->maclen * 2 + transform->keylen; - key2 = keyblk + transform->maclen * 2; - - memcpy( transform->mac_dec, keyblk, transform->maclen ); - memcpy( transform->mac_enc, keyblk + transform->maclen, - transform->maclen ); - - /* - * This is not used in TLS v1.1. - */ - iv_copy_len = ( transform->fixed_ivlen ) ? - transform->fixed_ivlen : transform->ivlen; - memcpy( transform->iv_dec, key1 + transform->keylen, iv_copy_len ); - memcpy( transform->iv_enc, key1 + transform->keylen + iv_copy_len, - iv_copy_len ); - } - -#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) - if( ssl_hw_record_init != NULL) - { - int ret = 0; - - SSL_DEBUG_MSG( 2, ( "going for ssl_hw_record_init()" ) ); - - if( ( ret = ssl_hw_record_init( ssl, key1, key2, transform->iv_enc, - transform->iv_dec, transform->mac_enc, - transform->mac_dec ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_hw_record_init", ret ); - return POLARSSL_ERR_SSL_HW_ACCEL_FAILED; - } - } -#endif - - switch( session->ciphersuite ) - { -#if defined(POLARSSL_ARC4_C) - case TLS_RSA_WITH_RC4_128_MD5: - case TLS_RSA_WITH_RC4_128_SHA: - arc4_setup( (arc4_context *) transform->ctx_enc, key1, - transform->keylen ); - arc4_setup( (arc4_context *) transform->ctx_dec, key2, - transform->keylen ); - break; -#endif - -#if defined(POLARSSL_DES_C) - case TLS_RSA_WITH_3DES_EDE_CBC_SHA: - case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: - des3_set3key_enc( (des3_context *) transform->ctx_enc, key1 ); - des3_set3key_dec( (des3_context *) transform->ctx_dec, key2 ); - break; -#endif - -#if defined(POLARSSL_AES_C) - case TLS_RSA_WITH_AES_128_CBC_SHA: - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: - case TLS_RSA_WITH_AES_128_CBC_SHA256: - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: - aes_setkey_enc( (aes_context *) transform->ctx_enc, key1, 128 ); - aes_setkey_dec( (aes_context *) transform->ctx_dec, key2, 128 ); - break; - - case TLS_RSA_WITH_AES_256_CBC_SHA: - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: - case TLS_RSA_WITH_AES_256_CBC_SHA256: - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: - aes_setkey_enc( (aes_context *) transform->ctx_enc, key1, 256 ); - aes_setkey_dec( (aes_context *) transform->ctx_dec, key2, 256 ); - break; - -#if defined(POLARSSL_GCM_C) - case TLS_RSA_WITH_AES_128_GCM_SHA256: - case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: - gcm_init( (gcm_context *) transform->ctx_enc, key1, 128 ); - gcm_init( (gcm_context *) transform->ctx_dec, key2, 128 ); - break; - - case TLS_RSA_WITH_AES_256_GCM_SHA384: - case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: - gcm_init( (gcm_context *) transform->ctx_enc, key1, 256 ); - gcm_init( (gcm_context *) transform->ctx_dec, key2, 256 ); - break; -#endif -#endif - -#if defined(POLARSSL_CAMELLIA_C) - case TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: - case TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: - case TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: - case TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: - camellia_setkey_enc( (camellia_context *) transform->ctx_enc, key1, 128 ); - camellia_setkey_dec( (camellia_context *) transform->ctx_dec, key2, 128 ); - break; - - case TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: - case TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: - case TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: - case TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: - camellia_setkey_enc( (camellia_context *) transform->ctx_enc, key1, 256 ); - camellia_setkey_dec( (camellia_context *) transform->ctx_dec, key2, 256 ); - break; -#endif - -#if defined(POLARSSL_ENABLE_WEAK_CIPHERSUITES) -#if defined(POLARSSL_CIPHER_NULL_CIPHER) - case TLS_RSA_WITH_NULL_MD5: - case TLS_RSA_WITH_NULL_SHA: - case TLS_RSA_WITH_NULL_SHA256: - break; -#endif /* defined(POLARSSL_CIPHER_NULL_CIPHER) */ - -#if defined(POLARSSL_DES_C) - case TLS_RSA_WITH_DES_CBC_SHA: - case TLS_DHE_RSA_WITH_DES_CBC_SHA: - des_setkey_enc( (des_context *) transform->ctx_enc, key1 ); - des_setkey_dec( (des_context *) transform->ctx_dec, key2 ); - break; -#endif -#endif /* defined(POLARSSL_ENABLE_WEAK_CIPHERSUITES) */ - - default: - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); - } - - memset( keyblk, 0, sizeof( keyblk ) ); - -#if defined(POLARSSL_ZLIB_SUPPORT) - // Initialize compression - // - if( session->compression == SSL_COMPRESS_DEFLATE ) - { - SSL_DEBUG_MSG( 3, ( "Initializing zlib states" ) ); - - memset( &transform->ctx_deflate, 0, sizeof( transform->ctx_deflate ) ); - memset( &transform->ctx_inflate, 0, sizeof( transform->ctx_inflate ) ); - - if( deflateInit( &transform->ctx_deflate, Z_DEFAULT_COMPRESSION ) != Z_OK || - inflateInit( &transform->ctx_inflate ) != Z_OK ) - { - SSL_DEBUG_MSG( 1, ( "Failed to initialize compression" ) ); - return( POLARSSL_ERR_SSL_COMPRESSION_FAILED ); - } - } -#endif /* POLARSSL_ZLIB_SUPPORT */ - - SSL_DEBUG_MSG( 2, ( "<= derive keys" ) ); - - return( 0 ); -} - -void ssl_calc_verify_ssl( ssl_context *ssl, unsigned char hash[36] ) -{ - md5_context md5; - sha1_context sha1; - unsigned char pad_1[48]; - unsigned char pad_2[48]; - - SSL_DEBUG_MSG( 2, ( "=> calc verify ssl" ) ); - - memcpy( &md5 , &ssl->handshake->fin_md5 , sizeof(md5_context) ); - memcpy( &sha1, &ssl->handshake->fin_sha1, sizeof(sha1_context) ); - - memset( pad_1, 0x36, 48 ); - memset( pad_2, 0x5C, 48 ); - - md5_update( &md5, ssl->session_negotiate->master, 48 ); - md5_update( &md5, pad_1, 48 ); - md5_finish( &md5, hash ); - - md5_starts( &md5 ); - md5_update( &md5, ssl->session_negotiate->master, 48 ); - md5_update( &md5, pad_2, 48 ); - md5_update( &md5, hash, 16 ); - md5_finish( &md5, hash ); - - sha1_update( &sha1, ssl->session_negotiate->master, 48 ); - sha1_update( &sha1, pad_1, 40 ); - sha1_finish( &sha1, hash + 16 ); - - sha1_starts( &sha1 ); - sha1_update( &sha1, ssl->session_negotiate->master, 48 ); - sha1_update( &sha1, pad_2, 40 ); - sha1_update( &sha1, hash + 16, 20 ); - sha1_finish( &sha1, hash + 16 ); - - SSL_DEBUG_BUF( 3, "calculated verify result", hash, 36 ); - SSL_DEBUG_MSG( 2, ( "<= calc verify" ) ); - - return; -} - -void ssl_calc_verify_tls( ssl_context *ssl, unsigned char hash[36] ) -{ - md5_context md5; - sha1_context sha1; - - SSL_DEBUG_MSG( 2, ( "=> calc verify tls" ) ); - - memcpy( &md5 , &ssl->handshake->fin_md5 , sizeof(md5_context) ); - memcpy( &sha1, &ssl->handshake->fin_sha1, sizeof(sha1_context) ); - - md5_finish( &md5, hash ); - sha1_finish( &sha1, hash + 16 ); - - SSL_DEBUG_BUF( 3, "calculated verify result", hash, 36 ); - SSL_DEBUG_MSG( 2, ( "<= calc verify" ) ); - - return; -} - -void ssl_calc_verify_tls_sha256( ssl_context *ssl, unsigned char hash[32] ) -{ - sha2_context sha2; - - SSL_DEBUG_MSG( 2, ( "=> calc verify sha256" ) ); - - memcpy( &sha2, &ssl->handshake->fin_sha2, sizeof(sha2_context) ); - sha2_finish( &sha2, hash ); - - SSL_DEBUG_BUF( 3, "calculated verify result", hash, 32 ); - SSL_DEBUG_MSG( 2, ( "<= calc verify" ) ); - - return; -} - -#if defined(POLARSSL_SHA4_C) -void ssl_calc_verify_tls_sha384( ssl_context *ssl, unsigned char hash[48] ) -{ - sha4_context sha4; - - SSL_DEBUG_MSG( 2, ( "=> calc verify sha384" ) ); - - memcpy( &sha4, &ssl->handshake->fin_sha4, sizeof(sha4_context) ); - sha4_finish( &sha4, hash ); - - SSL_DEBUG_BUF( 3, "calculated verify result", hash, 48 ); - SSL_DEBUG_MSG( 2, ( "<= calc verify" ) ); - - return; -} -#endif - -/* - * SSLv3.0 MAC functions - */ -static void ssl_mac_md5( unsigned char *secret, - unsigned char *buf, size_t len, - unsigned char *ctr, int type ) -{ - unsigned char header[11]; - unsigned char padding[48]; - md5_context md5; - - memcpy( header, ctr, 8 ); - header[ 8] = (unsigned char) type; - header[ 9] = (unsigned char)( len >> 8 ); - header[10] = (unsigned char)( len ); - - memset( padding, 0x36, 48 ); - md5_starts( &md5 ); - md5_update( &md5, secret, 16 ); - md5_update( &md5, padding, 48 ); - md5_update( &md5, header, 11 ); - md5_update( &md5, buf, len ); - md5_finish( &md5, buf + len ); - - memset( padding, 0x5C, 48 ); - md5_starts( &md5 ); - md5_update( &md5, secret, 16 ); - md5_update( &md5, padding, 48 ); - md5_update( &md5, buf + len, 16 ); - md5_finish( &md5, buf + len ); -} - -static void ssl_mac_sha1( unsigned char *secret, - unsigned char *buf, size_t len, - unsigned char *ctr, int type ) -{ - unsigned char header[11]; - unsigned char padding[40]; - sha1_context sha1; - - memcpy( header, ctr, 8 ); - header[ 8] = (unsigned char) type; - header[ 9] = (unsigned char)( len >> 8 ); - header[10] = (unsigned char)( len ); - - memset( padding, 0x36, 40 ); - sha1_starts( &sha1 ); - sha1_update( &sha1, secret, 20 ); - sha1_update( &sha1, padding, 40 ); - sha1_update( &sha1, header, 11 ); - sha1_update( &sha1, buf, len ); - sha1_finish( &sha1, buf + len ); - - memset( padding, 0x5C, 40 ); - sha1_starts( &sha1 ); - sha1_update( &sha1, secret, 20 ); - sha1_update( &sha1, padding, 40 ); - sha1_update( &sha1, buf + len, 20 ); - sha1_finish( &sha1, buf + len ); -} - -static void ssl_mac_sha2( unsigned char *secret, - unsigned char *buf, size_t len, - unsigned char *ctr, int type ) -{ - unsigned char header[11]; - unsigned char padding[32]; - sha2_context sha2; - - memcpy( header, ctr, 8 ); - header[ 8] = (unsigned char) type; - header[ 9] = (unsigned char)( len >> 8 ); - header[10] = (unsigned char)( len ); - - memset( padding, 0x36, 32 ); - sha2_starts( &sha2, 0 ); - sha2_update( &sha2, secret, 32 ); - sha2_update( &sha2, padding, 32 ); - sha2_update( &sha2, header, 11 ); - sha2_update( &sha2, buf, len ); - sha2_finish( &sha2, buf + len ); - - memset( padding, 0x5C, 32 ); - sha2_starts( &sha2, 0 ); - sha2_update( &sha2, secret, 32 ); - sha2_update( &sha2, padding, 32 ); - sha2_update( &sha2, buf + len, 32 ); - sha2_finish( &sha2, buf + len ); -} - -/* - * Encryption/decryption functions - */ -static int ssl_encrypt_buf( ssl_context *ssl ) -{ - size_t i, padlen; - - SSL_DEBUG_MSG( 2, ( "=> encrypt buf" ) ); - - /* - * Add MAC then encrypt - */ - if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) - { - if( ssl->transform_out->maclen == 16 ) - ssl_mac_md5( ssl->transform_out->mac_enc, - ssl->out_msg, ssl->out_msglen, - ssl->out_ctr, ssl->out_msgtype ); - else if( ssl->transform_out->maclen == 20 ) - ssl_mac_sha1( ssl->transform_out->mac_enc, - ssl->out_msg, ssl->out_msglen, - ssl->out_ctr, ssl->out_msgtype ); - else if( ssl->transform_out->maclen == 32 ) - ssl_mac_sha2( ssl->transform_out->mac_enc, - ssl->out_msg, ssl->out_msglen, - ssl->out_ctr, ssl->out_msgtype ); - else if( ssl->transform_out->maclen != 0 ) - { - SSL_DEBUG_MSG( 1, ( "invalid MAC len: %d", - ssl->transform_out->maclen ) ); - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); - } - } - else - { - if( ssl->transform_out->maclen == 16 ) - { - md5_context ctx; - md5_hmac_starts( &ctx, ssl->transform_out->mac_enc, 16 ); - md5_hmac_update( &ctx, ssl->out_ctr, 13 ); - md5_hmac_update( &ctx, ssl->out_msg, ssl->out_msglen ); - md5_hmac_finish( &ctx, ssl->out_msg + ssl->out_msglen ); - memset( &ctx, 0, sizeof(md5_context)); - } - else if( ssl->transform_out->maclen == 20 ) - { - sha1_context ctx; - sha1_hmac_starts( &ctx, ssl->transform_out->mac_enc, 20 ); - sha1_hmac_update( &ctx, ssl->out_ctr, 13 ); - sha1_hmac_update( &ctx, ssl->out_msg, ssl->out_msglen ); - sha1_hmac_finish( &ctx, ssl->out_msg + ssl->out_msglen ); - memset( &ctx, 0, sizeof(sha1_context)); - } - else if( ssl->transform_out->maclen == 32 ) - { - sha2_context ctx; - sha2_hmac_starts( &ctx, ssl->transform_out->mac_enc, 32, 0 ); - sha2_hmac_update( &ctx, ssl->out_ctr, 13 ); - sha2_hmac_update( &ctx, ssl->out_msg, ssl->out_msglen ); - sha2_hmac_finish( &ctx, ssl->out_msg + ssl->out_msglen ); - memset( &ctx, 0, sizeof(sha2_context)); - } - else if( ssl->transform_out->maclen != 0 ) - { - SSL_DEBUG_MSG( 1, ( "invalid MAC len: %d", - ssl->transform_out->maclen ) ); - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); - } - } - - SSL_DEBUG_BUF( 4, "computed mac", - ssl->out_msg + ssl->out_msglen, ssl->transform_out->maclen ); - - ssl->out_msglen += ssl->transform_out->maclen; - - if( ssl->transform_out->ivlen == 0 ) - { - padlen = 0; - - SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, " - "including %d bytes of padding", - ssl->out_msglen, 0 ) ); - - SSL_DEBUG_BUF( 4, "before encrypt: output payload", - ssl->out_msg, ssl->out_msglen ); - -#if defined(POLARSSL_ARC4_C) - if( ssl->session_out->ciphersuite == TLS_RSA_WITH_RC4_128_MD5 || - ssl->session_out->ciphersuite == TLS_RSA_WITH_RC4_128_SHA ) - { - arc4_crypt( (arc4_context *) ssl->transform_out->ctx_enc, - ssl->out_msglen, ssl->out_msg, - ssl->out_msg ); - } else -#endif -#if defined(POLARSSL_CIPHER_NULL_CIPHER) - if( ssl->session_out->ciphersuite == TLS_RSA_WITH_NULL_MD5 || - ssl->session_out->ciphersuite == TLS_RSA_WITH_NULL_SHA || - ssl->session_out->ciphersuite == TLS_RSA_WITH_NULL_SHA256 ) - { - } else -#endif - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); - } - else if( ssl->transform_out->ivlen == 12 ) - { - size_t enc_msglen; - unsigned char *enc_msg; - unsigned char add_data[13]; - int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; - - padlen = 0; - enc_msglen = ssl->out_msglen; - - memcpy( add_data, ssl->out_ctr, 8 ); - add_data[8] = ssl->out_msgtype; - add_data[9] = ssl->major_ver; - add_data[10] = ssl->minor_ver; - add_data[11] = ( ssl->out_msglen >> 8 ) & 0xFF; - add_data[12] = ssl->out_msglen & 0xFF; - - SSL_DEBUG_BUF( 4, "additional data used for AEAD", - add_data, 13 ); - -#if defined(POLARSSL_AES_C) && defined(POLARSSL_GCM_C) - - if( ssl->session_out->ciphersuite == TLS_RSA_WITH_AES_128_GCM_SHA256 || - ssl->session_out->ciphersuite == TLS_RSA_WITH_AES_256_GCM_SHA384 || - ssl->session_out->ciphersuite == TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 || - ssl->session_out->ciphersuite == TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ) - { - /* - * Generate IV - */ - ret = ssl->f_rng( ssl->p_rng, - ssl->transform_out->iv_enc + ssl->transform_out->fixed_ivlen, - ssl->transform_out->ivlen - ssl->transform_out->fixed_ivlen ); - if( ret != 0 ) - return( ret ); - - /* - * Shift message for ivlen bytes and prepend IV - */ - memmove( ssl->out_msg + ssl->transform_out->ivlen - - ssl->transform_out->fixed_ivlen, - ssl->out_msg, ssl->out_msglen ); - memcpy( ssl->out_msg, - ssl->transform_out->iv_enc + ssl->transform_out->fixed_ivlen, - ssl->transform_out->ivlen - ssl->transform_out->fixed_ivlen ); - - /* - * Fix pointer positions and message length with added IV - */ - enc_msg = ssl->out_msg + ssl->transform_out->ivlen - - ssl->transform_out->fixed_ivlen; - enc_msglen = ssl->out_msglen; - ssl->out_msglen += ssl->transform_out->ivlen - - ssl->transform_out->fixed_ivlen; - - SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, " - "including %d bytes of padding", - ssl->out_msglen, 0 ) ); - - SSL_DEBUG_BUF( 4, "before encrypt: output payload", - ssl->out_msg, ssl->out_msglen ); - - /* - * Adjust for tag - */ - ssl->out_msglen += 16; - - gcm_crypt_and_tag( (gcm_context *) ssl->transform_out->ctx_enc, - GCM_ENCRYPT, enc_msglen, - ssl->transform_out->iv_enc, ssl->transform_out->ivlen, - add_data, 13, - enc_msg, enc_msg, - 16, enc_msg + enc_msglen ); - - SSL_DEBUG_BUF( 4, "after encrypt: tag", - enc_msg + enc_msglen, 16 ); - - } else -#endif - return( ret ); - } - else - { - unsigned char *enc_msg; - size_t enc_msglen; - - padlen = ssl->transform_out->ivlen - ( ssl->out_msglen + 1 ) % - ssl->transform_out->ivlen; - if( padlen == ssl->transform_out->ivlen ) - padlen = 0; - - for( i = 0; i <= padlen; i++ ) - ssl->out_msg[ssl->out_msglen + i] = (unsigned char) padlen; - - ssl->out_msglen += padlen + 1; - - enc_msglen = ssl->out_msglen; - enc_msg = ssl->out_msg; - - /* - * Prepend per-record IV for block cipher in TLS v1.1 and up as per - * Method 1 (6.2.3.2. in RFC4346 and RFC5246) - */ - if( ssl->minor_ver >= SSL_MINOR_VERSION_2 ) - { - /* - * Generate IV - */ - int ret = ssl->f_rng( ssl->p_rng, ssl->transform_out->iv_enc, - ssl->transform_out->ivlen ); - if( ret != 0 ) - return( ret ); - - /* - * Shift message for ivlen bytes and prepend IV - */ - memmove( ssl->out_msg + ssl->transform_out->ivlen, ssl->out_msg, - ssl->out_msglen ); - memcpy( ssl->out_msg, ssl->transform_out->iv_enc, - ssl->transform_out->ivlen ); - - /* - * Fix pointer positions and message length with added IV - */ - enc_msg = ssl->out_msg + ssl->transform_out->ivlen; - enc_msglen = ssl->out_msglen; - ssl->out_msglen += ssl->transform_out->ivlen; - } - - SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, " - "including %d bytes of IV and %d bytes of padding", - ssl->out_msglen, ssl->transform_out->ivlen, padlen + 1 ) ); - - SSL_DEBUG_BUF( 4, "before encrypt: output payload", - ssl->out_msg, ssl->out_msglen ); - - switch( ssl->transform_out->ivlen ) - { -#if defined(POLARSSL_DES_C) - case 8: -#if defined(POLARSSL_ENABLE_WEAK_CIPHERSUITES) - if( ssl->session_out->ciphersuite == TLS_RSA_WITH_DES_CBC_SHA || - ssl->session_out->ciphersuite == TLS_DHE_RSA_WITH_DES_CBC_SHA ) - { - des_crypt_cbc( (des_context *) ssl->transform_out->ctx_enc, - DES_ENCRYPT, enc_msglen, - ssl->transform_out->iv_enc, enc_msg, enc_msg ); - } - else -#endif - des3_crypt_cbc( (des3_context *) ssl->transform_out->ctx_enc, - DES_ENCRYPT, enc_msglen, - ssl->transform_out->iv_enc, enc_msg, enc_msg ); - break; -#endif - - case 16: -#if defined(POLARSSL_AES_C) - if ( ssl->session_out->ciphersuite == TLS_RSA_WITH_AES_128_CBC_SHA || - ssl->session_out->ciphersuite == TLS_DHE_RSA_WITH_AES_128_CBC_SHA || - ssl->session_out->ciphersuite == TLS_RSA_WITH_AES_256_CBC_SHA || - ssl->session_out->ciphersuite == TLS_DHE_RSA_WITH_AES_256_CBC_SHA || - ssl->session_out->ciphersuite == TLS_RSA_WITH_AES_128_CBC_SHA256 || - ssl->session_out->ciphersuite == TLS_RSA_WITH_AES_256_CBC_SHA256 || - ssl->session_out->ciphersuite == TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 || - ssl->session_out->ciphersuite == TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 ) - { - aes_crypt_cbc( (aes_context *) ssl->transform_out->ctx_enc, - AES_ENCRYPT, enc_msglen, - ssl->transform_out->iv_enc, enc_msg, enc_msg); - break; - } -#endif - -#if defined(POLARSSL_CAMELLIA_C) - if ( ssl->session_out->ciphersuite == TLS_RSA_WITH_CAMELLIA_128_CBC_SHA || - ssl->session_out->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA || - ssl->session_out->ciphersuite == TLS_RSA_WITH_CAMELLIA_256_CBC_SHA || - ssl->session_out->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA || - ssl->session_out->ciphersuite == TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 || - ssl->session_out->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 || - ssl->session_out->ciphersuite == TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 || - ssl->session_out->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 ) - { - camellia_crypt_cbc( (camellia_context *) ssl->transform_out->ctx_enc, - CAMELLIA_ENCRYPT, enc_msglen, - ssl->transform_out->iv_enc, enc_msg, enc_msg ); - break; - } -#endif - - default: - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); - } - } - - for( i = 8; i > 0; i-- ) - if( ++ssl->out_ctr[i - 1] != 0 ) - break; - - SSL_DEBUG_MSG( 2, ( "<= encrypt buf" ) ); - - return( 0 ); -} - -/* - * TODO: Use digest version when integrated! - */ -#define POLARSSL_SSL_MAX_MAC_SIZE 32 - -static int ssl_decrypt_buf( ssl_context *ssl ) -{ - size_t i, padlen = 0, correct = 1; - unsigned char tmp[POLARSSL_SSL_MAX_MAC_SIZE]; - - SSL_DEBUG_MSG( 2, ( "=> decrypt buf" ) ); - - if( ssl->in_msglen < ssl->transform_in->minlen ) - { - SSL_DEBUG_MSG( 1, ( "in_msglen (%d) < minlen (%d)", - ssl->in_msglen, ssl->transform_in->minlen ) ); - return( POLARSSL_ERR_SSL_INVALID_MAC ); - } - - if( ssl->transform_in->ivlen == 0 ) - { -#if defined(POLARSSL_ARC4_C) - if( ssl->session_in->ciphersuite == TLS_RSA_WITH_RC4_128_MD5 || - ssl->session_in->ciphersuite == TLS_RSA_WITH_RC4_128_SHA ) - { - arc4_crypt( (arc4_context *) ssl->transform_in->ctx_dec, - ssl->in_msglen, ssl->in_msg, - ssl->in_msg ); - } else -#endif -#if defined(POLARSSL_CIPHER_NULL_CIPHER) - if( ssl->session_in->ciphersuite == TLS_RSA_WITH_NULL_MD5 || - ssl->session_in->ciphersuite == TLS_RSA_WITH_NULL_SHA || - ssl->session_in->ciphersuite == TLS_RSA_WITH_NULL_SHA256 ) - { - } else -#endif - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); - } - else if( ssl->transform_in->ivlen == 12 ) - { - unsigned char *dec_msg; - unsigned char *dec_msg_result; - size_t dec_msglen; - unsigned char add_data[13]; - int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; - -#if defined(POLARSSL_AES_C) && defined(POLARSSL_GCM_C) - if( ssl->session_in->ciphersuite == TLS_RSA_WITH_AES_128_GCM_SHA256 || - ssl->session_in->ciphersuite == TLS_RSA_WITH_AES_256_GCM_SHA384 || - ssl->session_in->ciphersuite == TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 || - ssl->session_in->ciphersuite == TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ) - { - dec_msglen = ssl->in_msglen - ( ssl->transform_in->ivlen - - ssl->transform_in->fixed_ivlen ); - dec_msglen -= 16; - dec_msg = ssl->in_msg + ( ssl->transform_in->ivlen - - ssl->transform_in->fixed_ivlen ); - dec_msg_result = ssl->in_msg; - ssl->in_msglen = dec_msglen; - - memcpy( add_data, ssl->in_ctr, 8 ); - add_data[8] = ssl->in_msgtype; - add_data[9] = ssl->major_ver; - add_data[10] = ssl->minor_ver; - add_data[11] = ( ssl->in_msglen >> 8 ) & 0xFF; - add_data[12] = ssl->in_msglen & 0xFF; - - SSL_DEBUG_BUF( 4, "additional data used for AEAD", - add_data, 13 ); - - memcpy( ssl->transform_in->iv_dec + ssl->transform_in->fixed_ivlen, - ssl->in_msg, - ssl->transform_in->ivlen - ssl->transform_in->fixed_ivlen ); - - SSL_DEBUG_BUF( 4, "IV used", ssl->transform_in->iv_dec, - ssl->transform_in->ivlen ); - SSL_DEBUG_BUF( 4, "TAG used", dec_msg + dec_msglen, 16 ); - - memcpy( ssl->transform_in->iv_dec + ssl->transform_in->fixed_ivlen, - ssl->in_msg, - ssl->transform_in->ivlen - ssl->transform_in->fixed_ivlen ); - - ret = gcm_auth_decrypt( (gcm_context *) ssl->transform_in->ctx_dec, - dec_msglen, - ssl->transform_in->iv_dec, - ssl->transform_in->ivlen, - add_data, 13, - dec_msg + dec_msglen, 16, - dec_msg, dec_msg_result ); - - if( ret != 0 ) - { - SSL_DEBUG_MSG( 1, ( "AEAD decrypt failed on validation (ret = -0x%02x)", - -ret ) ); - - return( POLARSSL_ERR_SSL_INVALID_MAC ); - } - } else -#endif - return( ret ); - } - else - { - /* - * Decrypt and check the padding - */ - unsigned char *dec_msg; - unsigned char *dec_msg_result; - size_t dec_msglen; - size_t minlen = 0; - - /* - * Check immediate ciphertext sanity - */ - if( ssl->in_msglen % ssl->transform_in->ivlen != 0 ) - { - SSL_DEBUG_MSG( 1, ( "msglen (%d) %% ivlen (%d) != 0", - ssl->in_msglen, ssl->transform_in->ivlen ) ); - return( POLARSSL_ERR_SSL_INVALID_MAC ); - } - - if( ssl->minor_ver >= SSL_MINOR_VERSION_2 ) - minlen += ssl->transform_in->ivlen; - - if( ssl->in_msglen < minlen + ssl->transform_in->ivlen || - ssl->in_msglen < minlen + ssl->transform_in->maclen + 1 ) - { - SSL_DEBUG_MSG( 1, ( "msglen (%d) < max( ivlen(%d), maclen (%d) + 1 ) ( + expl IV )", - ssl->in_msglen, ssl->transform_in->ivlen, ssl->transform_in->maclen ) ); - return( POLARSSL_ERR_SSL_INVALID_MAC ); - } - - dec_msglen = ssl->in_msglen; - dec_msg = ssl->in_msg; - dec_msg_result = ssl->in_msg; - - /* - * Initialize for prepended IV for block cipher in TLS v1.1 and up - */ - if( ssl->minor_ver >= SSL_MINOR_VERSION_2 ) - { - dec_msg += ssl->transform_in->ivlen; - dec_msglen -= ssl->transform_in->ivlen; - ssl->in_msglen -= ssl->transform_in->ivlen; - - for( i = 0; i < ssl->transform_in->ivlen; i++ ) - ssl->transform_in->iv_dec[i] = ssl->in_msg[i]; - } - - switch( ssl->transform_in->ivlen ) - { -#if defined(POLARSSL_DES_C) - case 8: -#if defined(POLARSSL_ENABLE_WEAK_CIPHERSUITES) - if( ssl->session_in->ciphersuite == TLS_RSA_WITH_DES_CBC_SHA || - ssl->session_in->ciphersuite == TLS_DHE_RSA_WITH_DES_CBC_SHA ) - { - des_crypt_cbc( (des_context *) ssl->transform_in->ctx_dec, - DES_DECRYPT, dec_msglen, - ssl->transform_in->iv_dec, dec_msg, dec_msg_result ); - } - else -#endif - des3_crypt_cbc( (des3_context *) ssl->transform_in->ctx_dec, - DES_DECRYPT, dec_msglen, - ssl->transform_in->iv_dec, dec_msg, dec_msg_result ); - break; -#endif - - case 16: -#if defined(POLARSSL_AES_C) - if ( ssl->session_in->ciphersuite == TLS_RSA_WITH_AES_128_CBC_SHA || - ssl->session_in->ciphersuite == TLS_DHE_RSA_WITH_AES_128_CBC_SHA || - ssl->session_in->ciphersuite == TLS_RSA_WITH_AES_256_CBC_SHA || - ssl->session_in->ciphersuite == TLS_DHE_RSA_WITH_AES_256_CBC_SHA || - ssl->session_in->ciphersuite == TLS_RSA_WITH_AES_128_CBC_SHA256 || - ssl->session_in->ciphersuite == TLS_RSA_WITH_AES_256_CBC_SHA256 || - ssl->session_in->ciphersuite == TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 || - ssl->session_in->ciphersuite == TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 ) - { - aes_crypt_cbc( (aes_context *) ssl->transform_in->ctx_dec, - AES_DECRYPT, dec_msglen, - ssl->transform_in->iv_dec, dec_msg, dec_msg_result ); - break; - } -#endif - -#if defined(POLARSSL_CAMELLIA_C) - if ( ssl->session_in->ciphersuite == TLS_RSA_WITH_CAMELLIA_128_CBC_SHA || - ssl->session_in->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA || - ssl->session_in->ciphersuite == TLS_RSA_WITH_CAMELLIA_256_CBC_SHA || - ssl->session_in->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA || - ssl->session_in->ciphersuite == TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 || - ssl->session_in->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 || - ssl->session_in->ciphersuite == TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 || - ssl->session_in->ciphersuite == TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 ) - { - camellia_crypt_cbc( (camellia_context *) ssl->transform_in->ctx_dec, - CAMELLIA_DECRYPT, dec_msglen, - ssl->transform_in->iv_dec, dec_msg, dec_msg_result ); - break; - } -#endif - - default: - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); - } - - padlen = 1 + ssl->in_msg[ssl->in_msglen - 1]; - - if( ssl->in_msglen < ssl->transform_in->maclen + padlen ) - { -#if defined(POLARSSL_SSL_DEBUG_ALL) - SSL_DEBUG_MSG( 1, ( "msglen (%d) < maclen (%d) + padlen (%d)", - ssl->in_msglen, ssl->transform_in->maclen, padlen ) ); -#endif - padlen = 0; - correct = 0; - } - - if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) - { - if( padlen > ssl->transform_in->ivlen ) - { -#if defined(POLARSSL_SSL_DEBUG_ALL) - SSL_DEBUG_MSG( 1, ( "bad padding length: is %d, " - "should be no more than %d", - padlen, ssl->transform_in->ivlen ) ); -#endif - correct = 0; - } - } - else - { - /* - * TLSv1+: always check the padding up to the first failure - * and fake check up to 256 bytes of padding - */ - size_t pad_count = 0, fake_pad_count = 0; - size_t padding_idx = ssl->in_msglen - padlen - 1; - - for( i = 1; i <= padlen; i++ ) - pad_count += ( ssl->in_msg[padding_idx + i] == padlen - 1 ); - - for( ; i <= 256; i++ ) - fake_pad_count += ( ssl->in_msg[padding_idx + i] == padlen - 1 ); - - correct &= ( pad_count == padlen ); /* Only 1 on correct padding */ - correct &= ( pad_count + fake_pad_count < 512 ); /* Always 1 */ - -#if defined(POLARSSL_SSL_DEBUG_ALL) - if( padlen > 0 && correct == 0) - SSL_DEBUG_MSG( 1, ( "bad padding byte detected" ) ); -#endif - padlen &= correct * 0x1FF; - } - } - - SSL_DEBUG_BUF( 4, "raw buffer after decryption", - ssl->in_msg, ssl->in_msglen ); - - /* - * Always compute the MAC (RFC4346, CBCTIME). - */ - ssl->in_msglen -= ( ssl->transform_in->maclen + padlen ); - - ssl->in_hdr[3] = (unsigned char)( ssl->in_msglen >> 8 ); - ssl->in_hdr[4] = (unsigned char)( ssl->in_msglen ); - - memcpy( tmp, ssl->in_msg + ssl->in_msglen, ssl->transform_in->maclen ); - - if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) - { - if( ssl->transform_in->maclen == 16 ) - ssl_mac_md5( ssl->transform_in->mac_dec, - ssl->in_msg, ssl->in_msglen, - ssl->in_ctr, ssl->in_msgtype ); - else if( ssl->transform_in->maclen == 20 ) - ssl_mac_sha1( ssl->transform_in->mac_dec, - ssl->in_msg, ssl->in_msglen, - ssl->in_ctr, ssl->in_msgtype ); - else if( ssl->transform_in->maclen == 32 ) - ssl_mac_sha2( ssl->transform_in->mac_dec, - ssl->in_msg, ssl->in_msglen, - ssl->in_ctr, ssl->in_msgtype ); - else if( ssl->transform_in->maclen != 0 ) - { - SSL_DEBUG_MSG( 1, ( "invalid MAC len: %d", - ssl->transform_in->maclen ) ); - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); - } - } - else - { - /* - * Process MAC and always update for padlen afterwards to make - * total time independent of padlen - * - * extra_run compensates MAC check for padlen - * - * Known timing attacks: - * - Lucky Thirteen (http://www.isg.rhul.ac.uk/tls/TLStiming.pdf) - * - * We use ( ( Lx + 8 ) / 64 ) to handle 'negative Lx' values - * correctly. (We round down instead of up, so -56 is the correct - * value for our calculations instead of -55) - */ - int j, extra_run = 0; - extra_run = ( 13 + ssl->in_msglen + padlen + 8 ) / 64 - - ( 13 + ssl->in_msglen + 8 ) / 64; - - extra_run &= correct * 0xFF; - - if( ssl->transform_in->maclen == 16 ) - { - md5_context ctx; - md5_hmac_starts( &ctx, ssl->transform_in->mac_dec, 16 ); - md5_hmac_update( &ctx, ssl->in_ctr, ssl->in_msglen + 13 ); - md5_hmac_finish( &ctx, ssl->in_msg + ssl->in_msglen ); - - for( j = 0; j < extra_run; j++ ) - md5_process( &ctx, ssl->in_msg ); - } - else if( ssl->transform_in->maclen == 20 ) - { - sha1_context ctx; - sha1_hmac_starts( &ctx, ssl->transform_in->mac_dec, 20 ); - sha1_hmac_update( &ctx, ssl->in_ctr, ssl->in_msglen + 13 ); - sha1_hmac_finish( &ctx, ssl->in_msg + ssl->in_msglen ); - - for( j = 0; j < extra_run; j++ ) - sha1_process( &ctx, ssl->in_msg ); - } - else if( ssl->transform_in->maclen == 32 ) - { - sha2_context ctx; - sha2_hmac_starts( &ctx, ssl->transform_in->mac_dec, 32, 0 ); - sha2_hmac_update( &ctx, ssl->in_ctr, ssl->in_msglen + 13 ); - sha2_hmac_finish( &ctx, ssl->in_msg + ssl->in_msglen ); - - for( j = 0; j < extra_run; j++ ) - sha2_process( &ctx, ssl->in_msg ); - } - else if( ssl->transform_in->maclen != 0 ) - { - SSL_DEBUG_MSG( 1, ( "invalid MAC len: %d", - ssl->transform_in->maclen ) ); - return( POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE ); - } - } - - SSL_DEBUG_BUF( 4, "message mac", tmp, ssl->transform_in->maclen ); - SSL_DEBUG_BUF( 4, "computed mac", ssl->in_msg + ssl->in_msglen, - ssl->transform_in->maclen ); - - if( memcmp( tmp, ssl->in_msg + ssl->in_msglen, - ssl->transform_in->maclen ) != 0 ) - { -#if defined(POLARSSL_SSL_DEBUG_ALL) - SSL_DEBUG_MSG( 1, ( "message mac does not match" ) ); -#endif - correct = 0; - } - - /* - * Finally check the correct flag - */ - if( correct == 0 ) - return( POLARSSL_ERR_SSL_INVALID_MAC ); - - if( ssl->in_msglen == 0 ) - { - ssl->nb_zero++; - - /* - * Three or more empty messages may be a DoS attack - * (excessive CPU consumption). - */ - if( ssl->nb_zero > 3 ) - { - SSL_DEBUG_MSG( 1, ( "received four consecutive empty " - "messages, possible DoS attack" ) ); - return( POLARSSL_ERR_SSL_INVALID_MAC ); - } - } - else - ssl->nb_zero = 0; - - for( i = 8; i > 0; i-- ) - if( ++ssl->in_ctr[i - 1] != 0 ) - break; - - SSL_DEBUG_MSG( 2, ( "<= decrypt buf" ) ); - - return( 0 ); -} - -#if defined(POLARSSL_ZLIB_SUPPORT) -/* - * Compression/decompression functions - */ -static int ssl_compress_buf( ssl_context *ssl ) -{ - int ret; - unsigned char *msg_post = ssl->out_msg; - size_t len_pre = ssl->out_msglen; - unsigned char *msg_pre; - - SSL_DEBUG_MSG( 2, ( "=> compress buf" ) ); - - msg_pre = (unsigned char*) malloc( len_pre ); - if( msg_pre == NULL ) - { - SSL_DEBUG_MSG( 1, ( "malloc(%d bytes) failed", len_pre ) ); - return( POLARSSL_ERR_SSL_MALLOC_FAILED ); - } - - memcpy( msg_pre, ssl->out_msg, len_pre ); - - SSL_DEBUG_MSG( 3, ( "before compression: msglen = %d, ", - ssl->out_msglen ) ); - - SSL_DEBUG_BUF( 4, "before compression: output payload", - ssl->out_msg, ssl->out_msglen ); - - ssl->transform_out->ctx_deflate.next_in = msg_pre; - ssl->transform_out->ctx_deflate.avail_in = len_pre; - ssl->transform_out->ctx_deflate.next_out = msg_post; - ssl->transform_out->ctx_deflate.avail_out = SSL_BUFFER_LEN; - - ret = deflate( &ssl->transform_out->ctx_deflate, Z_SYNC_FLUSH ); - if( ret != Z_OK ) - { - SSL_DEBUG_MSG( 1, ( "failed to perform compression (%d)", ret ) ); - return( POLARSSL_ERR_SSL_COMPRESSION_FAILED ); - } - - ssl->out_msglen = SSL_BUFFER_LEN - ssl->transform_out->ctx_deflate.avail_out; - - free( msg_pre ); - - SSL_DEBUG_MSG( 3, ( "after compression: msglen = %d, ", - ssl->out_msglen ) ); - - SSL_DEBUG_BUF( 4, "after compression: output payload", - ssl->out_msg, ssl->out_msglen ); - - SSL_DEBUG_MSG( 2, ( "<= compress buf" ) ); - - return( 0 ); -} - -static int ssl_decompress_buf( ssl_context *ssl ) -{ - int ret; - unsigned char *msg_post = ssl->in_msg; - size_t len_pre = ssl->in_msglen; - unsigned char *msg_pre; - - SSL_DEBUG_MSG( 2, ( "=> decompress buf" ) ); - - msg_pre = (unsigned char*) malloc( len_pre ); - if( msg_pre == NULL ) - { - SSL_DEBUG_MSG( 1, ( "malloc(%d bytes) failed", len_pre ) ); - return( POLARSSL_ERR_SSL_MALLOC_FAILED ); - } - - memcpy( msg_pre, ssl->in_msg, len_pre ); - - SSL_DEBUG_MSG( 3, ( "before decompression: msglen = %d, ", - ssl->in_msglen ) ); - - SSL_DEBUG_BUF( 4, "before decompression: input payload", - ssl->in_msg, ssl->in_msglen ); - - ssl->transform_in->ctx_inflate.next_in = msg_pre; - ssl->transform_in->ctx_inflate.avail_in = len_pre; - ssl->transform_in->ctx_inflate.next_out = msg_post; - ssl->transform_in->ctx_inflate.avail_out = SSL_MAX_CONTENT_LEN; - - ret = inflate( &ssl->transform_in->ctx_inflate, Z_SYNC_FLUSH ); - if( ret != Z_OK ) - { - SSL_DEBUG_MSG( 1, ( "failed to perform decompression (%d)", ret ) ); - return( POLARSSL_ERR_SSL_COMPRESSION_FAILED ); - } - - ssl->in_msglen = SSL_MAX_CONTENT_LEN - ssl->transform_in->ctx_inflate.avail_out; - - free( msg_pre ); - - SSL_DEBUG_MSG( 3, ( "after decompression: msglen = %d, ", - ssl->in_msglen ) ); - - SSL_DEBUG_BUF( 4, "after decompression: input payload", - ssl->in_msg, ssl->in_msglen ); - - SSL_DEBUG_MSG( 2, ( "<= decompress buf" ) ); - - return( 0 ); -} -#endif /* POLARSSL_ZLIB_SUPPORT */ - -/* - * Fill the input message buffer - */ -int ssl_fetch_input( ssl_context *ssl, size_t nb_want ) -{ - int ret; - size_t len; - - SSL_DEBUG_MSG( 2, ( "=> fetch input" ) ); - - while( ssl->in_left < nb_want ) - { - len = nb_want - ssl->in_left; - ret = ssl->f_recv( ssl->p_recv, ssl->in_hdr + ssl->in_left, len ); - - SSL_DEBUG_MSG( 2, ( "in_left: %d, nb_want: %d", - ssl->in_left, nb_want ) ); - SSL_DEBUG_RET( 2, "ssl->f_recv", ret ); - - if( ret == 0 ) - return( POLARSSL_ERR_SSL_CONN_EOF ); - - if( ret < 0 ) - return( ret ); - - ssl->in_left += ret; - } - - SSL_DEBUG_MSG( 2, ( "<= fetch input" ) ); - - return( 0 ); -} - -/* - * Flush any data not yet written - */ -int ssl_flush_output( ssl_context *ssl ) -{ - int ret; - unsigned char *buf; - - SSL_DEBUG_MSG( 2, ( "=> flush output" ) ); - - while( ssl->out_left > 0 ) - { - SSL_DEBUG_MSG( 2, ( "message length: %d, out_left: %d", - 5 + ssl->out_msglen, ssl->out_left ) ); - - if( ssl->out_msglen < ssl->out_left ) - { - size_t header_left = ssl->out_left - ssl->out_msglen; - - buf = ssl->out_hdr + 5 - header_left; - ret = ssl->f_send( ssl->p_send, buf, header_left ); - - SSL_DEBUG_RET( 2, "ssl->f_send (header)", ret ); - - if( ret <= 0 ) - return( ret ); - - ssl->out_left -= ret; - } - - buf = ssl->out_msg + ssl->out_msglen - ssl->out_left; - ret = ssl->f_send( ssl->p_send, buf, ssl->out_left ); - - SSL_DEBUG_RET( 2, "ssl->f_send", ret ); - - if( ret <= 0 ) - return( ret ); - - ssl->out_left -= ret; - } - - SSL_DEBUG_MSG( 2, ( "<= flush output" ) ); - - return( 0 ); -} - -/* - * Record layer functions - */ -int ssl_write_record( ssl_context *ssl ) -{ - int ret, done = 0; - size_t len = ssl->out_msglen; - - SSL_DEBUG_MSG( 2, ( "=> write record" ) ); - - if( ssl->out_msgtype == SSL_MSG_HANDSHAKE ) - { - ssl->out_msg[1] = (unsigned char)( ( len - 4 ) >> 16 ); - ssl->out_msg[2] = (unsigned char)( ( len - 4 ) >> 8 ); - ssl->out_msg[3] = (unsigned char)( ( len - 4 ) ); - - ssl->handshake->update_checksum( ssl, ssl->out_msg, len ); - } - -#if defined(POLARSSL_ZLIB_SUPPORT) - if( ssl->transform_out != NULL && - ssl->session_out->compression == SSL_COMPRESS_DEFLATE ) - { - if( ( ret = ssl_compress_buf( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_compress_buf", ret ); - return( ret ); - } - - len = ssl->out_msglen; - } -#endif /*POLARSSL_ZLIB_SUPPORT */ - -#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) - if( ssl_hw_record_write != NULL) - { - SSL_DEBUG_MSG( 2, ( "going for ssl_hw_record_write()" ) ); - - ret = ssl_hw_record_write( ssl ); - if( ret != 0 && ret != POLARSSL_ERR_SSL_HW_ACCEL_FALLTHROUGH ) - { - SSL_DEBUG_RET( 1, "ssl_hw_record_write", ret ); - return POLARSSL_ERR_SSL_HW_ACCEL_FAILED; - } - done = 1; - } -#endif - if( !done ) - { - ssl->out_hdr[0] = (unsigned char) ssl->out_msgtype; - ssl->out_hdr[1] = (unsigned char) ssl->major_ver; - ssl->out_hdr[2] = (unsigned char) ssl->minor_ver; - ssl->out_hdr[3] = (unsigned char)( len >> 8 ); - ssl->out_hdr[4] = (unsigned char)( len ); - - if( ssl->transform_out != NULL ) - { - if( ( ret = ssl_encrypt_buf( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_encrypt_buf", ret ); - return( ret ); - } - - len = ssl->out_msglen; - ssl->out_hdr[3] = (unsigned char)( len >> 8 ); - ssl->out_hdr[4] = (unsigned char)( len ); - } - - ssl->out_left = 5 + ssl->out_msglen; - - SSL_DEBUG_MSG( 3, ( "output record: msgtype = %d, " - "version = [%d:%d], msglen = %d", - ssl->out_hdr[0], ssl->out_hdr[1], ssl->out_hdr[2], - ( ssl->out_hdr[3] << 8 ) | ssl->out_hdr[4] ) ); - - SSL_DEBUG_BUF( 4, "output record header sent to network", - ssl->out_hdr, 5 ); - SSL_DEBUG_BUF( 4, "output record sent to network", - ssl->out_hdr + 32, ssl->out_msglen ); - } - - if( ( ret = ssl_flush_output( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_flush_output", ret ); - return( ret ); - } - - SSL_DEBUG_MSG( 2, ( "<= write record" ) ); - - return( 0 ); -} - -int ssl_read_record( ssl_context *ssl ) -{ - int ret, done = 0; - - SSL_DEBUG_MSG( 2, ( "=> read record" ) ); - - if( ssl->in_hslen != 0 && - ssl->in_hslen < ssl->in_msglen ) - { - /* - * Get next Handshake message in the current record - */ - ssl->in_msglen -= ssl->in_hslen; - - memmove( ssl->in_msg, ssl->in_msg + ssl->in_hslen, - ssl->in_msglen ); - - ssl->in_hslen = 4; - ssl->in_hslen += ( ssl->in_msg[2] << 8 ) | ssl->in_msg[3]; - - SSL_DEBUG_MSG( 3, ( "handshake message: msglen =" - " %d, type = %d, hslen = %d", - ssl->in_msglen, ssl->in_msg[0], ssl->in_hslen ) ); - - if( ssl->in_msglen < 4 || ssl->in_msg[1] != 0 ) - { - SSL_DEBUG_MSG( 1, ( "bad handshake length" ) ); - return( POLARSSL_ERR_SSL_INVALID_RECORD ); - } - - if( ssl->in_msglen < ssl->in_hslen ) - { - SSL_DEBUG_MSG( 1, ( "bad handshake length" ) ); - return( POLARSSL_ERR_SSL_INVALID_RECORD ); - } - - ssl->handshake->update_checksum( ssl, ssl->in_msg, ssl->in_hslen ); - - return( 0 ); - } - - ssl->in_hslen = 0; - - /* - * Read the record header and validate it - */ - if( ( ret = ssl_fetch_input( ssl, 5 ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_fetch_input", ret ); - return( ret ); - } - - ssl->in_msgtype = ssl->in_hdr[0]; - ssl->in_msglen = ( ssl->in_hdr[3] << 8 ) | ssl->in_hdr[4]; - - SSL_DEBUG_MSG( 3, ( "input record: msgtype = %d, " - "version = [%d:%d], msglen = %d", - ssl->in_hdr[0], ssl->in_hdr[1], ssl->in_hdr[2], - ( ssl->in_hdr[3] << 8 ) | ssl->in_hdr[4] ) ); - - if( ssl->in_hdr[1] != ssl->major_ver ) - { - SSL_DEBUG_MSG( 1, ( "major version mismatch" ) ); - return( POLARSSL_ERR_SSL_INVALID_RECORD ); - } - - if( ssl->in_hdr[2] > ssl->max_minor_ver ) - { - SSL_DEBUG_MSG( 1, ( "minor version mismatch" ) ); - return( POLARSSL_ERR_SSL_INVALID_RECORD ); - } - - /* - * Make sure the message length is acceptable - */ - if( ssl->transform_in == NULL ) - { - if( ssl->in_msglen < 1 || - ssl->in_msglen > SSL_MAX_CONTENT_LEN ) - { - SSL_DEBUG_MSG( 1, ( "bad message length" ) ); - return( POLARSSL_ERR_SSL_INVALID_RECORD ); - } - } - else - { - if( ssl->in_msglen < ssl->transform_in->minlen ) - { - SSL_DEBUG_MSG( 1, ( "bad message length" ) ); - return( POLARSSL_ERR_SSL_INVALID_RECORD ); - } - - if( ssl->minor_ver == SSL_MINOR_VERSION_0 && - ssl->in_msglen > ssl->transform_in->minlen + SSL_MAX_CONTENT_LEN ) - { - SSL_DEBUG_MSG( 1, ( "bad message length" ) ); - return( POLARSSL_ERR_SSL_INVALID_RECORD ); - } - - /* - * TLS encrypted messages can have up to 256 bytes of padding - */ - if( ssl->minor_ver >= SSL_MINOR_VERSION_1 && - ssl->in_msglen > ssl->transform_in->minlen + SSL_MAX_CONTENT_LEN + 256 ) - { - SSL_DEBUG_MSG( 1, ( "bad message length" ) ); - return( POLARSSL_ERR_SSL_INVALID_RECORD ); - } - } - - /* - * Read and optionally decrypt the message contents - */ - if( ( ret = ssl_fetch_input( ssl, 5 + ssl->in_msglen ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_fetch_input", ret ); - return( ret ); - } - - SSL_DEBUG_BUF( 4, "input record from network", - ssl->in_hdr, 5 + ssl->in_msglen ); - -#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) - if( ssl_hw_record_read != NULL) - { - SSL_DEBUG_MSG( 2, ( "going for ssl_hw_record_read()" ) ); - - ret = ssl_hw_record_read( ssl ); - if( ret != 0 && ret != POLARSSL_ERR_SSL_HW_ACCEL_FALLTHROUGH ) - { - SSL_DEBUG_RET( 1, "ssl_hw_record_read", ret ); - return POLARSSL_ERR_SSL_HW_ACCEL_FAILED; - } - done = 1; - } -#endif - if( !done && ssl->transform_in != NULL ) - { - if( ( ret = ssl_decrypt_buf( ssl ) ) != 0 ) - { -#if defined(POLARSSL_SSL_ALERT_MESSAGES) - if( ret == POLARSSL_ERR_SSL_INVALID_MAC ) - { - ssl_send_alert_message( ssl, - SSL_ALERT_LEVEL_FATAL, - SSL_ALERT_MSG_BAD_RECORD_MAC ); - } -#endif - SSL_DEBUG_RET( 1, "ssl_decrypt_buf", ret ); - return( ret ); - } - - SSL_DEBUG_BUF( 4, "input payload after decrypt", - ssl->in_msg, ssl->in_msglen ); - - if( ssl->in_msglen > SSL_MAX_CONTENT_LEN ) - { - SSL_DEBUG_MSG( 1, ( "bad message length" ) ); - return( POLARSSL_ERR_SSL_INVALID_RECORD ); - } - } - -#if defined(POLARSSL_ZLIB_SUPPORT) - if( ssl->transform_in != NULL && - ssl->session_in->compression == SSL_COMPRESS_DEFLATE ) - { - if( ( ret = ssl_decompress_buf( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_decompress_buf", ret ); - return( ret ); - } - - ssl->in_hdr[3] = (unsigned char)( ssl->in_msglen >> 8 ); - ssl->in_hdr[4] = (unsigned char)( ssl->in_msglen ); - } -#endif /* POLARSSL_ZLIB_SUPPORT */ - - if( ssl->in_msgtype != SSL_MSG_HANDSHAKE && - ssl->in_msgtype != SSL_MSG_ALERT && - ssl->in_msgtype != SSL_MSG_CHANGE_CIPHER_SPEC && - ssl->in_msgtype != SSL_MSG_APPLICATION_DATA ) - { - SSL_DEBUG_MSG( 1, ( "unknown record type" ) ); - - if( ( ret = ssl_send_alert_message( ssl, - SSL_ALERT_LEVEL_FATAL, - SSL_ALERT_MSG_UNEXPECTED_MESSAGE ) ) != 0 ) - { - return( ret ); - } - - return( POLARSSL_ERR_SSL_INVALID_RECORD ); - } - - if( ssl->in_msgtype == SSL_MSG_HANDSHAKE ) - { - ssl->in_hslen = 4; - ssl->in_hslen += ( ssl->in_msg[2] << 8 ) | ssl->in_msg[3]; - - SSL_DEBUG_MSG( 3, ( "handshake message: msglen =" - " %d, type = %d, hslen = %d", - ssl->in_msglen, ssl->in_msg[0], ssl->in_hslen ) ); - - /* - * Additional checks to validate the handshake header - */ - if( ssl->in_msglen < 4 || ssl->in_msg[1] != 0 ) - { - SSL_DEBUG_MSG( 1, ( "bad handshake length" ) ); - return( POLARSSL_ERR_SSL_INVALID_RECORD ); - } - - if( ssl->in_msglen < ssl->in_hslen ) - { - SSL_DEBUG_MSG( 1, ( "bad handshake length" ) ); - return( POLARSSL_ERR_SSL_INVALID_RECORD ); - } - - if( ssl->state != SSL_HANDSHAKE_OVER ) - ssl->handshake->update_checksum( ssl, ssl->in_msg, ssl->in_hslen ); - } - - if( ssl->in_msgtype == SSL_MSG_ALERT ) - { - SSL_DEBUG_MSG( 2, ( "got an alert message, type: [%d:%d]", - ssl->in_msg[0], ssl->in_msg[1] ) ); - - /* - * Ignore non-fatal alerts, except close_notify - */ - if( ssl->in_msg[0] == SSL_ALERT_LEVEL_FATAL ) - { - SSL_DEBUG_MSG( 1, ( "is a fatal alert message (msg %d)", - ssl->in_msg[1] ) ); - /** - * Subtract from error code as ssl->in_msg[1] is 7-bit positive - * error identifier. - */ - return( POLARSSL_ERR_SSL_FATAL_ALERT_MESSAGE ); - } - - if( ssl->in_msg[0] == SSL_ALERT_LEVEL_WARNING && - ssl->in_msg[1] == SSL_ALERT_MSG_CLOSE_NOTIFY ) - { - SSL_DEBUG_MSG( 2, ( "is a close notify message" ) ); - return( POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY ); - } - } - - ssl->in_left = 0; - - SSL_DEBUG_MSG( 2, ( "<= read record" ) ); - - return( 0 ); -} - -int ssl_send_fatal_handshake_failure( ssl_context *ssl ) -{ - int ret; - - if( ( ret = ssl_send_alert_message( ssl, - SSL_ALERT_LEVEL_FATAL, - SSL_ALERT_MSG_HANDSHAKE_FAILURE ) ) != 0 ) - { - return( ret ); - } - - return( 0 ); -} - -int ssl_send_alert_message( ssl_context *ssl, - unsigned char level, - unsigned char message ) -{ - int ret; - - SSL_DEBUG_MSG( 2, ( "=> send alert message" ) ); - - ssl->out_msgtype = SSL_MSG_ALERT; - ssl->out_msglen = 2; - ssl->out_msg[0] = level; - ssl->out_msg[1] = message; - - if( ( ret = ssl_write_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_write_record", ret ); - return( ret ); - } - - SSL_DEBUG_MSG( 2, ( "<= send alert message" ) ); - - return( 0 ); -} - -/* - * Handshake functions - */ -int ssl_write_certificate( ssl_context *ssl ) -{ - int ret; - size_t i, n; - const x509_cert *crt; - - SSL_DEBUG_MSG( 2, ( "=> write certificate" ) ); - - if( ssl->endpoint == SSL_IS_CLIENT ) - { - if( ssl->client_auth == 0 ) - { - SSL_DEBUG_MSG( 2, ( "<= skip write certificate" ) ); - ssl->state++; - return( 0 ); - } - - /* - * If using SSLv3 and got no cert, send an Alert message - * (otherwise an empty Certificate message will be sent). - */ - if( ssl->own_cert == NULL && - ssl->minor_ver == SSL_MINOR_VERSION_0 ) - { - ssl->out_msglen = 2; - ssl->out_msgtype = SSL_MSG_ALERT; - ssl->out_msg[0] = SSL_ALERT_LEVEL_WARNING; - ssl->out_msg[1] = SSL_ALERT_MSG_NO_CERT; - - SSL_DEBUG_MSG( 2, ( "got no certificate to send" ) ); - goto write_msg; - } - } - else /* SSL_IS_SERVER */ - { - if( ssl->own_cert == NULL ) - { - SSL_DEBUG_MSG( 1, ( "got no certificate to send" ) ); - return( POLARSSL_ERR_SSL_CERTIFICATE_REQUIRED ); - } - } - - SSL_DEBUG_CRT( 3, "own certificate", ssl->own_cert ); - - /* - * 0 . 0 handshake type - * 1 . 3 handshake length - * 4 . 6 length of all certs - * 7 . 9 length of cert. 1 - * 10 . n-1 peer certificate - * n . n+2 length of cert. 2 - * n+3 . ... upper level cert, etc. - */ - i = 7; - crt = ssl->own_cert; - - while( crt != NULL ) - { - n = crt->raw.len; - if( i + 3 + n > SSL_MAX_CONTENT_LEN ) - { - SSL_DEBUG_MSG( 1, ( "certificate too large, %d > %d", - i + 3 + n, SSL_MAX_CONTENT_LEN ) ); - return( POLARSSL_ERR_SSL_CERTIFICATE_TOO_LARGE ); - } - - ssl->out_msg[i ] = (unsigned char)( n >> 16 ); - ssl->out_msg[i + 1] = (unsigned char)( n >> 8 ); - ssl->out_msg[i + 2] = (unsigned char)( n ); - - i += 3; memcpy( ssl->out_msg + i, crt->raw.p, n ); - i += n; crt = crt->next; - } - - ssl->out_msg[4] = (unsigned char)( ( i - 7 ) >> 16 ); - ssl->out_msg[5] = (unsigned char)( ( i - 7 ) >> 8 ); - ssl->out_msg[6] = (unsigned char)( ( i - 7 ) ); - - ssl->out_msglen = i; - ssl->out_msgtype = SSL_MSG_HANDSHAKE; - ssl->out_msg[0] = SSL_HS_CERTIFICATE; - -write_msg: - - ssl->state++; - - if( ( ret = ssl_write_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_write_record", ret ); - return( ret ); - } - - SSL_DEBUG_MSG( 2, ( "<= write certificate" ) ); - - return( 0 ); -} - -int ssl_parse_certificate( ssl_context *ssl ) -{ - int ret; - size_t i, n; - - SSL_DEBUG_MSG( 2, ( "=> parse certificate" ) ); - - if( ssl->endpoint == SSL_IS_SERVER && - ssl->authmode == SSL_VERIFY_NONE ) - { - ssl->verify_result = BADCERT_SKIP_VERIFY; - SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) ); - ssl->state++; - return( 0 ); - } - - if( ( ret = ssl_read_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_read_record", ret ); - return( ret ); - } - - ssl->state++; - - /* - * Check if the client sent an empty certificate - */ - if( ssl->endpoint == SSL_IS_SERVER && - ssl->minor_ver == SSL_MINOR_VERSION_0 ) - { - if( ssl->in_msglen == 2 && - ssl->in_msgtype == SSL_MSG_ALERT && - ssl->in_msg[0] == SSL_ALERT_LEVEL_WARNING && - ssl->in_msg[1] == SSL_ALERT_MSG_NO_CERT ) - { - SSL_DEBUG_MSG( 1, ( "SSLv3 client has no certificate" ) ); - - ssl->verify_result = BADCERT_MISSING; - if( ssl->authmode == SSL_VERIFY_OPTIONAL ) - return( 0 ); - else - return( POLARSSL_ERR_SSL_NO_CLIENT_CERTIFICATE ); - } - } - - if( ssl->endpoint == SSL_IS_SERVER && - ssl->minor_ver != SSL_MINOR_VERSION_0 ) - { - if( ssl->in_hslen == 7 && - ssl->in_msgtype == SSL_MSG_HANDSHAKE && - ssl->in_msg[0] == SSL_HS_CERTIFICATE && - memcmp( ssl->in_msg + 4, "\0\0\0", 3 ) == 0 ) - { - SSL_DEBUG_MSG( 1, ( "TLSv1 client has no certificate" ) ); - - ssl->verify_result = BADCERT_MISSING; - if( ssl->authmode == SSL_VERIFY_REQUIRED ) - return( POLARSSL_ERR_SSL_NO_CLIENT_CERTIFICATE ); - else - return( 0 ); - } - } - - if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "bad certificate message" ) ); - return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); - } - - if( ssl->in_msg[0] != SSL_HS_CERTIFICATE || ssl->in_hslen < 10 ) - { - SSL_DEBUG_MSG( 1, ( "bad certificate message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE ); - } - - /* - * Same message structure as in ssl_write_certificate() - */ - n = ( ssl->in_msg[5] << 8 ) | ssl->in_msg[6]; - - if( ssl->in_msg[4] != 0 || ssl->in_hslen != 7 + n ) - { - SSL_DEBUG_MSG( 1, ( "bad certificate message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE ); - } - - if( ( ssl->session_negotiate->peer_cert = (x509_cert *) malloc( - sizeof( x509_cert ) ) ) == NULL ) - { - SSL_DEBUG_MSG( 1, ( "malloc(%d bytes) failed", - sizeof( x509_cert ) ) ); - return( POLARSSL_ERR_SSL_MALLOC_FAILED ); - } - - memset( ssl->session_negotiate->peer_cert, 0, sizeof( x509_cert ) ); - - i = 7; - - while( i < ssl->in_hslen ) - { - if( ssl->in_msg[i] != 0 ) - { - SSL_DEBUG_MSG( 1, ( "bad certificate message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE ); - } - - n = ( (unsigned int) ssl->in_msg[i + 1] << 8 ) - | (unsigned int) ssl->in_msg[i + 2]; - i += 3; - - if( n < 128 || i + n > ssl->in_hslen ) - { - SSL_DEBUG_MSG( 1, ( "bad certificate message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE ); - } - - ret = x509parse_crt_der( ssl->session_negotiate->peer_cert, - ssl->in_msg + i, n ); - if( ret != 0 ) - { - SSL_DEBUG_RET( 1, " x509parse_crt", ret ); - return( ret ); - } - - i += n; - } - - SSL_DEBUG_CRT( 3, "peer certificate", ssl->session_negotiate->peer_cert ); - - if( ssl->authmode != SSL_VERIFY_NONE ) - { - if( ssl->ca_chain == NULL ) - { - SSL_DEBUG_MSG( 1, ( "got no CA chain" ) ); - return( POLARSSL_ERR_SSL_CA_CHAIN_REQUIRED ); - } - - ret = x509parse_verify( ssl->session_negotiate->peer_cert, - ssl->ca_chain, ssl->ca_crl, - ssl->peer_cn, &ssl->verify_result, - ssl->f_vrfy, ssl->p_vrfy ); - - if( ret != 0 ) - SSL_DEBUG_RET( 1, "x509_verify_cert", ret ); - - if( ssl->authmode != SSL_VERIFY_REQUIRED ) - ret = 0; - } - - SSL_DEBUG_MSG( 2, ( "<= parse certificate" ) ); - - return( ret ); -} - -int ssl_write_change_cipher_spec( ssl_context *ssl ) -{ - int ret; - - SSL_DEBUG_MSG( 2, ( "=> write change cipher spec" ) ); - - ssl->out_msgtype = SSL_MSG_CHANGE_CIPHER_SPEC; - ssl->out_msglen = 1; - ssl->out_msg[0] = 1; - - ssl->state++; - - if( ( ret = ssl_write_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_write_record", ret ); - return( ret ); - } - - SSL_DEBUG_MSG( 2, ( "<= write change cipher spec" ) ); - - return( 0 ); -} - -int ssl_parse_change_cipher_spec( ssl_context *ssl ) -{ - int ret; - - SSL_DEBUG_MSG( 2, ( "=> parse change cipher spec" ) ); - - if( ( ret = ssl_read_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_read_record", ret ); - return( ret ); - } - - if( ssl->in_msgtype != SSL_MSG_CHANGE_CIPHER_SPEC ) - { - SSL_DEBUG_MSG( 1, ( "bad change cipher spec message" ) ); - return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); - } - - if( ssl->in_msglen != 1 || ssl->in_msg[0] != 1 ) - { - SSL_DEBUG_MSG( 1, ( "bad change cipher spec message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC ); - } - - ssl->state++; - - SSL_DEBUG_MSG( 2, ( "<= parse change cipher spec" ) ); - - return( 0 ); -} - -void ssl_optimize_checksum( ssl_context *ssl, int ciphersuite ) -{ -#if !defined(POLARSSL_SHA4_C) - ((void) ciphersuite); -#endif - - if( ssl->minor_ver < SSL_MINOR_VERSION_3 ) - ssl->handshake->update_checksum = ssl_update_checksum_md5sha1; -#if defined(POLARSSL_SHA4_C) - else if ( ciphersuite == TLS_RSA_WITH_AES_256_GCM_SHA384 || - ciphersuite == TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ) - { - ssl->handshake->update_checksum = ssl_update_checksum_sha384; - } -#endif - else - ssl->handshake->update_checksum = ssl_update_checksum_sha256; -} - -static void ssl_update_checksum_start( ssl_context *ssl, unsigned char *buf, - size_t len ) -{ - md5_update( &ssl->handshake->fin_md5 , buf, len ); - sha1_update( &ssl->handshake->fin_sha1, buf, len ); - sha2_update( &ssl->handshake->fin_sha2, buf, len ); -#if defined(POLARSSL_SHA4_C) - sha4_update( &ssl->handshake->fin_sha4, buf, len ); -#endif -} - -static void ssl_update_checksum_md5sha1( ssl_context *ssl, unsigned char *buf, - size_t len ) -{ - md5_update( &ssl->handshake->fin_md5 , buf, len ); - sha1_update( &ssl->handshake->fin_sha1, buf, len ); -} - -static void ssl_update_checksum_sha256( ssl_context *ssl, unsigned char *buf, - size_t len ) -{ - sha2_update( &ssl->handshake->fin_sha2, buf, len ); -} - -#if defined(POLARSSL_SHA4_C) -static void ssl_update_checksum_sha384( ssl_context *ssl, unsigned char *buf, - size_t len ) -{ - sha4_update( &ssl->handshake->fin_sha4, buf, len ); -} -#endif - -static void ssl_calc_finished_ssl( - ssl_context *ssl, unsigned char *buf, int from ) -{ - const char *sender; - md5_context md5; - sha1_context sha1; - - unsigned char padbuf[48]; - unsigned char md5sum[16]; - unsigned char sha1sum[20]; - - ssl_session *session = ssl->session_negotiate; - if( !session ) - session = ssl->session; - - SSL_DEBUG_MSG( 2, ( "=> calc finished ssl" ) ); - - memcpy( &md5 , &ssl->handshake->fin_md5 , sizeof(md5_context) ); - memcpy( &sha1, &ssl->handshake->fin_sha1, sizeof(sha1_context) ); - - /* - * SSLv3: - * hash = - * MD5( master + pad2 + - * MD5( handshake + sender + master + pad1 ) ) - * + SHA1( master + pad2 + - * SHA1( handshake + sender + master + pad1 ) ) - */ - -#if !defined(POLARSSL_MD5_ALT) - SSL_DEBUG_BUF( 4, "finished md5 state", (unsigned char *) - md5.state, sizeof( md5.state ) ); -#endif - -#if !defined(POLARSSL_SHA1_ALT) - SSL_DEBUG_BUF( 4, "finished sha1 state", (unsigned char *) - sha1.state, sizeof( sha1.state ) ); -#endif - - sender = ( from == SSL_IS_CLIENT ) ? "CLNT" - : "SRVR"; - - memset( padbuf, 0x36, 48 ); - - md5_update( &md5, (const unsigned char *) sender, 4 ); - md5_update( &md5, session->master, 48 ); - md5_update( &md5, padbuf, 48 ); - md5_finish( &md5, md5sum ); - - sha1_update( &sha1, (const unsigned char *) sender, 4 ); - sha1_update( &sha1, session->master, 48 ); - sha1_update( &sha1, padbuf, 40 ); - sha1_finish( &sha1, sha1sum ); - - memset( padbuf, 0x5C, 48 ); - - md5_starts( &md5 ); - md5_update( &md5, session->master, 48 ); - md5_update( &md5, padbuf, 48 ); - md5_update( &md5, md5sum, 16 ); - md5_finish( &md5, buf ); - - sha1_starts( &sha1 ); - sha1_update( &sha1, session->master, 48 ); - sha1_update( &sha1, padbuf , 40 ); - sha1_update( &sha1, sha1sum, 20 ); - sha1_finish( &sha1, buf + 16 ); - - SSL_DEBUG_BUF( 3, "calc finished result", buf, 36 ); - - memset( &md5, 0, sizeof( md5_context ) ); - memset( &sha1, 0, sizeof( sha1_context ) ); - - memset( padbuf, 0, sizeof( padbuf ) ); - memset( md5sum, 0, sizeof( md5sum ) ); - memset( sha1sum, 0, sizeof( sha1sum ) ); - - SSL_DEBUG_MSG( 2, ( "<= calc finished" ) ); -} - -static void ssl_calc_finished_tls( - ssl_context *ssl, unsigned char *buf, int from ) -{ - int len = 12; - const char *sender; - md5_context md5; - sha1_context sha1; - unsigned char padbuf[36]; - - ssl_session *session = ssl->session_negotiate; - if( !session ) - session = ssl->session; - - SSL_DEBUG_MSG( 2, ( "=> calc finished tls" ) ); - - memcpy( &md5 , &ssl->handshake->fin_md5 , sizeof(md5_context) ); - memcpy( &sha1, &ssl->handshake->fin_sha1, sizeof(sha1_context) ); - - /* - * TLSv1: - * hash = PRF( master, finished_label, - * MD5( handshake ) + SHA1( handshake ) )[0..11] - */ - -#if !defined(POLARSSL_MD5_ALT) - SSL_DEBUG_BUF( 4, "finished md5 state", (unsigned char *) - md5.state, sizeof( md5.state ) ); -#endif - -#if !defined(POLARSSL_SHA1_ALT) - SSL_DEBUG_BUF( 4, "finished sha1 state", (unsigned char *) - sha1.state, sizeof( sha1.state ) ); -#endif - - sender = ( from == SSL_IS_CLIENT ) - ? "client finished" - : "server finished"; - - md5_finish( &md5, padbuf ); - sha1_finish( &sha1, padbuf + 16 ); - - ssl->handshake->tls_prf( session->master, 48, (char *) sender, - padbuf, 36, buf, len ); - - SSL_DEBUG_BUF( 3, "calc finished result", buf, len ); - - memset( &md5, 0, sizeof( md5_context ) ); - memset( &sha1, 0, sizeof( sha1_context ) ); - - memset( padbuf, 0, sizeof( padbuf ) ); - - SSL_DEBUG_MSG( 2, ( "<= calc finished" ) ); -} - -static void ssl_calc_finished_tls_sha256( - ssl_context *ssl, unsigned char *buf, int from ) -{ - int len = 12; - const char *sender; - sha2_context sha2; - unsigned char padbuf[32]; - - ssl_session *session = ssl->session_negotiate; - if( !session ) - session = ssl->session; - - SSL_DEBUG_MSG( 2, ( "=> calc finished tls sha256" ) ); - - memcpy( &sha2, &ssl->handshake->fin_sha2, sizeof(sha2_context) ); - - /* - * TLSv1.2: - * hash = PRF( master, finished_label, - * Hash( handshake ) )[0.11] - */ - -#if !defined(POLARSSL_SHA2_ALT) - SSL_DEBUG_BUF( 4, "finished sha2 state", (unsigned char *) - sha2.state, sizeof( sha2.state ) ); -#endif - - sender = ( from == SSL_IS_CLIENT ) - ? "client finished" - : "server finished"; - - sha2_finish( &sha2, padbuf ); - - ssl->handshake->tls_prf( session->master, 48, (char *) sender, - padbuf, 32, buf, len ); - - SSL_DEBUG_BUF( 3, "calc finished result", buf, len ); - - memset( &sha2, 0, sizeof( sha2_context ) ); - - memset( padbuf, 0, sizeof( padbuf ) ); - - SSL_DEBUG_MSG( 2, ( "<= calc finished" ) ); -} - -#if defined(POLARSSL_SHA4_C) -static void ssl_calc_finished_tls_sha384( - ssl_context *ssl, unsigned char *buf, int from ) -{ - int len = 12; - const char *sender; - sha4_context sha4; - unsigned char padbuf[48]; - - ssl_session *session = ssl->session_negotiate; - if( !session ) - session = ssl->session; - - SSL_DEBUG_MSG( 2, ( "=> calc finished tls sha384" ) ); - - memcpy( &sha4, &ssl->handshake->fin_sha4, sizeof(sha4_context) ); - - /* - * TLSv1.2: - * hash = PRF( master, finished_label, - * Hash( handshake ) )[0.11] - */ - -#if !defined(POLARSSL_SHA4_ALT) - SSL_DEBUG_BUF( 4, "finished sha4 state", (unsigned char *) - sha4.state, sizeof( sha4.state ) ); -#endif - - sender = ( from == SSL_IS_CLIENT ) - ? "client finished" - : "server finished"; - - sha4_finish( &sha4, padbuf ); - - ssl->handshake->tls_prf( session->master, 48, (char *) sender, - padbuf, 48, buf, len ); - - SSL_DEBUG_BUF( 3, "calc finished result", buf, len ); - - memset( &sha4, 0, sizeof( sha4_context ) ); - - memset( padbuf, 0, sizeof( padbuf ) ); - - SSL_DEBUG_MSG( 2, ( "<= calc finished" ) ); -} -#endif - -void ssl_handshake_wrapup( ssl_context *ssl ) -{ - SSL_DEBUG_MSG( 3, ( "=> handshake wrapup" ) ); - - /* - * Free our handshake params - */ - ssl_handshake_free( ssl->handshake ); - free( ssl->handshake ); - ssl->handshake = NULL; - - /* - * Switch in our now active transform context - */ - if( ssl->transform ) - { - ssl_transform_free( ssl->transform ); - free( ssl->transform ); - } - ssl->transform = ssl->transform_negotiate; - ssl->transform_negotiate = NULL; - - if( ssl->session ) - { - ssl_session_free( ssl->session ); - free( ssl->session ); - } - ssl->session = ssl->session_negotiate; - ssl->session_negotiate = NULL; - - /* - * Add cache entry - */ - if( ssl->f_set_cache != NULL ) - if( ssl->f_set_cache( ssl->p_set_cache, ssl->session ) != 0 ) - SSL_DEBUG_MSG( 1, ( "cache did not store session" ) ); - - ssl->state++; - - SSL_DEBUG_MSG( 3, ( "<= handshake wrapup" ) ); -} - -int ssl_write_finished( ssl_context *ssl ) -{ - int ret, hash_len; - - SSL_DEBUG_MSG( 2, ( "=> write finished" ) ); - - ssl->handshake->calc_finished( ssl, ssl->out_msg + 4, ssl->endpoint ); - - // TODO TLS/1.2 Hash length is determined by cipher suite (Page 63) - hash_len = ( ssl->minor_ver == SSL_MINOR_VERSION_0 ) ? 36 : 12; - - ssl->verify_data_len = hash_len; - memcpy( ssl->own_verify_data, ssl->out_msg + 4, hash_len ); - - ssl->out_msglen = 4 + hash_len; - ssl->out_msgtype = SSL_MSG_HANDSHAKE; - ssl->out_msg[0] = SSL_HS_FINISHED; - - /* - * In case of session resuming, invert the client and server - * ChangeCipherSpec messages order. - */ - if( ssl->handshake->resume != 0 ) - { - if( ssl->endpoint == SSL_IS_CLIENT ) - ssl->state = SSL_HANDSHAKE_WRAPUP; - else - ssl->state = SSL_CLIENT_CHANGE_CIPHER_SPEC; - } - else - ssl->state++; - - /* - * Switch to our negotiated transform and session parameters for outbound data. - */ - SSL_DEBUG_MSG( 3, ( "switching to new transform spec for outbound data" ) ); - ssl->transform_out = ssl->transform_negotiate; - ssl->session_out = ssl->session_negotiate; - memset( ssl->out_ctr, 0, 8 ); - - if( ( ret = ssl_write_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_write_record", ret ); - return( ret ); - } - - SSL_DEBUG_MSG( 2, ( "<= write finished" ) ); - - return( 0 ); -} - -int ssl_parse_finished( ssl_context *ssl ) -{ - int ret; - unsigned int hash_len; - unsigned char buf[36]; - - SSL_DEBUG_MSG( 2, ( "=> parse finished" ) ); - - ssl->handshake->calc_finished( ssl, buf, ssl->endpoint ^ 1 ); - - /* - * Switch to our negotiated transform and session parameters for inbound data. - */ - SSL_DEBUG_MSG( 3, ( "switching to new transform spec for inbound data" ) ); - ssl->transform_in = ssl->transform_negotiate; - ssl->session_in = ssl->session_negotiate; - memset( ssl->in_ctr, 0, 8 ); - - if( ( ret = ssl_read_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_read_record", ret ); - return( ret ); - } - - if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "bad finished message" ) ); - return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); - } - - // TODO TLS/1.2 Hash length is determined by cipher suite (Page 63) - hash_len = ( ssl->minor_ver == SSL_MINOR_VERSION_0 ) ? 36 : 12; - - if( ssl->in_msg[0] != SSL_HS_FINISHED || - ssl->in_hslen != 4 + hash_len ) - { - SSL_DEBUG_MSG( 1, ( "bad finished message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_FINISHED ); - } - - if( memcmp( ssl->in_msg + 4, buf, hash_len ) != 0 ) - { - SSL_DEBUG_MSG( 1, ( "bad finished message" ) ); - return( POLARSSL_ERR_SSL_BAD_HS_FINISHED ); - } - - ssl->verify_data_len = hash_len; - memcpy( ssl->peer_verify_data, buf, hash_len ); - - if( ssl->handshake->resume != 0 ) - { - if( ssl->endpoint == SSL_IS_CLIENT ) - ssl->state = SSL_CLIENT_CHANGE_CIPHER_SPEC; - - if( ssl->endpoint == SSL_IS_SERVER ) - ssl->state = SSL_HANDSHAKE_WRAPUP; - } - else - ssl->state++; - - SSL_DEBUG_MSG( 2, ( "<= parse finished" ) ); - - return( 0 ); -} - -int ssl_handshake_init( ssl_context *ssl ) -{ - if( ssl->transform_negotiate ) - ssl_transform_free( ssl->transform_negotiate ); - else - ssl->transform_negotiate = malloc( sizeof(ssl_transform) ); - - if( ssl->session_negotiate ) - ssl_session_free( ssl->session_negotiate ); - else - ssl->session_negotiate = malloc( sizeof(ssl_session) ); - - if( ssl->handshake ) - ssl_handshake_free( ssl->handshake ); - else - ssl->handshake = malloc( sizeof(ssl_handshake_params) ); - - if( ssl->handshake == NULL || - ssl->transform_negotiate == NULL || - ssl->session_negotiate == NULL ) - { - SSL_DEBUG_MSG( 1, ( "malloc() of ssl sub-contexts failed" ) ); - return( POLARSSL_ERR_SSL_MALLOC_FAILED ); - } - - memset( ssl->handshake, 0, sizeof(ssl_handshake_params) ); - memset( ssl->transform_negotiate, 0, sizeof(ssl_transform) ); - memset( ssl->session_negotiate, 0, sizeof(ssl_session) ); - - md5_starts( &ssl->handshake->fin_md5 ); - sha1_starts( &ssl->handshake->fin_sha1 ); - sha2_starts( &ssl->handshake->fin_sha2, 0 ); -#if defined(POLARSSL_SHA4_C) - sha4_starts( &ssl->handshake->fin_sha4, 1 ); -#endif - - ssl->handshake->update_checksum = ssl_update_checksum_start; - ssl->handshake->sig_alg = SSL_HASH_SHA1; - - return( 0 ); -} - -/* - * Initialize an SSL context - */ -int ssl_init( ssl_context *ssl ) -{ - int ret; - int len = SSL_BUFFER_LEN; - - memset( ssl, 0, sizeof( ssl_context ) ); - - /* - * Sane defaults - */ - ssl->rsa_decrypt = ssl_rsa_decrypt; - ssl->rsa_sign = ssl_rsa_sign; - ssl->rsa_key_len = ssl_rsa_key_len; - - ssl->min_major_ver = SSL_MAJOR_VERSION_3; - ssl->min_minor_ver = SSL_MINOR_VERSION_0; - - ssl->ciphersuites = malloc( sizeof(int *) * 4 ); - ssl_set_ciphersuites( ssl, ssl_default_ciphersuites ); - -#if defined(POLARSSL_DHM_C) - if( ( ret = mpi_read_string( &ssl->dhm_P, 16, - POLARSSL_DHM_RFC5114_MODP_1024_P) ) != 0 || - ( ret = mpi_read_string( &ssl->dhm_G, 16, - POLARSSL_DHM_RFC5114_MODP_1024_G) ) != 0 ) - { - SSL_DEBUG_RET( 1, "mpi_read_string", ret ); - return( ret ); - } -#endif - - /* - * Prepare base structures - */ - ssl->in_ctr = (unsigned char *) malloc( len ); - ssl->in_hdr = ssl->in_ctr + 8; - ssl->in_msg = ssl->in_ctr + 13; - - if( ssl->in_ctr == NULL ) - { - SSL_DEBUG_MSG( 1, ( "malloc(%d bytes) failed", len ) ); - return( POLARSSL_ERR_SSL_MALLOC_FAILED ); - } - - ssl->out_ctr = (unsigned char *) malloc( len ); - ssl->out_hdr = ssl->out_ctr + 8; - ssl->out_msg = ssl->out_ctr + 40; - - if( ssl->out_ctr == NULL ) - { - SSL_DEBUG_MSG( 1, ( "malloc(%d bytes) failed", len ) ); - free( ssl-> in_ctr ); - return( POLARSSL_ERR_SSL_MALLOC_FAILED ); - } - - memset( ssl-> in_ctr, 0, SSL_BUFFER_LEN ); - memset( ssl->out_ctr, 0, SSL_BUFFER_LEN ); - - ssl->hostname = NULL; - ssl->hostname_len = 0; - - if( ( ret = ssl_handshake_init( ssl ) ) != 0 ) - return( ret ); - - return( 0 ); -} - -/* - * Reset an initialized and used SSL context for re-use while retaining - * all application-set variables, function pointers and data. - */ -int ssl_session_reset( ssl_context *ssl ) -{ - int ret; - - ssl->state = SSL_HELLO_REQUEST; - ssl->renegotiation = SSL_INITIAL_HANDSHAKE; - ssl->secure_renegotiation = SSL_LEGACY_RENEGOTIATION; - - ssl->verify_data_len = 0; - memset( ssl->own_verify_data, 0, 36 ); - memset( ssl->peer_verify_data, 0, 36 ); - - ssl->in_offt = NULL; - - ssl->in_msgtype = 0; - ssl->in_msglen = 0; - ssl->in_left = 0; - - ssl->in_hslen = 0; - ssl->nb_zero = 0; - - ssl->out_msgtype = 0; - ssl->out_msglen = 0; - ssl->out_left = 0; - - ssl->transform_in = NULL; - ssl->transform_out = NULL; - - memset( ssl->out_ctr, 0, SSL_BUFFER_LEN ); - memset( ssl->in_ctr, 0, SSL_BUFFER_LEN ); - -#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) - if( ssl_hw_record_reset != NULL) - { - SSL_DEBUG_MSG( 2, ( "going for ssl_hw_record_reset()" ) ); - if( ssl_hw_record_reset( ssl ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_hw_record_reset", ret ); - return( POLARSSL_ERR_SSL_HW_ACCEL_FAILED ); - } - } -#endif - - if( ssl->transform ) - { - ssl_transform_free( ssl->transform ); - free( ssl->transform ); - ssl->transform = NULL; - } - - if( ssl->session ) - { - ssl_session_free( ssl->session ); - free( ssl->session ); - ssl->session = NULL; - } - - if( ( ret = ssl_handshake_init( ssl ) ) != 0 ) - return( ret ); - - return( 0 ); -} - -/* - * SSL set accessors - */ -void ssl_set_endpoint( ssl_context *ssl, int endpoint ) -{ - ssl->endpoint = endpoint; -} - -void ssl_set_authmode( ssl_context *ssl, int authmode ) -{ - ssl->authmode = authmode; -} - -void ssl_set_verify( ssl_context *ssl, - int (*f_vrfy)(void *, x509_cert *, int, int *), - void *p_vrfy ) -{ - ssl->f_vrfy = f_vrfy; - ssl->p_vrfy = p_vrfy; -} - -void ssl_set_rng( ssl_context *ssl, - int (*f_rng)(void *, unsigned char *, size_t), - void *p_rng ) -{ - ssl->f_rng = f_rng; - ssl->p_rng = p_rng; -} - -void ssl_set_dbg( ssl_context *ssl, - void (*f_dbg)(void *, int, const char *), - void *p_dbg ) -{ - ssl->f_dbg = f_dbg; - ssl->p_dbg = p_dbg; -} - -void ssl_set_bio( ssl_context *ssl, - int (*f_recv)(void *, unsigned char *, size_t), void *p_recv, - int (*f_send)(void *, const unsigned char *, size_t), void *p_send ) -{ - ssl->f_recv = f_recv; - ssl->f_send = f_send; - ssl->p_recv = p_recv; - ssl->p_send = p_send; -} - -void ssl_set_session_cache( ssl_context *ssl, - int (*f_get_cache)(void *, ssl_session *), void *p_get_cache, - int (*f_set_cache)(void *, const ssl_session *), void *p_set_cache ) -{ - ssl->f_get_cache = f_get_cache; - ssl->p_get_cache = p_get_cache; - ssl->f_set_cache = f_set_cache; - ssl->p_set_cache = p_set_cache; -} - -void ssl_set_session( ssl_context *ssl, const ssl_session *session ) -{ - memcpy( ssl->session_negotiate, session, sizeof(ssl_session) ); - ssl->handshake->resume = 1; -} - -void ssl_set_ciphersuites( ssl_context *ssl, const int *ciphersuites ) -{ - ssl->ciphersuites[SSL_MINOR_VERSION_0] = ciphersuites; - ssl->ciphersuites[SSL_MINOR_VERSION_1] = ciphersuites; - ssl->ciphersuites[SSL_MINOR_VERSION_2] = ciphersuites; - ssl->ciphersuites[SSL_MINOR_VERSION_3] = ciphersuites; -} - -void ssl_set_ciphersuites_for_version( ssl_context *ssl, const int *ciphersuites, - int major, int minor ) -{ - if( major != SSL_MAJOR_VERSION_3 ) - return; - - if( minor < SSL_MINOR_VERSION_0 || minor > SSL_MINOR_VERSION_3 ) - return; - - ssl->ciphersuites[minor] = ciphersuites; -} - -void ssl_set_ca_chain( ssl_context *ssl, x509_cert *ca_chain, - x509_crl *ca_crl, const char *peer_cn ) -{ - ssl->ca_chain = ca_chain; - ssl->ca_crl = ca_crl; - ssl->peer_cn = peer_cn; -} - -void ssl_set_own_cert( ssl_context *ssl, x509_cert *own_cert, - rsa_context *rsa_key ) -{ - ssl->own_cert = own_cert; - ssl->rsa_key = rsa_key; -} - -void ssl_set_own_cert_alt( ssl_context *ssl, x509_cert *own_cert, - void *rsa_key, - rsa_decrypt_func rsa_decrypt, - rsa_sign_func rsa_sign, - rsa_key_len_func rsa_key_len ) -{ - ssl->own_cert = own_cert; - ssl->rsa_key = rsa_key; - ssl->rsa_decrypt = rsa_decrypt; - ssl->rsa_sign = rsa_sign; - ssl->rsa_key_len = rsa_key_len; -} - - -#if defined(POLARSSL_DHM_C) -int ssl_set_dh_param( ssl_context *ssl, const char *dhm_P, const char *dhm_G ) -{ - int ret; - - if( ( ret = mpi_read_string( &ssl->dhm_P, 16, dhm_P ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "mpi_read_string", ret ); - return( ret ); - } - - if( ( ret = mpi_read_string( &ssl->dhm_G, 16, dhm_G ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "mpi_read_string", ret ); - return( ret ); - } - - return( 0 ); -} - -int ssl_set_dh_param_ctx( ssl_context *ssl, dhm_context *dhm_ctx ) -{ - int ret; - - if( ( ret = mpi_copy(&ssl->dhm_P, &dhm_ctx->P) ) != 0 ) - { - SSL_DEBUG_RET( 1, "mpi_copy", ret ); - return( ret ); - } - - if( ( ret = mpi_copy(&ssl->dhm_G, &dhm_ctx->G) ) != 0 ) - { - SSL_DEBUG_RET( 1, "mpi_copy", ret ); - return( ret ); - } - - return( 0 ); -} -#endif /* POLARSSL_DHM_C */ - -int ssl_set_hostname( ssl_context *ssl, const char *hostname ) -{ - if( hostname == NULL ) - return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - - ssl->hostname_len = strlen( hostname ); - ssl->hostname = (unsigned char *) malloc( ssl->hostname_len + 1 ); - - if( ssl->hostname == NULL ) - return( POLARSSL_ERR_SSL_MALLOC_FAILED ); - - memcpy( ssl->hostname, (const unsigned char *) hostname, - ssl->hostname_len ); - - ssl->hostname[ssl->hostname_len] = '\0'; - - return( 0 ); -} - -void ssl_set_sni( ssl_context *ssl, - int (*f_sni)(void *, ssl_context *, - const unsigned char *, size_t), - void *p_sni ) -{ - ssl->f_sni = f_sni; - ssl->p_sni = p_sni; -} - -void ssl_set_max_version( ssl_context *ssl, int major, int minor ) -{ - ssl->max_major_ver = major; - ssl->max_minor_ver = minor; -} - -void ssl_set_min_version( ssl_context *ssl, int major, int minor ) -{ - ssl->min_major_ver = major; - ssl->min_minor_ver = minor; -} - -void ssl_set_renegotiation( ssl_context *ssl, int renegotiation ) -{ - ssl->disable_renegotiation = renegotiation; -} - -void ssl_legacy_renegotiation( ssl_context *ssl, int allow_legacy ) -{ - ssl->allow_legacy_renegotiation = allow_legacy; -} - -/* - * SSL get accessors - */ -size_t ssl_get_bytes_avail( const ssl_context *ssl ) -{ - return( ssl->in_offt == NULL ? 0 : ssl->in_msglen ); -} - -int ssl_get_verify_result( const ssl_context *ssl ) -{ - return( ssl->verify_result ); -} - -const char *ssl_get_ciphersuite_name( const int ciphersuite_id ) -{ - switch( ciphersuite_id ) - { -#if defined(POLARSSL_ARC4_C) - case TLS_RSA_WITH_RC4_128_MD5: - return( "TLS-RSA-WITH-RC4-128-MD5" ); - - case TLS_RSA_WITH_RC4_128_SHA: - return( "TLS-RSA-WITH-RC4-128-SHA" ); -#endif - -#if defined(POLARSSL_DES_C) - case TLS_RSA_WITH_3DES_EDE_CBC_SHA: - return( "TLS-RSA-WITH-3DES-EDE-CBC-SHA" ); - - case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: - return( "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA" ); -#endif - -#if defined(POLARSSL_AES_C) - case TLS_RSA_WITH_AES_128_CBC_SHA: - return( "TLS-RSA-WITH-AES-128-CBC-SHA" ); - - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: - return( "TLS-DHE-RSA-WITH-AES-128-CBC-SHA" ); - - case TLS_RSA_WITH_AES_256_CBC_SHA: - return( "TLS-RSA-WITH-AES-256-CBC-SHA" ); - - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: - return( "TLS-DHE-RSA-WITH-AES-256-CBC-SHA" ); - -#if defined(POLARSSL_SHA2_C) - case TLS_RSA_WITH_AES_128_CBC_SHA256: - return( "TLS-RSA-WITH-AES-128-CBC-SHA256" ); - - case TLS_RSA_WITH_AES_256_CBC_SHA256: - return( "TLS-RSA-WITH-AES-256-CBC-SHA256" ); - - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: - return( "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256" ); - - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: - return( "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256" ); -#endif - -#if defined(POLARSSL_GCM_C) && defined(POLARSSL_SHA2_C) - case TLS_RSA_WITH_AES_128_GCM_SHA256: - return( "TLS-RSA-WITH-AES-128-GCM-SHA256" ); - - case TLS_RSA_WITH_AES_256_GCM_SHA384: - return( "TLS-RSA-WITH-AES-256-GCM-SHA384" ); -#endif - -#if defined(POLARSSL_GCM_C) && defined(POLARSSL_SHA4_C) - case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: - return( "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256" ); - - case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: - return( "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384" ); -#endif -#endif /* POLARSSL_AES_C */ - -#if defined(POLARSSL_CAMELLIA_C) - case TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: - return( "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA" ); - - case TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: - return( "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA" ); - - case TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: - return( "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA" ); - - case TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: - return( "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA" ); - -#if defined(POLARSSL_SHA2_C) - case TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: - return( "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256" ); - - case TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: - return( "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256" ); - - case TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: - return( "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256" ); - - case TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: - return( "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256" ); -#endif -#endif - -#if defined(POLARSSL_ENABLE_WEAK_CIPHERSUITES) -#if defined(POLARSSL_CIPHER_NULL_CIPHER) - case TLS_RSA_WITH_NULL_MD5: - return( "TLS-RSA-WITH-NULL-MD5" ); - case TLS_RSA_WITH_NULL_SHA: - return( "TLS-RSA-WITH-NULL-SHA" ); - case TLS_RSA_WITH_NULL_SHA256: - return( "TLS-RSA-WITH-NULL-SHA256" ); -#endif /* defined(POLARSSL_CIPHER_NULL_CIPHER) */ - -#if defined(POLARSSL_DES_C) - case TLS_RSA_WITH_DES_CBC_SHA: - return( "TLS-RSA-WITH-DES-CBC-SHA" ); - case TLS_DHE_RSA_WITH_DES_CBC_SHA: - return( "TLS-DHE-RSA-WITH-DES-CBC-SHA" ); -#endif -#endif /* defined(POLARSSL_ENABLE_WEAK_CIPHERSUITES) */ - - default: - break; - } - - return( "unknown" ); -} - -int ssl_get_ciphersuite_id( const char *ciphersuite_name ) -{ -#if defined(POLARSSL_ARC4_C) - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-RC4-128-MD5")) - return( TLS_RSA_WITH_RC4_128_MD5 ); - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-RC4-128-SHA")) - return( TLS_RSA_WITH_RC4_128_SHA ); -#endif - -#if defined(POLARSSL_DES_C) - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-3DES-EDE-CBC-SHA")) - return( TLS_RSA_WITH_3DES_EDE_CBC_SHA ); - if (0 == strcasecmp(ciphersuite_name, "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA")) - return( TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA ); -#endif - -#if defined(POLARSSL_AES_C) - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-AES-128-CBC-SHA")) - return( TLS_RSA_WITH_AES_128_CBC_SHA ); - if (0 == strcasecmp(ciphersuite_name, "TLS-DHE-RSA-WITH-AES-128-CBC-SHA")) - return( TLS_DHE_RSA_WITH_AES_128_CBC_SHA ); - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-AES-256-CBC-SHA")) - return( TLS_RSA_WITH_AES_256_CBC_SHA ); - if (0 == strcasecmp(ciphersuite_name, "TLS-DHE-RSA-WITH-AES-256-CBC-SHA")) - return( TLS_DHE_RSA_WITH_AES_256_CBC_SHA ); - -#if defined(POLARSSL_SHA2_C) - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-AES-128-CBC-SHA256")) - return( TLS_RSA_WITH_AES_128_CBC_SHA256 ); - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-AES-256-CBC-SHA256")) - return( TLS_RSA_WITH_AES_256_CBC_SHA256 ); - if (0 == strcasecmp(ciphersuite_name, "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256")) - return( TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 ); - if (0 == strcasecmp(ciphersuite_name, "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256")) - return( TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 ); -#endif - -#if defined(POLARSSL_GCM_C) && defined(POLARSSL_SHA2_C) - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-AES-128-GCM-SHA256")) - return( TLS_RSA_WITH_AES_128_GCM_SHA256 ); - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-AES-256-GCM-SHA384")) - return( TLS_RSA_WITH_AES_256_GCM_SHA384 ); -#endif - -#if defined(POLARSSL_GCM_C) && defined(POLARSSL_SHA2_C) - if (0 == strcasecmp(ciphersuite_name, "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256")) - return( TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 ); - if (0 == strcasecmp(ciphersuite_name, "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384")) - return( TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ); -#endif -#endif - -#if defined(POLARSSL_CAMELLIA_C) - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA")) - return( TLS_RSA_WITH_CAMELLIA_128_CBC_SHA ); - if (0 == strcasecmp(ciphersuite_name, "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA")) - return( TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA ); - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA")) - return( TLS_RSA_WITH_CAMELLIA_256_CBC_SHA ); - if (0 == strcasecmp(ciphersuite_name, "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA")) - return( TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA ); - -#if defined(POLARSSL_SHA2_C) - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256")) - return( TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 ); - if (0 == strcasecmp(ciphersuite_name, "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256")) - return( TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 ); - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256")) - return( TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 ); - if (0 == strcasecmp(ciphersuite_name, "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256")) - return( TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 ); -#endif -#endif - -#if defined(POLARSSL_ENABLE_WEAK_CIPHERSUITES) -#if defined(POLARSSL_CIPHER_NULL_CIPHER) - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-NULL-MD5")) - return( TLS_RSA_WITH_NULL_MD5 ); - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-NULL-SHA")) - return( TLS_RSA_WITH_NULL_SHA ); - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-NULL-SHA256")) - return( TLS_RSA_WITH_NULL_SHA256 ); -#endif /* defined(POLARSSL_CIPHER_NULL_CIPHER) */ - -#if defined(POLARSSL_DES_C) - if (0 == strcasecmp(ciphersuite_name, "TLS-RSA-WITH-DES-CBC-SHA")) - return( TLS_RSA_WITH_DES_CBC_SHA ); - if (0 == strcasecmp(ciphersuite_name, "TLS-DHE-RSA-WITH-DES-CBC-SHA")) - return( TLS_DHE_RSA_WITH_DES_CBC_SHA ); -#endif -#endif /* defined(POLARSSL_ENABLE_WEAK_CIPHERSUITES) */ - - return( 0 ); -} - -const char *ssl_get_ciphersuite( const ssl_context *ssl ) -{ - if( ssl == NULL || ssl->session == NULL ) - return NULL; - - return ssl_get_ciphersuite_name( ssl->session->ciphersuite ); -} - -const char *ssl_get_version( const ssl_context *ssl ) -{ - switch( ssl->minor_ver ) - { - case SSL_MINOR_VERSION_0: - return( "SSLv3.0" ); - - case SSL_MINOR_VERSION_1: - return( "TLSv1.0" ); - - case SSL_MINOR_VERSION_2: - return( "TLSv1.1" ); - - case SSL_MINOR_VERSION_3: - return( "TLSv1.2" ); - - default: - break; - } - return( "unknown" ); -} - -const x509_cert *ssl_get_peer_cert( const ssl_context *ssl ) -{ - if( ssl == NULL || ssl->session == NULL ) - return NULL; - - return ssl->session->peer_cert; -} - -const int ssl_default_ciphersuites[] = -{ -#if defined(POLARSSL_DHM_C) -#if defined(POLARSSL_AES_C) -#if defined(POLARSSL_SHA2_C) - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, -#endif /* POLARSSL_SHA2_C */ -#if defined(POLARSSL_GCM_C) && defined(POLARSSL_SHA4_C) - TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, -#endif - TLS_DHE_RSA_WITH_AES_256_CBC_SHA, -#if defined(POLARSSL_SHA2_C) - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, -#endif -#if defined(POLARSSL_GCM_C) && defined(POLARSSL_SHA2_C) - TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, -#endif - TLS_DHE_RSA_WITH_AES_128_CBC_SHA, -#endif -#if defined(POLARSSL_CAMELLIA_C) -#if defined(POLARSSL_SHA2_C) - TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, -#endif /* POLARSSL_SHA2_C */ - TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, -#if defined(POLARSSL_SHA2_C) - TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, -#endif /* POLARSSL_SHA2_C */ - TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, -#endif -#if defined(POLARSSL_DES_C) - TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, -#endif -#endif - -#if defined(POLARSSL_AES_C) -#if defined(POLARSSL_SHA2_C) - TLS_RSA_WITH_AES_256_CBC_SHA256, -#endif /* POLARSSL_SHA2_C */ -#if defined(POLARSSL_GCM_C) && defined(POLARSSL_SHA4_C) - TLS_RSA_WITH_AES_256_GCM_SHA384, -#endif /* POLARSSL_SHA2_C */ - TLS_RSA_WITH_AES_256_CBC_SHA, -#endif -#if defined(POLARSSL_CAMELLIA_C) -#if defined(POLARSSL_SHA2_C) - TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, -#endif /* POLARSSL_SHA2_C */ - TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, -#endif -#if defined(POLARSSL_AES_C) -#if defined(POLARSSL_SHA2_C) - TLS_RSA_WITH_AES_128_CBC_SHA256, -#endif /* POLARSSL_SHA2_C */ -#if defined(POLARSSL_GCM_C) && defined(POLARSSL_SHA2_C) - TLS_RSA_WITH_AES_128_GCM_SHA256, -#endif /* POLARSSL_SHA2_C */ - TLS_RSA_WITH_AES_128_CBC_SHA, -#endif -#if defined(POLARSSL_CAMELLIA_C) -#if defined(POLARSSL_SHA2_C) - TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, -#endif /* POLARSSL_SHA2_C */ - TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, -#endif -#if defined(POLARSSL_DES_C) - TLS_RSA_WITH_3DES_EDE_CBC_SHA, -#endif -#if defined(POLARSSL_ARC4_C) - TLS_RSA_WITH_RC4_128_SHA, - TLS_RSA_WITH_RC4_128_MD5, -#endif - 0 -}; - -/* - * Perform a single step of the SSL handshake - */ -int ssl_handshake_step( ssl_context *ssl ) -{ - int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; - -#if defined(POLARSSL_SSL_CLI_C) - if( ssl->endpoint == SSL_IS_CLIENT ) - ret = ssl_handshake_client_step( ssl ); -#endif - -#if defined(POLARSSL_SSL_SRV_C) - if( ssl->endpoint == SSL_IS_SERVER ) - ret = ssl_handshake_server_step( ssl ); -#endif - - return( ret ); -} - -/* - * Perform the SSL handshake - */ -int ssl_handshake( ssl_context *ssl ) -{ - int ret = 0; - - SSL_DEBUG_MSG( 2, ( "=> handshake" ) ); - - while( ssl->state != SSL_HANDSHAKE_OVER ) - { - ret = ssl_handshake_step( ssl ); - - if( ret != 0 ) - break; - } - - SSL_DEBUG_MSG( 2, ( "<= handshake" ) ); - - return( ret ); -} - -/* - * Renegotiate current connection - */ -int ssl_renegotiate( ssl_context *ssl ) -{ - int ret; - - SSL_DEBUG_MSG( 2, ( "=> renegotiate" ) ); - - if( ssl->state != SSL_HANDSHAKE_OVER ) - return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - - ssl->state = SSL_HELLO_REQUEST; - ssl->renegotiation = SSL_RENEGOTIATION; - - if( ( ret = ssl_handshake_init( ssl ) ) != 0 ) - return( ret ); - - if( ( ret = ssl_handshake( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_handshake", ret ); - return( ret ); - } - - SSL_DEBUG_MSG( 2, ( "<= renegotiate" ) ); - - return( 0 ); -} - -/* - * Receive application data decrypted from the SSL layer - */ -int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len ) -{ - int ret; - size_t n; - - SSL_DEBUG_MSG( 2, ( "=> read" ) ); - - if( ssl->state != SSL_HANDSHAKE_OVER ) - { - if( ( ret = ssl_handshake( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_handshake", ret ); - return( ret ); - } - } - - if( ssl->in_offt == NULL ) - { - if( ( ret = ssl_read_record( ssl ) ) != 0 ) - { - if( ret == POLARSSL_ERR_SSL_CONN_EOF ) - return( 0 ); - - SSL_DEBUG_RET( 1, "ssl_read_record", ret ); - return( ret ); - } - - if( ssl->in_msglen == 0 && - ssl->in_msgtype == SSL_MSG_APPLICATION_DATA ) - { - /* - * OpenSSL sends empty messages to randomize the IV - */ - if( ( ret = ssl_read_record( ssl ) ) != 0 ) - { - if( ret == POLARSSL_ERR_SSL_CONN_EOF ) - return( 0 ); - - SSL_DEBUG_RET( 1, "ssl_read_record", ret ); - return( ret ); - } - } - - if( ssl->in_msgtype == SSL_MSG_HANDSHAKE ) - { - SSL_DEBUG_MSG( 1, ( "received handshake message" ) ); - - if( ssl->endpoint == SSL_IS_CLIENT && - ( ssl->in_msg[0] != SSL_HS_HELLO_REQUEST || - ssl->in_hslen != 4 ) ) - { - SSL_DEBUG_MSG( 1, ( "handshake received (not HelloRequest)" ) ); - return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); - } - - if( ssl->disable_renegotiation == SSL_RENEGOTIATION_DISABLED || - ( ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && - ssl->allow_legacy_renegotiation == SSL_LEGACY_NO_RENEGOTIATION ) ) - { - SSL_DEBUG_MSG( 3, ( "ignoring renegotiation, sending alert" ) ); - - if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) - { - /* - * SSLv3 does not have a "no_renegotiation" alert - */ - if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) - return( ret ); - } - else - { - if( ( ret = ssl_send_alert_message( ssl, - SSL_ALERT_LEVEL_WARNING, - SSL_ALERT_MSG_NO_RENEGOTIATION ) ) != 0 ) - { - return( ret ); - } - } - } - else - { - if( ( ret = ssl_renegotiate( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_renegotiate", ret ); - return( ret ); - } - - return( POLARSSL_ERR_NET_WANT_READ ); - } - } - else if( ssl->in_msgtype != SSL_MSG_APPLICATION_DATA ) - { - SSL_DEBUG_MSG( 1, ( "bad application data message" ) ); - return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); - } - - ssl->in_offt = ssl->in_msg; - } - - n = ( len < ssl->in_msglen ) - ? len : ssl->in_msglen; - - memcpy( buf, ssl->in_offt, n ); - ssl->in_msglen -= n; - - if( ssl->in_msglen == 0 ) - /* all bytes consumed */ - ssl->in_offt = NULL; - else - /* more data available */ - ssl->in_offt += n; - - SSL_DEBUG_MSG( 2, ( "<= read" ) ); - - return( (int) n ); -} - -/* - * Send application data to be encrypted by the SSL layer - */ -int ssl_write( ssl_context *ssl, const unsigned char *buf, size_t len ) -{ - int ret; - size_t n; - - SSL_DEBUG_MSG( 2, ( "=> write" ) ); - - if( ssl->state != SSL_HANDSHAKE_OVER ) - { - if( ( ret = ssl_handshake( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_handshake", ret ); - return( ret ); - } - } - - n = ( len < SSL_MAX_CONTENT_LEN ) - ? len : SSL_MAX_CONTENT_LEN; - - if( ssl->out_left != 0 ) - { - if( ( ret = ssl_flush_output( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_flush_output", ret ); - return( ret ); - } - } - else - { - ssl->out_msglen = n; - ssl->out_msgtype = SSL_MSG_APPLICATION_DATA; - memcpy( ssl->out_msg, buf, n ); - - if( ( ret = ssl_write_record( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_write_record", ret ); - return( ret ); - } - } - - SSL_DEBUG_MSG( 2, ( "<= write" ) ); - - return( (int) n ); -} - -/* - * Notify the peer that the connection is being closed - */ -int ssl_close_notify( ssl_context *ssl ) -{ - int ret; - - SSL_DEBUG_MSG( 2, ( "=> write close notify" ) ); - - if( ( ret = ssl_flush_output( ssl ) ) != 0 ) - { - SSL_DEBUG_RET( 1, "ssl_flush_output", ret ); - return( ret ); - } - - if( ssl->state == SSL_HANDSHAKE_OVER ) - { - if( ( ret = ssl_send_alert_message( ssl, - SSL_ALERT_LEVEL_WARNING, - SSL_ALERT_MSG_CLOSE_NOTIFY ) ) != 0 ) - { - return( ret ); - } - } - - SSL_DEBUG_MSG( 2, ( "<= write close notify" ) ); - - return( ret ); -} - -void ssl_transform_free( ssl_transform *transform ) -{ -#if defined(POLARSSL_ZLIB_SUPPORT) - deflateEnd( &transform->ctx_deflate ); - inflateEnd( &transform->ctx_inflate ); -#endif - - memset( transform, 0, sizeof( ssl_transform ) ); -} - -void ssl_handshake_free( ssl_handshake_params *handshake ) -{ -#if defined(POLARSSL_DHM_C) - dhm_free( &handshake->dhm_ctx ); -#endif - memset( handshake, 0, sizeof( ssl_handshake_params ) ); -} - -void ssl_session_free( ssl_session *session ) -{ - if( session->peer_cert != NULL ) - { - x509_free( session->peer_cert ); - free( session->peer_cert ); - } - - memset( session, 0, sizeof( ssl_session ) ); -} - -/* - * Free an SSL context - */ -void ssl_free( ssl_context *ssl ) -{ - SSL_DEBUG_MSG( 2, ( "=> free" ) ); - - free( ssl->ciphersuites ); - - if( ssl->out_ctr != NULL ) - { - memset( ssl->out_ctr, 0, SSL_BUFFER_LEN ); - free( ssl->out_ctr ); - } - - if( ssl->in_ctr != NULL ) - { - memset( ssl->in_ctr, 0, SSL_BUFFER_LEN ); - free( ssl->in_ctr ); - } - -#if defined(POLARSSL_DHM_C) - mpi_free( &ssl->dhm_P ); - mpi_free( &ssl->dhm_G ); -#endif - - if( ssl->transform ) - { - ssl_transform_free( ssl->transform ); - free( ssl->transform ); - } - - if( ssl->handshake ) - { - ssl_handshake_free( ssl->handshake ); - ssl_transform_free( ssl->transform_negotiate ); - ssl_session_free( ssl->session_negotiate ); - - free( ssl->handshake ); - free( ssl->transform_negotiate ); - free( ssl->session_negotiate ); - } - - if( ssl->session ) - { - ssl_session_free( ssl->session ); - free( ssl->session ); - } - - if ( ssl->hostname != NULL) - { - memset( ssl->hostname, 0, ssl->hostname_len ); - free( ssl->hostname ); - ssl->hostname_len = 0; - } - -#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) - if( ssl_hw_record_finish != NULL ) - { - SSL_DEBUG_MSG( 2, ( "going for ssl_hw_record_finish()" ) ); - ssl_hw_record_finish( ssl ); - } -#endif - - SSL_DEBUG_MSG( 2, ( "<= free" ) ); - - /* Actually clear after last debug message */ - memset( ssl, 0, sizeof( ssl_context ) ); -} - -#endif diff --git a/makerom/polarssl/timing.c b/makerom/polarssl/timing.c deleted file mode 100644 index 0273d1af..00000000 --- a/makerom/polarssl/timing.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Portable interface to the CPU cycle counter - * - * 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_TIMING_C) - -#include "polarssl/timing.h" - -#if defined(_WIN32) - -#include -#include - -struct _hr_time -{ - LARGE_INTEGER start; -}; - -#else - -#include -#include -#include -#include -#include - -struct _hr_time -{ - struct timeval start; -}; - -#endif - -#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ - (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) - -#define POLARSSL_HAVE_HARDCLOCK - -unsigned long hardclock( void ) -{ - unsigned long tsc; - __asm rdtsc - __asm mov [tsc], eax - return( tsc ); -} -#endif - -#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ - defined(__GNUC__) && defined(__i386__) - -#define POLARSSL_HAVE_HARDCLOCK - -unsigned long hardclock( void ) -{ - unsigned long lo, hi; - asm( "rdtsc" : "=a" (lo), "=d" (hi) ); - return( lo ); -} -#endif - -#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ - defined(__GNUC__) && (defined(__amd64__) || defined(__x86_64__)) - -#define POLARSSL_HAVE_HARDCLOCK - -unsigned long hardclock( void ) -{ - unsigned long lo, hi; - asm( "rdtsc" : "=a" (lo), "=d" (hi) ); - return( lo | (hi << 32) ); -} -#endif - -#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ - defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__)) - -#define POLARSSL_HAVE_HARDCLOCK - -unsigned long hardclock( void ) -{ - unsigned long tbl, tbu0, tbu1; - - do - { - asm( "mftbu %0" : "=r" (tbu0) ); - asm( "mftb %0" : "=r" (tbl ) ); - asm( "mftbu %0" : "=r" (tbu1) ); - } - while( tbu0 != tbu1 ); - - return( tbl ); -} -#endif - -#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ - defined(__GNUC__) && defined(__sparc64__) - -#if defined(__OpenBSD__) -#warning OpenBSD does not allow access to tick register using software version instead -#else -#define POLARSSL_HAVE_HARDCLOCK - -unsigned long hardclock( void ) -{ - unsigned long tick; - asm( "rdpr %%tick, %0;" : "=&r" (tick) ); - return( tick ); -} -#endif -#endif - -#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ - defined(__GNUC__) && defined(__sparc__) && !defined(__sparc64__) - -#define POLARSSL_HAVE_HARDCLOCK - -unsigned long hardclock( void ) -{ - unsigned long tick; - asm( ".byte 0x83, 0x41, 0x00, 0x00" ); - asm( "mov %%g1, %0" : "=r" (tick) ); - return( tick ); -} -#endif - -#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ - defined(__GNUC__) && defined(__alpha__) - -#define POLARSSL_HAVE_HARDCLOCK - -unsigned long hardclock( void ) -{ - unsigned long cc; - asm( "rpcc %0" : "=r" (cc) ); - return( cc & 0xFFFFFFFF ); -} -#endif - -#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ - defined(__GNUC__) && defined(__ia64__) - -#define POLARSSL_HAVE_HARDCLOCK - -unsigned long hardclock( void ) -{ - unsigned long itc; - asm( "mov %0 = ar.itc" : "=r" (itc) ); - return( itc ); -} -#endif - -#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(_MSC_VER) - -#define POLARSSL_HAVE_HARDCLOCK - -unsigned long hardclock( void ) -{ - LARGE_INTEGER offset; - - QueryPerformanceCounter( &offset ); - - return (unsigned long)( offset.QuadPart ); -} -#endif - -#if !defined(POLARSSL_HAVE_HARDCLOCK) - -#define POLARSSL_HAVE_HARDCLOCK - -static int hardclock_init = 0; -static struct timeval tv_init; - -unsigned long hardclock( void ) -{ - struct timeval tv_cur; - - if( hardclock_init == 0 ) - { - gettimeofday( &tv_init, NULL ); - hardclock_init = 1; - } - - gettimeofday( &tv_cur, NULL ); - return( ( tv_cur.tv_sec - tv_init.tv_sec ) * 1000000 - + ( tv_cur.tv_usec - tv_init.tv_usec ) ); -} -#endif - -volatile int alarmed = 0; - -#if defined(_WIN32) - -unsigned long get_timer( struct hr_time *val, int reset ) -{ - unsigned long delta; - LARGE_INTEGER offset, hfreq; - struct _hr_time *t = (struct _hr_time *) val; - - QueryPerformanceCounter( &offset ); - QueryPerformanceFrequency( &hfreq ); - - delta = (unsigned long)( ( 1000 * - ( offset.QuadPart - t->start.QuadPart ) ) / - hfreq.QuadPart ); - - if( reset ) - QueryPerformanceCounter( &t->start ); - - return( delta ); -} - -DWORD WINAPI TimerProc( LPVOID uElapse ) -{ - Sleep( (DWORD) uElapse ); - alarmed = 1; - return( TRUE ); -} - -void set_alarm( int seconds ) -{ - DWORD ThreadId; - - alarmed = 0; - CloseHandle( CreateThread( NULL, 0, TimerProc, - (LPVOID) ( seconds * 1000 ), 0, &ThreadId ) ); -} - -void m_sleep( int milliseconds ) -{ - Sleep( milliseconds ); -} - -#else - -unsigned long get_timer( struct hr_time *val, int reset ) -{ - unsigned long delta; - struct timeval offset; - struct _hr_time *t = (struct _hr_time *) val; - - gettimeofday( &offset, NULL ); - - delta = ( offset.tv_sec - t->start.tv_sec ) * 1000 - + ( offset.tv_usec - t->start.tv_usec ) / 1000; - - if( reset ) - { - t->start.tv_sec = offset.tv_sec; - t->start.tv_usec = offset.tv_usec; - } - - return( delta ); -} - -#if defined(INTEGRITY) -void m_sleep( int milliseconds ) -{ - usleep( milliseconds * 1000 ); -} - -#else - -static void sighandler( int signum ) -{ - alarmed = 1; - signal( signum, sighandler ); -} - -void set_alarm( int seconds ) -{ - alarmed = 0; - signal( SIGALRM, sighandler ); - alarm( seconds ); -} - -void m_sleep( int milliseconds ) -{ - struct timeval tv; - - tv.tv_sec = milliseconds / 1000; - tv.tv_usec = milliseconds * 1000; - - select( 0, NULL, NULL, NULL, &tv ); -} -#endif /* INTEGRITY */ - -#endif - -#endif diff --git a/makerom/polarssl/version.c b/makerom/polarssl/version.c deleted file mode 100644 index c1080b78..00000000 --- a/makerom/polarssl/version.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Version information - * - * 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_VERSION_C) - -#include "polarssl/version.h" -#include - -const char version[] = POLARSSL_VERSION_STRING; - -unsigned int version_get_number() -{ - return POLARSSL_VERSION_NUMBER; -} - -void version_get_string( char *string ) -{ - memcpy( string, POLARSSL_VERSION_STRING, sizeof( POLARSSL_VERSION_STRING ) ); -} - -void version_get_string_full( char *string ) -{ - memcpy( string, POLARSSL_VERSION_STRING_FULL, sizeof( POLARSSL_VERSION_STRING_FULL ) ); -} - -#endif /* POLARSSL_VERSION_C */ diff --git a/makerom/polarssl/x509parse.c b/makerom/polarssl/x509parse.c deleted file mode 100644 index 77725e06..00000000 --- a/makerom/polarssl/x509parse.c +++ /dev/null @@ -1,3809 +0,0 @@ -/* - * X.509 certificate and private key decoding - * - * Copyright (C) 2006-2011, 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 ITU-T X.509 standard defines a certificate format for PKI. - * - * http://www.ietf.org/rfc/rfc3279.txt - * http://www.ietf.org/rfc/rfc3280.txt - * - * ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc - * - * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf - * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_X509_PARSE_C) - -#include "polarssl/x509.h" -#include "polarssl/asn1.h" -#include "polarssl/pem.h" -#include "polarssl/des.h" -#if defined(POLARSSL_MD2_C) -#include "polarssl/md2.h" -#endif -#if defined(POLARSSL_MD4_C) -#include "polarssl/md4.h" -#endif -#if defined(POLARSSL_MD5_C) -#include "polarssl/md5.h" -#endif -#if defined(POLARSSL_SHA1_C) -#include "polarssl/sha1.h" -#endif -#if defined(POLARSSL_SHA2_C) -#include "polarssl/sha2.h" -#endif -#if defined(POLARSSL_SHA4_C) -#include "polarssl/sha4.h" -#endif -#include "polarssl/dhm.h" -#if defined(POLARSSL_PKCS5_C) -#include "polarssl/pkcs5.h" -#endif -#if defined(POLARSSL_PKCS12_C) -#include "polarssl/pkcs12.h" -#endif - -#include -#include -#if defined(_WIN32) -#include -#else -#include -#endif - -#if defined(POLARSSL_FS_IO) -#include -#if !defined(_WIN32) -#include -#include -#include -#endif -#endif - -/* Compare a given OID string with an OID x509_buf * */ -#define OID_CMP(oid_str, oid_buf) \ - ( ( OID_SIZE(oid_str) == (oid_buf)->len ) && \ - memcmp( (oid_str), (oid_buf)->p, (oid_buf)->len) == 0) - -/* - * Version ::= INTEGER { v1(0), v2(1), v3(2) } - */ -static int x509_get_version( unsigned char **p, - const unsigned char *end, - int *ver ) -{ - int ret; - size_t len; - - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0 ) ) != 0 ) - { - if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) - { - *ver = 0; - return( 0 ); - } - - return( ret ); - } - - end = *p + len; - - if( ( ret = asn1_get_int( p, end, ver ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_VERSION + ret ); - - if( *p != end ) - return( POLARSSL_ERR_X509_CERT_INVALID_VERSION + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); -} - -/* - * Version ::= INTEGER { v1(0), v2(1) } - */ -static int x509_crl_get_version( unsigned char **p, - const unsigned char *end, - int *ver ) -{ - int ret; - - if( ( ret = asn1_get_int( p, end, ver ) ) != 0 ) - { - if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) - { - *ver = 0; - return( 0 ); - } - - return( POLARSSL_ERR_X509_CERT_INVALID_VERSION + ret ); - } - - return( 0 ); -} - -/* - * CertificateSerialNumber ::= INTEGER - */ -static int x509_get_serial( unsigned char **p, - const unsigned char *end, - x509_buf *serial ) -{ - int ret; - - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_X509_CERT_INVALID_SERIAL + - POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - if( **p != ( ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2 ) && - **p != ASN1_INTEGER ) - return( POLARSSL_ERR_X509_CERT_INVALID_SERIAL + - POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); - - serial->tag = *(*p)++; - - if( ( ret = asn1_get_len( p, end, &serial->len ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_SERIAL + ret ); - - serial->p = *p; - *p += serial->len; - - return( 0 ); -} - -/* - * AlgorithmIdentifier ::= SEQUENCE { - * algorithm OBJECT IDENTIFIER, - * parameters ANY DEFINED BY algorithm OPTIONAL } - */ -static int x509_get_alg( unsigned char **p, - const unsigned char *end, - x509_buf *alg ) -{ - int ret; - size_t len; - - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_ALG + ret ); - - end = *p + len; - alg->tag = **p; - - if( ( ret = asn1_get_tag( p, end, &alg->len, ASN1_OID ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_ALG + ret ); - - alg->p = *p; - *p += alg->len; - - if( *p == end ) - return( 0 ); - - /* - * assume the algorithm parameters must be NULL - */ - if( ( ret = asn1_get_tag( p, end, &len, ASN1_NULL ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_ALG + ret ); - - if( *p != end ) - return( POLARSSL_ERR_X509_CERT_INVALID_ALG + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); -} - -/* - * AttributeTypeAndValue ::= SEQUENCE { - * type AttributeType, - * value AttributeValue } - * - * AttributeType ::= OBJECT IDENTIFIER - * - * AttributeValue ::= ANY DEFINED BY AttributeType - */ -static int x509_get_attr_type_value( unsigned char **p, - const unsigned char *end, - x509_name *cur ) -{ - int ret; - size_t len; - x509_buf *oid; - x509_buf *val; - - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_NAME + ret ); - - oid = &cur->oid; - oid->tag = **p; - - if( ( ret = asn1_get_tag( p, end, &oid->len, ASN1_OID ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_NAME + ret ); - - oid->p = *p; - *p += oid->len; - - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_X509_CERT_INVALID_NAME + - POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - if( **p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING && - **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING && - **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING ) - return( POLARSSL_ERR_X509_CERT_INVALID_NAME + - POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); - - val = &cur->val; - val->tag = *(*p)++; - - if( ( ret = asn1_get_len( p, end, &val->len ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_NAME + ret ); - - val->p = *p; - *p += val->len; - - cur->next = NULL; - - return( 0 ); -} - -/* - * RelativeDistinguishedName ::= - * SET OF AttributeTypeAndValue - * - * AttributeTypeAndValue ::= SEQUENCE { - * type AttributeType, - * value AttributeValue } - * - * AttributeType ::= OBJECT IDENTIFIER - * - * AttributeValue ::= ANY DEFINED BY AttributeType - */ -static int x509_get_name( unsigned char **p, - const unsigned char *end, - x509_name *cur ) -{ - int ret; - size_t len; - const unsigned char *end2; - x509_name *use; - - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SET ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_NAME + ret ); - - end2 = end; - end = *p + len; - use = cur; - - do - { - if( ( ret = x509_get_attr_type_value( p, end, use ) ) != 0 ) - return( ret ); - - if( *p != end ) - { - use->next = (x509_name *) malloc( - sizeof( x509_name ) ); - - if( use->next == NULL ) - return( POLARSSL_ERR_X509_MALLOC_FAILED ); - - memset( use->next, 0, sizeof( x509_name ) ); - - use = use->next; - } - } - while( *p != end ); - - /* - * recurse until end of SEQUENCE is reached - */ - if( *p == end2 ) - return( 0 ); - - cur->next = (x509_name *) malloc( - sizeof( x509_name ) ); - - if( cur->next == NULL ) - return( POLARSSL_ERR_X509_MALLOC_FAILED ); - - memset( cur->next, 0, sizeof( x509_name ) ); - - return( x509_get_name( p, end2, cur->next ) ); -} - -/* - * Time ::= CHOICE { - * utcTime UTCTime, - * generalTime GeneralizedTime } - */ -static int x509_get_time( unsigned char **p, - const unsigned char *end, - x509_time *time ) -{ - int ret; - size_t len; - char date[64]; - unsigned char tag; - - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_X509_CERT_INVALID_DATE + - POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - tag = **p; - - if ( tag == ASN1_UTC_TIME ) - { - (*p)++; - ret = asn1_get_len( p, end, &len ); - - if( ret != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_DATE + ret ); - - memset( date, 0, sizeof( date ) ); - memcpy( date, *p, ( len < sizeof( date ) - 1 ) ? - len : sizeof( date ) - 1 ); - - if( sscanf( date, "%2d%2d%2d%2d%2d%2d", - &time->year, &time->mon, &time->day, - &time->hour, &time->min, &time->sec ) < 5 ) - return( POLARSSL_ERR_X509_CERT_INVALID_DATE ); - - time->year += 100 * ( time->year < 50 ); - time->year += 1900; - - *p += len; - - return( 0 ); - } - else if ( tag == ASN1_GENERALIZED_TIME ) - { - (*p)++; - ret = asn1_get_len( p, end, &len ); - - if( ret != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_DATE + ret ); - - memset( date, 0, sizeof( date ) ); - memcpy( date, *p, ( len < sizeof( date ) - 1 ) ? - len : sizeof( date ) - 1 ); - - if( sscanf( date, "%4d%2d%2d%2d%2d%2d", - &time->year, &time->mon, &time->day, - &time->hour, &time->min, &time->sec ) < 5 ) - return( POLARSSL_ERR_X509_CERT_INVALID_DATE ); - - *p += len; - - return( 0 ); - } - else - return( POLARSSL_ERR_X509_CERT_INVALID_DATE + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); -} - - -/* - * Validity ::= SEQUENCE { - * notBefore Time, - * notAfter Time } - */ -static int x509_get_dates( unsigned char **p, - const unsigned char *end, - x509_time *from, - x509_time *to ) -{ - int ret; - size_t len; - - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_DATE + ret ); - - end = *p + len; - - if( ( ret = x509_get_time( p, end, from ) ) != 0 ) - return( ret ); - - if( ( ret = x509_get_time( p, end, to ) ) != 0 ) - return( ret ); - - if( *p != end ) - return( POLARSSL_ERR_X509_CERT_INVALID_DATE + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); -} - -/* - * SubjectPublicKeyInfo ::= SEQUENCE { - * algorithm AlgorithmIdentifier, - * subjectPublicKey BIT STRING } - */ -static int x509_get_pubkey( unsigned char **p, - const unsigned char *end, - x509_buf *pk_alg_oid, - mpi *N, mpi *E ) -{ - int ret; - size_t len; - unsigned char *end2; - - if( ( ret = x509_get_alg( p, end, pk_alg_oid ) ) != 0 ) - return( ret ); - - /* - * only RSA public keys handled at this time - */ - if( pk_alg_oid->len != 9 || - memcmp( pk_alg_oid->p, OID_PKCS1_RSA, 9 ) != 0 ) - { - return( POLARSSL_ERR_X509_UNKNOWN_PK_ALG ); - } - - if( ( ret = asn1_get_tag( p, end, &len, ASN1_BIT_STRING ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY + ret ); - - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY + - POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - end2 = *p + len; - - if( *(*p)++ != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY ); - - /* - * RSAPublicKey ::= SEQUENCE { - * modulus INTEGER, -- n - * publicExponent INTEGER -- e - * } - */ - if( ( ret = asn1_get_tag( p, end2, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY + ret ); - - if( *p + len != end2 ) - return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - if( ( ret = asn1_get_mpi( p, end2, N ) ) != 0 || - ( ret = asn1_get_mpi( p, end2, E ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY + ret ); - - if( *p != end ) - return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); -} - -static int x509_get_sig( unsigned char **p, - const unsigned char *end, - x509_buf *sig ) -{ - int ret; - size_t len; - - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE + - POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - sig->tag = **p; - - if( ( ret = asn1_get_tag( p, end, &len, ASN1_BIT_STRING ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE + ret ); - - - if( --len < 1 || *(*p)++ != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE ); - - sig->len = len; - sig->p = *p; - - *p += len; - - return( 0 ); -} - -/* - * X.509 v2/v3 unique identifier (not parsed) - */ -static int x509_get_uid( unsigned char **p, - const unsigned char *end, - x509_buf *uid, int n ) -{ - int ret; - - if( *p == end ) - return( 0 ); - - uid->tag = **p; - - if( ( ret = asn1_get_tag( p, end, &uid->len, - ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n ) ) != 0 ) - { - if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) - return( 0 ); - - return( ret ); - } - - uid->p = *p; - *p += uid->len; - - return( 0 ); -} - -/* - * X.509 Extensions (No parsing of extensions, pointer should - * be either manually updated or extensions should be parsed! - */ -static int x509_get_ext( unsigned char **p, - const unsigned char *end, - x509_buf *ext, int tag ) -{ - int ret; - size_t len; - - if( *p == end ) - return( 0 ); - - ext->tag = **p; - - if( ( ret = asn1_get_tag( p, end, &ext->len, - ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | tag ) ) != 0 ) - return( ret ); - - ext->p = *p; - end = *p + ext->len; - - /* - * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension - * - * Extension ::= SEQUENCE { - * extnID OBJECT IDENTIFIER, - * critical BOOLEAN DEFAULT FALSE, - * extnValue OCTET STRING } - */ - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - if( end != *p + len ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); -} - -/* - * X.509 CRL v2 extensions (no extensions parsed yet.) - */ -static int x509_get_crl_ext( unsigned char **p, - const unsigned char *end, - x509_buf *ext ) -{ - int ret; - size_t len = 0; - - /* Get explicit tag */ - if( ( ret = x509_get_ext( p, end, ext, 0) ) != 0 ) - { - if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) - return( 0 ); - - return( ret ); - } - - while( *p < end ) - { - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - *p += len; - } - - if( *p != end ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); -} - -/* - * X.509 CRL v2 entry extensions (no extensions parsed yet.) - */ -static int x509_get_crl_entry_ext( unsigned char **p, - const unsigned char *end, - x509_buf *ext ) -{ - int ret; - size_t len = 0; - - /* OPTIONAL */ - if (end <= *p) - return( 0 ); - - ext->tag = **p; - ext->p = *p; - - /* - * Get CRL-entry extension sequence header - * crlEntryExtensions Extensions OPTIONAL -- if present, MUST be v2 - */ - if( ( ret = asn1_get_tag( p, end, &ext->len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) - { - ext->p = NULL; - return( 0 ); - } - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - } - - end = *p + ext->len; - - if( end != *p + ext->len ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - while( *p < end ) - { - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - *p += len; - } - - if( *p != end ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); -} - -static int x509_get_basic_constraints( unsigned char **p, - const unsigned char *end, - int *ca_istrue, - int *max_pathlen ) -{ - int ret; - size_t len; - - /* - * BasicConstraints ::= SEQUENCE { - * cA BOOLEAN DEFAULT FALSE, - * pathLenConstraint INTEGER (0..MAX) OPTIONAL } - */ - *ca_istrue = 0; /* DEFAULT FALSE */ - *max_pathlen = 0; /* endless */ - - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - if( *p == end ) - return 0; - - if( ( ret = asn1_get_bool( p, end, ca_istrue ) ) != 0 ) - { - if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) - ret = asn1_get_int( p, end, ca_istrue ); - - if( ret != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - if( *ca_istrue != 0 ) - *ca_istrue = 1; - } - - if( *p == end ) - return 0; - - if( ( ret = asn1_get_int( p, end, max_pathlen ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - if( *p != end ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - (*max_pathlen)++; - - return 0; -} - -static int x509_get_ns_cert_type( unsigned char **p, - const unsigned char *end, - unsigned char *ns_cert_type) -{ - int ret; - x509_bitstring bs = { 0, 0, NULL }; - - if( ( ret = asn1_get_bitstring( p, end, &bs ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - if( bs.len != 1 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_INVALID_LENGTH ); - - /* Get actual bitstring */ - *ns_cert_type = *bs.p; - return 0; -} - -static int x509_get_key_usage( unsigned char **p, - const unsigned char *end, - unsigned char *key_usage) -{ - int ret; - x509_bitstring bs = { 0, 0, NULL }; - - if( ( ret = asn1_get_bitstring( p, end, &bs ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - if( bs.len < 1 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_INVALID_LENGTH ); - - /* Get actual bitstring */ - *key_usage = *bs.p; - return 0; -} - -/* - * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId - * - * KeyPurposeId ::= OBJECT IDENTIFIER - */ -static int x509_get_ext_key_usage( unsigned char **p, - const unsigned char *end, - x509_sequence *ext_key_usage) -{ - int ret; - - if( ( ret = asn1_get_sequence_of( p, end, ext_key_usage, ASN1_OID ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - /* Sequence length must be >= 1 */ - if( ext_key_usage->buf.p == NULL ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_INVALID_LENGTH ); - - return 0; -} - -/* - * SubjectAltName ::= GeneralNames - * - * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - * - * GeneralName ::= CHOICE { - * otherName [0] OtherName, - * rfc822Name [1] IA5String, - * dNSName [2] IA5String, - * x400Address [3] ORAddress, - * directoryName [4] Name, - * ediPartyName [5] EDIPartyName, - * uniformResourceIdentifier [6] IA5String, - * iPAddress [7] OCTET STRING, - * registeredID [8] OBJECT IDENTIFIER } - * - * OtherName ::= SEQUENCE { - * type-id OBJECT IDENTIFIER, - * value [0] EXPLICIT ANY DEFINED BY type-id } - * - * EDIPartyName ::= SEQUENCE { - * nameAssigner [0] DirectoryString OPTIONAL, - * partyName [1] DirectoryString } - * - * NOTE: PolarSSL only parses and uses dNSName at this point. - */ -static int x509_get_subject_alt_name( unsigned char **p, - const unsigned char *end, - x509_sequence *subject_alt_name ) -{ - int ret; - size_t len, tag_len; - asn1_buf *buf; - unsigned char tag; - asn1_sequence *cur = subject_alt_name; - - /* Get main sequence tag */ - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - if( *p + len != end ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - while( *p < end ) - { - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - tag = **p; - (*p)++; - if( ( ret = asn1_get_len( p, end, &tag_len ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - if( ( tag & ASN1_CONTEXT_SPECIFIC ) != ASN1_CONTEXT_SPECIFIC ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); - - if( tag != ( ASN1_CONTEXT_SPECIFIC | 2 ) ) - { - *p += tag_len; - continue; - } - - buf = &(cur->buf); - buf->tag = tag; - buf->p = *p; - buf->len = tag_len; - *p += buf->len; - - /* Allocate and assign next pointer */ - if (*p < end) - { - cur->next = (asn1_sequence *) malloc( - sizeof( asn1_sequence ) ); - - if( cur->next == NULL ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_MALLOC_FAILED ); - - memset( cur->next, 0, sizeof( asn1_sequence ) ); - cur = cur->next; - } - } - - /* Set final sequence entry's next pointer to NULL */ - cur->next = NULL; - - if( *p != end ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); -} - -/* - * X.509 v3 extensions - * - * TODO: Perform all of the basic constraints tests required by the RFC - * TODO: Set values for undetected extensions to a sane default? - * - */ -static int x509_get_crt_ext( unsigned char **p, - const unsigned char *end, - x509_cert *crt ) -{ - int ret; - size_t len; - unsigned char *end_ext_data, *end_ext_octet; - - if( ( ret = x509_get_ext( p, end, &crt->v3_ext, 3 ) ) != 0 ) - { - if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) - return( 0 ); - - return( ret ); - } - - while( *p < end ) - { - /* - * Extension ::= SEQUENCE { - * extnID OBJECT IDENTIFIER, - * critical BOOLEAN DEFAULT FALSE, - * extnValue OCTET STRING } - */ - x509_buf extn_oid = {0, 0, NULL}; - int is_critical = 0; /* DEFAULT FALSE */ - - if( ( ret = asn1_get_tag( p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - end_ext_data = *p + len; - - /* Get extension ID */ - extn_oid.tag = **p; - - if( ( ret = asn1_get_tag( p, end, &extn_oid.len, ASN1_OID ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - extn_oid.p = *p; - *p += extn_oid.len; - - if( ( end - *p ) < 1 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_OUT_OF_DATA ); - - /* Get optional critical */ - if( ( ret = asn1_get_bool( p, end_ext_data, &is_critical ) ) != 0 && - ( ret != POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - /* Data should be octet string type */ - if( ( ret = asn1_get_tag( p, end_ext_data, &len, - ASN1_OCTET_STRING ) ) != 0 ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); - - end_ext_octet = *p + len; - - if( end_ext_octet != end_ext_data ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - /* - * Detect supported extensions - */ - if( ( OID_SIZE( OID_BASIC_CONSTRAINTS ) == extn_oid.len ) && - memcmp( extn_oid.p, OID_BASIC_CONSTRAINTS, extn_oid.len ) == 0 ) - { - /* Parse basic constraints */ - if( ( ret = x509_get_basic_constraints( p, end_ext_octet, - &crt->ca_istrue, &crt->max_pathlen ) ) != 0 ) - return ( ret ); - crt->ext_types |= EXT_BASIC_CONSTRAINTS; - } - else if( ( OID_SIZE( OID_NS_CERT_TYPE ) == extn_oid.len ) && - memcmp( extn_oid.p, OID_NS_CERT_TYPE, extn_oid.len ) == 0 ) - { - /* Parse netscape certificate type */ - if( ( ret = x509_get_ns_cert_type( p, end_ext_octet, - &crt->ns_cert_type ) ) != 0 ) - return ( ret ); - crt->ext_types |= EXT_NS_CERT_TYPE; - } - else if( ( OID_SIZE( OID_KEY_USAGE ) == extn_oid.len ) && - memcmp( extn_oid.p, OID_KEY_USAGE, extn_oid.len ) == 0 ) - { - /* Parse key usage */ - if( ( ret = x509_get_key_usage( p, end_ext_octet, - &crt->key_usage ) ) != 0 ) - return ( ret ); - crt->ext_types |= EXT_KEY_USAGE; - } - else if( ( OID_SIZE( OID_EXTENDED_KEY_USAGE ) == extn_oid.len ) && - memcmp( extn_oid.p, OID_EXTENDED_KEY_USAGE, extn_oid.len ) == 0 ) - { - /* Parse extended key usage */ - if( ( ret = x509_get_ext_key_usage( p, end_ext_octet, - &crt->ext_key_usage ) ) != 0 ) - return ( ret ); - crt->ext_types |= EXT_EXTENDED_KEY_USAGE; - } - else if( ( OID_SIZE( OID_SUBJECT_ALT_NAME ) == extn_oid.len ) && - memcmp( extn_oid.p, OID_SUBJECT_ALT_NAME, extn_oid.len ) == 0 ) - { - /* Parse extended key usage */ - if( ( ret = x509_get_subject_alt_name( p, end_ext_octet, - &crt->subject_alt_names ) ) != 0 ) - return ( ret ); - crt->ext_types |= EXT_SUBJECT_ALT_NAME; - } - else - { - /* No parser found, skip extension */ - *p = end_ext_octet; - -#if !defined(POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION) - if( is_critical ) - { - /* Data is marked as critical: fail */ - return ( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); - } -#endif - } - } - - if( *p != end ) - return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); -} - -/* - * X.509 CRL Entries - */ -static int x509_get_entries( unsigned char **p, - const unsigned char *end, - x509_crl_entry *entry ) -{ - int ret; - size_t entry_len; - x509_crl_entry *cur_entry = entry; - - if( *p == end ) - return( 0 ); - - if( ( ret = asn1_get_tag( p, end, &entry_len, - ASN1_SEQUENCE | ASN1_CONSTRUCTED ) ) != 0 ) - { - if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) - return( 0 ); - - return( ret ); - } - - end = *p + entry_len; - - while( *p < end ) - { - size_t len2; - const unsigned char *end2; - - if( ( ret = asn1_get_tag( p, end, &len2, - ASN1_SEQUENCE | ASN1_CONSTRUCTED ) ) != 0 ) - { - return( ret ); - } - - cur_entry->raw.tag = **p; - cur_entry->raw.p = *p; - cur_entry->raw.len = len2; - end2 = *p + len2; - - if( ( ret = x509_get_serial( p, end2, &cur_entry->serial ) ) != 0 ) - return( ret ); - - if( ( ret = x509_get_time( p, end2, &cur_entry->revocation_date ) ) != 0 ) - return( ret ); - - if( ( ret = x509_get_crl_entry_ext( p, end2, &cur_entry->entry_ext ) ) != 0 ) - return( ret ); - - if ( *p < end ) - { - cur_entry->next = malloc( sizeof( x509_crl_entry ) ); - - if( cur_entry->next == NULL ) - return( POLARSSL_ERR_X509_MALLOC_FAILED ); - - cur_entry = cur_entry->next; - memset( cur_entry, 0, sizeof( x509_crl_entry ) ); - } - } - - return( 0 ); -} - -static int x509_get_sig_alg( const x509_buf *sig_oid, int *sig_alg ) -{ - if( sig_oid->len == 9 && - memcmp( sig_oid->p, OID_PKCS1, 8 ) == 0 ) - { - if( sig_oid->p[8] >= 2 && sig_oid->p[8] <= 5 ) - { - *sig_alg = sig_oid->p[8]; - return( 0 ); - } - - if ( sig_oid->p[8] >= 11 && sig_oid->p[8] <= 14 ) - { - *sig_alg = sig_oid->p[8]; - return( 0 ); - } - - return( POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG ); - } - if( sig_oid->len == 5 && - memcmp( sig_oid->p, OID_RSA_SHA_OBS, 5 ) == 0 ) - { - *sig_alg = SIG_RSA_SHA1; - return( 0 ); - } - - return( POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG ); -} - -/* - * Parse and fill a single X.509 certificate in DER format - */ -int x509parse_crt_der_core( x509_cert *crt, const unsigned char *buf, - size_t buflen ) -{ - int ret; - size_t len; - unsigned char *p, *end, *crt_end; - - /* - * Check for valid input - */ - if( crt == NULL || buf == NULL ) - return( POLARSSL_ERR_X509_INVALID_INPUT ); - - p = (unsigned char *) malloc( len = buflen ); - - if( p == NULL ) - return( POLARSSL_ERR_X509_MALLOC_FAILED ); - - memcpy( p, buf, buflen ); - - buflen = 0; - - crt->raw.p = p; - crt->raw.len = len; - end = p + len; - - /* - * Certificate ::= SEQUENCE { - * tbsCertificate TBSCertificate, - * signatureAlgorithm AlgorithmIdentifier, - * signatureValue BIT STRING } - */ - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - x509_free( crt ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT ); - } - - if( len > (size_t) ( end - p ) ) - { - x509_free( crt ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - } - crt_end = p + len; - - /* - * TBSCertificate ::= SEQUENCE { - */ - crt->tbs.p = p; - - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - x509_free( crt ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); - } - - end = p + len; - crt->tbs.len = end - crt->tbs.p; - - /* - * Version ::= INTEGER { v1(0), v2(1), v3(2) } - * - * CertificateSerialNumber ::= INTEGER - * - * signature AlgorithmIdentifier - */ - if( ( ret = x509_get_version( &p, end, &crt->version ) ) != 0 || - ( ret = x509_get_serial( &p, end, &crt->serial ) ) != 0 || - ( ret = x509_get_alg( &p, end, &crt->sig_oid1 ) ) != 0 ) - { - x509_free( crt ); - return( ret ); - } - - crt->version++; - - if( crt->version > 3 ) - { - x509_free( crt ); - return( POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION ); - } - - if( ( ret = x509_get_sig_alg( &crt->sig_oid1, &crt->sig_alg ) ) != 0 ) - { - x509_free( crt ); - return( ret ); - } - - /* - * issuer Name - */ - crt->issuer_raw.p = p; - - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - x509_free( crt ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); - } - - if( ( ret = x509_get_name( &p, p + len, &crt->issuer ) ) != 0 ) - { - x509_free( crt ); - return( ret ); - } - - crt->issuer_raw.len = p - crt->issuer_raw.p; - - /* - * Validity ::= SEQUENCE { - * notBefore Time, - * notAfter Time } - * - */ - if( ( ret = x509_get_dates( &p, end, &crt->valid_from, - &crt->valid_to ) ) != 0 ) - { - x509_free( crt ); - return( ret ); - } - - /* - * subject Name - */ - crt->subject_raw.p = p; - - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - x509_free( crt ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); - } - - if( len && ( ret = x509_get_name( &p, p + len, &crt->subject ) ) != 0 ) - { - x509_free( crt ); - return( ret ); - } - - crt->subject_raw.len = p - crt->subject_raw.p; - - /* - * SubjectPublicKeyInfo ::= SEQUENCE - * algorithm AlgorithmIdentifier, - * subjectPublicKey BIT STRING } - */ - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - x509_free( crt ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); - } - - if( ( ret = x509_get_pubkey( &p, p + len, &crt->pk_oid, - &crt->rsa.N, &crt->rsa.E ) ) != 0 ) - { - x509_free( crt ); - return( ret ); - } - - if( ( ret = rsa_check_pubkey( &crt->rsa ) ) != 0 ) - { - x509_free( crt ); - return( ret ); - } - - crt->rsa.len = mpi_size( &crt->rsa.N ); - - /* - * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, - * -- If present, version shall be v2 or v3 - * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, - * -- If present, version shall be v2 or v3 - * extensions [3] EXPLICIT Extensions OPTIONAL - * -- If present, version shall be v3 - */ - if( crt->version == 2 || crt->version == 3 ) - { - ret = x509_get_uid( &p, end, &crt->issuer_id, 1 ); - if( ret != 0 ) - { - x509_free( crt ); - return( ret ); - } - } - - if( crt->version == 2 || crt->version == 3 ) - { - ret = x509_get_uid( &p, end, &crt->subject_id, 2 ); - if( ret != 0 ) - { - x509_free( crt ); - return( ret ); - } - } - - if( crt->version == 3 ) - { - ret = x509_get_crt_ext( &p, end, crt); - if( ret != 0 ) - { - x509_free( crt ); - return( ret ); - } - } - - if( p != end ) - { - x509_free( crt ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - } - - end = crt_end; - - /* - * signatureAlgorithm AlgorithmIdentifier, - * signatureValue BIT STRING - */ - if( ( ret = x509_get_alg( &p, end, &crt->sig_oid2 ) ) != 0 ) - { - x509_free( crt ); - return( ret ); - } - - if( crt->sig_oid1.len != crt->sig_oid2.len || - memcmp( crt->sig_oid1.p, crt->sig_oid2.p, crt->sig_oid1.len ) != 0 ) - { - x509_free( crt ); - return( POLARSSL_ERR_X509_CERT_SIG_MISMATCH ); - } - - if( ( ret = x509_get_sig( &p, end, &crt->sig ) ) != 0 ) - { - x509_free( crt ); - return( ret ); - } - - if( p != end ) - { - x509_free( crt ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - } - - return( 0 ); -} - -/* - * Parse one X.509 certificate in DER format from a buffer and add them to a - * chained list - */ -int x509parse_crt_der( x509_cert *chain, const unsigned char *buf, size_t buflen ) -{ - int ret; - x509_cert *crt = chain, *prev = NULL; - - /* - * Check for valid input - */ - if( crt == NULL || buf == NULL ) - return( POLARSSL_ERR_X509_INVALID_INPUT ); - - while( crt->version != 0 && crt->next != NULL ) - { - prev = crt; - crt = crt->next; - } - - /* - * Add new certificate on the end of the chain if needed. - */ - if ( crt->version != 0 && crt->next == NULL) - { - crt->next = (x509_cert *) malloc( sizeof( x509_cert ) ); - - if( crt->next == NULL ) - return( POLARSSL_ERR_X509_MALLOC_FAILED ); - - prev = crt; - crt = crt->next; - memset( crt, 0, sizeof( x509_cert ) ); - } - - if( ( ret = x509parse_crt_der_core( crt, buf, buflen ) ) != 0 ) - { - if( prev ) - prev->next = NULL; - - if( crt != chain ) - free( crt ); - - return( ret ); - } - - return( 0 ); -} - -/* - * Parse one or more PEM certificates from a buffer and add them to the chained list - */ -int x509parse_crt( x509_cert *chain, const unsigned char *buf, size_t buflen ) -{ - int ret, success = 0, first_error = 0, total_failed = 0; - int buf_format = X509_FORMAT_DER; - - /* - * Check for valid input - */ - if( chain == NULL || buf == NULL ) - return( POLARSSL_ERR_X509_INVALID_INPUT ); - - /* - * Determine buffer content. Buffer contains either one DER certificate or - * one or more PEM certificates. - */ -#if defined(POLARSSL_PEM_C) - if( strstr( (const char *) buf, "-----BEGIN CERTIFICATE-----" ) != NULL ) - buf_format = X509_FORMAT_PEM; -#endif - - if( buf_format == X509_FORMAT_DER ) - return x509parse_crt_der( chain, buf, buflen ); - -#if defined(POLARSSL_PEM_C) - if( buf_format == X509_FORMAT_PEM ) - { - pem_context pem; - - while( buflen > 0 ) - { - size_t use_len; - pem_init( &pem ); - - ret = pem_read_buffer( &pem, - "-----BEGIN CERTIFICATE-----", - "-----END CERTIFICATE-----", - buf, NULL, 0, &use_len ); - - if( ret == 0 ) - { - /* - * Was PEM encoded - */ - buflen -= use_len; - buf += use_len; - } - else if( ret == POLARSSL_ERR_PEM_BAD_INPUT_DATA ) - { - return( ret ); - } - else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) - { - pem_free( &pem ); - - /* - * PEM header and footer were found - */ - buflen -= use_len; - buf += use_len; - - if( first_error == 0 ) - first_error = ret; - - continue; - } - else - break; - - ret = x509parse_crt_der( chain, pem.buf, pem.buflen ); - - pem_free( &pem ); - - if( ret != 0 ) - { - /* - * Quit parsing on a memory error - */ - if( ret == POLARSSL_ERR_X509_MALLOC_FAILED ) - return( ret ); - - if( first_error == 0 ) - first_error = ret; - - total_failed++; - continue; - } - - success = 1; - } - } -#endif - - if( success ) - return( total_failed ); - else if( first_error ) - return( first_error ); - else - return( POLARSSL_ERR_X509_CERT_UNKNOWN_FORMAT ); -} - -/* - * Parse one or more CRLs and add them to the chained list - */ -int x509parse_crl( x509_crl *chain, const unsigned char *buf, size_t buflen ) -{ - int ret; - size_t len; - unsigned char *p, *end; - x509_crl *crl; -#if defined(POLARSSL_PEM_C) - size_t use_len; - pem_context pem; -#endif - - crl = chain; - - /* - * Check for valid input - */ - if( crl == NULL || buf == NULL ) - return( POLARSSL_ERR_X509_INVALID_INPUT ); - - while( crl->version != 0 && crl->next != NULL ) - crl = crl->next; - - /* - * Add new CRL on the end of the chain if needed. - */ - if ( crl->version != 0 && crl->next == NULL) - { - crl->next = (x509_crl *) malloc( sizeof( x509_crl ) ); - - if( crl->next == NULL ) - { - x509_crl_free( crl ); - return( POLARSSL_ERR_X509_MALLOC_FAILED ); - } - - crl = crl->next; - memset( crl, 0, sizeof( x509_crl ) ); - } - -#if defined(POLARSSL_PEM_C) - pem_init( &pem ); - ret = pem_read_buffer( &pem, - "-----BEGIN X509 CRL-----", - "-----END X509 CRL-----", - buf, NULL, 0, &use_len ); - - if( ret == 0 ) - { - /* - * Was PEM encoded - */ - buflen -= use_len; - buf += use_len; - - /* - * Steal PEM buffer - */ - p = pem.buf; - pem.buf = NULL; - len = pem.buflen; - pem_free( &pem ); - } - else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) - { - pem_free( &pem ); - return( ret ); - } - else - { - /* - * nope, copy the raw DER data - */ - p = (unsigned char *) malloc( len = buflen ); - - if( p == NULL ) - return( POLARSSL_ERR_X509_MALLOC_FAILED ); - - memcpy( p, buf, buflen ); - - buflen = 0; - } -#else - p = (unsigned char *) malloc( len = buflen ); - - if( p == NULL ) - return( POLARSSL_ERR_X509_MALLOC_FAILED ); - - memcpy( p, buf, buflen ); - - buflen = 0; -#endif - - crl->raw.p = p; - crl->raw.len = len; - end = p + len; - - /* - * CertificateList ::= SEQUENCE { - * tbsCertList TBSCertList, - * signatureAlgorithm AlgorithmIdentifier, - * signatureValue BIT STRING } - */ - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - x509_crl_free( crl ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT ); - } - - if( len != (size_t) ( end - p ) ) - { - x509_crl_free( crl ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - } - - /* - * TBSCertList ::= SEQUENCE { - */ - crl->tbs.p = p; - - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - x509_crl_free( crl ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); - } - - end = p + len; - crl->tbs.len = end - crl->tbs.p; - - /* - * Version ::= INTEGER OPTIONAL { v1(0), v2(1) } - * -- if present, MUST be v2 - * - * signature AlgorithmIdentifier - */ - if( ( ret = x509_crl_get_version( &p, end, &crl->version ) ) != 0 || - ( ret = x509_get_alg( &p, end, &crl->sig_oid1 ) ) != 0 ) - { - x509_crl_free( crl ); - return( ret ); - } - - crl->version++; - - if( crl->version > 2 ) - { - x509_crl_free( crl ); - return( POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION ); - } - - if( ( ret = x509_get_sig_alg( &crl->sig_oid1, &crl->sig_alg ) ) != 0 ) - { - x509_crl_free( crl ); - return( POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG ); - } - - /* - * issuer Name - */ - crl->issuer_raw.p = p; - - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - x509_crl_free( crl ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); - } - - if( ( ret = x509_get_name( &p, p + len, &crl->issuer ) ) != 0 ) - { - x509_crl_free( crl ); - return( ret ); - } - - crl->issuer_raw.len = p - crl->issuer_raw.p; - - /* - * thisUpdate Time - * nextUpdate Time OPTIONAL - */ - if( ( ret = x509_get_time( &p, end, &crl->this_update ) ) != 0 ) - { - x509_crl_free( crl ); - return( ret ); - } - - if( ( ret = x509_get_time( &p, end, &crl->next_update ) ) != 0 ) - { - if ( ret != ( POLARSSL_ERR_X509_CERT_INVALID_DATE + - POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) && - ret != ( POLARSSL_ERR_X509_CERT_INVALID_DATE + - POLARSSL_ERR_ASN1_OUT_OF_DATA ) ) - { - x509_crl_free( crl ); - return( ret ); - } - } - - /* - * revokedCertificates SEQUENCE OF SEQUENCE { - * userCertificate CertificateSerialNumber, - * revocationDate Time, - * crlEntryExtensions Extensions OPTIONAL - * -- if present, MUST be v2 - * } OPTIONAL - */ - if( ( ret = x509_get_entries( &p, end, &crl->entry ) ) != 0 ) - { - x509_crl_free( crl ); - return( ret ); - } - - /* - * crlExtensions EXPLICIT Extensions OPTIONAL - * -- if present, MUST be v2 - */ - if( crl->version == 2 ) - { - ret = x509_get_crl_ext( &p, end, &crl->crl_ext ); - - if( ret != 0 ) - { - x509_crl_free( crl ); - return( ret ); - } - } - - if( p != end ) - { - x509_crl_free( crl ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - } - - end = crl->raw.p + crl->raw.len; - - /* - * signatureAlgorithm AlgorithmIdentifier, - * signatureValue BIT STRING - */ - if( ( ret = x509_get_alg( &p, end, &crl->sig_oid2 ) ) != 0 ) - { - x509_crl_free( crl ); - return( ret ); - } - - if( crl->sig_oid1.len != crl->sig_oid2.len || - memcmp( crl->sig_oid1.p, crl->sig_oid2.p, crl->sig_oid1.len ) != 0 ) - { - x509_crl_free( crl ); - return( POLARSSL_ERR_X509_CERT_SIG_MISMATCH ); - } - - if( ( ret = x509_get_sig( &p, end, &crl->sig ) ) != 0 ) - { - x509_crl_free( crl ); - return( ret ); - } - - if( p != end ) - { - x509_crl_free( crl ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - } - - if( buflen > 0 ) - { - crl->next = (x509_crl *) malloc( sizeof( x509_crl ) ); - - if( crl->next == NULL ) - { - x509_crl_free( crl ); - return( POLARSSL_ERR_X509_MALLOC_FAILED ); - } - - crl = crl->next; - memset( crl, 0, sizeof( x509_crl ) ); - - return( x509parse_crl( crl, buf, buflen ) ); - } - - return( 0 ); -} - -#if defined(POLARSSL_FS_IO) -/* - * Load all data from a file into a given buffer. - */ -int load_file( const char *path, unsigned char **buf, size_t *n ) -{ - FILE *f; - - if( ( f = fopen( path, "rb" ) ) == NULL ) - return( POLARSSL_ERR_X509_FILE_IO_ERROR ); - - fseek( f, 0, SEEK_END ); - *n = (size_t) ftell( f ); - fseek( f, 0, SEEK_SET ); - - if( ( *buf = (unsigned char *) malloc( *n + 1 ) ) == NULL ) - return( POLARSSL_ERR_X509_MALLOC_FAILED ); - - if( fread( *buf, 1, *n, f ) != *n ) - { - fclose( f ); - free( *buf ); - return( POLARSSL_ERR_X509_FILE_IO_ERROR ); - } - - fclose( f ); - - (*buf)[*n] = '\0'; - - return( 0 ); -} - -/* - * Load one or more certificates and add them to the chained list - */ -int x509parse_crtfile( x509_cert *chain, const char *path ) -{ - int ret; - size_t n; - unsigned char *buf; - - if ( (ret = load_file( path, &buf, &n ) ) != 0 ) - return( ret ); - - ret = x509parse_crt( chain, buf, n ); - - memset( buf, 0, n + 1 ); - free( buf ); - - return( ret ); -} - -int x509parse_crtpath( x509_cert *chain, const char *path ) -{ - int ret = 0; -#if defined(_WIN32) - int w_ret; - WCHAR szDir[MAX_PATH]; - char filename[MAX_PATH]; - char *p; - int len = strlen( path ); - - WIN32_FIND_DATAW file_data; - HANDLE hFind; - - if( len > MAX_PATH - 3 ) - return( POLARSSL_ERR_X509_INVALID_INPUT ); - - memset( szDir, 0, sizeof(szDir) ); - memset( filename, 0, MAX_PATH ); - memcpy( filename, path, len ); - filename[len++] = '\\'; - p = filename + len; - filename[len++] = '*'; - - w_ret = MultiByteToWideChar( CP_ACP, 0, path, len, szDir, MAX_PATH - 3 ); - - hFind = FindFirstFileW( szDir, &file_data ); - if (hFind == INVALID_HANDLE_VALUE) - return( POLARSSL_ERR_X509_FILE_IO_ERROR ); - - len = MAX_PATH - len; - do - { - memset( p, 0, len ); - - if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) - continue; - - w_ret = WideCharToMultiByte( CP_ACP, 0, file_data.cFileName, - lstrlenW(file_data.cFileName), - p, len - 1, - NULL, NULL ); - - w_ret = x509parse_crtfile( chain, filename ); - if( w_ret < 0 ) - ret++; - else - ret += w_ret; - } - while( FindNextFileW( hFind, &file_data ) != 0 ); - - if (GetLastError() != ERROR_NO_MORE_FILES) - ret = POLARSSL_ERR_X509_FILE_IO_ERROR; - -cleanup: - FindClose( hFind ); -#else - int t_ret, i; - struct stat sb; - struct dirent entry, *result = NULL; - char entry_name[255]; - DIR *dir = opendir( path ); - - if( dir == NULL) - return( POLARSSL_ERR_X509_FILE_IO_ERROR ); - - while( ( t_ret = readdir_r( dir, &entry, &result ) ) == 0 ) - { - if( result == NULL ) - break; - - snprintf( entry_name, sizeof(entry_name), "%s/%s", path, entry.d_name ); - - i = stat( entry_name, &sb ); - - if( i == -1 ) - return( POLARSSL_ERR_X509_FILE_IO_ERROR ); - - if( !S_ISREG( sb.st_mode ) ) - continue; - - // Ignore parse errors - // - t_ret = x509parse_crtfile( chain, entry_name ); - if( t_ret < 0 ) - ret++; - else - ret += t_ret; - } - closedir( dir ); -#endif - - return( ret ); -} - -/* - * Load one or more CRLs and add them to the chained list - */ -int x509parse_crlfile( x509_crl *chain, const char *path ) -{ - int ret; - size_t n; - unsigned char *buf; - - if ( (ret = load_file( path, &buf, &n ) ) != 0 ) - return( ret ); - - ret = x509parse_crl( chain, buf, n ); - - memset( buf, 0, n + 1 ); - free( buf ); - - return( ret ); -} - -/* - * Load and parse a private RSA key - */ -int x509parse_keyfile( rsa_context *rsa, const char *path, const char *pwd ) -{ - int ret; - size_t n; - unsigned char *buf; - - if ( (ret = load_file( path, &buf, &n ) ) != 0 ) - return( ret ); - - if( pwd == NULL ) - ret = x509parse_key( rsa, buf, n, NULL, 0 ); - else - ret = x509parse_key( rsa, buf, n, - (unsigned char *) pwd, strlen( pwd ) ); - - memset( buf, 0, n + 1 ); - free( buf ); - - return( ret ); -} - -/* - * Load and parse a public RSA key - */ -int x509parse_public_keyfile( rsa_context *rsa, const char *path ) -{ - int ret; - size_t n; - unsigned char *buf; - - if ( (ret = load_file( path, &buf, &n ) ) != 0 ) - return( ret ); - - ret = x509parse_public_key( rsa, buf, n ); - - memset( buf, 0, n + 1 ); - free( buf ); - - return( ret ); -} -#endif /* POLARSSL_FS_IO */ - -/* - * Parse a PKCS#1 encoded private RSA key - */ -static int x509parse_key_pkcs1_der( rsa_context *rsa, - const unsigned char *key, - size_t keylen ) -{ - int ret; - size_t len; - unsigned char *p, *end; - - p = (unsigned char *) key; - end = p + keylen; - - /* - * This function parses the RSAPrivateKey (PKCS#1) - * - * RSAPrivateKey ::= SEQUENCE { - * version Version, - * modulus INTEGER, -- n - * publicExponent INTEGER, -- e - * privateExponent INTEGER, -- d - * prime1 INTEGER, -- p - * prime2 INTEGER, -- q - * exponent1 INTEGER, -- d mod (p-1) - * exponent2 INTEGER, -- d mod (q-1) - * coefficient INTEGER, -- (inverse of q) mod p - * otherPrimeInfos OtherPrimeInfos OPTIONAL - * } - */ - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - } - - end = p + len; - - if( ( ret = asn1_get_int( &p, end, &rsa->ver ) ) != 0 ) - { - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - } - - if( rsa->ver != 0 ) - { - return( POLARSSL_ERR_X509_KEY_INVALID_VERSION + ret ); - } - - if( ( ret = asn1_get_mpi( &p, end, &rsa->N ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->E ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->D ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->P ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->Q ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->DP ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->DQ ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &rsa->QP ) ) != 0 ) - { - rsa_free( rsa ); - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - } - - rsa->len = mpi_size( &rsa->N ); - - if( p != end ) - { - rsa_free( rsa ); - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - } - - if( ( ret = rsa_check_privkey( rsa ) ) != 0 ) - { - rsa_free( rsa ); - return( ret ); - } - - return( 0 ); -} - -/* - * Parse an unencrypted PKCS#8 encoded private RSA key - */ -static int x509parse_key_pkcs8_unencrypted_der( - rsa_context *rsa, - const unsigned char *key, - size_t keylen ) -{ - int ret; - size_t len; - unsigned char *p, *end; - x509_buf pk_alg_oid; - - p = (unsigned char *) key; - end = p + keylen; - - /* - * This function parses the PrivatKeyInfo object (PKCS#8) - * - * PrivateKeyInfo ::= SEQUENCE { - * version Version, - * algorithm AlgorithmIdentifier, - * PrivateKey BIT STRING - * } - * - * AlgorithmIdentifier ::= SEQUENCE { - * algorithm OBJECT IDENTIFIER, - * parameters ANY DEFINED BY algorithm OPTIONAL - * } - * - * The PrivateKey BIT STRING is a PKCS#1 RSAPrivateKey - */ - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - } - - end = p + len; - - if( ( ret = asn1_get_int( &p, end, &rsa->ver ) ) != 0 ) - { - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - } - - if( rsa->ver != 0 ) - { - return( POLARSSL_ERR_X509_KEY_INVALID_VERSION + ret ); - } - - if( ( ret = x509_get_alg( &p, end, &pk_alg_oid ) ) != 0 ) - { - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - } - - /* - * only RSA keys handled at this time - */ - if( pk_alg_oid.len != 9 || - memcmp( pk_alg_oid.p, OID_PKCS1_RSA, 9 ) != 0 ) - { - return( POLARSSL_ERR_X509_UNKNOWN_PK_ALG ); - } - - /* - * Get the OCTET STRING and parse the PKCS#1 format inside - */ - if( ( ret = asn1_get_tag( &p, end, &len, ASN1_OCTET_STRING ) ) != 0 ) - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - - if( ( end - p ) < 1 ) - { - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + - POLARSSL_ERR_ASN1_OUT_OF_DATA ); - } - - end = p + len; - - if( ( ret = x509parse_key_pkcs1_der( rsa, p, end - p ) ) != 0 ) - return( ret ); - - return( 0 ); -} - -/* - * Parse an encrypted PKCS#8 encoded private RSA key - */ -static int x509parse_key_pkcs8_encrypted_der( - rsa_context *rsa, - const unsigned char *key, - size_t keylen, - const unsigned char *pwd, - size_t pwdlen ) -{ - int ret; - size_t len; - unsigned char *p, *end, *end2; - x509_buf pbe_alg_oid, pbe_params; - unsigned char buf[2048]; - - memset(buf, 0, 2048); - - p = (unsigned char *) key; - end = p + keylen; - - if( pwdlen == 0 ) - return( POLARSSL_ERR_X509_PASSWORD_REQUIRED ); - - /* - * This function parses the EncryptedPrivatKeyInfo object (PKCS#8) - * - * EncryptedPrivateKeyInfo ::= SEQUENCE { - * encryptionAlgorithm EncryptionAlgorithmIdentifier, - * encryptedData EncryptedData - * } - * - * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier - * - * EncryptedData ::= OCTET STRING - * - * The EncryptedData OCTET STRING is a PKCS#8 PrivateKeyInfo - */ - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - } - - end = p + len; - - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - } - - end2 = p + len; - - if( ( ret = asn1_get_tag( &p, end, &pbe_alg_oid.len, ASN1_OID ) ) != 0 ) - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - - pbe_alg_oid.p = p; - p += pbe_alg_oid.len; - - /* - * Store the algorithm parameters - */ - pbe_params.p = p; - pbe_params.len = end2 - p; - p += pbe_params.len; - - if( ( ret = asn1_get_tag( &p, end, &len, ASN1_OCTET_STRING ) ) != 0 ) - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - - // buf has been sized to 2048 bytes - if( len > 2048 ) - return( POLARSSL_ERR_X509_INVALID_INPUT ); - - /* - * Decrypt EncryptedData with appropriate PDE - */ -#if defined(POLARSSL_PKCS12_C) - if( OID_CMP( OID_PKCS12_PBE_SHA1_DES3_EDE_CBC, &pbe_alg_oid ) ) - { - if( ( ret = pkcs12_pbe( &pbe_params, PKCS12_PBE_DECRYPT, - POLARSSL_CIPHER_DES_EDE3_CBC, POLARSSL_MD_SHA1, - pwd, pwdlen, p, len, buf ) ) != 0 ) - { - if( ret == POLARSSL_ERR_PKCS12_PASSWORD_MISMATCH ) - return( POLARSSL_ERR_X509_PASSWORD_MISMATCH ); - - return( ret ); - } - } - else if( OID_CMP( OID_PKCS12_PBE_SHA1_DES2_EDE_CBC, &pbe_alg_oid ) ) - { - if( ( ret = pkcs12_pbe( &pbe_params, PKCS12_PBE_DECRYPT, - POLARSSL_CIPHER_DES_EDE_CBC, POLARSSL_MD_SHA1, - pwd, pwdlen, p, len, buf ) ) != 0 ) - { - if( ret == POLARSSL_ERR_PKCS12_PASSWORD_MISMATCH ) - return( POLARSSL_ERR_X509_PASSWORD_MISMATCH ); - - return( ret ); - } - } - else if( OID_CMP( OID_PKCS12_PBE_SHA1_RC4_128, &pbe_alg_oid ) ) - { - if( ( ret = pkcs12_pbe_sha1_rc4_128( &pbe_params, - PKCS12_PBE_DECRYPT, - pwd, pwdlen, - p, len, buf ) ) != 0 ) - { - return( ret ); - } - - // Best guess for password mismatch when using RC4. If first tag is - // not ASN1_CONSTRUCTED | ASN1_SEQUENCE - // - if( *buf != ( ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) - return( POLARSSL_ERR_X509_PASSWORD_MISMATCH ); - } - else -#endif /* POLARSSL_PKCS12_C */ -#if defined(POLARSSL_PKCS5_C) - if( OID_CMP( OID_PKCS5_PBES2, &pbe_alg_oid ) ) - { - if( ( ret = pkcs5_pbes2( &pbe_params, PKCS5_DECRYPT, pwd, pwdlen, - p, len, buf ) ) != 0 ) - { - if( ret == POLARSSL_ERR_PKCS5_PASSWORD_MISMATCH ) - return( POLARSSL_ERR_X509_PASSWORD_MISMATCH ); - - return( ret ); - } - } - else -#endif /* POLARSSL_PKCS5_C */ - return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); - - return x509parse_key_pkcs8_unencrypted_der( rsa, buf, len ); -} - -/* - * Parse a private RSA key - */ -int x509parse_key( rsa_context *rsa, const unsigned char *key, size_t keylen, - const unsigned char *pwd, size_t pwdlen ) -{ - int ret; - -#if defined(POLARSSL_PEM_C) - size_t len; - pem_context pem; - - pem_init( &pem ); - ret = pem_read_buffer( &pem, - "-----BEGIN RSA PRIVATE KEY-----", - "-----END RSA PRIVATE KEY-----", - key, pwd, pwdlen, &len ); - if( ret == 0 ) - { - if( ( ret = x509parse_key_pkcs1_der( rsa, pem.buf, pem.buflen ) ) != 0 ) - { - rsa_free( rsa ); - } - - pem_free( &pem ); - return( ret ); - } - else if( ret == POLARSSL_ERR_PEM_PASSWORD_MISMATCH ) - return( POLARSSL_ERR_X509_PASSWORD_MISMATCH ); - else if( ret == POLARSSL_ERR_PEM_PASSWORD_REQUIRED ) - return( POLARSSL_ERR_X509_PASSWORD_REQUIRED ); - else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) - return( ret ); - - ret = pem_read_buffer( &pem, - "-----BEGIN PRIVATE KEY-----", - "-----END PRIVATE KEY-----", - key, NULL, 0, &len ); - if( ret == 0 ) - { - if( ( ret = x509parse_key_pkcs8_unencrypted_der( rsa, - pem.buf, pem.buflen ) ) != 0 ) - { - rsa_free( rsa ); - } - - pem_free( &pem ); - return( ret ); - } - else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) - return( ret ); - - ret = pem_read_buffer( &pem, - "-----BEGIN ENCRYPTED PRIVATE KEY-----", - "-----END ENCRYPTED PRIVATE KEY-----", - key, NULL, 0, &len ); - if( ret == 0 ) - { - if( ( ret = x509parse_key_pkcs8_encrypted_der( rsa, - pem.buf, pem.buflen, - pwd, pwdlen ) ) != 0 ) - { - rsa_free( rsa ); - } - - pem_free( &pem ); - return( ret ); - } - else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) - return( ret ); -#else - ((void) pwd); - ((void) pwdlen); -#endif /* POLARSSL_PEM_C */ - - // At this point we only know it's not a PEM formatted key. Could be any - // of the known DER encoded private key formats - // - // We try the different DER format parsers to see if one passes without - // error - // - if( ( ret = x509parse_key_pkcs8_encrypted_der( rsa, key, keylen, - pwd, pwdlen ) ) == 0 ) - { - return( 0 ); - } - - rsa_free( rsa ); - - if( ret == POLARSSL_ERR_X509_PASSWORD_MISMATCH ) - { - return( ret ); - } - - if( ( ret = x509parse_key_pkcs8_unencrypted_der( rsa, key, keylen ) ) == 0 ) - return( 0 ); - - rsa_free( rsa ); - - if( ( ret = x509parse_key_pkcs1_der( rsa, key, keylen ) ) == 0 ) - return( 0 ); - - rsa_free( rsa ); - - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT ); -} - -/* - * Parse a public RSA key - */ -int x509parse_public_key( rsa_context *rsa, const unsigned char *key, size_t keylen ) -{ - int ret; - size_t len; - unsigned char *p, *end; - x509_buf alg_oid; -#if defined(POLARSSL_PEM_C) - pem_context pem; - - pem_init( &pem ); - ret = pem_read_buffer( &pem, - "-----BEGIN PUBLIC KEY-----", - "-----END PUBLIC KEY-----", - key, NULL, 0, &len ); - - if( ret == 0 ) - { - /* - * Was PEM encoded - */ - keylen = pem.buflen; - } - else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) - { - pem_free( &pem ); - return( ret ); - } - - p = ( ret == 0 ) ? pem.buf : (unsigned char *) key; -#else - p = (unsigned char *) key; -#endif - end = p + keylen; - - /* - * PublicKeyInfo ::= SEQUENCE { - * algorithm AlgorithmIdentifier, - * PublicKey BIT STRING - * } - * - * AlgorithmIdentifier ::= SEQUENCE { - * algorithm OBJECT IDENTIFIER, - * parameters ANY DEFINED BY algorithm OPTIONAL - * } - * - * RSAPublicKey ::= SEQUENCE { - * modulus INTEGER, -- n - * publicExponent INTEGER -- e - * } - */ - - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { -#if defined(POLARSSL_PEM_C) - pem_free( &pem ); -#endif - rsa_free( rsa ); - return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); - } - - if( ( ret = x509_get_pubkey( &p, end, &alg_oid, &rsa->N, &rsa->E ) ) != 0 ) - { -#if defined(POLARSSL_PEM_C) - pem_free( &pem ); -#endif - rsa_free( rsa ); - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - } - - if( ( ret = rsa_check_pubkey( rsa ) ) != 0 ) - { -#if defined(POLARSSL_PEM_C) - pem_free( &pem ); -#endif - rsa_free( rsa ); - return( ret ); - } - - rsa->len = mpi_size( &rsa->N ); - -#if defined(POLARSSL_PEM_C) - pem_free( &pem ); -#endif - - return( 0 ); -} - -#if defined(POLARSSL_DHM_C) -/* - * Parse DHM parameters - */ -int x509parse_dhm( dhm_context *dhm, const unsigned char *dhmin, size_t dhminlen ) -{ - int ret; - size_t len; - unsigned char *p, *end; -#if defined(POLARSSL_PEM_C) - pem_context pem; - - pem_init( &pem ); - - ret = pem_read_buffer( &pem, - "-----BEGIN DH PARAMETERS-----", - "-----END DH PARAMETERS-----", - dhmin, NULL, 0, &dhminlen ); - - if( ret == 0 ) - { - /* - * Was PEM encoded - */ - dhminlen = pem.buflen; - } - else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) - { - pem_free( &pem ); - return( ret ); - } - - p = ( ret == 0 ) ? pem.buf : (unsigned char *) dhmin; -#else - p = (unsigned char *) dhmin; -#endif - end = p + dhminlen; - - memset( dhm, 0, sizeof( dhm_context ) ); - - /* - * DHParams ::= SEQUENCE { - * prime INTEGER, -- P - * generator INTEGER, -- g - * } - */ - if( ( ret = asn1_get_tag( &p, end, &len, - ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) - { -#if defined(POLARSSL_PEM_C) - pem_free( &pem ); -#endif - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - } - - end = p + len; - - if( ( ret = asn1_get_mpi( &p, end, &dhm->P ) ) != 0 || - ( ret = asn1_get_mpi( &p, end, &dhm->G ) ) != 0 ) - { -#if defined(POLARSSL_PEM_C) - pem_free( &pem ); -#endif - dhm_free( dhm ); - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); - } - - if( p != end ) - { -#if defined(POLARSSL_PEM_C) - pem_free( &pem ); -#endif - dhm_free( dhm ); - return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + - POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); - } - -#if defined(POLARSSL_PEM_C) - pem_free( &pem ); -#endif - - return( 0 ); -} - -#if defined(POLARSSL_FS_IO) -/* - * Load and parse a private RSA key - */ -int x509parse_dhmfile( dhm_context *dhm, const char *path ) -{ - int ret; - size_t n; - unsigned char *buf; - - if ( ( ret = load_file( path, &buf, &n ) ) != 0 ) - return( ret ); - - ret = x509parse_dhm( dhm, buf, n ); - - memset( buf, 0, n + 1 ); - free( buf ); - - return( ret ); -} -#endif /* POLARSSL_FS_IO */ -#endif /* POLARSSL_DHM_C */ - -#if defined _MSC_VER && !defined snprintf -#include - -#if !defined vsnprintf -#define vsnprintf _vsnprintf -#endif // vsnprintf - -/* - * Windows _snprintf and _vsnprintf are not compatible to linux versions. - * Result value is not size of buffer needed, but -1 if no fit is possible. - * - * This fuction tries to 'fix' this by at least suggesting enlarging the - * size by 20. - */ -int compat_snprintf(char *str, size_t size, const char *format, ...) -{ - va_list ap; - int res = -1; - - va_start( ap, format ); - - res = vsnprintf( str, size, format, ap ); - - va_end( ap ); - - // No quick fix possible - if ( res < 0 ) - return( (int) size + 20 ); - - return res; -} - -#define snprintf compat_snprintf -#endif - -#define POLARSSL_ERR_DEBUG_BUF_TOO_SMALL -2 - -#define SAFE_SNPRINTF() \ -{ \ - if( ret == -1 ) \ - return( -1 ); \ - \ - if ( (unsigned int) ret > n ) { \ - p[n - 1] = '\0'; \ - return POLARSSL_ERR_DEBUG_BUF_TOO_SMALL;\ - } \ - \ - n -= (unsigned int) ret; \ - p += (unsigned int) ret; \ -} - -/* - * Store the name in printable form into buf; no more - * than size characters will be written - */ -int x509parse_dn_gets( char *buf, size_t size, const x509_name *dn ) -{ - int ret; - size_t i, n; - unsigned char c; - const x509_name *name; - char s[128], *p; - - memset( s, 0, sizeof( s ) ); - - name = dn; - p = buf; - n = size; - - while( name != NULL ) - { - if( !name->oid.p ) - { - name = name->next; - continue; - } - - if( name != dn ) - { - ret = snprintf( p, n, ", " ); - SAFE_SNPRINTF(); - } - - if( name->oid.len == 3 && - memcmp( name->oid.p, OID_X520, 2 ) == 0 ) - { - switch( name->oid.p[2] ) - { - case X520_COMMON_NAME: - ret = snprintf( p, n, "CN=" ); break; - - case X520_COUNTRY: - ret = snprintf( p, n, "C=" ); break; - - case X520_LOCALITY: - ret = snprintf( p, n, "L=" ); break; - - case X520_STATE: - ret = snprintf( p, n, "ST=" ); break; - - case X520_ORGANIZATION: - ret = snprintf( p, n, "O=" ); break; - - case X520_ORG_UNIT: - ret = snprintf( p, n, "OU=" ); break; - - default: - ret = snprintf( p, n, "0x%02X=", - name->oid.p[2] ); - break; - } - SAFE_SNPRINTF(); - } - else if( name->oid.len == 9 && - memcmp( name->oid.p, OID_PKCS9, 8 ) == 0 ) - { - switch( name->oid.p[8] ) - { - case PKCS9_EMAIL: - ret = snprintf( p, n, "emailAddress=" ); break; - - default: - ret = snprintf( p, n, "0x%02X=", - name->oid.p[8] ); - break; - } - SAFE_SNPRINTF(); - } - else - { - ret = snprintf( p, n, "\?\?=" ); - SAFE_SNPRINTF(); - } - - for( i = 0; i < name->val.len; i++ ) - { - if( i >= sizeof( s ) - 1 ) - break; - - c = name->val.p[i]; - if( c < 32 || c == 127 || ( c > 128 && c < 160 ) ) - s[i] = '?'; - else s[i] = c; - } - s[i] = '\0'; - ret = snprintf( p, n, "%s", s ); - SAFE_SNPRINTF(); - name = name->next; - } - - return( (int) ( size - n ) ); -} - -/* - * Store the serial in printable form into buf; no more - * than size characters will be written - */ -int x509parse_serial_gets( char *buf, size_t size, const x509_buf *serial ) -{ - int ret; - size_t i, n, nr; - char *p; - - p = buf; - n = size; - - nr = ( serial->len <= 32 ) - ? serial->len : 28; - - for( i = 0; i < nr; i++ ) - { - if( i == 0 && nr > 1 && serial->p[i] == 0x0 ) - continue; - - ret = snprintf( p, n, "%02X%s", - serial->p[i], ( i < nr - 1 ) ? ":" : "" ); - SAFE_SNPRINTF(); - } - - if( nr != serial->len ) - { - ret = snprintf( p, n, "...." ); - SAFE_SNPRINTF(); - } - - return( (int) ( size - n ) ); -} - -/* - * Return an informational string about the certificate. - */ -int x509parse_cert_info( char *buf, size_t size, const char *prefix, - const x509_cert *crt ) -{ - int ret; - size_t n; - char *p; - - p = buf; - n = size; - - ret = snprintf( p, n, "%scert. version : %d\n", - prefix, crt->version ); - SAFE_SNPRINTF(); - ret = snprintf( p, n, "%sserial number : ", - prefix ); - SAFE_SNPRINTF(); - - ret = x509parse_serial_gets( p, n, &crt->serial); - SAFE_SNPRINTF(); - - ret = snprintf( p, n, "\n%sissuer name : ", prefix ); - SAFE_SNPRINTF(); - ret = x509parse_dn_gets( p, n, &crt->issuer ); - SAFE_SNPRINTF(); - - ret = snprintf( p, n, "\n%ssubject name : ", prefix ); - SAFE_SNPRINTF(); - ret = x509parse_dn_gets( p, n, &crt->subject ); - SAFE_SNPRINTF(); - - ret = snprintf( p, n, "\n%sissued on : " \ - "%04d-%02d-%02d %02d:%02d:%02d", prefix, - crt->valid_from.year, crt->valid_from.mon, - crt->valid_from.day, crt->valid_from.hour, - crt->valid_from.min, crt->valid_from.sec ); - SAFE_SNPRINTF(); - - ret = snprintf( p, n, "\n%sexpires on : " \ - "%04d-%02d-%02d %02d:%02d:%02d", prefix, - crt->valid_to.year, crt->valid_to.mon, - crt->valid_to.day, crt->valid_to.hour, - crt->valid_to.min, crt->valid_to.sec ); - SAFE_SNPRINTF(); - - ret = snprintf( p, n, "\n%ssigned using : RSA+", prefix ); - SAFE_SNPRINTF(); - - switch( crt->sig_alg ) - { - case SIG_RSA_MD2 : ret = snprintf( p, n, "MD2" ); break; - case SIG_RSA_MD4 : ret = snprintf( p, n, "MD4" ); break; - case SIG_RSA_MD5 : ret = snprintf( p, n, "MD5" ); break; - case SIG_RSA_SHA1 : ret = snprintf( p, n, "SHA1" ); break; - case SIG_RSA_SHA224 : ret = snprintf( p, n, "SHA224" ); break; - case SIG_RSA_SHA256 : ret = snprintf( p, n, "SHA256" ); break; - case SIG_RSA_SHA384 : ret = snprintf( p, n, "SHA384" ); break; - case SIG_RSA_SHA512 : ret = snprintf( p, n, "SHA512" ); break; - default: ret = snprintf( p, n, "???" ); break; - } - SAFE_SNPRINTF(); - - ret = snprintf( p, n, "\n%sRSA key size : %d bits\n", prefix, - (int) crt->rsa.N.n * (int) sizeof( t_uint ) * 8 ); - SAFE_SNPRINTF(); - - return( (int) ( size - n ) ); -} - -/* - * Return an informational string describing the given OID - */ -const char *x509_oid_get_description( x509_buf *oid ) -{ - if ( oid == NULL ) - return ( NULL ); - - else if( OID_CMP( OID_SERVER_AUTH, oid ) ) - return( STRING_SERVER_AUTH ); - - else if( OID_CMP( OID_CLIENT_AUTH, oid ) ) - return( STRING_CLIENT_AUTH ); - - else if( OID_CMP( OID_CODE_SIGNING, oid ) ) - return( STRING_CODE_SIGNING ); - - else if( OID_CMP( OID_EMAIL_PROTECTION, oid ) ) - return( STRING_EMAIL_PROTECTION ); - - else if( OID_CMP( OID_TIME_STAMPING, oid ) ) - return( STRING_TIME_STAMPING ); - - else if( OID_CMP( OID_OCSP_SIGNING, oid ) ) - return( STRING_OCSP_SIGNING ); - - return( NULL ); -} - -/* Return the x.y.z.... style numeric string for the given OID */ -int x509_oid_get_numeric_string( char *buf, size_t size, x509_buf *oid ) -{ - int ret; - size_t i, n; - unsigned int value; - char *p; - - p = buf; - n = size; - - /* First byte contains first two dots */ - if( oid->len > 0 ) - { - ret = snprintf( p, n, "%d.%d", oid->p[0]/40, oid->p[0]%40 ); - SAFE_SNPRINTF(); - } - - /* TODO: value can overflow in value. */ - value = 0; - for( i = 1; i < oid->len; i++ ) - { - value <<= 7; - value += oid->p[i] & 0x7F; - - if( !( oid->p[i] & 0x80 ) ) - { - /* Last byte */ - ret = snprintf( p, n, ".%d", value ); - SAFE_SNPRINTF(); - value = 0; - } - } - - return( (int) ( size - n ) ); -} - -/* - * Return an informational string about the CRL. - */ -int x509parse_crl_info( char *buf, size_t size, const char *prefix, - const x509_crl *crl ) -{ - int ret; - size_t n; - char *p; - const x509_crl_entry *entry; - - p = buf; - n = size; - - ret = snprintf( p, n, "%sCRL version : %d", - prefix, crl->version ); - SAFE_SNPRINTF(); - - ret = snprintf( p, n, "\n%sissuer name : ", prefix ); - SAFE_SNPRINTF(); - ret = x509parse_dn_gets( p, n, &crl->issuer ); - SAFE_SNPRINTF(); - - ret = snprintf( p, n, "\n%sthis update : " \ - "%04d-%02d-%02d %02d:%02d:%02d", prefix, - crl->this_update.year, crl->this_update.mon, - crl->this_update.day, crl->this_update.hour, - crl->this_update.min, crl->this_update.sec ); - SAFE_SNPRINTF(); - - ret = snprintf( p, n, "\n%snext update : " \ - "%04d-%02d-%02d %02d:%02d:%02d", prefix, - crl->next_update.year, crl->next_update.mon, - crl->next_update.day, crl->next_update.hour, - crl->next_update.min, crl->next_update.sec ); - SAFE_SNPRINTF(); - - entry = &crl->entry; - - ret = snprintf( p, n, "\n%sRevoked certificates:", - prefix ); - SAFE_SNPRINTF(); - - while( entry != NULL && entry->raw.len != 0 ) - { - ret = snprintf( p, n, "\n%sserial number: ", - prefix ); - SAFE_SNPRINTF(); - - ret = x509parse_serial_gets( p, n, &entry->serial); - SAFE_SNPRINTF(); - - ret = snprintf( p, n, " revocation date: " \ - "%04d-%02d-%02d %02d:%02d:%02d", - entry->revocation_date.year, entry->revocation_date.mon, - entry->revocation_date.day, entry->revocation_date.hour, - entry->revocation_date.min, entry->revocation_date.sec ); - SAFE_SNPRINTF(); - - entry = entry->next; - } - - ret = snprintf( p, n, "\n%ssigned using : RSA+", prefix ); - SAFE_SNPRINTF(); - - switch( crl->sig_alg ) - { - case SIG_RSA_MD2 : ret = snprintf( p, n, "MD2" ); break; - case SIG_RSA_MD4 : ret = snprintf( p, n, "MD4" ); break; - case SIG_RSA_MD5 : ret = snprintf( p, n, "MD5" ); break; - case SIG_RSA_SHA1 : ret = snprintf( p, n, "SHA1" ); break; - case SIG_RSA_SHA224 : ret = snprintf( p, n, "SHA224" ); break; - case SIG_RSA_SHA256 : ret = snprintf( p, n, "SHA256" ); break; - case SIG_RSA_SHA384 : ret = snprintf( p, n, "SHA384" ); break; - case SIG_RSA_SHA512 : ret = snprintf( p, n, "SHA512" ); break; - default: ret = snprintf( p, n, "???" ); break; - } - SAFE_SNPRINTF(); - - ret = snprintf( p, n, "\n" ); - SAFE_SNPRINTF(); - - return( (int) ( size - n ) ); -} - -/* - * Return 0 if the x509_time is still valid, or 1 otherwise. - */ -int x509parse_time_expired( const x509_time *to ) -{ - int year, mon, day; - int hour, min, sec; - -#if defined(_WIN32) - SYSTEMTIME st; - - GetLocalTime(&st); - - year = st.wYear; - mon = st.wMonth; - day = st.wDay; - hour = st.wHour; - min = st.wMinute; - sec = st.wSecond; -#else - struct tm *lt; - time_t tt; - - tt = time( NULL ); - lt = localtime( &tt ); - - year = lt->tm_year + 1900; - mon = lt->tm_mon + 1; - day = lt->tm_mday; - hour = lt->tm_hour; - min = lt->tm_min; - sec = lt->tm_sec; -#endif - - if( year > to->year ) - return( 1 ); - - if( year == to->year && - mon > to->mon ) - return( 1 ); - - if( year == to->year && - mon == to->mon && - day > to->day ) - return( 1 ); - - if( year == to->year && - mon == to->mon && - day == to->day && - hour > to->hour ) - return( 1 ); - - if( year == to->year && - mon == to->mon && - day == to->day && - hour == to->hour && - min > to->min ) - return( 1 ); - - if( year == to->year && - mon == to->mon && - day == to->day && - hour == to->hour && - min == to->min && - sec > to->sec ) - return( 1 ); - - return( 0 ); -} - -/* - * Return 1 if the certificate is revoked, or 0 otherwise. - */ -int x509parse_revoked( const x509_cert *crt, const x509_crl *crl ) -{ - const x509_crl_entry *cur = &crl->entry; - - while( cur != NULL && cur->serial.len != 0 ) - { - if( crt->serial.len == cur->serial.len && - memcmp( crt->serial.p, cur->serial.p, crt->serial.len ) == 0 ) - { - if( x509parse_time_expired( &cur->revocation_date ) ) - return( 1 ); - } - - cur = cur->next; - } - - return( 0 ); -} - -/* - * Wrapper for x509 hashes. - */ -static void x509_hash( const unsigned char *in, size_t len, int alg, - unsigned char *out ) -{ - switch( alg ) - { -#if defined(POLARSSL_MD2_C) - case SIG_RSA_MD2 : md2( in, len, out ); break; -#endif -#if defined(POLARSSL_MD4_C) - case SIG_RSA_MD4 : md4( in, len, out ); break; -#endif -#if defined(POLARSSL_MD5_C) - case SIG_RSA_MD5 : md5( in, len, out ); break; -#endif -#if defined(POLARSSL_SHA1_C) - case SIG_RSA_SHA1 : sha1( in, len, out ); break; -#endif -#if defined(POLARSSL_SHA2_C) - case SIG_RSA_SHA224 : sha2( in, len, out, 1 ); break; - case SIG_RSA_SHA256 : sha2( in, len, out, 0 ); break; -#endif -#if defined(POLARSSL_SHA4_C) - case SIG_RSA_SHA384 : sha4( in, len, out, 1 ); break; - case SIG_RSA_SHA512 : sha4( in, len, out, 0 ); break; -#endif - default: - memset( out, '\xFF', 64 ); - break; - } -} - -/* - * Check that the given certificate is valid accoring to the CRL. - */ -static int x509parse_verifycrl(x509_cert *crt, x509_cert *ca, - x509_crl *crl_list) -{ - int flags = 0; - int hash_id; - unsigned char hash[64]; - - if( ca == NULL ) - return( flags ); - - /* - * TODO: What happens if no CRL is present? - * Suggestion: Revocation state should be unknown if no CRL is present. - * For backwards compatibility this is not yet implemented. - */ - - while( crl_list != NULL ) - { - if( crl_list->version == 0 || - crl_list->issuer_raw.len != ca->subject_raw.len || - memcmp( crl_list->issuer_raw.p, ca->subject_raw.p, - crl_list->issuer_raw.len ) != 0 ) - { - crl_list = crl_list->next; - continue; - } - - /* - * Check if CRL is correctly signed by the trusted CA - */ - hash_id = crl_list->sig_alg; - - x509_hash( crl_list->tbs.p, crl_list->tbs.len, hash_id, hash ); - - if( !rsa_pkcs1_verify( &ca->rsa, RSA_PUBLIC, hash_id, - 0, hash, crl_list->sig.p ) == 0 ) - { - /* - * CRL is not trusted - */ - flags |= BADCRL_NOT_TRUSTED; - break; - } - - /* - * Check for validity of CRL (Do not drop out) - */ - if( x509parse_time_expired( &crl_list->next_update ) ) - flags |= BADCRL_EXPIRED; - - /* - * Check if certificate is revoked - */ - if( x509parse_revoked(crt, crl_list) ) - { - flags |= BADCERT_REVOKED; - break; - } - - crl_list = crl_list->next; - } - return flags; -} - -int x509_wildcard_verify( const char *cn, x509_buf *name ) -{ - size_t i; - size_t cn_idx = 0; - - if( name->len < 3 || name->p[0] != '*' || name->p[1] != '.' ) - return( 0 ); - - for( i = 0; i < strlen( cn ); ++i ) - { - if( cn[i] == '.' ) - { - cn_idx = i; - break; - } - } - - if( cn_idx == 0 ) - return( 0 ); - - if( strlen( cn ) - cn_idx == name->len - 1 && - memcmp( name->p + 1, cn + cn_idx, name->len - 1 ) == 0 ) - { - return( 1 ); - } - - return( 0 ); -} - -static int x509parse_verify_top( - x509_cert *child, x509_cert *trust_ca, - x509_crl *ca_crl, int path_cnt, int *flags, - int (*f_vrfy)(void *, x509_cert *, int, int *), - void *p_vrfy ) -{ - int hash_id, ret; - int ca_flags = 0, check_path_cnt = path_cnt + 1; - unsigned char hash[64]; - - if( x509parse_time_expired( &child->valid_to ) ) - *flags |= BADCERT_EXPIRED; - - /* - * Child is the top of the chain. Check against the trust_ca list. - */ - *flags |= BADCERT_NOT_TRUSTED; - - while( trust_ca != NULL ) - { - if( trust_ca->version == 0 || - child->issuer_raw.len != trust_ca->subject_raw.len || - memcmp( child->issuer_raw.p, trust_ca->subject_raw.p, - child->issuer_raw.len ) != 0 ) - { - trust_ca = trust_ca->next; - continue; - } - - /* - * Reduce path_len to check against if top of the chain is - * the same as the trusted CA - */ - if( child->subject_raw.len == trust_ca->subject_raw.len && - memcmp( child->subject_raw.p, trust_ca->subject_raw.p, - child->issuer_raw.len ) == 0 ) - { - check_path_cnt--; - } - - if( trust_ca->max_pathlen > 0 && - trust_ca->max_pathlen < check_path_cnt ) - { - trust_ca = trust_ca->next; - continue; - } - - hash_id = child->sig_alg; - - x509_hash( child->tbs.p, child->tbs.len, hash_id, hash ); - - if( rsa_pkcs1_verify( &trust_ca->rsa, RSA_PUBLIC, hash_id, - 0, hash, child->sig.p ) != 0 ) - { - trust_ca = trust_ca->next; - continue; - } - - /* - * Top of chain is signed by a trusted CA - */ - *flags &= ~BADCERT_NOT_TRUSTED; - break; - } - - /* - * If top of chain is not the same as the trusted CA send a verify request - * to the callback for any issues with validity and CRL presence for the - * trusted CA certificate. - */ - if( trust_ca != NULL && - ( child->subject_raw.len != trust_ca->subject_raw.len || - memcmp( child->subject_raw.p, trust_ca->subject_raw.p, - child->issuer_raw.len ) != 0 ) ) - { - /* Check trusted CA's CRL for then chain's top crt */ - *flags |= x509parse_verifycrl( child, trust_ca, ca_crl ); - - if( x509parse_time_expired( &trust_ca->valid_to ) ) - ca_flags |= BADCERT_EXPIRED; - - if( NULL != f_vrfy ) - { - if( ( ret = f_vrfy( p_vrfy, trust_ca, path_cnt + 1, &ca_flags ) ) != 0 ) - return( ret ); - } - } - - /* Call callback on top cert */ - if( NULL != f_vrfy ) - { - if( ( ret = f_vrfy(p_vrfy, child, path_cnt, flags ) ) != 0 ) - return( ret ); - } - - *flags |= ca_flags; - - return( 0 ); -} - -static int x509parse_verify_child( - x509_cert *child, x509_cert *parent, x509_cert *trust_ca, - x509_crl *ca_crl, int path_cnt, int *flags, - int (*f_vrfy)(void *, x509_cert *, int, int *), - void *p_vrfy ) -{ - int hash_id, ret; - int parent_flags = 0; - unsigned char hash[64]; - x509_cert *grandparent; - - if( x509parse_time_expired( &child->valid_to ) ) - *flags |= BADCERT_EXPIRED; - - hash_id = child->sig_alg; - - x509_hash( child->tbs.p, child->tbs.len, hash_id, hash ); - - if( rsa_pkcs1_verify( &parent->rsa, RSA_PUBLIC, hash_id, 0, hash, - child->sig.p ) != 0 ) - *flags |= BADCERT_NOT_TRUSTED; - - /* Check trusted CA's CRL for the given crt */ - *flags |= x509parse_verifycrl(child, parent, ca_crl); - - grandparent = parent->next; - - while( grandparent != NULL ) - { - if( grandparent->version == 0 || - grandparent->ca_istrue == 0 || - parent->issuer_raw.len != grandparent->subject_raw.len || - memcmp( parent->issuer_raw.p, grandparent->subject_raw.p, - parent->issuer_raw.len ) != 0 ) - { - grandparent = grandparent->next; - continue; - } - break; - } - - if( grandparent != NULL ) - { - /* - * Part of the chain - */ - ret = x509parse_verify_child( parent, grandparent, trust_ca, ca_crl, path_cnt + 1, &parent_flags, f_vrfy, p_vrfy ); - if( ret != 0 ) - return( ret ); - } - else - { - ret = x509parse_verify_top( parent, trust_ca, ca_crl, path_cnt + 1, &parent_flags, f_vrfy, p_vrfy ); - if( ret != 0 ) - return( ret ); - } - - /* child is verified to be a child of the parent, call verify callback */ - if( NULL != f_vrfy ) - if( ( ret = f_vrfy( p_vrfy, child, path_cnt, flags ) ) != 0 ) - return( ret ); - - *flags |= parent_flags; - - return( 0 ); -} - -/* - * Verify the certificate validity - */ -int x509parse_verify( x509_cert *crt, - x509_cert *trust_ca, - x509_crl *ca_crl, - const char *cn, int *flags, - int (*f_vrfy)(void *, x509_cert *, int, int *), - void *p_vrfy ) -{ - size_t cn_len; - int ret; - int pathlen = 0; - x509_cert *parent; - x509_name *name; - x509_sequence *cur = NULL; - - *flags = 0; - - if( cn != NULL ) - { - name = &crt->subject; - cn_len = strlen( cn ); - - if( crt->ext_types & EXT_SUBJECT_ALT_NAME ) - { - cur = &crt->subject_alt_names; - - while( cur != NULL ) - { - if( cur->buf.len == cn_len && - memcmp( cn, cur->buf.p, cn_len ) == 0 ) - break; - - if( cur->buf.len > 2 && - memcmp( cur->buf.p, "*.", 2 ) == 0 && - x509_wildcard_verify( cn, &cur->buf ) ) - break; - - cur = cur->next; - } - - if( cur == NULL ) - *flags |= BADCERT_CN_MISMATCH; - } - else - { - while( name != NULL ) - { - if( name->oid.len == 3 && - memcmp( name->oid.p, OID_CN, 3 ) == 0 ) - { - if( name->val.len == cn_len && - memcmp( name->val.p, cn, cn_len ) == 0 ) - break; - - if( name->val.len > 2 && - memcmp( name->val.p, "*.", 2 ) == 0 && - x509_wildcard_verify( cn, &name->val ) ) - break; - } - - name = name->next; - } - - if( name == NULL ) - *flags |= BADCERT_CN_MISMATCH; - } - } - - /* - * Iterate upwards in the given cert chain, to find our crt parent. - * Ignore any upper cert with CA != TRUE. - */ - parent = crt->next; - - while( parent != NULL && parent->version != 0 ) - { - if( parent->ca_istrue == 0 || - crt->issuer_raw.len != parent->subject_raw.len || - memcmp( crt->issuer_raw.p, parent->subject_raw.p, - crt->issuer_raw.len ) != 0 ) - { - parent = parent->next; - continue; - } - break; - } - - if( parent != NULL ) - { - /* - * Part of the chain - */ - ret = x509parse_verify_child( crt, parent, trust_ca, ca_crl, pathlen, flags, f_vrfy, p_vrfy ); - if( ret != 0 ) - return( ret ); - } - else - { - ret = x509parse_verify_top( crt, trust_ca, ca_crl, pathlen, flags, f_vrfy, p_vrfy ); - if( ret != 0 ) - return( ret ); - } - - if( *flags != 0 ) - return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED ); - - return( 0 ); -} - -/* - * Unallocate all certificate data - */ -void x509_free( x509_cert *crt ) -{ - x509_cert *cert_cur = crt; - x509_cert *cert_prv; - x509_name *name_cur; - x509_name *name_prv; - x509_sequence *seq_cur; - x509_sequence *seq_prv; - - if( crt == NULL ) - return; - - do - { - rsa_free( &cert_cur->rsa ); - - name_cur = cert_cur->issuer.next; - while( name_cur != NULL ) - { - name_prv = name_cur; - name_cur = name_cur->next; - memset( name_prv, 0, sizeof( x509_name ) ); - free( name_prv ); - } - - name_cur = cert_cur->subject.next; - while( name_cur != NULL ) - { - name_prv = name_cur; - name_cur = name_cur->next; - memset( name_prv, 0, sizeof( x509_name ) ); - free( name_prv ); - } - - seq_cur = cert_cur->ext_key_usage.next; - while( seq_cur != NULL ) - { - seq_prv = seq_cur; - seq_cur = seq_cur->next; - memset( seq_prv, 0, sizeof( x509_sequence ) ); - free( seq_prv ); - } - - seq_cur = cert_cur->subject_alt_names.next; - while( seq_cur != NULL ) - { - seq_prv = seq_cur; - seq_cur = seq_cur->next; - memset( seq_prv, 0, sizeof( x509_sequence ) ); - free( seq_prv ); - } - - if( cert_cur->raw.p != NULL ) - { - memset( cert_cur->raw.p, 0, cert_cur->raw.len ); - free( cert_cur->raw.p ); - } - - cert_cur = cert_cur->next; - } - while( cert_cur != NULL ); - - cert_cur = crt; - do - { - cert_prv = cert_cur; - cert_cur = cert_cur->next; - - memset( cert_prv, 0, sizeof( x509_cert ) ); - if( cert_prv != crt ) - free( cert_prv ); - } - while( cert_cur != NULL ); -} - -/* - * Unallocate all CRL data - */ -void x509_crl_free( x509_crl *crl ) -{ - x509_crl *crl_cur = crl; - x509_crl *crl_prv; - x509_name *name_cur; - x509_name *name_prv; - x509_crl_entry *entry_cur; - x509_crl_entry *entry_prv; - - if( crl == NULL ) - return; - - do - { - name_cur = crl_cur->issuer.next; - while( name_cur != NULL ) - { - name_prv = name_cur; - name_cur = name_cur->next; - memset( name_prv, 0, sizeof( x509_name ) ); - free( name_prv ); - } - - entry_cur = crl_cur->entry.next; - while( entry_cur != NULL ) - { - entry_prv = entry_cur; - entry_cur = entry_cur->next; - memset( entry_prv, 0, sizeof( x509_crl_entry ) ); - free( entry_prv ); - } - - if( crl_cur->raw.p != NULL ) - { - memset( crl_cur->raw.p, 0, crl_cur->raw.len ); - free( crl_cur->raw.p ); - } - - crl_cur = crl_cur->next; - } - while( crl_cur != NULL ); - - crl_cur = crl; - do - { - crl_prv = crl_cur; - crl_cur = crl_cur->next; - - memset( crl_prv, 0, sizeof( x509_crl ) ); - if( crl_prv != crl ) - free( crl_prv ); - } - while( crl_cur != NULL ); -} - -#if defined(POLARSSL_SELF_TEST) - -#include "polarssl/certs.h" - -/* - * Checkup routine - */ -int x509_self_test( int verbose ) -{ -#if defined(POLARSSL_CERTS_C) && defined(POLARSSL_MD5_C) - int ret; - int flags; - size_t i, j; - x509_cert cacert; - x509_cert clicert; - rsa_context rsa; -#if defined(POLARSSL_DHM_C) - dhm_context dhm; -#endif - - if( verbose != 0 ) - printf( " X.509 certificate load: " ); - - memset( &clicert, 0, sizeof( x509_cert ) ); - - ret = x509parse_crt( &clicert, (const unsigned char *) test_cli_crt, - strlen( test_cli_crt ) ); - if( ret != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( ret ); - } - - memset( &cacert, 0, sizeof( x509_cert ) ); - - ret = x509parse_crt( &cacert, (const unsigned char *) test_ca_crt, - strlen( test_ca_crt ) ); - if( ret != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( ret ); - } - - if( verbose != 0 ) - printf( "passed\n X.509 private key load: " ); - - i = strlen( test_ca_key ); - j = strlen( test_ca_pwd ); - - rsa_init( &rsa, RSA_PKCS_V15, 0 ); - - if( ( ret = x509parse_key( &rsa, - (const unsigned char *) test_ca_key, i, - (const unsigned char *) test_ca_pwd, j ) ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( ret ); - } - - if( verbose != 0 ) - printf( "passed\n X.509 signature verify: "); - - ret = x509parse_verify( &clicert, &cacert, NULL, "PolarSSL Client 2", &flags, NULL, NULL ); - if( ret != 0 ) - { - printf("%02x", flags); - if( verbose != 0 ) - printf( "failed\n" ); - - return( ret ); - } - -#if defined(POLARSSL_DHM_C) - if( verbose != 0 ) - printf( "passed\n X.509 DHM parameter load: " ); - - i = strlen( test_dhm_params ); - j = strlen( test_ca_pwd ); - - if( ( ret = x509parse_dhm( &dhm, (const unsigned char *) test_dhm_params, i ) ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( ret ); - } - - if( verbose != 0 ) - printf( "passed\n\n" ); -#endif - - x509_free( &cacert ); - x509_free( &clicert ); - rsa_free( &rsa ); -#if defined(POLARSSL_DHM_C) - dhm_free( &dhm ); -#endif - - return( 0 ); -#else - ((void) verbose); - return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); -#endif -} - -#endif - -#endif diff --git a/makerom/polarssl/x509write.c b/makerom/polarssl/x509write.c deleted file mode 100644 index 9f5a9100..00000000 --- a/makerom/polarssl/x509write.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * X509 buffer writing functionality - * - * Copyright (C) 2006-2012, 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_X509_WRITE_C) - -#include "polarssl/asn1write.h" -#include "polarssl/x509write.h" -#include "polarssl/x509.h" -#include "polarssl/sha1.h" -#include "polarssl/sha2.h" -#include "polarssl/sha4.h" -#include "polarssl/md4.h" -#include "polarssl/md5.h" - -int x509_write_pubkey_der( unsigned char *buf, size_t size, rsa_context *rsa ) -{ - int ret; - unsigned char *c; - size_t len = 0; - - c = buf + size - 1; - - ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->E ) ); - ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->N ) ); - - ASN1_CHK_ADD( len, asn1_write_len( &c, buf, len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( &c, buf, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ); - - if( c - buf < 1 ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - *--c = 0; - len += 1; - - ASN1_CHK_ADD( len, asn1_write_len( &c, buf, len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( &c, buf, ASN1_BIT_STRING ) ); - - ASN1_CHK_ADD( len, asn1_write_algorithm_identifier( &c, buf, OID_PKCS1_RSA ) ); - - ASN1_CHK_ADD( len, asn1_write_len( &c, buf, len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( &c, buf, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ); - - return( len ); -} - -int x509_write_key_der( unsigned char *buf, size_t size, rsa_context *rsa ) -{ - int ret; - unsigned char *c; - size_t len = 0; - - c = buf + size - 1; - - ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->QP ) ); - ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->DQ ) ); - ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->DP ) ); - ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->Q ) ); - ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->P ) ); - ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->D ) ); - ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->E ) ); - ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->N ) ); - ASN1_CHK_ADD( len, asn1_write_int( &c, buf, 0 ) ); - - ASN1_CHK_ADD( len, asn1_write_len( &c, buf, len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( &c, buf, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ); - - // TODO: Make NON RSA Specific variant later on -/* *--c = 0; - len += 1; - - len += asn1_write_len( &c, len); - len += asn1_write_tag( &c, ASN1_BIT_STRING ); - - len += asn1_write_oid( &c, OID_PKCS1_RSA ); - - len += asn1_write_int( &c, 0 ); - - len += asn1_write_len( &c, len); - len += asn1_write_tag( &c, ASN1_CONSTRUCTED | ASN1_SEQUENCE );*/ - -/* for(i = 0; i < len; ++i) - { - if (i % 16 == 0 ) printf("\n"); - printf("%02x ", c[i]); - } - printf("\n");*/ - - return( len ); -} - -int x509_write_name( unsigned char **p, unsigned char *start, char *oid, - char *name ) -{ - int ret; - size_t string_len = 0; - size_t oid_len = 0; - size_t len = 0; - - // Write PrintableString for all except OID_PKCS9_EMAIL - // - if( OID_SIZE( OID_PKCS9_EMAIL ) == strlen( oid ) && - memcmp( oid, OID_PKCS9_EMAIL, strlen( oid ) ) == 0 ) - { - ASN1_CHK_ADD( string_len, asn1_write_ia5_string( p, start, name ) ); - } - else - ASN1_CHK_ADD( string_len, asn1_write_printable_string( p, start, name ) ); - - // Write OID - // - ASN1_CHK_ADD( oid_len, asn1_write_oid( p, start, oid ) ); - - len = oid_len + string_len; - ASN1_CHK_ADD( len, asn1_write_len( p, start, oid_len + string_len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ); - - ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_CONSTRUCTED | ASN1_SET ) ); - - return( len ); -} - -/* - * Wrapper for x509 hashes. - */ -static void x509_hash( const unsigned char *in, size_t len, int alg, - unsigned char *out ) -{ - switch( alg ) - { -#if defined(POLARSSL_MD2_C) - case SIG_RSA_MD2 : md2( in, len, out ); break; -#endif -#if defined(POLARSSL_MD4_C) - case SIG_RSA_MD4 : md4( in, len, out ); break; -#endif -#if defined(POLARSSL_MD5_C) - case SIG_RSA_MD5 : md5( in, len, out ); break; -#endif -#if defined(POLARSSL_SHA1_C) - case SIG_RSA_SHA1 : sha1( in, len, out ); break; -#endif -#if defined(POLARSSL_SHA2_C) - case SIG_RSA_SHA224 : sha2( in, len, out, 1 ); break; - case SIG_RSA_SHA256 : sha2( in, len, out, 0 ); break; -#endif -#if defined(POLARSSL_SHA4_C) - case SIG_RSA_SHA384 : sha4( in, len, out, 1 ); break; - case SIG_RSA_SHA512 : sha4( in, len, out, 0 ); break; -#endif - default: - memset( out, '\xFF', 64 ); - break; - } -} - -int x509_write_sig( unsigned char **p, unsigned char *start, char *oid, - unsigned char *sig, size_t size ) -{ - int ret; - size_t len = 0; - - if( *p - start < (int) size + 1 ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - len = size; - (*p) -= len; - memcpy( *p, sig, len ); - - *--(*p) = 0; - len += 1; - - ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_BIT_STRING ) ); - - // Write OID - // - ASN1_CHK_ADD( len, asn1_write_algorithm_identifier( p, start, oid ) ); - - return( len ); -} - -int x509_write_cert_req( unsigned char *buf, size_t size, rsa_context *rsa, - x509_req_name *req_name, int hash_id ) -{ - int ret; - char sig_oid[10]; - unsigned char *c, *c2; - unsigned char hash[64]; - unsigned char sig[POLARSSL_MPI_MAX_SIZE]; - unsigned char tmp_buf[2048]; - size_t sub_len = 0, pub_len = 0, sig_len = 0; - size_t len = 0; - x509_req_name *cur = req_name; - - c = tmp_buf + 2048 - 1; - - ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, 0 ) ); - ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | ASN1_CONTEXT_SPECIFIC ) ); - - ASN1_CHK_ADD( pub_len, asn1_write_mpi( &c, tmp_buf, &rsa->E ) ); - ASN1_CHK_ADD( pub_len, asn1_write_mpi( &c, tmp_buf, &rsa->N ) ); - - ASN1_CHK_ADD( pub_len, asn1_write_len( &c, tmp_buf, pub_len ) ); - ASN1_CHK_ADD( pub_len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ); - - if( c - tmp_buf < 1 ) - return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); - - *--c = 0; - pub_len += 1; - - ASN1_CHK_ADD( pub_len, asn1_write_len( &c, tmp_buf, pub_len ) ); - ASN1_CHK_ADD( pub_len, asn1_write_tag( &c, tmp_buf, ASN1_BIT_STRING ) ); - - ASN1_CHK_ADD( pub_len, asn1_write_algorithm_identifier( &c, tmp_buf, OID_PKCS1_RSA ) ); - - len += pub_len; - ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, pub_len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ); - - while( cur != NULL ) - { - ASN1_CHK_ADD( sub_len, x509_write_name( &c, tmp_buf, cur->oid, cur->name ) ); - - cur = cur->next; - } - - len += sub_len; - ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, sub_len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ); - - ASN1_CHK_ADD( len, asn1_write_int( &c, tmp_buf, 0 ) ); - - ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ); - - x509_hash( c, len, hash_id, hash ); - - rsa_pkcs1_sign( rsa, NULL, NULL, RSA_PRIVATE, hash_id, 0, hash, sig ); - - // Generate correct OID - // - memcpy( sig_oid, OID_PKCS1, 8 ); - sig_oid[8] = hash_id; - sig_oid[9] = '\0'; - - c2 = buf + size - 1; - ASN1_CHK_ADD( sig_len, x509_write_sig( &c2, buf, sig_oid, sig, rsa->len ) ); - - c2 -= len; - memcpy( c2, c, len ); - - len += sig_len; - ASN1_CHK_ADD( len, asn1_write_len( &c2, buf, len ) ); - ASN1_CHK_ADD( len, asn1_write_tag( &c2, buf, ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ); - - return( len ); -} - -#endif diff --git a/makerom/polarssl/xtea.c b/makerom/polarssl/xtea.c deleted file mode 100644 index f8ab014f..00000000 --- a/makerom/polarssl/xtea.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * An 32-bit implementation of the XTEA algorithm - * - * Copyright (C) 2006-2013, 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_XTEA_C) - -#include "polarssl/xtea.h" - -#if !defined(POLARSSL_XTEA_ALT) - -/* - * 32-bit integer manipulation macros (big endian) - */ -#ifndef GET_UINT32_BE -#define GET_UINT32_BE(n,b,i) \ -{ \ - (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ - | ( (uint32_t) (b)[(i) + 1] << 16 ) \ - | ( (uint32_t) (b)[(i) + 2] << 8 ) \ - | ( (uint32_t) (b)[(i) + 3] ); \ -} -#endif - -#ifndef PUT_UINT32_BE -#define PUT_UINT32_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 - -/* - * XTEA key schedule - */ -void xtea_setup( xtea_context *ctx, unsigned char key[16] ) -{ - int i; - - memset(ctx, 0, sizeof(xtea_context)); - - for( i = 0; i < 4; i++ ) - { - GET_UINT32_BE( ctx->k[i], key, i << 2 ); - } -} - -/* - * XTEA encrypt function - */ -int xtea_crypt_ecb( xtea_context *ctx, int mode, unsigned char input[8], - unsigned char output[8]) -{ - uint32_t *k, v0, v1, i; - - k = ctx->k; - - GET_UINT32_BE( v0, input, 0 ); - GET_UINT32_BE( v1, input, 4 ); - - if( mode == XTEA_ENCRYPT ) - { - uint32_t sum = 0, delta = 0x9E3779B9; - - for( i = 0; i < 32; i++ ) - { - v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]); - sum += delta; - v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]); - } - } - else /* XTEA_DECRYPT */ - { - uint32_t delta = 0x9E3779B9, sum = delta * 32; - - for( i = 0; i < 32; i++ ) - { - v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]); - sum -= delta; - v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]); - } - } - - PUT_UINT32_BE( v0, output, 0 ); - PUT_UINT32_BE( v1, output, 4 ); - - return( 0 ); -} - -/* - * XTEA-CBC buffer encryption/decryption - */ -int xtea_crypt_cbc( xtea_context *ctx, - int mode, - size_t length, - unsigned char iv[8], - unsigned char *input, - unsigned char *output) -{ - int i; - unsigned char temp[8]; - - if(length % 8) - return( POLARSSL_ERR_XTEA_INVALID_INPUT_LENGTH ); - - if( mode == XTEA_DECRYPT ) - { - while( length > 0 ) - { - memcpy( temp, input, 8 ); - xtea_crypt_ecb( ctx, mode, input, output ); - - for(i = 0; i < 8; i++) - output[i] = (unsigned char)( output[i] ^ iv[i] ); - - memcpy( iv, temp, 8 ); - - input += 8; - output += 8; - length -= 8; - } - } - else - { - while( length > 0 ) - { - for( i = 0; i < 8; i++ ) - output[i] = (unsigned char)( input[i] ^ iv[i] ); - - xtea_crypt_ecb( ctx, mode, output, output ); - memcpy( iv, output, 8 ); - - input += 8; - output += 8; - length -= 8; - } - } - - return( 0 ); -} -#endif /* !POLARSSL_XTEA_ALT */ - -#if defined(POLARSSL_SELF_TEST) - -#include -#include - -/* - * XTEA tests vectors (non-official) - */ - -static const unsigned char xtea_test_key[6][16] = -{ - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f }, - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f }, - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 } -}; - -static const unsigned char xtea_test_pt[6][8] = -{ - { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48 }, - { 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 }, - { 0x5a, 0x5b, 0x6e, 0x27, 0x89, 0x48, 0xd7, 0x7f }, - { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48 }, - { 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 }, - { 0x70, 0xe1, 0x22, 0x5d, 0x6e, 0x4e, 0x76, 0x55 } -}; - -static const unsigned char xtea_test_ct[6][8] = -{ - { 0x49, 0x7d, 0xf3, 0xd0, 0x72, 0x61, 0x2c, 0xb5 }, - { 0xe7, 0x8f, 0x2d, 0x13, 0x74, 0x43, 0x41, 0xd8 }, - { 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 }, - { 0xa0, 0x39, 0x05, 0x89, 0xf8, 0xb8, 0xef, 0xa5 }, - { 0xed, 0x23, 0x37, 0x5a, 0x82, 0x1a, 0x8c, 0x2d }, - { 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 } -}; - -/* - * Checkup routine - */ -int xtea_self_test( int verbose ) -{ - int i; - unsigned char buf[8]; - xtea_context ctx; - - for( i = 0; i < 6; i++ ) - { - if( verbose != 0 ) - printf( " XTEA test #%d: ", i + 1 ); - - memcpy( buf, xtea_test_pt[i], 8 ); - - xtea_setup( &ctx, (unsigned char *) xtea_test_key[i] ); - xtea_crypt_ecb( &ctx, XTEA_ENCRYPT, buf, buf ); - - if( memcmp( buf, xtea_test_ct[i], 8 ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( verbose != 0 ) - printf( "passed\n" ); - } - - if( verbose != 0 ) - printf( "\n" ); - - return( 0 ); -} - -#endif - -#endif From 9823dfd2d85899c28546c79ffc6c200324ee7ef8 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 17 Sep 2015 08:11:36 +0800 Subject: [PATCH 085/317] makerom: added VS2015 solution file. --- makerom/makerom.sln | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 makerom/makerom.sln diff --git a/makerom/makerom.sln b/makerom/makerom.sln new file mode 100644 index 00000000..ff089e36 --- /dev/null +++ b/makerom/makerom.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "makerom", "makerom.vcxproj", "{21926330-F5A5-4643-AD32-D4F167CE226B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {21926330-F5A5-4643-AD32-D4F167CE226B}.Debug|x64.ActiveCfg = Debug|x64 + {21926330-F5A5-4643-AD32-D4F167CE226B}.Debug|x64.Build.0 = Debug|x64 + {21926330-F5A5-4643-AD32-D4F167CE226B}.Debug|x86.ActiveCfg = Debug|Win32 + {21926330-F5A5-4643-AD32-D4F167CE226B}.Debug|x86.Build.0 = Debug|Win32 + {21926330-F5A5-4643-AD32-D4F167CE226B}.Release|x64.ActiveCfg = Release|x64 + {21926330-F5A5-4643-AD32-D4F167CE226B}.Release|x64.Build.0 = Release|x64 + {21926330-F5A5-4643-AD32-D4F167CE226B}.Release|x86.ActiveCfg = Release|Win32 + {21926330-F5A5-4643-AD32-D4F167CE226B}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal From 49e12c8d69aca964b208cabd293041a63fef3237 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 19 Sep 2015 19:46:58 +0800 Subject: [PATCH 086/317] makerom: misc VS2015 stuff --- makerom/makerom.vcxproj | 47 +++----------- makerom/makerom.vcxproj.filters | 105 -------------------------------- 2 files changed, 10 insertions(+), 142 deletions(-) diff --git a/makerom/makerom.vcxproj b/makerom/makerom.vcxproj index 97ca195f..472d5b49 100644 --- a/makerom/makerom.vcxproj +++ b/makerom/makerom.vcxproj @@ -39,7 +39,7 @@ v140 - Application + Makefile false v140 @@ -76,7 +76,15 @@ make rebuild WIN32;NDEBUG;$(NMakePreprocessorDefinitions) - + + C:\Program Files\mingw-w64\x86_64-4.9.2-win32-seh-rt_v4-rev2\mingw64\x86_64-w64-mingw32\include;$(IncludePath) + AllRules.ruleset + true + + + + true + @@ -208,50 +216,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/makerom/makerom.vcxproj.filters b/makerom/makerom.vcxproj.filters index 0f4ba0ba..5fb196a6 100644 --- a/makerom/makerom.vcxproj.filters +++ b/makerom/makerom.vcxproj.filters @@ -443,99 +443,21 @@ Source Files\libyaml - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - Source Files\polarssl Source Files\polarssl - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - Source Files\polarssl Source Files\polarssl - - Source Files\polarssl - - - Source Files\polarssl - Source Files\polarssl - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - Source Files\polarssl @@ -548,33 +470,6 @@ Source Files\polarssl - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - From 1bcc5de71c617729adf9863f929adaaafd9d31fe Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 19 Sep 2015 19:50:26 +0800 Subject: [PATCH 087/317] makerom: now supports unstripped ELFs --- makerom/elf.c | 178 ++++++++++++++++++++++------------------------ makerom/elf.h | 1 + makerom/elf_hdr.h | 38 ++++++---- 3 files changed, 112 insertions(+), 105 deletions(-) diff --git a/makerom/elf.c b/makerom/elf.c index 7bd07ded..8625215e 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -110,13 +110,26 @@ int BuildExeFsCode(ncch_settings *set) if(result) goto finish; finish: - if(result){ - if(result == NOT_ELF_FILE) fprintf(stderr,"[ELF ERROR] Not ELF File\n"); - else if(result == NOT_ARM_ELF) fprintf(stderr,"[ELF ERROR] Not ARM ELF\n"); - else if(result == NON_EXECUTABLE_ELF) fprintf(stderr,"[ELF ERROR] Not Executeable ELF\n"); - else if(result == NOT_FIND_TEXT_SEGMENT) fprintf(stderr,"[ELF ERROR] Failed to retrieve text sections from ELF\n"); - else if(result == NOT_FIND_DATA_SEGMENT) fprintf(stderr,"[ELF ERROR] Failed to retrieve data sections from ELF\n"); - else fprintf(stderr,"[ELF ERROR] Failed to process ELF file (%d)\n",result); + switch (result) { + case (0) : + break; + case (NOT_ELF_FILE) : + fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + break; + case (NOT_ARM_ELF) : + fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + break; + case (NON_EXECUTABLE_ELF) : + fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + break; + case (NOT_FIND_TEXT_SEGMENT) : + fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + break; + case (NOT_FIND_DATA_SEGMENT) : + fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + break; + default: + fprintf(stderr, "[ELF ERROR] Failed to process ELF file (%d)\n", result); } for(int i = 0; i < elf->activeSegments; i++) free(elf->segments[i].sections); @@ -735,30 +748,22 @@ u16 GetElfSectionIndexFromName(char *name, elf_context *elf, u8 *elfFile) bool IsBss(elf_section_entry *section) { - if(section->type == 8 && section->flags == 3) - return true; - return false; + return (section->type == SHT_NOBITS && section->flags == (SHF_WRITE | SHF_ALLOC)); } bool IsData(elf_section_entry *section) { - if(section->type == 1 && section->flags == 3) - return true; - return false; + return (section->type == SHT_PROGBITS && section->flags == (SHF_WRITE | SHF_ALLOC)); } bool IsRoData(elf_section_entry *section) { - if(section->type == 1 && section->flags == 2) - return true; - return false; + return (section->type == SHT_PROGBITS && section->flags == SHF_ALLOC); } bool IsText(elf_section_entry *section) { - if(section->type == 1 && section->flags == 6) - return true; - return false; + return (section->type == SHT_PROGBITS && section->flags == (SHF_ALLOC | SHF_EXECINSTR)); } /* ProgramHeader Functions */ @@ -908,108 +913,99 @@ u64 GetELFProgramEntryAlignment(u16 index, elf_context *elf, u8 *elfFile) return 0; } +void InitSegment(elf_segment *segment) +{ + memset(segment, 0, sizeof(elf_segment)); + + segment->sectionNumMax = 10; + segment->sectionNum = 0; + segment->sections = calloc(segment->sectionNumMax, sizeof(elf_section_entry)); +} + +void AddSegmentSection(elf_segment *segment, elf_section_entry *section) +{ + if (segment->sectionNum < segment->sectionNumMax) + memcpy(&segment->sections[segment->sectionNum], section, sizeof(elf_section_entry)); + else { + segment->sectionNumMax *= 2; + elf_section_entry *tmp = calloc(segment->sectionNumMax, sizeof(elf_section_entry)); + for (int k = 0; k < segment->sectionNum; k++) + memcpy(&tmp[k], &segment->sections[k], sizeof(elf_section_entry)); + free(segment->sections); + segment->sections = tmp; + memcpy(&segment->sections[segment->sectionNum], section, sizeof(elf_section_entry)); + } + + segment->sectionNum++; +} int CreateElfSegments(elf_context *elf, u8 *elfFile) { - int num = 0; // Interate through Each Program Header elf->activeSegments = 0; elf->segments = calloc(elf->programTableEntryCount,sizeof(elf_segment)); - elf_segment *segment = malloc(sizeof(elf_segment)); // Temporary Buffer + + elf_segment segment; + + bool foundFirstSection = false; + int curr, prev; + u32 padding, size, sizeInMemory; + for (int i = 0; i < elf->programTableEntryCount; i++){ - if (elf->programHeaders[i].sizeInMemory != 0 && elf->programHeaders[i].type == 1){ - memset(segment,0,sizeof(elf_segment)); - - bool foundFirstSection = false; - u32 size = 0; - u32 vAddr = elf->programHeaders[i].virtualAddress; - u32 memorySize = elf->programHeaders[i].sizeInMemory; - //printf("Segment Size in memory: 0x%x\n",memorySize); - //printf("Segment Alignment: 0x%x\n",elf->programHeaders[i].alignment); - - u16 SectionInfoCapacity = 10; - segment->sectionNum = 0; - segment->sections = calloc(SectionInfoCapacity,sizeof(elf_section_entry)); + if (elf->programHeaders[i].sizeInMemory != 0 && elf->programHeaders[i].type == PF_X){ + InitSegment(&segment); + + foundFirstSection = false; + size = 0; + sizeInMemory = elf->programHeaders[i].sizeInMemory; // Itterate Through Section Headers - for (int j = num; j < elf->sectionTableEntryCount; j++){ - if (!foundFirstSection){ - if (elf->sections[j].address != vAddr) - continue; - - while (j < (int)elf->sections[j].size && elf->sections[j].address == vAddr && !IsIgnoreSection(elf->sections[j])) - j++; + for (curr = 0; curr < elf->sectionTableEntryCount && size != sizeInMemory; curr++){ + // Skip irrelevant sections + if (IsIgnoreSection(elf->sections[curr])) + continue; - j--; + if (!foundFirstSection) { + if (elf->sections[curr].address != elf->programHeaders[i].virtualAddress) + continue; foundFirstSection = true; - segment->vAddr = elf->sections[j].address; - segment->name = elf->sections[j].name; - } - - if(segment->sectionNum < SectionInfoCapacity) - memcpy(&segment->sections[segment->sectionNum],&elf->sections[j],sizeof(elf_section_entry)); - else{ - SectionInfoCapacity = SectionInfoCapacity*2; - elf_section_entry *tmp = calloc(SectionInfoCapacity,sizeof(elf_section_entry)); - for(int k = 0; k < segment->sectionNum; k++) - memcpy(&tmp[k],&segment->sections[k],sizeof(elf_section_entry)); - free(segment->sections); - segment->sections = tmp; - memcpy(&segment->sections[segment->sectionNum],&elf->sections[j],sizeof(elf_section_entry)); - } - segment->sectionNum++; + segment.vAddr = elf->sections[curr].address; + segment.name = elf->sections[curr].name; - if(size == 0) - size += elf->sections[j].size; - else{ - u32 padding = elf->sections[j].address - (elf->sections[j-1].address + elf->sections[j-1].size); - size += padding + elf->sections[j].size; + AddSegmentSection(&segment, &elf->sections[curr]); + size = elf->sections[curr].size; } - - //printf("Section name: %s",elf->sections[j].name); - //printf(" 0x%lx",elf->sections[j].size); - //printf(" (Total Size: 0x%x)\n",size); - - if (size == memorySize) - break; + else { + AddSegmentSection(&segment, &elf->sections[curr]); + padding = elf->sections[curr].address - (elf->sections[prev].address + elf->sections[prev].size); + size += padding + elf->sections[curr].size; + } + prev = curr; - if (size > memorySize){ - fprintf(stderr,"[ELF ERROR] Too large section size.\n Segment size = 0x%x\n Section Size = 0x%x\n", memorySize, size); + // Catch section parsing fails + if (size > sizeInMemory){ + fprintf(stderr,"[ELF ERROR] Too large section size.\n Segment size = 0x%x\n Section Size = 0x%x\n", sizeInMemory, size); return ELF_SEGMENT_SECTION_SIZE_MISMATCH; } } - if(segment->sectionNum){ - segment->header = &elf->programHeaders[i]; - memcpy(&elf->segments[elf->activeSegments],segment,sizeof(elf_segment)); + if(segment.sectionNum){ + segment.header = &elf->programHeaders[i]; + memcpy(&elf->segments[elf->activeSegments],&segment,sizeof(elf_segment)); elf->activeSegments++; } else{ - free(segment->sections); - free(segment); + free(segment.sections); fprintf(stderr,"[ELF ERROR] Program Header Has no corresponding Sections, ELF Cannot be proccessed\n"); return ELF_SEGMENTS_NOT_FOUND; } } } - free(segment); return 0; } bool IsIgnoreSection(elf_section_entry info) { - if (info.address) - return false; - - if (info.type != 1 && info.type != 0) - return true; - - char IgnoreSectionNames[7][20] = { ".debug_abbrev", ".debug_frame", ".debug_info", ".debug_line", ".debug_loc", ".debug_pubnames", ".comment" }; - for (int i = 0; i < 7; i++){ - if (strcmp(IgnoreSectionNames[i],info.name) == 0) - return true; - } - return false; - + return (info.type != SHT_PROGBITS && info.type != SHT_NOBITS && info.type != SHT_INIT_ARRAY && info.type != SHT_FINI_ARRAY); } diff --git a/makerom/elf.h b/makerom/elf.h index 269f2093..a6b856af 100644 --- a/makerom/elf.h +++ b/makerom/elf.h @@ -45,6 +45,7 @@ typedef struct elf_program_entry *header; u32 sectionNum; + u32 sectionNumMax; elf_section_entry *sections; } elf_segment; diff --git a/makerom/elf_hdr.h b/makerom/elf_hdr.h index a4a76d1e..eb6aa897 100644 --- a/makerom/elf_hdr.h +++ b/makerom/elf_hdr.h @@ -91,20 +91,25 @@ typedef struct #define SHT_REL 9 /* Relocation entries, no addends */ #define SHT_SHLIB 10 /* Reserved */ #define SHT_DYNSYM 11 /* Dynamic linker symbol table */ -#define SHT_NUM 12 /* Number of defined types. */ -#define SHT_LOOS 0x60000000 /* Start OS-specific */ -#define SHT_LOSUNW 0x6ffffffb /* Sun-specific low bound. */ -#define SHT_SUNW_COMDAT 0x6ffffffb -#define SHT_SUNW_syminfo 0x6ffffffc -#define SHT_GNU_verdef 0x6ffffffd /* titleVersion definition section. */ -#define SHT_GNU_verneed 0x6ffffffe /* titleVersion needs section. */ -#define SHT_GNU_versym 0x6fffffff /* titleVersion symbol table. */ -#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ -#define SHT_HIOS 0x6fffffff /* End OS-specific type */ -#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ -#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ -#define SHT_LOUSER 0x80000000 /* Start of application-specific */ -#define SHT_HIUSER 0x8fffffff /* End of application-specific */ +#define SHT_UNKNOWN12 12 +#define SHT_UNKNOWN13 13 +#define SHT_INIT_ARRAY 14 +#define SHT_FINI_ARRAY 15 +#define SHT_PREINIT_ARRAY 16 +#define SHT_GROUP 17 +#define SHT_SYMTAB_SHNDX 18 +#define SHT_NUM 19 + +#define SHF_WRITE 0x01 /* sh_flags */ +#define SHF_ALLOC 0x02 +#define SHF_EXECINSTR 0x04 +#define SHF_MERGE 0x10 +#define SHF_STRINGS 0x20 +#define SHF_INFO_LINK 0x40 +#define SHF_LINK_ORDER 0x80 +#define SHF_OS_NONCONFORMING 0x100 +#define SHF_GROUP 0x200 +#define SHF_TLS 0x400 typedef struct @@ -151,6 +156,11 @@ typedef struct #define PT_LOPROC 0x70000000 /* Start of processor-specific */ #define PT_HIPROC 0x7fffffff /* End of processor-specific */ +#define PF_R 0x4 /* p_flags */ +#define PF_W 0x2 +#define PF_X 0x1 + + typedef struct { u8 p_type[4]; /* Segment type */ From a7e874647fcd8de940a9785ff18d826a7bc6e3fb Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 19 Sep 2015 22:22:41 +0800 Subject: [PATCH 088/317] makerom: fixed type --- makerom/elf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/makerom/elf.c b/makerom/elf.c index 8625215e..c7306be9 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -117,16 +117,16 @@ int BuildExeFsCode(ncch_settings *set) fprintf(stderr, "[ELF ERROR] Not ELF File\n"); break; case (NOT_ARM_ELF) : - fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + fprintf(stderr, "[ELF ERROR] Not ARM ELF\n"); break; case (NON_EXECUTABLE_ELF) : - fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + fprintf(stderr, "[ELF ERROR] Not Executeable ELF\n"); break; case (NOT_FIND_TEXT_SEGMENT) : - fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + fprintf(stderr, "[ELF ERROR] Failed to retrieve text sections from ELF\n"); break; case (NOT_FIND_DATA_SEGMENT) : - fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + fprintf(stderr, "[ELF ERROR] Failed to retrieve data sections from ELF\n"); break; default: fprintf(stderr, "[ELF ERROR] Failed to process ELF file (%d)\n", result); From 8d7cbe60bc5fdee36c401bbd840819bca378d641 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 19 Sep 2015 22:26:34 +0800 Subject: [PATCH 089/317] makerom: fixed typo --- makerom/elf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/makerom/elf.c b/makerom/elf.c index 8625215e..c7306be9 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -117,16 +117,16 @@ int BuildExeFsCode(ncch_settings *set) fprintf(stderr, "[ELF ERROR] Not ELF File\n"); break; case (NOT_ARM_ELF) : - fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + fprintf(stderr, "[ELF ERROR] Not ARM ELF\n"); break; case (NON_EXECUTABLE_ELF) : - fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + fprintf(stderr, "[ELF ERROR] Not Executeable ELF\n"); break; case (NOT_FIND_TEXT_SEGMENT) : - fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + fprintf(stderr, "[ELF ERROR] Failed to retrieve text sections from ELF\n"); break; case (NOT_FIND_DATA_SEGMENT) : - fprintf(stderr, "[ELF ERROR] Not ELF File\n"); + fprintf(stderr, "[ELF ERROR] Failed to retrieve data sections from ELF\n"); break; default: fprintf(stderr, "[ELF ERROR] Failed to process ELF file (%d)\n", result); From c1fb98960e36374c05ca034dd484647f9c6bb14d Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 28 Sep 2015 22:04:47 +0800 Subject: [PATCH 090/317] ctrtool: added cpuspeed/enablel2cache to exheader output --- ctrtool/exheader.c | 14 ++++++++++++-- ctrtool/exheader.h | 4 ++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index 92db1a65..e7fee984 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -136,7 +136,7 @@ void exheader_deserialise_arm11localcaps_permissions(exheader_arm11systemlocalca caps->core_version = getle32(arm11->coreversion); caps->enable_l2_cache = (arm11->flag[0] >> 0) & 1; - caps->use_additional_cores = (arm11->flag[0] >> 1) & 1; + caps->new3ds_cpu_speed = (arm11->flag[0] >> 1) & 1; caps->new3ds_systemmode = (arm11->flag[1] >> 0) & 15; caps->ideal_processor = (arm11->flag[2] >> 0) & 3; @@ -504,7 +504,7 @@ void exheader_verify(exheader_context* ctx) ctx->validold3dssystemmode = Good; ctx->validnew3dssystemmode = Good; ctx->validenablel2cache = Good; - ctx->validuseadditionalcores = Good; + ctx->validnew3dscpuspeed = Good; ctx->validservicecontrol = Good; for(i=0; i<8; i++) @@ -533,6 +533,14 @@ void exheader_verify(exheader_context* ctx) if (ctx->system_local_caps.new3ds_systemmode > accessdesc.new3ds_systemmode) ctx->validnew3dssystemmode = Fail; + if (ctx->system_local_caps.enable_l2_cache != accessdesc.enable_l2_cache) + ctx->validenablel2cache = Fail; + + if (ctx->system_local_caps.new3ds_cpu_speed != accessdesc.new3ds_cpu_speed) + ctx->validnew3dscpuspeed = Fail; + + + // Storage Info Verify if(ctx->system_local_caps.system_saveid[0] & ~accessdesc.system_saveid[0]) @@ -628,6 +636,8 @@ void exheader_print(exheader_context* ctx) fprintf(stdout, "Core version: 0x%X\n", getle32(ctx->header.arm11systemlocalcaps.coreversion)); fprintf(stdout, "System mode: %d %s\n", ctx->system_local_caps.old3ds_systemmode, exheader_getvalidstring(ctx->validold3dssystemmode)); fprintf(stdout, "System mode (New3DS): %d %s\n", ctx->system_local_caps.new3ds_systemmode, exheader_getvalidstring(ctx->validnew3dssystemmode)); + fprintf(stdout, "CPU Speed (New3DS): %d %s\n", ctx->system_local_caps.new3ds_cpu_speed? "804MHz" : "268MHz", exheader_getvalidstring(ctx->validnew3dscpuspeed)); + fprintf(stdout, "Enable L2 Cache: %d %s\n", ctx->system_local_caps.enable_l2_cache ? "YES" : "NO", exheader_getvalidstring(ctx->validnew3dscpuspeed)); fprintf(stdout, "Ideal processor: %d %s\n", ctx->system_local_caps.ideal_processor, exheader_getvalidstring(ctx->valididealprocessor)); fprintf(stdout, "Affinity mask: %d %s\n", ctx->system_local_caps.affinity_mask, exheader_getvalidstring(ctx->validaffinitymask)); fprintf(stdout, "Main thread priority: %d %s\n", ctx->system_local_caps.priority, exheader_getvalidstring(ctx->validpriority)); diff --git a/ctrtool/exheader.h b/ctrtool/exheader.h index a4432c26..f0ac02c5 100644 --- a/ctrtool/exheader.h +++ b/ctrtool/exheader.h @@ -72,7 +72,7 @@ typedef struct // flag u8 enable_l2_cache; - u8 use_additional_cores; + u8 new3ds_cpu_speed; u8 new3ds_systemmode; u8 ideal_processor; u8 affinity_mask; @@ -154,7 +154,7 @@ typedef struct int validold3dssystemmode; int validnew3dssystemmode; int validenablel2cache; - int validuseadditionalcores; + int validnew3dscpuspeed; int validcoreversion; int validsystemsaveID[2]; int validaccessinfo; From af7789f7e80e8d03f4a38fc20dc9acefe27101d1 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 28 Sep 2015 22:05:41 +0800 Subject: [PATCH 091/317] makerom: fixed romfs bug --- makerom/romfs_gen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 5a99ba4a..9f35c606 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -376,7 +376,7 @@ void AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) u32_to_u8(entry->namesize,0,LE); /* Get hash table index */ - hashindex = GetFileHashTableIndex(ctx, parent, (fs_romfs_char*)ROMFS_EMPTY_PATH); + hashindex = GetDirHashTableIndex(ctx, parent, (fs_romfs_char*)ROMFS_EMPTY_PATH); /* Increment used dir table length */ ctx->u_dirTableLen += sizeof(romfs_direntry); @@ -390,7 +390,7 @@ void AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) memcpy(name_pos,(u8*)fs->name,fs->name_len); /* Get hash table index */ - hashindex = GetFileHashTableIndex(ctx, parent, fs->name); + hashindex = GetDirHashTableIndex(ctx, parent, fs->name); /* Increment used dir table length */ ctx->u_dirTableLen += sizeof(romfs_direntry) + (u32)align(fs->name_len,4); From 91cdb199f944a664fcbf39810d29cf2fb5a4a87d Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 28 Sep 2015 22:48:31 +0800 Subject: [PATCH 092/317] makerom: added n3ds core2 access flag --- makerom/exheader.c | 2 ++ makerom/exheader.h | 1 + makerom/rsf_settings.c | 1 + makerom/user_settings.h | 1 + 4 files changed, 5 insertions(+) diff --git a/makerom/exheader.c b/makerom/exheader.c index e159ebe8..3a9f5f63 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -1014,6 +1014,8 @@ int SetARM11KernelDescOtherCapabilities(ARM11KernelCapabilityDescriptor *desc, r otherCapabilities |= othcap_RUNNABLE_ON_SLEEP; if(rsf->AccessControlInfo.SpecialMemoryArrange) otherCapabilities |= othcap_SPECIAL_MEMORY_ARRANGE; + if (rsf->AccessControlInfo.CanAccessCore2) + otherCapabilities |= othcap_CAN_ACCESS_CORE2; if(rsf->AccessControlInfo.MemoryType){ if(strcasecmp(rsf->AccessControlInfo.MemoryType,"application") == 0) diff --git a/makerom/exheader.h b/makerom/exheader.h index 4bac931d..d9cfd616 100644 --- a/makerom/exheader.h +++ b/makerom/exheader.h @@ -45,6 +45,7 @@ typedef enum othcap_CAN_SHARE_DEVICE_MEMORY = (1 << 6), othcap_RUNNABLE_ON_SLEEP = (1 << 7), othcap_SPECIAL_MEMORY_ARRANGE = (1 << 12), + othcap_CAN_ACCESS_CORE2 = (1 << 13), } other_capabilities_flags; typedef enum diff --git a/makerom/rsf_settings.c b/makerom/rsf_settings.c index 8240b456..ede105d5 100644 --- a/makerom/rsf_settings.c +++ b/makerom/rsf_settings.c @@ -102,6 +102,7 @@ void GET_AccessControlInfo(ctr_yaml_context *ctx, rsf_settings *rsf) else if(cmpYamlValue("UseOtherVariationSaveData",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.UseOtherVariationSaveData,"UseOtherVariationSaveData",ctx); else if(cmpYamlValue("RunnableOnSleep",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.RunnableOnSleep,"RunnableOnSleep",ctx); else if(cmpYamlValue("SpecialMemoryArrange",ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.SpecialMemoryArrange,"SpecialMemoryArrange",ctx); + else if(cmpYamlValue("CanAccessCore2", ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.CanAccessCore2, "CanAccessCore2", ctx); else if(cmpYamlValue("UseExtSaveData", ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.UseExtSaveData, "UseExtSaveData", ctx); else if(cmpYamlValue("EnableL2Cache", ctx)) SetBoolYAMLValue(&rsf->AccessControlInfo.EnableL2Cache, "EnableL2Cache", ctx); diff --git a/makerom/user_settings.h b/makerom/user_settings.h index 299f5ef9..a2cacb81 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -86,6 +86,7 @@ typedef struct bool UseOtherVariationSaveData; bool RunnableOnSleep; bool SpecialMemoryArrange; + bool CanAccessCore2; bool UseExtSaveData; bool EnableL2Cache; From 0c19bcddb68d349cd5ce6f52572b7dee2dadba92 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 28 Sep 2015 23:05:56 +0800 Subject: [PATCH 093/317] makerom: allow encrypting ncch with test PKI --- makerom/keyset.c | 6 ++---- makerom/ncch.c | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/makerom/keyset.c b/makerom/keyset.c index 66ebeff6..a270d38d 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -96,10 +96,8 @@ int LoadKeysFromResources(keys_struct *keys) SetCurrentCommonKey(keys,0); // NCCH - keys->aes.normalKey = NULL; - keys->aes.systemFixedKey = NULL; - //SetNormalKey(keys,zeros_aesKey); - //SetSystemFixedKey(keys,(u8*)zeros_aesKey); + SetNormalKey(keys,zeros_aesKey); + SetSystemFixedKey(keys,zeros_aesKey); /* RSA Keys */ keys->rsa.isFalseSign = true; diff --git a/makerom/ncch.c b/makerom/ncch.c index 438940bf..0cc87ecf 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -49,8 +49,7 @@ int SignCXI(ncch_hdr *hdr, keys_struct *keys) int CheckCXISignature(ncch_hdr *hdr, u8 *pubk) { - int result = RsaSignVerify(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),pubk,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); - return result; + return RsaSignVerify(GetNcchHdrData(hdr), GetNcchHdrDataLen(hdr), GetNcchHdrSig(hdr), pubk, NULL, RSA_2048_SHA256, CTR_RSA_VERIFY); } // NCCH Build Functions From 0fa36aa7dde14ffe10608504b40b807b736f6877 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 29 Sep 2015 00:08:46 +0800 Subject: [PATCH 094/317] ctrtool: n3ds core 2 access flag detection. --- ctrtool/exheader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index e7fee984..7312f036 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -278,6 +278,7 @@ void exheader_print_arm11kernelcapabilities(exheader_context* ctx) 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"); + fprintf(stdout, " > Access Core 2: %s\n", (descriptor&(1<<13))?"YES":"NO"); switch(memorytype) From 02e43005f8b9440a3dada6f07f88f4c10b020a5d Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 29 Sep 2015 00:22:42 +0800 Subject: [PATCH 095/317] makerom: fixed romfs little endian dependent code --- makerom/romfs.h | 6 ++---- makerom/romfs_gen.c | 19 ++++++++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/makerom/romfs.h b/makerom/romfs.h index 54bc218e..96ec1233 100644 --- a/makerom/romfs.h +++ b/makerom/romfs.h @@ -86,18 +86,16 @@ typedef struct fs_dir *fs; - u32 *dirHashTable; + u8 *dirHashTable; u32 m_dirHashTable; - u32 u_dirHashTable; u8 *dirTable; u32 dirNum; u32 m_dirTableLen; u32 u_dirTableLen; - u32 *fileHashTable; + u8 *fileHashTable; u32 m_fileHashTable; - u32 u_fileHashTable; u8 *fileTable; u32 fileNum; diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 9f35c606..552a932f 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -186,10 +186,8 @@ void CalcRomfsSize(romfs_buildctx *ctx) ctx->dirNum = 1; // root dir CalcDirSize(ctx,ctx->fs); - ctx->u_dirHashTable = 0; ctx->m_dirHashTable = GetHashTableCount(ctx->dirNum); - ctx->u_fileHashTable = 0; ctx->m_fileHashTable = GetHashTableCount(ctx->fileNum); u32 romfsHdrSize = align(sizeof(romfs_infoheader) + ctx->m_dirHashTable*sizeof(u32) + ctx->m_dirTableLen + ctx->m_fileHashTable*sizeof(u32) + ctx->m_fileTableLen,0x10); @@ -267,7 +265,7 @@ void BuildRomfsHeader(romfs_buildctx *ctx) for(int i = 0; i < 4; i++){ if(i == 0){ - ctx->dirHashTable = (u32*)(ctx->level[3].pos + level3_pos); + ctx->dirHashTable = ctx->level[3].pos + level3_pos; u32_to_u8(ctx->romfsHdr->section[i].offset,level3_pos,LE); u32_to_u8(ctx->romfsHdr->section[i].size,ctx->m_dirHashTable*sizeof(u32),LE); level3_pos += ctx->m_dirHashTable*sizeof(u32); @@ -279,7 +277,7 @@ void BuildRomfsHeader(romfs_buildctx *ctx) level3_pos += ctx->m_dirTableLen; } else if(i == 2){ - ctx->fileHashTable = (u32*)(ctx->level[3].pos + level3_pos); + ctx->fileHashTable = ctx->level[3].pos + level3_pos; u32_to_u8(ctx->romfsHdr->section[i].offset,level3_pos,LE); u32_to_u8(ctx->romfsHdr->section[i].size,ctx->m_fileHashTable*sizeof(u32),LE); level3_pos += ctx->m_fileHashTable*sizeof(u32); @@ -300,11 +298,11 @@ void BuildRomfsHeader(romfs_buildctx *ctx) u32_to_u8(ctx->romfsHdr->dataoffset,align(level3_pos,0x10),LE); for (u32 i = 0; i < ctx->m_dirHashTable; i++) { - ctx->dirHashTable[i] = ROMFS_UNUSED_ENTRY; + u32_to_u8(ctx->dirHashTable+i*4, ROMFS_UNUSED_ENTRY, LE); } for (u32 i = 0; i < ctx->m_fileHashTable; i++) { - ctx->fileHashTable[i] = ROMFS_UNUSED_ENTRY; + u32_to_u8(ctx->fileHashTable+i*4, ROMFS_UNUSED_ENTRY, LE); } } @@ -335,9 +333,8 @@ void AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) /* Set hash data */ u32 hashindex = GetFileHashTableIndex(ctx, parent, file->name); - u32_to_u8(entry->hashoffset, ctx->fileHashTable[hashindex], LE); - ctx->fileHashTable[hashindex] = ctx->u_fileTableLen; - + u32_to_u8(entry->hashoffset, u8_to_u32(ctx->fileHashTable + hashindex*4, LE), LE); + u32_to_u8(ctx->fileHashTable + hashindex*4, ctx->u_fileTableLen, LE); /* Import data */ if(file->size) @@ -397,8 +394,8 @@ void AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) } /* Set hash data */ - u32_to_u8(entry->hashoffset, ctx->dirHashTable[hashindex], LE); - ctx->dirHashTable[hashindex] = offset; + u32_to_u8(entry->hashoffset, u8_to_u32(ctx->dirHashTable + hashindex*4, LE), LE); + u32_to_u8(ctx->dirHashTable + hashindex*4, offset, LE); } void AddDirChildrenToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 dir) From 2d30a8583257ba46de1d8cbdaa5b09408b3e23c9 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 29 Sep 2015 01:26:54 +0800 Subject: [PATCH 096/317] ctrtool: fixed fprintf fail --- ctrtool/exheader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index 7312f036..d35f0bb2 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -637,8 +637,8 @@ void exheader_print(exheader_context* ctx) fprintf(stdout, "Core version: 0x%X\n", getle32(ctx->header.arm11systemlocalcaps.coreversion)); fprintf(stdout, "System mode: %d %s\n", ctx->system_local_caps.old3ds_systemmode, exheader_getvalidstring(ctx->validold3dssystemmode)); fprintf(stdout, "System mode (New3DS): %d %s\n", ctx->system_local_caps.new3ds_systemmode, exheader_getvalidstring(ctx->validnew3dssystemmode)); - fprintf(stdout, "CPU Speed (New3DS): %d %s\n", ctx->system_local_caps.new3ds_cpu_speed? "804MHz" : "268MHz", exheader_getvalidstring(ctx->validnew3dscpuspeed)); - fprintf(stdout, "Enable L2 Cache: %d %s\n", ctx->system_local_caps.enable_l2_cache ? "YES" : "NO", exheader_getvalidstring(ctx->validnew3dscpuspeed)); + fprintf(stdout, "CPU Speed (New3DS): %s %s\n", ctx->system_local_caps.new3ds_cpu_speed? "804MHz" : "268MHz", exheader_getvalidstring(ctx->validnew3dscpuspeed)); + fprintf(stdout, "Enable L2 Cache: %s %s\n", ctx->system_local_caps.enable_l2_cache ? "YES" : "NO", exheader_getvalidstring(ctx->validnew3dscpuspeed)); fprintf(stdout, "Ideal processor: %d %s\n", ctx->system_local_caps.ideal_processor, exheader_getvalidstring(ctx->valididealprocessor)); fprintf(stdout, "Affinity mask: %d %s\n", ctx->system_local_caps.affinity_mask, exheader_getvalidstring(ctx->validaffinitymask)); fprintf(stdout, "Main thread priority: %d %s\n", ctx->system_local_caps.priority, exheader_getvalidstring(ctx->validpriority)); From a8635fc601b5d4d7b3f588549d933891686bd65d Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 29 Sep 2015 16:24:44 +0800 Subject: [PATCH 097/317] makerom: fixed linux segfault when using prebuilt romfs binaries. --- makerom/romfs.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/makerom/romfs.c b/makerom/romfs.c index 64190c44..9e3956a1 100644 --- a/makerom/romfs.c +++ b/makerom/romfs.c @@ -45,8 +45,6 @@ int BuildRomFs(romfs_buildctx *ctx) void FreeRomFsCtx(romfs_buildctx *ctx) { - if(ctx->romfsBinary) - fclose(ctx->romfsBinary); if(ctx->fs){ fs_FreeFiles(ctx->fs); fs_FreeDir(ctx->fs); From 7ab299b57f3de03ea23388a113e8bf2db411d60c Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 6 Oct 2015 10:42:28 +0800 Subject: [PATCH 098/317] [makerom] Fixed ELF parsing bug. https://github.com/profi200/Project_CTR/pull/2#issuecomment-145699191 --- makerom/elf.c | 13 ++++++++++++- makerom/elf_hdr.h | 33 +++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/makerom/elf.c b/makerom/elf.c index c7306be9..c281a086 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -955,6 +955,8 @@ int CreateElfSegments(elf_context *elf, u8 *elfFile) if (elf->programHeaders[i].sizeInMemory != 0 && elf->programHeaders[i].type == PF_X){ InitSegment(&segment); + printf("new segment\n"); + foundFirstSection = false; size = 0; sizeInMemory = elf->programHeaders[i].sizeInMemory; @@ -965,10 +967,14 @@ int CreateElfSegments(elf_context *elf, u8 *elfFile) if (IsIgnoreSection(elf->sections[curr])) continue; + if (!foundFirstSection) { if (elf->sections[curr].address != elf->programHeaders[i].virtualAddress) continue; + printf("first section name: %s (vaddr = 0x%llx, size = 0x%llx)\n", elf->sections[curr].name, elf->sections[curr].address, elf->sections[curr].size); + + foundFirstSection = true; segment.vAddr = elf->sections[curr].address; segment.name = elf->sections[curr].name; @@ -977,6 +983,9 @@ int CreateElfSegments(elf_context *elf, u8 *elfFile) size = elf->sections[curr].size; } else { + + printf("follw section name: %s (vaddr = 0x%llx, size = 0x%llx)\n", elf->sections[curr].name, elf->sections[curr].address, elf->sections[curr].size); + AddSegmentSection(&segment, &elf->sections[curr]); padding = elf->sections[curr].address - (elf->sections[prev].address + elf->sections[prev].size); size += padding + elf->sections[curr].size; @@ -1007,5 +1016,7 @@ int CreateElfSegments(elf_context *elf, u8 *elfFile) bool IsIgnoreSection(elf_section_entry info) { - return (info.type != SHT_PROGBITS && info.type != SHT_NOBITS && info.type != SHT_INIT_ARRAY && info.type != SHT_FINI_ARRAY); + printf("%s:0x%x,0x%x\n", info.name, info.type, info.flags); + + return (info.type != SHT_PROGBITS && info.type != SHT_NOBITS && info.type != SHT_INIT_ARRAY && info.type != SHT_FINI_ARRAY && info.type != SHT_ARM_EXIDX); } diff --git a/makerom/elf_hdr.h b/makerom/elf_hdr.h index eb6aa897..eac9d64e 100644 --- a/makerom/elf_hdr.h +++ b/makerom/elf_hdr.h @@ -79,26 +79,31 @@ typedef struct /* Legal values for sh_type (section type). */ -#define SHT_NULL 0 /* Section header table entry unused */ -#define SHT_PROGBITS 1 /* Program data */ -#define SHT_SYMTAB 2 /* Symbol table */ -#define SHT_STRTAB 3 /* String table */ -#define SHT_RELA 4 /* Relocation entries with addends */ -#define SHT_HASH 5 /* Symbol hash table */ -#define SHT_DYNAMIC 6 /* Dynamic linking information */ -#define SHT_NOTE 7 /* Notes */ -#define SHT_NOBITS 8 /* Program space with no data (bss) */ -#define SHT_REL 9 /* Relocation entries, no addends */ -#define SHT_SHLIB 10 /* Reserved */ -#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ #define SHT_UNKNOWN12 12 #define SHT_UNKNOWN13 13 #define SHT_INIT_ARRAY 14 #define SHT_FINI_ARRAY 15 #define SHT_PREINIT_ARRAY 16 -#define SHT_GROUP 17 +#define SHT_GROUP 17 #define SHT_SYMTAB_SHNDX 18 -#define SHT_NUM 19 +#define SHT_NUM 19 +#define SHT_ARM_EXIDX 0x70000001 /* Exception Index table */ +#define SHT_ARM_PREEMPTMAP 0x70000002 /* BPABI DLL dynamic linking pre-emption map*/ +#define SHT_ARM_ATTRIBUTES 0x70000003 /* Object file compatibility attributes */ +#define SHT_ARM_DEBUGOVERLAY 0x70000004 +#define SHT_ARM_OVERLAYSECTION 0x70000005 #define SHF_WRITE 0x01 /* sh_flags */ #define SHF_ALLOC 0x02 From ea695ba66acf66b5f194328b3d15041cd51c75c5 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 6 Oct 2015 10:55:59 +0800 Subject: [PATCH 099/317] [makerom] Removed debug code. --- makerom/elf.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/makerom/elf.c b/makerom/elf.c index c281a086..17168e29 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -955,8 +955,6 @@ int CreateElfSegments(elf_context *elf, u8 *elfFile) if (elf->programHeaders[i].sizeInMemory != 0 && elf->programHeaders[i].type == PF_X){ InitSegment(&segment); - printf("new segment\n"); - foundFirstSection = false; size = 0; sizeInMemory = elf->programHeaders[i].sizeInMemory; @@ -972,9 +970,6 @@ int CreateElfSegments(elf_context *elf, u8 *elfFile) if (elf->sections[curr].address != elf->programHeaders[i].virtualAddress) continue; - printf("first section name: %s (vaddr = 0x%llx, size = 0x%llx)\n", elf->sections[curr].name, elf->sections[curr].address, elf->sections[curr].size); - - foundFirstSection = true; segment.vAddr = elf->sections[curr].address; segment.name = elf->sections[curr].name; @@ -983,9 +978,6 @@ int CreateElfSegments(elf_context *elf, u8 *elfFile) size = elf->sections[curr].size; } else { - - printf("follw section name: %s (vaddr = 0x%llx, size = 0x%llx)\n", elf->sections[curr].name, elf->sections[curr].address, elf->sections[curr].size); - AddSegmentSection(&segment, &elf->sections[curr]); padding = elf->sections[curr].address - (elf->sections[prev].address + elf->sections[prev].size); size += padding + elf->sections[curr].size; @@ -1016,7 +1008,5 @@ int CreateElfSegments(elf_context *elf, u8 *elfFile) bool IsIgnoreSection(elf_section_entry info) { - printf("%s:0x%x,0x%x\n", info.name, info.type, info.flags); - return (info.type != SHT_PROGBITS && info.type != SHT_NOBITS && info.type != SHT_INIT_ARRAY && info.type != SHT_FINI_ARRAY && info.type != SHT_ARM_EXIDX); } From 954c0cf1260f014e673ac52c0618014fc03591b2 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 7 Oct 2015 20:23:36 +0800 Subject: [PATCH 100/317] [makerom/ctrtool] Made SystemMode(Ext) more meaningful. --- ctrtool/exheader.c | 39 +++++++++++++++++++++++++++++++++++++-- ctrtool/exheader.h | 17 +++++++++++++++++ makerom/exheader.c | 38 ++++++++++++++++++++++++++++++-------- makerom/exheader.h | 17 +++++++++++++++++ 4 files changed, 101 insertions(+), 10 deletions(-) diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index d35f0bb2..a7e16b16 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -583,6 +583,41 @@ const char* exheader_getvalidstring(int valid) return "(FAIL)"; } +const char* exheader_getsystemmodestring(u8 systemmode) +{ + switch (systemmode) + { + case (sysmode_64MB) : + return "64MB"; + case (sysmode_96MB) : + return "96MB"; + case (sysmode_80MB) : + return "80MB"; + case (sysmode_72MB) : + return "72MB"; + case (sysmode_32MB) : + return "32MB"; + default: + return "Unknown"; + } +} + +const char* exheader_getsystemmodeextstring(u8 systemmodeext, u8 systemmode) +{ + switch (systemmodeext) + { + case (sysmode_ext_LEGACY) : + return exheader_getsystemmodestring(systemmode); + case (sysmode_ext_124MB) : + return "124MB"; + case (sysmode_ext_178MB) : + return "178MB"; + default: + return "124MB"; + } +} + + void exheader_print(exheader_context* ctx) { u32 i; @@ -635,8 +670,8 @@ void exheader_print(exheader_context* ctx) fprintf(stdout, "Program id: %016"PRIx64" %s\n", getle64(ctx->header.arm11systemlocalcaps.programid), exheader_getvalidstring(ctx->validprogramid)); fprintf(stdout, "Core version: 0x%X\n", getle32(ctx->header.arm11systemlocalcaps.coreversion)); - fprintf(stdout, "System mode: %d %s\n", ctx->system_local_caps.old3ds_systemmode, exheader_getvalidstring(ctx->validold3dssystemmode)); - fprintf(stdout, "System mode (New3DS): %d %s\n", ctx->system_local_caps.new3ds_systemmode, exheader_getvalidstring(ctx->validnew3dssystemmode)); + fprintf(stdout, "System mode: %s %s\n", exheader_getsystemmodestring(ctx->system_local_caps.old3ds_systemmode), exheader_getvalidstring(ctx->validold3dssystemmode)); + fprintf(stdout, "System mode (New3DS): %s %s\n", exheader_getsystemmodeextstring(ctx->system_local_caps.new3ds_systemmode, ctx->system_local_caps.old3ds_systemmode), exheader_getvalidstring(ctx->validnew3dssystemmode)); fprintf(stdout, "CPU Speed (New3DS): %s %s\n", ctx->system_local_caps.new3ds_cpu_speed? "804MHz" : "268MHz", exheader_getvalidstring(ctx->validnew3dscpuspeed)); fprintf(stdout, "Enable L2 Cache: %s %s\n", ctx->system_local_caps.enable_l2_cache ? "YES" : "NO", exheader_getvalidstring(ctx->validnew3dscpuspeed)); fprintf(stdout, "Ideal processor: %d %s\n", ctx->system_local_caps.ideal_processor, exheader_getvalidstring(ctx->valididealprocessor)); diff --git a/ctrtool/exheader.h b/ctrtool/exheader.h index f0ac02c5..feffa2ce 100644 --- a/ctrtool/exheader.h +++ b/ctrtool/exheader.h @@ -6,6 +6,23 @@ #include "ctr.h" #include "settings.h" +typedef enum +{ + sysmode_64MB, + sysmode_UNK, + sysmode_96MB, + sysmode_80MB, + sysmode_72MB, + sysmode_32MB, +} exheader_systemmode; + +typedef enum +{ + sysmode_ext_LEGACY, + sysmode_ext_124MB, + sysmode_ext_178MB, +} exheader_systemmodeext; + typedef struct { u8 reserved[5]; diff --git a/makerom/exheader.c b/makerom/exheader.c index 3a9f5f63..d994c1ec 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -334,14 +334,21 @@ int SetARM11SystemLocalInfoFlags(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_ arm11->flag[0] |= cpuspeed_268MHz << 1; /* Flag[1] (SystemModeExt) */ - u8 systemModeExt = 0; if (rsf->AccessControlInfo.SystemModeExt) { - systemModeExt = strtol(rsf->AccessControlInfo.SystemModeExt, NULL, 0); - if (systemModeExt > 15) { - fprintf(stderr, "[EXHEADER ERROR] Unexpected SystemModeExt: 0x%x. Expected range: 0x0 - 0xf\n", systemModeExt); + if (strcasecmp(rsf->AccessControlInfo.SystemModeExt, "Legacy") == 0) + arm11->flag[1] = sysmode_ext_LEGACY; + else if (strcasecmp(rsf->AccessControlInfo.SystemModeExt, "124MB") == 0) + arm11->flag[1] = sysmode_ext_124MB; + else if (strcasecmp(rsf->AccessControlInfo.SystemModeExt, "178MB") == 0) + arm11->flag[1] = sysmode_ext_178MB; + + else { + fprintf(stderr, "[EXHEADER ERROR] Unexpected SystemModeExt: %s\n", rsf->AccessControlInfo.SystemModeExt); return EXHDR_BAD_RSF_OPT; } - arm11->flag[1] = systemModeExt & 0xf; + } + else { + arm11->flag[1] = sysmode_ext_LEGACY; } /* Flag[2] */ @@ -364,12 +371,27 @@ int SetARM11SystemLocalInfoFlags(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_ } } if(rsf->AccessControlInfo.SystemMode){ - systemMode = strtol(rsf->AccessControlInfo.SystemMode,NULL,0); - if(systemMode > 15){ - fprintf(stderr,"[EXHEADER ERROR] Unexpected SystemMode: 0x%x. Expected range: 0x0 - 0xf\n",systemMode); + if (strcasecmp(rsf->AccessControlInfo.SystemMode, "64MB") == 0 || strcasecmp(rsf->AccessControlInfo.SystemMode, "prod") == 0) + systemMode = sysmode_64MB; + //else if (strcasecmp(rsf->AccessControlInfo.SystemMode, "UNK") == 0 || strcasecmp(rsf->AccessControlInfo.SystemMode, "null") == 0) + // systemMode = sysmode_UNK; + else if (strcasecmp(rsf->AccessControlInfo.SystemMode, "96MB") == 0 || strcasecmp(rsf->AccessControlInfo.SystemMode, "dev1") == 0) + systemMode = sysmode_96MB; + else if (strcasecmp(rsf->AccessControlInfo.SystemMode, "80MB") == 0 || strcasecmp(rsf->AccessControlInfo.SystemMode, "dev2") == 0) + systemMode = sysmode_80MB; + else if (strcasecmp(rsf->AccessControlInfo.SystemMode, "72MB") == 0 || strcasecmp(rsf->AccessControlInfo.SystemMode, "dev3") == 0) + systemMode = sysmode_72MB; + else if (strcasecmp(rsf->AccessControlInfo.SystemMode, "32MB") == 0 || strcasecmp(rsf->AccessControlInfo.SystemMode, "dev4") == 0) + systemMode = sysmode_32MB; + + else { + fprintf(stderr, "[EXHEADER ERROR] Unexpected SystemMode: %s\n", rsf->AccessControlInfo.SystemMode); return EXHDR_BAD_RSF_OPT; } } + else { + systemMode = sysmode_64MB; + } arm11->flag[2] = (u8)(systemMode << 4 | affinityMask << 2 | idealProcessor); /* flag[3] (Thread Priority) */ diff --git a/makerom/exheader.h b/makerom/exheader.h index d9cfd616..8d91e564 100644 --- a/makerom/exheader.h +++ b/makerom/exheader.h @@ -6,6 +6,23 @@ typedef enum infoflag_SD_APPLICATION = 2, } system_info_flags; +typedef enum +{ + sysmode_64MB, // prod + sysmode_UNK, // null + sysmode_96MB, // dev1 + sysmode_80MB, // dev2 + sysmode_72MB, // dev3 + sysmode_32MB, // dev4 +} system_mode; + +typedef enum +{ + sysmode_ext_LEGACY, + sysmode_ext_124MB, // snake Prod + sysmode_ext_178MB, // snake Dev1 +} system_mode_ext; + typedef enum { memtype_APPLICATION = 1, From d5f0d2c096cf5b13e212e98150b31b5dc3065007 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 9 Oct 2015 21:35:27 +0800 Subject: [PATCH 101/317] [makerom] RSF key: "ExeFs" & "PlainRegion" (and childs), are no longer needed. --- makerom/elf.c | 203 ++++++++++++++-------------------------- makerom/elf_hdr.h | 1 + makerom/rsf_settings.c | 59 ------------ makerom/user_settings.h | 12 --- 4 files changed, 73 insertions(+), 202 deletions(-) diff --git a/makerom/elf.c b/makerom/elf.c index 17168e29..aae35399 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -5,6 +5,8 @@ #include "elf.h" #include "blz.h" +const char *SDK_PLAINREGION_SEGMENT_NAME = ".module_id"; + int ImportPlainRegionFromFile(ncch_settings *set); int ImportExeFsCodeBinaryFromFile(ncch_settings *set); @@ -14,9 +16,7 @@ u32 SizeToPage(u32 memorySize, elf_context *elf); int GetBSSFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set); int ImportPlainRegionFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set); int CreateExeFsCode(elf_context *elf, u8 *elfFile, ncch_settings *set); -int CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u8 *elfFile, char **names, u32 nameNum); -elf_segment** GetContinuousSegments(u16 *ContinuousSegmentNum, elf_context *elf, char **names, u32 nameNum); -elf_segment** GetSegments(u16 *SegmentNum, elf_context *elf, char **names, u32 nameNum); +int CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u8 *elfFile, u64 segment_flags); // ELF Functions int GetElfContext(elf_context *elf, u8 *elfFile); @@ -234,39 +234,28 @@ int GetBSSFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set) int ImportPlainRegionFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set) // Doesn't work same as N makerom { - if(!set->rsfSet->PlainRegionNum) return 0; - u16 *index = calloc(set->rsfSet->PlainRegionNum,sizeof(u16)); - - /* Getting index Values for each section */ - for(int i = 0; i < set->rsfSet->PlainRegionNum; i++){ - index[i] = GetElfSectionIndexFromName(set->rsfSet->PlainRegion[i],elf,elfFile); - } - - // Eliminating Duplicated Sections - for(int i = set->rsfSet->PlainRegionNum - 1; i >= 0; i--){ - for(int j = i-1; j >= 0; j--){ - if(index[i] == index[j]) index[i] = 0; + u64 size = 0; + u64 offset = 0; + for (u16 i = 0; i < elf->activeSegments; i++) { + if (strcmp(elf->segments[i].name, SDK_PLAINREGION_SEGMENT_NAME) == 0) { + size = elf->segments[i].header->sizeInFile; + offset = elf->segments[i].header->offsetInFile; + break; } } - - /* Calculating Total Size of Data */ - u64 totalSize = 0; - for(int i = 0; i < set->rsfSet->PlainRegionNum; i++){ - totalSize += elf->sections[index[i]].size; - } - /* Creating Output Buffer */ - set->sections.plainRegion.size = align(totalSize,set->options.blockSize); - set->sections.plainRegion.buffer = malloc(set->sections.plainRegion.size); - if(!set->sections.plainRegion.buffer) {fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR;} - memset(set->sections.plainRegion.buffer,0,set->sections.plainRegion.size); + + if (size > 0) { + /* Creating Output Buffer */ + set->sections.plainRegion.size = align(size, set->options.blockSize); + set->sections.plainRegion.buffer = malloc(set->sections.plainRegion.size); + if (!set->sections.plainRegion.buffer) { fprintf(stderr, "[ELF ERROR] Not enough memory\n"); return MEM_ERROR; } + memset(set->sections.plainRegion.buffer, 0, set->sections.plainRegion.size); - /* Storing Sections */ - u64 pos = 0; - for(int i = 0; i < set->rsfSet->PlainRegionNum; i++){ - memcpy((set->sections.plainRegion.buffer+pos),elf->sections[index[i]].ptr,elf->sections[index[i]].size); - pos += elf->sections[index[i]].size; + /* Copy Plain Region */ + memcpy(set->sections.plainRegion.buffer, elfFile+offset, size); } + return 0; } @@ -280,11 +269,11 @@ int CreateExeFsCode(elf_context *elf, u8 *elfFile, ncch_settings *set) code_segment rwdata; memset(&rwdata,0,sizeof(code_segment)); - int result = CreateCodeSegmentFromElf(&text,elf,elfFile,set->rsfSet->ExeFs.Text,set->rsfSet->ExeFs.TextNum); + int result = CreateCodeSegmentFromElf(&text,elf,elfFile,(PF_R|PF_X)); if(result) return result; - result = CreateCodeSegmentFromElf(&rodata,elf,elfFile,set->rsfSet->ExeFs.ReadOnly,set->rsfSet->ExeFs.ReadOnlyNum); + result = CreateCodeSegmentFromElf(&rodata,elf,elfFile,(PF_R)); if(result) return result; - result = CreateCodeSegmentFromElf(&rwdata,elf,elfFile,set->rsfSet->ExeFs.ReadWrite,set->rsfSet->ExeFs.ReadWriteNum); + result = CreateCodeSegmentFromElf(&rwdata,elf,elfFile,(PF_R | PF_W)); if(result) return result; /* Checking the existence of essential ELF Segments */ @@ -336,131 +325,81 @@ int CreateExeFsCode(elf_context *elf, u8 *elfFile, ncch_settings *set) return 0; } -int CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u8 *elfFile, char **names, u32 nameNum) +int CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u8 *elfFile, u64 segment_flags) { - u16 ContinuousSegmentNum = 0; - memset(out,0,sizeof(code_segment)); - elf_segment **ContinuousSegments = GetContinuousSegments(&ContinuousSegmentNum,elf,names,nameNum); - if (ContinuousSegments == NULL){ - if(!ContinuousSegmentNum){// Nothing Was Found - //printf("Nothing was found\n"); - return 0; + memset(out, 0, sizeof(code_segment)); + + u16 seg_num = 0; + elf_segment **seg = calloc(elf->activeSegments, sizeof(elf_segment*)); + + for (u16 i = 0; i < elf->activeSegments; i++) { + // Skip SDK ELF plain region + if (strcmp(elf->segments[i].name, SDK_PLAINREGION_SEGMENT_NAME) == 0) + continue; + + //printf("SegName: %s (flags: %x)\n", elf->segments[i].name, elf->segments[i].header->flags); + if ((elf->segments[i].header->flags & ~PF_CTRSDK) == segment_flags) { + if (seg_num == 0) { + seg[seg_num] = &elf->segments[i]; + seg_num++; + } + else if (elf->segments[i].vAddr == (u32)align(seg[seg_num - 1]->vAddr, seg[seg_num-1]->header->alignment)) { + seg[seg_num] = &elf->segments[i]; + seg_num++; + } } - else // Error with found segments - return ELF_SEGMENTS_NOT_CONTINUOUS; } - + + /* Return if there are no applicable segment */ + if (seg_num == 0) + return 0; + /* Getting Segment Size/Settings */ u32 vAddr = 0; u32 memorySize = 0; - for(int i = 0; i < ContinuousSegmentNum; i++){ - if (i==0){ - vAddr = ContinuousSegments[i]->vAddr; + for (u16 i = 0; i < seg_num; i++) { + if (i == 0) { + vAddr = seg[i]->vAddr; } - else{ // Add rounded size from previous segment - u32 padding = ContinuousSegments[i]->vAddr - (vAddr + memorySize); + else { // Add rounded size from previous segment + u32 padding = seg[i]->vAddr - (vAddr + memorySize); memorySize += padding; } - memorySize += ContinuousSegments[i]->header->sizeInMemory; + memorySize += seg[i]->header->sizeInMemory; - if(IsBss(&ContinuousSegments[i]->sections[ContinuousSegments[i]->sectionNum-1])) - memorySize -= ContinuousSegments[i]->sections[ContinuousSegments[i]->sectionNum-1].size; + if (IsBss(&seg[i]->sections[seg[i]->sectionNum - 1])) + memorySize -= seg[i]->sections[seg[i]->sectionNum - 1].size; } - + // For Check #ifdef DEBUG - printf("Address: 0x%x\n",vAddr); - printf("Size: 0x%x\n",memorySize); + printf("Address: 0x%x\n", vAddr); + printf("Size: 0x%x\n", memorySize); #endif out->address = vAddr; out->size = memorySize; - out->maxPageNum = SizeToPage(memorySize,elf); + out->maxPageNum = SizeToPage(memorySize, elf); out->data = malloc(memorySize); - + /* Writing Segment to Buffer */ - //vAddr = 0; - //memorySize = 0; - for(int i = 0; i < ContinuousSegmentNum; i++){ - /* - if (i==0) - vAddr = ContinuousSegments[i]->vAddr; + for (int i = 0; i < seg_num; i++) { - else{ - u32 num = ContinuousSegments[i]->vAddr - (vAddr + memorySize); - memorySize += num; - } - */ - //u32 size = 0; - for (int j = 0; j < ContinuousSegments[i]->sectionNum; j++){ - elf_section_entry *section = &ContinuousSegments[i]->sections[j]; - if (!IsBss(section)){ - u8 *pos = (out->data + (section->address - ContinuousSegments[i]->vAddr)); - memcpy(pos,section->ptr,section->size); + for (int j = 0; j < seg[i]->sectionNum; j++) { + elf_section_entry *section = &seg[i]->sections[j]; + if (!IsBss(section)) { + u8 *pos = (out->data + (section->address - seg[i]->vAddr)); + memcpy(pos, section->ptr, section->size); //size += section->size; } - - //else if (j == (ContinuousSegments[i]->sectionNum-1)) - //memorySize -= section->size; - //'else - //size += section->size; } } - free(ContinuousSegments); + free(seg); return 0; } - -elf_segment** GetContinuousSegments(u16 *ContinuousSegmentNum, elf_context *elf, char **names, u32 nameNum) -{ - u16 SegmentNum = 0; - elf_segment **Segments = GetSegments(&SegmentNum, elf, names, nameNum); - if (Segments == NULL || SegmentNum == 0){ // No Segments for the names were found - //printf("Not Found Segment\n"); - return NULL; - } - - if (SegmentNum == 1){ //Return as there is no need to check - *ContinuousSegmentNum = SegmentNum; - return Segments; - } - - u32 vAddr = Segments[0]->vAddr + Segments[0]->header->sizeInMemory; - for (int i = 1; i < SegmentNum; i++){ - if (Segments[i]->vAddr != (u32)align(vAddr,Segments[i]->header->alignment)){ //Each Segment must start after each other - fprintf(stderr,"[ELF ERROR] %s segment and %s segment are not continuous\n", Segments[i]->name, Segments[i - 1]->name); - free(Segments); - *ContinuousSegmentNum = 0xffff; // Signify to function that an error occured - return NULL; - } - } - *ContinuousSegmentNum = SegmentNum; - return Segments; -} - - -elf_segment** GetSegments(u16 *SegmentNum, elf_context *elf, char **names, u32 nameNum) -{ - if (names == NULL) - { - return NULL; - } - - elf_segment **Segments = calloc(nameNum,sizeof(elf_segment*)); - *SegmentNum = 0; // There can be a max of nameNum Segments, however, they might not all exist - for (int i = 0; i < nameNum; i++){ - for(int j = 0; j < elf->activeSegments; j++){ - if(strcmp(names[i],elf->segments[j].name) == 0){ // If there is a match, store Segment data pointer & increment index - Segments[*SegmentNum] = &elf->segments[j]; - *SegmentNum = *SegmentNum + 1; - } - } - } - return Segments; -} - // ELF Functions int GetElfContext(elf_context *elf, u8 *elfFile) @@ -547,9 +486,11 @@ void PrintElfContext(elf_context *elf, u8 *elfFile) printf(" Segment [%d][%s]\n",i,elf->segments[i].name); printf(" > Size : 0x%"PRIx64"\n",elf->segments[i].header->sizeInFile); printf(" > Address : 0x%"PRIx64"\n",elf->segments[i].vAddr); + printf(" > Flags: 0x%"PRIx64"\n", elf->segments[i].header->flags); + printf(" > Type: 0x%"PRIx64"\n", elf->segments[i].header->type); printf(" > Sections : %d\n",elf->segments[i].sectionNum); for(int j = 0; j < elf->segments[i].sectionNum; j++) - printf(" > Section [%d][%s]\n",j,elf->segments[i].sections[j].name); + printf(" > Section [%d][%s][0x%"PRIx64"][0x%"PRIx64"]\n",j,elf->segments[i].sections[j].name, elf->segments[i].sections[j].flags, elf->segments[i].sections[j].type); /* char outpath[100]; diff --git a/makerom/elf_hdr.h b/makerom/elf_hdr.h index eac9d64e..4fb772d7 100644 --- a/makerom/elf_hdr.h +++ b/makerom/elf_hdr.h @@ -161,6 +161,7 @@ typedef struct #define PT_LOPROC 0x70000000 /* Start of processor-specific */ #define PT_HIPROC 0x7fffffff /* End of processor-specific */ +#define PF_CTRSDK 0x80000000 #define PF_R 0x4 /* p_flags */ #define PF_W 0x2 #define PF_X 0x1 diff --git a/makerom/rsf_settings.c b/makerom/rsf_settings.c index ede105d5..66fcc0f2 100644 --- a/makerom/rsf_settings.c +++ b/makerom/rsf_settings.c @@ -5,8 +5,6 @@ void GET_AccessControlInfo(ctr_yaml_context *ctx, rsf_settings *rsf); void GET_SystemControlInfo(ctr_yaml_context *ctx, rsf_settings *rsf); void GET_BasicInfo(ctr_yaml_context *ctx, rsf_settings *rsf); void GET_RomFs(ctr_yaml_context *ctx, rsf_settings *rsf); -void GET_ExeFs(ctr_yaml_context *ctx, rsf_settings *rsf); -void GET_PlainRegion(ctr_yaml_context *ctx, rsf_settings *rsf); void GET_TitleInfo(ctr_yaml_context *ctx, rsf_settings *rsf); void GET_CardInfo(ctr_yaml_context *ctx, rsf_settings *rsf); void GET_CommonHeaderKey(ctr_yaml_context *ctx, rsf_settings *rsf); @@ -23,8 +21,6 @@ void EvaluateRSF(rsf_settings *rsf, ctr_yaml_context *ctx) else if(cmpYamlValue("SystemControlInfo",ctx)) {FinishEvent(ctx); GET_SystemControlInfo(ctx,rsf); goto GET_NextGroup;} else if(cmpYamlValue("BasicInfo",ctx)) {FinishEvent(ctx); GET_BasicInfo(ctx,rsf); goto GET_NextGroup;} else if(cmpYamlValue("RomFs",ctx)) {FinishEvent(ctx); GET_RomFs(ctx,rsf); goto GET_NextGroup;} - else if(cmpYamlValue("ExeFs",ctx)) {FinishEvent(ctx); GET_ExeFs(ctx,rsf); goto GET_NextGroup;} - else if(cmpYamlValue("PlainRegion",ctx)) {FinishEvent(ctx); GET_PlainRegion(ctx,rsf); goto GET_NextGroup;} else if(cmpYamlValue("TitleInfo",ctx)) {FinishEvent(ctx); GET_TitleInfo(ctx,rsf); goto GET_NextGroup;} else if(cmpYamlValue("CardInfo",ctx)) {FinishEvent(ctx); GET_CardInfo(ctx,rsf); goto GET_NextGroup;} else if(cmpYamlValue("CommonHeaderKey",ctx)) {FinishEvent(ctx); GET_CommonHeaderKey(ctx,rsf); goto GET_NextGroup;} @@ -240,39 +236,6 @@ void GET_RomFs(ctr_yaml_context *ctx, rsf_settings *rsf) FinishEvent(ctx); } -void GET_ExeFs(ctr_yaml_context *ctx, rsf_settings *rsf) -{ - /* Checking That Group is in a map */ - if(!CheckMappingEvent(ctx)) return; - u32 InitLevel = ctx->Level; - /* Checking each child */ - GetEvent(ctx); - while(ctx->Level == InitLevel){ - if(ctx->error || ctx->done) return; - // Handle childs - - if(cmpYamlValue("Text",ctx)) rsf->ExeFs.TextNum = SetYAMLSequence(&rsf->ExeFs.Text,"Text",ctx); - else if(cmpYamlValue("ReadOnly",ctx)) rsf->ExeFs.ReadOnlyNum = SetYAMLSequence(&rsf->ExeFs.ReadOnly,"ReadOnly",ctx); - else if(cmpYamlValue("ReadWrite",ctx)) rsf->ExeFs.ReadWriteNum = SetYAMLSequence(&rsf->ExeFs.ReadWrite,"ReadWrite",ctx); - - else{ - fprintf(stderr,"[RSF ERROR] Unrecognised key '%s'\n",GetYamlString(ctx)); - ctx->error = YAML_UNKNOWN_KEY; - FinishEvent(ctx); - return; - } - // Finish event start next - FinishEvent(ctx); - GetEvent(ctx); - } - FinishEvent(ctx); -} - -void GET_PlainRegion(ctr_yaml_context *ctx, rsf_settings *rsf) -{ - rsf->PlainRegionNum = SetYAMLSequence(&rsf->PlainRegion,"PlainRegion",ctx); -} - void GET_TitleInfo(ctr_yaml_context *ctx, rsf_settings *rsf) { /* Checking That Group is in a map */ @@ -487,28 +450,6 @@ void free_RsfSettings(rsf_settings *set) } free(set->RomFs.File); - //ExeFs - for(u32 i = 0; i < set->ExeFs.TextNum; i++){ - free(set->ExeFs.Text[i]); - } - free(set->ExeFs.Text); - - for(u32 i = 0; i < set->ExeFs.ReadOnlyNum; i++){ - free(set->ExeFs.ReadOnly[i]); - } - free(set->ExeFs.ReadOnly); - - for(u32 i = 0; i < set->ExeFs.ReadWriteNum; i++){ - free(set->ExeFs.ReadWrite[i]); - } - free(set->ExeFs.ReadWrite); - - //PlainRegion - for(u32 i = 0; i < set->PlainRegionNum; i++){ - free(set->PlainRegion[i]); - } - free(set->PlainRegion); - //TitleInfo free(set->TitleInfo.Platform); free(set->TitleInfo.Category); diff --git a/makerom/user_settings.h b/makerom/user_settings.h index a2cacb81..6e18c986 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -169,18 +169,6 @@ typedef struct char **File; } RomFs; - struct{ - u32 TextNum; - char **Text; - u32 ReadOnlyNum; - char **ReadOnly; - u32 ReadWriteNum; - char **ReadWrite; - } ExeFs; - - u32 PlainRegionNum; - char **PlainRegion; - struct{ // Strings char *Platform; From aa694f20f9ef3d940b581d6a6619127b7d9b77ff Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 9 Oct 2015 21:35:54 +0800 Subject: [PATCH 102/317] [makerom] Relaxed RSF requirements. --- makerom/exheader.c | 16 +++++----------- makerom/ncch.c | 12 ++++++++---- makerom/titleid.c | 8 ++++---- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/makerom/exheader.c b/makerom/exheader.c index d994c1ec..a52fa42a 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -4,6 +4,7 @@ #include "accessdesc.h" #include "titleid.h" +const char *DEFAULT_EXHEADER_NAME = "CtrApp"; /* Prototypes */ void free_ExHeaderSettings(exheader_settings *exhdrset); @@ -189,17 +190,10 @@ int get_ExHeaderSettingsFromRsf(exheader_settings *exhdrset) int get_ExHeaderCodeSetInfo(exhdr_CodeSetInfo *CodeSetInfo, rsf_settings *rsf) { /* Name */ - if(rsf->BasicInfo.Title){ - //if(strlen(rsf->BasicInfo.Title) > 8){ - // fprintf(stderr,"[EXHEADER ERROR] Parameter Too Long \"BasicInfo/Title\"\n"); - // return EXHDR_BAD_RSF_OPT; - //} - strncpy((char*)CodeSetInfo->name,rsf->BasicInfo.Title,8); - } - else{ - ErrorParamNotFound("BasicInfo/Title"); - return EXHDR_BAD_RSF_OPT; - } + if (rsf->BasicInfo.Title) + strncpy((char*)CodeSetInfo->name, rsf->BasicInfo.Title, 8); + else + strncpy((char*)CodeSetInfo->name, DEFAULT_EXHEADER_NAME, 8); /* Stack Size */ if(rsf->SystemControlInfo.StackSize) diff --git a/makerom/ncch.c b/makerom/ncch.c index 0cc87ecf..4a8d00e3 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -12,6 +12,8 @@ #include "ncch_logo.h" // Contains Logos const u32 NCCH_BLOCK_SIZE = 0x200; +const char *DEFAULT_PRODUCT_CODE = "CTR-P-CTAP"; +const char *DEFAULT_MAKER_CODE = "00"; // Private Prototypes int SignCFA(ncch_hdr *hdr, keys_struct *keys); @@ -591,18 +593,20 @@ int SetCommonHeaderBasicData(ncch_settings *set, ncch_hdr *hdr) fprintf(stderr,"[NCCH ERROR] Invalid Product Code\n"); return NCCH_BAD_RSF_SET; } - memcpy(hdr->productCode,set->rsfSet->BasicInfo.ProductCode,strlen((char*)set->rsfSet->BasicInfo.ProductCode)); + strncpy((char*)hdr->productCode,set->rsfSet->BasicInfo.ProductCode, 16); } - else memcpy(hdr->productCode,"CTR-P-CTAP",10); + else + strncpy((char*)hdr->productCode, DEFAULT_PRODUCT_CODE, 16); if(set->rsfSet->BasicInfo.CompanyCode){ if(strlen((char*)set->rsfSet->BasicInfo.CompanyCode) != 2){ fprintf(stderr,"[NCCH ERROR] CompanyCode length must be 2\n"); return NCCH_BAD_RSF_SET; } - memcpy(hdr->makerCode,set->rsfSet->BasicInfo.CompanyCode,2); + strncpy((char*)hdr->makerCode, set->rsfSet->BasicInfo.CompanyCode, 2); } - else memcpy(hdr->makerCode,"00",2); + else + strncpy((char*)hdr->makerCode, DEFAULT_MAKER_CODE, 2); // Setting Encryption Settings if(!set->options.Encrypt) diff --git a/makerom/titleid.c b/makerom/titleid.c index b9045341..9bf82174 100644 --- a/makerom/titleid.c +++ b/makerom/titleid.c @@ -2,6 +2,8 @@ #include "ncch_read.h" #include "titleid.h" +const u32 DEFAULT_UNIQUE_ID = 0xff3ff; + void SetPIDType(u16 *type); int SetPIDCategoryFromName(u16 *cat, char *CategoryStr); int SetPIDCategoryFromFlags(u16 *cat, char **CategoryFlags, u32 FlagNum); @@ -52,10 +54,8 @@ int GetProgramID(u64 *dest, rsf_settings *rsf, bool IsForExheader) // Getting UniqueId if(rsf->TitleInfo.UniqueId) GetUniqueID(&uniqueId,rsf); - else{ - fprintf(stderr,"[ID ERROR] ParameterNotFound: \"TitleInfo/UniqueId\"\n"); - return PID_BAD_RSF_SET; - } + else + uniqueId = DEFAULT_UNIQUE_ID; // Getting Variation if(SetTitleVariation(&variation,category,rsf) == PID_INVALID_VARIATION) From 0e6a981b72de6baf036355f7c40a244fbaed9d3e Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 9 Oct 2015 21:40:35 +0800 Subject: [PATCH 103/317] [ctrtool] NCCH MakerCode is now displayed as a string. --- ctrtool/ncch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 07f8755d..5de61cef 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -575,7 +575,7 @@ void ncch_print(ncch_context* ctx) memdump(stdout, "Signature (FAIL): ", header->signature, 0x100); fprintf(stdout, "Content size: 0x%08x\n", getle32(header->contentsize)*mediaunitsize); fprintf(stdout, "Partition id: %016"PRIx64"\n", getle64(header->partitionid)); - fprintf(stdout, "Maker code: %04x\n", getle16(header->makercode)); + fprintf(stdout, "Maker code: %.2s\n", header->makercode); fprintf(stdout, "Version: %04x\n", getle16(header->version)); fprintf(stdout, "Title seed check: %08x\n", getle32(header->seedcheck)); fprintf(stdout, "Program id: %016"PRIx64"\n", getle64(header->programid)); From 2e2f1b09f7c13957a155e94016bc0dbbd328bce4 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 12 Oct 2015 09:28:41 +0800 Subject: [PATCH 104/317] [makerom] Fixed CpuSpeed typo. --- makerom/exheader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/exheader.c b/makerom/exheader.c index a52fa42a..0d4b0bf0 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -315,7 +315,7 @@ int SetARM11SystemLocalInfoFlags(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_ arm11->flag[0] |= rsf->AccessControlInfo.EnableL2Cache; if (rsf->AccessControlInfo.CpuSpeed) { - if(strcasecmp(rsf->AccessControlInfo.CpuSpeed, "256mhz") == 0) + if(strcasecmp(rsf->AccessControlInfo.CpuSpeed, "268mhz") == 0) arm11->flag[0] |= cpuspeed_268MHz << 1; else if(strcasecmp(rsf->AccessControlInfo.CpuSpeed, "804mhz") == 0) arm11->flag[0] |= cpuspeed_804MHz << 1; From 2444715bc2c60f02ffda79c355bca94bec6c706e Mon Sep 17 00:00:00 2001 From: profi200 Date: Tue, 20 Oct 2015 16:26:57 +0200 Subject: [PATCH 105/317] Fixed static linking of ctrtool & added a few more gcc flags. --- ctrtool/Makefile | 4 ++-- makerom/Makefile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index f1a4e603..599ec8b8 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -1,9 +1,9 @@ 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 = -static-libstdc++ +LIBS = -static-libgcc -static-libstdc++ CXXFLAGS = -I. -CFLAGS = -Wall -Wno-unused-variable -Wno-unused-but-set-variable -I. +CFLAGS = -Wall -O2 -flto -static -Wno-unused-variable -Wno-unused-but-set-variable -I. OUTPUT = ctrtool CC = gcc diff --git a/makerom/Makefile b/makerom/Makefile index ef47794a..741a3f11 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -5,7 +5,7 @@ OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) # Compiler Settings LIBS = -static-libgcc CXXFLAGS = -I. -CFLAGS = --std=c99 -Wall -Wno-unused-but-set-variable -Wno-unused-value -I. -DMAKEROM_VER_MAJOR=$(VER_MAJOR) -DMAKEROM_VER_MINOR=$(VER_MINOR) $(MAKEROM_BUILD_FLAGS) -m64 +CFLAGS = --std=c99 -Wall -O2 -flto -static -Wno-unused-but-set-variable -Wno-unused-value -I. -DMAKEROM_VER_MAJOR=$(VER_MAJOR) -DMAKEROM_VER_MINOR=$(VER_MINOR) $(MAKEROM_BUILD_FLAGS) -m64 CC = gcc # MAKEROM Build Settings @@ -22,4 +22,4 @@ build: $(OBJS) g++ -o $(OUTPUT) $(LIBS) $(OBJS) -m64 clean: - rm -rf $(OUTPUT) $(OBJS) *.cci *.cia *.cxi *.cfa \ No newline at end of file + rm -rf $(OUTPUT) $(OBJS) *.cci *.cia *.cxi *.cfa From 1e8a0a23919824615097ab1c1baaf126c799c436 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 22 Oct 2015 01:08:13 +0800 Subject: [PATCH 106/317] [ctrtool] Corrected array size. --- ctrtool/exheader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/exheader.h b/ctrtool/exheader.h index feffa2ce..63bb1690 100644 --- a/ctrtool/exheader.h +++ b/ctrtool/exheader.h @@ -100,7 +100,7 @@ typedef struct u64 extdata_id; u32 other_user_saveid[3]; u8 use_other_variation_savedata; - u32 accessible_saveid[3]; + u32 accessible_saveid[6]; u32 system_saveid[2]; u64 accessinfo; From 4dba06b3be508fe5518939af5da165fedb69e2e3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 22 Oct 2015 01:09:00 +0800 Subject: [PATCH 107/317] [ctrtool] Made makefile consistent with makerom. --- ctrtool/Makefile | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 599ec8b8..165afc5e 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -1,15 +1,17 @@ -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 +# Sources +SRC_DIR = . polarssl tinyxml +OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) + +# Compiler Settings LIBS = -static-libgcc -static-libstdc++ CXXFLAGS = -I. -CFLAGS = -Wall -O2 -flto -static -Wno-unused-variable -Wno-unused-but-set-variable -I. +CFLAGS = -O2 -flto -Wall -Wno-unused-variable -Wno-unused-but-set-variable -I. OUTPUT = ctrtool CC = gcc +CXX = g++ -main: $(OBJS) $(POLAR_OBJS) $(TINYXML_OBJS) - g++ -o $(OUTPUT) $(LIBS) $(OBJS) $(POLAR_OBJS) $(TINYXML_OBJS) - +main: $(OBJS) + $(CXX) -o $(OUTPUT) $(LIBS) $(OBJS) clean: - rm -rf $(OUTPUT) $(OBJS) $(POLAR_OBJS) $(TINYXML_OBJS) + rm -rf $(OUTPUT) $(OBJS) From dd9538b0969cdae99a6290ce4a9181349ccaad76 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 22 Oct 2015 01:30:33 +0800 Subject: [PATCH 108/317] [makerom] Fixed "self referencing problems" work around. --- makerom/dir.c | 22 +++++++++------------- makerom/dir.h | 24 ++++++++++++------------ makerom/romfs_gen.c | 4 ++-- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/makerom/dir.c b/makerom/dir.c index f44a397d..46580a79 100644 --- a/makerom/dir.c +++ b/makerom/dir.c @@ -2,7 +2,8 @@ #include "dir.h" #include "utf.h" -/* This is mainly a FS interface for ROMFS generation */ +/* This is the FS interface for ROMFS generation */ +/* Tested working on Windows/Linux/OSX */ int fs_InitDir(u16 *path, u32 pathlen, fs_dir *dir); int fs_ManageDirSlot(fs_dir *dir); int fs_ManageFileSlot(fs_dir *dir); @@ -13,7 +14,7 @@ bool fs_EntryIsDirNav(fs_entry *entry); int fs_AddDir(fs_entry *entry, fs_dir *dir); int fs_AddFile(fs_entry *entry, fs_dir *dir); -int fs_u16StrLen(fs_romfs_char *str) +int fs_RomFsStrLen(fs_romfs_char *str) { int i; for( i = 0; str[i] != 0x0; i++ ); @@ -159,8 +160,7 @@ int fs_AddDir(fs_entry *entry, fs_dir *dir) fs_ManageDirSlot(dir); u32 current_slot = dir->u_dir; dir->u_dir++; - fs_dir *tmp = (fs_dir*)dir->dir; - return fs_OpenDir(entry->fs_name,entry->name,entry->name_len,&tmp[current_slot]); + return fs_OpenDir(entry->fs_name,entry->name,entry->name_len,&dir->dir[current_slot]); } int fs_AddFile(fs_entry *entry, fs_dir *dir) @@ -288,9 +288,8 @@ void fs_PrintDir(fs_dir *dir, u32 depth) // This is just for simple debugging, p } if(dir->u_dir) { - fs_dir *tmp = (fs_dir*)dir->dir; for(u32 i = 0; i < dir->u_dir; i++) - fs_PrintDir(&tmp[i],depth+1); + fs_PrintDir(&dir->dir[i],depth+1); } } @@ -305,13 +304,11 @@ void fs_FreeDir(fs_dir *dir) free(dir->file); - fs_dir *tmp = (fs_dir*)dir->dir; - //printf("free dir names\n"); + //printf("free dir names and\n"); for(u32 i = 0; i < dir->u_dir; i++) { - //wprintf(L"freeing: %s\n",tmp[i].name); - free(tmp[i].name); - fs_FreeDir(&tmp[i]); + free(dir->dir[i].name); + fs_FreeDir(&dir->dir[i]); } //printf("free dir struct\n"); free(dir->dir); @@ -326,7 +323,6 @@ void fs_FreeFiles(fs_dir *dir) fclose(dir->file[i].fp); } - fs_dir *tmp = (fs_dir*)dir->dir; for(u32 i = 0; i < dir->u_dir; i++) - fs_FreeFiles(&tmp[i]); + fs_FreeFiles(&dir->dir[i]); } \ No newline at end of file diff --git a/makerom/dir.h b/makerom/dir.h index c6e16f6b..1279b362 100644 --- a/makerom/dir.h +++ b/makerom/dir.h @@ -21,7 +21,7 @@ #endif -typedef struct +struct fs_entry { bool IsDir; fs_char *fs_name; @@ -29,35 +29,35 @@ typedef struct u32 name_len; u64 size; FILE *fp; -} fs_entry; +}; -typedef struct +struct fs_file { fs_romfs_char *name; u32 name_len; u64 size; FILE *fp; -} fs_file; +}; -typedef struct +struct fs_dir { fs_romfs_char *name; u32 name_len; - void *dir; // treated as type 'fs_dir'. This officially type 'void' to prevent self referencing problems + struct fs_dir *dir; u32 m_dir; u32 u_dir; - fs_file *file; + struct fs_file *file; u32 m_file; u32 u_file; -} fs_dir; +}; +typedef struct fs_entry fs_entry; +typedef struct fs_file fs_file; +typedef struct fs_dir fs_dir; -int fs_u8String_to_u16String(u16 **dst, u32 *dst_len, u8 *src, u32 src_len); -int fs_u16String_to_u16String(u16 **dst, u32 *dst_len, u16 *src, u32 src_len); -int fs_u32String_to_u16String(u16 **dst, u32 *dst_len, u32 *src, u32 src_len); -int fs_u16StrLen(fs_romfs_char *str); +int fs_RomFsStrLen(fs_romfs_char *str); int fs_OpenDir(fs_char *fs_path, fs_romfs_char *path, u32 pathlen, fs_dir *dir); void fs_PrintDir(fs_dir *dir, u32 depth); diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 552a932f..dc86a716 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -308,13 +308,13 @@ void BuildRomfsHeader(romfs_buildctx *ctx) u32 GetFileHashTableIndex(romfs_buildctx *ctx, u32 parent, fs_romfs_char *path) { - u32 hash = CalcPathHash(parent, path, 0, fs_u16StrLen(path)); + u32 hash = CalcPathHash(parent, path, 0, fs_RomFsStrLen(path)); return hash % ctx->m_fileHashTable; } u32 GetDirHashTableIndex(romfs_buildctx *ctx, u32 parent, fs_romfs_char* path) { - u32 hash = CalcPathHash(parent, path, 0, fs_u16StrLen(path)); + u32 hash = CalcPathHash(parent, path, 0, fs_RomFsStrLen(path)); return hash % ctx->m_dirHashTable; } From d7d45411babed772b1a78f5085d47f7999a870bb Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 22 Oct 2015 01:35:53 +0800 Subject: [PATCH 109/317] [makerom/ctrtool] Updated usage text. (now with build datestamp) --- ctrtool/main.c | 5 +- makerom/Makefile | 9 +- makerom/user_settings.c | 727 +++++++++++++++++++++------------------- 3 files changed, 383 insertions(+), 358 deletions(-) diff --git a/ctrtool/main.c b/ctrtool/main.c index 98671ab4..dabd615e 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -38,9 +38,10 @@ typedef struct static void usage(const char *argv0) { fprintf(stderr, - "Usage: %s [options...] \n" "CTRTOOL (c) neimod, 3DSGuy.\n" + "Built: %s %s\n" "\n" + "Usage: %s [options...] \n" "Options:\n" " -i, --info Show file info.\n" " This is the default action.\n" @@ -86,7 +87,7 @@ static void usage(const char *argv0) " --romfsdir=dir Specify RomFS directory path.\n" " --listromfs List files in RomFS.\n" "\n", - argv0); + __TIME__, __DATE__, argv0); exit(1); } diff --git a/makerom/Makefile b/makerom/Makefile index 741a3f11..e68a81c4 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -5,13 +5,12 @@ OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) # Compiler Settings LIBS = -static-libgcc CXXFLAGS = -I. -CFLAGS = --std=c99 -Wall -O2 -flto -static -Wno-unused-but-set-variable -Wno-unused-value -I. -DMAKEROM_VER_MAJOR=$(VER_MAJOR) -DMAKEROM_VER_MINOR=$(VER_MINOR) $(MAKEROM_BUILD_FLAGS) -m64 +CFLAGS = --std=c99 -O2 -flto -Wall -Wno-unused-but-set-variable -Wno-unused-value -I. $(MAKEROM_BUILD_FLAGS) CC = gcc - +CXX = g++ + # MAKEROM Build Settings MAKEROM_BUILD_FLAGS = #-DDEBUG -VER_MAJOR = 0 -VER_MINOR = 14 OUTPUT = makerom main: build @@ -19,7 +18,7 @@ main: build rebuild: clean build build: $(OBJS) - g++ -o $(OUTPUT) $(LIBS) $(OBJS) -m64 + $(CXX) -o $(OUTPUT) $(LIBS) $(OBJS) clean: rm -rf $(OUTPUT) $(OBJS) *.cci *.cia *.cxi *.cfa diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 792611a3..7477b821 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -2,6 +2,7 @@ // Private Prototypes void DisplayHelp(char *app_name); +void DisplayExtendedHelp(char *app_name); void SetDefaults(user_settings *set); int SetArgument(int argc, int i, char *argv[], user_settings *set); int CheckArgumentCombination(user_settings *set); @@ -12,72 +13,76 @@ void PrintNoNeedParam(char *arg); int ParseArgs(int argc, char *argv[], user_settings *set) { - if(argv == NULL || set == NULL) + if (argv == NULL || set == NULL) return USR_PTR_PASS_FAIL; - - if(argc < 2){ + + if (argc < 2) { DisplayHelp(argv[0]); return USR_HELP; } - + // Detecting Help Requried - for(int i = 1; i < argc; i++){ - if(strcmp(argv[i],"-help") == 0){ + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-help") == 0) { DisplayHelp(argv[0]); return USR_HELP; } + else if (strcmp(argv[i], "-exthelp") == 0) { + DisplayExtendedHelp(argv[0]); + return USR_HELP; + } } - + // Allocating Memory for Content Path Ptrs - set->common.contentPath = calloc(CIA_MAX_CONTENT,sizeof(char*)); - if(set->common.contentPath == NULL){ - fprintf(stderr,"[SETTING ERROR] Not Enough Memory\n"); + set->common.contentPath = calloc(CIA_MAX_CONTENT, sizeof(char*)); + if (set->common.contentPath == NULL) { + fprintf(stderr, "[SETTING ERROR] Not Enough Memory\n"); return USR_MEM_ERROR; } - + // Initialise Keys InitKeys(&set->common.keys); // Setting Defaults SetDefaults(set); - + // Parsing Arguments int set_result; int i = 1; - while(i < argc){ - set_result = SetArgument(argc,i,argv,set); - if(set_result < 1){ - fprintf(stderr,"[RESULT] Invalid arguments, see '%s -help'\n",argv[0]); + while (i < argc) { + set_result = SetArgument(argc, i, argv, set); + if (set_result < 1) { + fprintf(stderr, "[RESULT] Invalid arguments, see '%s -help'\n", argv[0]); return set_result; } i += set_result; } - + // Checking arguments - if((set_result = CheckArgumentCombination(set)) != 0) + if ((set_result = CheckArgumentCombination(set)) != 0) return set_result; - + // Setting Keys - if((set_result = SetKeys(&set->common.keys)) != 0) + if ((set_result = SetKeys(&set->common.keys)) != 0) return set_result; // Generating outpath if required - if(!set->common.outFileName){ + if (!set->common.outFileName) { char *source_path = NULL; - if(set->ncch.buildNcch0) + if (set->ncch.buildNcch0) source_path = set->common.rsfPath; - else if(set->common.workingFileType == infile_ncsd || set->common.workingFileType == infile_cia || set->common.workingFileType == infile_srl) + else if (set->common.workingFileType == infile_ncsd || set->common.workingFileType == infile_cia || set->common.workingFileType == infile_srl) source_path = set->common.workingFilePath; - else + else source_path = set->common.contentPath[0]; u16 outfile_len = strlen(source_path) + 0x10; - set->common.outFileName = calloc(outfile_len,sizeof(char)); - if(!set->common.outFileName){ - fprintf(stderr,"[SETTING ERROR] Not Enough Memory\n"); + set->common.outFileName = calloc(outfile_len, sizeof(char)); + if (!set->common.outFileName) { + fprintf(stderr, "[SETTING ERROR] Not Enough Memory\n"); return USR_MEM_ERROR; } set->common.outFileName_mallocd = true; - append_filextention(set->common.outFileName,outfile_len,source_path,(char*)&output_extention[set->common.outFormat-1]); + append_filextention(set->common.outFileName, outfile_len, source_path, (char*)&output_extention[set->common.outFormat - 1]); } return 0; } @@ -96,7 +101,7 @@ void SetDefaults(user_settings *set) set->ncch.ncchType = format_not_set; // RSF Settings - clrmem(&set->common.rsfSet,sizeof(rsf_settings)); + clrmem(&set->common.rsfSet, sizeof(rsf_settings)); set->common.rsfSet.Option.EnableCompress = true; set->common.rsfSet.Option.EnableCrypt = true; set->common.rsfSet.Option.UseOnSD = false; @@ -116,53 +121,53 @@ void SetDefaults(user_settings *set) set->cia.titleVersion[VER_MAJOR] = MAX_U16; // invalid so changes can be detected set->cia.randomTitleKey = false; set->common.keys.aes.currentCommonKey = MAX_U8 + 1; // invalid so changes can be detected - for(int i = 0; i < CIA_MAX_CONTENT; i++) + for (int i = 0; i < CIA_MAX_CONTENT; i++) set->cia.contentId[i] = MAX_U32 + 1; // invalid so changes can be detected } int SetArgument(int argc, int i, char *argv[], user_settings *set) { u16 ParamNum = 0; - for(int j = i+1; j < argc && argv[j][0] != '-'; j++) + for (int j = i + 1; j < argc && argv[j][0] != '-'; j++) ParamNum++; // Global Settings - if(strcmp(argv[i],"-rsf") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + if (strcmp(argv[i], "-rsf") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->common.rsfPath = argv[i+1]; + set->common.rsfPath = argv[i + 1]; return 2; } - else if(strcmp(argv[i],"-f") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-f") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - if(strcasecmp(argv[i+1],"ncch") == 0 || strcasecmp(argv[i+1],"cxi") == 0 || strcasecmp(argv[i+1],"cfa") == 0) + if (strcasecmp(argv[i + 1], "ncch") == 0 || strcasecmp(argv[i + 1], "cxi") == 0 || strcasecmp(argv[i + 1], "cfa") == 0) set->common.outFormat = NCCH; - else if(strcasecmp(argv[i+1],"cci") == 0) + else if (strcasecmp(argv[i + 1], "cci") == 0) set->common.outFormat = CCI; - else if(strcasecmp(argv[i+1],"cia") == 0) + else if (strcasecmp(argv[i + 1], "cia") == 0) set->common.outFormat = CIA; else { - fprintf(stderr,"[SETTING ERROR] Invalid output format '%s'\n",argv[i+1]); + fprintf(stderr, "[SETTING ERROR] Invalid output format '%s'\n", argv[i + 1]); return USR_BAD_ARG; - } + } return 2; } - else if(strcmp(argv[i],"-o") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-o") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->common.outFileName = argv[i+1]; + set->common.outFileName = argv[i + 1]; set->common.outFileName_mallocd = false; return 2; } - else if(strcmp(argv[i],"-v") == 0){ - if(ParamNum){ + else if (strcmp(argv[i], "-v") == 0) { + if (ParamNum) { PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } @@ -170,64 +175,64 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return 1; } // Key Options - else if(strcmp(argv[i],"-target") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-target") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - if(strcasecmp(argv[i+1],"test") == 0 || strcasecmp(argv[i+1],"t") == 0) + if (strcasecmp(argv[i + 1], "test") == 0 || strcasecmp(argv[i + 1], "t") == 0) set->common.keys.keyset = pki_TEST; //else if(strcasecmp(argv[i+1],"beta") == 0 || strcasecmp(argv[i+1],"b") == 0) // set->common.keys.keyset = pki_BETA; - else if(strcasecmp(argv[i+1],"debug") == 0 || strcasecmp(argv[i+1],"development") == 0 || strcasecmp(argv[i+1],"d") == 0) + else if (strcasecmp(argv[i + 1], "debug") == 0 || strcasecmp(argv[i + 1], "development") == 0 || strcasecmp(argv[i + 1], "d") == 0) set->common.keys.keyset = pki_DEVELOPMENT; - else if(strcasecmp(argv[i+1],"retail") == 0 || strcasecmp(argv[i+1],"production") == 0 || strcasecmp(argv[i+1],"p") == 0) + else if (strcasecmp(argv[i + 1], "retail") == 0 || strcasecmp(argv[i + 1], "production") == 0 || strcasecmp(argv[i + 1], "p") == 0) set->common.keys.keyset = pki_PRODUCTION; //else if(strcasecmp(argv[i+1],"custom") == 0 || strcasecmp(argv[i+1],"c") == 0) // set->common.keys.keyset = pki_CUSTOM; - else{ - fprintf(stderr,"[SETTING ERROR] Unrecognised target '%s'\n",argv[i+1]); + else { + fprintf(stderr, "[SETTING ERROR] Unrecognised target '%s'\n", argv[i + 1]); return USR_BAD_ARG; } return 2; } - else if(strcmp(argv[i],"-ckeyid") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-ckeyid") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->common.keys.aes.currentCommonKey = strtol(argv[i+1],NULL,0); - if(set->common.keys.aes.currentCommonKey > MAX_CMN_KEY) + set->common.keys.aes.currentCommonKey = strtol(argv[i + 1], NULL, 0); + if (set->common.keys.aes.currentCommonKey > MAX_CMN_KEY) { - fprintf(stderr,"[SETTING ERROR] Invalid Common Key Index: 0x%x\n",set->common.keys.aes.currentCommonKey); + fprintf(stderr, "[SETTING ERROR] Invalid Common Key Index: 0x%x\n", set->common.keys.aes.currentCommonKey); return USR_BAD_ARG; } return 2; } - else if(strcmp(argv[i],"-ncchseckey") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-ncchseckey") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } set->ncch.useSecCrypto = true; - set->ncch.keyXID = strtol(argv[i+1],NULL,0); - if(set->ncch.keyXID > MAX_NCCH_KEYX) + set->ncch.keyXID = strtol(argv[i + 1], NULL, 0); + if (set->ncch.keyXID > MAX_NCCH_KEYX) { - fprintf(stderr,"[SETTING ERROR] Invalid NCCH KeyX Index: 0x%x\n",set->ncch.keyXID); + fprintf(stderr, "[SETTING ERROR] Invalid NCCH KeyX Index: 0x%x\n", set->ncch.keyXID); return USR_BAD_ARG; } return 2; } - else if(strcmp(argv[i],"-showkeys") == 0){ - if(ParamNum){ + else if (strcmp(argv[i], "-showkeys") == 0) { + if (ParamNum) { PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->common.keys.dumpkeys = true; return 1; } - else if(strcmp(argv[i],"-fsign") == 0){ - if(ParamNum){ + else if (strcmp(argv[i], "-fsign") == 0) { + if (ParamNum) { PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } @@ -236,110 +241,110 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } // Ncch Options - else if(strcmp(argv[i],"-elf") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-elf") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->ncch.elfPath = argv[i+1]; + set->ncch.elfPath = argv[i + 1]; set->ncch.ncchType |= CXI; return 2; } - - else if(strcmp(argv[i],"-icon") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + + else if (strcmp(argv[i], "-icon") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->ncch.iconPath = argv[i+1]; + set->ncch.iconPath = argv[i + 1]; set->ncch.ncchType |= CFA; return 2; } - else if(strcmp(argv[i],"-banner") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-banner") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->ncch.bannerPath = argv[i+1]; + set->ncch.bannerPath = argv[i + 1]; set->ncch.ncchType |= CFA; return 2; } - else if(strcmp(argv[i],"-logo") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-logo") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->ncch.logoPath = argv[i+1]; + set->ncch.logoPath = argv[i + 1]; set->ncch.ncchType |= CFA; return 2; } - else if(strcmp(argv[i],"-desc") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-desc") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - char *tmp = argv[i+1]; - char *tmp2 = strstr(tmp,":"); - if(!tmp2){ - fprintf(stderr,"[SETTING ERROR] Bad argument '%s %s', correct format:\n",argv[i],argv[i+1]); - fprintf(stderr," -desc :\n"); + char *tmp = argv[i + 1]; + char *tmp2 = strstr(tmp, ":"); + if (!tmp2) { + fprintf(stderr, "[SETTING ERROR] Bad argument '%s %s', correct format:\n", argv[i], argv[i + 1]); + fprintf(stderr, " -desc :\n"); } - if(strlen(tmp2) < 2){ - fprintf(stderr,"[SETTING ERROR] Bad argument '%s %s', correct format:\n",argv[i],argv[i+1]); - fprintf(stderr," -desc :\n"); + if (strlen(tmp2) < 2) { + fprintf(stderr, "[SETTING ERROR] Bad argument '%s %s', correct format:\n", argv[i], argv[i + 1]); + fprintf(stderr, " -desc :\n"); } - u32 app_type_len = (u32)(tmp2-tmp); - char *app_type = calloc(app_type_len+1,sizeof(char)); - memcpy(app_type,tmp,app_type_len); + u32 app_type_len = (u32)(tmp2 - tmp); + char *app_type = calloc(app_type_len + 1, sizeof(char)); + memcpy(app_type, tmp, app_type_len); - if(strcasecmp(app_type,"App") == 0 || strcasecmp(app_type,"SDApp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_APP; - else if(strcasecmp(app_type,"ECApp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_EC_APP; - else if(strcasecmp(app_type,"Demo") == 0) set->common.keys.accessDescSign.presetType = desc_preset_DEMO; - else if(strcasecmp(app_type,"DlpChild") == 0 || strcasecmp(app_type,"Dlp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_DLP; - else if(strcasecmp(app_type,"FIRM") == 0) set->common.keys.accessDescSign.presetType = desc_preset_FIRM; - else{ - fprintf(stderr,"[SETTING ERROR] Accessdesc AppType preset '%s' not valid, please manually configure RSF\n",app_type); + if (strcasecmp(app_type, "App") == 0 || strcasecmp(app_type, "SDApp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_APP; + else if (strcasecmp(app_type, "ECApp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_EC_APP; + else if (strcasecmp(app_type, "Demo") == 0) set->common.keys.accessDescSign.presetType = desc_preset_DEMO; + else if (strcasecmp(app_type, "DlpChild") == 0 || strcasecmp(app_type, "Dlp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_DLP; + else if (strcasecmp(app_type, "FIRM") == 0) set->common.keys.accessDescSign.presetType = desc_preset_FIRM; + else { + fprintf(stderr, "[SETTING ERROR] Accessdesc AppType preset '%s' not valid, please manually configure RSF\n", app_type); return USR_BAD_ARG; } - char *target_firmware = (tmp2+1); - set->common.keys.accessDescSign.targetFirmware = strtoul(target_firmware,NULL,0); - switch(set->common.keys.accessDescSign.targetFirmware){ - case 1: - set->common.keys.accessDescSign.targetFirmware = 0x1B; // or 0x1C - break; - case 2: - set->common.keys.accessDescSign.targetFirmware = 0x1D; // or 0x1E/0x1F - break; - case 3: - set->common.keys.accessDescSign.targetFirmware = 0x20; - break; - case 4: - set->common.keys.accessDescSign.targetFirmware = 0x21; // or 0x22 - break; - case 5: - set->common.keys.accessDescSign.targetFirmware = 0x23; // or 0x24 - break; - case 6: - set->common.keys.accessDescSign.targetFirmware = 0x25; // or 0x26 - break; - case 7: - set->common.keys.accessDescSign.targetFirmware = 0x27; // or 0x28 - break; - case 8: - set->common.keys.accessDescSign.targetFirmware = 0x2C; - break; - default: - break; - } - + char *target_firmware = (tmp2 + 1); + set->common.keys.accessDescSign.targetFirmware = strtoul(target_firmware, NULL, 0); + switch (set->common.keys.accessDescSign.targetFirmware) { + case 1: + set->common.keys.accessDescSign.targetFirmware = 0x1B; // or 0x1C + break; + case 2: + set->common.keys.accessDescSign.targetFirmware = 0x1D; // or 0x1E/0x1F + break; + case 3: + set->common.keys.accessDescSign.targetFirmware = 0x20; + break; + case 4: + set->common.keys.accessDescSign.targetFirmware = 0x21; // or 0x22 + break; + case 5: + set->common.keys.accessDescSign.targetFirmware = 0x23; // or 0x24 + break; + case 6: + set->common.keys.accessDescSign.targetFirmware = 0x25; // or 0x26 + break; + case 7: + set->common.keys.accessDescSign.targetFirmware = 0x27; // or 0x28 + break; + case 8: + set->common.keys.accessDescSign.targetFirmware = 0x2C; + break; + default: + break; + } + set->ncch.ncchType |= CXI; return 2; } - else if(strcmp(argv[i],"-exefslogo") == 0){ - if(ParamNum){ + else if (strcmp(argv[i], "-exefslogo") == 0) { + if (ParamNum) { PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } @@ -349,438 +354,427 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } // Ncch Rebuild Options - else if(strcmp(argv[i],"-code") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-code") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->ncch.codePath = argv[i+1]; + set->ncch.codePath = argv[i + 1]; set->ncch.ncchType |= CXI; return 2; } - else if(strcmp(argv[i],"-exheader") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-exheader") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->ncch.exheaderPath = argv[i+1]; + set->ncch.exheaderPath = argv[i + 1]; set->ncch.ncchType |= CXI; return 2; } - else if(strcmp(argv[i],"-plainrgn") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-plainrgn") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->ncch.plainRegionPath = argv[i+1]; + set->ncch.plainRegionPath = argv[i + 1]; set->ncch.ncchType |= CXI; return 2; } - else if(strcmp(argv[i],"-romfs") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-romfs") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->ncch.romfsPath = argv[i+1]; + set->ncch.romfsPath = argv[i + 1]; set->ncch.ncchType |= CFA; return 2; } // Cci Options - else if(strcmp(argv[i],"-devcci") == 0){ - if(ParamNum){ + else if (strcmp(argv[i], "-devcci") == 0) { + if (ParamNum) { PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->cci.useSDKStockData = true; return 1; } - else if(strcmp(argv[i],"-nomodtid") == 0){ - if(ParamNum){ + else if (strcmp(argv[i], "-nomodtid") == 0) { + if (ParamNum) { PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->cci.dontModifyNcchTitleID = true; return 1; } - else if(strcmp(argv[i],"-alignwr") == 0){ - if(ParamNum){ + else if (strcmp(argv[i], "-alignwr") == 0) { + if (ParamNum) { PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->cci.closeAlignWritableRegion = true; return 1; } - else if(strcmp(argv[i],"-cverinfo") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-cverinfo") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_BAD_ARG; } - char *pos = strstr(argv[i+1],":"); - if(!pos || strlen(pos) < 2){ - fprintf(stderr,"[SETTING ERROR] Bad argument '%s %s', correct format:\n",argv[i],argv[i+1]); - fprintf(stderr," %s :<'cia'/'tmd'>\n",argv[i]); + char *pos = strstr(argv[i + 1], ":"); + if (!pos || strlen(pos) < 2) { + fprintf(stderr, "[SETTING ERROR] Bad argument '%s %s', correct format:\n", argv[i], argv[i + 1]); + fprintf(stderr, " %s :<'cia'/'tmd'>\n", argv[i]); return USR_BAD_ARG; } - + char *dtype = pos + 1; - if(strcasecmp(dtype,"tmd") == 0) + if (strcasecmp(dtype, "tmd") == 0) set->cci.cverDataType = CVER_DTYPE_TMD; - else if(strcasecmp(dtype,"cia") == 0) + else if (strcasecmp(dtype, "cia") == 0) set->cci.cverDataType = CVER_DTYPE_CIA; - else{ - fprintf(stderr,"[SETTING ERROR] Unrecognised cver data type:\"%s\"\n",dtype); + else { + fprintf(stderr, "[SETTING ERROR] Unrecognised cver data type:\"%s\"\n", dtype); return USR_BAD_ARG; } - - u32 path_len = (pos-argv[i+1])+1; - set->cci.cverDataPath = calloc(path_len,sizeof(char)); - strncpy(set->cci.cverDataPath,argv[i+1],path_len-1); - - if(!AssertFile(set->cci.cverDataPath)){ - fprintf(stderr,"[SETTING ERROR] Failed to open '%s'\n",set->cci.cverDataPath); + + u32 path_len = (pos - argv[i + 1]) + 1; + set->cci.cverDataPath = calloc(path_len, sizeof(char)); + strncpy(set->cci.cverDataPath, argv[i + 1], path_len - 1); + + if (!AssertFile(set->cci.cverDataPath)) { + fprintf(stderr, "[SETTING ERROR] Failed to open '%s'\n", set->cci.cverDataPath); return USR_BAD_ARG; } - + return 2; } // Cia Options - else if(strcmp(argv[i],"-major") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-major") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } set->cia.useNormTitleVer = true; - u32 ver = strtoul(argv[i+1],NULL,10); - if(ver > VER_MAJOR_MAX){ - fprintf(stderr,"[SETTING ERROR] Major version: '%d' is too large, max: '%d'\n",ver,VER_MAJOR_MAX); + u32 ver = strtoul(argv[i + 1], NULL, 10); + if (ver > VER_MAJOR_MAX) { + fprintf(stderr, "[SETTING ERROR] Major version: '%d' is too large, max: '%d'\n", ver, VER_MAJOR_MAX); return USR_BAD_ARG; } set->cia.titleVersion[VER_MAJOR] = ver; return 2; } - else if(strcmp(argv[i],"-minor") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-minor") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } set->cia.useNormTitleVer = true; - u32 ver = strtoul(argv[i+1],NULL,10); - if(ver > VER_MINOR_MAX){ - fprintf(stderr,"[SETTING ERROR] Minor version: '%d' is too large, max: '%d'\n",ver,VER_MINOR_MAX); + u32 ver = strtoul(argv[i + 1], NULL, 10); + if (ver > VER_MINOR_MAX) { + fprintf(stderr, "[SETTING ERROR] Minor version: '%d' is too large, max: '%d'\n", ver, VER_MINOR_MAX); return USR_BAD_ARG; } set->cia.titleVersion[VER_MINOR] = ver; return 2; } - else if(strcmp(argv[i],"-micro") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-micro") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - u32 ver = strtoul(argv[i+1],NULL,10); - if(ver > VER_MICRO_MAX){ - fprintf(stderr,"[SETTING ERROR] Micro version: '%d' is too large, max: '%d'\n",ver,VER_MICRO_MAX); + u32 ver = strtoul(argv[i + 1], NULL, 10); + if (ver > VER_MICRO_MAX) { + fprintf(stderr, "[SETTING ERROR] Micro version: '%d' is too large, max: '%d'\n", ver, VER_MICRO_MAX); return USR_BAD_ARG; } set->cia.titleVersion[VER_MICRO] = ver; return 2; } - else if(strcmp(argv[i],"-dver") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-dver") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } set->cia.useDataTitleVer = true; - u32 ver = strtoul(argv[i+1],NULL,10); - if(ver > VER_DVER_MAX){ - fprintf(stderr,"[SETTING ERROR] Data version: '%d' is too large, max: '%d'\n",ver,VER_DVER_MAX); + u32 ver = strtoul(argv[i + 1], NULL, 10); + if (ver > VER_DVER_MAX) { + fprintf(stderr, "[SETTING ERROR] Data version: '%d' is too large, max: '%d'\n", ver, VER_DVER_MAX); return USR_BAD_ARG; } set->cia.titleVersion[VER_MAJOR] = (ver >> 6) & VER_MAJOR_MAX; set->cia.titleVersion[VER_MINOR] = ver & VER_MINOR_MAX; return 2; } - else if(strcmp(argv[i],"-deviceid") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-deviceid") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->cia.deviceId = strtoul(argv[i+1],NULL,16); + set->cia.deviceId = strtoul(argv[i + 1], NULL, 16); return 2; } - else if(strcmp(argv[i],"-esaccid") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-esaccid") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->cia.eshopAccId = strtoul(argv[i+1],NULL,16); + set->cia.eshopAccId = strtoul(argv[i + 1], NULL, 16); return 2; } - else if(strcmp(argv[i],"-rand") == 0){ - if(ParamNum){ + else if (strcmp(argv[i], "-rand") == 0) { + if (ParamNum) { PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->cia.randomTitleKey = true; return 1; } - else if(strcmp(argv[i],"-dlc") == 0){ - if(ParamNum){ + else if (strcmp(argv[i], "-dlc") == 0) { + if (ParamNum) { PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->cia.DlcContent = true; return 1; } - else if(strcmp(argv[i],"-srl") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-srl") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } set->ncch.buildNcch0 = false; set->common.workingFileType = infile_srl; - set->common.workingFilePath = argv[i+1]; + set->common.workingFilePath = argv[i + 1]; set->common.outFormat = CIA; return 2; } // Ncch Container Conversion - else if(strcmp(argv[i],"-ccitocia") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-ccitocia") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } set->ncch.buildNcch0 = false; set->common.workingFileType = infile_ncsd; - set->common.workingFilePath = argv[i+1]; + set->common.workingFilePath = argv[i + 1]; set->common.outFormat = CIA; return 2; } - else if(strcmp(argv[i],"-ciatocci") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-ciatocci") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } set->ncch.buildNcch0 = false; set->common.workingFileType = infile_cia; - set->common.workingFilePath = argv[i+1]; + set->common.workingFilePath = argv[i + 1]; set->common.outFormat = CCI; return 2; } - else if(strcmp(argv[i],"-inclupd") == 0){ - if(ParamNum){ + else if (strcmp(argv[i], "-inclupd") == 0) { + if (ParamNum) { PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } set->cia.includeUpdateNcch = true; return 1; } - + // Other Setting - else if(strcmp(argv[i],"-content") == 0 || strcmp(argv[i],"-i") == 0){ - if(ParamNum != 1){ - PrintArgReqParam(argv[i],1); + else if (strcmp(argv[i], "-content") == 0 || strcmp(argv[i], "-i") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - char *pos = strstr(argv[i+1],":"); - if(!pos || strlen(pos) < 2){ - fprintf(stderr,"[SETTING ERROR] Bad argument '%s %s', correct format:\n",argv[i],argv[i+1]); - fprintf(stderr," %s :\n",argv[i]); - fprintf(stderr," If generating a CIA, then use the format:\n"); - fprintf(stderr," %s ::\n",argv[i]); + char *pos = strstr(argv[i + 1], ":"); + if (!pos || strlen(pos) < 2) { + fprintf(stderr, "[SETTING ERROR] Bad argument '%s %s', correct format:\n", argv[i], argv[i + 1]); + fprintf(stderr, " %s :\n", argv[i]); + fprintf(stderr, " If generating a CIA, then use the format:\n"); + fprintf(stderr, " %s ::\n", argv[i]); return USR_BAD_ARG; - } + } /* Getting Content Index */ - u16 content_index = strtol((char*)(pos+1),NULL,0); + u16 content_index = strtol((char*)(pos + 1), NULL, 0); /* Storing Content Filepath */ - u32 path_len = (u32)(pos-argv[i+1])+1; - - if(set->common.contentPath[content_index] != NULL){ - fprintf(stderr,"[SETTING ERROR] Content %d is already specified\n",content_index); + u32 path_len = (u32)(pos - argv[i + 1]) + 1; + + if (set->common.contentPath[content_index] != NULL) { + fprintf(stderr, "[SETTING ERROR] Content %d is already specified\n", content_index); return USR_BAD_ARG; } - set->common.contentPath[content_index] = calloc(path_len,sizeof(char)); - if(set->common.contentPath[content_index] == NULL){ - fprintf(stderr,"[SETTING ERROR] Not enough memory\n"); + set->common.contentPath[content_index] = calloc(path_len, sizeof(char)); + if (set->common.contentPath[content_index] == NULL) { + fprintf(stderr, "[SETTING ERROR] Not enough memory\n"); return USR_MEM_ERROR; } - strncpy(set->common.contentPath[content_index],argv[i+1],path_len-1); - if(!AssertFile(set->common.contentPath[content_index])){ - fprintf(stderr,"[SETTING ERROR] '%s' could not be opened\n",set->common.contentPath[content_index]); + strncpy(set->common.contentPath[content_index], argv[i + 1], path_len - 1); + if (!AssertFile(set->common.contentPath[content_index])) { + fprintf(stderr, "[SETTING ERROR] '%s' could not be opened\n", set->common.contentPath[content_index]); return USR_BAD_ARG; } set->common.contentSize[content_index] = GetFileSize64(set->common.contentPath[content_index]); - + /* Get ContentID for CIA gen */ - char *pos2 = strstr(pos+1,":"); - if(pos2) - set->cia.contentId[content_index] = strtoul((pos2+1),NULL,0); - + char *pos2 = strstr(pos + 1, ":"); + if (pos2) + set->cia.contentId[content_index] = strtoul((pos2 + 1), NULL, 0); + /* Return Next Arg Pos*/ return 2; } // RSF Value Substitution - else if(strncmp(argv[i],"-D",2) == 0){ - if(ParamNum){ + else if (strncmp(argv[i], "-D", 2) == 0) { + if (ParamNum) { PrintNoNeedParam("-DNAME=VALUE"); return USR_BAD_ARG; } - if(set->dname.m_items == 0){ + if (set->dname.m_items == 0) { set->dname.m_items = 10; set->dname.u_items = 0; - set->dname.items = calloc(set->dname.m_items,sizeof(dname_item)); - if(!set->dname.items){ - fprintf(stderr,"[SETTING ERROR] Not enough memory\n"); + set->dname.items = calloc(set->dname.m_items, sizeof(dname_item)); + if (!set->dname.items) { + fprintf(stderr, "[SETTING ERROR] Not enough memory\n"); return MEM_ERROR; } } - else if(set->dname.m_items == set->dname.u_items){ + else if (set->dname.m_items == set->dname.u_items) { set->dname.m_items *= 2; - /* - dname_item *tmp = malloc(sizeof(dname_item)*set->dname.m_items); - if(!tmp){ - fprintf(stderr,"[SETTING ERROR] Not enough memory\n"); - return MEM_ERROR; - } - memset(tmp,0,sizeof(dname_item)*set->dname.m_items); - memcpy(tmp,set->dname.items,sizeof(dname_item)*set->dname.u_items); - free(set->dname.items); - set->dname.items = tmp; - */ - set->dname.items = realloc(set->dname.items,sizeof(dname_item)*set->dname.m_items); - if(!set->dname.items){ - fprintf(stderr,"[SETTING ERROR] Not enough memory\n"); + set->dname.items = realloc(set->dname.items, sizeof(dname_item)*set->dname.m_items); + if (!set->dname.items) { + fprintf(stderr, "[SETTING ERROR] Not enough memory\n"); return MEM_ERROR; } } - char *name_pos = (char*)(argv[i]+2); + char *name_pos = (char*)(argv[i] + 2); u32 name_len = 0; - char *val_pos = strstr(name_pos,"="); + char *val_pos = strstr(name_pos, "="); u32 val_len = 0; - if(!val_pos){ - fprintf(stderr,"[SETTING ERROR] Format: '%s' is invalid\n",argv[i]); + if (!val_pos) { + fprintf(stderr, "[SETTING ERROR] Format: '%s' is invalid\n", argv[i]); return USR_BAD_ARG; } - if(strlen(val_pos) < 2){ - fprintf(stderr,"[SETTING ERROR] Format: '%s' is invalid\n",argv[i]); + if (strlen(val_pos) < 2) { + fprintf(stderr, "[SETTING ERROR] Format: '%s' is invalid\n", argv[i]); return USR_BAD_ARG; } val_pos = (val_pos + 1); name_len = (val_pos - 1 - name_pos); - set->dname.items[set->dname.u_items].name = malloc(name_len+1); - memset(set->dname.items[set->dname.u_items].name,0,name_len+1); - memcpy(set->dname.items[set->dname.u_items].name,name_pos,name_len); + set->dname.items[set->dname.u_items].name = malloc(name_len + 1); + memset(set->dname.items[set->dname.u_items].name, 0, name_len + 1); + memcpy(set->dname.items[set->dname.u_items].name, name_pos, name_len); val_len = strlen(val_pos); - set->dname.items[set->dname.u_items].value = malloc(val_len+1); - memset(set->dname.items[set->dname.u_items].value,0,val_len+1); - memcpy(set->dname.items[set->dname.u_items].value,val_pos,val_len); + set->dname.items[set->dname.u_items].value = malloc(val_len + 1); + memset(set->dname.items[set->dname.u_items].value, 0, val_len + 1); + memcpy(set->dname.items[set->dname.u_items].value, val_pos, val_len); set->dname.u_items++; return 1; } - + // If not a valid argument - fprintf(stderr,"[SETTING ERROR] Unrecognised argument '%s'\n",argv[i]); + fprintf(stderr, "[SETTING ERROR] Unrecognised argument '%s'\n", argv[i]); return USR_UNK_ARG; } int CheckArgumentCombination(user_settings *set) { - if(set->ncch.ncchType & (CXI|CFA)){ + if (set->ncch.ncchType & (CXI | CFA)) { set->ncch.buildNcch0 = true; - if(set->ncch.ncchType & CXI) + if (set->ncch.ncchType & CXI) set->ncch.ncchType = CXI; else - set->ncch.ncchType = CFA; - } - - if(set->common.outFormat == NCCH){ + set->ncch.ncchType = CFA; + } + + if (set->common.outFormat == NCCH) { set->ncch.buildNcch0 = true; - if(set->ncch.ncchType) + if (set->ncch.ncchType) set->common.outFormat = set->ncch.ncchType; - else{ + else { set->ncch.ncchType = CFA; set->common.outFormat = CFA; } } - for(int i = 0; i < CIA_MAX_CONTENT; i++){ - if( i > CCI_MAX_CONTENT-1 && set->common.contentPath[i] && set->common.outFormat == CCI){ - fprintf(stderr,"[SETTING ERROR] Content indexes > %d are invalid for CCI\n",CCI_MAX_CONTENT-1); + for (int i = 0; i < CIA_MAX_CONTENT; i++) { + if (i > CCI_MAX_CONTENT - 1 && set->common.contentPath[i] && set->common.outFormat == CCI) { + fprintf(stderr, "[SETTING ERROR] Content indexes > %d are invalid for CCI\n", CCI_MAX_CONTENT - 1); return USR_BAD_ARG; } - if(set->common.contentPath[i] && (set->common.outFormat == CXI || set->common.outFormat == CFA)){ - fprintf(stderr,"[SETTING ERROR] You cannot specify content while outputting CXI/CFA files\n"); + if (set->common.contentPath[i] && (set->common.outFormat == CXI || set->common.outFormat == CFA)) { + fprintf(stderr, "[SETTING ERROR] You cannot specify content while outputting CXI/CFA files\n"); return USR_BAD_ARG; } } - - if(set->common.contentPath[0] && set->ncch.buildNcch0){ - fprintf(stderr,"[SETTING ERROR] You cannot both import and build content 0\n"); + + if (set->common.contentPath[0] && set->ncch.buildNcch0) { + fprintf(stderr, "[SETTING ERROR] You cannot both import and build content 0\n"); return USR_BAD_ARG; } - if(set->common.outFormat == CIA && set->cci.cverDataPath){ - fprintf(stderr,"[SETTING ERROR] You cannot use argument \"-cverinfo\" when generating a CIA\n"); + if (set->common.outFormat == CIA && set->cci.cverDataPath) { + fprintf(stderr, "[SETTING ERROR] You cannot use argument \"-cverinfo\" when generating a CIA\n"); return USR_BAD_ARG; } - if(set->cia.useDataTitleVer && set->cia.useNormTitleVer){ - fprintf(stderr,"[SETTING ERROR] Arguments \"-dver\" and \"-major\"/\"-minor\" cannot be used together\n"); + if (set->cia.useDataTitleVer && set->cia.useNormTitleVer) { + fprintf(stderr, "[SETTING ERROR] Arguments \"-dver\" and \"-major\"/\"-minor\" cannot be used together\n"); return USR_BAD_ARG; } - if(set->ncch.elfPath && set->ncch.codePath){ - fprintf(stderr,"[SETTING ERROR] Arguments \"-elf\" and \"-code\" cannot be used together\n"); + if (set->ncch.elfPath && set->ncch.codePath) { + fprintf(stderr, "[SETTING ERROR] Arguments \"-elf\" and \"-code\" cannot be used together\n"); return USR_BAD_ARG; } bool buildCXI = set->ncch.ncchType == CXI; bool buildCFA = set->ncch.ncchType == CFA; // Detecting Required Arguments - if(buildCXI && !set->ncch.elfPath && !set->ncch.codePath){ + if (buildCXI && !set->ncch.elfPath && !set->ncch.codePath) { PrintNeedsArg("-elf"); return USR_BAD_ARG; } - if((buildCXI || buildCFA) && !set->common.rsfPath){ + if ((buildCXI || buildCFA) && !set->common.rsfPath) { PrintNeedsArg("-rsf"); return USR_BAD_ARG; } - if(buildCXI && set->ncch.codePath && !set->ncch.exheaderPath){ + if (buildCXI && set->ncch.codePath && !set->ncch.exheaderPath) { PrintNeedsArg("-exheader"); return USR_BAD_ARG; } // Reporting bad arguments - if(!buildCXI && set->ncch.elfPath){ + if (!buildCXI && set->ncch.elfPath) { PrintArgInvalid("-elf"); return USR_BAD_ARG; } - if(!buildCXI && set->ncch.codePath){ + if (!buildCXI && set->ncch.codePath) { PrintArgInvalid("-code"); return USR_BAD_ARG; } - if(!buildCXI && set->ncch.exheaderPath){ + if (!buildCXI && set->ncch.exheaderPath) { PrintArgInvalid("-exheader"); return USR_BAD_ARG; } - if(!buildCXI && set->ncch.plainRegionPath){ + if (!buildCXI && set->ncch.plainRegionPath) { PrintArgInvalid("-plainrgn"); return USR_BAD_ARG; } - if(!set->ncch.buildNcch0 && set->ncch.includeExefsLogo){ + if (!set->ncch.buildNcch0 && set->ncch.includeExefsLogo) { PrintArgInvalid("-exefslogo"); return USR_BAD_ARG; } - if(!set->ncch.buildNcch0 && set->ncch.romfsPath){ + if (!set->ncch.buildNcch0 && set->ncch.romfsPath) { PrintArgInvalid("-romfs"); return USR_BAD_ARG; } @@ -790,78 +784,110 @@ int CheckArgumentCombination(user_settings *set) void init_UserSettings(user_settings *set) { - memset(set,0,sizeof(user_settings)); + memset(set, 0, sizeof(user_settings)); } void free_UserSettings(user_settings *set) { // Free Content Paths - if(set->common.contentPath){ - for(int i = 0; i < CIA_MAX_CONTENT; i++) + if (set->common.contentPath) { + for (int i = 0; i < CIA_MAX_CONTENT; i++) free(set->common.contentPath[i]); free(set->common.contentPath); } // free -DNAME=VALUE - for(u32 i = 0; i < set->dname.u_items; i++){ + for (u32 i = 0; i < set->dname.u_items; i++) { free(set->dname.items[i].name); free(set->dname.items[i].value); } free(set->dname.items); free(set->cci.cverDataPath); - + // Free Spec File Setting free_RsfSettings(&set->common.rsfSet); // Free Key Data FreeKeys(&set->common.keys); - + // Free Working File free(set->common.workingFile.buffer); - + // Free outfile path, if malloc'd - if(set->common.outFileName_mallocd) + if (set->common.outFileName_mallocd) free(set->common.outFileName); - + // Clear settings init_UserSettings(set); - + // Free free(set); } void PrintNeedsArg(char *arg) { - fprintf(stderr,"[SETTING ERROR] Argument \"%s\" is required\n",arg); + fprintf(stderr, "[SETTING ERROR] Argument \"%s\" is required\n", arg); } void PrintArgInvalid(char *arg) { - fprintf(stderr,"[SETTING ERROR] Argument \"%s\" is invalid\n",arg); + fprintf(stderr, "[SETTING ERROR] Argument \"%s\" is invalid\n", arg); } void PrintArgReqParam(char *arg, u32 paramNum) { - if(paramNum == 1) - fprintf(stderr,"[SETTING ERROR] \"%s\" takes one parameter\n",arg); + if (paramNum == 1) + fprintf(stderr, "[SETTING ERROR] \"%s\" takes one parameter\n", arg); else - fprintf(stderr,"[SETTING ERROR] \"%s\" requires %d parameters\n",arg,paramNum); + fprintf(stderr, "[SETTING ERROR] \"%s\" requires %d parameters\n", arg, paramNum); } void PrintNoNeedParam(char *arg) { - fprintf(stderr,"[SETTING ERROR] \"%s\" does not take a parameter\n",arg); + fprintf(stderr, "[SETTING ERROR] \"%s\" does not take a parameter\n", arg); +} + +void DisplayBanner(void) +{ + printf("CTR MAKEROM v0.14 (C) 3DSGuy 2014\n"); + printf("Built: %s %s\n\n", __TIME__, __DATE__); } void DisplayHelp(char *app_name) { - printf("CTR MAKEROM %d.%d\n",MAKEROM_VER_MAJOR,MAKEROM_VER_MINOR); - printf("(C) 3DSGuy 2014\n"); - printf("Usage: %s [options... ]\n",app_name); + DisplayBanner(); + printf("Usage: %s [options... ]\n", app_name); printf("Option Parameter Explanation\n"); printf("GLOBAL OPTIONS:\n"); printf(" -help Display this text\n"); + printf(" -exthelp Display extended usage help\n"); + printf(" -rsf ROM Spec File (*.rsf)\n"); + printf(" -f Output format, defaults to 'ncch'\n"); + printf(" -o Output file\n"); + printf(" -v Verbose output\n"); + printf(" -DNAME=VALUE Substitute values in RSF file\n"); + printf("NCCH OPTIONS:\n"); + printf(" -elf ELF file\n"); + printf(" -icon Icon file\n"); + printf(" -banner Banner file\n"); + printf(" -desc : Specify Access Descriptor template\n"); + printf("NCCH REBUILD OPTIONS:\n"); + printf(" -code Decompressed ExeFS \".code\"\n"); + printf(" -exheader Exheader template\n"); + printf(" -romfs RomFS binary\n"); + printf("CIA/CCI OPTIONS:\n"); + printf(" -content : Specify content files\n"); +} + +void DisplayExtendedHelp(char *app_name) +{ + DisplayBanner(); + printf("Usage: %s [options... ]\n", app_name); + printf("Option Parameter Explanation\n"); + printf("GLOBAL OPTIONS:\n"); + printf(" -help Display simple usage help\n"); + printf(" -exthelp Display this text\n"); printf(" -rsf ROM Spec File (*.rsf)\n"); printf(" -f Output format, defaults to 'ncch'\n"); printf(" -o Output file\n"); @@ -887,7 +913,7 @@ void DisplayHelp(char *app_name) printf(" -code Decompressed ExeFS \".code\"\n"); printf(" -exheader Exheader template\n"); printf(" -plainrgn Plain Region binary\n"); - printf(" -romfs RomFS binary\n"); + printf(" -romfs RomFS binary\n"); printf("CCI OPTIONS:\n"); printf(" -content : Specify content files\n"); printf(" -devcci Use external CTRSDK \"CardInfo\" method\n"); @@ -896,7 +922,7 @@ void DisplayHelp(char *app_name) printf(" -cverinfo : Include cver title info\n"); printf("CIA OPTIONS:\n"); printf(" -content :: Specify content files\n"); - printf(" -major Major version\n"); + printf(" -ver Title Version\n"); printf(" -minor Minor version\n"); printf(" -micro Micro version\n"); printf(" -dver Data-title version\n"); @@ -909,5 +935,4 @@ void DisplayHelp(char *app_name) printf(" -ccitocia Convert CCI to CIA\n"); printf(" -ciatocci Convert CIA to CCI\n"); printf(" -inclupd Include \"Update NCCH\" in CCI to CIA conversion\n"); - } \ No newline at end of file From 4c965b8c9237855e98520be1c854b54f131edef1 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 22 Oct 2015 01:45:08 +0800 Subject: [PATCH 110/317] [makerom] Simplified CIA version logic. --- makerom/cia.c | 26 ++++---------------------- makerom/cia_build.h | 4 ++-- makerom/user_settings.c | 38 +++++++++++++++++++++++++++++++++----- makerom/user_settings.h | 2 ++ 4 files changed, 41 insertions(+), 29 deletions(-) diff --git a/makerom/cia.c b/makerom/cia.c index 8c496bc3..719c91b4 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -170,6 +170,7 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) ciaset->cert.caCrlVersion = 0; ciaset->cert.signerCrlVersion = 0; + ciaset->common.useCxiRemasterVersion = !usrset->cia.useFullTitleVer; for(int i = 0; i < 3; i++){ ciaset->common.titleVersion[i] = usrset->cia.titleVersion[i]; } @@ -239,6 +240,7 @@ int GetSettingsFromNcch0(cia_settings *ciaset, u32 ncch0_offset) int result = VerifyNcch(ncch0, ciaset->keys, false, ciaset->verbose == false); if(result == UNABLE_TO_LOAD_NCCH_KEY){ ciaset->content.keyFound = false; + ciaset->common.useCxiRemasterVersion = false; if(!ciaset->content.IsCfa){ fprintf(stderr,"[CIA WARNING] CXI AES Key could not be loaded\n"); fprintf(stderr," Meta Region, SaveDataSize, Remaster Version cannot be obtained\n"); @@ -306,35 +308,15 @@ int GetTmdDataFromNcch(cia_settings *ciaset, u8 *ncch, ncch_info *ncch_ctx, u8 * ciaset->tmd.savedataSize = 0; // Process title version - // If this is a CFA, the -major must be set, since CFAs don't have an exheader - if(ciaset->content.IsCfa){ - if(ciaset->common.titleVersion[VER_MAJOR] == MAX_U16){ // '-major' wasn't set - fprintf(stderr,"[CIA ERROR] Invalid major version. Use \"-major\" option.\n"); - result = CIA_BAD_VERSION; - goto cleanup; - } - } - // Otherwise this is a CXI, if it can be decrypted, -major cannot be set and must be read from exheader - else if(ciaset->content.keyFound){ - if(ciaset->common.titleVersion[VER_MAJOR] != MAX_U16){ // '-major' was set - fprintf(stderr,"[CIA ERROR] Option \"-major\" cannot be applied for cxi.\n"); - result = CIA_BAD_VERSION; - goto cleanup; - } + // If this is a CXI, and we have to pull version major from exheader... + if(!ciaset->content.IsCfa && ciaset->common.useCxiRemasterVersion){ // Setting remaster ver ciaset->common.titleVersion[VER_MAJOR] = GetRemasterVersion_frm_exhdr(exhdr); } - - // CXI which cannot be decrypted - else{ - if(ciaset->common.titleVersion[VER_MAJOR] == MAX_U16) // '-major' wasn't set - ciaset->common.titleVersion[VER_MAJOR] = 0; - } // Calculate u16 title version ciaset->tmd.version = ciaset->tik.version = SetupVersion(ciaset->common.titleVersion[VER_MAJOR],ciaset->common.titleVersion[VER_MINOR],ciaset->common.titleVersion[VER_MICRO]); -cleanup: free(exhdr); return result; } diff --git a/makerom/cia_build.h b/makerom/cia_build.h index 0c24277e..f25f1562 100644 --- a/makerom/cia_build.h +++ b/makerom/cia_build.h @@ -7,7 +7,6 @@ typedef enum CIA_NO_NCCH0 = -1, CIA_INVALID_NCCH0 = -2, CIA_CONFILCTING_CONTENT_IDS = -3, - CIA_BAD_VERSION = -4, } cia_errors; typedef struct @@ -23,8 +22,9 @@ typedef struct bool verbose; struct{ + bool useCxiRemasterVersion; u64 titleId; - u16 titleVersion[4]; + u16 titleVersion[3]; u8 titleKey[16]; } common; diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 7477b821..aa5bc468 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -118,7 +118,7 @@ void SetDefaults(user_settings *set) set->cia.deviceId = 0; set->cia.eshopAccId = 0; set->cia.useDataTitleVer = false; - set->cia.titleVersion[VER_MAJOR] = MAX_U16; // invalid so changes can be detected + set->cia.useFullTitleVer = false; set->cia.randomTitleKey = false; set->common.keys.aes.currentCommonKey = MAX_U8 + 1; // invalid so changes can be detected for (int i = 0; i < CIA_MAX_CONTENT; i++) @@ -450,13 +450,29 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } // Cia Options + else if (strcmp(argv[i], "-ver") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); + return USR_ARG_REQ_PARAM; + } + set->cia.useFullTitleVer = true; + u32 ver = strtoul(argv[i + 1], NULL, 0); + if (ver > VER_MAX) { + fprintf(stderr, "[SETTING ERROR] Version: '%d' is too large, max: '%d'\n", ver, VER_MAX); + return USR_BAD_ARG; + } + set->cia.titleVersion[VER_MAJOR] = (ver >> 10) & VER_MAJOR_MAX; + set->cia.titleVersion[VER_MINOR] = (ver >> 4) & VER_MINOR_MAX; + set->cia.titleVersion[VER_MICRO] = ver & VER_MICRO_MAX; + return 2; + } else if (strcmp(argv[i], "-major") == 0) { if (ParamNum != 1) { PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } set->cia.useNormTitleVer = true; - u32 ver = strtoul(argv[i + 1], NULL, 10); + u32 ver = strtoul(argv[i + 1], NULL, 0); if (ver > VER_MAJOR_MAX) { fprintf(stderr, "[SETTING ERROR] Major version: '%d' is too large, max: '%d'\n", ver, VER_MAJOR_MAX); return USR_BAD_ARG; @@ -470,7 +486,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->cia.useNormTitleVer = true; - u32 ver = strtoul(argv[i + 1], NULL, 10); + u32 ver = strtoul(argv[i + 1], NULL, 0); if (ver > VER_MINOR_MAX) { fprintf(stderr, "[SETTING ERROR] Minor version: '%d' is too large, max: '%d'\n", ver, VER_MINOR_MAX); return USR_BAD_ARG; @@ -483,7 +499,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - u32 ver = strtoul(argv[i + 1], NULL, 10); + u32 ver = strtoul(argv[i + 1], NULL, 0); if (ver > VER_MICRO_MAX) { fprintf(stderr, "[SETTING ERROR] Micro version: '%d' is too large, max: '%d'\n", ver, VER_MICRO_MAX); return USR_BAD_ARG; @@ -497,7 +513,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) return USR_ARG_REQ_PARAM; } set->cia.useDataTitleVer = true; - u32 ver = strtoul(argv[i + 1], NULL, 10); + u32 ver = strtoul(argv[i + 1], NULL, 0); if (ver > VER_DVER_MAX) { fprintf(stderr, "[SETTING ERROR] Data version: '%d' is too large, max: '%d'\n", ver, VER_DVER_MAX); return USR_BAD_ARG; @@ -732,6 +748,16 @@ int CheckArgumentCombination(user_settings *set) return USR_BAD_ARG; } + if (set->cia.useDataTitleVer && set->cia.useFullTitleVer) { + fprintf(stderr, "[SETTING ERROR] Arguments \"-dver\" and \"-ver\" cannot be used together\n"); + return USR_BAD_ARG; + } + + if (set->cia.useNormTitleVer && set->cia.useFullTitleVer) { + fprintf(stderr, "[SETTING ERROR] Arguments \"-ver\" and \"-major\"/\"-minor\" cannot be used together\n"); + return USR_BAD_ARG; + } + if (set->ncch.elfPath && set->ncch.codePath) { fprintf(stderr, "[SETTING ERROR] Arguments \"-elf\" and \"-code\" cannot be used together\n"); return USR_BAD_ARG; @@ -878,6 +904,7 @@ void DisplayHelp(char *app_name) printf(" -romfs RomFS binary\n"); printf("CIA/CCI OPTIONS:\n"); printf(" -content : Specify content files\n"); + printf(" -ver Title Version\n"); } void DisplayExtendedHelp(char *app_name) @@ -923,6 +950,7 @@ void DisplayExtendedHelp(char *app_name) printf("CIA OPTIONS:\n"); printf(" -content :: Specify content files\n"); printf(" -ver Title Version\n"); + printf(" -major Major version\n"); printf(" -minor Minor version\n"); printf(" -micro Micro version\n"); printf(" -dver Data-title version\n"); diff --git a/makerom/user_settings.h b/makerom/user_settings.h index 6e18c986..f06f95ff 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -15,6 +15,7 @@ typedef enum typedef enum { + VER_MAX = 65535, VER_MAJOR_MAX = 63, VER_MINOR_MAX = 63, VER_MICRO_MAX = 15, @@ -291,6 +292,7 @@ typedef struct bool useNormTitleVer; bool useDataTitleVer; + bool useFullTitleVer; u16 titleVersion[3]; u32 deviceId; From 3702b58db37d8a471d304c9be06bbb636a563628 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 24 Oct 2015 01:32:08 +0800 Subject: [PATCH 111/317] [makerom] Cleaned keyset.c --- makerom/keyset.c | 149 ++++++++++++++++++++++++----------------------- makerom/keyset.h | 8 +-- makerom/utils.c | 16 +---- makerom/utils.h | 3 +- 4 files changed, 81 insertions(+), 95 deletions(-) diff --git a/makerom/keyset.c b/makerom/keyset.c index a270d38d..cdb32a6b 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -6,24 +6,23 @@ #include "pki/dev.h" // Development PKI // Private Prototypes -int SetRsaKeySet(u8 **PrivDest, u8 *PrivSource, u8 **PubDest, u8 *PubSource); -int SetunFixedKey(keys_struct *keys, u8 *unFixedKey); +int SetRsaKeySet(u8 **priv_exp_dst, const u8 *priv_exp_src, u8 **modulus_dst, const u8 *modulus_src); void InitCommonKeySlots(keys_struct *keys); void InitNcchKeyXSlots(keys_struct *keys); -int SetNcchKeyX(keys_struct *keys, u8 *keyX, u8 index); +int SetNcchKeyX(keys_struct *keys, const u8 *keyX, u8 index); -FILE* keyset_OpenFile(char *dir, char *name, bool FileRequired); void keysetOpenError(char *file); +FILE* keyset_OpenFile(char *dir, char *name, bool FileRequired); -int SetTIK_RsaKey(keys_struct *keys, u8 *PrivateExp, u8 *PublicMod); -int SetTMD_RsaKey(keys_struct *keys, u8 *PrivateExp, u8 *PublicMod); -int Set_CCI_CFA_RsaKey(keys_struct *keys, u8 *PrivateExp, u8 *PublicMod); -int SetAccessDesc_RsaKey(keys_struct *keys, u8 *PrivateExp, u8 *PublicMod); -int SetCXI_RsaKey(keys_struct *keys, u8 *PrivateExp, u8 *PublicMod); +int SetTIK_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus); +int SetTMD_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus); +int Set_CCI_CFA_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus); +int SetAccessDesc_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus); +int SetCXI_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus); -int SetCaCert(keys_struct *keys, u8 *Cert); -int SetTikCert(keys_struct *keys, u8 *Cert); -int SetTmdCert(keys_struct *keys, u8 *Cert); +int SetCaCert(keys_struct *keys, const u8 *cert); +int SetTikCert(keys_struct *keys, const u8 *cert); +int SetTmdCert(keys_struct *keys, const u8 *cert); int LoadKeysFromResources(keys_struct *keys); void SetDummyRsaData(keys_struct *keys); @@ -48,7 +47,7 @@ void PrintBadKeySize(char *path, u32 size) fprintf(stderr,"[KEYSET ERROR] %s has invalid size (0x%x)\n",path,size); } -u8* AesKeyScrambler(u8 *key, u8 *keyX, u8 *keyY) +u8* AesKeyScrambler(u8 *key, const u8 *keyX, const u8 *keyY) { // Process keyX/keyY to get raw normal key for(int i = 0; i < 16; i++) @@ -91,7 +90,7 @@ int LoadKeysFromResources(keys_struct *keys) keys->keysetLoaded = true; /* AES Keys */ // CIA - //SetCommonKey(keys,(u8*)zeros_aesKey,1); + //SetCommonKey(keys, zeros_aesKey,1); if(keys->aes.currentCommonKey > 0xff) SetCurrentCommonKey(keys,0); @@ -107,33 +106,33 @@ int LoadKeysFromResources(keys_struct *keys) /* AES Keys */ // CIA for(int i = 0; i < 2; i++) - SetCommonKey(keys,(u8*)ctr_common_etd_key_dpki[i],i); + SetCommonKey(keys, ctr_common_etd_key_dpki[i],i); if(keys->aes.currentCommonKey > 0xff) SetCurrentCommonKey(keys,0); // NCCH - SetNormalKey(keys,(u8*)dev_fixed_ncch_key[0]); - SetSystemFixedKey(keys,(u8*)dev_fixed_ncch_key[1]); + SetNormalKey(keys, dev_fixed_ncch_key[0]); + SetSystemFixedKey(keys, dev_fixed_ncch_key[1]); /* for(int i = 0; i < 2; i++) - SetNcchKeyX(keys,(u8*)dev_unfixed_ncch_keyX[i],i); + SetNcchKeyX(keys, dev_unfixed_ncch_keyX[i],i); */ /* RSA Keys */ // CIA - SetTIK_RsaKey(keys,(u8*)xs9_dpki_rsa_priv,(u8*)xs9_dpki_rsa_pub); - SetTMD_RsaKey(keys,(u8*)cpA_dpki_rsa_priv,(u8*)cpA_dpki_rsa_pub); + SetTIK_RsaKey(keys, xs9_dpki_rsa_priv, xs9_dpki_rsa_pub); + SetTMD_RsaKey(keys, cpA_dpki_rsa_priv, cpA_dpki_rsa_pub); // CCI/CFA - Set_CCI_CFA_RsaKey(keys,(u8*)dev_ncsd_cfa_priv,(u8*)dev_ncsd_cfa_pub); + Set_CCI_CFA_RsaKey(keys, dev_ncsd_cfa_priv, dev_ncsd_cfa_pub); // CXI - SetAccessDesc_RsaKey(keys,(u8*)dev_acex_priv,(u8*)dev_acex_pub); + SetAccessDesc_RsaKey(keys, dev_acex_priv, dev_acex_pub); /* Certs */ - SetCaCert(keys,(u8*)ca4_dpki_cert); - SetTikCert(keys,(u8*)xs9_dpki_cert); - SetTmdCert(keys,(u8*)cpA_dpki_cert); + SetCaCert(keys, ca4_dpki_cert); + SetTikCert(keys, xs9_dpki_cert); + SetTmdCert(keys, cpA_dpki_cert); } else if(keys->keyset == pki_PRODUCTION){ keys->keysetLoaded = true; @@ -141,7 +140,7 @@ int LoadKeysFromResources(keys_struct *keys) // CIA //for(int i = 0; i < 6; i++){ // keys->aes.commonKey[i] = malloc(16); - // AesKeyScrambler(keys->aes.commonKey[i],(u8*)ctr_common_etd_keyX_ppki,(u8*)ctr_common_etd_keyY_ppki[i]); + // AesKeyScrambler(keys->aes.commonKey[i], ctr_common_etd_keyX_ppki, ctr_common_etd_keyY_ppki[i]); //} if(keys->aes.currentCommonKey > 0xff) SetCurrentCommonKey(keys,0); @@ -151,22 +150,22 @@ int LoadKeysFromResources(keys_struct *keys) keys->aes.systemFixedKey = NULL; /* for(int i = 0; i < 2; i++) - SetNcchKeyX(keys,(u8*)prod_unfixed_ncch_keyX[i],i); + SetNcchKeyX(keys, prod_unfixed_ncch_keyX[i],i); */ /* RSA Keys */ // CIA - SetTIK_RsaKey(keys,(u8*)xsC_ppki_rsa_priv,(u8*)xsC_ppki_rsa_pub); - SetTMD_RsaKey(keys,(u8*)cpB_ppki_rsa_priv,(u8*)cpB_ppki_rsa_pub); + SetTIK_RsaKey(keys, xsC_ppki_rsa_priv, xsC_ppki_rsa_pub); + SetTMD_RsaKey(keys, cpB_ppki_rsa_priv, cpB_ppki_rsa_pub); // CCI/CFA - Set_CCI_CFA_RsaKey(keys,(u8*)prod_ncsd_cfa_priv,(u8*)prod_ncsd_cfa_pub); + Set_CCI_CFA_RsaKey(keys, prod_ncsd_cfa_priv, prod_ncsd_cfa_pub); // CXI - SetAccessDesc_RsaKey(keys,(u8*)prod_acex_priv,(u8*)prod_acex_pub); + SetAccessDesc_RsaKey(keys, prod_acex_priv, prod_acex_pub); /* Certs */ - SetCaCert(keys,(u8*)ca3_ppki_cert); - SetTikCert(keys,(u8*)xsC_ppki_cert); - SetTmdCert(keys,(u8*)cpB_ppki_cert); + SetCaCert(keys, ca3_ppki_cert); + SetTikCert(keys, xsC_ppki_cert); + SetTmdCert(keys, cpB_ppki_cert); } return 0; } @@ -174,23 +173,23 @@ int LoadKeysFromResources(keys_struct *keys) void SetDummyRsaData(keys_struct *keys) { if(!keys->rsa.xsPvt || !keys->rsa.xsPub) - SetTIK_RsaKey(keys,(u8*)tpki_rsa_privExp,(u8*)tpki_rsa_pubMod); + SetTIK_RsaKey(keys, tpki_rsa_privExp, tpki_rsa_pubMod); if(!keys->rsa.cpPvt || !keys->rsa.cpPub) - SetTMD_RsaKey(keys,(u8*)tpki_rsa_privExp,(u8*)tpki_rsa_pubMod); + SetTMD_RsaKey(keys, tpki_rsa_privExp, tpki_rsa_pubMod); if(!keys->rsa.cciCfaPvt || !keys->rsa.cciCfaPub) - Set_CCI_CFA_RsaKey(keys,(u8*)tpki_rsa_privExp,(u8*)tpki_rsa_pubMod); + Set_CCI_CFA_RsaKey(keys, tpki_rsa_privExp, tpki_rsa_pubMod); if(!keys->rsa.acexPvt || !keys->rsa.acexPub) - SetAccessDesc_RsaKey(keys,(u8*)tpki_rsa_privExp,(u8*)tpki_rsa_pubMod); + SetAccessDesc_RsaKey(keys, tpki_rsa_privExp, tpki_rsa_pubMod); /* Certs */ if(!keys->certs.caCert) - SetCaCert(keys,(u8*)ca3_tpki_cert); + SetCaCert(keys, ca3_tpki_cert); if(!keys->certs.xsCert) - SetTikCert(keys,(u8*)xsC_tpki_cert); + SetTikCert(keys, xsC_tpki_cert); if(!keys->certs.cpCert) - SetTmdCert(keys,(u8*)cpB_tpki_cert); + SetTmdCert(keys, cpB_tpki_cert); } int LoadKeysFromKeyfile(keys_struct *keys) @@ -255,7 +254,12 @@ void DumpKeyset(keys_struct *keys) memdump(stdout," [PVT] ",keys->rsa.cciCfaPvt,0x100); } -FILE* keyset_OpenFile(char *dir, char *name, bool FileRequired) +void keysetOpenError(char *file) +{ + fprintf(stderr, "[KEYSET ERROR] Failed to open: %s\n", file); +} + +FILE* keyset_OpenFile(char *dir, char *name, bool is_required) { int file_path_len = sizeof(char)*(strlen(dir)+strlen(name)+1); char *file_path = malloc(file_path_len); @@ -265,17 +269,14 @@ FILE* keyset_OpenFile(char *dir, char *name, bool FileRequired) FILE *fp = fopen(file_path,"rb"); - if(!fp && FileRequired) - fprintf(stderr,"[KEYSET ERROR] Failed to open: %s\n",file_path); + if (!fp && is_required) + keysetOpenError(file_path); free(file_path); return fp; } -void keysetOpenError(char *file) -{ - fprintf(stderr,"[KEYSET ERROR] Failed to open: %s\n",file); -} + void FreeKeys(keys_struct *keys) { @@ -316,24 +317,24 @@ void FreeKeys(keys_struct *keys) memset(keys,0,sizeof(keys_struct)); } -int SetRsaKeySet(u8 **PrivDest, u8 *PrivSource, u8 **PubDest, u8 *PubSource) +int SetRsaKeySet(u8 **priv_exp_dst, const u8 *priv_exp_src, u8 **modulus_dst, const u8 *modulus_src) { int result = 0; - if(PrivSource){ - result = CopyData(PrivDest,PrivSource,0x100); + if(priv_exp_src){ + result = CopyData(priv_exp_dst,priv_exp_src,0x100); if(result) return result; } - if(PubSource){ - result = CopyData(PubDest,PubSource,0x100); + if(modulus_src){ + result = CopyData(modulus_dst,modulus_src,0x100); if(result) return result; } return 0; } -int SetCommonKey(keys_struct *keys, u8 *commonKey, u8 index) +int SetCommonKey(keys_struct *keys, const u8 *key, u8 index) { if(!keys) return -1; - return CopyData(&keys->aes.commonKey[index],commonKey,AES_128_KEY_SIZE); + return CopyData(&keys->aes.commonKey[index],key,AES_128_KEY_SIZE); } void InitCommonKeySlots(keys_struct *keys) @@ -342,7 +343,7 @@ void InitCommonKeySlots(keys_struct *keys) keys->aes.commonKey = calloc(MAX_CMN_KEY+1,sizeof(u8*)); } -int SetNcchKeyX(keys_struct *keys, u8 *keyX, u8 index) +int SetNcchKeyX(keys_struct *keys, const u8 *keyX, u8 index) { if(!keys) return -1; return CopyData(&keys->aes.ncchKeyX[index],keyX,AES_128_KEY_SIZE); @@ -361,55 +362,55 @@ int SetCurrentCommonKey(keys_struct *keys, u8 Index) return 0; } -int SetNormalKey(keys_struct *keys, u8 *systemFixedKey) +int SetNormalKey(keys_struct *keys, const u8 *key) { if(!keys) return -1; - return CopyData(&keys->aes.normalKey,systemFixedKey,16); + return CopyData(&keys->aes.normalKey,key,16); } -int SetSystemFixedKey(keys_struct *keys, u8 *systemFixedKey) +int SetSystemFixedKey(keys_struct *keys, const u8 *key) { if(!keys) return -1; - return CopyData(&keys->aes.systemFixedKey,systemFixedKey,16); + return CopyData(&keys->aes.systemFixedKey,key,16); } -int SetTIK_RsaKey(keys_struct *keys, u8 *PrivateExp, u8 *PublicMod) +int SetTIK_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus) { if(!keys) return -1; - return SetRsaKeySet(&keys->rsa.xsPvt,PrivateExp,&keys->rsa.xsPub,PublicMod); + return SetRsaKeySet(&keys->rsa.xsPvt,priv_exp,&keys->rsa.xsPub,modulus); } -int SetTMD_RsaKey(keys_struct *keys, u8 *PrivateExp, u8 *PublicMod) +int SetTMD_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus) { if(!keys) return -1; - return SetRsaKeySet(&keys->rsa.cpPvt,PrivateExp,&keys->rsa.cpPub,PublicMod); + return SetRsaKeySet(&keys->rsa.cpPvt,priv_exp,&keys->rsa.cpPub,modulus); } -int Set_CCI_CFA_RsaKey(keys_struct *keys, u8 *PrivateExp, u8 *PublicMod) +int Set_CCI_CFA_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus) { if(!keys) return -1; - return SetRsaKeySet(&keys->rsa.cciCfaPvt,PrivateExp,&keys->rsa.cciCfaPub,PublicMod); + return SetRsaKeySet(&keys->rsa.cciCfaPvt,priv_exp,&keys->rsa.cciCfaPub,modulus); } -int SetAccessDesc_RsaKey(keys_struct *keys, u8 *PrivateExp, u8 *PublicMod) +int SetAccessDesc_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus) { if(!keys) return -1; - return SetRsaKeySet(&keys->rsa.acexPvt,PrivateExp,&keys->rsa.acexPub,PublicMod); + return SetRsaKeySet(&keys->rsa.acexPvt,priv_exp,&keys->rsa.acexPub,modulus); } -int SetCaCert(keys_struct *keys, u8 *Cert) +int SetCaCert(keys_struct *keys, const u8 *cert) { if(!keys) return -1; - return CopyData(&keys->certs.caCert,Cert,0x400); + return CopyData(&keys->certs.caCert,cert,0x400); } -int SetTikCert(keys_struct *keys, u8 *Cert) +int SetTikCert(keys_struct *keys, const u8 *cert) { if(!keys) return -1; - return CopyData(&keys->certs.xsCert,Cert,0x300); + return CopyData(&keys->certs.xsCert,cert,0x300); } -int SetTmdCert(keys_struct *keys, u8 *Cert) +int SetTmdCert(keys_struct *keys, const u8 *cert) { if(!keys) return -1; - return CopyData(&keys->certs.cpCert,Cert,0x400); + return CopyData(&keys->certs.cpCert,cert,0x400); } \ No newline at end of file diff --git a/makerom/keyset.h b/makerom/keyset.h index 9d6c0c60..54d38fcc 100644 --- a/makerom/keyset.h +++ b/makerom/keyset.h @@ -102,9 +102,9 @@ void InitKeys(keys_struct *keys); int SetKeys(keys_struct *keys); void FreeKeys(keys_struct *keys); -int SetCommonKey(keys_struct *keys, u8 *commonKey, u8 Index); +int SetCommonKey(keys_struct *keys, const u8 *key, u8 Index); int SetCurrentCommonKey(keys_struct *keys, u8 Index); -int SetNormalKey(keys_struct *keys, u8 *systemFixedKey); -int SetSystemFixedKey(keys_struct *keys, u8 *systemFixedKey); +int SetNormalKey(keys_struct *keys, const u8 *key); +int SetSystemFixedKey(keys_struct *keys, const u8 *key); -u8* AesKeyScrambler(u8 *key, u8 *keyX, u8 *keyY); +u8* AesKeyScrambler(u8 *key, const u8 *keyX, const u8 *keyY); diff --git a/makerom/utils.c b/makerom/utils.c index 450427fd..5a97fb66 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -4,21 +4,7 @@ #include "polarssl/base64.h" // Memory -void endian_memcpy(u8 *destination, u8 *source, u32 size, int endianness) -{ - for (u32 i = 0; i < size; i++){ - switch (endianness){ - case(BE): - destination[i] = source[i]; - break; - case(LE): - destination[i] = source[((size-1)-i)]; - break; - } - } -} - -int CopyData(u8 **dest, u8 *source, u64 size) +int CopyData(u8 **dest, const u8 *source, u64 size) { if(!*dest){ *dest = malloc(size); diff --git a/makerom/utils.h b/makerom/utils.h index da32454f..c479f113 100644 --- a/makerom/utils.h +++ b/makerom/utils.h @@ -7,8 +7,7 @@ typedef struct } buffer_struct; // Memory -void endian_memcpy(u8 *destination, u8 *source, u32 size, int endianness); -int CopyData(u8 **dest, u8 *source, u64 size); +int CopyData(u8 **dest, const u8 *source, u64 size); void rndset(void *ptr, u64 num); void clrmem(void *ptr, u64 num); From 3f3418e8402be61825a14dbf5d1fc88b2b9f0d21 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 24 Oct 2015 13:50:05 +0800 Subject: [PATCH 112/317] [makerom] Updated NCSD spec, as per 3dbrew.org contributor research. --- makerom/cardinfo.c | 38 ++++++++++++++++++++++++++------------ makerom/cardinfo.h | 20 +++++++------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/makerom/cardinfo.c b/makerom/cardinfo.c index e9b80a41..4a278b7a 100644 --- a/makerom/cardinfo.c +++ b/makerom/cardinfo.c @@ -7,8 +7,8 @@ void InitCardInfoHdr(cardinfo_hdr **cihdr, devcardinfo_hdr **dcihdr, cci_setting int SetWriteableAddress(cardinfo_hdr *hdr, cci_settings *set); int SetCardInfoBitmask(cardinfo_hdr *hdr, cci_settings *set); int SetCardInfoNotes(cardinfo_hdr *hdr, cci_settings *set); -void ImportNcch0Data(cardinfo_hdr *hdr, cci_settings *set); -void SetInitialData(cardinfo_hdr *hdr, cci_settings *set); +void SetNcchHeader(cardinfo_hdr *hdr, cci_settings *set); +void SetCardSeedData(cardinfo_hdr *hdr, cci_settings *set); void SetDevCardInfo(devcardinfo_hdr *hdr, cci_settings *set); @@ -25,8 +25,8 @@ int GenCardInfoHdr(cci_settings *set) return GEN_HDR_FAIL; if(SetCardInfoNotes(cihdr,set)) return GEN_HDR_FAIL; - ImportNcch0Data(cihdr,set); - SetInitialData(cihdr,set); + SetNcchHeader(cihdr,set); + SetCardSeedData(cihdr,set); if(dcihdr) SetDevCardInfo(dcihdr,set); @@ -49,6 +49,8 @@ void InitCardInfoHdr(cardinfo_hdr **cihdr, devcardinfo_hdr **dcihdr, cci_setting else *dcihdr = NULL; + //clrmem(set->headers.cardinfohdr.buffer, set->headers.cardinfohdr.size); + return; } @@ -174,7 +176,7 @@ int SetCardInfoNotes(cardinfo_hdr *hdr, cci_settings *set) return 0; } -void ImportNcch0Data(cardinfo_hdr *hdr, cci_settings *set) +void SetNcchHeader(cardinfo_hdr *hdr, cci_settings *set) { u8 *ncch; ncch_hdr *ncchHdr; @@ -182,19 +184,31 @@ void ImportNcch0Data(cardinfo_hdr *hdr, cci_settings *set) ncch = set->content.data + set->content.dOffset[0]; ncchHdr = (ncch_hdr*)ncch; - memcpy(hdr->ncch0TitleId,ncchHdr->titleId,8); memcpy(hdr->ncch0Hdr,GetNcchHdrData(ncchHdr),GetNcchHdrDataLen(ncchHdr)); return; } -void SetInitialData(cardinfo_hdr *hdr, cci_settings *set) +void SetCardSeedData(cardinfo_hdr *hdr, cci_settings *set) { - clrmem(hdr->initialData,0x30); - if(set->options.useExternalSdkCardInfo) - memcpy(hdr->initialData,(u8*)stock_initial_data,0x30); - else - rndset(hdr->initialData,0x2c); + u8 *ncch; + ncch_hdr *ncchHdr; + + ncch = set->content.data + set->content.dOffset[0]; + ncchHdr = (ncch_hdr*)ncch; + + if (set->options.useExternalSdkCardInfo) { + memcpy(hdr->cardSeedKeyY, ncchHdr->titleId, 8); + clrmem(hdr->encCardSeed, 0x10); + memcpy(hdr->cardSeedMac, stock_card_seed_mac, 0x10); + clrmem(hdr->cardSeedNonce, 0xC); + } + else { + memcpy(hdr->cardSeedKeyY, ncchHdr->titleId, 8); + rndset(hdr->encCardSeed, 0x10); + rndset(hdr->cardSeedMac, 0x10); + rndset(hdr->cardSeedNonce, 0xC); + } return; } diff --git a/makerom/cardinfo.h b/makerom/cardinfo.h index 271edac8..1b368056 100644 --- a/makerom/cardinfo.h +++ b/makerom/cardinfo.h @@ -17,10 +17,11 @@ typedef struct u8 writableAddress[4]; u8 cardInfoBitmask[4]; cardinfo_notes notes; - u8 ncch0TitleId[8]; - u8 reserved0[8]; - u8 initialData[0x30]; - u8 reserved1[0xc0]; + u8 cardSeedKeyY[0x10]; + u8 encCardSeed[0x10]; + u8 cardSeedMac[0x10]; + u8 cardSeedNonce[0xc]; + u8 reserved1[0xc4]; u8 ncch0Hdr[0x100]; } cardinfo_hdr; @@ -31,16 +32,9 @@ typedef struct u8 cardDeviceReserved2[0xf0]; } devcardinfo_hdr; -static const u8 stock_initial_data[0x30] = +static const u8 stock_card_seed_mac[0x30] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xAD, 0x88, - 0xAC, 0x41, 0xA2, 0xB1, 0x5E, 0x8F, - 0x66, 0x9C, 0x97, 0xE5, 0xE1, 0x5E, - 0xA3, 0xEB, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0xAD, 0x88, 0xAC, 0x41, 0xA2, 0xB1, 0x5E, 0x8F, 0x66, 0x9C, 0x97, 0xE5, 0xE1, 0x5E, 0xA3, 0xEB }; static const u8 stock_title_key[0x10] = From a4b261bde7801b48a1fe5ee5619ca5a380361da4 Mon Sep 17 00:00:00 2001 From: profi200 Date: Sat, 31 Oct 2015 13:10:46 +0100 Subject: [PATCH 113/317] Added padlock.c back. Fixes building for x86 targets. --- makerom/polarssl/padlock.c | 162 +++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 makerom/polarssl/padlock.c diff --git a/makerom/polarssl/padlock.c b/makerom/polarssl/padlock.c new file mode 100644 index 00000000..9ddac15e --- /dev/null +++ b/makerom/polarssl/padlock.c @@ -0,0 +1,162 @@ +/* + * VIA PadLock support functions + * + * 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 implementation is based on the VIA PadLock Programming Guide: + * + * http://www.via.com.tw/en/downloads/whitepapers/initiatives/padlock/ + * programming_guide.pdf + */ + +#include "polarssl/config.h" + +#if defined(POLARSSL_PADLOCK_C) + +#include "polarssl/padlock.h" + +#if defined(POLARSSL_HAVE_X86) + +/* + * PadLock detection routine + */ +int padlock_supports( int feature ) +{ + static int flags = -1; + int ebx, edx; + + if( flags == -1 ) + { + __asm__( "movl %%ebx, %0 \n" \ + "movl $0xC0000000, %%eax \n" \ + "cpuid \n" \ + "cmpl $0xC0000001, %%eax \n" \ + "movl $0, %%edx \n" \ + "jb unsupported \n" \ + "movl $0xC0000001, %%eax \n" \ + "cpuid \n" \ + "unsupported: \n" \ + "movl %%edx, %1 \n" \ + "movl %2, %%ebx \n" + : "=m" (ebx), "=m" (edx) + : "m" (ebx) + : "eax", "ecx", "edx" ); + + flags = edx; + } + + return( flags & feature ); +} + +/* + * PadLock AES-ECB block en(de)cryption + */ +int padlock_xcryptecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ) +{ + int ebx; + uint32_t *rk; + uint32_t *blk; + uint32_t *ctrl; + unsigned char buf[256]; + + rk = ctx->rk; + blk = PADLOCK_ALIGN16( buf ); + memcpy( blk, input, 16 ); + + ctrl = blk + 4; + *ctrl = 0x80 | ctx->nr | ( ( ctx->nr + ( mode^1 ) - 10 ) << 9 ); + + __asm__( "pushfl; popfl \n" \ + "movl %%ebx, %0 \n" \ + "movl $1, %%ecx \n" \ + "movl %2, %%edx \n" \ + "movl %3, %%ebx \n" \ + "movl %4, %%esi \n" \ + "movl %4, %%edi \n" \ + ".byte 0xf3,0x0f,0xa7,0xc8\n" \ + "movl %1, %%ebx \n" + : "=m" (ebx) + : "m" (ebx), "m" (ctrl), "m" (rk), "m" (blk) + : "ecx", "edx", "esi", "edi" ); + + memcpy( output, blk, 16 ); + + return( 0 ); +} + +/* + * PadLock AES-CBC buffer en(de)cryption + */ +int padlock_xcryptcbc( aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int ebx; + size_t count; + uint32_t *rk; + uint32_t *iw; + uint32_t *ctrl; + unsigned char buf[256]; + + if( ( (long) input & 15 ) != 0 || + ( (long) output & 15 ) != 0 ) + return( POLARSSL_ERR_PADLOCK_DATA_MISALIGNED ); + + rk = ctx->rk; + iw = PADLOCK_ALIGN16( buf ); + memcpy( iw, iv, 16 ); + + ctrl = iw + 4; + *ctrl = 0x80 | ctx->nr | ( ( ctx->nr + (mode^1) - 10 ) << 9 ); + + count = (length + 15) >> 4; + + __asm__( "pushfl; popfl \n" \ + "movl %%ebx, %0 \n" \ + "movl %2, %%ecx \n" \ + "movl %3, %%edx \n" \ + "movl %4, %%ebx \n" \ + "movl %5, %%esi \n" \ + "movl %6, %%edi \n" \ + "movl %7, %%eax \n" \ + ".byte 0xf3,0x0f,0xa7,0xd0\n" \ + "movl %1, %%ebx \n" + : "=m" (ebx) + : "m" (ebx), "m" (count), "m" (ctrl), + "m" (rk), "m" (input), "m" (output), "m" (iw) + : "eax", "ecx", "edx", "esi", "edi" ); + + memcpy( iv, iw, 16 ); + + return( 0 ); +} + +#endif + +#endif From 7116b5549fa108d2ea92dda7924801fc21b60b3e Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 3 Nov 2015 16:14:27 +0800 Subject: [PATCH 114/317] [ctrtool] Changed TMD title version representation. --- ctrtool/tmd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ctrtool/tmd.c b/ctrtool/tmd.c index 8ba2dd24..af5691ff 100644 --- a/ctrtool/tmd.c +++ b/ctrtool/tmd.c @@ -87,6 +87,7 @@ void tmd_print(tmd_context* ctx) ctr_tmd_body* body = 0; unsigned int contentcount = 0; unsigned int savesize = 0; + unsigned int titlever = 0; unsigned int i; if (type == TMD_RSA_2048_SHA256 || type == TMD_RSA_2048_SHA1) @@ -106,6 +107,7 @@ void tmd_print(tmd_context* ctx) contentcount = getbe16(body->contentcount); savesize = getle32(body->savedatasize); + titlever = getbe16(body->titleversion); fprintf(stdout, "\nTMD header:\n"); fprintf(stdout, "Signature type: %s\n", tmd_get_type_string(type)); @@ -124,7 +126,7 @@ void tmd_print(tmd_context* ctx) else fprintf(stdout, "Save Size: %dMB (%08x)\n", savesize/sizeMB, savesize); fprintf(stdout, "Access rights: %08x\n", getbe32(body->accessrights)); - fprintf(stdout, "Title version: %04x\n", getbe16(body->titleversion)); + fprintf(stdout, "Title version: %d.%d.%d (v%d)\n", (titlever >> 10) & 0x3F, (titlever >> 4) & 0x3F, titlever & 0xF, titlever); fprintf(stdout, "Content count: %04x\n", getbe16(body->contentcount)); fprintf(stdout, "Boot content: %04x\n", getbe16(body->bootcontent)); memdump(stdout, "Hash: ", body->hash, 32); From 3cb804ee6a2dc58829206ded1ddf606d9b1f7e3b Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 3 Nov 2015 16:18:01 +0800 Subject: [PATCH 115/317] [makerom] Separated ELF processing from code segment construction. StackSize now read from exheader template. --- makerom/accessdesc.c | 10 +- makerom/code.c | 388 +++++++++++++ makerom/code.h | 3 + makerom/elf.c | 937 +++++--------------------------- makerom/elf.h | 139 +++-- makerom/elf_hdr.h | 192 ------- makerom/exheader.c | 91 ++-- makerom/exheader.h | 27 +- makerom/makerom.vcxproj | 3 +- makerom/makerom.vcxproj.filters | 9 +- makerom/ncch.c | 2 +- makerom/ncch_build.h | 1 + makerom/utils.c | 6 +- makerom/utils.h | 6 +- 14 files changed, 726 insertions(+), 1088 deletions(-) create mode 100644 makerom/code.c create mode 100644 makerom/code.h delete mode 100644 makerom/elf_hdr.h diff --git a/makerom/accessdesc.c b/makerom/accessdesc.c index fcb17be3..dde1b58b 100644 --- a/makerom/accessdesc.c +++ b/makerom/accessdesc.c @@ -44,12 +44,10 @@ int accessdesc_SignWithKey(exheader_settings *exhdrset) memcpy(&exhdrset->acexDesc->arm9AccessControlInfo,&exhdrset->exHdr->arm9AccessControlInfo,sizeof(exhdr_ARM9AccessControlInfo)); /* Adjust Data */ - u8 *flag = &exhdrset->acexDesc->arm11SystemLocalCapabilities.flag[2]; - u8 SystemMode = (*flag>>4)&0xF; - u8 AffinityMask = (*flag>>2)&0x3; - u8 IdealProcessor = 1<<((*flag>>0)&0x3); - *flag = (u8)(SystemMode << 4 | AffinityMask << 2 | IdealProcessor); - exhdrset->acexDesc->arm11SystemLocalCapabilities.flag[3] /= 2; + exhdr_ARM11SystemLocalCapabilities *arm11 = &exhdrset->acexDesc->arm11SystemLocalCapabilities; + + arm11->idealProcessor = 1 << arm11->idealProcessor; + arm11->threadPriority /= 2; /* Sign AccessDesc */ return SignAccessDesc(exhdrset->acexDesc,exhdrset->keys); diff --git a/makerom/code.c b/makerom/code.c new file mode 100644 index 00000000..ea31b2c0 --- /dev/null +++ b/makerom/code.c @@ -0,0 +1,388 @@ +#include "lib.h" +#include "elf.h" +#include "blz.h" +#include "ncch_build.h" +#include "exheader_read.h" +#include "code.h" + +const char *SDK_PLAINREGION_SEGMENT_NAME = ".module_id"; + +typedef struct code_segment +{ + u32 address; + u32 size; + u32 maxPageNum; + u8 *data; +} code_segment; + +u32 GetPageSize(ncch_settings *set) +{ + if (set->rsfSet->Option.PageSize) + return strtoul(set->rsfSet->Option.PageSize, NULL, 10); + return 0x1000; +} + +u32 SizeToPage(u32 memorySize, elf_context *elf) +{ + return align(memorySize, elf->pageSize) / elf->pageSize; +} + +int ImportPlainRegionFromFile(ncch_settings *set) +{ + set->sections.plainRegion.size = align(set->componentFilePtrs.plainregionSize, set->options.blockSize); + set->sections.plainRegion.buffer = malloc(set->sections.plainRegion.size); + if (!set->sections.plainRegion.buffer) { fprintf(stderr, "[ELF ERROR] Not enough memory\n"); return MEM_ERROR; } + ReadFile64(set->sections.plainRegion.buffer, set->componentFilePtrs.plainregionSize, 0, set->componentFilePtrs.plainregion); + return 0; +} + +int ImportExeFsCodeBinaryFromFile(ncch_settings *set) +{ + u32 size = set->componentFilePtrs.codeSize; + u8 *buffer = malloc(size); + if (!buffer) { + fprintf(stderr, "[ELF ERROR] Not enough memory\n"); + return MEM_ERROR; + } + ReadFile64(buffer, size, 0, set->componentFilePtrs.code); + + set->exefsSections.code.size = set->componentFilePtrs.codeSize; + set->exefsSections.code.buffer = malloc(set->exefsSections.code.size); + if (!set->exefsSections.code.buffer) { fprintf(stderr, "[ELF ERROR] Not enough memory\n"); return MEM_ERROR; } + ReadFile64(set->exefsSections.code.buffer, set->exefsSections.code.size, 0, set->componentFilePtrs.code); + if (set->options.CompressCode) { + u32 new_len; + set->exefsSections.code.buffer = BLZ_Code(buffer, size, &new_len, BLZ_NORMAL); + set->exefsSections.code.size = new_len; + free(buffer); + } + else { + set->exefsSections.code.size = size; + set->exefsSections.code.buffer = buffer; + } + + size = set->componentFilePtrs.exhdrSize; + if (size < sizeof(extended_hdr)) { + fprintf(stderr, "[ELF ERROR] Exheader code info template is too small\n"); + return FAILED_TO_IMPORT_FILE; + } + extended_hdr *exhdr = malloc(size); + if (!exhdr) { + fprintf(stderr, "[ELF ERROR] Not enough memory\n"); + return MEM_ERROR; + } + ReadFile64(exhdr, size, 0, set->componentFilePtrs.exhdr); + + /* Setting code_segment data */ + set->codeDetails.textAddress = u8_to_u32(exhdr->codeSetInfo.text.address, LE); + set->codeDetails.textMaxPages = u8_to_u32(exhdr->codeSetInfo.text.numMaxPages, LE); + set->codeDetails.textSize = u8_to_u32(exhdr->codeSetInfo.text.codeSize, LE); + + set->codeDetails.roAddress = u8_to_u32(exhdr->codeSetInfo.rodata.address, LE); + set->codeDetails.roMaxPages = u8_to_u32(exhdr->codeSetInfo.rodata.numMaxPages, LE); + set->codeDetails.roSize = u8_to_u32(exhdr->codeSetInfo.rodata.codeSize, LE); + + set->codeDetails.rwAddress = u8_to_u32(exhdr->codeSetInfo.data.address, LE); + set->codeDetails.rwMaxPages = u8_to_u32(exhdr->codeSetInfo.data.numMaxPages, LE); + set->codeDetails.rwSize = u8_to_u32(exhdr->codeSetInfo.data.codeSize, LE); + + set->codeDetails.bssSize = u8_to_u32(exhdr->codeSetInfo.bssSize, LE); + + set->codeDetails.stackSize = u8_to_u32(exhdr->codeSetInfo.stackSize, LE); + + free(exhdr); + + return 0; +} + +int GetBSSFromElf(elf_context *elf, ncch_settings *set) +{ + set->codeDetails.bssSize = 0; + + for (int i = 0; i < elf->sectionTableEntryCount; i++) { + if (IsBss(&elf->sections[i])) + set->codeDetails.bssSize = elf->sections[i].size; + } + + return 0; +} + +int ImportPlainRegionFromElf(elf_context *elf, ncch_settings *set) // Doesn't work same as N makerom +{ + u64 size = 0; + u64 offset = 0; + for (u16 i = 0; i < elf->activeSegments; i++) { + if (strcmp(elf->segments[i].name, SDK_PLAINREGION_SEGMENT_NAME) == 0) { + size = elf->segments[i].header->sizeInFile; + offset = elf->segments[i].header->offsetInFile; + break; + } + } + + + if (size > 0) { + /* Creating Output Buffer */ + set->sections.plainRegion.size = align(size, set->options.blockSize); + set->sections.plainRegion.buffer = malloc(set->sections.plainRegion.size); + if (!set->sections.plainRegion.buffer) { fprintf(stderr, "[ELF ERROR] Not enough memory\n"); return MEM_ERROR; } + memset(set->sections.plainRegion.buffer, 0, set->sections.plainRegion.size); + + /* Copy Plain Region */ + memcpy(set->sections.plainRegion.buffer, elf->file + offset, size); + } + + return 0; +} + +int CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u64 segment_flags) +{ + memset(out, 0, sizeof(code_segment)); + + u16 seg_num = 0; + elf_segment **seg = calloc(elf->activeSegments, sizeof(elf_segment*)); + + for (u16 i = 0; i < elf->activeSegments; i++) { + // Skip SDK ELF plain region + if (strcmp(elf->segments[i].name, SDK_PLAINREGION_SEGMENT_NAME) == 0) + continue; + + //printf("SegName: %s (flags: %x)\n", elf->segments[i].name, elf->segments[i].header->flags); + if ((elf->segments[i].header->flags & ~PF_CTRSDK) == segment_flags) { + if (seg_num == 0) { + seg[seg_num] = &elf->segments[i]; + seg_num++; + } + else if (elf->segments[i].vAddr == (u32)align(seg[seg_num - 1]->vAddr, seg[seg_num - 1]->header->alignment)) { + seg[seg_num] = &elf->segments[i]; + seg_num++; + } + } + } + + /* Return if there are no applicable segment */ + if (seg_num == 0) + return 0; + + /* Getting Segment Size/Settings */ + u32 vAddr = 0; + u32 memorySize = 0; + for (u16 i = 0; i < seg_num; i++) { + if (i == 0) { + vAddr = seg[i]->vAddr; + } + else { // Add rounded size from previous segment + u32 padding = seg[i]->vAddr - (vAddr + memorySize); + memorySize += padding; + } + + memorySize += seg[i]->header->sizeInMemory; + + if (IsBss(&seg[i]->sections[seg[i]->sectionNum - 1])) + memorySize -= seg[i]->sections[seg[i]->sectionNum - 1].size; + } + + // For Check +#ifdef DEBUG + printf("Address: 0x%x\n", vAddr); + printf("Size: 0x%x\n", memorySize); +#endif + + out->address = vAddr; + out->size = memorySize; + out->maxPageNum = SizeToPage(memorySize, elf); + out->data = malloc(memorySize); + + /* Writing Segment to Buffer */ + for (int i = 0; i < seg_num; i++) { + + for (int j = 0; j < seg[i]->sectionNum; j++) { + elf_section_entry *section = &seg[i]->sections[j]; + if (!IsBss(section)) { + u8 *pos = (out->data + (section->address - seg[i]->vAddr)); + memcpy(pos, section->ptr, section->size); + //size += section->size; + } + } + } + + free(seg); + return 0; +} + +int CreateExeFsCode(elf_context *elf, ncch_settings *set) +{ + /* Getting Code Segments */ + code_segment text; + memset(&text, 0, sizeof(code_segment)); + code_segment rodata; + memset(&rodata, 0, sizeof(code_segment)); + code_segment rwdata; + memset(&rwdata, 0, sizeof(code_segment)); + + int result = CreateCodeSegmentFromElf(&text, elf, PF_TEXT); + if (result) return result; + result = CreateCodeSegmentFromElf(&rodata, elf, PF_RODATA); + if (result) return result; + result = CreateCodeSegmentFromElf(&rwdata, elf, PF_DATA); + if (result) return result; + + /* Checking the existence of essential ELF Segments */ + if (!text.size) return NOT_FIND_TEXT_SEGMENT; + if (!rwdata.size) return NOT_FIND_DATA_SEGMENT; + + /* Allocating Buffer for ExeFs Code */ + u32 size = (text.maxPageNum + rodata.maxPageNum + rwdata.maxPageNum)*elf->pageSize; + u8 *code = calloc(1, size); + + /* Writing Code into Buffer */ + u8 *textPos = (code + 0); + u8 *rodataPos = (code + text.maxPageNum*elf->pageSize); + u8 *rwdataPos = (code + (text.maxPageNum + rodata.maxPageNum)*elf->pageSize); + if (text.size) memcpy(textPos, text.data, text.size); + if (rodata.size) memcpy(rodataPos, rodata.data, rodata.size); + if (rwdata.size) memcpy(rwdataPos, rwdata.data, rwdata.size); + + + /* Compressing If needed */ + if (set->options.CompressCode) { + u32 new_len; + set->exefsSections.code.buffer = BLZ_Code(code, size, &new_len, BLZ_NORMAL); + set->exefsSections.code.size = new_len; + free(code); + } + else { + set->exefsSections.code.size = size; + set->exefsSections.code.buffer = code; + } + + /* Setting code_segment data and freeing original buffers */ + set->codeDetails.textAddress = text.address; + set->codeDetails.textMaxPages = text.maxPageNum; + set->codeDetails.textSize = text.size; + if (text.size) free(text.data); + + set->codeDetails.roAddress = rodata.address; + set->codeDetails.roMaxPages = rodata.maxPageNum; + set->codeDetails.roSize = rodata.size; + if (rodata.size) free(rodata.data); + + set->codeDetails.rwAddress = rwdata.address; + set->codeDetails.rwMaxPages = rwdata.maxPageNum; + set->codeDetails.rwSize = rwdata.size; + if (rwdata.size) free(rwdata.data); + + if (set->rsfSet->SystemControlInfo.StackSize) + set->codeDetails.stackSize = strtoul(set->rsfSet->SystemControlInfo.StackSize, NULL, 0); + else { + fprintf(stderr, "[CODE ERROR] RSF Parameter Not Found: \"SystemControlInfo/StackSize\"\n"); + return 1; + } + + /* Return */ + return 0; +} + +void PrintElfContext(elf_context *elf) +{ + printf("[ELF] Program Table Data\n"); + printf(" Offset: 0x%x\n", elf->programTableOffset); + printf(" Size: 0x%x\n", elf->programTableEntrySize); + printf(" Count: 0x%x\n", elf->programTableEntryCount); + printf("[ELF] Section Table Data\n"); + printf(" Offset: 0x%x\n", elf->sectionTableOffset); + printf(" Size: 0x%x\n", elf->sectionTableEntrySize); + printf(" Count: 0x%x\n", elf->sectionTableEntryCount); + printf(" Label index: 0x%x\n", elf->sectionHeaderNameEntryIndex); + for (int i = 0; i < elf->activeSegments; i++) { + printf(" Segment [%d][%s]\n", i, elf->segments[i].name); + printf(" > Size : 0x%x\n", elf->segments[i].header->sizeInFile); + printf(" > Address : 0x%x\n", elf->segments[i].vAddr); + printf(" > Flags: 0x%x\n", elf->segments[i].header->flags); + printf(" > Type: 0x%x\n", elf->segments[i].header->type); + printf(" > Sections : %d\n", elf->segments[i].sectionNum); + for (int j = 0; j < elf->segments[i].sectionNum; j++) + printf(" > Section [%d][%s][0x%x][0x%x]\n", j, elf->segments[i].sections[j].name, elf->segments[i].sections[j].flags, elf->segments[i].sections[j].type); + + /* + char outpath[100]; + memset(&outpath,0,100); + sprintf(outpath,"%s.bin",elf->sections[i].name); + chdir("elfsections"); + FILE *tmp = fopen(outpath,"wb"); + WriteBuffer(elf->sections[i].ptr,elf->sections[i].size,0,tmp); + fclose(tmp); + chdir(".."); + */ + } + +} + +int BuildExeFsCode(ncch_settings *set) +{ + int result = 0; + if (set->options.IsCfa) + return result; + + if (set->componentFilePtrs.plainregion) // Import PlainRegion from file + if ((result = ImportPlainRegionFromFile(set))) return result; + if (!set->options.IsBuildingCodeSection) // Import ExeFs Code from file and return + return ImportExeFsCodeBinaryFromFile(set); + + /* Import ELF */ + u8 *elfFile = malloc(set->componentFilePtrs.elfSize); + if (!elfFile) { + fprintf(stderr, "[CODE ERROR] Not enough memory\n"); + return MEM_ERROR; + } + ReadFile64(elfFile, set->componentFilePtrs.elfSize, 0, set->componentFilePtrs.elf); + + /* Create ELF Context */ + elf_context *elf = calloc(1, sizeof(elf_context)); + if (!elf) { + fprintf(stderr, "[CODE ERROR] Not enough memory\n"); + free(elfFile); + return MEM_ERROR; + } + + if ((result = GetElfContext(elf, elfFile))) goto finish; + + /* Setting Page Size */ + elf->pageSize = GetPageSize(set); + + if (!set->componentFilePtrs.plainregion) + if ((result = ImportPlainRegionFromElf(elf, set))) goto finish; + + if (set->options.verbose) + PrintElfContext(elf); + + if ((result = CreateExeFsCode(elf, set))) goto finish; + if ((result = GetBSSFromElf(elf, set))) goto finish; + +finish: + switch (result) { + case (0) : + break; + case (NOT_ELF_FILE) : + fprintf(stderr, "[CODE ERROR] Not ELF File\n"); + break; + case (NOT_CTR_ARM_ELF) : + fprintf(stderr, "[CODE ERROR] Not CTR ARM ELF\n"); + break; + case (NON_EXECUTABLE_ELF) : + fprintf(stderr, "[CODE ERROR] Not Executeable ELF\n"); + break; + case (NOT_FIND_TEXT_SEGMENT) : + fprintf(stderr, "[CODE ERROR] Failed to retrieve text sections from ELF\n"); + break; + case (NOT_FIND_DATA_SEGMENT) : + fprintf(stderr, "[CODE ERROR] Failed to retrieve data sections from ELF\n"); + break; + default: + fprintf(stderr, "[CODE ERROR] Failed to process ELF file (%d)\n", result); + } + + FreeElfContext(elf); + free(elfFile); + free(elf); + return result; +} diff --git a/makerom/code.h b/makerom/code.h new file mode 100644 index 00000000..fdad0498 --- /dev/null +++ b/makerom/code.h @@ -0,0 +1,3 @@ +#pragma once + +int BuildExeFsCode(ncch_settings *ncchset); \ No newline at end of file diff --git a/makerom/elf.c b/makerom/elf.c index aae35399..6aad3ef1 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -1,430 +1,149 @@ #include "lib.h" -#include "ncch_build.h" -#include "exheader_read.h" -#include "elf_hdr.h" #include "elf.h" -#include "blz.h" -const char *SDK_PLAINREGION_SEGMENT_NAME = ".module_id"; - -int ImportPlainRegionFromFile(ncch_settings *set); -int ImportExeFsCodeBinaryFromFile(ncch_settings *set); - -u32 GetPageSize(ncch_settings *set); -u32 SizeToPage(u32 memorySize, elf_context *elf); - -int GetBSSFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set); -int ImportPlainRegionFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set); -int CreateExeFsCode(elf_context *elf, u8 *elfFile, ncch_settings *set); -int CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u8 *elfFile, u64 segment_flags); +static const u32 ELF_MAGIC = 0x7f454c46; + +typedef enum elf_bit_format_types +{ + elf_32_bit = 1, + elf_64_bit = 2, +} elf_bit_format_types; + +typedef enum elf_endianness +{ + elf_little_endian = 1, + elf_big_endian = 2, +} elf_endianness; + +typedef enum elf_type +{ + elf_relocatable = 1, + elf_executeable = 2, + elf_shared = 3, + elf_core = 4, +} elf_type; + +typedef enum elf_target_architecture +{ + elf_arm = 0x28, +} elf_target_architecture; + +typedef struct elf_hdr +{ + u8 magic[4]; + u8 bitFormat; + u8 endianness; + u8 elfVersion; + u8 os; + u8 padding0[8]; + u8 type[2]; + u8 targetArchitecture[2]; + u8 version[4]; + u8 entryPoint[4]; + u8 programHeaderTableOffset[4]; + u8 sectionHeaderTableOffset[4]; + u8 flags[4]; + u8 headerSize[2]; + u8 programHeaderEntrySize[2]; + u8 programHeaderEntryCount[2]; + u8 sectionTableEntrySize[2]; + u8 sectionHeaderEntryCount[2]; + u8 sectionHeaderNameEntryIndex[2]; +} elf_hdr; + +/* taken from elf specs, will not follow global style */ + +/* Section header. */ +typedef struct elf_shdr +{ + u8 name[4]; /* Section name (string tbl index) */ + u8 type[4]; /* Section type */ + u8 flags[4]; /* Section flags */ + u8 addr[4]; /* Section virtual addr at execution */ + u8 offset[4]; /* Section file offset */ + u8 size[4]; /* Section size in bytes */ + u8 link[4]; /* Link to another section */ + u8 info[4]; /* Additional section information */ + u8 addralign[4]; /* Section alignment */ + u8 entsize[4]; /* Entry size if section holds table */ +} elf_shdr; + +/* Program segment header. */ +typedef struct elf_phdr +{ + u8 type[4]; /* Segment type */ + u8 offset[4]; /* Segment file offset */ + u8 vaddr[4]; /* Segment virtual address */ + u8 paddr[4]; /* Segment physical address */ + u8 filesz[4]; /* Segment size in file */ + u8 memsz[4]; /* Segment size in memory */ + u8 flags[4]; /* Segment flags */ + u8 align[4]; /* Segment alignment */ +} elf_phdr; // ELF Functions -int GetElfContext(elf_context *elf, u8 *elfFile); -int GetElfSectionEntries(elf_context *elf, u8 *elfFile); -int GetElfProgramEntries(elf_context *elf, u8 *elfFile); -void PrintElfContext(elf_context *elf, u8 *elfFile); -int ReadElfHdr(elf_context *elf, u8 *elfFile); +int GetElfSectionEntries(elf_context *elf); +int GetElfProgramEntries(elf_context *elf); +void PrintElfContext(elf_context *elf); +int ReadElfHdr(elf_context *elf); -int CreateElfSegments(elf_context *elf, u8 *elfFile); +int CreateElfSegments(elf_context *elf); bool IsIgnoreSection(elf_section_entry info); -/* ELF Section Entry Functions */ -u8* GetELFSectionHeader(u16 index, elf_context *elf, u8 *elfFile); -u8* GetELFSectionEntry(u16 index, elf_context *elf, u8 *elfFile); -char* GetELFSectionEntryName(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFSectionEntryType(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFSectionEntryFlags(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFSectionEntryAddress(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFSectionEntryFileOffset(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFSectionEntrySize(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFSectionEntryAlignment(u16 index, elf_context *elf, u8 *elfFile); - -u16 GetElfSectionIndexFromName(char *name, elf_context *elf, u8 *elfFile); - -bool IsBss(elf_section_entry *section); -bool IsData(elf_section_entry *section); -bool IsRoData(elf_section_entry *section); -bool IsText(elf_section_entry *section); - -/* ELF Program Entry Functions */ -u8* GetELFProgramHeader(u16 index, elf_context *elf, u8 *elfFile); -u8* GetELFProgramEntry(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFProgramEntryType(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFProgramEntryFlags(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFProgramEntryFileSize(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFProgramEntryFileOffset(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFProgramEntryMemorySize(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFProgramEntryVAddress(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFProgramEntryPAddress(u16 index, elf_context *elf, u8 *elfFile); -u64 GetELFProgramEntryAlignment(u16 index, elf_context *elf, u8 *elfFile); - - -int BuildExeFsCode(ncch_settings *set) -{ - int result = 0; - if(set->options.IsCfa) - return result; - if(set->componentFilePtrs.plainregion){ // Import PlainRegion from file - result = ImportPlainRegionFromFile(set); - if(result) return result; - } - if(!set->options.IsBuildingCodeSection){ // Import ExeFs Code from file and return - result = ImportExeFsCodeBinaryFromFile(set); - return result; - } - - /* Import ELF */ - u8 *elfFile = malloc(set->componentFilePtrs.elfSize); - if(!elfFile) { - fprintf(stderr,"[ELF ERROR] Not enough memory\n"); - return MEM_ERROR; - } - ReadFile64(elfFile,set->componentFilePtrs.elfSize,0,set->componentFilePtrs.elf); - - /* Create ELF Context */ - elf_context *elf = calloc(1,sizeof(elf_context)); - if(!elf) { - fprintf(stderr,"[ELF ERROR] Not enough memory\n"); - free(elfFile); - return MEM_ERROR; - } - - result = GetElfContext(elf,elfFile); - if(result) goto finish; - - /* Setting Page Size */ - elf->pageSize = GetPageSize(set); - - if(!set->componentFilePtrs.plainregion){ - result = ImportPlainRegionFromElf(elf,elfFile,set); - if(result) goto finish; - } - - if(set->options.verbose) - PrintElfContext(elf,elfFile); - - result = CreateExeFsCode(elf,elfFile,set); - if(result) goto finish; - - result = GetBSSFromElf(elf,elfFile,set); - if(result) goto finish; - -finish: - switch (result) { - case (0) : - break; - case (NOT_ELF_FILE) : - fprintf(stderr, "[ELF ERROR] Not ELF File\n"); - break; - case (NOT_ARM_ELF) : - fprintf(stderr, "[ELF ERROR] Not ARM ELF\n"); - break; - case (NON_EXECUTABLE_ELF) : - fprintf(stderr, "[ELF ERROR] Not Executeable ELF\n"); - break; - case (NOT_FIND_TEXT_SEGMENT) : - fprintf(stderr, "[ELF ERROR] Failed to retrieve text sections from ELF\n"); - break; - case (NOT_FIND_DATA_SEGMENT) : - fprintf(stderr, "[ELF ERROR] Failed to retrieve data sections from ELF\n"); - break; - default: - fprintf(stderr, "[ELF ERROR] Failed to process ELF file (%d)\n", result); - } - for(int i = 0; i < elf->activeSegments; i++) - free(elf->segments[i].sections); - free(elfFile); - free(elf->sections); - free(elf->programHeaders); - free(elf->segments); - free(elf); - return result; -} - -int ImportPlainRegionFromFile(ncch_settings *set) -{ - set->sections.plainRegion.size = align(set->componentFilePtrs.plainregionSize,set->options.blockSize); - set->sections.plainRegion.buffer = malloc(set->sections.plainRegion.size); - if(!set->sections.plainRegion.buffer) {fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR;} - ReadFile64(set->sections.plainRegion.buffer,set->componentFilePtrs.plainregionSize,0,set->componentFilePtrs.plainregion); - return 0; -} - -int ImportExeFsCodeBinaryFromFile(ncch_settings *set) -{ - u32 size = set->componentFilePtrs.codeSize; - u8 *buffer = malloc(size); - if(!buffer) { - fprintf(stderr,"[ELF ERROR] Not enough memory\n"); - return MEM_ERROR; - } - ReadFile64(buffer,size,0,set->componentFilePtrs.code); - - set->exefsSections.code.size = set->componentFilePtrs.codeSize; - set->exefsSections.code.buffer = malloc(set->exefsSections.code.size); - if(!set->exefsSections.code.buffer) {fprintf(stderr,"[ELF ERROR] Not enough memory\n"); return MEM_ERROR;} - ReadFile64(set->exefsSections.code.buffer,set->exefsSections.code.size,0,set->componentFilePtrs.code); - if(set->options.CompressCode){ - u32 new_len; - set->exefsSections.code.buffer = BLZ_Code(buffer,size,&new_len,BLZ_NORMAL); - set->exefsSections.code.size = new_len; - free(buffer); - } - else{ - set->exefsSections.code.size = size; - set->exefsSections.code.buffer = buffer; - } - - size = set->componentFilePtrs.exhdrSize; - if(size < sizeof(extended_hdr)){ - fprintf(stderr,"[ELF ERROR] Exheader code info template is too small\n"); - return FAILED_TO_IMPORT_FILE; - } - extended_hdr *exhdr = malloc(size); - if(!exhdr) { - fprintf(stderr,"[ELF ERROR] Not enough memory\n"); - return MEM_ERROR; - } - ReadFile64(exhdr,size,0,set->componentFilePtrs.exhdr); - - /* Setting code_segment data */ - set->codeDetails.textAddress = u8_to_u32(exhdr->codeSetInfo.text.address,LE); - set->codeDetails.textMaxPages = u8_to_u32(exhdr->codeSetInfo.text.numMaxPages,LE); - set->codeDetails.textSize = u8_to_u32(exhdr->codeSetInfo.text.codeSize,LE); - - set->codeDetails.roAddress = u8_to_u32(exhdr->codeSetInfo.rodata.address,LE); - set->codeDetails.roMaxPages = u8_to_u32(exhdr->codeSetInfo.rodata.numMaxPages,LE); - set->codeDetails.roSize = u8_to_u32(exhdr->codeSetInfo.rodata.codeSize,LE); - - set->codeDetails.rwAddress = u8_to_u32(exhdr->codeSetInfo.data.address,LE); - set->codeDetails.rwMaxPages = u8_to_u32(exhdr->codeSetInfo.data.numMaxPages,LE); - set->codeDetails.rwSize = u8_to_u32(exhdr->codeSetInfo.data.codeSize,LE); - - set->codeDetails.bssSize = u8_to_u32(exhdr->codeSetInfo.bssSize,LE); - - free(exhdr); - - return 0; -} - -u32 GetPageSize(ncch_settings *set) -{ - if(set->rsfSet->Option.PageSize) - return strtoul(set->rsfSet->Option.PageSize,NULL,10); - return 0x1000; -} - -u32 SizeToPage(u32 memorySize, elf_context *elf) -{ - return align(memorySize,elf->pageSize)/elf->pageSize; -} - +// ELF Functions -int GetBSSFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set) +int GetElfContext(elf_context *elf, const u8 *elfFile) { - set->codeDetails.bssSize = 0; + elf->file = elfFile; - for(int i = 0; i < elf->sectionTableEntryCount; i++){ - if(IsBss(&elf->sections[i])) - set->codeDetails.bssSize = elf->sections[i].size; - } - - return 0; -} + int result; -int ImportPlainRegionFromElf(elf_context *elf, u8 *elfFile, ncch_settings *set) // Doesn't work same as N makerom -{ - u64 size = 0; - u64 offset = 0; - for (u16 i = 0; i < elf->activeSegments; i++) { - if (strcmp(elf->segments[i].name, SDK_PLAINREGION_SEGMENT_NAME) == 0) { - size = elf->segments[i].header->sizeInFile; - offset = elf->segments[i].header->offsetInFile; - break; - } - } - - - if (size > 0) { - /* Creating Output Buffer */ - set->sections.plainRegion.size = align(size, set->options.blockSize); - set->sections.plainRegion.buffer = malloc(set->sections.plainRegion.size); - if (!set->sections.plainRegion.buffer) { fprintf(stderr, "[ELF ERROR] Not enough memory\n"); return MEM_ERROR; } - memset(set->sections.plainRegion.buffer, 0, set->sections.plainRegion.size); - - /* Copy Plain Region */ - memcpy(set->sections.plainRegion.buffer, elfFile+offset, size); - } + if((result = ReadElfHdr(elf))) return result; + if((result = GetElfSectionEntries(elf))) return result; + if((result = GetElfProgramEntries(elf))) return result; + if((result = CreateElfSegments(elf))) return result; return 0; } -int CreateExeFsCode(elf_context *elf, u8 *elfFile, ncch_settings *set) +void FreeElfContext(elf_context *elf) { - /* Getting Code Segments */ - code_segment text; - memset(&text,0,sizeof(code_segment)); - code_segment rodata; - memset(&rodata,0,sizeof(code_segment)); - code_segment rwdata; - memset(&rwdata,0,sizeof(code_segment)); - - int result = CreateCodeSegmentFromElf(&text,elf,elfFile,(PF_R|PF_X)); - if(result) return result; - result = CreateCodeSegmentFromElf(&rodata,elf,elfFile,(PF_R)); - if(result) return result; - result = CreateCodeSegmentFromElf(&rwdata,elf,elfFile,(PF_R | PF_W)); - if(result) return result; - - /* Checking the existence of essential ELF Segments */ - if(!text.size) return NOT_FIND_TEXT_SEGMENT; - if(!rwdata.size) return NOT_FIND_DATA_SEGMENT; - - /* Allocating Buffer for ExeFs Code */ - u32 size = (text.maxPageNum + rodata.maxPageNum + rwdata.maxPageNum)*elf->pageSize; - u8 *code = calloc(1,size); - - /* Writing Code into Buffer */ - u8 *textPos = (code + 0); - u8 *rodataPos = (code + text.maxPageNum*elf->pageSize); - u8 *rwdataPos = (code + (text.maxPageNum + rodata.maxPageNum)*elf->pageSize); - if(text.size) memcpy(textPos,text.data,text.size); - if(rodata.size) memcpy(rodataPos,rodata.data,rodata.size); - if(rwdata.size) memcpy(rwdataPos,rwdata.data,rwdata.size); - - - /* Compressing If needed */ - if(set->options.CompressCode){ - u32 new_len; - set->exefsSections.code.buffer = BLZ_Code(code,size,&new_len,BLZ_NORMAL); - set->exefsSections.code.size = new_len; - free(code); - } - else{ - set->exefsSections.code.size = size; - set->exefsSections.code.buffer = code; - } - - /* Setting code_segment data and freeing original buffers */ - set->codeDetails.textAddress = text.address; - set->codeDetails.textMaxPages = text.maxPageNum; - set->codeDetails.textSize = text.size; - if(text.size) free(text.data); - - set->codeDetails.roAddress = rodata.address; - set->codeDetails.roMaxPages = rodata.maxPageNum; - set->codeDetails.roSize = rodata.size; - if(rodata.size) free(rodata.data); - - set->codeDetails.rwAddress = rwdata.address; - set->codeDetails.rwMaxPages = rwdata.maxPageNum; - set->codeDetails.rwSize = rwdata.size; - if(rwdata.size) free(rwdata.data); - - /* Return */ - return 0; + for (int i = 0; i < elf->activeSegments; i++) + free(elf->segments[i].sections); + free(elf->sections); + free(elf->programHeaders); + free(elf->segments); } -int CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u8 *elfFile, u64 segment_flags) +int ReadElfHdr(elf_context *elf) { - memset(out, 0, sizeof(code_segment)); - - u16 seg_num = 0; - elf_segment **seg = calloc(elf->activeSegments, sizeof(elf_segment*)); - - for (u16 i = 0; i < elf->activeSegments; i++) { - // Skip SDK ELF plain region - if (strcmp(elf->segments[i].name, SDK_PLAINREGION_SEGMENT_NAME) == 0) - continue; - - //printf("SegName: %s (flags: %x)\n", elf->segments[i].name, elf->segments[i].header->flags); - if ((elf->segments[i].header->flags & ~PF_CTRSDK) == segment_flags) { - if (seg_num == 0) { - seg[seg_num] = &elf->segments[i]; - seg_num++; - } - else if (elf->segments[i].vAddr == (u32)align(seg[seg_num - 1]->vAddr, seg[seg_num-1]->header->alignment)) { - seg[seg_num] = &elf->segments[i]; - seg_num++; - } - } - } + const elf_hdr *hdr = (const elf_hdr*)elf->file; - /* Return if there are no applicable segment */ - if (seg_num == 0) - return 0; + if (u8_to_u32(hdr->magic, BE) != ELF_MAGIC) + return NOT_ELF_FILE; + if (hdr->bitFormat != elf_32_bit) + return NOT_CTR_ARM_ELF; + if (hdr->endianness != elf_little_endian) + return NOT_CTR_ARM_ELF; + if (u8_to_u16(hdr->targetArchitecture, LE) != elf_arm) + return NOT_CTR_ARM_ELF; + if (u8_to_u16(hdr->type, LE) != elf_executeable) + return NON_EXECUTABLE_ELF; - /* Getting Segment Size/Settings */ - u32 vAddr = 0; - u32 memorySize = 0; - for (u16 i = 0; i < seg_num; i++) { - if (i == 0) { - vAddr = seg[i]->vAddr; - } - else { // Add rounded size from previous segment - u32 padding = seg[i]->vAddr - (vAddr + memorySize); - memorySize += padding; - } + elf->programTableOffset = u8_to_u32(hdr->programHeaderTableOffset, LE); + elf->programTableEntrySize = u8_to_u16(hdr->programHeaderEntrySize, LE); + elf->programTableEntryCount = u8_to_u16(hdr->programHeaderEntryCount, LE); - memorySize += seg[i]->header->sizeInMemory; + elf->sectionTableOffset = u8_to_u32(hdr->sectionHeaderTableOffset, LE); + elf->sectionTableEntrySize = u8_to_u16(hdr->sectionTableEntrySize, LE); + elf->sectionTableEntryCount = u8_to_u16(hdr->sectionHeaderEntryCount, LE); - if (IsBss(&seg[i]->sections[seg[i]->sectionNum - 1])) - memorySize -= seg[i]->sections[seg[i]->sectionNum - 1].size; - } + elf->sectionHeaderNameEntryIndex = u8_to_u16(hdr->sectionHeaderNameEntryIndex, LE); - // For Check -#ifdef DEBUG - printf("Address: 0x%x\n", vAddr); - printf("Size: 0x%x\n", memorySize); -#endif - - out->address = vAddr; - out->size = memorySize; - out->maxPageNum = SizeToPage(memorySize, elf); - out->data = malloc(memorySize); - - /* Writing Segment to Buffer */ - for (int i = 0; i < seg_num; i++) { - - for (int j = 0; j < seg[i]->sectionNum; j++) { - elf_section_entry *section = &seg[i]->sections[j]; - if (!IsBss(section)) { - u8 *pos = (out->data + (section->address - seg[i]->vAddr)); - memcpy(pos, section->ptr, section->size); - //size += section->size; - } - } - } - - free(seg); return 0; } -// ELF Functions - -int GetElfContext(elf_context *elf, u8 *elfFile) -{ - if(u8_to_u32(elfFile,BE) != ELF_MAGIC) return NOT_ELF_FILE; - - elf->Is64bit = (elfFile[4] == elf_64_bit); - elf->IsLittleEndian = (elfFile[5] == elf_little_endian); - - int result = ReadElfHdr(elf,elfFile); - if(result) return result; - - result = GetElfSectionEntries(elf,elfFile); - if(result) return result; - - result = GetElfProgramEntries(elf,elfFile); - if(result) return result; - - result = CreateElfSegments(elf,elfFile); - if(result) return result; - - return 0; -} - -int GetElfSectionEntries(elf_context *elf, u8 *elfFile) +int GetElfSectionEntries(elf_context *elf) { elf->sections = calloc(elf->sectionTableEntryCount,sizeof(elf_section_entry)); if(!elf->sections) { @@ -432,20 +151,23 @@ int GetElfSectionEntries(elf_context *elf, u8 *elfFile) return MEM_ERROR; } + const elf_shdr *shdr = (const elf_shdr *)(elf->file + elf->sectionTableOffset); + const char *nameTable = (const char*)(elf->file + u8_to_u32(shdr[elf->sectionHeaderNameEntryIndex].offset, LE)); + for(int i = 0; i < elf->sectionTableEntryCount; i++){ - elf->sections[i].name = GetELFSectionEntryName(i,elf,elfFile); - elf->sections[i].type = GetELFSectionEntryType(i,elf,elfFile); - elf->sections[i].flags = GetELFSectionEntryFlags(i,elf,elfFile); - elf->sections[i].ptr = GetELFSectionEntry(i,elf,elfFile); - elf->sections[i].offsetInFile = GetELFSectionEntryFileOffset(i,elf,elfFile); - elf->sections[i].size = GetELFSectionEntrySize(i,elf,elfFile); - elf->sections[i].address = GetELFSectionEntryAddress(i,elf,elfFile); - elf->sections[i].alignment = GetELFSectionEntryAlignment(i,elf,elfFile); + elf->sections[i].name = nameTable + u8_to_u32(shdr[i].name, LE); + elf->sections[i].type = u8_to_u32(shdr[i].type, LE); + elf->sections[i].flags = u8_to_u32(shdr[i].flags, LE); + elf->sections[i].offsetInFile = u8_to_u32(shdr[i].offset, LE); + elf->sections[i].size = u8_to_u32(shdr[i].size, LE); + elf->sections[i].ptr = elf->file + elf->sections[i].offsetInFile; + elf->sections[i].address = u8_to_u32(shdr[i].addr, LE); + elf->sections[i].alignment = u8_to_u32(shdr[i].addralign, LE); } return 0; } -int GetElfProgramEntries(elf_context *elf, u8 *elfFile) +int GetElfProgramEntries(elf_context *elf) { elf->programHeaders = calloc(elf->programTableEntryCount,sizeof(elf_program_entry)); if(!elf->programHeaders) { @@ -453,240 +175,25 @@ int GetElfProgramEntries(elf_context *elf, u8 *elfFile) return MEM_ERROR; } - for(int i = 0; i < elf->programTableEntryCount; i++){ - elf->programHeaders[i].type = GetELFProgramEntryType(i,elf,elfFile); - elf->programHeaders[i].flags = GetELFProgramEntryFlags(i,elf,elfFile); - elf->programHeaders[i].ptr = GetELFProgramEntry(i,elf,elfFile); - elf->programHeaders[i].offsetInFile = GetELFProgramEntryFileOffset(i,elf,elfFile); - elf->programHeaders[i].sizeInFile = GetELFProgramEntryFileSize(i,elf,elfFile); - elf->programHeaders[i].physicalAddress = GetELFProgramEntryPAddress(i,elf,elfFile); - elf->programHeaders[i].virtualAddress = GetELFProgramEntryVAddress(i,elf,elfFile); - elf->programHeaders[i].sizeInMemory = GetELFProgramEntryMemorySize(i,elf,elfFile); - elf->programHeaders[i].alignment = GetELFProgramEntryAlignment(i,elf,elfFile); - } + const elf_phdr *phdr = (const elf_phdr*)(elf->file + elf->programTableOffset); - return 0; -} - -void PrintElfContext(elf_context *elf, u8 *elfFile) -{ - printf("[ELF] Basic Details\n"); - printf(" Class: %s\n",elf->Is64bit ? "64-bit" : "32-bit"); - printf(" Data: %s\n",elf->IsLittleEndian ? "Little Endian" : "Big Endian"); - printf("[ELF] Program Table Data\n"); - printf(" Offset: 0x%"PRIx64"\n",elf->programTableOffset); - printf(" Size: 0x%x\n",elf->programTableEntrySize); - printf(" Count: 0x%x\n",elf->programTableEntryCount); - printf("[ELF] Section Table Data\n"); - printf(" Offset: 0x%"PRIx64"\n",elf->sectionTableOffset); - printf(" Size: 0x%x\n",elf->sectionTableEntrySize); - printf(" Count: 0x%x\n",elf->sectionTableEntryCount); - printf(" Label index: 0x%x\n",elf->sectionHeaderNameEntryIndex); - for(int i = 0; i < elf->activeSegments; i++){ - printf(" Segment [%d][%s]\n",i,elf->segments[i].name); - printf(" > Size : 0x%"PRIx64"\n",elf->segments[i].header->sizeInFile); - printf(" > Address : 0x%"PRIx64"\n",elf->segments[i].vAddr); - printf(" > Flags: 0x%"PRIx64"\n", elf->segments[i].header->flags); - printf(" > Type: 0x%"PRIx64"\n", elf->segments[i].header->type); - printf(" > Sections : %d\n",elf->segments[i].sectionNum); - for(int j = 0; j < elf->segments[i].sectionNum; j++) - printf(" > Section [%d][%s][0x%"PRIx64"][0x%"PRIx64"]\n",j,elf->segments[i].sections[j].name, elf->segments[i].sections[j].flags, elf->segments[i].sections[j].type); - - /* - char outpath[100]; - memset(&outpath,0,100); - sprintf(outpath,"%s.bin",elf->sections[i].name); - chdir("elfsections"); - FILE *tmp = fopen(outpath,"wb"); - WriteBuffer(elf->sections[i].ptr,elf->sections[i].size,0,tmp); - fclose(tmp); - chdir(".."); - */ - } - -} - -int ReadElfHdr(elf_context *elf, u8 *elfFile) -{ - if(elf->Is64bit){ - elf_64_hdr *hdr = (elf_64_hdr*)elfFile; - - u16 Architecture = u8_to_u16(hdr->targetArchitecture,elf->IsLittleEndian); - u16 Type = u8_to_u16(hdr->type,elf->IsLittleEndian); - if(Architecture != elf_arm) return NOT_ARM_ELF; - if(Type != elf_executeable) return NON_EXECUTABLE_ELF; - - elf->programTableOffset = u8_to_u64(hdr->programHeaderTableOffset,elf->IsLittleEndian); - elf->programTableEntrySize = u8_to_u16(hdr->programHeaderEntrySize,elf->IsLittleEndian); - elf->programTableEntryCount = u8_to_u16(hdr->programHeaderEntryCount,elf->IsLittleEndian); - - elf->sectionTableOffset = u8_to_u64(hdr->sectionHeaderTableOffset,elf->IsLittleEndian); - elf->sectionTableEntrySize = u8_to_u16(hdr->sectionTableEntrySize,elf->IsLittleEndian); - elf->sectionTableEntryCount = u8_to_u16(hdr->sectionHeaderEntryCount,elf->IsLittleEndian); - - elf->sectionHeaderNameEntryIndex = u8_to_u16(hdr->sectionHeaderNameEntryIndex,elf->IsLittleEndian); + for(int i = 0; i < elf->programTableEntryCount; i++){ + elf->programHeaders[i].type = u8_to_u32(phdr[i].type, LE); + elf->programHeaders[i].flags = u8_to_u32(phdr[i].flags, LE); + elf->programHeaders[i].offsetInFile = u8_to_u32(phdr[i].offset, LE); + elf->programHeaders[i].sizeInFile = u8_to_u32(phdr[i].filesz, LE); + elf->programHeaders[i].ptr = elf->file + elf->programHeaders[i].offsetInFile; + elf->programHeaders[i].physicalAddress = u8_to_u32(phdr[i].paddr, LE); + elf->programHeaders[i].virtualAddress = u8_to_u32(phdr[i].vaddr, LE); + elf->programHeaders[i].sizeInMemory = u8_to_u32(phdr[i].memsz, LE); + elf->programHeaders[i].alignment = u8_to_u32(phdr[i].align, LE); } - else{ - elf_32_hdr *hdr = (elf_32_hdr*)elfFile; - - u16 Architecture = u8_to_u16(hdr->targetArchitecture,elf->IsLittleEndian); - u16 Type = u8_to_u16(hdr->type,elf->IsLittleEndian); - if(Architecture != elf_arm) return NOT_ARM_ELF; - if(Type != elf_executeable) return NON_EXECUTABLE_ELF; - - elf->programTableOffset = u8_to_u32(hdr->programHeaderTableOffset,elf->IsLittleEndian); - elf->programTableEntrySize = u8_to_u16(hdr->programHeaderEntrySize,elf->IsLittleEndian); - elf->programTableEntryCount = u8_to_u16(hdr->programHeaderEntryCount,elf->IsLittleEndian); - - elf->sectionTableOffset = u8_to_u32(hdr->sectionHeaderTableOffset,elf->IsLittleEndian); - elf->sectionTableEntrySize = u8_to_u16(hdr->sectionTableEntrySize,elf->IsLittleEndian); - elf->sectionTableEntryCount = u8_to_u16(hdr->sectionHeaderEntryCount,elf->IsLittleEndian); - elf->sectionHeaderNameEntryIndex = u8_to_u16(hdr->sectionHeaderNameEntryIndex,elf->IsLittleEndian); - } return 0; } /* Section Hdr Functions */ -u8* GetELFSectionHeader(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->sectionTableEntryCount) return NULL; - - return (elfFile + elf->sectionTableOffset + elf->sectionTableEntrySize*index); -} - -u8* GetELFSectionEntry(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->sectionTableEntryCount) return NULL; - - return (u8*) (elfFile + GetELFSectionEntryFileOffset(index,elf,elfFile)); -} - -char* GetELFSectionEntryName(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->sectionTableEntryCount) return 0; - - u64 NameIndex = 0; - if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); - NameIndex = u8_to_u64(shdr->sh_name,elf->IsLittleEndian); - } - else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); - NameIndex = u8_to_u32(shdr->sh_name,elf->IsLittleEndian); - } - - u8 *NameTable = GetELFSectionEntry(elf->sectionHeaderNameEntryIndex,elf,elfFile); - - return (char*)(NameTable+NameIndex); -} - -u64 GetELFSectionEntryType(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->sectionTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); - return u8_to_u64(shdr->sh_type,elf->IsLittleEndian); - } - else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); - return u8_to_u32(shdr->sh_type,elf->IsLittleEndian); - } - - return 0; -} - -u64 GetELFSectionEntryFlags(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->sectionTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); - return u8_to_u64(shdr->sh_flags,elf->IsLittleEndian); - } - else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); - return u8_to_u32(shdr->sh_flags,elf->IsLittleEndian); - } - - return 0; -} - -u64 GetELFSectionEntryAddress(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->sectionTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); - return u8_to_u64(shdr->sh_addr,elf->IsLittleEndian); - } - else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); - return u8_to_u32(shdr->sh_addr,elf->IsLittleEndian); - } - - return 0; -} - -u64 GetELFSectionEntryFileOffset(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->sectionTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); - return u8_to_u64(shdr->sh_offset,elf->IsLittleEndian); - } - else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); - return u8_to_u32(shdr->sh_offset,elf->IsLittleEndian); - } - - return 0; -} - -u64 GetELFSectionEntrySize(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->sectionTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); - return u8_to_u64(shdr->sh_size,elf->IsLittleEndian); - } - else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); - return u8_to_u32(shdr->sh_size,elf->IsLittleEndian); - } - - return 0; -} - -u64 GetELFSectionEntryAlignment(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->sectionTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_shdr *shdr = (elf_64_shdr*)GetELFSectionHeader(index,elf,elfFile); - return u8_to_u64(shdr->sh_addralign,elf->IsLittleEndian); - } - else{ - elf_32_shdr *shdr = (elf_32_shdr*)GetELFSectionHeader(index,elf,elfFile); - return u8_to_u32(shdr->sh_addralign,elf->IsLittleEndian); - } - - return 0; -} - - -u16 GetElfSectionIndexFromName(char *name, elf_context *elf, u8 *elfFile) -{ - for(int i = 0; i < elf->sectionTableEntryCount; i++){ - if(strcmp(name,elf->sections[i].name) == 0) return i; - } - return 0; // Assuming 0 is always empty -} - bool IsBss(elf_section_entry *section) { return (section->type == SHT_NOBITS && section->flags == (SHF_WRITE | SHF_ALLOC)); @@ -707,153 +214,7 @@ bool IsText(elf_section_entry *section) return (section->type == SHT_PROGBITS && section->flags == (SHF_ALLOC | SHF_EXECINSTR)); } -/* ProgramHeader Functions */ - -u8* GetELFProgramHeader(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->programTableEntryCount) return NULL; - - return (elfFile + elf->programTableOffset + elf->programTableEntrySize*index); -} - -u8* GetELFProgramEntry(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->programTableEntryCount) return NULL; - - return (u8*) (elfFile + GetELFProgramEntryFileOffset(index,elf,elfFile)); - - return NULL; -} - -u64 GetELFProgramEntryType(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->programTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u64(phdr->p_type,elf->IsLittleEndian); - } - else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u32(phdr->p_type,elf->IsLittleEndian); - } - - return 0; -} - -u64 GetELFProgramEntryFlags(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->programTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u64(phdr->p_flags,elf->IsLittleEndian); - } - else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u32(phdr->p_flags,elf->IsLittleEndian); - } - - return 0; -} - -u64 GetELFProgramEntryFileSize(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->programTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u64(phdr->p_filesz,elf->IsLittleEndian); - } - else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u32(phdr->p_filesz,elf->IsLittleEndian); - } - - return 0; -} - -u64 GetELFProgramEntryFileOffset(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->programTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u64(phdr->p_offset,elf->IsLittleEndian); - } - else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u32(phdr->p_offset,elf->IsLittleEndian); - } - - return 0; -} - -u64 GetELFProgramEntryMemorySize(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->programTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u64(phdr->p_memsz,elf->IsLittleEndian); - } - else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u32(phdr->p_memsz,elf->IsLittleEndian); - } - - return 0; -} - -u64 GetELFProgramEntryVAddress(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->programTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u64(phdr->p_vaddr,elf->IsLittleEndian); - } - else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u32(phdr->p_vaddr,elf->IsLittleEndian); - } - - return 0; -} - -u64 GetELFProgramEntryPAddress(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->programTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u64(phdr->p_paddr,elf->IsLittleEndian); - } - else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u32(phdr->p_paddr,elf->IsLittleEndian); - } - - return 0; -} - - -u64 GetELFProgramEntryAlignment(u16 index, elf_context *elf, u8 *elfFile) -{ - if(index >= elf->programTableEntryCount) return 0; - - if(elf->Is64bit){ - elf_64_phdr *phdr = (elf_64_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u64(phdr->p_align,elf->IsLittleEndian); - } - else{ - elf_32_phdr *phdr = (elf_32_phdr*)GetELFProgramHeader(index,elf,elfFile); - return u8_to_u32(phdr->p_align,elf->IsLittleEndian); - } - - return 0; -} - +/* Program Segment Functions */ void InitSegment(elf_segment *segment) { memset(segment, 0, sizeof(elf_segment)); @@ -880,7 +241,12 @@ void AddSegmentSection(elf_segment *segment, elf_section_entry *section) segment->sectionNum++; } -int CreateElfSegments(elf_context *elf, u8 *elfFile) +bool IsIgnoreSection(elf_section_entry info) +{ + return !(info.flags & SHF_ALLOC);//(info.type != SHT_PROGBITS && info.type != SHT_NOBITS && info.type != SHT_INIT_ARRAY && info.type != SHT_FINI_ARRAY && info.type != SHT_ARM_EXIDX); +} + +int CreateElfSegments(elf_context *elf) { // Interate through Each Program Header elf->activeSegments = 0; @@ -945,9 +311,4 @@ int CreateElfSegments(elf_context *elf, u8 *elfFile) } return 0; -} - -bool IsIgnoreSection(elf_section_entry info) -{ - return (info.type != SHT_PROGBITS && info.type != SHT_NOBITS && info.type != SHT_INIT_ARRAY && info.type != SHT_FINI_ARRAY && info.type != SHT_ARM_EXIDX); -} +} \ No newline at end of file diff --git a/makerom/elf.h b/makerom/elf.h index a6b856af..c568a5b5 100644 --- a/makerom/elf.h +++ b/makerom/elf.h @@ -1,9 +1,9 @@ #pragma once -typedef enum +typedef enum elf_errors { NOT_ELF_FILE = -10, - NOT_ARM_ELF = -11, + NOT_CTR_ARM_ELF = -11, NON_EXECUTABLE_ELF = -12, ELF_SECTION_NOT_FOUND = -13, NOT_FIND_TEXT_SEGMENT = -14, @@ -13,35 +13,101 @@ typedef enum ELF_SEGMENTS_NOT_FOUND = -18, } elf_errors; -typedef struct +typedef enum elf_section_type { - char *name; - u64 type; - u64 flags; - u8 *ptr; - u64 offsetInFile; - u64 size; - u64 address; - u64 alignment; + SHT_NULL, + SHT_PROGBITS, + SHT_SYMTAB, + SHT_STRTAB, + SHT_RELA, + SHT_HASH, + SHT_DYNAMIC, + SHT_NOTE, + SHT_NOBITS, + SHT_REL, + SHT_SHLIB, + SHT_DYNSYM, + SHT_UNKNOWN12, + SHT_UNKNOWN13, + SHT_INIT_ARRAY, + SHT_FINI_ARRAY, + SHT_PREINIT_ARRAY, + SHT_GROUP, + SHT_SYMTAB_SHNDX, + SHT_NUM, + SHT_ARM_EXIDX = 0x70000001, + SHT_ARM_PREEMPTMAP, + SHT_ARM_ATTRIBUTES, + SHT_ARM_DEBUGOVERLAY, + SHT_ARM_OVERLAYSECTION +} elf_section_type; + +typedef enum elf_section_flag +{ + SHF_WRITE = 0x1, + SHF_ALLOC = 0x2, + SHF_EXECINSTR = 0x4, + SHF_MERGE = 0x10, + SHF_STRINGS = 0x20, + SHF_INFO_LINK = 0x40, + SHF_LINK_ORDER = 0x80, + SHF_OS_NONCONFORMING = 0x100, + SHF_GROUP = 0x200, + SHF_TLS = 0x400 +} elf_section_flag; + +typedef struct elf_section_entry +{ + const char *name; + u32 type; + u32 flags; + const u8 *ptr; + u32 offsetInFile; + u32 size; + u32 address; + u32 alignment; } elf_section_entry; -typedef struct +typedef enum elf_program_type +{ + PT_NULL, + PT_LOAD, + PT_DYNAMIC, + PT_INTERP, + PT_NOTE, + PT_SHLIB, + PT_PHDR, +} elf_program_type; + +typedef enum elf_program_flag { - u64 type; - u64 flags; - u8 *ptr; - u64 offsetInFile; - u64 sizeInFile; - u64 virtualAddress; - u64 physicalAddress; - u64 sizeInMemory; - u64 alignment; + PF_X = 0x1, + PF_W = 0x2, + PF_R = 0x4, + PF_CTRSDK = 0x80000000, + + PF_TEXT = (PF_R|PF_X), + PF_DATA = (PF_R|PF_W), + PF_RODATA = PF_R +} elf_program_flag; + +typedef struct elf_program_entry +{ + u32 type; + u32 flags; + const u8 *ptr; + u32 offsetInFile; + u32 sizeInFile; + u32 virtualAddress; + u32 physicalAddress; + u32 sizeInMemory; + u32 alignment; } elf_program_entry; -typedef struct +typedef struct elf_segment { - char *name; - u64 vAddr; + const char *name; + u32 vAddr; elf_program_entry *header; u32 sectionNum; @@ -49,25 +115,17 @@ typedef struct elf_section_entry *sections; } elf_segment; -typedef struct +typedef struct elf_context { - u32 address; - u32 size; - u32 maxPageNum; - u8 *data; -} code_segment; + const u8 *file; -typedef struct -{ u32 pageSize; - bool IsLittleEndian; - bool Is64bit; - u64 programTableOffset; + u32 programTableOffset; u16 programTableEntrySize; u16 programTableEntryCount; - u64 sectionTableOffset; + u32 sectionTableOffset; u16 sectionTableEntrySize; u16 sectionTableEntryCount; @@ -78,7 +136,12 @@ typedef struct u16 activeSegments; elf_segment *segments; - } elf_context; -int BuildExeFsCode(ncch_settings *ncchset); \ No newline at end of file +bool IsBss(elf_section_entry *section); +bool IsData(elf_section_entry *section); +bool IsRoData(elf_section_entry *section); +bool IsText(elf_section_entry *section); + +int GetElfContext(elf_context *elf, const u8 *elfFile); +void FreeElfContext(elf_context *elf); \ No newline at end of file diff --git a/makerom/elf_hdr.h b/makerom/elf_hdr.h deleted file mode 100644 index 4fb772d7..00000000 --- a/makerom/elf_hdr.h +++ /dev/null @@ -1,192 +0,0 @@ -#pragma once - -static const u32 ELF_MAGIC = 0x7f454c46; - -typedef enum -{ - elf_32_bit = 1, - elf_64_bit = 2, -} elf_bit_format_types; - -typedef enum -{ - elf_little_endian = 1, - elf_big_endian = 2, -} elf_endianness; - -typedef enum -{ - elf_relocatable = 1, - elf_executeable = 2, - elf_shared = 3, - elf_core = 4, -} elf_type; - -typedef enum -{ - elf_arm = 0x28, -} elf_target_architecture; - -typedef struct -{ - u8 magic[4]; - u8 bitFormat; - u8 endianness; - u8 elfVersion; - u8 os; - u8 padding0[8]; - u8 type[2]; - u8 targetArchitecture[2]; - u8 version[4]; - u8 entryPoint[4]; - u8 programHeaderTableOffset[4]; - u8 sectionHeaderTableOffset[4]; - u8 flags[4]; - u8 headerSize[2]; - u8 programHeaderEntrySize[2]; - u8 programHeaderEntryCount[2]; - u8 sectionTableEntrySize[2]; - u8 sectionHeaderEntryCount[2]; - u8 sectionHeaderNameEntryIndex[2]; -} elf_32_hdr; - -typedef struct -{ - u8 magic[4]; - u8 bitFormat; - u8 endianness; - u8 elfVersion; - u8 os; - u8 padding0[8]; - u8 type[2]; - u8 targetArchitecture[2]; - u8 version[4]; - u8 entryPoint[8]; - u8 programHeaderTableOffset[8]; - u8 sectionHeaderTableOffset[8]; - u8 flags[4]; - u8 headerSize[2]; - u8 programHeaderEntrySize[2]; - u8 programHeaderEntryCount[2]; - u8 sectionTableEntrySize[2]; - u8 sectionHeaderEntryCount[2]; - u8 sectionHeaderNameEntryIndex[2]; -} elf_64_hdr; - -/* taken from elf specs, will not follow global style */ - -/* Section header. */ - -/* Legal values for sh_type (section type). */ - -#define SHT_NULL 0 /* Section header table entry unused */ -#define SHT_PROGBITS 1 /* Program data */ -#define SHT_SYMTAB 2 /* Symbol table */ -#define SHT_STRTAB 3 /* String table */ -#define SHT_RELA 4 /* Relocation entries with addends */ -#define SHT_HASH 5 /* Symbol hash table */ -#define SHT_DYNAMIC 6 /* Dynamic linking information */ -#define SHT_NOTE 7 /* Notes */ -#define SHT_NOBITS 8 /* Program space with no data (bss) */ -#define SHT_REL 9 /* Relocation entries, no addends */ -#define SHT_SHLIB 10 /* Reserved */ -#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ -#define SHT_UNKNOWN12 12 -#define SHT_UNKNOWN13 13 -#define SHT_INIT_ARRAY 14 -#define SHT_FINI_ARRAY 15 -#define SHT_PREINIT_ARRAY 16 -#define SHT_GROUP 17 -#define SHT_SYMTAB_SHNDX 18 -#define SHT_NUM 19 -#define SHT_ARM_EXIDX 0x70000001 /* Exception Index table */ -#define SHT_ARM_PREEMPTMAP 0x70000002 /* BPABI DLL dynamic linking pre-emption map*/ -#define SHT_ARM_ATTRIBUTES 0x70000003 /* Object file compatibility attributes */ -#define SHT_ARM_DEBUGOVERLAY 0x70000004 -#define SHT_ARM_OVERLAYSECTION 0x70000005 - -#define SHF_WRITE 0x01 /* sh_flags */ -#define SHF_ALLOC 0x02 -#define SHF_EXECINSTR 0x04 -#define SHF_MERGE 0x10 -#define SHF_STRINGS 0x20 -#define SHF_INFO_LINK 0x40 -#define SHF_LINK_ORDER 0x80 -#define SHF_OS_NONCONFORMING 0x100 -#define SHF_GROUP 0x200 -#define SHF_TLS 0x400 - - -typedef struct -{ - u8 sh_name[4]; /* Section name (string tbl index) */ - u8 sh_type[4]; /* Section type */ - u8 sh_flags[4]; /* Section flags */ - u8 sh_addr[4]; /* Section virtual addr at execution */ - u8 sh_offset[4]; /* Section file offset */ - u8 sh_size[4]; /* Section size in bytes */ - u8 sh_link[4]; /* Link to another section */ - u8 sh_info[4]; /* Additional section information */ - u8 sh_addralign[4]; /* Section alignment */ - u8 sh_entsize[4]; /* Entry size if section holds table */ -} elf_32_shdr; - -typedef struct -{ - u8 sh_name[8]; /* Section name (string tbl index) */ - u8 sh_type[8]; /* Section type */ - u8 sh_flags[8]; /* Section flags */ - u8 sh_addr[8]; /* Section virtual addr at execution */ - u8 sh_offset[8]; /* Section file offset */ - u8 sh_size[8]; /* Section size in bytes */ - u8 sh_link[8]; /* Link to another section */ - u8 sh_info[8]; /* Additional section information */ - u8 sh_addralign[8]; /* Section alignment */ - u8 sh_entsize[8]; /* Entry size if section holds table */ -} elf_64_shdr; - -/* Program segment header. */ - -/* p_type legal values */ -#define PT_NULL 0 /* Program header table entry unused */ -#define PT_LOAD 1 /* Loadable program segment */ -#define PT_DYNAMIC 2 /* Dynamic linking information */ -#define PT_INTERP 3 /* Program interpreter */ -#define PT_NOTE 4 /* Auxiliary information */ -#define PT_SHLIB 5 /* Reserved */ -#define PT_PHDR 6 /* Entry for header table itself */ -#define PT_NUM 7 /* Number of defined types. */ -#define PT_LOOS 0x60000000 /* Start of OS-specific */ -#define PT_HIOS 0x6fffffff /* End of OS-specific */ -#define PT_LOPROC 0x70000000 /* Start of processor-specific */ -#define PT_HIPROC 0x7fffffff /* End of processor-specific */ - -#define PF_CTRSDK 0x80000000 -#define PF_R 0x4 /* p_flags */ -#define PF_W 0x2 -#define PF_X 0x1 - - -typedef struct -{ - u8 p_type[4]; /* Segment type */ - u8 p_offset[4]; /* Segment file offset */ - u8 p_vaddr[4]; /* Segment virtual address */ - u8 p_paddr[4]; /* Segment physical address */ - u8 p_filesz[4]; /* Segment size in file */ - u8 p_memsz[4]; /* Segment size in memory */ - u8 p_flags[4]; /* Segment flags */ - u8 p_align[4]; /* Segment alignment */ -} elf_32_phdr; - -typedef struct -{ - u8 p_type[8]; /* Segment type */ - u8 p_flags[8]; /* Segment flags */ - u8 p_offset[8]; /* Segment file offset */ - u8 p_vaddr[8]; /* Segment virtual address */ - u8 p_paddr[8]; /* Segment physical address */ - u8 p_filesz[8]; /* Segment size in file */ - u8 p_memsz[8]; /* Segment size in memory */ - u8 p_align[8]; /* Segment alignment */ -} elf_64_phdr; \ No newline at end of file diff --git a/makerom/exheader.c b/makerom/exheader.c index 0d4b0bf0..0d8065b7 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -129,8 +129,6 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * exhdrset->exHdr = (extended_hdr*)ncchset->sections.exhdr.buffer; exhdrset->acexDesc = (access_descriptor*)ncchset->sections.acexDesc.buffer; - /* BSS Size */ - u32_to_u8(exhdrset->exHdr->codeSetInfo.bssSize,ncchset->codeDetails.bssSize,LE); /* Data */ u32_to_u8(exhdrset->exHdr->codeSetInfo.data.address,ncchset->codeDetails.rwAddress,LE); u32_to_u8(exhdrset->exHdr->codeSetInfo.data.codeSize,ncchset->codeDetails.rwSize,LE); @@ -143,12 +141,16 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * u32_to_u8(exhdrset->exHdr->codeSetInfo.text.address,ncchset->codeDetails.textAddress,LE); u32_to_u8(exhdrset->exHdr->codeSetInfo.text.codeSize,ncchset->codeDetails.textSize,LE); u32_to_u8(exhdrset->exHdr->codeSetInfo.text.numMaxPages,ncchset->codeDetails.textMaxPages,LE); + /* BSS Size */ + u32_to_u8(exhdrset->exHdr->codeSetInfo.bssSize, ncchset->codeDetails.bssSize, LE); + /* Stack Size */ + u32_to_u8(exhdrset->exHdr->codeSetInfo.stackSize, ncchset->codeDetails.stackSize, LE); /* Set Simple Flags */ if(ncchset->options.CompressCode) - exhdrset->exHdr->codeSetInfo.flag |= infoflag_COMPRESS_EXEFS_0; - if(ncchset->options.UseOnSD) - exhdrset->exHdr->codeSetInfo.flag |= infoflag_SD_APPLICATION; + exhdrset->exHdr->codeSetInfo.compressExeFs0 = true; + if (ncchset->options.UseOnSD) + exhdrset->exHdr->codeSetInfo.useOnSd = true; if(!ncchset->options.UseRomFS) exhdrset->exHdr->arm11SystemLocalCapabilities.storageInfo.otherAttributes |= attribute_NOT_USE_ROMFS; @@ -195,13 +197,6 @@ int get_ExHeaderCodeSetInfo(exhdr_CodeSetInfo *CodeSetInfo, rsf_settings *rsf) else strncpy((char*)CodeSetInfo->name, DEFAULT_EXHEADER_NAME, 8); - /* Stack Size */ - if(rsf->SystemControlInfo.StackSize) - u32_to_u8(CodeSetInfo->stackSize, strtoul(rsf->SystemControlInfo.StackSize,NULL,0), LE); - else{ - ErrorParamNotFound("SystemControlInfo/StackSize"); - return EXHDR_BAD_RSF_OPT; - } /* Remaster Version */ if(rsf->SystemControlInfo.RemasterVersion) u16_to_u8(CodeSetInfo->remasterVersion, strtol(rsf->SystemControlInfo.RemasterVersion,NULL,0), LE); @@ -311,93 +306,87 @@ int SetARM11SystemLocalInfoFlags(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_ return EXHDR_BAD_RSF_OPT; } - /* Flag[0] */ - arm11->flag[0] |= rsf->AccessControlInfo.EnableL2Cache; + /* Defaults */ + arm11->enableL2Cache = false; + arm11->cpuSpeed = cpuspeed_268MHz; + arm11->systemModeExt = sysmode_ext_LEGACY; + arm11->affinityMask = 0; + arm11->idealProcessor = 0; + arm11->systemMode = sysmode_64MB; + + /* flag[0] */ + arm11->enableL2Cache |= rsf->AccessControlInfo.EnableL2Cache; if (rsf->AccessControlInfo.CpuSpeed) { if(strcasecmp(rsf->AccessControlInfo.CpuSpeed, "268mhz") == 0) - arm11->flag[0] |= cpuspeed_268MHz << 1; + arm11->cpuSpeed |= cpuspeed_268MHz; else if(strcasecmp(rsf->AccessControlInfo.CpuSpeed, "804mhz") == 0) - arm11->flag[0] |= cpuspeed_804MHz << 1; + arm11->cpuSpeed |= cpuspeed_804MHz; else { fprintf(stderr, "[EXHEADER ERROR] Invalid cpu speed: 0x%s\n", rsf->AccessControlInfo.CpuSpeed); return EXHDR_BAD_RSF_OPT; } } - else - arm11->flag[0] |= cpuspeed_268MHz << 1; - /* Flag[1] (SystemModeExt) */ + /* flag[1] (SystemModeExt) */ if (rsf->AccessControlInfo.SystemModeExt) { if (strcasecmp(rsf->AccessControlInfo.SystemModeExt, "Legacy") == 0) - arm11->flag[1] = sysmode_ext_LEGACY; + arm11->systemModeExt = sysmode_ext_LEGACY; else if (strcasecmp(rsf->AccessControlInfo.SystemModeExt, "124MB") == 0) - arm11->flag[1] = sysmode_ext_124MB; + arm11->systemModeExt = sysmode_ext_124MB; else if (strcasecmp(rsf->AccessControlInfo.SystemModeExt, "178MB") == 0) - arm11->flag[1] = sysmode_ext_178MB; + arm11->systemModeExt = sysmode_ext_178MB; else { fprintf(stderr, "[EXHEADER ERROR] Unexpected SystemModeExt: %s\n", rsf->AccessControlInfo.SystemModeExt); return EXHDR_BAD_RSF_OPT; } } - else { - arm11->flag[1] = sysmode_ext_LEGACY; - } - /* Flag[2] */ - u8 affinityMask = 0; - u8 idealProcessor = 0; - u8 systemMode = 0; - + /* flag[2] */ if(rsf->AccessControlInfo.AffinityMask){ - affinityMask = strtol(rsf->AccessControlInfo.AffinityMask,NULL,0); - if(affinityMask > 1){ - fprintf(stderr,"[EXHEADER ERROR] Unexpected AffinityMask: %d. Expected range: 0x0 - 0x1\n",affinityMask); + arm11->affinityMask = strtol(rsf->AccessControlInfo.AffinityMask,NULL,0); + if(arm11->affinityMask > 1){ + fprintf(stderr,"[EXHEADER ERROR] Unexpected AffinityMask: %d. Expected range: 0x0 - 0x1\n", arm11->affinityMask); return EXHDR_BAD_RSF_OPT; } } if(rsf->AccessControlInfo.IdealProcessor){ - idealProcessor = strtol(rsf->AccessControlInfo.IdealProcessor,NULL,0); - if(idealProcessor > 1){ - fprintf(stderr,"[EXHEADER ERROR] Unexpected IdealProcessor: %d. Expected range: 0x0 - 0x1\n",idealProcessor); + arm11->idealProcessor = strtol(rsf->AccessControlInfo.IdealProcessor,NULL,0); + if(arm11->idealProcessor > 1){ + fprintf(stderr,"[EXHEADER ERROR] Unexpected IdealProcessor: %d. Expected range: 0x0 - 0x1\n", arm11->idealProcessor); return EXHDR_BAD_RSF_OPT; } } if(rsf->AccessControlInfo.SystemMode){ if (strcasecmp(rsf->AccessControlInfo.SystemMode, "64MB") == 0 || strcasecmp(rsf->AccessControlInfo.SystemMode, "prod") == 0) - systemMode = sysmode_64MB; + arm11->systemMode = sysmode_64MB; //else if (strcasecmp(rsf->AccessControlInfo.SystemMode, "UNK") == 0 || strcasecmp(rsf->AccessControlInfo.SystemMode, "null") == 0) - // systemMode = sysmode_UNK; + // arm11->systemMode = sysmode_UNK; else if (strcasecmp(rsf->AccessControlInfo.SystemMode, "96MB") == 0 || strcasecmp(rsf->AccessControlInfo.SystemMode, "dev1") == 0) - systemMode = sysmode_96MB; + arm11->systemMode = sysmode_96MB; else if (strcasecmp(rsf->AccessControlInfo.SystemMode, "80MB") == 0 || strcasecmp(rsf->AccessControlInfo.SystemMode, "dev2") == 0) - systemMode = sysmode_80MB; + arm11->systemMode = sysmode_80MB; else if (strcasecmp(rsf->AccessControlInfo.SystemMode, "72MB") == 0 || strcasecmp(rsf->AccessControlInfo.SystemMode, "dev3") == 0) - systemMode = sysmode_72MB; + arm11->systemMode = sysmode_72MB; else if (strcasecmp(rsf->AccessControlInfo.SystemMode, "32MB") == 0 || strcasecmp(rsf->AccessControlInfo.SystemMode, "dev4") == 0) - systemMode = sysmode_32MB; + arm11->systemMode = sysmode_32MB; else { fprintf(stderr, "[EXHEADER ERROR] Unexpected SystemMode: %s\n", rsf->AccessControlInfo.SystemMode); return EXHDR_BAD_RSF_OPT; } } - else { - systemMode = sysmode_64MB; - } - arm11->flag[2] = (u8)(systemMode << 4 | affinityMask << 2 | idealProcessor); /* flag[3] (Thread Priority) */ if(rsf->AccessControlInfo.Priority){ - u8 priority = strtoul(rsf->AccessControlInfo.Priority,NULL,0); + arm11->threadPriority = strtoul(rsf->AccessControlInfo.Priority,NULL,0); if(GetAppType(rsf) == processtype_APPLICATION) - priority += 32; - if(priority > 127){ - fprintf(stderr,"[EXHEADER ERROR] Invalid Priority: %d\n",priority); + arm11->threadPriority += 32; + if(arm11->threadPriority < 0){ + fprintf(stderr,"[EXHEADER ERROR] Invalid Priority: %d\n", arm11->threadPriority); return EXHDR_BAD_RSF_OPT; } - arm11->flag[3] = priority; } else{ ErrorParamNotFound("AccessControlInfo/Priority"); diff --git a/makerom/exheader.h b/makerom/exheader.h index 8d91e564..d3ef983b 100644 --- a/makerom/exheader.h +++ b/makerom/exheader.h @@ -121,7 +121,14 @@ typedef struct { u8 name[8]; u8 padding0[5]; - u8 flag; + union { + u8 flag; + struct { + u8 compressExeFs0 : 1; + u8 useOnSd : 1; + }; + }; + u8 remasterVersion[2]; // le u16 exhdr_CodeSegmentInfo text; u8 stackSize[4]; // le u32 @@ -151,7 +158,23 @@ typedef struct { u8 programId[8]; u8 coreVersion[4]; - u8 flag[4]; + union { + u8 flag[4]; + struct { + u8 enableL2Cache : 1; + u8 cpuSpeed : 1; + u8: 6; + + u8 systemModeExt : 4; + u8: 4; + + u8 idealProcessor : 2; + u8 affinityMask : 2; + u8 systemMode : 4; + + s8 threadPriority; + }; + }; u8 resourceLimitDescriptor[16][2]; exhdr_StorageInfo storageInfo; u8 serviceAccessControl[34][8]; // Those char[8] server names diff --git a/makerom/makerom.vcxproj b/makerom/makerom.vcxproj index 472d5b49..ecc931c2 100644 --- a/makerom/makerom.vcxproj +++ b/makerom/makerom.vcxproj @@ -97,6 +97,7 @@ + @@ -105,7 +106,6 @@ - @@ -198,6 +198,7 @@ + diff --git a/makerom/makerom.vcxproj.filters b/makerom/makerom.vcxproj.filters index 5fb196a6..049b9f80 100644 --- a/makerom/makerom.vcxproj.filters +++ b/makerom/makerom.vcxproj.filters @@ -72,9 +72,6 @@ Header Files - - Header Files - Header Files @@ -339,6 +336,9 @@ Resource Files\DESC + + Header Files + @@ -470,6 +470,9 @@ Source Files\polarssl + + Source Files + diff --git a/makerom/ncch.c b/makerom/ncch.c index 4a8d00e3..69621d46 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -3,7 +3,7 @@ #include "ncch_build.h" #include "exheader_build.h" #include "exheader_read.h" -#include "elf.h" +#include "code.h" #include "exefs_build.h" #include "exefs_read.h" #include "romfs.h" diff --git a/makerom/ncch_build.h b/makerom/ncch_build.h index 96f47c4e..d97ab7b7 100644 --- a/makerom/ncch_build.h +++ b/makerom/ncch_build.h @@ -70,6 +70,7 @@ typedef struct u32 rwSize; u32 rwMaxPages; u32 bssSize; + u32 stackSize; } codeDetails; struct diff --git a/makerom/utils.c b/makerom/utils.c index 5a97fb66..dd08733b 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -368,7 +368,7 @@ int fseek_64(FILE *fp, u64 file_pos) } //Data Size conversion -u16 u8_to_u16(u8 *value, u8 endianness) +u16 u8_to_u16(const u8 *value, u8 endianness) { u16 new_value; switch(endianness){ @@ -378,7 +378,7 @@ u16 u8_to_u16(u8 *value, u8 endianness) return new_value; } -u32 u8_to_u32(u8 *value, u8 endianness) +u32 u8_to_u32(const u8 *value, u8 endianness) { u32 new_value; switch(endianness){ @@ -389,7 +389,7 @@ u32 u8_to_u32(u8 *value, u8 endianness) } -u64 u8_to_u64(u8 *value, u8 endianness) +u64 u8_to_u64(const u8 *value, u8 endianness) { u64 ret = 0; switch(endianness){ diff --git a/makerom/utils.h b/makerom/utils.h index c479f113..6d30d511 100644 --- a/makerom/utils.h +++ b/makerom/utils.h @@ -59,9 +59,9 @@ void ReadFile64(void *outbuff, u64 size, u64 offset, FILE *file); int fseek_64(FILE *fp, u64 file_pos); //Data Size conversion -u16 u8_to_u16(u8 *value, u8 endianness); -u32 u8_to_u32(u8 *value, u8 endianness); -u64 u8_to_u64(u8 *value, u8 endianness); +u16 u8_to_u16(const u8 *value, u8 endianness); +u32 u8_to_u32(const u8 *value, u8 endianness); +u64 u8_to_u64(const u8 *value, u8 endianness); int u16_to_u8(u8 *out_value, u16 in_value, u8 endianness); int u32_to_u8(u8 *out_value, u32 in_value, u8 endianness); int u64_to_u8(u8 *out_value, u64 in_value, u8 endianness); From 88c0f66c4a8cf82f570582aab40f43e05b9729be Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 11 Nov 2015 01:55:20 +0800 Subject: [PATCH 116/317] [makerom] Misc --- makerom/dir.c | 42 +++++++++++++++++++-------------------- makerom/dir.h | 6 +++--- makerom/romfs_gen.c | 48 ++++++++++++++++++++------------------------- 3 files changed, 45 insertions(+), 51 deletions(-) diff --git a/makerom/dir.c b/makerom/dir.c index 46580a79..8d8027b3 100644 --- a/makerom/dir.c +++ b/makerom/dir.c @@ -28,9 +28,9 @@ int fs_InitDir(u16 *path, u32 pathlen, fs_dir *dir) memcpy(dir->name,path,dir->name_len); - dir->m_dir = 10; - dir->u_dir = 0; - dir->dir = calloc(dir->m_dir,sizeof(fs_dir)); + dir->m_child = 10; + dir->u_child = 0; + dir->child = calloc(dir->m_child,sizeof(fs_dir)); dir->m_file = 10; dir->u_file = 0; @@ -41,13 +41,13 @@ int fs_InitDir(u16 *path, u32 pathlen, fs_dir *dir) int fs_ManageDirSlot(fs_dir *dir) { - if(dir->u_dir >= dir->m_dir) + if(dir->u_child >= dir->m_child) { - dir->m_dir *= 2; - fs_dir *tmp = calloc(dir->m_dir,sizeof(fs_dir)); - memcpy(tmp,dir->dir,sizeof(fs_dir)*dir->u_dir); - free(dir->dir); - dir->dir = tmp; + dir->m_child *= 2; + fs_dir *tmp = calloc(dir->m_child,sizeof(fs_dir)); + memcpy(tmp,dir->child,sizeof(fs_dir)*dir->u_child); + free(dir->child); + dir->child = tmp; } return 0; } @@ -158,9 +158,9 @@ bool fs_EntryIsDirNav(fs_entry *entry) int fs_AddDir(fs_entry *entry, fs_dir *dir) { fs_ManageDirSlot(dir); - u32 current_slot = dir->u_dir; - dir->u_dir++; - return fs_OpenDir(entry->fs_name,entry->name,entry->name_len,&dir->dir[current_slot]); + u32 current_slot = dir->u_child; + dir->u_child++; + return fs_OpenDir(entry->fs_name,entry->name,entry->name_len,&dir->child[current_slot]); } int fs_AddFile(fs_entry *entry, fs_dir *dir) @@ -286,10 +286,10 @@ void fs_PrintDir(fs_dir *dir, u32 depth) // This is just for simple debugging, p #endif } } - if(dir->u_dir) + if(dir->u_child) { - for(u32 i = 0; i < dir->u_dir; i++) - fs_PrintDir(&dir->dir[i],depth+1); + for(u32 i = 0; i < dir->u_child; i++) + fs_PrintDir(&dir->child[i],depth+1); } } @@ -305,13 +305,13 @@ void fs_FreeDir(fs_dir *dir) //printf("free dir names and\n"); - for(u32 i = 0; i < dir->u_dir; i++) + for(u32 i = 0; i < dir->u_child; i++) { - free(dir->dir[i].name); - fs_FreeDir(&dir->dir[i]); + free(dir->child[i].name); + fs_FreeDir(&dir->child[i]); } //printf("free dir struct\n"); - free(dir->dir); + free(dir->child); } @@ -323,6 +323,6 @@ void fs_FreeFiles(fs_dir *dir) fclose(dir->file[i].fp); } - for(u32 i = 0; i < dir->u_dir; i++) - fs_FreeFiles(&dir->dir[i]); + for(u32 i = 0; i < dir->u_child; i++) + fs_FreeFiles(&dir->child[i]); } \ No newline at end of file diff --git a/makerom/dir.h b/makerom/dir.h index 1279b362..f7c6938d 100644 --- a/makerom/dir.h +++ b/makerom/dir.h @@ -44,9 +44,9 @@ struct fs_dir fs_romfs_char *name; u32 name_len; - struct fs_dir *dir; - u32 m_dir; - u32 u_dir; + struct fs_dir *child; + u32 m_child; + u32 u_child; struct fs_file *file; u32 m_file; diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index dc86a716..3db457bf 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -59,7 +59,7 @@ int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) free(fs_raw); /* Abort romfs making, if no wanted files/directories were found */ - if(ctx->fs->u_file == 0 && ctx->fs->u_dir == 0){ + if(ctx->fs->u_file == 0 && ctx->fs->u_child == 0){ ctx->romfsSize = 0; goto finish; } @@ -132,10 +132,9 @@ bool IsDirWanted(fs_dir *dir, void *filter_criteria) break; } } - fs_dir *tmp = (fs_dir*)dir->dir; - for(u32 i = 0; i < dir->u_dir; i++) + for(u32 i = 0; i < dir->u_child; i++) { - if(IsDirWanted(&tmp[i],filter_criteria)) + if(IsDirWanted(&dir->child[i],filter_criteria)) { ret = true; break; @@ -158,13 +157,12 @@ void CalcDirSize(romfs_buildctx *ctx, fs_dir *fs) ctx->m_dataLen = align(ctx->m_dataLen,0x10) + fs->file[i].size; } - fs_dir *dir = (fs_dir*)fs->dir; - for(u32 i = 0; i < fs->u_dir; i++) + for(u32 i = 0; i < fs->u_child; i++) { - CalcDirSize(ctx,&dir[i]); + CalcDirSize(ctx,&fs->child[i]); } ctx->fileNum += fs->u_file; - ctx->dirNum += fs->u_dir; + ctx->dirNum += fs->u_child; } u32 GetHashTableCount(u32 num) @@ -214,23 +212,20 @@ int FilterRomFS(fs_dir *fs_raw, fs_dir *fs_filtered, void *filter_criteria) fs_filtered->name = calloc(fs_filtered->name_len+2,1); memcpy(fs_filtered->name,fs_raw->name,fs_filtered->name_len); - fs_filtered->u_dir = 0; - fs_filtered->m_dir = fs_raw->u_dir; - fs_filtered->dir = calloc(fs_filtered->m_dir,sizeof(fs_dir)); + fs_filtered->u_child = 0; + fs_filtered->m_child = fs_raw->u_child; + fs_filtered->child = calloc(fs_filtered->m_child,sizeof(fs_dir)); fs_filtered->u_file = 0; fs_filtered->m_file = fs_raw->u_file; fs_filtered->file = calloc(fs_filtered->m_file,sizeof(fs_file)); - - fs_dir *dir_raw = (fs_dir*)fs_raw->dir; - fs_dir *dir_filtered = (fs_dir*)fs_filtered->dir; - for(u32 i = 0; i < fs_raw->u_dir; i++) + for(u32 i = 0; i < fs_raw->u_child; i++) { - if(IsDirWanted(&dir_raw[i],filter_criteria)) + if(IsDirWanted(&fs_raw->child[i],filter_criteria)) { - FilterRomFS(&dir_raw[i],&dir_filtered[fs_filtered->u_dir],filter_criteria); - fs_filtered->u_dir++; + FilterRomFS(&fs_raw->child[i],&fs_filtered->child[fs_filtered->u_child],filter_criteria); + fs_filtered->u_child++; } } @@ -421,34 +416,33 @@ void AddDirChildrenToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 dir) } } - if (fs->u_dir) + if (fs->u_child) { /* Prepare to store child addresses */ - u32 *childs = calloc(fs->u_dir, sizeof(u32)); + u32 *childs = calloc(fs->u_child, sizeof(u32)); /* Create child directory entries*/ u32_to_u8(entry->childoffset, ctx->u_dirTableLen, LE); - fs_dir *subdir = (fs_dir*)fs->dir; - for (u32 i = 0; i < fs->u_dir; i++) + for (u32 i = 0; i < fs->u_child; i++) { /* Store address fo child */ childs[i] = ctx->u_dirTableLen; u32 dir_sibling = 0; - if (i >= fs->u_dir - 1) + if (i >= fs->u_child - 1) dir_sibling = ROMFS_UNUSED_ENTRY; else - dir_sibling = ctx->u_dirTableLen + sizeof(romfs_direntry) + (u32)align(subdir[i].name_len, 4); + dir_sibling = ctx->u_dirTableLen + sizeof(romfs_direntry) + (u32)align(fs->child[i].name_len, 4); /* Create child directory entry */ - AddDirToRomfs(ctx, &subdir[i], dir, dir_sibling); + AddDirToRomfs(ctx, &fs->child[i], dir, dir_sibling); } /* Populate child's childs */ - for (u32 i = 0; i < fs->u_dir; i++) + for (u32 i = 0; i < fs->u_child; i++) { - AddDirChildrenToRomfs(ctx, &subdir[i], dir, childs[i]); + AddDirChildrenToRomfs(ctx, &fs->child[i], dir, childs[i]); } free(childs); From e7dc460436fcf8fd4a71f1936df1545bea61b993 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 11 Nov 2015 23:20:03 +0800 Subject: [PATCH 117/317] [makerom] Fixed makerom ROMFS file limitation. +Several bug fixes. --- makerom/Makefile | 2 +- makerom/cia.c | 23 +++- makerom/code.c | 14 +- makerom/dir.c | 287 +++++++++++++++++++++++----------------- makerom/dir.h | 28 +++- makerom/ncch.c | 19 ++- makerom/ncsd.c | 24 ++++ makerom/romfs.c | 2 +- makerom/romfs.h | 2 + makerom/romfs_gen.c | 125 +++++++---------- makerom/titleid.c | 14 +- makerom/user_settings.c | 43 +++--- makerom/utils.c | 44 +++--- makerom/utils.h | 12 +- 14 files changed, 368 insertions(+), 271 deletions(-) diff --git a/makerom/Makefile b/makerom/Makefile index e68a81c4..d15d60f4 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -5,7 +5,7 @@ OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) # Compiler Settings LIBS = -static-libgcc CXXFLAGS = -I. -CFLAGS = --std=c99 -O2 -flto -Wall -Wno-unused-but-set-variable -Wno-unused-value -I. $(MAKEROM_BUILD_FLAGS) +CFLAGS = --std=c99 -Wall -Wno-unused-but-set-variable -Wno-unused-value -I. $(MAKEROM_BUILD_FLAGS) CC = gcc CXX = g++ diff --git a/makerom/cia.c b/makerom/cia.c index 719c91b4..36b54d92 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -571,16 +571,27 @@ u16 SetupVersion(u16 major, u16 minor, u16 micro) void GetContentHashes(cia_settings *ciaset) { - for(int i = 0; i < ciaset->content.count; i++) - ShaCalc(ciaset->ciaSections.content.buffer+ciaset->content.offset[i],ciaset->content.size[i],ciaset->content.hash[i],CTR_SHA_256); + for (int i = 0; i < ciaset->content.count; i++) { + if (ciaset->verbose) + printf("[CIA] Hashing content %d... ", i); + ShaCalc(ciaset->ciaSections.content.buffer + ciaset->content.offset[i], ciaset->content.size[i], ciaset->content.hash[i], CTR_SHA_256); + if (ciaset->verbose) + printf("Done!\n"); + } } void EncryptContent(cia_settings *ciaset) { for(int i = 0; i < ciaset->content.count; i++){ + if (ciaset->verbose) + printf("[CIA] Encrypting content %d... ", i); + ciaset->content.flags[i] |= content_Encrypted; u8 *content = ciaset->ciaSections.content.buffer+ciaset->content.offset[i]; CryptContent(content, content, ciaset->content.size[i], ciaset->common.titleKey, i, ENC); + + if (ciaset->verbose) + printf("Done!\n"); } } @@ -638,12 +649,20 @@ int BuildCiaHdr(cia_settings *ciaset) int WriteCiaToFile(cia_settings *ciaset) { + if (ciaset->verbose) { + printf("[CIA] Writing to file... "); + } WriteBuffer(ciaset->ciaSections.ciaHdr.buffer,ciaset->ciaSections.ciaHdr.size,0,ciaset->out); WriteBuffer(ciaset->ciaSections.certChain.buffer,ciaset->ciaSections.certChain.size,ciaset->ciaSections.certChainOffset,ciaset->out); WriteBuffer(ciaset->ciaSections.tik.buffer,ciaset->ciaSections.tik.size,ciaset->ciaSections.tikOffset,ciaset->out); WriteBuffer(ciaset->ciaSections.tmd.buffer,ciaset->ciaSections.tmd.size,ciaset->ciaSections.tmdOffset,ciaset->out); WriteBuffer(ciaset->ciaSections.content.buffer,ciaset->ciaSections.content.size,ciaset->ciaSections.contentOffset,ciaset->out); WriteBuffer(ciaset->ciaSections.meta.buffer,ciaset->ciaSections.meta.size,ciaset->ciaSections.metaOffset,ciaset->out); + + if (ciaset->verbose) { + printf("Done!\n"); + } + return 0; } diff --git a/makerom/code.c b/makerom/code.c index ea31b2c0..07ee74a7 100644 --- a/makerom/code.c +++ b/makerom/code.c @@ -41,7 +41,7 @@ int ImportExeFsCodeBinaryFromFile(ncch_settings *set) u32 size = set->componentFilePtrs.codeSize; u8 *buffer = malloc(size); if (!buffer) { - fprintf(stderr, "[ELF ERROR] Not enough memory\n"); + fprintf(stderr, "[CODE ERROR] Not enough memory\n"); return MEM_ERROR; } ReadFile64(buffer, size, 0, set->componentFilePtrs.code); @@ -51,10 +51,14 @@ int ImportExeFsCodeBinaryFromFile(ncch_settings *set) if (!set->exefsSections.code.buffer) { fprintf(stderr, "[ELF ERROR] Not enough memory\n"); return MEM_ERROR; } ReadFile64(set->exefsSections.code.buffer, set->exefsSections.code.size, 0, set->componentFilePtrs.code); if (set->options.CompressCode) { + if (set->options.verbose) + printf("[CODE] Compressing code... "); u32 new_len; set->exefsSections.code.buffer = BLZ_Code(buffer, size, &new_len, BLZ_NORMAL); set->exefsSections.code.size = new_len; free(buffer); + if (set->options.verbose) + printf("Done!\n"); } else { set->exefsSections.code.size = size; @@ -63,12 +67,12 @@ int ImportExeFsCodeBinaryFromFile(ncch_settings *set) size = set->componentFilePtrs.exhdrSize; if (size < sizeof(extended_hdr)) { - fprintf(stderr, "[ELF ERROR] Exheader code info template is too small\n"); + fprintf(stderr, "[CODE ERROR] Exheader code info template is too small\n"); return FAILED_TO_IMPORT_FILE; } extended_hdr *exhdr = malloc(size); if (!exhdr) { - fprintf(stderr, "[ELF ERROR] Not enough memory\n"); + fprintf(stderr, "[CODE ERROR] Not enough memory\n"); return MEM_ERROR; } ReadFile64(exhdr, size, 0, set->componentFilePtrs.exhdr); @@ -245,10 +249,14 @@ int CreateExeFsCode(elf_context *elf, ncch_settings *set) /* Compressing If needed */ if (set->options.CompressCode) { + if (set->options.verbose) + printf("[CODE] Compressing code... "); u32 new_len; set->exefsSections.code.buffer = BLZ_Code(code, size, &new_len, BLZ_NORMAL); set->exefsSections.code.size = new_len; free(code); + if (set->options.verbose) + printf("Done!\n"); } else { set->exefsSections.code.size = size; diff --git a/makerom/dir.c b/makerom/dir.c index 8d8027b3..097c44ec 100644 --- a/makerom/dir.c +++ b/makerom/dir.c @@ -2,31 +2,125 @@ #include "dir.h" #include "utf.h" +const fs_romfs_char FS_CURRENT_DIR_PATH = 0x2E; +const fs_romfs_char FS_PARENT_DIR_PATH[2] = { 0x2E,0x2E }; + /* This is the FS interface for ROMFS generation */ /* Tested working on Windows/Linux/OSX */ -int fs_InitDir(u16 *path, u32 pathlen, fs_dir *dir); +int fs_InitDir(const fs_entry *entry, fs_dir *dir); int fs_ManageDirSlot(fs_dir *dir); int fs_ManageFileSlot(fs_dir *dir); void fs_chdirUp(void); -fs_entry* fs_GetEntry(fs_DIR *dp); +fs_entry* fs_GetEntry(const fs_char *parent_path, fs_DIR *dp); void fs_FreeEntry(fs_entry *entry); bool fs_EntryIsDirNav(fs_entry *entry); int fs_AddDir(fs_entry *entry, fs_dir *dir); int fs_AddFile(fs_entry *entry, fs_dir *dir); -int fs_RomFsStrLen(fs_romfs_char *str) +u32 fs_romfs_strlen(const fs_romfs_char *str) { - int i; + u32 i; for( i = 0; str[i] != 0x0; i++ ); return i; } -int fs_InitDir(u16 *path, u32 pathlen, fs_dir *dir) +u32 fs_strlen(const fs_char *str) { - dir->name_len = pathlen; - dir->name = calloc(dir->name_len+2,1); - memcpy(dir->name,path,dir->name_len); - + u32 i; + for (i = 0; str[i] != 0x0; i++); + return i; +} + +FILE* fs_fopen(fs_char *path) +{ +#ifdef _WIN32 + return _wfopen(path, L"rb"); +#else + return fopen(path, "rb"); +#endif +} + +u64 fs_fsize(fs_char *path) +{ +#ifdef _WIN32 + return wGetFileSize64(path); +#else + return GetFileSize64(path); +#endif +} + +fs_char* fs_AppendToPath(const fs_char *src, const fs_char *add) +{ + u32 src_len, add_len; + fs_char *new_path; + + src_len = fs_strlen(src); + add_len = fs_strlen(add); + new_path = calloc(src_len + add_len + 0x10, sizeof(fs_char)); + +#ifdef _WIN32 + _snwprintf(new_path, src_len + add_len + 0x10, L"%s%c%s", src, FS_PATH_SEPARATOR, add); +#else + snprintf(new_path, src_len + add_len + 0x10, "%s%c%s", src, FS_PATH_SEPARATOR, add); +#endif + + return new_path; +} + +fs_char* fs_CopyPath(const fs_char *src) +{ + u32 src_len; + fs_char *new_path; + + src_len = fs_strlen(src); + new_path = calloc(src_len + 0x10, sizeof(fs_char)); + + for (u32 i = 0; i < src_len; i++) + new_path[i] = src[i]; + + return new_path; +} + +fs_romfs_char* fs_CopyRomfsName(const fs_romfs_char *src) +{ + u32 src_len; + fs_romfs_char *new_path; + + src_len = fs_strlen(src); + new_path = calloc(src_len + 0x10, sizeof(fs_romfs_char)); + + for (u32 i = 0; i < src_len; i++) + new_path[i] = src[i]; + + return new_path; +} + +void fs_fputs(FILE *out, const fs_char *str) +{ +#ifdef _WIN32 + wprintf(L"%s", str); +#else + printf("%s", str); +#endif +} + +void fs_romfs_fputs(FILE *out, const fs_romfs_char *str) +{ +#ifdef _WIN32 + wprintf(L"%s", str); +#else + const char *name = (const char*)str; + for (u32 i = 0; i < fs_romfs_strlen(str)*2; i += 2) + putchar(name[i]); +#endif +} + +int fs_InitDir(const fs_entry *entry, fs_dir *dir) +{ + dir->fs_path = fs_CopyPath(entry->fs_path); + + dir->name_len = entry->name_len; + dir->name = fs_CopyRomfsName(entry->name); dir->m_child = 10; dir->u_child = 0; @@ -65,16 +159,7 @@ int fs_ManageFileSlot(fs_dir *dir) return 0; } -void fs_chdirUp(void) -{ -#ifdef _WIN32 - fs_chdir(L".."); -#else - fs_chdir(".."); -#endif -} - -fs_entry* fs_GetEntry(fs_DIR *dp) +fs_entry* fs_GetEntry(const fs_char *parent_path, fs_DIR *dp) { // Directory structs struct fs_dirent *tmp_entry; @@ -99,9 +184,9 @@ fs_entry* fs_GetEntry(fs_DIR *dp) memset(entry,0,sizeof(fs_entry)); //Copy FS compatible Entry name - entry->fs_name = malloc(sizeof(fs_char)*(namlen+1)); - memset(entry->fs_name,0,sizeof(fs_char)*(namlen+1)); - memcpy(entry->fs_name,tmp_entry->d_name,sizeof(fs_char)*namlen); + fs_char *fs_name = calloc(sizeof(fs_char)*(namlen+1),1); + memcpy(fs_name,tmp_entry->d_name,sizeof(fs_char)*namlen); + entry->fs_path = fs_AppendToPath(parent_path, fs_name); // Convert Entry name into RomFS u16 char (windows wchar_t, thanks Nintendo) #if _WIN32 @@ -111,45 +196,41 @@ fs_entry* fs_GetEntry(fs_DIR *dp) #endif //printf("get dir entry from dir ptr to check if dir\n"); - tmp_dptr = fs_opendir(entry->fs_name); + tmp_dptr = fs_opendir(entry->fs_path); if(tmp_dptr) { //printf("is dir\n"); fs_closedir(tmp_dptr); entry->IsDir = true; entry->size = 0; - entry->fp = NULL; } else // Open file if it is a file { entry->IsDir = false; -#ifdef _WIN32 - entry->size = wGetFileSize64(entry->fs_name); - entry->fp = _wfopen(entry->fs_name,L"rb"); -#else - entry->size = GetFileSize64(entry->fs_name); - entry->fp = fopen(entry->fs_name,"rb"); -#endif + entry->size = fs_fsize(entry->fs_path); + } + + // Don't bother returning current entry, if it is useless + if (fs_EntryIsDirNav(entry)) { + fs_FreeEntry(entry); + return fs_GetEntry(parent_path, dp); } - //printf("fs_GetEntry() return\n"); + return entry; } void fs_FreeEntry(fs_entry *entry) { - free(entry->fs_name); + free(entry->fs_path); free(entry->name); free(entry); } bool fs_EntryIsDirNav(fs_entry *entry) { - //memdump(stdout,"Entry RomFS Name: ",(u8*)entry->name,entry->name_len); - const fs_romfs_char currentdir = 0x2E; - const fs_romfs_char upperdir[2] = {0x2E,0x2E}; - if(entry->name_len == sizeof(fs_romfs_char)*1 && memcmp(entry->name,¤tdir,sizeof(fs_romfs_char)*1) == 0) + if(entry->name_len == sizeof(fs_romfs_char)*1 && memcmp(entry->name,&FS_CURRENT_DIR_PATH,sizeof(fs_romfs_char)*1) == 0) return true; - if(entry->name_len == sizeof(fs_romfs_char)*2 && memcmp(entry->name,upperdir,sizeof(fs_romfs_char)*2) == 0) + if(entry->name_len == sizeof(fs_romfs_char)*2 && memcmp(entry->name,FS_PARENT_DIR_PATH,sizeof(fs_romfs_char)*2) == 0) return true; return false; @@ -159,84 +240,70 @@ int fs_AddDir(fs_entry *entry, fs_dir *dir) { fs_ManageDirSlot(dir); u32 current_slot = dir->u_child; + dir->u_child++; - return fs_OpenDir(entry->fs_name,entry->name,entry->name_len,&dir->child[current_slot]); + return fs_OpenDir(entry,&dir->child[current_slot]); } int fs_AddFile(fs_entry *entry, fs_dir *dir) { fs_ManageFileSlot(dir); + dir->file[dir->u_file].fs_path = fs_CopyPath(entry->fs_path); dir->file[dir->u_file].name_len = entry->name_len; - dir->file[dir->u_file].name = malloc(entry->name_len+2); - memset(dir->file[dir->u_file].name,0,entry->name_len+2); - memcpy(dir->file[dir->u_file].name,entry->name,entry->name_len); - + dir->file[dir->u_file].name = fs_CopyRomfsName(entry->name); dir->file[dir->u_file].size = entry->size; - dir->file[dir->u_file].fp = entry->fp; dir->u_file++; return 0; } -int fs_OpenDir(fs_char *fs_path, fs_romfs_char *path, u32 pathlen, fs_dir *dir) +int fs_OpenRootDir(const char *path, fs_dir *dir) +{ + fs_entry *root = calloc(1, sizeof(fs_entry)); + u32 nul; + + root->IsDir = true; + root->size = 0; + + str_u16_to_u16(&root->name, &root->name_len, ROMFS_EMPTY_PATH, 0); +#ifdef _WIN32 + str_u8_to_u16(&root->fs_path, &nul, (u8*)path, strlen(path)); +#else + str_u8_to_u8(&root->fs_path, &nul, (u8*)path, strlen(path)); +#endif + + + int ret = fs_OpenDir(root, dir); + + fs_FreeEntry(root); + + return ret; +} + +int fs_OpenDir(fs_entry *curr_dir_entry, fs_dir *dir) { //printf("init open dir\n"); int ret = 0; fs_DIR *dp; fs_entry *entry; + //printf("do some more init\n"); + fs_InitDir(curr_dir_entry, dir); + //wprintf(L" rec: \"%s\" (%d)\n",dir->name,dir->name_len); + //printf("check if path exists\n"); - dp = fs_opendir(fs_path); + dp = fs_opendir(dir->fs_path); if(!dp) { - //wprintf(L"[!] Failed to open directory: \"%s\"\n",path); + wprintf(L"[!] Failed to open directory: \"%s\"\n",dir->fs_path); return -1; } - //printf("do some more init\n"); - fs_InitDir(path,pathlen,dir); - //wprintf(L" rec: \"%s\" (%d)\n",dir->name,dir->name_len); - - //printf("chdir\n"); - fs_chdir(fs_path); - //printf("read entries\n"); - while((entry = fs_GetEntry(dp))) - { - if(!entry) - { - ret = -1; - break; - } - - if(entry->IsDir) - { - //printf("Found Dir "); - if(!fs_EntryIsDirNav(entry)) - { -#ifdef _WIN32 - //wprintf(L"is a dir: \"%s\" (%d)\n",entry->fs_name,entry->name_len); -#else - //printf("is a dir: \"%s\" (%d)\n",entry->fs_name,entry->name_len); -#endif - ret = fs_AddDir(entry,dir); - } - else - { - //printf("Not wanted dir\n"); - ret = 0; - } - } - else - { -#ifdef _WIN32 - //wprintf(L"is a file: \"%s\" (%d)\n",entry->fs_name,entry->name_len); -#else - //printf("is a file: \"%s\" (%d)\n",entry->fs_name,entry->name_len); -#endif - ret = fs_AddFile(entry,dir); - } - + while((entry = fs_GetEntry(dir->fs_path, dp)) != NULL) + { + ret = entry->IsDir? fs_AddDir(entry, dir) : fs_AddFile(entry, dir); + //printf("free entry\n"); fs_FreeEntry(entry); @@ -246,11 +313,7 @@ int fs_OpenDir(fs_char *fs_path, fs_romfs_char *path, u32 pathlen, fs_dir *dir) break; } } - //printf("close dir ptr\n"); fs_closedir(dp); - //printf("return up dir\n"); - fs_chdirUp(); - //printf("return from fs_OpenDir();\n"); return ret; } @@ -260,14 +323,11 @@ void fs_PrintDir(fs_dir *dir, u32 depth) // This is just for simple debugging, p for(u32 i = 0; i < depth; i++) printf(" "); -#ifdef _WIN32 - wprintf(L"%s\n",dir->name); -#else - char *name = (char*)dir->name; - for(u32 i = 0; i < dir->name_len; i+=2) - putchar(name[i]); + if (depth > 0) + fs_romfs_fputs(stdout, dir->name); + else + printf("romfs:"); putchar('\n'); -#endif if(dir->u_file) { @@ -275,15 +335,8 @@ void fs_PrintDir(fs_dir *dir, u32 depth) // This is just for simple debugging, p { for(u32 j = 0; j < depth+1; j++) printf(" "); - -#ifdef _WIN32 - wprintf(L"%s (0x%lx)\n",dir->file[i].name,dir->file[i].size); -#else - name = (char*)dir->file[i].name; - for(u32 j = 0; j < dir->file[i].name_len; j+=2) - putchar(name[j]); - printf(" (0x%llx)\n",dir->file[i].size); -#endif + fs_romfs_fputs(stdout, dir->file[i].name); + printf(" (0x%"PRIx64")\n", dir->file[i].size); } } if(dir->u_child) @@ -298,6 +351,7 @@ void fs_FreeDir(fs_dir *dir) //printf("DIR!! free file names\n"); for(u32 i = 0; i < dir->u_file; i++) { + free(dir->file[i].fs_path); free(dir->file[i].name); } //printf("free file struct\n"); @@ -307,22 +361,11 @@ void fs_FreeDir(fs_dir *dir) //printf("free dir names and\n"); for(u32 i = 0; i < dir->u_child; i++) { + free(dir->child[i].fs_path); free(dir->child[i].name); fs_FreeDir(&dir->child[i]); } //printf("free dir struct\n"); free(dir->child); -} - -void fs_FreeFiles(fs_dir *dir) -{ - for(u32 i = 0; i < dir->u_file; i++) - { - if(dir->file[i].fp) - fclose(dir->file[i].fp); - } - - for(u32 i = 0; i < dir->u_child; i++) - fs_FreeFiles(&dir->child[i]); } \ No newline at end of file diff --git a/makerom/dir.h b/makerom/dir.h index f7c6938d..aadd98f7 100644 --- a/makerom/dir.h +++ b/makerom/dir.h @@ -9,6 +9,7 @@ #define fs_chdir _wchdir #define fs_opendir _wopendir #define fs_closedir _wclosedir + #define FS_PATH_SEPARATOR '\\' #else #define fs_romfs_char u16 #define fs_char char @@ -18,29 +19,30 @@ #define fs_chdir chdir #define fs_opendir opendir #define fs_closedir closedir + #define FS_PATH_SEPARATOR '/' #endif struct fs_entry { bool IsDir; - fs_char *fs_name; + fs_char *fs_path; fs_romfs_char *name; u32 name_len; u64 size; - FILE *fp; }; struct fs_file { + fs_char *fs_path; fs_romfs_char *name; u32 name_len; u64 size; - FILE *fp; }; struct fs_dir { + fs_char *fs_path; fs_romfs_char *name; u32 name_len; @@ -57,9 +59,21 @@ typedef struct fs_entry fs_entry; typedef struct fs_file fs_file; typedef struct fs_dir fs_dir; -int fs_RomFsStrLen(fs_romfs_char *str); +static const fs_romfs_char ROMFS_EMPTY_PATH[2] = { 0 }; +static const fs_char FS_EMPTY_PATH[2] = { 0 }; -int fs_OpenDir(fs_char *fs_path, fs_romfs_char *path, u32 pathlen, fs_dir *dir); +u32 fs_romfs_strlen(const fs_romfs_char *str); +u32 fs_strlen(const fs_char *str); +FILE* fs_fopen(fs_char *path); +u64 fs_fsize(fs_char *path); + +fs_char* fs_AppendToPath(const fs_char *src, const fs_char *add); +fs_char* fs_CopyPath(const fs_char *src); +fs_romfs_char* fs_CopyRomfsName(const fs_romfs_char *src); +void fs_fputs(FILE *out, const fs_char *str); +void fs_romfs_fputs(FILE *out, const fs_romfs_char *str); + +int fs_OpenRootDir(const char *path, fs_dir *dir); +int fs_OpenDir(fs_entry *entry, fs_dir *dir); void fs_PrintDir(fs_dir *dir, u32 depth); -void fs_FreeDir(fs_dir *dir); -void fs_FreeFiles(fs_dir *dir); \ No newline at end of file +void fs_FreeDir(fs_dir *dir); \ No newline at end of file diff --git a/makerom/ncch.c b/makerom/ncch.c index 69621d46..384c9de5 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -536,12 +536,19 @@ int FinaliseNcch(ncch_settings *set) // Crypting Exheader/AcexDesc if(set->cryptoDetails.exhdrSize){ + if (set->options.verbose) + printf("[NCCH] Encypting Exheader... "); CryptNcchRegion(exhdr,set->cryptoDetails.exhdrSize,0x0,set->cryptoDetails.titleId,set->keys->aes.ncchKey0,ncch_exhdr); CryptNcchRegion(acexDesc,set->cryptoDetails.acexSize,set->cryptoDetails.exhdrSize,set->cryptoDetails.titleId,set->keys->aes.ncchKey0,ncch_exhdr); + if (set->options.verbose) + printf("Done!\n"); } // Crypting ExeFs Files if(set->cryptoDetails.exefsSize){ + if (set->options.verbose) + printf("[NCCH] Encrypting ExeFS... "); + exefs_hdr *exefsHdr = (exefs_hdr*)exefs; for(int i = 0; i < MAX_EXEFS_SECTIONS; i++){ u8 *key = NULL; @@ -559,11 +566,19 @@ int FinaliseNcch(ncch_settings *set) } // Crypting ExeFs Header CryptNcchRegion(exefs,sizeof(exefs_hdr),0x0,set->cryptoDetails.titleId,set->keys->aes.ncchKey0,ncch_exefs); + + if (set->options.verbose) + printf("Done!\n"); } // Crypting RomFs - if(set->cryptoDetails.romfsSize) - CryptNcchRegion(romfs,set->cryptoDetails.romfsSize,0x0,set->cryptoDetails.titleId,set->keys->aes.ncchKey1,ncch_romfs); + if (set->cryptoDetails.romfsSize) { + if (set->options.verbose) + printf("[NCCH] Encrypting RomFS... "); + CryptNcchRegion(romfs, set->cryptoDetails.romfsSize, 0x0, set->cryptoDetails.titleId, set->keys->aes.ncchKey1, ncch_romfs); + if (set->options.verbose) + printf("Done!\n"); + } } return 0; diff --git a/makerom/ncsd.c b/makerom/ncsd.c index 23d7dfe1..29e4f76e 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -615,6 +615,10 @@ int CheckRomConfig(cci_settings *set) void WriteCciDataToOutput(cci_settings *set) { + if (set->options.verbose) { + printf("[CCI] Writing header to file... "); + } + // NCSD Header WriteBuffer(set->headers.ccihdr.buffer, set->headers.ccihdr.size, 0, set->out); // Card Info Header @@ -629,18 +633,34 @@ void WriteCciDataToOutput(cci_settings *set) memset(dummy_data, 0xff, len); WriteBuffer(dummy_data, len, (set->headers.ccihdr.size + set->headers.cardinfohdr.size),set->out); free(dummy_data); + + if (set->options.verbose) { + printf("Done!\n"); + } // NCCH Partitions u8 *ncch; for(int i = 0; i < CCI_MAX_CONTENT; i++){ if(set->content.active[i]){ + if (set->options.verbose) { + printf("[CCI] Writing content %d to file... ", i); + } + ncch = set->content.data + set->content.dOffset[i]; WriteBuffer(ncch, set->content.dSize[i], set->content.cOffset[i], set->out); + + if (set->options.verbose) { + printf("Done!\n"); + } } } // Cci Padding if(set->options.padCci){ + if (set->options.verbose) { + printf("[CCI] Writing padding to file... "); + } + fseek_64(set->out,set->romInfo.usedSize); // Determining Size of Padding @@ -655,6 +675,10 @@ void WriteCciDataToOutput(cci_settings *set) fwrite(pad,set->romInfo.blockSize,1,set->out); free(pad); + + if (set->options.verbose) { + printf("Done!"); + } } return; diff --git a/makerom/romfs.c b/makerom/romfs.c index 9e3956a1..bf174fc8 100644 --- a/makerom/romfs.c +++ b/makerom/romfs.c @@ -10,6 +10,7 @@ void FreeRomFsCtx(romfs_buildctx *ctx); // RomFs Build Functions int SetupRomFs(ncch_settings *ncchset, romfs_buildctx *ctx) { + ctx->verbose = ncchset->options.verbose; ctx->output = NULL; ctx->romfsSize = 0; @@ -46,7 +47,6 @@ int BuildRomFs(romfs_buildctx *ctx) void FreeRomFsCtx(romfs_buildctx *ctx) { if(ctx->fs){ - fs_FreeFiles(ctx->fs); fs_FreeDir(ctx->fs); free(ctx->fs); } diff --git a/makerom/romfs.h b/makerom/romfs.h index 96ec1233..48d5400d 100644 --- a/makerom/romfs.h +++ b/makerom/romfs.h @@ -72,6 +72,8 @@ typedef struct typedef struct { + bool verbose; + u8 *output; u64 romfsSize; u64 romfsHeaderSize; diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 3db457bf..8eba46cc 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -5,7 +5,6 @@ const int ROMFS_BLOCK_SIZE = 0x1000; const unsigned int ROMFS_UNUSED_ENTRY = 0xffffffff; -const fs_romfs_char ROMFS_EMPTY_PATH[2] = {0x0000, 0x0000}; // Build bool IsFileWanted(fs_file *file, void *filter_criteria); @@ -21,60 +20,39 @@ void PopulateRomfs(romfs_buildctx *ctx); void BuildRomfsHeader(romfs_buildctx *ctx); void BuildIvfcHeader(romfs_buildctx *ctx); void GenIvfcHashTree(romfs_buildctx *ctx); -u32 CalcPathHash(u32 parent, fs_romfs_char* path, u32 start, u32 length); +u32 CalcPathHash(u32 parent, const fs_romfs_char* path, u32 start, u32 length); int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) { - /* Input Path */ - const int CWD_MAX_LEN = 1024; - char *cwd = calloc(CWD_MAX_LEN,sizeof(char)); - getcwd(cwd,CWD_MAX_LEN); - - char *dir = ncchset->rsfSet->RomFs.RootPath; - - fs_char *fs_path; - fs_romfs_char *path; - u32 path_len; -#ifdef _WIN32 - str_u8_to_u16(&path,&path_len,(u8*)dir,strlen(dir)); - fs_path = path; -#else - str_utf8_to_u16(&path,&path_len,(u8*)dir,strlen(dir)); - fs_path = dir; -#endif - /* FS Structures */ void *filter_criteria = NULL; fs_dir *fs_raw = calloc(1,sizeof(fs_dir)); ctx->fs = calloc(1,sizeof(fs_dir)); /* Import FS and process */ - fs_OpenDir(fs_path,path,path_len,fs_raw); + fs_OpenRootDir(ncchset->rsfSet->RomFs.RootPath,fs_raw); FilterRomFS(fs_raw,ctx->fs,filter_criteria); /* free unfiltered FS */ - fs_FreeFiles(fs_raw); // All important FPs have been moved with FilterRomFS, so only un-wanted FPs are closed here fs_FreeDir(fs_raw); free(fs_raw); /* Abort romfs making, if no wanted files/directories were found */ if(ctx->fs->u_file == 0 && ctx->fs->u_child == 0){ ctx->romfsSize = 0; - goto finish; + return 0; } + CalcRomfsSize(ctx); - /* Print Filtered FS */ - if(ncchset->options.verbose){ + if (ctx->verbose) { printf("[ROMFS] File System:\n"); - fs_PrintDir(ctx->fs,0); + printf(" > Size: %"PRIx64"\n", ctx->romfsSize); + printf(" > Directories: %d\n", ctx->dirNum); + printf(" > Files: %d\n", ctx->fileNum); } - - CalcRomfsSize(ctx); - -finish: - chdir(cwd); + return 0; } @@ -208,9 +186,10 @@ int FilterRomFS(fs_dir *fs_raw, fs_dir *fs_filtered, void *filter_criteria) if(!IsDirWanted(fs_raw,filter_criteria)) return 0; + fs_filtered->fs_path = fs_CopyPath(fs_raw->fs_path); + fs_filtered->name_len = fs_raw->name_len; - fs_filtered->name = calloc(fs_filtered->name_len+2,1); - memcpy(fs_filtered->name,fs_raw->name,fs_filtered->name_len); + fs_filtered->name = fs_CopyRomfsName(fs_raw->name); fs_filtered->u_child = 0; fs_filtered->m_child = fs_raw->u_child; @@ -233,16 +212,13 @@ int FilterRomFS(fs_dir *fs_raw, fs_dir *fs_filtered, void *filter_criteria) { if(IsFileWanted(&fs_raw->file[i],filter_criteria)) { + fs_filtered->file[fs_filtered->u_file].fs_path = fs_CopyPath(fs_raw->file[i].fs_path); + fs_filtered->file[fs_filtered->u_file].name_len = fs_raw->file[i].name_len; - fs_filtered->file[fs_filtered->u_file].name = malloc(fs_filtered->file[fs_filtered->u_file].name_len+2); - memset(fs_filtered->file[fs_filtered->u_file].name,0,fs_filtered->file[fs_filtered->u_file].name_len+2); - memcpy(fs_filtered->file[fs_filtered->u_file].name,fs_raw->file[i].name,fs_filtered->file[fs_filtered->u_file].name_len); + fs_filtered->file[fs_filtered->u_file].name = fs_CopyRomfsName(fs_raw->file[i].name); fs_filtered->file[fs_filtered->u_file].size = fs_raw->file[i].size; - fs_filtered->file[fs_filtered->u_file].fp = fs_raw->file[i].fp; - fs_raw->file[i].fp = NULL; - fs_filtered->u_file++; } } @@ -301,15 +277,15 @@ void BuildRomfsHeader(romfs_buildctx *ctx) } } -u32 GetFileHashTableIndex(romfs_buildctx *ctx, u32 parent, fs_romfs_char *path) +u32 GetFileHashTableIndex(romfs_buildctx *ctx, u32 parent, const fs_romfs_char *path) { - u32 hash = CalcPathHash(parent, path, 0, fs_RomFsStrLen(path)); + u32 hash = CalcPathHash(parent, path, 0, fs_romfs_strlen(path)); return hash % ctx->m_fileHashTable; } -u32 GetDirHashTableIndex(romfs_buildctx *ctx, u32 parent, fs_romfs_char* path) +u32 GetDirHashTableIndex(romfs_buildctx *ctx, u32 parent, const fs_romfs_char* path) { - u32 hash = CalcPathHash(parent, path, 0, fs_RomFsStrLen(path)); + u32 hash = CalcPathHash(parent, path, 0, fs_romfs_strlen(path)); return hash % ctx->m_dirHashTable; } @@ -338,7 +314,21 @@ void AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) u64_to_u8(entry->dataoffset,ctx->u_dataLen,LE); u64_to_u8(entry->datasize,file->size,LE); u8 *data_pos = (ctx->data + ctx->u_dataLen); - ReadFile64(data_pos,file->size,0,file->fp); + + if (ctx->verbose) { + printf("[ROMFS] Reading \""); + fs_fputs(stdout, file->fs_path); + printf("\"... "); + } + + FILE *fp = fs_fopen(file->fs_path); + fread(data_pos, file->size, 1, fp); + fclose(fp); + + if (ctx->verbose) { + printf("Done!\n"); + } + ctx->u_dataLen += file->size; // adding file size } else @@ -351,7 +341,6 @@ void AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) void AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) { u32 offset = ctx->u_dirTableLen; - u32 hashindex; romfs_direntry *entry = (romfs_direntry*)(ctx->dirTable + offset); /* Set entry data */ @@ -360,37 +349,19 @@ void AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) u32_to_u8(entry->childoffset, ROMFS_UNUSED_ENTRY, LE); u32_to_u8(entry->fileoffset, ROMFS_UNUSED_ENTRY, LE); - - /* If root dir ... */ - if(offset == 0) - { - /* Import name (root dir has no name) */ - u32_to_u8(entry->namesize,0,LE); - - /* Get hash table index */ - hashindex = GetDirHashTableIndex(ctx, parent, (fs_romfs_char*)ROMFS_EMPTY_PATH); - - /* Increment used dir table length */ - ctx->u_dirTableLen += sizeof(romfs_direntry); - } - else - { - /* Import name */ - u32_to_u8(entry->namesize,fs->name_len,LE); - u8 *name_pos = (u8*)(ctx->dirTable + ctx->u_dirTableLen + sizeof(romfs_direntry)); - memset(name_pos,0,(u32)align(fs->name_len,4)); - memcpy(name_pos,(u8*)fs->name,fs->name_len); - - /* Get hash table index */ - hashindex = GetDirHashTableIndex(ctx, parent, fs->name); - - /* Increment used dir table length */ - ctx->u_dirTableLen += sizeof(romfs_direntry) + (u32)align(fs->name_len,4); - } + /* Import name */ + u32_to_u8(entry->namesize,fs->name_len,LE); + u8 *name_pos = (u8*)(ctx->dirTable + ctx->u_dirTableLen + sizeof(romfs_direntry)); + memset(name_pos,0,(u32)align(fs->name_len,4)); + memcpy(name_pos,(u8*)fs->name,fs->name_len); /* Set hash data */ - u32_to_u8(entry->hashoffset, u8_to_u32(ctx->dirHashTable + hashindex*4, LE), LE); - u32_to_u8(ctx->dirHashTable + hashindex*4, offset, LE); + u32 hashindex = GetDirHashTableIndex(ctx, parent, fs->name); + u32_to_u8(entry->hashoffset, u8_to_u32(ctx->dirHashTable + hashindex * 4, LE), LE); + u32_to_u8(ctx->dirHashTable + hashindex * 4, offset, LE); + + /* Increment used dir table length */ + ctx->u_dirTableLen += sizeof(romfs_direntry) + (u32)align(fs->name_len,4); } void AddDirChildrenToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 dir) @@ -425,10 +396,10 @@ void AddDirChildrenToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 dir) u32_to_u8(entry->childoffset, ctx->u_dirTableLen, LE); for (u32 i = 0; i < fs->u_child; i++) { - /* Store address fo child */ + /* Store address for child */ childs[i] = ctx->u_dirTableLen; - + /* If is the last child directory, no more siblings */ u32 dir_sibling = 0; if (i >= fs->u_child - 1) dir_sibling = ROMFS_UNUSED_ENTRY; @@ -488,7 +459,7 @@ void GenIvfcHashTree(romfs_buildctx *ctx) return; } -u32 CalcPathHash(u32 parent, fs_romfs_char* path, u32 start, u32 length) +u32 CalcPathHash(u32 parent, const fs_romfs_char* path, u32 start, u32 length) { u32 hash = parent ^ 123456789; for( u32 index = 0; index < length; index++ ) diff --git a/makerom/titleid.c b/makerom/titleid.c index 9bf82174..33961a4e 100644 --- a/makerom/titleid.c +++ b/makerom/titleid.c @@ -2,6 +2,7 @@ #include "ncch_read.h" #include "titleid.h" +const u16 DEFAULT_CATEGORY = PROGRAM_ID_CATEGORY_APPLICATION; const u32 DEFAULT_UNIQUE_ID = 0xff3ff; void SetPIDType(u16 *type); @@ -28,7 +29,7 @@ u32 GetTidUniqueId(u64 titleId) int GetProgramID(u64 *dest, rsf_settings *rsf, bool IsForExheader) { - int ret; + int ret = 0; u32 uniqueId; u16 type,category; u8 variation; @@ -42,12 +43,15 @@ int GetProgramID(u64 *dest, rsf_settings *rsf, bool IsForExheader) SetPIDType(&type); // Getting Category - if(rsf->TitleInfo.Category) - ret = SetPIDCategoryFromName(&category,rsf->TitleInfo.Category); - else if(rsf->TitleInfo.CategoryFlags) - ret = SetPIDCategoryFromFlags(&category,rsf->TitleInfo.CategoryFlags,rsf->TitleInfo.CategoryFlagsNum); if(IsForExheader && rsf->TitleInfo.TargetCategory) ret = SetPIDCategoryFromName(&category,rsf->TitleInfo.TargetCategory); + else if (rsf->TitleInfo.Category) + ret = SetPIDCategoryFromName(&category, rsf->TitleInfo.Category); + else if (rsf->TitleInfo.CategoryFlags) + ret = SetPIDCategoryFromFlags(&category, rsf->TitleInfo.CategoryFlags, rsf->TitleInfo.CategoryFlagsNum); + else + category = DEFAULT_CATEGORY; + if(ret == PID_INVALID_CATEGORY) // Error occured return PID_BAD_RSF_SET; diff --git a/makerom/user_settings.c b/makerom/user_settings.c index aa5bc468..8bfe595a 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -6,6 +6,7 @@ void DisplayExtendedHelp(char *app_name); void SetDefaults(user_settings *set); int SetArgument(int argc, int i, char *argv[], user_settings *set); int CheckArgumentCombination(user_settings *set); +const char* GetOutputExtention(u8 file_type); void PrintNeedsArg(char *arg); void PrintArgInvalid(char *arg); void PrintArgReqParam(char *arg, u32 paramNum); @@ -75,14 +76,7 @@ int ParseArgs(int argc, char *argv[], user_settings *set) source_path = set->common.workingFilePath; else source_path = set->common.contentPath[0]; - u16 outfile_len = strlen(source_path) + 0x10; - set->common.outFileName = calloc(outfile_len, sizeof(char)); - if (!set->common.outFileName) { - fprintf(stderr, "[SETTING ERROR] Not Enough Memory\n"); - return USR_MEM_ERROR; - } - set->common.outFileName_mallocd = true; - append_filextention(set->common.outFileName, outfile_len, source_path, (char*)&output_extention[set->common.outFormat - 1]); + set->common.outFileName = replace_filextention(source_path, GetOutputExtention(set->common.outFormat)); } return 0; } @@ -95,7 +89,7 @@ void SetDefaults(user_settings *set) // Build NCCH Info set->ncch.useSecCrypto = false; - set->ncch.buildNcch0 = false; + set->ncch.buildNcch0 = true; set->ncch.includeExefsLogo = false; set->common.outFormat = NCCH; set->ncch.ncchType = format_not_set; @@ -704,22 +698,18 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) int CheckArgumentCombination(user_settings *set) { - if (set->ncch.ncchType & (CXI | CFA)) { + if (set->common.contentPath[0] == NULL) { set->ncch.buildNcch0 = true; if (set->ncch.ncchType & CXI) set->ncch.ncchType = CXI; else set->ncch.ncchType = CFA; - } - if (set->common.outFormat == NCCH) { - set->ncch.buildNcch0 = true; - if (set->ncch.ncchType) + if (set->common.outFormat == NCCH) set->common.outFormat = set->ncch.ncchType; - else { - set->ncch.ncchType = CFA; - set->common.outFormat = CFA; - } + } + else { + set->ncch.buildNcch0 = false; } for (int i = 0; i < CIA_MAX_CONTENT; i++) { @@ -733,11 +723,6 @@ int CheckArgumentCombination(user_settings *set) } } - if (set->common.contentPath[0] && set->ncch.buildNcch0) { - fprintf(stderr, "[SETTING ERROR] You cannot both import and build content 0\n"); - return USR_BAD_ARG; - } - if (set->common.outFormat == CIA && set->cci.cverDataPath) { fprintf(stderr, "[SETTING ERROR] You cannot use argument \"-cverinfo\" when generating a CIA\n"); return USR_BAD_ARG; @@ -808,6 +793,18 @@ int CheckArgumentCombination(user_settings *set) return 0; } +const char* GetOutputExtention(u8 file_type) +{ + switch (file_type) { + case(NCCH) : return ".app"; + case(CXI) : return ".cxi"; + case(CFA) : return ".cfa"; + case(CIA) : return ".cia"; + case(CCI) : return ".cci"; + default: return ".bin"; + } +} + void init_UserSettings(user_settings *set) { memset(set, 0, sizeof(user_settings)); diff --git a/makerom/utils.c b/makerom/utils.c index dd08733b..2899b770 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -53,27 +53,27 @@ u64 max64(u64 a, u64 b) } // Strings -int append_filextention(char *output, u16 max_outlen, char *input, char extention[]) +char* replace_filextention(const char *input, const char *new_ext) { - if(output == NULL || input == NULL){ - printf("[!] Memory Error\n"); - return Fail; - } - memset(output,0,max_outlen); - u16 extention_point = strlen(input)+1; - for(int i = strlen(input)-1; i > 0; i--){ - if(input[i] == '.'){ - extention_point = i; - break; - } + if(input == NULL || new_ext == NULL) + return NULL; + + char *new_name; + char *ext = strrchr(input, '.'); + + // If there is no existing extention, just append new_ext + if (!ext) { + new_name = calloc(strlen(input) + strlen(new_ext), 1); + sprintf(new_name, "%s%s", input, new_ext); } - if(extention_point+strlen(extention) >= max_outlen){ - printf("[!] Input File Name Too Large for Output buffer\n"); - return Fail; + else { + u32 size = ext - input; + new_name = calloc(size + strlen(new_ext), 1); + snprintf(new_name, size, "%s", input); + sprintf(new_name, "%s%s", new_name, new_ext); } - memcpy(output,input,extention_point); - sprintf(output,"%s%s",output,extention); - return 0; + + return new_name; } void memdump(FILE* fout, const char* prefix, const u8* data, u32 size) @@ -104,7 +104,7 @@ void memdump(FILE* fout, const char* prefix, const u8* data, u32 size) } } -int str_u8_to_u16(u16 **dst, u32 *dst_len, u8 *src, u32 src_len) +int str_u8_to_u16(u16 **dst, u32 *dst_len, const u8 *src, u32 src_len) { *dst_len = src_len*sizeof(u16); *dst = malloc((*dst_len)+sizeof(u16)); @@ -117,7 +117,7 @@ int str_u8_to_u16(u16 **dst, u32 *dst_len, u8 *src, u32 src_len) return 0; } -int str_u16_to_u16(u16 **dst, u32 *dst_len, u16 *src, u32 src_len) +int str_u16_to_u16(u16 **dst, u32 *dst_len, const u16 *src, u32 src_len) { *dst_len = src_len*sizeof(u16); *dst = malloc((*dst_len)+sizeof(u16)); @@ -130,7 +130,7 @@ int str_u16_to_u16(u16 **dst, u32 *dst_len, u16 *src, u32 src_len) return 0; } -int str_u32_to_u16(u16 **dst, u32 *dst_len, u32 *src, u32 src_len) +int str_u32_to_u16(u16 **dst, u32 *dst_len, const u32 *src, u32 src_len) { *dst_len = src_len*sizeof(u16); *dst = malloc((*dst_len)+sizeof(u16)); @@ -144,7 +144,7 @@ int str_u32_to_u16(u16 **dst, u32 *dst_len, u32 *src, u32 src_len) } #ifndef _WIN32 -int str_utf8_to_u16(u16 **dst, u32 *dst_len, u8 *src, u32 src_len) +int str_utf8_to_u16(u16 **dst, u32 *dst_len, const u8 *src, u32 src_len) { *dst_len = src_len*sizeof(u16); *dst = malloc((*dst_len)+sizeof(u16)); diff --git a/makerom/utils.h b/makerom/utils.h index 6d30d511..1a89100e 100644 --- a/makerom/utils.h +++ b/makerom/utils.h @@ -18,13 +18,13 @@ u64 min64(u64 a, u64 b); u64 max64(u64 a, u64 b); // Strings -void memdump(FILE* fout, const char* prefix, const u8* data, u32 size); -int append_filextention(char *output, u16 max_outlen, char *input, char extention[]); -int str_u8_to_u16(u16 **dst, u32 *dst_len, u8 *src, u32 src_len); -int str_u16_to_u16(u16 **dst, u32 *dst_len, u16 *src, u32 src_len); -int str_u32_to_u16(u16 **dst, u32 *dst_len, u32 *src, u32 src_len); +void memdump(FILE* fout, const char* prefix, const const u8* data, u32 size); +char* replace_filextention(const char *input, const char *extention); +int str_u8_to_u16(u16 **dst, u32 *dst_len, const u8 *src, u32 src_len); +int str_u16_to_u16(u16 **dst, u32 *dst_len, const u16 *src, u32 src_len); +int str_u32_to_u16(u16 **dst, u32 *dst_len, const u32 *src, u32 src_len); #ifndef _WIN32 -int str_utf8_to_u16(u16 **dst, u32 *dst_len, u8 *src, u32 src_len); +int str_utf8_to_u16(u16 **dst, u32 *dst_len, const u8 *src, u32 src_len); #endif // Base64 From 7270990f66edaa7e2b45ded7412cf757b4383b67 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 11 Nov 2015 23:24:12 +0800 Subject: [PATCH 118/317] [makerom] misc --- makerom/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/Makefile b/makerom/Makefile index d15d60f4..e68a81c4 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -5,7 +5,7 @@ OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) # Compiler Settings LIBS = -static-libgcc CXXFLAGS = -I. -CFLAGS = --std=c99 -Wall -Wno-unused-but-set-variable -Wno-unused-value -I. $(MAKEROM_BUILD_FLAGS) +CFLAGS = --std=c99 -O2 -flto -Wall -Wno-unused-but-set-variable -Wno-unused-value -I. $(MAKEROM_BUILD_FLAGS) CC = gcc CXX = g++ From 7c3b5faed6230e8c90fb0a87c35520ad9ecd65ba Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 12 Nov 2015 08:33:05 +0800 Subject: [PATCH 119/317] [makerom] misc --- makerom/user_settings.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 8bfe595a..c2655684 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -76,6 +76,7 @@ int ParseArgs(int argc, char *argv[], user_settings *set) source_path = set->common.workingFilePath; else source_path = set->common.contentPath[0]; + set->common.outFileName_mallocd = true; set->common.outFileName = replace_filextention(source_path, GetOutputExtention(set->common.outFormat)); } return 0; @@ -676,13 +677,11 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } val_pos = (val_pos + 1); name_len = (val_pos - 1 - name_pos); - set->dname.items[set->dname.u_items].name = malloc(name_len + 1); - memset(set->dname.items[set->dname.u_items].name, 0, name_len + 1); + set->dname.items[set->dname.u_items].name = calloc(name_len + 1, sizeof(char)); memcpy(set->dname.items[set->dname.u_items].name, name_pos, name_len); val_len = strlen(val_pos); - set->dname.items[set->dname.u_items].value = malloc(val_len + 1); - memset(set->dname.items[set->dname.u_items].value, 0, val_len + 1); + set->dname.items[set->dname.u_items].value = calloc(val_len + 1, sizeof(char)); memcpy(set->dname.items[set->dname.u_items].value, val_pos, val_len); set->dname.u_items++; From 0854cbe5d1bd94b8da4638ce1120780e6cfacff4 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 15 Nov 2015 03:19:53 +0800 Subject: [PATCH 120/317] [makerom] fixed romfs (for linux) --- makerom/dir.c | 343 ++++++++++++-------------------- makerom/dir.h | 61 +++--- makerom/makerom.vcxproj.filters | 12 +- makerom/romfs.c | 2 +- makerom/romfs.h | 2 +- makerom/romfs_gen.c | 95 ++++----- makerom/utils.c | 121 ++++++----- makerom/utils.h | 9 +- 8 files changed, 285 insertions(+), 360 deletions(-) diff --git a/makerom/dir.c b/makerom/dir.c index 097c44ec..7cb724d2 100644 --- a/makerom/dir.c +++ b/makerom/dir.c @@ -2,35 +2,36 @@ #include "dir.h" #include "utf.h" -const fs_romfs_char FS_CURRENT_DIR_PATH = 0x2E; -const fs_romfs_char FS_PARENT_DIR_PATH[2] = { 0x2E,0x2E }; - /* This is the FS interface for ROMFS generation */ /* Tested working on Windows/Linux/OSX */ -int fs_InitDir(const fs_entry *entry, fs_dir *dir); -int fs_ManageDirSlot(fs_dir *dir); -int fs_ManageFileSlot(fs_dir *dir); -void fs_chdirUp(void); -fs_entry* fs_GetEntry(const fs_char *parent_path, fs_DIR *dp); -void fs_FreeEntry(fs_entry *entry); -bool fs_EntryIsDirNav(fs_entry *entry); -int fs_AddDir(fs_entry *entry, fs_dir *dir); -int fs_AddFile(fs_entry *entry, fs_dir *dir); - -u32 fs_romfs_strlen(const fs_romfs_char *str) +int OpenDir(romfs_dir *dir); +int InitDir(romfs_dir *dir); +int ManageDir(romfs_dir *dir); + +u32 fs_strlen(const fs_char *str) { - u32 i; - for( i = 0; str[i] != 0x0; i++ ); - return i; +#ifdef _WIN32 + return strlen_char16(str); +#else + return strlen(str); +#endif } -u32 fs_strlen(const fs_char *str) +u32 romfs_strlen(const romfs_char *str) { - u32 i; - for (i = 0; str[i] != 0x0; i++); - return i; + return strlen_char16(str); } +int fs_strcmp(const fs_char *str1, const fs_char *str2) +{ +#ifdef _WIN32 + return wcscmp(str1, str2); +#else + return strcmp(str1, str2); +#endif +} + + FILE* fs_fopen(fs_char *path) { #ifdef _WIN32 @@ -67,264 +68,164 @@ fs_char* fs_AppendToPath(const fs_char *src, const fs_char *add) return new_path; } -fs_char* fs_CopyPath(const fs_char *src) +fs_char* fs_CopyStr(const fs_char *src) { - u32 src_len; - fs_char *new_path; - - src_len = fs_strlen(src); - new_path = calloc(src_len + 0x10, sizeof(fs_char)); - - for (u32 i = 0; i < src_len; i++) - new_path[i] = src[i]; - - return new_path; +#ifdef _WIN32 + return strcopy_16to16(src); +#else + return strcopy_8to8(src); +#endif } -fs_romfs_char* fs_CopyRomfsName(const fs_romfs_char *src) +romfs_char* romfs_CopyConvertStr(const fs_char *src) { - u32 src_len; - fs_romfs_char *new_path; - - src_len = fs_strlen(src); - new_path = calloc(src_len + 0x10, sizeof(fs_romfs_char)); +#ifdef _WIN32 + return strcopy_16to16(src); +#else + return strcopy_utf8to16(src); +#endif +} - for (u32 i = 0; i < src_len; i++) - new_path[i] = src[i]; - return new_path; +romfs_char* romfs_CopyStr(const romfs_char *src) +{ + return strcopy_16to16(src); } -void fs_fputs(FILE *out, const fs_char *str) +void fs_fputs(const fs_char *str, FILE *out) { #ifdef _WIN32 - wprintf(L"%s", str); + fwprintf(out,L"%s", str); #else - printf("%s", str); + fprintf(out,"%s", str); #endif } -void fs_romfs_fputs(FILE *out, const fs_romfs_char *str) +void romfs_fputs(const romfs_char *str, FILE *out) { #ifdef _WIN32 - wprintf(L"%s", str); + fwprintf(out,L"%s", str); #else const char *name = (const char*)str; - for (u32 i = 0; i < fs_romfs_strlen(str)*2; i += 2) - putchar(name[i]); + for (u32 i = 0; i < romfs_strlen(str)*2; i += 2) + fputc(name[i],out); #endif } -int fs_InitDir(const fs_entry *entry, fs_dir *dir) +int InitDir(romfs_dir *dir) { - dir->fs_path = fs_CopyPath(entry->fs_path); - - dir->name_len = entry->name_len; - dir->name = fs_CopyRomfsName(entry->name); - dir->m_child = 10; dir->u_child = 0; - dir->child = calloc(dir->m_child,sizeof(fs_dir)); + dir->child = calloc(dir->m_child,sizeof(romfs_dir)); dir->m_file = 10; dir->u_file = 0; - dir->file = calloc(dir->m_file,sizeof(fs_file)); - - return 0; -} + dir->file = calloc(dir->m_file,sizeof(romfs_file)); -int fs_ManageDirSlot(fs_dir *dir) -{ - if(dir->u_child >= dir->m_child) - { - dir->m_child *= 2; - fs_dir *tmp = calloc(dir->m_child,sizeof(fs_dir)); - memcpy(tmp,dir->child,sizeof(fs_dir)*dir->u_child); - free(dir->child); - dir->child = tmp; - } - return 0; -} - -int fs_ManageFileSlot(fs_dir *dir) -{ - if(dir->u_file >= dir->m_file) - { - dir->m_file *= 2; - fs_file *tmp = calloc(dir->m_file,sizeof(fs_file)); - memcpy(tmp,dir->file,sizeof(fs_file)*dir->u_file); - free(dir->file); - dir->file = tmp; - } + if (dir->child == NULL || dir->file == NULL) + return MEM_ERROR; + return 0; } -fs_entry* fs_GetEntry(const fs_char *parent_path, fs_DIR *dp) +int ManageDir(romfs_dir *dir) { - // Directory structs - struct fs_dirent *tmp_entry; - fs_DIR *tmp_dptr; - u32 namlen = 0; - - //printf("get api dir entry from dir ptr\n"); - tmp_entry = fs_readdir(dp); - - //printf("if null, return\n"); - if(!tmp_entry) - return NULL; - -#ifdef _WIN32 - namlen = tmp_entry->d_namlen; -#else - namlen = strlen(tmp_entry->d_name); -#endif - - //printf("allocate memory for entry\n"); - fs_entry *entry = malloc(sizeof(fs_entry)); - memset(entry,0,sizeof(fs_entry)); - - //Copy FS compatible Entry name - fs_char *fs_name = calloc(sizeof(fs_char)*(namlen+1),1); - memcpy(fs_name,tmp_entry->d_name,sizeof(fs_char)*namlen); - entry->fs_path = fs_AppendToPath(parent_path, fs_name); - - // Convert Entry name into RomFS u16 char (windows wchar_t, thanks Nintendo) -#if _WIN32 - str_u16_to_u16(&entry->name,&entry->name_len,tmp_entry->d_name,namlen); -#else - str_utf8_to_u16(&entry->name,&entry->name_len,(u8*)tmp_entry->d_name,namlen); -#endif - - //printf("get dir entry from dir ptr to check if dir\n"); - tmp_dptr = fs_opendir(entry->fs_path); - if(tmp_dptr) - { - //printf("is dir\n"); - fs_closedir(tmp_dptr); - entry->IsDir = true; - entry->size = 0; - } - else // Open file if it is a file - { - entry->IsDir = false; - entry->size = fs_fsize(entry->fs_path); + if (dir->u_child >= dir->m_child) { + dir->m_child = 2 * dir->u_child; + dir->child = realloc(dir->child, dir->m_child*sizeof(romfs_dir)); } - - // Don't bother returning current entry, if it is useless - if (fs_EntryIsDirNav(entry)) { - fs_FreeEntry(entry); - return fs_GetEntry(parent_path, dp); + if (dir->u_file >= dir->m_file) { + dir->m_file = 2 * dir->u_file; + dir->file = realloc(dir->file, dir->m_file*sizeof(romfs_file)); } - return entry; -} - -void fs_FreeEntry(fs_entry *entry) -{ - free(entry->fs_path); - free(entry->name); - free(entry); -} - -bool fs_EntryIsDirNav(fs_entry *entry) -{ - if(entry->name_len == sizeof(fs_romfs_char)*1 && memcmp(entry->name,&FS_CURRENT_DIR_PATH,sizeof(fs_romfs_char)*1) == 0) - return true; - if(entry->name_len == sizeof(fs_romfs_char)*2 && memcmp(entry->name,FS_PARENT_DIR_PATH,sizeof(fs_romfs_char)*2) == 0) - return true; - return false; - -} - -int fs_AddDir(fs_entry *entry, fs_dir *dir) -{ - fs_ManageDirSlot(dir); - u32 current_slot = dir->u_child; - - dir->u_child++; - return fs_OpenDir(entry,&dir->child[current_slot]); -} + if (dir->child == NULL || dir->file == NULL) + return MEM_ERROR; -int fs_AddFile(fs_entry *entry, fs_dir *dir) -{ - fs_ManageFileSlot(dir); - dir->file[dir->u_file].fs_path = fs_CopyPath(entry->fs_path); - dir->file[dir->u_file].name_len = entry->name_len; - dir->file[dir->u_file].name = fs_CopyRomfsName(entry->name); - dir->file[dir->u_file].size = entry->size; - - dir->u_file++; return 0; } -int fs_OpenRootDir(const char *path, fs_dir *dir) +int OpenRootDir(const char *path, romfs_dir *dir) { - fs_entry *root = calloc(1, sizeof(fs_entry)); - u32 nul; - - root->IsDir = true; - root->size = 0; - - str_u16_to_u16(&root->name, &root->name_len, ROMFS_EMPTY_PATH, 0); + // Create native FS path #ifdef _WIN32 - str_u8_to_u16(&root->fs_path, &nul, (u8*)path, strlen(path)); + dir->path = strcopy_8to16(path); #else - str_u8_to_u8(&root->fs_path, &nul, (u8*)path, strlen(path)); + dir->path = strcopy_8to8(path); #endif - - - int ret = fs_OpenDir(root, dir); - - fs_FreeEntry(root); - - return ret; + // Copy romfs name (empty string) + dir->name = romfs_CopyStr(ROMFS_EMPTY_PATH); + dir->namesize = 0; + + return OpenDir(dir); } -int fs_OpenDir(fs_entry *curr_dir_entry, fs_dir *dir) +int OpenDir(romfs_dir *dir) { - //printf("init open dir\n"); - int ret = 0; - fs_DIR *dp; - fs_entry *entry; - - //printf("do some more init\n"); - fs_InitDir(curr_dir_entry, dir); - //wprintf(L" rec: \"%s\" (%d)\n",dir->name,dir->name_len); + fs_DIR *dp, *tmp_dp; + struct fs_dirent *entry; - //printf("check if path exists\n"); - dp = fs_opendir(dir->fs_path); - if(!dp) + if (InitDir(dir)) + return MEM_ERROR; + + // Open Directory + if((dp = fs_opendir(dir->path)) == NULL) { - wprintf(L"[!] Failed to open directory: \"%s\"\n",dir->fs_path); + printf("[ROMFS] Failed to open directory: \""); + fs_fputs(dir->path, stdout); + printf("\"\n"); return -1; } - //printf("read entries\n"); - while((entry = fs_GetEntry(dir->fs_path, dp)) != NULL) - { - ret = entry->IsDir? fs_AddDir(entry, dir) : fs_AddFile(entry, dir); + // Process Entries + while ((entry = fs_readdir(dp)) != NULL) + { + // Skip if "." or ".." + if (fs_strcmp(entry->d_name, FS_CURRENT_DIR_PATH) == 0 || fs_strcmp(entry->d_name, FS_PARENT_DIR_PATH) == 0) + continue; + + // Ensures that there is always memory for child directory and file structs + if (ManageDir(dir)) + return MEM_ERROR; - //printf("free entry\n"); - fs_FreeEntry(entry); + // Get native FS path + fs_char *path = fs_AppendToPath(dir->path, entry->d_name); - if(ret) - { - //printf("error parsing entry\n"); - break; + // Opening directory with fs path to test if directory + if ((tmp_dp = fs_opendir(path)) != NULL) { + fs_closedir(tmp_dp); + + dir->child[dir->u_child].path = path; + dir->child[dir->u_child].name = romfs_CopyConvertStr(entry->d_name); + dir->child[dir->u_child].namesize = fs_strlen(entry->d_name)*sizeof(romfs_char); + dir->u_child++; + + // Populate directory + OpenDir(&dir->child[dir->u_child-1]); + } + // Otherwise this is a file + else { + dir->file[dir->u_file].path = path; + dir->file[dir->u_file].name = romfs_CopyConvertStr(entry->d_name); + dir->file[dir->u_file].namesize = fs_strlen(entry->d_name)*sizeof(romfs_char); + dir->file[dir->u_file].size = fs_fsize(path); + dir->u_file++; } } + fs_closedir(dp); - return ret; + + return 0; } -void fs_PrintDir(fs_dir *dir, u32 depth) // This is just for simple debugging, please don't shoot me +void PrintDir(romfs_dir *dir, u32 depth) { for(u32 i = 0; i < depth; i++) printf(" "); if (depth > 0) - fs_romfs_fputs(stdout, dir->name); + romfs_fputs(dir->name, stdout); else printf("romfs:"); putchar('\n'); @@ -335,23 +236,23 @@ void fs_PrintDir(fs_dir *dir, u32 depth) // This is just for simple debugging, p { for(u32 j = 0; j < depth+1; j++) printf(" "); - fs_romfs_fputs(stdout, dir->file[i].name); + romfs_fputs(dir->file[i].name, stdout); printf(" (0x%"PRIx64")\n", dir->file[i].size); } } if(dir->u_child) { for(u32 i = 0; i < dir->u_child; i++) - fs_PrintDir(&dir->child[i],depth+1); + PrintDir(&dir->child[i],depth+1); } } -void fs_FreeDir(fs_dir *dir) +void FreeDir(romfs_dir *dir) { //printf("DIR!! free file names\n"); for(u32 i = 0; i < dir->u_file; i++) { - free(dir->file[i].fs_path); + free(dir->file[i].path); free(dir->file[i].name); } //printf("free file struct\n"); @@ -361,9 +262,9 @@ void fs_FreeDir(fs_dir *dir) //printf("free dir names and\n"); for(u32 i = 0; i < dir->u_child; i++) { - free(dir->child[i].fs_path); + free(dir->child[i].path); free(dir->child[i].name); - fs_FreeDir(&dir->child[i]); + FreeDir(&dir->child[i]); } //printf("free dir struct\n"); free(dir->child); diff --git a/makerom/dir.h b/makerom/dir.h index aadd98f7..0826f21d 100644 --- a/makerom/dir.h +++ b/makerom/dir.h @@ -1,7 +1,7 @@ #pragma once #ifdef _WIN32 - #define fs_romfs_char u16 + #define romfs_char u16 #define fs_char wchar_t #define fs_dirent _wdirent #define fs_DIR _WDIR @@ -11,7 +11,7 @@ #define fs_closedir _wclosedir #define FS_PATH_SEPARATOR '\\' #else - #define fs_romfs_char u16 + #define romfs_char u16 #define fs_char char #define fs_dirent dirent #define fs_DIR DIR @@ -21,59 +21,50 @@ #define fs_closedir closedir #define FS_PATH_SEPARATOR '/' #endif - - -struct fs_entry -{ - bool IsDir; - fs_char *fs_path; - fs_romfs_char *name; - u32 name_len; - u64 size; -}; -struct fs_file +struct romfs_file { - fs_char *fs_path; - fs_romfs_char *name; - u32 name_len; + fs_char *path; + romfs_char *name; + u32 namesize; u64 size; }; -struct fs_dir +struct romfs_dir { - fs_char *fs_path; - fs_romfs_char *name; - u32 name_len; + fs_char *path; + romfs_char *name; + u32 namesize; - struct fs_dir *child; + struct romfs_dir *child; u32 m_child; u32 u_child; - struct fs_file *file; + struct romfs_file *file; u32 m_file; u32 u_file; }; -typedef struct fs_entry fs_entry; -typedef struct fs_file fs_file; -typedef struct fs_dir fs_dir; +typedef struct romfs_file romfs_file; +typedef struct romfs_dir romfs_dir; -static const fs_romfs_char ROMFS_EMPTY_PATH[2] = { 0 }; +static const romfs_char ROMFS_EMPTY_PATH[2] = { 0 }; static const fs_char FS_EMPTY_PATH[2] = { 0 }; +static const fs_char FS_CURRENT_DIR_PATH[2] = { '.' }; +static const fs_char FS_PARENT_DIR_PATH[3] = { '.', '.' }; -u32 fs_romfs_strlen(const fs_romfs_char *str); +u32 romfs_strlen(const romfs_char *str); u32 fs_strlen(const fs_char *str); +int fs_strcmp(const fs_char *str1, const fs_char *str2); FILE* fs_fopen(fs_char *path); u64 fs_fsize(fs_char *path); fs_char* fs_AppendToPath(const fs_char *src, const fs_char *add); -fs_char* fs_CopyPath(const fs_char *src); -fs_romfs_char* fs_CopyRomfsName(const fs_romfs_char *src); -void fs_fputs(FILE *out, const fs_char *str); -void fs_romfs_fputs(FILE *out, const fs_romfs_char *str); +fs_char* fs_CopyStr(const fs_char *src); +romfs_char* romfs_CopyStr(const romfs_char *src); +void fs_fputs(const fs_char *str, FILE *out); +void romfs_fputs(const romfs_char *str, FILE *out); -int fs_OpenRootDir(const char *path, fs_dir *dir); -int fs_OpenDir(fs_entry *entry, fs_dir *dir); -void fs_PrintDir(fs_dir *dir, u32 depth); -void fs_FreeDir(fs_dir *dir); \ No newline at end of file +int OpenRootDir(const char *path, romfs_dir *dir); +void PrintDir(romfs_dir *dir, u32 depth); +void FreeDir(romfs_dir *dir); \ No newline at end of file diff --git a/makerom/makerom.vcxproj.filters b/makerom/makerom.vcxproj.filters index 049b9f80..a4f1f217 100644 --- a/makerom/makerom.vcxproj.filters +++ b/makerom/makerom.vcxproj.filters @@ -66,9 +66,6 @@ Header Files - - Header Files - Header Files @@ -339,6 +336,9 @@ Header Files + + Header Files + @@ -362,9 +362,6 @@ Source Files - - Source Files - Source Files @@ -473,6 +470,9 @@ Source Files + + Source Files + diff --git a/makerom/romfs.c b/makerom/romfs.c index bf174fc8..205db6f2 100644 --- a/makerom/romfs.c +++ b/makerom/romfs.c @@ -47,7 +47,7 @@ int BuildRomFs(romfs_buildctx *ctx) void FreeRomFsCtx(romfs_buildctx *ctx) { if(ctx->fs){ - fs_FreeDir(ctx->fs); + FreeDir(ctx->fs); free(ctx->fs); } } \ No newline at end of file diff --git a/makerom/romfs.h b/makerom/romfs.h index 48d5400d..e170b180 100644 --- a/makerom/romfs.h +++ b/makerom/romfs.h @@ -86,7 +86,7 @@ typedef struct ivfc_hdr *ivfcHdr; romfs_infoheader *romfsHdr; - fs_dir *fs; + romfs_dir *fs; u8 *dirHashTable; u32 m_dirHashTable; diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 8eba46cc..bfa1371d 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -7,35 +7,36 @@ const int ROMFS_BLOCK_SIZE = 0x1000; const unsigned int ROMFS_UNUSED_ENTRY = 0xffffffff; // Build -bool IsFileWanted(fs_file *file, void *filter_criteria); -bool IsDirWanted(fs_dir *dir, void *filter_criteria); -void CalcDirSize(romfs_buildctx *ctx, fs_dir *fs); +bool IsFileWanted(romfs_file *file, void *filter_criteria); +bool IsDirWanted(romfs_dir *dir, void *filter_criteria); +void CalcDirSize(romfs_buildctx *ctx, romfs_dir *fs); void CalcRomfsSize(romfs_buildctx *ctx); -int FilterRomFS(fs_dir *fs_raw, fs_dir *fs_filtered, void *filter_criteria); -void AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling); -void AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling); -void AddDirChildrenToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 dir); +int FilterRomFS(romfs_dir *fs_raw, romfs_dir *fs_filtered, void *filter_criteria); +void AddFileToRomfs(romfs_buildctx *ctx, romfs_file *file, u32 parent, u32 sibling); +void AddDirToRomfs(romfs_buildctx *ctx, romfs_dir *fs, u32 parent, u32 sibling); +void AddDirChildrenToRomfs(romfs_buildctx *ctx, romfs_dir *fs, u32 parent, u32 dir); void PopulateHashTable(romfs_buildctx *ctx); void PopulateRomfs(romfs_buildctx *ctx); void BuildRomfsHeader(romfs_buildctx *ctx); void BuildIvfcHeader(romfs_buildctx *ctx); void GenIvfcHashTree(romfs_buildctx *ctx); -u32 CalcPathHash(u32 parent, const fs_romfs_char* path, u32 start, u32 length); +u32 CalcPathHash(u32 parent, const romfs_char* path, u32 start, u32 length); int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) { /* FS Structures */ void *filter_criteria = NULL; - fs_dir *fs_raw = calloc(1,sizeof(fs_dir)); - ctx->fs = calloc(1,sizeof(fs_dir)); + romfs_dir *fs_raw = calloc(1,sizeof(romfs_dir)); + ctx->fs = calloc(1,sizeof(romfs_dir)); /* Import FS and process */ - fs_OpenRootDir(ncchset->rsfSet->RomFs.RootPath,fs_raw); + OpenRootDir(ncchset->rsfSet->RomFs.RootPath,fs_raw); FilterRomFS(fs_raw,ctx->fs,filter_criteria); + /* free unfiltered FS */ - fs_FreeDir(fs_raw); + FreeDir(fs_raw); free(fs_raw); /* Abort romfs making, if no wanted files/directories were found */ @@ -94,12 +95,12 @@ int BuildRomFsBinary(romfs_buildctx *ctx) } -bool IsFileWanted(fs_file *file, void *filter_criteria) +bool IsFileWanted(romfs_file *file, void *filter_criteria) { return true; } -bool IsDirWanted(fs_dir *dir, void *filter_criteria) +bool IsDirWanted(romfs_dir *dir, void *filter_criteria) { bool ret = false; for(u32 i = 0; i < dir->u_file; i++) @@ -121,16 +122,16 @@ bool IsDirWanted(fs_dir *dir, void *filter_criteria) return ret; } -void CalcDirSize(romfs_buildctx *ctx, fs_dir *fs) +void CalcDirSize(romfs_buildctx *ctx, romfs_dir *fs) { if(ctx->m_dirTableLen == 0) ctx->m_dirTableLen = sizeof(romfs_direntry); else - ctx->m_dirTableLen += sizeof(romfs_direntry) + align(fs->name_len,4); + ctx->m_dirTableLen += sizeof(romfs_direntry) + align(fs->namesize,4); for(u32 i = 0; i < fs->u_file; i++) { - ctx->m_fileTableLen += sizeof(romfs_fileentry) + align(fs->file[i].name_len,4); + ctx->m_fileTableLen += sizeof(romfs_fileentry) + align(fs->file[i].namesize,4); if(fs->file[i].size) ctx->m_dataLen = align(ctx->m_dataLen,0x10) + fs->file[i].size; } @@ -180,24 +181,24 @@ void CalcRomfsSize(romfs_buildctx *ctx) ctx->romfsSize += align(ctx->level[i].size,ROMFS_BLOCK_SIZE); } -int FilterRomFS(fs_dir *fs_raw, fs_dir *fs_filtered, void *filter_criteria) +int FilterRomFS(romfs_dir *fs_raw, romfs_dir *fs_filtered, void *filter_criteria) { - memset(fs_filtered,0,sizeof(fs_dir)); + memset(fs_filtered,0,sizeof(romfs_dir)); if(!IsDirWanted(fs_raw,filter_criteria)) return 0; - fs_filtered->fs_path = fs_CopyPath(fs_raw->fs_path); + fs_filtered->path = fs_CopyStr(fs_raw->path); - fs_filtered->name_len = fs_raw->name_len; - fs_filtered->name = fs_CopyRomfsName(fs_raw->name); + fs_filtered->namesize = fs_raw->namesize; + fs_filtered->name = romfs_CopyStr(fs_raw->name); fs_filtered->u_child = 0; fs_filtered->m_child = fs_raw->u_child; - fs_filtered->child = calloc(fs_filtered->m_child,sizeof(fs_dir)); + fs_filtered->child = calloc(fs_filtered->m_child,sizeof(romfs_dir)); fs_filtered->u_file = 0; fs_filtered->m_file = fs_raw->u_file; - fs_filtered->file = calloc(fs_filtered->m_file,sizeof(fs_file)); + fs_filtered->file = calloc(fs_filtered->m_file,sizeof(romfs_file)); for(u32 i = 0; i < fs_raw->u_child; i++) { @@ -212,10 +213,10 @@ int FilterRomFS(fs_dir *fs_raw, fs_dir *fs_filtered, void *filter_criteria) { if(IsFileWanted(&fs_raw->file[i],filter_criteria)) { - fs_filtered->file[fs_filtered->u_file].fs_path = fs_CopyPath(fs_raw->file[i].fs_path); + fs_filtered->file[fs_filtered->u_file].path = fs_CopyStr(fs_raw->file[i].path); - fs_filtered->file[fs_filtered->u_file].name_len = fs_raw->file[i].name_len; - fs_filtered->file[fs_filtered->u_file].name = fs_CopyRomfsName(fs_raw->file[i].name); + fs_filtered->file[fs_filtered->u_file].namesize = fs_raw->file[i].namesize; + fs_filtered->file[fs_filtered->u_file].name = romfs_CopyStr(fs_raw->file[i].name); fs_filtered->file[fs_filtered->u_file].size = fs_raw->file[i].size; @@ -277,19 +278,19 @@ void BuildRomfsHeader(romfs_buildctx *ctx) } } -u32 GetFileHashTableIndex(romfs_buildctx *ctx, u32 parent, const fs_romfs_char *path) +u32 GetFileHashTableIndex(romfs_buildctx *ctx, u32 parent, const romfs_char *path) { - u32 hash = CalcPathHash(parent, path, 0, fs_romfs_strlen(path)); + u32 hash = CalcPathHash(parent, path, 0, romfs_strlen(path)); return hash % ctx->m_fileHashTable; } -u32 GetDirHashTableIndex(romfs_buildctx *ctx, u32 parent, const fs_romfs_char* path) +u32 GetDirHashTableIndex(romfs_buildctx *ctx, u32 parent, const romfs_char* path) { - u32 hash = CalcPathHash(parent, path, 0, fs_romfs_strlen(path)); + u32 hash = CalcPathHash(parent, path, 0, romfs_strlen(path)); return hash % ctx->m_dirHashTable; } -void AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) +void AddFileToRomfs(romfs_buildctx *ctx, romfs_file *file, u32 parent, u32 sibling) { romfs_fileentry *entry = (romfs_fileentry*)(ctx->fileTable + ctx->u_fileTableLen); @@ -297,10 +298,10 @@ void AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) u32_to_u8(entry->siblingoffset,sibling,LE); /* Import name */ - u32_to_u8(entry->namesize,file->name_len,LE); + u32_to_u8(entry->namesize,file->namesize,LE); u8 *name_pos = (u8*)(ctx->fileTable + ctx->u_fileTableLen + sizeof(romfs_fileentry)); - memset(name_pos,0,align(file->name_len,4)); - memcpy(name_pos,(u8*)file->name,file->name_len); + memset(name_pos,0,align(file->namesize,4)); + memcpy(name_pos,(u8*)file->name,file->namesize); /* Set hash data */ u32 hashindex = GetFileHashTableIndex(ctx, parent, file->name); @@ -317,11 +318,11 @@ void AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) if (ctx->verbose) { printf("[ROMFS] Reading \""); - fs_fputs(stdout, file->fs_path); + fs_fputs(file->path, stdout); printf("\"... "); } - FILE *fp = fs_fopen(file->fs_path); + FILE *fp = fs_fopen(file->path); fread(data_pos, file->size, 1, fp); fclose(fp); @@ -335,10 +336,10 @@ void AddFileToRomfs(romfs_buildctx *ctx, fs_file *file, u32 parent, u32 sibling) u64_to_u8(entry->dataoffset,0x00,LE); /* Increment used file table length */ - ctx->u_fileTableLen += sizeof(romfs_fileentry) + align(file->name_len,4); + ctx->u_fileTableLen += sizeof(romfs_fileentry) + align(file->namesize,4); } -void AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) +void AddDirToRomfs(romfs_buildctx *ctx, romfs_dir *fs, u32 parent, u32 sibling) { u32 offset = ctx->u_dirTableLen; romfs_direntry *entry = (romfs_direntry*)(ctx->dirTable + offset); @@ -350,10 +351,10 @@ void AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) u32_to_u8(entry->fileoffset, ROMFS_UNUSED_ENTRY, LE); /* Import name */ - u32_to_u8(entry->namesize,fs->name_len,LE); + u32_to_u8(entry->namesize,fs->namesize,LE); u8 *name_pos = (u8*)(ctx->dirTable + ctx->u_dirTableLen + sizeof(romfs_direntry)); - memset(name_pos,0,(u32)align(fs->name_len,4)); - memcpy(name_pos,(u8*)fs->name,fs->name_len); + memset(name_pos,0,(u32)align(fs->namesize,4)); + memcpy(name_pos,(u8*)fs->name,fs->namesize); /* Set hash data */ u32 hashindex = GetDirHashTableIndex(ctx, parent, fs->name); @@ -361,10 +362,10 @@ void AddDirToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 sibling) u32_to_u8(ctx->dirHashTable + hashindex * 4, offset, LE); /* Increment used dir table length */ - ctx->u_dirTableLen += sizeof(romfs_direntry) + (u32)align(fs->name_len,4); + ctx->u_dirTableLen += sizeof(romfs_direntry) + (u32)align(fs->namesize,4); } -void AddDirChildrenToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 dir) +void AddDirChildrenToRomfs(romfs_buildctx *ctx, romfs_dir *fs, u32 parent, u32 dir) { romfs_direntry *entry = (romfs_direntry*)(ctx->dirTable + dir); @@ -380,7 +381,7 @@ void AddDirChildrenToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 dir) if (i >= fs->u_file - 1) file_sibling = ROMFS_UNUSED_ENTRY; else - file_sibling = ctx->u_fileTableLen + sizeof(romfs_fileentry) + (u32)align(fs->file[i].name_len, 4); + file_sibling = ctx->u_fileTableLen + sizeof(romfs_fileentry) + (u32)align(fs->file[i].namesize, 4); /* Create file entry */ AddFileToRomfs(ctx, &fs->file[i], dir, file_sibling); @@ -404,7 +405,7 @@ void AddDirChildrenToRomfs(romfs_buildctx *ctx, fs_dir *fs, u32 parent, u32 dir) if (i >= fs->u_child - 1) dir_sibling = ROMFS_UNUSED_ENTRY; else - dir_sibling = ctx->u_dirTableLen + sizeof(romfs_direntry) + (u32)align(fs->child[i].name_len, 4); + dir_sibling = ctx->u_dirTableLen + sizeof(romfs_direntry) + (u32)align(fs->child[i].namesize, 4); /* Create child directory entry */ AddDirToRomfs(ctx, &fs->child[i], dir, dir_sibling); @@ -459,7 +460,7 @@ void GenIvfcHashTree(romfs_buildctx *ctx) return; } -u32 CalcPathHash(u32 parent, const fs_romfs_char* path, u32 start, u32 length) +u32 CalcPathHash(u32 parent, const romfs_char* path, u32 start, u32 length) { u32 hash = parent ^ 123456789; for( u32 index = 0; index < length; index++ ) diff --git a/makerom/utils.c b/makerom/utils.c index 2899b770..f552c5f0 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -68,8 +68,8 @@ char* replace_filextention(const char *input, const char *new_ext) } else { u32 size = ext - input; - new_name = calloc(size + strlen(new_ext), 1); - snprintf(new_name, size, "%s", input); + new_name = calloc(size + strlen(new_ext) + 1, 1); + strncpy(new_name, input, size); sprintf(new_name, "%s%s", new_name, new_ext); } @@ -104,61 +104,92 @@ void memdump(FILE* fout, const char* prefix, const u8* data, u32 size) } } -int str_u8_to_u16(u16 **dst, u32 *dst_len, const u8 *src, u32 src_len) +u32 strlen_char16(const u16 *str) { - *dst_len = src_len*sizeof(u16); - *dst = malloc((*dst_len)+sizeof(u16)); - if(*dst == NULL) - return -1; - memset(*dst,0,(*dst_len)+sizeof(u16)); - u16 *tmp = *dst; - for(int i=0; i Date: Sun, 15 Nov 2015 03:26:25 +0800 Subject: [PATCH 121/317] [makerom] renamed dir.c/dir.h to romfs_fs.c/romfs_fs.h --- makerom/makerom.vcxproj | 4 ++-- makerom/makerom.vcxproj.filters | 4 ++-- makerom/ncch.c | 1 - makerom/romfs.c | 1 - makerom/romfs.h | 1 + makerom/{dir.c => romfs_fs.c} | 2 +- makerom/{dir.h => romfs_fs.h} | 0 makerom/romfs_gen.c | 1 - 8 files changed, 6 insertions(+), 8 deletions(-) rename makerom/{dir.c => romfs_fs.c} (99%) rename makerom/{dir.h => romfs_fs.h} (100%) diff --git a/makerom/makerom.vcxproj b/makerom/makerom.vcxproj index ecc931c2..195b75f9 100644 --- a/makerom/makerom.vcxproj +++ b/makerom/makerom.vcxproj @@ -104,7 +104,7 @@ - + @@ -201,7 +201,7 @@ - + diff --git a/makerom/makerom.vcxproj.filters b/makerom/makerom.vcxproj.filters index a4f1f217..b0c347c1 100644 --- a/makerom/makerom.vcxproj.filters +++ b/makerom/makerom.vcxproj.filters @@ -336,7 +336,7 @@ Header Files - + Header Files @@ -470,7 +470,7 @@ Source Files - + Source Files diff --git a/makerom/ncch.c b/makerom/ncch.c index 384c9de5..7182f539 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -1,5 +1,4 @@ #include "lib.h" -#include "dir.h" #include "ncch_build.h" #include "exheader_build.h" #include "exheader_read.h" diff --git a/makerom/romfs.c b/makerom/romfs.c index 205db6f2..7ab6ba17 100644 --- a/makerom/romfs.c +++ b/makerom/romfs.c @@ -1,5 +1,4 @@ #include "lib.h" -#include "dir.h" #include "ncch_build.h" #include "romfs.h" #include "romfs_gen.h" diff --git a/makerom/romfs.h b/makerom/romfs.h index e170b180..eacac3a8 100644 --- a/makerom/romfs.h +++ b/makerom/romfs.h @@ -1,4 +1,5 @@ #pragma once +#include "romfs_fs.h" typedef enum { diff --git a/makerom/dir.c b/makerom/romfs_fs.c similarity index 99% rename from makerom/dir.c rename to makerom/romfs_fs.c index 7cb724d2..8c21e3ad 100644 --- a/makerom/dir.c +++ b/makerom/romfs_fs.c @@ -1,5 +1,5 @@ #include "lib.h" -#include "dir.h" +#include "romfs_fs.h" #include "utf.h" /* This is the FS interface for ROMFS generation */ diff --git a/makerom/dir.h b/makerom/romfs_fs.h similarity index 100% rename from makerom/dir.h rename to makerom/romfs_fs.h diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index bfa1371d..5bed2204 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -1,5 +1,4 @@ #include "lib.h" -#include "dir.h" #include "ncch_build.h" #include "romfs.h" From f8e3b4723162ca9194889b2e2c57003a1d724fea Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 15 Nov 2015 03:41:47 +0800 Subject: [PATCH 122/317] [makerom] misc --- makerom/romfs_gen.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 5bed2204..74cdaa90 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -19,7 +19,7 @@ void PopulateRomfs(romfs_buildctx *ctx); void BuildRomfsHeader(romfs_buildctx *ctx); void BuildIvfcHeader(romfs_buildctx *ctx); void GenIvfcHashTree(romfs_buildctx *ctx); -u32 CalcPathHash(u32 parent, const romfs_char* path, u32 start, u32 length); +u32 CalcPathHash(u32 parent, const romfs_char* path); int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) @@ -279,13 +279,13 @@ void BuildRomfsHeader(romfs_buildctx *ctx) u32 GetFileHashTableIndex(romfs_buildctx *ctx, u32 parent, const romfs_char *path) { - u32 hash = CalcPathHash(parent, path, 0, romfs_strlen(path)); + u32 hash = CalcPathHash(parent, path); return hash % ctx->m_fileHashTable; } u32 GetDirHashTableIndex(romfs_buildctx *ctx, u32 parent, const romfs_char* path) { - u32 hash = CalcPathHash(parent, path, 0, romfs_strlen(path)); + u32 hash = CalcPathHash(parent, path); return hash % ctx->m_dirHashTable; } @@ -459,13 +459,14 @@ void GenIvfcHashTree(romfs_buildctx *ctx) return; } -u32 CalcPathHash(u32 parent, const romfs_char* path, u32 start, u32 length) +u32 CalcPathHash(u32 parent, const romfs_char* path) { + u32 len = romfs_strlen(path); u32 hash = parent ^ 123456789; - for( u32 index = 0; index < length; index++ ) + for( u32 i = 0; i < len; i++ ) { hash = (u32)((hash >> 5) | (hash << 27));//ror - hash ^= (u16)path[start + index]; + hash ^= (u16)path[i]; } return hash; } From 4e6cbfbd8003128e304ee6626fbe509477c98913 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 15 Nov 2015 03:53:01 +0800 Subject: [PATCH 123/317] [makerom] made function name more accurate. --- makerom/romfs_fs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/makerom/romfs_fs.c b/makerom/romfs_fs.c index 8c21e3ad..4c092652 100644 --- a/makerom/romfs_fs.c +++ b/makerom/romfs_fs.c @@ -4,7 +4,7 @@ /* This is the FS interface for ROMFS generation */ /* Tested working on Windows/Linux/OSX */ -int OpenDir(romfs_dir *dir); +int PopulateDir(romfs_dir *dir); int InitDir(romfs_dir *dir); int ManageDir(romfs_dir *dir); @@ -157,10 +157,10 @@ int OpenRootDir(const char *path, romfs_dir *dir) dir->name = romfs_CopyStr(ROMFS_EMPTY_PATH); dir->namesize = 0; - return OpenDir(dir); + return PopulateDir(dir); } -int OpenDir(romfs_dir *dir) +int PopulateDir(romfs_dir *dir) { fs_DIR *dp, *tmp_dp; struct fs_dirent *entry; @@ -201,7 +201,7 @@ int OpenDir(romfs_dir *dir) dir->u_child++; // Populate directory - OpenDir(&dir->child[dir->u_child-1]); + PopulateDir(&dir->child[dir->u_child-1]); } // Otherwise this is a file else { From bb10fd8ab3eebb55d1bdeb051e6822d4dd2d2f33 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 15 Nov 2015 05:25:41 +0800 Subject: [PATCH 124/317] [makerom] Changed ReadFile64 & WriteBuffer to read/write data in blocks. (Works around fread/fwrite buffer size limitations) --- makerom/utils.c | 22 +++++++++++++++------- makerom/utils.h | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/makerom/utils.c b/makerom/utils.c index f552c5f0..849126cc 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -3,6 +3,8 @@ #include "polarssl/base64.h" +#define IO_BLOCKSIZE 5*MB + // Memory int CopyData(u8 **dest, const u8 *source, u64 size) { @@ -370,22 +372,28 @@ u8* ImportFile(char *file, u64 size) return NULL; } FILE *fp = fopen(file,"rb"); - fread(data,fsize,1,fp); + ReadFile64(data, fsize, 0, fp); fclose(fp); return data; } -void WriteBuffer(void *buffer, u64 size, u64 offset, FILE *output) +void WriteBuffer(const void *buffer, u64 size, u64 offset, FILE *fp) { - fseek_64(output,offset); - fwrite(buffer,size,1,output); + const u8* _buffer = (const u8*)buffer; + fseek_64(fp,offset); + for (; size > IO_BLOCKSIZE; size -= IO_BLOCKSIZE, _buffer += IO_BLOCKSIZE) + fwrite(_buffer, IO_BLOCKSIZE, 1, fp); + fwrite(_buffer,size,1,fp); } -void ReadFile64(void *outbuff, u64 size, u64 offset, FILE *file) +void ReadFile64(void *outbuff, u64 size, u64 offset, FILE *fp) { - fseek_64(file,offset); - fread(outbuff,size,1,file); + u8* _buffer = (u8*)outbuff; + fseek_64(fp, offset); + for (; size > IO_BLOCKSIZE; size -= IO_BLOCKSIZE, _buffer += IO_BLOCKSIZE) + fread(_buffer, IO_BLOCKSIZE, 1, fp); + fread(_buffer, size, 1, fp); } int fseek_64(FILE *fp, u64 file_pos) diff --git a/makerom/utils.h b/makerom/utils.h index b46d0a49..e21a8af4 100644 --- a/makerom/utils.h +++ b/makerom/utils.h @@ -55,7 +55,7 @@ u64 wGetFileSize64(u16 *filename); //IO Misc u8* ImportFile(char *file, u64 size); -void WriteBuffer(void *buffer, u64 size, u64 offset, FILE *output); +void WriteBuffer(const void *buffer, u64 size, u64 offset, FILE *output); void ReadFile64(void *outbuff, u64 size, u64 offset, FILE *file); int fseek_64(FILE *fp, u64 file_pos); From 2dd838b693f0b0c13540b858cff1c09c35479b35 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 15 Nov 2015 05:26:25 +0800 Subject: [PATCH 125/317] [makerom] Added IVFC hash generation to verbose output. --- makerom/romfs_gen.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 74cdaa90..9d8a97e7 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -448,12 +448,16 @@ void BuildIvfcHeader(romfs_buildctx *ctx) void GenIvfcHashTree(romfs_buildctx *ctx) { for(int i = 2; i >= 0; i--){ + if (ctx->verbose) + printf("[ROMFS] Generating IVFC level %d hashes... ", i+1); u32 numHashes = align(ctx->level[i+1].size,ROMFS_BLOCK_SIZE) / ROMFS_BLOCK_SIZE; for(u32 j = 0; j < numHashes; j++){ u8 *datapos = (u8*)(ctx->level[i+1].pos + ROMFS_BLOCK_SIZE * j); u8 *hashpos = (u8*)(ctx->level[i].pos + SHA_256_LEN * j); ShaCalc(datapos, ROMFS_BLOCK_SIZE, hashpos, CTR_SHA_256); } + if (ctx->verbose) + printf("Done!\n"); } return; From 7e04a8249e423e80bde2137a7b5a7578561166c4 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 20 Nov 2015 03:10:00 +0800 Subject: [PATCH 126/317] [makerom] Fixed size limitation on NCCH encryption. --- makerom/crypto.c | 2 +- makerom/makerom.vcxproj | 2 ++ makerom/makerom.vcxproj.filters | 6 ++++++ makerom/ncch.c | 22 +++++++++++----------- makerom/polarssl/aes.c | 6 +++--- makerom/polarssl/aes.h | 6 +++--- makerom/romfs_import.c | 4 ++-- makerom/types.h | 16 ++++++++-------- 8 files changed, 36 insertions(+), 28 deletions(-) diff --git a/makerom/crypto.c b/makerom/crypto.c index e83b9f71..f654efe2 100644 --- a/makerom/crypto.c +++ b/makerom/crypto.c @@ -35,7 +35,7 @@ void AesCtrCrypt(u8 *key, u8 *ctr, u8 *input, u8 *output, u64 length, u64 offset { u8 stream[16]; aes_context aes; - u64 nc_off = 0; + size_t nc_off = 0; clrmem(&aes,sizeof(aes_context)); aes_setkey_enc(&aes, key, 128); diff --git a/makerom/makerom.vcxproj b/makerom/makerom.vcxproj index 195b75f9..7ec49cdc 100644 --- a/makerom/makerom.vcxproj +++ b/makerom/makerom.vcxproj @@ -201,6 +201,8 @@ + + diff --git a/makerom/makerom.vcxproj.filters b/makerom/makerom.vcxproj.filters index b0c347c1..6603f558 100644 --- a/makerom/makerom.vcxproj.filters +++ b/makerom/makerom.vcxproj.filters @@ -473,6 +473,12 @@ Source Files + + Source Files\polarssl + + + Source Files\polarssl + diff --git a/makerom/ncch.c b/makerom/ncch.c index 7182f539..ecfd7eea 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -1053,7 +1053,7 @@ int GetNcchInfo(ncch_info *info, ncch_hdr *hdr) info->titleId = u8_to_u64(hdr->titleId,LE); info->programId = u8_to_u64(hdr->programId,LE); - u32 block_size = GetNcchBlockSize(hdr); + u64 block_size = GetNcchBlockSize(hdr); info->formatVersion = u8_to_u16(hdr->formatVersion,LE); if(!IsCfa(hdr)){ @@ -1061,18 +1061,18 @@ int GetNcchInfo(ncch_info *info, ncch_hdr *hdr) info->exhdrSize = u8_to_u32(hdr->exhdrSize,LE); info->acexOffset = (info->exhdrOffset + info->exhdrSize); info->acexSize = sizeof(access_descriptor); - info->plainRegionOffset = (u64)(u8_to_u32(hdr->plainRegionOffset,LE)*block_size); - info->plainRegionSize = (u64)(u8_to_u32(hdr->plainRegionSize,LE)*block_size); + info->plainRegionOffset = ((u64)u8_to_u32(hdr->plainRegionOffset,LE))*block_size; + info->plainRegionSize = ((u64)u8_to_u32(hdr->plainRegionSize,LE))*block_size; } - info->logoOffset = (u64)(u8_to_u32(hdr->logoOffset,LE)*block_size); - info->logoSize = (u64)(u8_to_u32(hdr->logoSize,LE)*block_size); - info->exefsOffset = (u64)(u8_to_u32(hdr->exefsOffset,LE)*block_size); - info->exefsSize = (u64)(u8_to_u32(hdr->exefsSize,LE)*block_size); - info->exefsHashDataSize = (u64)(u8_to_u32(hdr->exefsHashSize,LE)*block_size); - info->romfsOffset = (u64) (u8_to_u32(hdr->romfsOffset,LE)*block_size); - info->romfsSize = (u64) (u8_to_u32(hdr->romfsSize,LE)*block_size); - info->romfsHashDataSize = (u64)(u8_to_u32(hdr->romfsHashSize,LE)*block_size); + info->logoOffset = ((u64)u8_to_u32(hdr->logoOffset,LE))*block_size; + info->logoSize = ((u64)u8_to_u32(hdr->logoSize,LE))*block_size; + info->exefsOffset = ((u64)u8_to_u32(hdr->exefsOffset,LE))*block_size; + info->exefsSize = ((u64)u8_to_u32(hdr->exefsSize,LE))*block_size; + info->exefsHashDataSize = ((u64)u8_to_u32(hdr->exefsHashSize,LE))*block_size; + info->romfsOffset = ((u64)u8_to_u32(hdr->romfsOffset,LE))*block_size; + info->romfsSize = ((u64)u8_to_u32(hdr->romfsSize,LE))*block_size; + info->romfsHashDataSize = ((u64)u8_to_u32(hdr->romfsHashSize,LE))*block_size; return 0; } diff --git a/makerom/polarssl/aes.c b/makerom/polarssl/aes.c index 6456c54d..8d8ebb63 100644 --- a/makerom/polarssl/aes.c +++ b/makerom/polarssl/aes.c @@ -839,14 +839,14 @@ int aes_crypt_cbc( aes_context *ctx, */ int aes_crypt_cfb128( aes_context *ctx, int mode, - size_t length, + uint64_t length, size_t *iv_off, unsigned char iv[16], const unsigned char *input, unsigned char *output ) { int c; - size_t n = *iv_off; + size_t n = *iv_off; if( mode == AES_DECRYPT ) { @@ -886,7 +886,7 @@ int aes_crypt_cfb128( aes_context *ctx, * AES-CTR buffer encryption/decryption */ int aes_crypt_ctr( aes_context *ctx, - size_t length, + uint64_t length, size_t *nc_off, unsigned char nonce_counter[16], unsigned char stream_block[16], diff --git a/makerom/polarssl/aes.h b/makerom/polarssl/aes.h index 0a4519cf..469bef32 100644 --- a/makerom/polarssl/aes.h +++ b/makerom/polarssl/aes.h @@ -116,7 +116,7 @@ int aes_crypt_ecb( aes_context *ctx, */ int aes_crypt_cbc( aes_context *ctx, int mode, - size_t length, + uint64_t length, unsigned char iv[16], const unsigned char *input, unsigned char *output ); @@ -141,7 +141,7 @@ int aes_crypt_cbc( aes_context *ctx, */ int aes_crypt_cfb128( aes_context *ctx, int mode, - size_t length, + uint64_t length, size_t *iv_off, unsigned char iv[16], const unsigned char *input, @@ -169,7 +169,7 @@ int aes_crypt_cfb128( aes_context *ctx, * \return 0 if successful */ int aes_crypt_ctr( aes_context *ctx, - size_t length, + uint64_t length, size_t *nc_off, unsigned char nonce_counter[16], unsigned char stream_block[16], diff --git a/makerom/romfs_import.c b/makerom/romfs_import.c index 48d68f64..37762bfb 100644 --- a/makerom/romfs_import.c +++ b/makerom/romfs_import.c @@ -1,5 +1,5 @@ #include "lib.h" -#include "dir.h" +#include "romfs_fs.h" #include "ncch_build.h" #include "romfs.h" @@ -30,4 +30,4 @@ int ImportRomFsBinaryFromFile(romfs_buildctx *ctx) return INVALID_ROMFS_FILE; } return 0; -} \ No newline at end of file +} diff --git a/makerom/types.h b/makerom/types.h index c12d2427..4f8696ff 100644 --- a/makerom/types.h +++ b/makerom/types.h @@ -36,12 +36,12 @@ typedef enum MAX_U64 = 0xffffffffffffffff, } data_type_max; -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long long u64; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; -typedef signed char s8; -typedef signed short s16; -typedef signed int s32; -typedef signed long long s64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; From 127d1161e54863f2b8218843909069a54165306a Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 20 Nov 2015 03:13:31 +0800 Subject: [PATCH 127/317] [ctrtool] Fixed issue #7 --- ctrtool/cia.c | 41 +++++++++--------- ctrtool/cia.h | 22 +++++----- ctrtool/ctr.c | 37 ++++++---------- ctrtool/ctr.h | 2 +- ctrtool/cwav.c | 14 +++--- ctrtool/cwav.h | 8 ++-- ctrtool/exefs.c | 12 +++--- ctrtool/exefs.h | 8 ++-- ctrtool/exheader.c | 6 +-- ctrtool/exheader.h | 8 ++-- ctrtool/firm.c | 8 ++-- ctrtool/firm.h | 4 +- ctrtool/ivfc.c | 41 ++++++++---------- ctrtool/ivfc.h | 13 +++--- ctrtool/lzss.c | 2 +- ctrtool/main.c | 14 ++---- ctrtool/ncch.c | 105 +++++++++++++++++++++------------------------ ctrtool/ncch.h | 28 ++++++------ ctrtool/ncsd.c | 8 ++-- ctrtool/ncsd.h | 10 ++--- ctrtool/romfs.c | 23 ++++------ ctrtool/romfs.h | 8 ++-- ctrtool/tik.c | 4 +- ctrtool/tik.h | 4 +- ctrtool/tmd.c | 4 +- ctrtool/tmd.h | 4 +- ctrtool/types.h | 17 ++++---- ctrtool/utils.c | 33 +++++++++----- ctrtool/utils.h | 6 +++ 29 files changed, 241 insertions(+), 253 deletions(-) diff --git a/ctrtool/cia.c b/ctrtool/cia.c index c0963a54..91333bde 100644 --- a/ctrtool/cia.c +++ b/ctrtool/cia.c @@ -20,12 +20,12 @@ void cia_set_file(cia_context* ctx, FILE* file) ctx->file = file; } -void cia_set_offset(cia_context* ctx, u32 offset) +void cia_set_offset(cia_context* ctx, u64 offset) { ctx->offset = offset; } -void cia_set_size(cia_context* ctx, u32 size) +void cia_set_size(cia_context* ctx, u64 size) { ctx->size = size; } @@ -39,8 +39,8 @@ void cia_set_usersettings(cia_context* ctx, settings* usersettings) void cia_save(cia_context* ctx, u32 type, u32 flags) { - u32 offset; - u32 size; + u64 offset; + u64 size; u16 contentflags; u8 docrypto; filepath* path = 0; @@ -133,14 +133,13 @@ void cia_save(cia_context* ctx, u32 type, u32 flags) 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) +void cia_save_blob(cia_context *ctx, char *out_path, u64 offset, u64 size, int do_cbc) { FILE *fout = 0; u8 buffer[16*1024]; - fseek(ctx->file, ctx->offset + offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); - fout = fopen(out_path, "wb"); if (fout == NULL) { @@ -180,7 +179,7 @@ void cia_save_blob(cia_context *ctx, char *out_path, u32 offset, u32 size, int d void cia_process(cia_context* ctx, u32 actions) { - fseek(ctx->file, 0, SEEK_SET); + fseeko64(ctx->file, 0, SEEK_SET); if (fread(&ctx->header, 1, sizeof(ctr_ciaheader), ctx->file) != sizeof(ctr_ciaheader)) { @@ -192,14 +191,14 @@ void cia_process(cia_context* ctx, u32 actions) 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->sizecontent = 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); + ctx->offsetmeta = align64(ctx->offsetcontent + ctx->sizecontent, 64); if (actions & InfoFlag) cia_print(ctx); @@ -260,7 +259,7 @@ void cia_verify_contents(cia_context *ctx, u32 actions) 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); + fseeko64(ctx->file, ctx->offset + ctx->offsetcontent, SEEK_SET); for(i = 0; i < getbe16(body->contentcount); i++) { content_size = getbe64(chunk->size) & 0xffffffff; @@ -298,14 +297,14 @@ void cia_print(cia_context* ctx) 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%016"PRIx64"\n", getle64(header->contentsize)); + fprintf(stdout, "Certificates offset: 0x%"PRIx64"\n", ctx->offsetcerts); + fprintf(stdout, "Certificates size: 0x%x\n", ctx->sizecert); + fprintf(stdout, "Ticket offset: 0x%"PRIx64"n", ctx->offsettik); + fprintf(stdout, "Ticket size 0x%x\n", ctx->sizetik); + fprintf(stdout, "TMD offset: 0x%"PRIx64"\n", ctx->offsettmd); + fprintf(stdout, "TMD size: 0x%x\n", ctx->sizetmd); + fprintf(stdout, "Meta offset: 0x%"PRIx64"\n", ctx->offsetmeta); + fprintf(stdout, "Meta size: 0x%x\n", ctx->sizemeta); + fprintf(stdout, "Content offset: 0x%"PRIx64"\n", ctx->offsetcontent); + fprintf(stdout, "Content size: 0x%"PRIx64"\n", ctx->sizecontent); } diff --git a/ctrtool/cia.h b/ctrtool/cia.h index 531c7c18..52b01521 100644 --- a/ctrtool/cia.h +++ b/ctrtool/cia.h @@ -33,8 +33,8 @@ typedef struct typedef struct { FILE* file; - u32 offset; - u32 size; + u64 offset; + u64 size; u8 titlekey[16]; u8 iv[16]; ctr_ciaheader header; @@ -48,25 +48,25 @@ typedef struct u32 sizecert; u32 sizetik; u32 sizetmd; - u32 sizecontent; + u64 sizecontent; u32 sizemeta; - u32 offsetcerts; - u32 offsettik; - u32 offsettmd; - u32 offsetcontent; - u32 offsetmeta; + u64 offsetcerts; + u64 offsettik; + u64 offsettmd; + u64 offsetcontent; + u64 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_offset(cia_context* ctx, u64 offset); +void cia_set_size(cia_context* ctx, u64 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_save_blob(cia_context *ctx, char *out_path, u64 offset, u64 size, int do_cbc); void cia_verify_contents(cia_context *ctx, u32 actions); #endif // _CIA_H_ diff --git a/ctrtool/ctr.c b/ctrtool/ctr.c index 417d03fe..6948bf8f 100644 --- a/ctrtool/ctr.c +++ b/ctrtool/ctr.c @@ -13,33 +13,24 @@ void ctr_set_iv( ctr_aes_context* ctx, } void ctr_add_counter( ctr_aes_context* ctx, - u32 carry ) + u32 block_num ) { - 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; + u32 i, j; + for (i = 0; i < block_num; i++) { + for (j = 0x10; j > 0; j--) { + // increment u8 by 1 + ctx->ctr[j - 1]++; - if (sum < counter[i]) - carry = 1; - else - carry = 0; + // if it didn't overflow to 0, then we can exit now + if (ctx->ctr[j - 1]) + break; - counter[i] = sum; - } + // if we reach here, the next u8 needs to be incremented - 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; + // Loop to beginning back if needed + if (j == 1) + j = 0x10; + } } } diff --git a/ctrtool/ctr.h b/ctrtool/ctr.h index 93210517..66caff7b 100644 --- a/ctrtool/ctr.h +++ b/ctrtool/ctr.h @@ -56,7 +56,7 @@ void ctr_set_iv( ctr_aes_context* ctx, u8 iv[16] ); void ctr_add_counter( ctr_aes_context* ctx, - u32 carry ); + u32 block_num ); void ctr_set_counter( ctr_aes_context* ctx, u8 ctr[16] ); diff --git a/ctrtool/cwav.c b/ctrtool/cwav.c index e532db0a..4c1242d7 100644 --- a/ctrtool/cwav.c +++ b/ctrtool/cwav.c @@ -37,12 +37,12 @@ void cwav_set_file(cwav_context* ctx, FILE* file) ctx->file = file; } -void cwav_set_offset(cwav_context* ctx, u32 offset) +void cwav_set_offset(cwav_context* ctx, u64 offset) { ctx->offset = offset; } -void cwav_set_size(cwav_context* ctx, u32 size) +void cwav_set_size(cwav_context* ctx, u64 size) { ctx->size = size; } @@ -57,12 +57,12 @@ void cwav_process(cwav_context* ctx, u32 actions) u32 i; u32 infoheaderoffset; - fseek(ctx->file, ctx->offset, SEEK_SET); + fseeko64(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); + fseeko64(ctx->file, ctx->offset + infoheaderoffset, SEEK_SET); fread(&ctx->infoheader, 1, sizeof(cwav_infoheader), ctx->file); ctx->channelcount = getle32(ctx->infoheader.channelcount); @@ -79,7 +79,7 @@ void cwav_process(cwav_context* ctx, u32 actions) { u32 channeloffset = infoheaderoffset + 0x1C + getle32(ctx->channel[i].inforef.offset); - fseek(ctx->file, ctx->offset + channeloffset, SEEK_SET); + fseeko64(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) @@ -88,7 +88,7 @@ void cwav_process(cwav_context* ctx, u32 actions) { u32 codecoffset = channeloffset + getle32(ctx->channel[i].info.codecref.offset); - fseek(ctx->file, ctx->offset + codecoffset, SEEK_SET); + fseeko64(ctx->file, ctx->offset + codecoffset, SEEK_SET); fread(&ctx->channel[i].infodspadpcm, sizeof(cwav_dspadpcminfo), 1, ctx->file); } } @@ -98,7 +98,7 @@ void cwav_process(cwav_context* ctx, u32 actions) { u32 codecoffset = channeloffset + getle32(ctx->channel[i].info.codecref.offset); - fseek(ctx->file, ctx->offset + codecoffset, SEEK_SET); + fseeko64(ctx->file, ctx->offset + codecoffset, SEEK_SET); fread(&ctx->channel[i].infoimaadpcm, sizeof(cwav_imaadpcminfo), 1, ctx->file); } } diff --git a/ctrtool/cwav.h b/ctrtool/cwav.h index e75f22cb..180f4b00 100644 --- a/ctrtool/cwav.h +++ b/ctrtool/cwav.h @@ -165,8 +165,8 @@ typedef struct { FILE* file; settings* usersettings; - u32 offset; - u32 size; + u64 offset; + u64 size; u32 channelcount; cwav_header header; cwav_infoheader infoheader; @@ -175,8 +175,8 @@ typedef struct 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_offset(cwav_context* ctx, u64 offset); +void cwav_set_size(cwav_context* ctx, u64 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); diff --git a/ctrtool/exefs.c b/ctrtool/exefs.c index 78070710..36180438 100644 --- a/ctrtool/exefs.c +++ b/ctrtool/exefs.c @@ -18,12 +18,12 @@ void exefs_set_file(exefs_context* ctx, FILE* file) ctx->file = file; } -void exefs_set_offset(exefs_context* ctx, u32 offset) +void exefs_set_offset(exefs_context* ctx, u64 offset) { ctx->offset = offset; } -void exefs_set_size(exefs_context* ctx, u32 size) +void exefs_set_size(exefs_context* ctx, u64 size) { ctx->size = size; } @@ -122,10 +122,8 @@ void exefs_save(exefs_context* ctx, u32 index, u32 flags) fprintf(stderr, "Error, failed to create file %s\n", outfname); goto clean; } - - - fseek(ctx->file, ctx->offset + offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); ctr_add_counter(&ctx->aes, offset / 0x10); @@ -209,7 +207,7 @@ void exefs_save(exefs_context* ctx, u32 index, u32 flags) void exefs_read_header(exefs_context* ctx, u32 flags) { - fseek(ctx->file, ctx->offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset, SEEK_SET); fread(&ctx->header, 1, sizeof(exefs_header), ctx->file); ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); @@ -270,7 +268,7 @@ int exefs_verify(exefs_context* ctx, u32 index, u32 flags) if (size == 0) return 0; - fseek(ctx->file, ctx->offset + offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); ctr_add_counter(&ctx->aes, offset / 0x10); diff --git a/ctrtool/exefs.h b/ctrtool/exefs.h index a0624783..28afffb6 100644 --- a/ctrtool/exefs.h +++ b/ctrtool/exefs.h @@ -30,8 +30,8 @@ typedef struct u8 partitionid[8]; u8 counter[16]; u8 key[16]; - u32 offset; - u32 size; + u64 offset; + u64 size; exefs_header header; ctr_aes_context aes; ctr_sha256_context sha; @@ -42,8 +42,8 @@ typedef struct 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_offset(exefs_context* ctx, u64 offset); +void exefs_set_size(exefs_context* ctx, u64 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]); diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index a7e16b16..9715616f 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -17,12 +17,12 @@ void exheader_set_file(exheader_context* ctx, FILE* file) ctx->file = file; } -void exheader_set_offset(exheader_context* ctx, u32 offset) +void exheader_set_offset(exheader_context* ctx, u64 offset) { ctx->offset = offset; } -void exheader_set_size(exheader_context* ctx, u32 size) +void exheader_set_size(exheader_context* ctx, u64 size) { ctx->size = size; } @@ -88,7 +88,7 @@ void exheader_read(exheader_context* ctx, u32 actions) { if (ctx->haveread == 0) { - fseek(ctx->file, ctx->offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset, SEEK_SET); fread(&ctx->header, 1, sizeof(exheader_header), ctx->file); ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); diff --git a/ctrtool/exheader.h b/ctrtool/exheader.h index 63bb1690..9691e0f1 100644 --- a/ctrtool/exheader.h +++ b/ctrtool/exheader.h @@ -154,8 +154,8 @@ typedef struct u8 hash[32]; u8 counter[16]; u8 key[16]; - u32 offset; - u32 size; + u64 offset; + u64 size; exheader_header header; exheader_arm11systemlocalcaps_deserialised system_local_caps; @@ -181,8 +181,8 @@ typedef struct 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_offset(exheader_context* ctx, u64 offset); +void exheader_set_size(exheader_context* ctx, u64 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]); diff --git a/ctrtool/firm.c b/ctrtool/firm.c index 5c3d07d7..e865cc0e 100644 --- a/ctrtool/firm.c +++ b/ctrtool/firm.c @@ -16,7 +16,7 @@ void firm_set_file(firm_context* ctx, FILE* file) ctx->file = file; } -void firm_set_offset(firm_context* ctx, u32 offset) +void firm_set_offset(firm_context* ctx, u64 offset) { ctx->offset = offset; } @@ -66,7 +66,7 @@ void firm_save(firm_context* ctx, u32 index, u32 flags) - fseek(ctx->file, ctx->offset + offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); fprintf(stdout, "Saving section %d to %s...\n", index, outpath.pathname); while(size) @@ -100,7 +100,7 @@ void firm_process(firm_context* ctx, u32 actions) { u32 i; - fseek(ctx->file, ctx->offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset, SEEK_SET); fread(&ctx->header, 1, sizeof(firm_header), ctx->file); if (getle32(ctx->header.magic) != MAGIC_FIRM) @@ -154,7 +154,7 @@ int firm_verify(firm_context* ctx, u32 flags) if (size == 0) return 0; - fseek(ctx->file, ctx->offset + offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); ctr_sha_256_init(&ctx->sha); diff --git a/ctrtool/firm.h b/ctrtool/firm.h index c663f71b..29af98f4 100644 --- a/ctrtool/firm.h +++ b/ctrtool/firm.h @@ -35,7 +35,7 @@ typedef struct { FILE* file; settings* usersettings; - u32 offset; + u64 offset; u32 size; firm_header header; ctr_sha256_context sha; @@ -45,7 +45,7 @@ typedef struct 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_offset(firm_context* ctx, u64 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); diff --git a/ctrtool/ivfc.c b/ctrtool/ivfc.c index a4796ac3..7fbb5fc8 100644 --- a/ctrtool/ivfc.c +++ b/ctrtool/ivfc.c @@ -17,12 +17,12 @@ void ivfc_set_usersettings(ivfc_context* ctx, settings* usersettings) ctx->usersettings = usersettings; } -void ivfc_set_offset(ivfc_context* ctx, u32 offset) +void ivfc_set_offset(ivfc_context* ctx, u64 offset) { ctx->offset = offset; } -void ivfc_set_size(ivfc_context* ctx, u32 size) +void ivfc_set_size(ivfc_context* ctx, u64 size) { ctx->size = size; } @@ -35,9 +35,7 @@ void ivfc_set_file(ivfc_context* ctx, FILE* file) void ivfc_process(ivfc_context* ctx, u32 actions) { - - - fseek(ctx->file, ctx->offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset, SEEK_SET); fread(&ctx->header, 1, sizeof(ivfc_header), ctx->file); if (getle32(ctx->header.magic) != MAGIC_IVFC) @@ -55,22 +53,22 @@ void ivfc_process(ivfc_context* ctx, u32 actions) 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->bodyoffset = align64(IVFC_HEADER_SIZE + 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[0].dataoffset = ctx->level[2].dataoffset + ctx->level[2].datasize; + ctx->level[0].datasize = align64(getle64(ctx->romfsheader.level1.hashdatasize), ctx->level[0].hashblocksize); - ctx->level[1].dataoffset = ctx->level[2].hashoffset; + ctx->level[1].dataoffset = ctx->level[0].dataoffset + ctx->level[0].datasize; 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); + ctx->level[0].hashoffset = IVFC_HEADER_SIZE; + ctx->level[1].hashoffset = ctx->level[0].dataoffset; + ctx->level[2].hashoffset = ctx->level[1].dataoffset; } if (actions & VerifyFlag) @@ -98,7 +96,7 @@ void ivfc_verify(ivfc_context* ctx, u32 flags) ivfc_level* level = ctx->level + i; blockcount = level->datasize / level->hashblocksize; - if (blockcount * level->hashblocksize != level->datasize) + if (level->datasize % level->hashblocksize != 0) { fprintf(stderr, "Error, IVFC block size mismatch\n"); return; @@ -110,7 +108,6 @@ void ivfc_verify(ivfc_context* ctx, u32 flags) { u8 calchash[32]; u8 testhash[32]; - ivfc_hash(ctx, level->dataoffset + level->hashblocksize * j, level->hashblocksize, calchash); ivfc_read(ctx, level->hashoffset + 0x20 * j, 0x20, testhash); @@ -121,15 +118,15 @@ void ivfc_verify(ivfc_context* ctx, u32 flags) } } -void ivfc_read(ivfc_context* ctx, u32 offset, u32 size, u8* buffer) +void ivfc_read(ivfc_context* ctx, u64 offset, u64 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); + fprintf(stderr, "Error, IVFC offset out of range (offset=0x%08"PRIx64", size=0x%08"PRIx64")\n", offset, size); return; } - fseek(ctx->file, ctx->offset + offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); if (size != fread(buffer, 1, size, ctx->file)) { fprintf(stderr, "Error, IVFC could not read file\n"); @@ -137,7 +134,7 @@ void ivfc_read(ivfc_context* ctx, u32 offset, u32 size, u8* buffer) } } -void ivfc_hash(ivfc_context* ctx, u32 offset, u32 size, u8* hash) +void ivfc_hash(ivfc_context* ctx, u64 offset, u64 size, u8* hash) { if (size > IVFC_MAX_BUFFERSIZE) { @@ -157,7 +154,7 @@ void ivfc_print(ivfc_context* ctx) 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, "Header: %.4s\n", header->magic); fprintf(stdout, "Id: %08x\n", getle32(header->id)); for(i=0; ilevelcount; i++) @@ -169,9 +166,9 @@ void ivfc_print(ivfc_context* ctx) fprintf(stdout, "Level %d: \n", i); else fprintf(stdout, "Level %d (%s): \n", i, level->hashcheck == Good? "GOOD" : "FAIL"); - fprintf(stdout, " Data offset: 0x%016"PRIx64"\n", ctx->offset + level->dataoffset); - fprintf(stdout, " Data size: 0x%016"PRIx64"\n", level->datasize); - fprintf(stdout, " Hash offset: 0x%016"PRIx64"\n", ctx->offset + level->hashoffset); + fprintf(stdout, " Data offset: 0x%08"PRIx64"\n", ctx->offset + level->dataoffset); + fprintf(stdout, " Data size: 0x%08"PRIx64"\n", level->datasize); + fprintf(stdout, " Hash offset: 0x%08"PRIx64"\n", ctx->offset + level->hashoffset); fprintf(stdout, " Hash block size: 0x%08x\n", level->hashblocksize); } } diff --git a/ctrtool/ivfc.h b/ctrtool/ivfc.h index e4e92569..5c0aa1b1 100644 --- a/ctrtool/ivfc.h +++ b/ctrtool/ivfc.h @@ -4,6 +4,7 @@ #include "types.h" #include "settings.h" +#define IVFC_HEADER_SIZE 0x60 #define IVFC_MAX_LEVEL 4 #define IVFC_MAX_BUFFERSIZE 0x4000 @@ -43,8 +44,8 @@ typedef struct typedef struct { FILE* file; - u32 offset; - u32 size; + u64 offset; + u64 size; settings* usersettings; ivfc_header header; @@ -59,14 +60,14 @@ typedef struct 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_offset(ivfc_context* ctx, u64 offset); +void ivfc_set_size(ivfc_context* ctx, u64 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); +void ivfc_read(ivfc_context* ctx, u64 offset, u64 size, u8* buffer); +void ivfc_hash(ivfc_context* ctx, u64 offset, u64 size, u8* hash); #endif // __IVFC_H__ diff --git a/ctrtool/lzss.c b/ctrtool/lzss.c index 93bc7696..1a853a5b 100644 --- a/ctrtool/lzss.c +++ b/ctrtool/lzss.c @@ -40,7 +40,7 @@ void lzss_process(lzss_context* ctx, u32 actions) FILE* fout = 0; - fseek(ctx->file, ctx->offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset, SEEK_SET); if (actions & ExtractFlag) diff --git a/ctrtool/main.c b/ctrtool/main.c index dabd615e..c5ef33cf 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -31,7 +31,7 @@ typedef struct int actions; u32 filetype; FILE* infile; - u32 infilesize; + u64 infilesize; settings usersettings; } toolcontext; @@ -259,6 +259,7 @@ int main(int argc, char* argv[]) if (ctx.actions & ShowKeysFlag) keyset_dump(&ctx.usersettings.keys); + ctx.infilesize = _fsize(infname); ctx.infile = fopen(infname, "rb"); if (ctx.infile == 0) @@ -267,16 +268,9 @@ int main(int argc, char* argv[]) 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); + fseeko64(ctx.infile, 0x100, SEEK_SET); fread(&magic, 1, 4, ctx.infile); switch(getle32(magic)) @@ -296,7 +290,7 @@ int main(int argc, char* argv[]) if (ctx.filetype == FILETYPE_UNKNOWN) { - fseek(ctx.infile, 0, SEEK_SET); + fseeko64(ctx.infile, 0, SEEK_SET); fread(magic, 1, 4, ctx.infile); switch(getle32(magic)) diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 5de61cef..991afc3a 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -30,12 +30,12 @@ void ncch_set_usersettings(ncch_context* ctx, settings* usersettings) ctx->usersettings = usersettings; } -void ncch_set_offset(ncch_context* ctx, u32 offset) +void ncch_set_offset(ncch_context* ctx, u64 offset) { ctx->offset = offset; } -void ncch_set_size(ncch_context* ctx, u32 size) +void ncch_set_size(ncch_context* ctx, u64 size) { ctx->size = size; } @@ -51,7 +51,7 @@ void ncch_get_counter(ncch_context* ctx, u8 counter[16], u8 type) u32 mediaunitsize = ncch_get_mediaunit_size(ctx); u8* partitionid = ctx->header.partitionid; u32 i; - u32 x = 0; + u64 x = 0; memset(counter, 0, 16); @@ -81,8 +81,8 @@ void ncch_get_counter(ncch_context* ctx, u8 counter[16], u8 type) int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags) { - u32 offset = 0; - u32 size = 0; + u64 offset = 0; + u64 size = 0; u8 counter[16]; @@ -126,7 +126,7 @@ int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags) ctx->extractsize = size; ctx->extractflags = flags; - fseek(ctx->file, offset, SEEK_SET); + fseeko64(ctx->file, offset, SEEK_SET); ncch_get_counter(ctx, counter, type); ctr_init_counter(&ctx->aes, ctx->key, counter); @@ -138,25 +138,25 @@ int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags) int ncch_extract_buffer(ncch_context* ctx, u8* buffer, u32 buffersize, u32* outsize, u8 nocrypto) { - u32 max = buffersize; + u32 read_len = buffersize; - if (max > ctx->extractsize) - max = ctx->extractsize; + if (read_len > ctx->extractsize) + read_len = ctx->extractsize; - *outsize = max; + *outsize = read_len; if (ctx->extractsize) { - if (max != fread(buffer, 1, max, ctx->file)) + if (read_len != fread(buffer, 1, read_len, ctx->file)) { fprintf(stdout, "Error reading input file\n"); goto clean; } if (ctx->encrypted && !nocrypto) - ctr_crypt_counter(&ctx->aes, buffer, buffer, max); + ctr_crypt_counter(&ctx->aes, buffer, buffer, read_len); - ctx->extractsize -= max; + ctx->extractsize -= read_len; } return 1; @@ -203,15 +203,15 @@ void ncch_save(ncch_context* ctx, u32 type, u32 flags) while(1) { - u32 max; + u32 read_len; - if (0 == ncch_extract_buffer(ctx, buffer, sizeof(buffer), &max, type == NCCHTYPE_LOGO)) + if (0 == ncch_extract_buffer(ctx, buffer, sizeof(buffer), &read_len, type == NCCHTYPE_LOGO)) goto clean; - if (max == 0) + if (read_len == 0) break; - if (max != fwrite(buffer, 1, max, fout)) + if (read_len != fwrite(buffer, 1, read_len, fout)) { fprintf(stdout, "Error writing output file\n"); goto clean; @@ -307,7 +307,7 @@ void ncch_process(ncch_context* ctx, u32 actions) int result = 1; - fseek(ctx->file, ctx->offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset, SEEK_SET); fread(&ctx->header, 1, 0x200, ctx->file); if (getle32(ctx->header.magic) != MAGIC_NCCH) @@ -385,53 +385,47 @@ int ncch_signature_verify(ncch_context* ctx, rsakey2048* key) } -u32 ncch_get_exefs_offset(ncch_context* ctx) +u64 ncch_get_exefs_offset(ncch_context* ctx) { - u32 mediaunitsize = ncch_get_mediaunit_size(ctx); - return ctx->offset + getle32(ctx->header.exefsoffset) * mediaunitsize; + return ctx->offset + getle32(ctx->header.exefsoffset) * ncch_get_mediaunit_size(ctx); } -u32 ncch_get_exefs_size(ncch_context* ctx) +u64 ncch_get_exefs_size(ncch_context* ctx) { - u32 mediaunitsize = ncch_get_mediaunit_size(ctx); - return getle32(ctx->header.exefssize) * mediaunitsize; + return getle32(ctx->header.exefssize) * ncch_get_mediaunit_size(ctx); } -u32 ncch_get_romfs_offset(ncch_context* ctx) +u64 ncch_get_romfs_offset(ncch_context* ctx) { - u32 mediaunitsize = ncch_get_mediaunit_size(ctx); - return ctx->offset + getle32(ctx->header.romfsoffset) * mediaunitsize; + return ctx->offset + getle32(ctx->header.romfsoffset) * ncch_get_mediaunit_size(ctx); } -u32 ncch_get_romfs_size(ncch_context* ctx) +u64 ncch_get_romfs_size(ncch_context* ctx) { - u32 mediaunitsize = ncch_get_mediaunit_size(ctx); - return getle32(ctx->header.romfssize) * mediaunitsize; + return getle32(ctx->header.romfssize) * ncch_get_mediaunit_size(ctx); } -u32 ncch_get_exheader_offset(ncch_context* ctx) +u64 ncch_get_exheader_offset(ncch_context* ctx) { return ctx->offset + 0x200; } -u32 ncch_get_exheader_size(ncch_context* ctx) +u64 ncch_get_exheader_size(ncch_context* ctx) { return getle32(ctx->header.extendedheadersize); } -u32 ncch_get_logo_offset(ncch_context* ctx) +u64 ncch_get_logo_offset(ncch_context* ctx) { - u32 mediaunitsize = ncch_get_mediaunit_size(ctx); - return ctx->offset + getle32(ctx->header.logooffset) * mediaunitsize; + return ctx->offset + getle32(ctx->header.logooffset) * ncch_get_mediaunit_size(ctx); } -u32 ncch_get_logo_size(ncch_context* ctx) +u64 ncch_get_logo_size(ncch_context* ctx) { - u32 mediaunitsize = ncch_get_mediaunit_size(ctx); - return getle32(ctx->header.logosize) * mediaunitsize; + return getle32(ctx->header.logosize) * ncch_get_mediaunit_size(ctx); } -u32 ncch_get_mediaunit_size(ncch_context* ctx) +u64 ncch_get_mediaunit_size(ncch_context* ctx) { unsigned int mediaunitsize = settings_get_mediaunit_size(ctx->usersettings); @@ -473,7 +467,7 @@ void ncch_determine_key(ncch_context* ctx, u32 actions) // 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); + fseeko64(ctx->file, ncch_get_exheader_offset(ctx), SEEK_SET); memset(&exheader, 0, sizeof(exheader)); fread(&exheader, 1, sizeof(exheader), ctx->file); @@ -560,9 +554,8 @@ static const char* contentplatformtostring(unsigned char platform) void ncch_print(ncch_context* ctx) { ctr_ncchheader *header = &ctx->header; - u32 offset = ctx->offset; - u32 mediaunitsize = ncch_get_mediaunit_size(ctx); - + u64 offset = ctx->offset; + u64 mediaunitsize = ncch_get_mediaunit_size(ctx); fprintf(stdout, "\nNCCH:\n"); @@ -573,10 +566,10 @@ void ncch_print(ncch_context* ctx) 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, "Content size: 0x%08"PRIx64"\n", getle32(header->contentsize)*mediaunitsize); fprintf(stdout, "Partition id: %016"PRIx64"\n", getle64(header->partitionid)); fprintf(stdout, "Maker code: %.2s\n", header->makercode); - fprintf(stdout, "Version: %04x\n", getle16(header->version)); + fprintf(stdout, "Version: %d\n", getle16(header->version)); fprintf(stdout, "Title seed check: %08x\n", getle32(header->seedcheck)); fprintf(stdout, "Program id: %016"PRIx64"\n", getle64(header->programid)); if(ctx->logohashcheck == Unchecked) @@ -594,7 +587,7 @@ void ncch_print(ncch_context* ctx) else memdump(stdout, "Exheader hash (FAIL): ", header->extendedheaderhash, 0x20); fprintf(stdout, "Flags: %016"PRIx64"\n", getle64(header->flags)); - fprintf(stdout, " > Mediaunit size: 0x%x\n", mediaunitsize); + fprintf(stdout, " > Mediaunit size: 0x%x\n", (u32)mediaunitsize); if (header->flags[7] & 4) fprintf(stdout, " > Crypto key: None\n"); else if (header->flags[7] & 1) @@ -608,16 +601,16 @@ void ncch_print(ncch_context* ctx) 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, "Logo offset: 0x%08x\n", getle32(header->logosize)? offset+getle32(header->logooffset)*mediaunitsize : 0); - fprintf(stdout, "Logo size: 0x%08x\n", getle32(header->logosize)*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); + fprintf(stdout, "Plain region offset: 0x%08"PRIx64"\n", getle32(header->plainregionsize)? offset+getle32(header->plainregionoffset)*mediaunitsize : 0); + fprintf(stdout, "Plain region size: 0x%08"PRIx64"\n", getle32(header->plainregionsize)*mediaunitsize); + fprintf(stdout, "Logo offset: 0x%08"PRIx64"\n", getle32(header->logosize)? offset+getle32(header->logooffset)*mediaunitsize : 0); + fprintf(stdout, "Logo size: 0x%08"PRIx64"\n", getle32(header->logosize)*mediaunitsize); + fprintf(stdout, "ExeFS offset: 0x%08"PRIx64"\n", getle32(header->exefssize)? offset+getle32(header->exefsoffset)*mediaunitsize : 0); + fprintf(stdout, "ExeFS size: 0x%08"PRIx64"\n", getle32(header->exefssize)*mediaunitsize); + fprintf(stdout, "ExeFS hash region size: 0x%08"PRIx64"\n", getle32(header->exefshashregionsize)*mediaunitsize); + fprintf(stdout, "RomFS offset: 0x%08"PRIx64"\n", getle32(header->romfssize)? offset+getle32(header->romfsoffset)*mediaunitsize : 0); + fprintf(stdout, "RomFS size: 0x%08"PRIx64"\n", getle32(header->romfssize)*mediaunitsize); + fprintf(stdout, "RomFS hash region size: 0x%08"PRIx64"\n", getle32(header->romfshashregionsize)*mediaunitsize); if (ctx->exefshashcheck == Unchecked) memdump(stdout, "ExeFS Hash: ", header->exefssuperblockhash, 0x20); else if (ctx->exefshashcheck == Good) diff --git a/ctrtool/ncch.h b/ctrtool/ncch.h index f453bb58..4bad2be1 100644 --- a/ctrtool/ncch.h +++ b/ctrtool/ncch.h @@ -57,8 +57,8 @@ typedef struct FILE* file; u8 key[16]; u32 encrypted; - u32 offset; - u32 size; + u64 offset; + u64 size; settings* usersettings; ctr_ncchheader header; ctr_aes_context aes; @@ -69,31 +69,31 @@ typedef struct int exheaderhashcheck; int logohashcheck; int headersigcheck; - u32 extractsize; + u64 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_offset(ncch_context* ctx, u64 offset); +void ncch_set_size(ncch_context* ctx, u64 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); -u32 ncch_get_logo_offset(ncch_context* ctx); -u32 ncch_get_logo_size(ncch_context* ctx); +u64 ncch_get_exefs_offset(ncch_context* ctx); +u64 ncch_get_exefs_size(ncch_context* ctx); +u64 ncch_get_romfs_offset(ncch_context* ctx); +u64 ncch_get_romfs_size(ncch_context* ctx); +u64 ncch_get_exheader_offset(ncch_context* ctx); +u64 ncch_get_exheader_size(ncch_context* ctx); +u64 ncch_get_logo_offset(ncch_context* ctx); +u64 ncch_get_logo_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, u8 nocrypto); -u32 ncch_get_mediaunit_size(ncch_context* ctx); +u64 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 index cfa55a34..384216a1 100644 --- a/ctrtool/ncsd.c +++ b/ctrtool/ncsd.c @@ -13,7 +13,7 @@ void ncsd_init(ncsd_context* ctx) memset(ctx, 0, sizeof(ncsd_context)); } -void ncsd_set_offset(ncsd_context* ctx, u32 offset) +void ncsd_set_offset(ncsd_context* ctx, u64 offset) { ctx->offset = offset; } @@ -23,7 +23,7 @@ void ncsd_set_file(ncsd_context* ctx, FILE* file) ctx->file = file; } -void ncsd_set_size(ncsd_context* ctx, u32 size) +void ncsd_set_size(ncsd_context* ctx, u64 size) { ctx->size = size; } @@ -48,7 +48,7 @@ int ncsd_signature_verify(const void* blob, rsakey2048* key) return ctr_rsa_verify_hash(sig, hash, key); } -unsigned int ncsd_get_mediaunit_size(ncsd_context* ctx) +u64 ncsd_get_mediaunit_size(ncsd_context* ctx) { unsigned int mediaunitsize = settings_get_mediaunit_size(ctx->usersettings); @@ -60,7 +60,7 @@ unsigned int ncsd_get_mediaunit_size(ncsd_context* ctx) void ncsd_process(ncsd_context* ctx, u32 actions) { - fseek(ctx->file, ctx->offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset, SEEK_SET); fread(&ctx->header, 1, 0x200, ctx->file); if (getle32(ctx->header.magic) != MAGIC_NCSD) diff --git a/ctrtool/ncsd.h b/ctrtool/ncsd.h index 58b85061..afd895a6 100644 --- a/ctrtool/ncsd.h +++ b/ctrtool/ncsd.h @@ -33,8 +33,8 @@ typedef struct typedef struct { FILE* file; - u32 offset; - u32 size; + u64 offset; + u64 size; u32 ncch_index; ctr_ncsdheader header; settings* usersettings; @@ -44,14 +44,14 @@ typedef struct 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_offset(ncsd_context* ctx, u64 offset); +void ncsd_set_size(ncsd_context* ctx, u64 size); void ncsd_set_ncch_index(ncsd_context* ctx, u32 ncch_index); 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); +u64 ncsd_get_mediaunit_size(ncsd_context* ctx); #endif // _NCSD_H_ diff --git a/ctrtool/romfs.c b/ctrtool/romfs.c index ad451817..df95fdc5 100644 --- a/ctrtool/romfs.c +++ b/ctrtool/romfs.c @@ -18,12 +18,12 @@ void romfs_set_file(romfs_context* ctx, FILE* file) ctx->file = file; } -void romfs_set_offset(romfs_context* ctx, u32 offset) +void romfs_set_offset(romfs_context* ctx, u64 offset) { ctx->offset = offset; } -void romfs_set_size(romfs_context* ctx, u32 size) +void romfs_set_size(romfs_context* ctx, u64 size) { ctx->size = size; } @@ -49,7 +49,7 @@ void romfs_process(romfs_context* ctx, u32 actions) ivfc_set_usersettings(&ctx->ivfc, ctx->usersettings); ivfc_process(&ctx->ivfc, actions); - fseek(ctx->file, ctx->offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset, SEEK_SET); fread(&ctx->header, 1, sizeof(romfs_header), ctx->file); if (getle32(ctx->header.magic) != MAGIC_IVFC) @@ -60,7 +60,7 @@ void romfs_process(romfs_context* ctx, u32 actions) ctx->infoblockoffset = ctx->offset + 0x1000; - fseek(ctx->file, ctx->infoblockoffset, SEEK_SET); + fseeko64(ctx->file, ctx->infoblockoffset, SEEK_SET); fread(&ctx->infoheader, 1, sizeof(romfs_infoheader), ctx->file); if (getle32(ctx->infoheader.headersize) != sizeof(romfs_infoheader)) @@ -83,13 +83,13 @@ void romfs_process(romfs_context* ctx, u32 actions) if (ctx->dirblock) { - fseek(ctx->file, dirblockoffset, SEEK_SET); + fseeko64(ctx->file, dirblockoffset, SEEK_SET); fread(ctx->dirblock, 1, dirblocksize, ctx->file); } if (ctx->fileblock) { - fseek(ctx->file, fileblockoffset, SEEK_SET); + fseeko64(ctx->file, fileblockoffset, SEEK_SET); fread(ctx->fileblock, 1, fileblocksize, ctx->file); } @@ -295,13 +295,8 @@ void romfs_extract_datafile(romfs_context* ctx, u64 offset, u64 size, filepath* 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); + fseeko64(ctx->file, offset, SEEK_SET); outfile = fopen(path->pathname, "wb"); if (outfile == 0) { @@ -344,9 +339,9 @@ void romfs_print(romfs_context* ctx) 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 offset: 0x%08"PRIX64"\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)); + fprintf(stdout, "Data offset: 0x%08"PRIX64"\n", ctx->offset + 0x1000 + getle32(ctx->infoheader.dataoffset)); } diff --git a/ctrtool/romfs.h b/ctrtool/romfs.h index 93256351..fa84bdfe 100644 --- a/ctrtool/romfs.h +++ b/ctrtool/romfs.h @@ -56,8 +56,8 @@ typedef struct { FILE* file; settings* usersettings; - u32 offset; - u32 size; + u64 offset; + u64 size; romfs_header header; romfs_infoheader infoheader; u8* dirblock; @@ -73,8 +73,8 @@ typedef struct 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_offset(romfs_context* ctx, u64 offset); +void romfs_set_size(romfs_context* ctx, u64 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); diff --git a/ctrtool/tik.c b/ctrtool/tik.c index 2f3c4ef6..ce582c38 100644 --- a/ctrtool/tik.c +++ b/ctrtool/tik.c @@ -16,7 +16,7 @@ void tik_set_file(tik_context* ctx, FILE* file) ctx->file = file; } -void tik_set_offset(tik_context* ctx, u32 offset) +void tik_set_offset(tik_context* ctx, u64 offset) { ctx->offset = offset; } @@ -76,7 +76,7 @@ void tik_process(tik_context* ctx, u32 actions) goto clean; } - fseek(ctx->file, ctx->offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset, SEEK_SET); fread((u8*)&ctx->tik, 1, sizeof(eticket), ctx->file); tik_decrypt_titlekey(ctx, ctx->titlekey); diff --git a/ctrtool/tik.h b/ctrtool/tik.h index 78b93f06..edd72c5b 100644 --- a/ctrtool/tik.h +++ b/ctrtool/tik.h @@ -41,7 +41,7 @@ typedef struct typedef struct { FILE* file; - u32 offset; + u64 offset; u32 size; u8 titlekey[16]; eticket tik; @@ -51,7 +51,7 @@ typedef struct 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_offset(tik_context* ctx, u64 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]); diff --git a/ctrtool/tmd.c b/ctrtool/tmd.c index af5691ff..bcb687f1 100644 --- a/ctrtool/tmd.c +++ b/ctrtool/tmd.c @@ -17,7 +17,7 @@ void tmd_set_file(tmd_context* ctx, FILE* file) ctx->file = file; } -void tmd_set_offset(tmd_context* ctx, u32 offset) +void tmd_set_offset(tmd_context* ctx, u64 offset) { ctx->offset = offset; } @@ -39,7 +39,7 @@ void tmd_process(tmd_context* ctx, u32 actions) if (ctx->buffer) { - fseek(ctx->file, ctx->offset, SEEK_SET); + fseeko64(ctx->file, ctx->offset, SEEK_SET); fread(ctx->buffer, 1, ctx->size, ctx->file); if (actions & InfoFlag) diff --git a/ctrtool/tmd.h b/ctrtool/tmd.h index 6272429d..d9701a7d 100644 --- a/ctrtool/tmd.h +++ b/ctrtool/tmd.h @@ -74,7 +74,7 @@ typedef struct typedef struct { FILE* file; - u32 offset; + u64 offset; u32 size; u8* buffer; u8 content_hash_stat[64]; @@ -89,7 +89,7 @@ extern "C" { 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_offset(tmd_context* ctx, u64 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); diff --git a/ctrtool/types.h b/ctrtool/types.h index bade7f15..9234fe1f 100644 --- a/ctrtool/types.h +++ b/ctrtool/types.h @@ -2,16 +2,17 @@ #define __TYPES_H__ #include +#include -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long long u64; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; -typedef signed char s8; -typedef signed short s16; -typedef signed int s32; -typedef signed long long s64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; enum flags { diff --git a/ctrtool/utils.c b/ctrtool/utils.c index a7f2897b..c0435866 100644 --- a/ctrtool/utils.c +++ b/ctrtool/utils.c @@ -1,13 +1,13 @@ #include #include +#include +#include #include "utils.h" #ifdef _WIN32 #include -#else -#include -#include +#include #endif @@ -20,7 +20,7 @@ u32 align(u32 offset, u32 alignment) u64 align64(u64 offset, u32 alignment) { - u64 mask = ~(alignment-1); + u64 mask = ~(u64)(alignment-1); return (offset + (alignment-1)) & mask; } @@ -91,19 +91,15 @@ void putle32(u8* p, u32 n) void readkeyfile(u8* key, const char* keyfname) { + u32 keysize = _fsize(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); @@ -190,3 +186,20 @@ int makedir(const char* dir) return mkdir(dir, 0777); #endif } + +u64 _fsize(const char *filename) +{ +#ifdef _WIN32 + struct _stat64 st; + if (_stat64(filename, &st) != 0) + return 0; + else + return st.st_size; +#else + struct stat st; + if (stat(filename, &st) != 0) + return 0; + else + return st.st_size; +#endif +} diff --git a/ctrtool/utils.h b/ctrtool/utils.h index 31a06fb0..519a95e5 100644 --- a/ctrtool/utils.h +++ b/ctrtool/utils.h @@ -35,6 +35,12 @@ int key_load(char *name, u8 *out_buf); int makedir(const char* dir); +u64 _fsize(const char *filename); + +#ifndef _WIN32 +extern int fseeko64 (FILE *__stream, __off64_t __off, int __whence); +#endif + #ifdef __cplusplus } #endif From c2b89979c9bca7dad7db55a659893c635ea1f009 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 20 Nov 2015 15:07:58 +0800 Subject: [PATCH 128/317] [makerom] Fixes issue #11 --- makerom/polarssl/aes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/polarssl/aes.c b/makerom/polarssl/aes.c index 8d8ebb63..7c1f0fd1 100644 --- a/makerom/polarssl/aes.c +++ b/makerom/polarssl/aes.c @@ -774,7 +774,7 @@ int aes_crypt_ecb( aes_context *ctx, */ int aes_crypt_cbc( aes_context *ctx, int mode, - size_t length, + uint64_t length, unsigned char iv[16], const unsigned char *input, unsigned char *output ) From 40dabc5a4609ed86cffac2ac1b59aaa3602095b2 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 23 Nov 2015 00:32:24 +0800 Subject: [PATCH 129/317] [makerom/ctrool] Implemented ROMFS unicode support. (oschar.c) --- ctrtool/ctrtool.vcxproj | 2 + ctrtool/ctrtool.vcxproj.filters | 6 + ctrtool/main.c | 2 +- ctrtool/oschar.c | 204 ++++++++++++ ctrtool/oschar.h | 97 ++++++ ctrtool/romfs.c | 80 +++-- ctrtool/romfs.h | 9 +- ctrtool/utils.c | 4 +- makerom/makerom.vcxproj | 6 +- makerom/makerom.vcxproj.filters | 12 +- makerom/ncsd.c | 3 +- makerom/oschar.c | 204 ++++++++++++ makerom/oschar.h | 97 ++++++ makerom/romfs_fs.c | 146 ++------- makerom/romfs_fs.h | 51 +-- makerom/romfs_gen.c | 23 +- makerom/romfs_gen.h | 1 + makerom/types.h | 2 +- makerom/utf.c | 563 -------------------------------- makerom/utf.h | 149 --------- makerom/utils.c | 102 +----- makerom/utils.h | 13 - 22 files changed, 725 insertions(+), 1051 deletions(-) create mode 100644 ctrtool/oschar.c create mode 100644 ctrtool/oschar.h create mode 100644 makerom/oschar.c create mode 100644 makerom/oschar.h delete mode 100644 makerom/utf.c delete mode 100644 makerom/utf.h diff --git a/ctrtool/ctrtool.vcxproj b/ctrtool/ctrtool.vcxproj index 628d52fe..d16bc575 100644 --- a/ctrtool/ctrtool.vcxproj +++ b/ctrtool/ctrtool.vcxproj @@ -99,6 +99,7 @@ + @@ -130,6 +131,7 @@ + diff --git a/ctrtool/ctrtool.vcxproj.filters b/ctrtool/ctrtool.vcxproj.filters index c51e94eb..075c29de 100644 --- a/ctrtool/ctrtool.vcxproj.filters +++ b/ctrtool/ctrtool.vcxproj.filters @@ -114,6 +114,9 @@ Source Files\tinyxml + + Source Files + @@ -203,5 +206,8 @@ Header Files\tinyxml + + Header Files + \ No newline at end of file diff --git a/ctrtool/main.c b/ctrtool/main.c index c5ef33cf..25b688d5 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -102,7 +102,7 @@ int main(int argc, char* argv[]) char keysetfname[512] = "keys.xml"; keyset tmpkeys; unsigned int checkkeysetfile = 0; - + memset(&ctx, 0, sizeof(toolcontext)); ctx.actions = InfoFlag | ExtractFlag; ctx.filetype = FILETYPE_UNKNOWN; diff --git a/ctrtool/oschar.c b/ctrtool/oschar.c new file mode 100644 index 00000000..1ef05ddb --- /dev/null +++ b/ctrtool/oschar.c @@ -0,0 +1,204 @@ +#include +#ifndef _WIN32 +#include +#endif +#include "oschar.h" + +int os_fstat(const oschar_t *path) +{ + struct _osstat st; + return os_stat(path, &st); +} + +uint64_t os_fsize(const oschar_t *path) +{ + struct _osstat st; + if (os_stat(path, &st) != 0) + return 0; + else + return st.st_size; +} + +int os_makedir(const oschar_t *dir) +{ +#ifdef _WIN32 + return _wmkdir(dir); +#else + return mkdir(dir, 0777); +#endif +} + +uint32_t utf16_strlen(const utf16char_t *str) +{ + uint32_t i; + for (i = 0; str[i] != 0x0; i++); + return i; +} + +void utf16_fputs(const utf16char_t *str, FILE *out) +{ + oschar_t *_str = os_CopyConvertUTF16Str(str); + os_fputs(_str, out); + free(_str); +} + +char* strcopy_8to8(const char *src) +{ + uint32_t src_len; + char *dst; + + if (!src) + return NULL; + + src_len = strlen(src); + + // Allocate memory for expanded string + dst = calloc(src_len + 1, sizeof(char)); + if (!dst) + return NULL; + + // Copy elements from src into dst + strncpy(dst, src, src_len); + + return dst; +} + +utf16char_t* strcopy_8to16(const char *src) +{ + uint32_t src_len, i; + utf16char_t *dst; + + if (!src) + return NULL; + + src_len = strlen(src); + + // Allocate memory for expanded string + dst = calloc(src_len + 1, sizeof(utf16char_t)); + if (!dst) + return NULL; + + // Copy elements from src into dst + for (i = 0; i < src_len; i++) + dst[i] = src[i]; + + return dst; +} + + +utf16char_t* strcopy_16to16(const utf16char_t *src) +{ + uint32_t src_len, i; + utf16char_t *dst; + + if (!src) + return NULL; + + src_len = utf16_strlen(src); + + // Allocate memory for expanded string + dst = calloc(src_len + 1, sizeof(utf16char_t)); + if (!dst) + return NULL; + + // Copy elements from src into dst + for (i = 0; i < src_len; i++) + dst[i] = src[i]; + + return dst; +} + +#ifndef _WIN32 +utf16char_t* strcopy_UTF8toUTF16(const char *src) +{ + uint32_t src_len, dst_len; + size_t in_bytes, out_bytes; + utf16char_t *dst; + char *in, *out; + + if (!src) + return NULL; + + src_len = strlen(src); + dst_len = src_len + 1; + + // Allocate memory for string + dst = calloc(dst_len, sizeof(utf16char_t)); + if (!dst) + return NULL; + + in = (char*)src; + out = (char*)dst; + in_bytes = src_len*sizeof(char); + out_bytes = dst_len*sizeof(utf16char_t); + + iconv_t cd = iconv_open("UTF-16LE", "UTF-8"); + iconv(cd, &in, &in_bytes, &out, &out_bytes); + return dst; +} + +char* strcopy_UTF16toUTF8(const utf16char_t *src) +{ + uint32_t src_len, dst_len; + size_t in_bytes, out_bytes; + char *dst; + char *in, *out; + + if (!src) + return NULL; + + src_len = utf16_strlen(src); + dst_len = src_len * 2; + + // Allocate memory for string + dst = calloc(dst_len, sizeof(char)); // twice the size, as UTF-8 will use up to two bytes for converted UTF16 chars afaik + if (!dst) + return NULL; + + in = (char*)src; + out = (char*)dst; + in_bytes = src_len*sizeof(uint16_t); + out_bytes = dst_len*sizeof(char); + + iconv_t cd = iconv_open("UTF-8", "UTF-16LE"); + iconv(cd, &in, &in_bytes, &out, &out_bytes); + return dst; +} +#endif + +oschar_t* os_AppendToPath(const oschar_t *src, const oschar_t *add) +{ + uint32_t len; + oschar_t *new_path; + + len = os_strlen(src) + os_strlen(add) + 0x10; + new_path = calloc(len, sizeof(oschar_t)); + +#ifdef _WIN32 + _snwprintf(new_path, len, L"%s%c%s", src, OS_PATH_SEPARATOR, add); +#else + snprintf(new_path, len, "%s%c%s", src, OS_PATH_SEPARATOR, add); +#endif + + return new_path; +} + +oschar_t* os_AppendUTF16StrToPath(const oschar_t *src, const utf16char_t *add) +{ + uint32_t len; + oschar_t *new_path, *_add; + + _add = os_CopyConvertUTF16Str(add); + + len = os_strlen(src) + os_strlen(_add) + 0x10; + new_path = calloc(len, sizeof(oschar_t)); + +#ifdef _WIN32 + _snwprintf(new_path, len, L"%s%c%s", src, OS_PATH_SEPARATOR, _add); +#else + snprintf(new_path, len, "%s%c%s", src, OS_PATH_SEPARATOR, _add); +#endif + + free(_add); + return new_path; +} \ No newline at end of file diff --git a/ctrtool/oschar.h b/ctrtool/oschar.h new file mode 100644 index 00000000..570a6ca6 --- /dev/null +++ b/ctrtool/oschar.h @@ -0,0 +1,97 @@ +#pragma once +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#endif + +// Nintendo uses UTF16-LE chars for extended ASCII support +typedef uint16_t utf16char_t; + +// Native OS char type for unicode support +#ifdef _WIN32 +typedef wchar_t oschar_t; // UTF16-LE +#else +typedef char oschar_t; // UTF8 +#endif + + // Simple redirect macros for functions and types +#ifdef _WIN32 +#define os_strlen wcslen +#define os_strcmp wcscmp +#define os_fputs fputws + +#define os_CopyStr strcopy_16to16 +#define os_CopyConvertCharStr strcopy_8to16 +#define os_CopyConvertUTF16Str strcopy_16to16 +#define utf16_CopyStr strcopy_16to16 +#define utf16_CopyConvertOsStr strcopy_16to16 + +#define _osdirent _wdirent +#define _OSDIR _WDIR +#define os_readdir _wreaddir +#define os_opendir _wopendir +#define os_closedir _wclosedir +#define os_chdir _wchdir + +#define _osstat _stat64 +#define os_stat _wstat64 + +#define os_fopen _wfopen +#define OS_MODE_READ L"rb" +#define OS_MODE_WRITE L"wb" +#define OS_MODE_EDIT L"rb+" +#define OS_PATH_SEPARATOR '\\' +#else +#define os_strlen strlen +#define os_strcmp strcmp +#define os_fputs fputs + +#define os_CopyStr strcopy_8to8 +#define os_CopyConvertUTF16Str strcopy_UTF16toUTF8 +#define os_CopyConvertCharStr strcopy_8to8 +#define utf16_CopyStr strcopy_16to16 +#define utf16_CopyConvertOsStr strcopy_UTF8toUTF16 + +#define _osdirent dirent +#define _OSDIR DIR +#define os_readdir readdir +#define os_opendir opendir +#define os_closedir closedir +#define os_chdir chdir + +#define _osstat stat +#define os_stat stat + +#define os_fopen fopen +#define OS_MODE_READ "rb" +#define OS_MODE_WRITE "wb" +#define OS_MODE_EDIT "rb+" +#define OS_PATH_SEPARATOR '/' +#endif + +/* File related */ +int os_fstat(const oschar_t* path); +uint64_t os_fsize(const oschar_t* path); +int os_makedir(const oschar_t *dir); + +/* UTF16 String property functions */ +uint32_t utf16_strlen(const utf16char_t* str); +void utf16_fputs(const utf16char_t *str, FILE *out); + +/* String Copy and Conversion */ +char* strcopy_8to8(const char *src); +utf16char_t* strcopy_8to16(const char *src); +utf16char_t* strcopy_16to16(const utf16char_t *src); +#ifndef _WIN32 +utf16char_t* strcopy_UTF8toUTF16(const char *src); +char* strcopy_UTF16toUTF8(const utf16char_t *src); +#endif + +/* String Append and Create */ +oschar_t* os_AppendToPath(const oschar_t *src, const oschar_t *add); +oschar_t* os_AppendUTF16StrToPath(const oschar_t *src, const utf16char_t *add); \ No newline at end of file diff --git a/ctrtool/romfs.c b/ctrtool/romfs.c index df95fdc5..3ff49df3 100644 --- a/ctrtool/romfs.c +++ b/ctrtool/romfs.c @@ -96,8 +96,13 @@ void romfs_process(romfs_context* ctx, u32 actions) if (actions & InfoFlag) romfs_print(ctx); - romfs_visit_dir(ctx, 0, 0, actions, settings_get_romfs_dir_path(ctx->usersettings)); + if (settings_get_romfs_dir_path(ctx->usersettings)->valid) + ctx->extractdir = os_CopyConvertCharStr(settings_get_romfs_dir_path(ctx->usersettings)->pathname); + else + ctx->extractdir = NULL; + romfs_visit_dir(ctx, 0, 0, actions, ctx->extractdir); + free(ctx->extractdir); } int romfs_dirblock_read(romfs_context* ctx, u32 diroffset, u32 dirsize, void* buffer) @@ -171,12 +176,12 @@ int romfs_fileblock_readentry(romfs_context* ctx, u32 fileoffset, romfs_fileentr -void romfs_visit_dir(romfs_context* ctx, u32 diroffset, u32 depth, u32 actions, filepath* rootpath) +void romfs_visit_dir(romfs_context* ctx, u32 diroffset, u32 depth, u32 actions, const oschar_t* rootpath) { u32 siblingoffset; u32 childoffset; u32 fileoffset; - filepath currentpath; + oschar_t* currentpath; romfs_direntry* entry = &ctx->direntry; @@ -190,32 +195,39 @@ void romfs_visit_dir(romfs_context* ctx, u32 diroffset, u32 depth, u32 actions, // fwprintf(stdout, L"%ls\n", entry->name); - if (rootpath && rootpath->valid) + if (rootpath && os_strlen(rootpath)) { - filepath_copy(¤tpath, rootpath); - filepath_append_utf16(¤tpath, entry->name); - if (currentpath.valid) + if (utf16_strlen((const utf16char_t*)entry->name) > 0) + currentpath = os_AppendUTF16StrToPath(rootpath, (const utf16char_t*)entry->name); + else // root dir, use the provided extract path instead of the empty root name. + currentpath = os_CopyStr(rootpath); + + if (currentpath) { - makedir(currentpath.pathname); + os_makedir(currentpath); } else { - fprintf(stderr, "Error creating directory in root %s\n", rootpath->pathname); + fputs("Error creating directory in root ", stderr); + os_fputs(rootpath, stderr); + fputs("\n", stderr); return; } } else { - filepath_init(¤tpath); - + currentpath = os_CopyConvertUTF16Str((const utf16char_t*)entry->name); if (settings_get_list_romfs_files(ctx->usersettings)) { u32 i; for(i=0; iname); + os_fputs(currentpath, stdout); + fputs("\n", stdout); } + free(currentpath); + currentpath = NULL; } @@ -224,20 +236,22 @@ void romfs_visit_dir(romfs_context* ctx, u32 diroffset, u32 depth, u32 actions, fileoffset = getle32(entry->fileoffset); if (fileoffset != (~0)) - romfs_visit_file(ctx, fileoffset, depth+1, actions, ¤tpath); + romfs_visit_file(ctx, fileoffset, depth+1, actions, currentpath); if (childoffset != (~0)) - romfs_visit_dir(ctx, childoffset, depth+1, actions, ¤tpath); + romfs_visit_dir(ctx, childoffset, depth+1, actions, currentpath); if (siblingoffset != (~0)) romfs_visit_dir(ctx, siblingoffset, depth, actions, rootpath); + + free(currentpath); } -void romfs_visit_file(romfs_context* ctx, u32 fileoffset, u32 depth, u32 actions, filepath* rootpath) +void romfs_visit_file(romfs_context* ctx, u32 fileoffset, u32 depth, u32 actions, const oschar_t* rootpath) { u32 siblingoffset = 0; - filepath currentpath; + oschar_t* currentpath = NULL; romfs_fileentry* entry = &ctx->fileentry; @@ -250,55 +264,63 @@ void romfs_visit_file(romfs_context* ctx, u32 fileoffset, u32 depth, u32 actions // getle64(entry->datasize), getle32(entry->unknown)); // fwprintf(stdout, L"%ls\n", entry->name); - if (rootpath && rootpath->valid) + if (rootpath && os_strlen(rootpath)) { - filepath_copy(¤tpath, rootpath); - filepath_append_utf16(¤tpath, entry->name); - if (currentpath.valid) + currentpath = os_AppendUTF16StrToPath(rootpath, (const utf16char_t*)entry->name); + if (currentpath) { - fprintf(stdout, "Saving %s...\n", currentpath.pathname); - romfs_extract_datafile(ctx, getle64(entry->dataoffset), getle64(entry->datasize), ¤tpath); + fputs("Saving ", stdout); + os_fputs(currentpath, stdout); + fputs("...\n", stdout); + romfs_extract_datafile(ctx, getle64(entry->dataoffset), getle64(entry->datasize), currentpath); } else { - fprintf(stderr, "Error creating directory in root %s\n", rootpath->pathname); + fputs("Error creating file in root ", stderr); + os_fputs(rootpath, stderr); + fputs("\n", stderr); return; } } else { - filepath_init(¤tpath); + currentpath = os_CopyConvertUTF16Str((const utf16char_t*)entry->name); if (settings_get_list_romfs_files(ctx->usersettings)) { u32 i; for(i=0; iname); + os_fputs(currentpath, stdout); + fputs("\n", stdout); } + free(currentpath); + currentpath = NULL; } siblingoffset = getle32(entry->siblingoffset); if (siblingoffset != (~0)) romfs_visit_file(ctx, siblingoffset, depth, actions, rootpath); + + free(currentpath); } -void romfs_extract_datafile(romfs_context* ctx, u64 offset, u64 size, filepath* path) +void romfs_extract_datafile(romfs_context* ctx, u64 offset, u64 size, const oschar_t* path) { FILE* outfile = 0; u32 max; u8 buffer[4096]; - if (path == 0 || path->valid == 0) + if (path == NULL || os_strlen(path) == 0) goto clean; offset += ctx->datablockoffset; fseeko64(ctx->file, offset, SEEK_SET); - outfile = fopen(path->pathname, "wb"); - if (outfile == 0) + outfile = os_fopen(path, OS_MODE_WRITE); + if (outfile == NULL) { fprintf(stderr, "Error opening file for writing\n"); goto clean; diff --git a/ctrtool/romfs.h b/ctrtool/romfs.h index fa84bdfe..c560ab4b 100644 --- a/ctrtool/romfs.h +++ b/ctrtool/romfs.h @@ -4,7 +4,7 @@ #include "types.h" #include "info.h" #include "ctr.h" -#include "filepath.h" +#include "oschar.h" #include "settings.h" #include "ivfc.h" @@ -55,6 +55,7 @@ typedef struct typedef struct { FILE* file; + oschar_t* extractdir; settings* usersettings; u64 offset; u64 size; @@ -81,9 +82,9 @@ int romfs_dirblock_read(romfs_context* ctx, u32 diroffset, u32 dirsize, void* b 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_visit_dir(romfs_context* ctx, u32 diroffset, u32 depth, u32 actions, const oschar_t* rootpath); +void romfs_visit_file(romfs_context* ctx, u32 fileoffset, u32 depth, u32 actions, const oschar_t* rootpath); +void romfs_extract_datafile(romfs_context* ctx, u64 offset, u64 size, const oschar_t* path); void romfs_process(romfs_context* ctx, u32 actions); void romfs_print(romfs_context* ctx); diff --git a/ctrtool/utils.c b/ctrtool/utils.c index c0435866..bda20d93 100644 --- a/ctrtool/utils.c +++ b/ctrtool/utils.c @@ -1,6 +1,8 @@ #include +#include #include +#include #include #include #include "utils.h" @@ -202,4 +204,4 @@ u64 _fsize(const char *filename) else return st.st_size; #endif -} +} \ No newline at end of file diff --git a/makerom/makerom.vcxproj b/makerom/makerom.vcxproj index 7ec49cdc..896eb095 100644 --- a/makerom/makerom.vcxproj +++ b/makerom/makerom.vcxproj @@ -67,7 +67,7 @@ make clean make rebuild WIN32;_DEBUG;$(NMakePreprocessorDefinitions) - C:\Program Files\mingw-w64\x86_64-5.2.0-win32-seh-rt_v4-rev0\mingw64\x86_64-w64-mingw32\include;$(IncludePath) + C:\Program Files\mingw-w64\x86_64-4.9.2-win32-seh-rt_v4-rev2\mingw64\x86_64-w64-mingw32\include make @@ -104,6 +104,7 @@ + @@ -188,7 +189,6 @@ - @@ -201,6 +201,7 @@ + @@ -236,7 +237,6 @@ - diff --git a/makerom/makerom.vcxproj.filters b/makerom/makerom.vcxproj.filters index 6603f558..bcb59c1f 100644 --- a/makerom/makerom.vcxproj.filters +++ b/makerom/makerom.vcxproj.filters @@ -156,9 +156,6 @@ Header Files - - Header Files - Header Files @@ -339,6 +336,9 @@ Header Files + + Header Files + @@ -407,9 +407,6 @@ Source Files - - Source Files - Source Files @@ -479,6 +476,9 @@ Source Files\polarssl + + Source Files + diff --git a/makerom/ncsd.c b/makerom/ncsd.c index 29e4f76e..ba2a23bb 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -153,7 +153,7 @@ int ImportNcchForCci(cci_settings *set) bool CanCiaBeCci(u64 titleId, u16 count, tmd_content_chunk *content) { - if(GetTidCategory(titleId) != PROGRAM_ID_CATEGORY_APPLICATION && GetTidCategory(titleId) != PROGRAM_ID_CATEGORY_SYSTEM_APPLICATION) + if(GetTidCategory(titleId) != PROGRAM_ID_CATEGORY_APPLICATION) return false; if(count > CCI_MAX_CONTENT) @@ -222,6 +222,7 @@ int ProcessCiaForCci(cci_settings *set) return 0; } +/* This need to be more automagical */ void GetTitleSaveSize(cci_settings *set) { if(set->rsf->SystemControlInfo.SaveDataSize) diff --git a/makerom/oschar.c b/makerom/oschar.c new file mode 100644 index 00000000..1ef05ddb --- /dev/null +++ b/makerom/oschar.c @@ -0,0 +1,204 @@ +#include +#ifndef _WIN32 +#include +#endif +#include "oschar.h" + +int os_fstat(const oschar_t *path) +{ + struct _osstat st; + return os_stat(path, &st); +} + +uint64_t os_fsize(const oschar_t *path) +{ + struct _osstat st; + if (os_stat(path, &st) != 0) + return 0; + else + return st.st_size; +} + +int os_makedir(const oschar_t *dir) +{ +#ifdef _WIN32 + return _wmkdir(dir); +#else + return mkdir(dir, 0777); +#endif +} + +uint32_t utf16_strlen(const utf16char_t *str) +{ + uint32_t i; + for (i = 0; str[i] != 0x0; i++); + return i; +} + +void utf16_fputs(const utf16char_t *str, FILE *out) +{ + oschar_t *_str = os_CopyConvertUTF16Str(str); + os_fputs(_str, out); + free(_str); +} + +char* strcopy_8to8(const char *src) +{ + uint32_t src_len; + char *dst; + + if (!src) + return NULL; + + src_len = strlen(src); + + // Allocate memory for expanded string + dst = calloc(src_len + 1, sizeof(char)); + if (!dst) + return NULL; + + // Copy elements from src into dst + strncpy(dst, src, src_len); + + return dst; +} + +utf16char_t* strcopy_8to16(const char *src) +{ + uint32_t src_len, i; + utf16char_t *dst; + + if (!src) + return NULL; + + src_len = strlen(src); + + // Allocate memory for expanded string + dst = calloc(src_len + 1, sizeof(utf16char_t)); + if (!dst) + return NULL; + + // Copy elements from src into dst + for (i = 0; i < src_len; i++) + dst[i] = src[i]; + + return dst; +} + + +utf16char_t* strcopy_16to16(const utf16char_t *src) +{ + uint32_t src_len, i; + utf16char_t *dst; + + if (!src) + return NULL; + + src_len = utf16_strlen(src); + + // Allocate memory for expanded string + dst = calloc(src_len + 1, sizeof(utf16char_t)); + if (!dst) + return NULL; + + // Copy elements from src into dst + for (i = 0; i < src_len; i++) + dst[i] = src[i]; + + return dst; +} + +#ifndef _WIN32 +utf16char_t* strcopy_UTF8toUTF16(const char *src) +{ + uint32_t src_len, dst_len; + size_t in_bytes, out_bytes; + utf16char_t *dst; + char *in, *out; + + if (!src) + return NULL; + + src_len = strlen(src); + dst_len = src_len + 1; + + // Allocate memory for string + dst = calloc(dst_len, sizeof(utf16char_t)); + if (!dst) + return NULL; + + in = (char*)src; + out = (char*)dst; + in_bytes = src_len*sizeof(char); + out_bytes = dst_len*sizeof(utf16char_t); + + iconv_t cd = iconv_open("UTF-16LE", "UTF-8"); + iconv(cd, &in, &in_bytes, &out, &out_bytes); + return dst; +} + +char* strcopy_UTF16toUTF8(const utf16char_t *src) +{ + uint32_t src_len, dst_len; + size_t in_bytes, out_bytes; + char *dst; + char *in, *out; + + if (!src) + return NULL; + + src_len = utf16_strlen(src); + dst_len = src_len * 2; + + // Allocate memory for string + dst = calloc(dst_len, sizeof(char)); // twice the size, as UTF-8 will use up to two bytes for converted UTF16 chars afaik + if (!dst) + return NULL; + + in = (char*)src; + out = (char*)dst; + in_bytes = src_len*sizeof(uint16_t); + out_bytes = dst_len*sizeof(char); + + iconv_t cd = iconv_open("UTF-8", "UTF-16LE"); + iconv(cd, &in, &in_bytes, &out, &out_bytes); + return dst; +} +#endif + +oschar_t* os_AppendToPath(const oschar_t *src, const oschar_t *add) +{ + uint32_t len; + oschar_t *new_path; + + len = os_strlen(src) + os_strlen(add) + 0x10; + new_path = calloc(len, sizeof(oschar_t)); + +#ifdef _WIN32 + _snwprintf(new_path, len, L"%s%c%s", src, OS_PATH_SEPARATOR, add); +#else + snprintf(new_path, len, "%s%c%s", src, OS_PATH_SEPARATOR, add); +#endif + + return new_path; +} + +oschar_t* os_AppendUTF16StrToPath(const oschar_t *src, const utf16char_t *add) +{ + uint32_t len; + oschar_t *new_path, *_add; + + _add = os_CopyConvertUTF16Str(add); + + len = os_strlen(src) + os_strlen(_add) + 0x10; + new_path = calloc(len, sizeof(oschar_t)); + +#ifdef _WIN32 + _snwprintf(new_path, len, L"%s%c%s", src, OS_PATH_SEPARATOR, _add); +#else + snprintf(new_path, len, "%s%c%s", src, OS_PATH_SEPARATOR, _add); +#endif + + free(_add); + return new_path; +} \ No newline at end of file diff --git a/makerom/oschar.h b/makerom/oschar.h new file mode 100644 index 00000000..570a6ca6 --- /dev/null +++ b/makerom/oschar.h @@ -0,0 +1,97 @@ +#pragma once +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#endif + +// Nintendo uses UTF16-LE chars for extended ASCII support +typedef uint16_t utf16char_t; + +// Native OS char type for unicode support +#ifdef _WIN32 +typedef wchar_t oschar_t; // UTF16-LE +#else +typedef char oschar_t; // UTF8 +#endif + + // Simple redirect macros for functions and types +#ifdef _WIN32 +#define os_strlen wcslen +#define os_strcmp wcscmp +#define os_fputs fputws + +#define os_CopyStr strcopy_16to16 +#define os_CopyConvertCharStr strcopy_8to16 +#define os_CopyConvertUTF16Str strcopy_16to16 +#define utf16_CopyStr strcopy_16to16 +#define utf16_CopyConvertOsStr strcopy_16to16 + +#define _osdirent _wdirent +#define _OSDIR _WDIR +#define os_readdir _wreaddir +#define os_opendir _wopendir +#define os_closedir _wclosedir +#define os_chdir _wchdir + +#define _osstat _stat64 +#define os_stat _wstat64 + +#define os_fopen _wfopen +#define OS_MODE_READ L"rb" +#define OS_MODE_WRITE L"wb" +#define OS_MODE_EDIT L"rb+" +#define OS_PATH_SEPARATOR '\\' +#else +#define os_strlen strlen +#define os_strcmp strcmp +#define os_fputs fputs + +#define os_CopyStr strcopy_8to8 +#define os_CopyConvertUTF16Str strcopy_UTF16toUTF8 +#define os_CopyConvertCharStr strcopy_8to8 +#define utf16_CopyStr strcopy_16to16 +#define utf16_CopyConvertOsStr strcopy_UTF8toUTF16 + +#define _osdirent dirent +#define _OSDIR DIR +#define os_readdir readdir +#define os_opendir opendir +#define os_closedir closedir +#define os_chdir chdir + +#define _osstat stat +#define os_stat stat + +#define os_fopen fopen +#define OS_MODE_READ "rb" +#define OS_MODE_WRITE "wb" +#define OS_MODE_EDIT "rb+" +#define OS_PATH_SEPARATOR '/' +#endif + +/* File related */ +int os_fstat(const oschar_t* path); +uint64_t os_fsize(const oschar_t* path); +int os_makedir(const oschar_t *dir); + +/* UTF16 String property functions */ +uint32_t utf16_strlen(const utf16char_t* str); +void utf16_fputs(const utf16char_t *str, FILE *out); + +/* String Copy and Conversion */ +char* strcopy_8to8(const char *src); +utf16char_t* strcopy_8to16(const char *src); +utf16char_t* strcopy_16to16(const utf16char_t *src); +#ifndef _WIN32 +utf16char_t* strcopy_UTF8toUTF16(const char *src); +char* strcopy_UTF16toUTF8(const utf16char_t *src); +#endif + +/* String Append and Create */ +oschar_t* os_AppendToPath(const oschar_t *src, const oschar_t *add); +oschar_t* os_AppendUTF16StrToPath(const oschar_t *src, const utf16char_t *add); \ No newline at end of file diff --git a/makerom/romfs_fs.c b/makerom/romfs_fs.c index 4c092652..19c121a1 100644 --- a/makerom/romfs_fs.c +++ b/makerom/romfs_fs.c @@ -1,6 +1,5 @@ #include "lib.h" #include "romfs_fs.h" -#include "utf.h" /* This is the FS interface for ROMFS generation */ /* Tested working on Windows/Linux/OSX */ @@ -8,109 +7,6 @@ int PopulateDir(romfs_dir *dir); int InitDir(romfs_dir *dir); int ManageDir(romfs_dir *dir); -u32 fs_strlen(const fs_char *str) -{ -#ifdef _WIN32 - return strlen_char16(str); -#else - return strlen(str); -#endif -} - -u32 romfs_strlen(const romfs_char *str) -{ - return strlen_char16(str); -} - -int fs_strcmp(const fs_char *str1, const fs_char *str2) -{ -#ifdef _WIN32 - return wcscmp(str1, str2); -#else - return strcmp(str1, str2); -#endif -} - - -FILE* fs_fopen(fs_char *path) -{ -#ifdef _WIN32 - return _wfopen(path, L"rb"); -#else - return fopen(path, "rb"); -#endif -} - -u64 fs_fsize(fs_char *path) -{ -#ifdef _WIN32 - return wGetFileSize64(path); -#else - return GetFileSize64(path); -#endif -} - -fs_char* fs_AppendToPath(const fs_char *src, const fs_char *add) -{ - u32 src_len, add_len; - fs_char *new_path; - - src_len = fs_strlen(src); - add_len = fs_strlen(add); - new_path = calloc(src_len + add_len + 0x10, sizeof(fs_char)); - -#ifdef _WIN32 - _snwprintf(new_path, src_len + add_len + 0x10, L"%s%c%s", src, FS_PATH_SEPARATOR, add); -#else - snprintf(new_path, src_len + add_len + 0x10, "%s%c%s", src, FS_PATH_SEPARATOR, add); -#endif - - return new_path; -} - -fs_char* fs_CopyStr(const fs_char *src) -{ -#ifdef _WIN32 - return strcopy_16to16(src); -#else - return strcopy_8to8(src); -#endif -} - -romfs_char* romfs_CopyConvertStr(const fs_char *src) -{ -#ifdef _WIN32 - return strcopy_16to16(src); -#else - return strcopy_utf8to16(src); -#endif -} - - -romfs_char* romfs_CopyStr(const romfs_char *src) -{ - return strcopy_16to16(src); -} - -void fs_fputs(const fs_char *str, FILE *out) -{ -#ifdef _WIN32 - fwprintf(out,L"%s", str); -#else - fprintf(out,"%s", str); -#endif -} - -void romfs_fputs(const romfs_char *str, FILE *out) -{ -#ifdef _WIN32 - fwprintf(out,L"%s", str); -#else - const char *name = (const char*)str; - for (u32 i = 0; i < romfs_strlen(str)*2; i += 2) - fputc(name[i],out); -#endif -} int InitDir(romfs_dir *dir) { @@ -148,13 +44,9 @@ int ManageDir(romfs_dir *dir) int OpenRootDir(const char *path, romfs_dir *dir) { // Create native FS path -#ifdef _WIN32 - dir->path = strcopy_8to16(path); -#else - dir->path = strcopy_8to8(path); -#endif + dir->path = os_CopyConvertCharStr(path); // Copy romfs name (empty string) - dir->name = romfs_CopyStr(ROMFS_EMPTY_PATH); + dir->name = utf16_CopyStr(ROMFS_EMPTY_PATH); dir->namesize = 0; return PopulateDir(dir); @@ -162,26 +54,26 @@ int OpenRootDir(const char *path, romfs_dir *dir) int PopulateDir(romfs_dir *dir) { - fs_DIR *dp, *tmp_dp; - struct fs_dirent *entry; + _OSDIR *dp, *tmp_dp; + struct _osdirent *entry; if (InitDir(dir)) return MEM_ERROR; // Open Directory - if((dp = fs_opendir(dir->path)) == NULL) + if((dp = os_opendir(dir->path)) == NULL) { printf("[ROMFS] Failed to open directory: \""); - fs_fputs(dir->path, stdout); + os_fputs(dir->path, stdout); printf("\"\n"); return -1; } // Process Entries - while ((entry = fs_readdir(dp)) != NULL) + while ((entry = os_readdir(dp)) != NULL) { // Skip if "." or ".." - if (fs_strcmp(entry->d_name, FS_CURRENT_DIR_PATH) == 0 || fs_strcmp(entry->d_name, FS_PARENT_DIR_PATH) == 0) + if (os_strcmp(entry->d_name, OS_CURRENT_DIR_PATH) == 0 || os_strcmp(entry->d_name, OS_PARENT_DIR_PATH) == 0) continue; // Ensures that there is always memory for child directory and file structs @@ -189,15 +81,15 @@ int PopulateDir(romfs_dir *dir) return MEM_ERROR; // Get native FS path - fs_char *path = fs_AppendToPath(dir->path, entry->d_name); + oschar_t *path = os_AppendToPath(dir->path, entry->d_name); // Opening directory with fs path to test if directory - if ((tmp_dp = fs_opendir(path)) != NULL) { - fs_closedir(tmp_dp); + if ((tmp_dp = os_opendir(path)) != NULL) { + os_closedir(tmp_dp); dir->child[dir->u_child].path = path; - dir->child[dir->u_child].name = romfs_CopyConvertStr(entry->d_name); - dir->child[dir->u_child].namesize = fs_strlen(entry->d_name)*sizeof(romfs_char); + dir->child[dir->u_child].name = utf16_CopyConvertOsStr(entry->d_name); + dir->child[dir->u_child].namesize = os_strlen(entry->d_name)*sizeof(utf16char_t); dir->u_child++; // Populate directory @@ -206,14 +98,14 @@ int PopulateDir(romfs_dir *dir) // Otherwise this is a file else { dir->file[dir->u_file].path = path; - dir->file[dir->u_file].name = romfs_CopyConvertStr(entry->d_name); - dir->file[dir->u_file].namesize = fs_strlen(entry->d_name)*sizeof(romfs_char); - dir->file[dir->u_file].size = fs_fsize(path); + dir->file[dir->u_file].name = utf16_CopyConvertOsStr(entry->d_name); + dir->file[dir->u_file].namesize = os_strlen(entry->d_name)*sizeof(utf16char_t); + dir->file[dir->u_file].size = os_fsize(path); dir->u_file++; } } - fs_closedir(dp); + os_closedir(dp); return 0; } @@ -225,7 +117,7 @@ void PrintDir(romfs_dir *dir, u32 depth) printf(" "); if (depth > 0) - romfs_fputs(dir->name, stdout); + utf16_fputs(dir->name, stdout); else printf("romfs:"); putchar('\n'); @@ -236,7 +128,7 @@ void PrintDir(romfs_dir *dir, u32 depth) { for(u32 j = 0; j < depth+1; j++) printf(" "); - romfs_fputs(dir->file[i].name, stdout); + utf16_fputs(dir->file[i].name, stdout); printf(" (0x%"PRIx64")\n", dir->file[i].size); } } diff --git a/makerom/romfs_fs.h b/makerom/romfs_fs.h index 0826f21d..cd7704f2 100644 --- a/makerom/romfs_fs.h +++ b/makerom/romfs_fs.h @@ -1,39 +1,18 @@ #pragma once - -#ifdef _WIN32 - #define romfs_char u16 - #define fs_char wchar_t - #define fs_dirent _wdirent - #define fs_DIR _WDIR - #define fs_readdir _wreaddir - #define fs_chdir _wchdir - #define fs_opendir _wopendir - #define fs_closedir _wclosedir - #define FS_PATH_SEPARATOR '\\' -#else - #define romfs_char u16 - #define fs_char char - #define fs_dirent dirent - #define fs_DIR DIR - #define fs_readdir readdir - #define fs_chdir chdir - #define fs_opendir opendir - #define fs_closedir closedir - #define FS_PATH_SEPARATOR '/' -#endif +#include "oschar.h" struct romfs_file { - fs_char *path; - romfs_char *name; + oschar_t *path; + utf16char_t *name; u32 namesize; u64 size; }; struct romfs_dir { - fs_char *path; - romfs_char *name; + oschar_t *path; + utf16char_t *name; u32 namesize; struct romfs_dir *child; @@ -48,22 +27,10 @@ struct romfs_dir typedef struct romfs_file romfs_file; typedef struct romfs_dir romfs_dir; -static const romfs_char ROMFS_EMPTY_PATH[2] = { 0 }; -static const fs_char FS_EMPTY_PATH[2] = { 0 }; -static const fs_char FS_CURRENT_DIR_PATH[2] = { '.' }; -static const fs_char FS_PARENT_DIR_PATH[3] = { '.', '.' }; - -u32 romfs_strlen(const romfs_char *str); -u32 fs_strlen(const fs_char *str); -int fs_strcmp(const fs_char *str1, const fs_char *str2); -FILE* fs_fopen(fs_char *path); -u64 fs_fsize(fs_char *path); - -fs_char* fs_AppendToPath(const fs_char *src, const fs_char *add); -fs_char* fs_CopyStr(const fs_char *src); -romfs_char* romfs_CopyStr(const romfs_char *src); -void fs_fputs(const fs_char *str, FILE *out); -void romfs_fputs(const romfs_char *str, FILE *out); +static const utf16char_t ROMFS_EMPTY_PATH[2] = { 0 }; +static const oschar_t OS_EMPTY_PATH[2] = { 0 }; +static const oschar_t OS_CURRENT_DIR_PATH[2] = { '.' }; +static const oschar_t OS_PARENT_DIR_PATH[3] = { '.', '.' }; int OpenRootDir(const char *path, romfs_dir *dir); void PrintDir(romfs_dir *dir, u32 depth); diff --git a/makerom/romfs_gen.c b/makerom/romfs_gen.c index 9d8a97e7..30a7d913 100644 --- a/makerom/romfs_gen.c +++ b/makerom/romfs_gen.c @@ -19,7 +19,7 @@ void PopulateRomfs(romfs_buildctx *ctx); void BuildRomfsHeader(romfs_buildctx *ctx); void BuildIvfcHeader(romfs_buildctx *ctx); void GenIvfcHashTree(romfs_buildctx *ctx); -u32 CalcPathHash(u32 parent, const romfs_char* path); +u32 CalcPathHash(u32 parent, const utf16char_t* path); int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) @@ -32,7 +32,6 @@ int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx) /* Import FS and process */ OpenRootDir(ncchset->rsfSet->RomFs.RootPath,fs_raw); FilterRomFS(fs_raw,ctx->fs,filter_criteria); - /* free unfiltered FS */ FreeDir(fs_raw); @@ -186,10 +185,10 @@ int FilterRomFS(romfs_dir *fs_raw, romfs_dir *fs_filtered, void *filter_criteria if(!IsDirWanted(fs_raw,filter_criteria)) return 0; - fs_filtered->path = fs_CopyStr(fs_raw->path); + fs_filtered->path = os_CopyStr(fs_raw->path); fs_filtered->namesize = fs_raw->namesize; - fs_filtered->name = romfs_CopyStr(fs_raw->name); + fs_filtered->name = utf16_CopyStr(fs_raw->name); fs_filtered->u_child = 0; fs_filtered->m_child = fs_raw->u_child; @@ -212,10 +211,10 @@ int FilterRomFS(romfs_dir *fs_raw, romfs_dir *fs_filtered, void *filter_criteria { if(IsFileWanted(&fs_raw->file[i],filter_criteria)) { - fs_filtered->file[fs_filtered->u_file].path = fs_CopyStr(fs_raw->file[i].path); + fs_filtered->file[fs_filtered->u_file].path = os_CopyStr(fs_raw->file[i].path); fs_filtered->file[fs_filtered->u_file].namesize = fs_raw->file[i].namesize; - fs_filtered->file[fs_filtered->u_file].name = romfs_CopyStr(fs_raw->file[i].name); + fs_filtered->file[fs_filtered->u_file].name = utf16_CopyStr(fs_raw->file[i].name); fs_filtered->file[fs_filtered->u_file].size = fs_raw->file[i].size; @@ -277,13 +276,13 @@ void BuildRomfsHeader(romfs_buildctx *ctx) } } -u32 GetFileHashTableIndex(romfs_buildctx *ctx, u32 parent, const romfs_char *path) +u32 GetFileHashTableIndex(romfs_buildctx *ctx, u32 parent, const utf16char_t *path) { u32 hash = CalcPathHash(parent, path); return hash % ctx->m_fileHashTable; } -u32 GetDirHashTableIndex(romfs_buildctx *ctx, u32 parent, const romfs_char* path) +u32 GetDirHashTableIndex(romfs_buildctx *ctx, u32 parent, const utf16char_t* path) { u32 hash = CalcPathHash(parent, path); return hash % ctx->m_dirHashTable; @@ -317,11 +316,11 @@ void AddFileToRomfs(romfs_buildctx *ctx, romfs_file *file, u32 parent, u32 sibli if (ctx->verbose) { printf("[ROMFS] Reading \""); - fs_fputs(file->path, stdout); + os_fputs(file->path, stdout); printf("\"... "); } - FILE *fp = fs_fopen(file->path); + FILE *fp = os_fopen(file->path, OS_MODE_READ); fread(data_pos, file->size, 1, fp); fclose(fp); @@ -463,9 +462,9 @@ void GenIvfcHashTree(romfs_buildctx *ctx) return; } -u32 CalcPathHash(u32 parent, const romfs_char* path) +u32 CalcPathHash(u32 parent, const utf16char_t* path) { - u32 len = romfs_strlen(path); + u32 len = utf16_strlen(path); u32 hash = parent ^ 123456789; for( u32 i = 0; i < len; i++ ) { diff --git a/makerom/romfs_gen.h b/makerom/romfs_gen.h index b3559341..75db968d 100644 --- a/makerom/romfs_gen.h +++ b/makerom/romfs_gen.h @@ -1,4 +1,5 @@ #pragma once +#include "romfs.h" int PrepareBuildRomFsBinary(ncch_settings *ncchset, romfs_buildctx *ctx); int BuildRomFsBinary(romfs_buildctx *ctx); diff --git a/makerom/types.h b/makerom/types.h index 4f8696ff..e9984b5e 100644 --- a/makerom/types.h +++ b/makerom/types.h @@ -44,4 +44,4 @@ typedef uint64_t u64; typedef int8_t s8; typedef int16_t s16; typedef int32_t s32; -typedef int64_t s64; +typedef int64_t s64; \ No newline at end of file diff --git a/makerom/utf.c b/makerom/utf.c deleted file mode 100644 index 6ecadefd..00000000 --- a/makerom/utf.c +++ /dev/null @@ -1,563 +0,0 @@ -/* - * Copyright 2001-2004 Unicode, Inc. - * - * Disclaimer - * - * This source code is provided as is by Unicode, Inc. No claims are - * made as to fitness for any particular purpose. No warranties of any - * kind are expressed or implied. The recipient agrees to determine - * applicability of information provided. If this file has been - * purchased on magnetic or optical media from Unicode, Inc., the - * sole remedy for any claim will be exchange of defective media - * within 90 days of receipt. - * - * Limitations on Rights to Redistribute This Code - * - * Unicode, Inc. hereby grants the right to freely use the information - * supplied in this file in the creation of products supporting the - * Unicode Standard, and to make copies of this file in any form - * for internal or external distribution as long as this notice - * remains attached. - */ - -/* --------------------------------------------------------------------- - - Conversions between UTF32, UTF-16, and UTF-8. Source code file. - Author: Mark E. Davis, 1994. - Rev History: Rick McGowan, fixes & updates May 2001. - Sept 2001: fixed const & error conditions per - mods suggested by S. Parent & A. Lillich. - June 2002: Tim Dodd added detection and handling of incomplete - source sequences, enhanced error detection, added casts - to eliminate compiler warnings. - July 2003: slight mods to back out aggressive FFFE detection. - Jan 2004: updated switches in from-UTF8 conversions. - Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. - - See the header file "utf.h" for complete documentation. - ------------------------------------------------------------------------- */ - - -#include "utf.h" -#ifdef CVTUTF_DEBUG -#include -#endif - -static const int halfShift = 10; /* used for shifting by 10 bits */ - -static const UTF32 halfBase = 0x0010000UL; -static const UTF32 halfMask = 0x3FFUL; - -#define UNI_SUR_HIGH_START (UTF32)0xD800 -#define UNI_SUR_HIGH_END (UTF32)0xDBFF -#define UNI_SUR_LOW_START (UTF32)0xDC00 -#define UNI_SUR_LOW_END (UTF32)0xDFFF -#define false 0 -#define true 1 - -/* --------------------------------------------------------------------- */ - -/* - * Index into the table below with the first byte of a UTF-8 sequence to - * get the number of trailing bytes that are supposed to follow it. - * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is - * left as-is for anyone who may want to do such conversion, which was - * allowed in earlier algorithms. - */ -static const char trailingBytesForUTF8[256] = { - 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,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,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,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 -}; - -/* - * Magic values subtracted from a buffer value during UTF8 conversion. - * This table contains as many values as there might be trailing bytes - * in a UTF-8 sequence. - */ -static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, - 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; - -/* - * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed - * into the first byte, depending on how many bytes follow. There are - * as many entries in this table as there are UTF-8 sequence types. - * (I.e., one byte sequence, two byte... etc.). Remember that sequencs - * for *legal* UTF-8 will be 4 or fewer bytes total. - */ -static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - -/* --------------------------------------------------------------------- */ - -/* The interface converts a whole buffer to avoid function-call overhead. - * Constants have been gathered. Loops & conditionals have been removed as - * much as possible for efficiency, in favor of drop-through switches. - * (See "Note A" at the bottom of the file for equivalent code.) - * If your compiler supports it, the "isLegalUTF8" call can be turned - * into an inline function. - */ - - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF32toUTF16 ( - const UTF32** sourceStart, const UTF32* sourceEnd, - UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF32* source = *sourceStart; - UTF16* target = *targetStart; - while (source < sourceEnd) { - UTF32 ch; - if (target >= targetEnd) { - result = targetExhausted; break; - } - ch = *source++; - if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ - /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { - if (flags == strictConversion) { - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } else { - *target++ = UNI_REPLACEMENT_CHAR; - } - } else { - *target++ = (UTF16)ch; /* normal case */ - } - } else if (ch > UNI_MAX_LEGAL_UTF32) { - if (flags == strictConversion) { - result = sourceIllegal; - } else { - *target++ = UNI_REPLACEMENT_CHAR; - } - } else { - /* target is a character in range 0xFFFF - 0x10FFFF. */ - if (target + 1 >= targetEnd) { - --source; /* Back up source pointer! */ - result = targetExhausted; break; - } - ch -= halfBase; - *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); - *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); - } - } - *sourceStart = source; - *targetStart = target; - return result; -} - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF16toUTF32 ( - const UTF16** sourceStart, const UTF16* sourceEnd, - UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF16* source = *sourceStart; - UTF32* target = *targetStart; - UTF32 ch, ch2; - while (source < sourceEnd) { - const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ - ch = *source++; - /* If we have a surrogate pair, convert to UTF32 first. */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { - /* If the 16 bits following the high surrogate are in the source buffer... */ - if (source < sourceEnd) { - ch2 = *source; - /* If it's a low surrogate, convert to UTF32. */ - if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { - ch = ((ch - UNI_SUR_HIGH_START) << halfShift) - + (ch2 - UNI_SUR_LOW_START) + halfBase; - ++source; - } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } else { /* We don't have the 16 bits following the high surrogate. */ - --source; /* return to the high surrogate */ - result = sourceExhausted; - break; - } - } else if (flags == strictConversion) { - /* UTF-16 surrogate values are illegal in UTF-32 */ - if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } - if (target >= targetEnd) { - source = oldSource; /* Back up source pointer! */ - result = targetExhausted; break; - } - *target++ = ch; - } - *sourceStart = source; - *targetStart = target; -#ifdef CVTUTF_DEBUG -if (result == sourceIllegal) { - fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); - fflush(stderr); -} -#endif - return result; -} -ConversionResult ConvertUTF16toUTF8 ( - const UTF16** sourceStart, const UTF16* sourceEnd, - UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF16* source = *sourceStart; - UTF8* target = *targetStart; - while (source < sourceEnd) { - UTF32 ch; - unsigned short bytesToWrite = 0; - const UTF32 byteMask = 0xBF; - const UTF32 byteMark = 0x80; - const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ - ch = *source++; - /* If we have a surrogate pair, convert to UTF32 first. */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { - /* If the 16 bits following the high surrogate are in the source buffer... */ - if (source < sourceEnd) { - UTF32 ch2 = *source; - /* If it's a low surrogate, convert to UTF32. */ - if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { - ch = ((ch - UNI_SUR_HIGH_START) << halfShift) - + (ch2 - UNI_SUR_LOW_START) + halfBase; - ++source; - } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } else { /* We don't have the 16 bits following the high surrogate. */ - --source; /* return to the high surrogate */ - result = sourceExhausted; - break; - } - } else if (flags == strictConversion) { - /* UTF-16 surrogate values are illegal in UTF-32 */ - if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } - /* Figure out how many bytes the result will require */ - if (ch < (UTF32)0x80) { bytesToWrite = 1; - } else if (ch < (UTF32)0x800) { bytesToWrite = 2; - } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; - } else if (ch < (UTF32)0x110000) { bytesToWrite = 4; - } else { bytesToWrite = 3; - ch = UNI_REPLACEMENT_CHAR; - } - - target += bytesToWrite; - if (target > targetEnd) { - source = oldSource; /* Back up source pointer! */ - target -= bytesToWrite; result = targetExhausted; break; - } - switch (bytesToWrite) { /* note: everything falls through. */ - case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; - case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; - case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; - case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); - } - target += bytesToWrite; - } - *sourceStart = source; - *targetStart = target; - return result; -} - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF32toUTF8 ( - const UTF32** sourceStart, const UTF32* sourceEnd, - UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF32* source = *sourceStart; - UTF8* target = *targetStart; - while (source < sourceEnd) { - UTF32 ch; - unsigned short bytesToWrite = 0; - const UTF32 byteMask = 0xBF; - const UTF32 byteMark = 0x80; - ch = *source++; - if (flags == strictConversion ) { - /* UTF-16 surrogate values are illegal in UTF-32 */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } - /* - * Figure out how many bytes the result will require. Turn any - * illegally large UTF32 things (> Plane 17) into replacement chars. - */ - if (ch < (UTF32)0x80) { bytesToWrite = 1; - } else if (ch < (UTF32)0x800) { bytesToWrite = 2; - } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; - } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; - } else { bytesToWrite = 3; - ch = UNI_REPLACEMENT_CHAR; - result = sourceIllegal; - } - - target += bytesToWrite; - if (target > targetEnd) { - --source; /* Back up source pointer! */ - target -= bytesToWrite; result = targetExhausted; break; - } - switch (bytesToWrite) { /* note: everything falls through. */ - case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; - case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; - case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; - case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); - } - target += bytesToWrite; - } - *sourceStart = source; - *targetStart = target; - return result; -} - -/* --------------------------------------------------------------------- */ - -/* - * Utility routine to tell whether a sequence of bytes is legal UTF-8. - * This must be called with the length pre-determined by the first byte. - * If not calling this from ConvertUTF8to*, then the length can be set by: - * length = trailingBytesForUTF8[*source]+1; - * and the sequence is illegal right away if there aren't that many bytes - * available. - * If presented with a length > 4, this returns false. The Unicode - * definition of UTF-8 goes up to 4-byte sequences. - */ - -static Boolean isLegalUTF8(const UTF8 *source, int length) { - UTF8 a; - const UTF8 *srcptr = source+length; - switch (length) { - default: return false; - /* Everything else falls through when "true"... */ - case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; - case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; - case 2: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; - - switch (*source) { - /* no fall-through in this inner switch */ - case 0xE0: if (a < 0xA0) return false; break; - case 0xED: if (a > 0x9F) return false; break; - case 0xF0: if (a < 0x90) return false; break; - case 0xF4: if (a > 0x8F) return false; break; - default: if (a < 0x80) return false; - } - - case 1: if (*source >= 0x80 && *source < 0xC2) return false; - } - if (*source > 0xF4) return false; - return true; -} - -/* --------------------------------------------------------------------- */ - -/* - * Exported function to return whether a UTF-8 sequence is legal or not. - * This is not used here; it's just exported. - */ -Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { - int length = trailingBytesForUTF8[*source]+1; - if (length > sourceEnd - source) { - return false; - } - return isLegalUTF8(source, length); -} - -/* --------------------------------------------------------------------- */ - -/* - * Exported function to return the total number of bytes in a codepoint - * represented in UTF-8, given the value of the first byte. - */ -unsigned getNumBytesForUTF8(UTF8 first) { - return trailingBytesForUTF8[first] + 1; -} - -/* --------------------------------------------------------------------- */ - -/* - * Exported function to return whether a UTF-8 string is legal or not. - * This is not used here; it's just exported. - */ -Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd) { - while (*source != sourceEnd) { - int length = trailingBytesForUTF8[**source] + 1; - if (length > sourceEnd - *source || !isLegalUTF8(*source, length)) - return false; - *source += length; - } - return true; -} - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF8toUTF16 ( - const UTF8** sourceStart, const UTF8* sourceEnd, - UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF8* source = *sourceStart; - UTF16* target = *targetStart; - while (source < sourceEnd) { - UTF32 ch = 0; - unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; - if (extraBytesToRead >= sourceEnd - source) { - result = sourceExhausted; break; - } - /* Do this check whether lenient or strict */ - if (!isLegalUTF8(source, extraBytesToRead+1)) { - result = sourceIllegal; - break; - } - /* - * The cases all fall through. See "Note A" below. - */ - switch (extraBytesToRead) { - case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ - case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ - case 3: ch += *source++; ch <<= 6; - case 2: ch += *source++; ch <<= 6; - case 1: ch += *source++; ch <<= 6; - case 0: ch += *source++; - } - ch -= offsetsFromUTF8[extraBytesToRead]; - - if (target >= targetEnd) { - source -= (extraBytesToRead+1); /* Back up source pointer! */ - result = targetExhausted; break; - } - if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ - /* UTF-16 surrogate values are illegal in UTF-32 */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { - if (flags == strictConversion) { - source -= (extraBytesToRead+1); /* return to the illegal value itself */ - result = sourceIllegal; - break; - } else { - *target++ = UNI_REPLACEMENT_CHAR; - } - } else { - *target++ = (UTF16)ch; /* normal case */ - } - } else if (ch > UNI_MAX_UTF16) { - if (flags == strictConversion) { - result = sourceIllegal; - source -= (extraBytesToRead+1); /* return to the start */ - break; /* Bail out; shouldn't continue */ - } else { - *target++ = UNI_REPLACEMENT_CHAR; - } - } else { - /* target is a character in range 0xFFFF - 0x10FFFF. */ - if (target + 1 >= targetEnd) { - source -= (extraBytesToRead+1); /* Back up source pointer! */ - result = targetExhausted; break; - } - ch -= halfBase; - *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); - *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); - } - } - *sourceStart = source; - *targetStart = target; - return result; -} - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF8toUTF32 ( - const UTF8** sourceStart, const UTF8* sourceEnd, - UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF8* source = *sourceStart; - UTF32* target = *targetStart; - while (source < sourceEnd) { - UTF32 ch = 0; - unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; - if (extraBytesToRead >= sourceEnd - source) { - result = sourceExhausted; break; - } - /* Do this check whether lenient or strict */ - if (!isLegalUTF8(source, extraBytesToRead+1)) { - result = sourceIllegal; - break; - } - /* - * The cases all fall through. See "Note A" below. - */ - switch (extraBytesToRead) { - case 5: ch += *source++; ch <<= 6; - case 4: ch += *source++; ch <<= 6; - case 3: ch += *source++; ch <<= 6; - case 2: ch += *source++; ch <<= 6; - case 1: ch += *source++; ch <<= 6; - case 0: ch += *source++; - } - ch -= offsetsFromUTF8[extraBytesToRead]; - - if (target >= targetEnd) { - source -= (extraBytesToRead+1); /* Back up the source pointer! */ - result = targetExhausted; break; - } - if (ch <= UNI_MAX_LEGAL_UTF32) { - /* - * UTF-16 surrogate values are illegal in UTF-32, and anything - * over Plane 17 (> 0x10FFFF) is illegal. - */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { - if (flags == strictConversion) { - source -= (extraBytesToRead+1); /* return to the illegal value itself */ - result = sourceIllegal; - break; - } else { - *target++ = UNI_REPLACEMENT_CHAR; - } - } else { - *target++ = ch; - } - } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ - result = sourceIllegal; - *target++ = UNI_REPLACEMENT_CHAR; - } - } - *sourceStart = source; - *targetStart = target; - return result; -} - -/* --------------------------------------------------------------------- - - Note A. - The fall-through switches in UTF-8 reading code save a - temp variable, some decrements & conditionals. The switches - are equivalent to the following loop: - { - int tmpBytesToRead = extraBytesToRead+1; - do { - ch += *source++; - --tmpBytesToRead; - if (tmpBytesToRead) ch <<= 6; - } while (tmpBytesToRead > 0); - } - In UTF-8 writing code, the switches on "bytesToWrite" are - similarly unrolled loops. - - --------------------------------------------------------------------- */ diff --git a/makerom/utf.h b/makerom/utf.h deleted file mode 100644 index 0b74f167..00000000 --- a/makerom/utf.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2001-2004 Unicode, Inc. - * - * Disclaimer - * - * This source code is provided as is by Unicode, Inc. No claims are - * made as to fitness for any particular purpose. No warranties of any - * kind are expressed or implied. The recipient agrees to determine - * applicability of information provided. If this file has been - * purchased on magnetic or optical media from Unicode, Inc., the - * sole remedy for any claim will be exchange of defective media - * within 90 days of receipt. - * - * Limitations on Rights to Redistribute This Code - * - * Unicode, Inc. hereby grants the right to freely use the information - * supplied in this file in the creation of products supporting the - * Unicode Standard, and to make copies of this file in any form - * for internal or external distribution as long as this notice - * remains attached. - */ - -/* --------------------------------------------------------------------- - - Conversions between UTF32, UTF-16, and UTF-8. Header file. - - Several funtions are included here, forming a complete set of - conversions between the three formats. UTF-7 is not included - here, but is handled in a separate source file. - - Each of these routines takes pointers to input buffers and output - buffers. The input buffers are const. - - Each routine converts the text between *sourceStart and sourceEnd, - putting the result into the buffer between *targetStart and - targetEnd. Note: the end pointers are *after* the last item: e.g. - *(sourceEnd - 1) is the last item. - - The return result indicates whether the conversion was successful, - and if not, whether the problem was in the source or target buffers. - (Only the first encountered problem is indicated.) - - After the conversion, *sourceStart and *targetStart are both - updated to point to the end of last text successfully converted in - the respective buffers. - - Input parameters: - sourceStart - pointer to a pointer to the source buffer. - The contents of this are modified on return so that - it points at the next thing to be converted. - targetStart - similarly, pointer to pointer to the target buffer. - sourceEnd, targetEnd - respectively pointers to the ends of the - two buffers, for overflow checking only. - - These conversion functions take a ConversionFlags argument. When this - flag is set to strict, both irregular sequences and isolated surrogates - will cause an error. When the flag is set to lenient, both irregular - sequences and isolated surrogates are converted. - - Whether the flag is strict or lenient, all illegal sequences will cause - an error return. This includes sequences such as: , , - or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code - must check for illegal sequences. - - When the flag is set to lenient, characters over 0x10FFFF are converted - to the replacement character; otherwise (when the flag is set to strict) - they constitute an error. - - Output parameters: - The value "sourceIllegal" is returned from some routines if the input - sequence is malformed. When "sourceIllegal" is returned, the source - value will point to the illegal value that caused the problem. E.g., - in UTF-8 when a sequence is malformed, it points to the start of the - malformed sequence. - - Author: Mark E. Davis, 1994. - Rev History: Rick McGowan, fixes & updates May 2001. - Fixes & updates, Sept 2001. - ------------------------------------------------------------------------- */ - -#pragma once - -/* --------------------------------------------------------------------- - The following 4 definitions are compiler-specific. - The C standard does not guarantee that wchar_t has at least - 16 bits, so wchar_t is no less portable than unsigned short! - All should be unsigned values to avoid sign extension during - bit mask & shift operations. ------------------------------------------------------------------------- */ - -typedef unsigned int UTF32; /* at least 32 bits */ -typedef unsigned short UTF16; /* at least 16 bits */ -typedef unsigned char UTF8; /* typically 8 bits */ -typedef unsigned char Boolean; /* 0 or 1 */ - -/* Some fundamental constants */ -#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD -#define UNI_MAX_BMP (UTF32)0x0000FFFF -#define UNI_MAX_UTF16 (UTF32)0x0010FFFF -#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF -#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF - -#define UNI_MAX_UTF8_BYTES_PER_CODE_POINT 4 - -#define UNI_UTF16_BYTE_ORDER_MARK_NATIVE 0xFEFF -#define UNI_UTF16_BYTE_ORDER_MARK_SWAPPED 0xFFFE - -typedef enum { - conversionOK, /* conversion successful */ - sourceExhausted, /* partial character in source, but hit end */ - targetExhausted, /* insuff. room in target for conversion */ - sourceIllegal /* source sequence is illegal/malformed */ -} ConversionResult; - -typedef enum { - strictConversion = 0, - lenientConversion -} ConversionFlags; - -ConversionResult ConvertUTF32toUTF16 ( - const UTF32** sourceStart, const UTF32* sourceEnd, - UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); - -ConversionResult ConvertUTF16toUTF32 ( - const UTF16** sourceStart, const UTF16* sourceEnd, - UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); - -ConversionResult ConvertUTF16toUTF8 ( - const UTF16** sourceStart, const UTF16* sourceEnd, - UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); - -ConversionResult ConvertUTF32toUTF8 ( - const UTF32** sourceStart, const UTF32* sourceEnd, - UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); - -Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); - -unsigned getNumBytesForUTF8(UTF8 first); - -Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd); - -ConversionResult ConvertUTF8toUTF16 ( - const UTF8** sourceStart, const UTF8* sourceEnd, - UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); - -ConversionResult ConvertUTF8toUTF32 ( - const UTF8** sourceStart, const UTF8* sourceEnd, - UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); diff --git a/makerom/utils.c b/makerom/utils.c index 849126cc..92303ed2 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -1,5 +1,7 @@ #include "lib.h" -#include "utf.h" +#ifndef _WIN32 +#include +#endif #include "polarssl/base64.h" @@ -106,95 +108,6 @@ void memdump(FILE* fout, const char* prefix, const u8* data, u32 size) } } -u32 strlen_char16(const u16 *str) -{ - u32 i; - for (i = 0; str[i] != 0x0; i++); - return i; -} - -char* strcopy_8to8(const char *src) -{ - if (!src) - return NULL; - - u32 src_len = strlen(src); - - // Allocate memory for expanded string - char *dst = calloc(src_len + 1, sizeof(u8)); - if (!dst) - return NULL; - - // Copy elements from src into dst - strncpy(dst, src, src_len); - - return dst; -} - -u16* strcopy_8to16(const char *src) -{ - if (!src) - return NULL; - - u32 src_len = strlen(src); - - // Allocate memory for expanded string - u16 *dst = calloc(src_len+1, sizeof(u16)); - if (!dst) - return NULL; - - // Copy elements from src into dst - for (u32 i = 0; i < src_len; i++) - dst[i] = src[i]; - - return dst; -} - - -u16* strcopy_16to16(const u16 *src) -{ - if (!src) - return NULL; - - u32 src_len = strlen_char16(src); - - // Allocate memory for expanded string - u16 *dst = calloc(src_len + 1, sizeof(u16)); - if (!dst) - return NULL; - - // Copy elements from src into dst - for (u32 i = 0; i < src_len; i++) - dst[i] = src[i]; - - return dst; -} - -#ifndef _WIN32 -u16* strcopy_utf8to16(const char *src) -{ - if (!src) - return NULL; - - u32 src_len = strlen(src); - - // Allocate memory for expanded string - u16 *dst = calloc(src_len + 1, sizeof(u16)); - if (!dst) - return NULL; - - UTF16 *target_start = dst; - UTF16 *target_end = (target_start + (src_len*sizeof(u16))); - - UTF8 *src_start = (UTF8*)src; - UTF8 *src_end = (UTF8*)(src + src_len*sizeof(char)); - - ConvertUTF8toUTF16((const UTF8 **)&src_start, src_end, &target_start, target_end, strictConversion); - - return dst; -} -#endif - // Base64 bool IsValidB64Char(char chr) { @@ -347,15 +260,6 @@ int TruncateFile64(char *filename, u64 filelen) #endif } -// Wide Char IO -#ifdef _WIN32 -u64 wGetFileSize64(u16 *filename) -{ - struct _stat64 st; - _wstat64((wchar_t*)filename, &st); - return st.st_size; -} -#endif //IO Misc u8* ImportFile(char *file, u64 size) diff --git a/makerom/utils.h b/makerom/utils.h index e21a8af4..6370e415 100644 --- a/makerom/utils.h +++ b/makerom/utils.h @@ -20,13 +20,6 @@ u64 max64(u64 a, u64 b); // Strings void memdump(FILE* fout, const char* prefix, const const u8* data, u32 size); char* replace_filextention(const char *input, const char *extention); -u32 strlen_char16(const u16 *str); -char* strcopy_8to8(const char *src); -u16* strcopy_8to16(const char *src); -u16* strcopy_16to16(const u16 *src); -#ifndef _WIN32 -u16* strcopy_utf8to16(const char *src); -#endif // Base64 bool IsValidB64Char(char chr); @@ -45,14 +38,8 @@ u64 u64GetRand(void); bool AssertFile(char *filename); u64 GetFileSize64(char *filename); int makedir(const char* dir); -char *getcwdir(char *buffer,int maxlen); int TruncateFile64(char *filename, u64 filelen); -//Wide Char IO -#ifdef _WIN32 -u64 wGetFileSize64(u16 *filename); -#endif - //IO Misc u8* ImportFile(char *file, u64 size); void WriteBuffer(const void *buffer, u64 size, u64 offset, FILE *output); From 9d29a4ba62c5e7326f15736faa48192aba1b6990 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 25 Nov 2015 11:54:05 +0800 Subject: [PATCH 130/317] [makerom/ctrtool] Misc #include cleanup. --- ctrtool/ivfc.c | 1 - ctrtool/oschar.h | 4 +--- ctrtool/romfs.c | 3 --- ctrtool/utils.c | 10 ++-------- makerom/oschar.h | 4 +--- makerom/utils.c | 4 ---- 6 files changed, 4 insertions(+), 22 deletions(-) diff --git a/ctrtool/ivfc.c b/ctrtool/ivfc.c index 7fbb5fc8..90df3810 100644 --- a/ctrtool/ivfc.c +++ b/ctrtool/ivfc.c @@ -5,7 +5,6 @@ #include "utils.h" #include "ivfc.h" #include "ctr.h" -#include void ivfc_init(ivfc_context* ctx) { diff --git a/ctrtool/oschar.h b/ctrtool/oschar.h index 570a6ca6..9c788b07 100644 --- a/ctrtool/oschar.h +++ b/ctrtool/oschar.h @@ -3,9 +3,7 @@ #include #include #include -#include #ifdef _WIN32 -#include #include #endif @@ -19,7 +17,7 @@ typedef wchar_t oschar_t; // UTF16-LE typedef char oschar_t; // UTF8 #endif - // Simple redirect macros for functions and types +// Simple redirect macros for functions and types #ifdef _WIN32 #define os_strlen wcslen #define os_strcmp wcscmp diff --git a/ctrtool/romfs.c b/ctrtool/romfs.c index 3ff49df3..05ce5c60 100644 --- a/ctrtool/romfs.c +++ b/ctrtool/romfs.c @@ -1,8 +1,5 @@ #include #include -#include -#include - #include "types.h" #include "romfs.h" #include "utils.h" diff --git a/ctrtool/utils.c b/ctrtool/utils.c index bda20d93..d2d4adb6 100644 --- a/ctrtool/utils.c +++ b/ctrtool/utils.c @@ -1,16 +1,10 @@ - #include -#include -#include -#include #include -#include -#include "utils.h" - #ifdef _WIN32 #include -#include #endif +#include "utils.h" + u32 align(u32 offset, u32 alignment) diff --git a/makerom/oschar.h b/makerom/oschar.h index 570a6ca6..9c788b07 100644 --- a/makerom/oschar.h +++ b/makerom/oschar.h @@ -3,9 +3,7 @@ #include #include #include -#include #ifdef _WIN32 -#include #include #endif @@ -19,7 +17,7 @@ typedef wchar_t oschar_t; // UTF16-LE typedef char oschar_t; // UTF8 #endif - // Simple redirect macros for functions and types +// Simple redirect macros for functions and types #ifdef _WIN32 #define os_strlen wcslen #define os_strcmp wcscmp diff --git a/makerom/utils.c b/makerom/utils.c index 92303ed2..999433e7 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -1,8 +1,4 @@ #include "lib.h" -#ifndef _WIN32 -#include -#endif - #include "polarssl/base64.h" #define IO_BLOCKSIZE 5*MB From c8023e202b1e8762b8a4419f510c81332f48e3ef Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 26 Nov 2015 11:28:09 +0800 Subject: [PATCH 131/317] [makerom] Misc, added code comments. --- makerom/user_settings.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index c2655684..8eada633 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -697,13 +697,16 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) int CheckArgumentCombination(user_settings *set) { + // If content 0 was not specified, we must build it (a NCCH file) if (set->common.contentPath[0] == NULL) { set->ncch.buildNcch0 = true; + // A CXI can contain elements of a CFA, but not the other way round. if (set->ncch.ncchType & CXI) set->ncch.ncchType = CXI; else set->ncch.ncchType = CFA; + // If we are creating a NCCH file (as opposed to CIA/CCI), specify which NCCH type is the output format if (set->common.outFormat == NCCH) set->common.outFormat = set->ncch.ncchType; } From 085e37296e42443f55461047f5dddf4ff1ce3a26 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 26 Nov 2015 11:39:23 +0800 Subject: [PATCH 132/317] [makerom] Added Homebrew NCCH Logo (credit: yellows8). Accessible via "BasicInfo/Logo: Homebrew" --- makerom/ncch.c | 9 +++++++++ makerom/ncch_logo.h | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/makerom/ncch.c b/makerom/ncch.c index ecfd7eea..577843b2 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -338,6 +338,15 @@ int ImportLogo(ncch_settings *set) } memcpy(set->sections.logo.buffer,iQue_without_ISBN_LZ,0x2000); } + else if (strcasecmp(set->rsfSet->BasicInfo.Logo, "homebrew") == 0) { + set->sections.logo.size = 0x2000; + set->sections.logo.buffer = malloc(set->sections.logo.size); + if (!set->sections.logo.buffer) { + fprintf(stderr, "[NCCH ERROR] Not enough memory\n"); + return MEM_ERROR; + } + memcpy(set->sections.logo.buffer, Homebrew_LZ, 0x2000); + } else if(strcasecmp(set->rsfSet->BasicInfo.Logo,"none") != 0){ fprintf(stderr,"[NCCH ERROR] Invalid logo name\n"); return NCCH_BAD_RSF_SET; diff --git a/makerom/ncch_logo.h b/makerom/ncch_logo.h index 3ba94388..018d2a86 100644 --- a/makerom/ncch_logo.h +++ b/makerom/ncch_logo.h @@ -23,4 +23,9 @@ static const unsigned char iQue_with_ISBN_LZ[0x2000] = static const unsigned char iQue_without_ISBN_LZ[0x2000] = { 0x11, 0x48, 0x65, 0x00, 0x00, 0x64, 0x61, 0x72, 0x63, 0xFF, 0xFE, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x28, 0x65, 0x00, 0x00, 0x83, 0x30, 0x09, 0x1C, 0x04, 0x00, 0x00, 0x40, 0x20, 0x03, 0x30, 0x13, 0xAB, 0x30, 0x18, 0x15, 0x20, 0x1D, 0x02, 0xA0, 0x0B, 0x06, 0x20, 0x2B, 0x30, 0x18, 0x5A, 0x09, 0x20, 0x35, 0x10, 0x20, 0x39, 0x30, 0x2B, 0xF8, 0x20, 0x41, 0x54, 0x85, 0x30, 0x0B, 0x05, 0x00, 0x00, 0xEC, 0x20, 0x4D, 0x98, 0x30, 0x17, 0xD0, 0x20, 0x28, 0x30, 0x17, 0xDC, 0x30, 0x23, 0x07, 0x00, 0x00, 0xCC, 0x09, 0x0C, 0x00, 0x00, 0x20, 0x20, 0x51, 0x20, 0x14, 0x20, 0x2B, 0xA0, 0x20, 0x0B, 0x64, 0x20, 0x5D, 0x80, 0x20, 0x00, 0x00, 0x4C, 0x99, 0x20, 0x5C, 0xA8, 0x01, 0x50, 0x53, 0x20, 0x22, 0x00, 0xB2, 0x20, 0x75, 0x34, 0xE0, 0x22, 0x20, 0x13, 0x20, 0x74, 0xE2, 0x20, 0x81, 0xA0, 0x25, 0x13, 0x00, 0x00, 0x3C, 0x20, 0x68, 0x12, 0x02, 0x50, 0x77, 0x30, 0x8F, 0x41, 0x1C, 0x20, 0x90, 0x00, 0x36, 0x00, 0x00, 0x28, 0x20, 0xAB, 0x4D, 0x3E, 0x20, 0x9C, 0x80, 0x3A, 0x20, 0x0B, 0x20, 0xAD, 0x60, 0x30, 0x17, 0x54, 0x3C, 0x50, 0x17, 0x82, 0x30, 0x17, 0x40, 0x20, 0x23, 0x08, 0x00, 0x2D, 0x00, 0xA4, 0x30, 0x2F, 0x49, 0x20, 0x67, 0x20, 0xE9, 0xC4, 0x30, 0x3B, 0x56, 0x4A, 0x50, 0x17, 0xDC, 0x30, 0x3B, 0x52, 0x20, 0x47, 0x20, 0xE0, 0xF4, 0xB2, 0x30, 0x53, 0x55, 0x20, 0x53, 0x30, 0xD4, 0x00, 0x2E, 0x21, 0x13, 0x61, 0x02, 0x00, 0x6E, 0x00, 0x69, 0x00, 0x6D, 0x21, 0x1D, 0x4E, 0x82, 0x20, 0x07, 0x6E, 0x00, 0x74, 0x00, 0x65, 0x20, 0x11, 0x64, 0x0A, 0x00, 0x6F, 0x00, 0x4C, 0x20, 0x03, 0x67, 0x20, 0x07, 0x5F, 0x28, 0x00, 0x44, 0x20, 0x03, 0x30, 0x20, 0x01, 0x5F, 0x00, 0x53, 0x20, 0x00, 0x63, 0x40, 0x1F, 0x65, 0x00, 0x4F, 0x00, 0x75, 0xAB, 0x20, 0x2B, 0x41, 0x20, 0x43, 0x62, 0x20, 0x13, 0x6C, 0x40, 0x47, 0x02, 0x50, 0x43, 0x57, 0x42, 0x03, 0x20, 0x43, 0x43, 0x01, 0x80, 0x87, 0x55, 0x03, 0x20, 0xCB, 0x00, 0x90, 0x43, 0x01, 0x90, 0xCB, 0xD7, 0x00, 0x90, 0x87, 0xF0, 0xCB, 0x62, 0x21, 0x5D, 0x79, 0x21, 0x97, 0x01, 0x31, 0xA1, 0x71, 0x8D, 0xEA, 0x00, 0xF0, 0x2F, 0x71, 0x05, 0xD0, 0x2F, 0x74, 0x42, 0x09, 0x67, 0x23, 0x29, 0x33, 0xBD, 0x22, 0x01, 0x73, 0xA2, 0x01, 0xB0, 0x5B, 0x52, 0x2D, 0x00, 0x10, 0x21, 0x31, 0x01, 0x00, 0x21, 0x55, 0x32, 0x01, 0x00, 0x43, 0x33, 0xE0, 0x65, 0x4C, 0x23, 0x71, 0x4D, 0x22, 0xA3, 0x1F, 0x73, 0x00, 0x6B, 0x00, 0x40, 0x85, 0x30, 0x1F, 0x00, 0xB0, 0x17, 0xF0, 0x93, 0xF2, 0xE3, 0x41, 0x5F, 0x20, 0xB5, 0x32, 0x00, 0x38, 0x00, 0x78, 0x23, 0x68, 0x60, 0x34, 0xE0, 0xE1, 0x70, 0x02, 0x43, 0x4C, 0x41, 0x4E, 0xFF, 0x46, 0xFE, 0x23, 0xD0, 0x00, 0x02, 0x02, 0x34, 0x03, 0x33, 0x96, 0x70, 0x1D, 0x61, 0x74, 0x31, 0x23, 0x82, 0x44, 0x3E, 0x34, 0x59, 0x28, 0x33, 0x73, 0x10, 0xFF, 0xFF, 0xFF, 0x34, 0x4C, 0x53, 0x63, 0x65, 0x6E, 0x04, 0x65, 0x4F, 0x75, 0x74, 0x41, 0x24, 0x71, 0x47, 0x5F, 0x0C, 0x41, 0x5F, 0x30, 0x30, 0xA0, 0x4C, 0x40, 0x3B, 0x69, 0x31, 0xCD, 0x33, 0xA3, 0x50, 0x3F, 0x02, 0x00, 0x30, 0x59, 0x34, 0x9D, 0x68, 0x24, 0xA1, 0x00, 0x50, 0x5F, 0x4E, 0x69, 0x6E, 0x4C, 0x6F, 0x67, 0x70, 0x6F, 0xA0, 0x35, 0x34, 0xA0, 0x34, 0xBD, 0x43, 0x4C, 0x56, 0x43, 0x8F, 0x74, 0x3F, 0x00, 0x10, 0x02, 0x40, 0x87, 0x40, 0x0B, 0xD0, 0x5D, 0x20, 0x76, 0x20, 0x7F, 0x43, 0x34, 0xEC, 0x4E, 0x5F, 0x52, 0x6F, 0x6F, 0x73, 0x74, 0xD0, 0x7E, 0x00, 0x70, 0x4B, 0x80, 0x57, 0x00, 0x20, 0x20, 0x3B, 0x50, 0x3F, 0xF8, 0x00, 0x30, 0xFF, 0x34, 0xF7, 0xB0, 0xFF, 0x24, 0xD2, 0x80, 0xFF, 0x00, 0x00, 0x1E, 0xD5, 0x45, 0x4C, 0x70, 0xFF, 0x42, 0x40, 0xFF, 0x42, 0x00, 0x40, 0xFF, 0x9C, 0x80, 0x2D, 0xB2, 0x90, 0xFF, 0x5C, 0x01, 0xE0, 0xFF, 0x90, 0xB3, 0x34, 0xC2, 0x02, 0xD0, 0xF3, 0xC8, 0xFD, 0x20, 0xAA, 0xD0, 0xF3, 0x00, 0x70, 0xFF, 0xF1, 0xFF, 0x31, 0x39, 0x71, 0xFF, 0x65, 0x23, 0x31, 0xC5, 0xB1, 0xFF, 0x31, 0x84, 0x47, 0x5F, 0x43, 0x00, 0x81, 0xFF, 0x0F, 0xE1, 0xFF, 0x99, 0x02, 0x90, 0xFF, 0x12, 0xC3, 0x02, 0x31, 0xF3, 0x92, 0x3F, 0x80, 0xBF, 0x91, 0xF3, 0x7B, 0x60, 0x32, 0xB6, 0x00, 0x80, 0xFF, 0x36, 0xDF, 0x03, 0x32, 0xFF, 0x7C, 0x27, 0x27, 0x53, 0x3F, 0x55, 0x0D, 0x42, 0xFF, 0x48, 0x27, 0x9D, 0xB0, 0x27, 0xA1, 0xA4, 0x27, 0x8D, 0x60, 0x24, 0x27, 0x88, 0x36, 0xCB, 0x30, 0x03, 0x00, 0x00, 0xBC, 0xAD, 0x20, 0x03, 0x24, 0x27, 0xAB, 0x88, 0x37, 0x73, 0x27, 0x94, 0xB0, 0x26, 0xE7, 0x02, 0x74, 0x0A, 0x00, 0x00, 0x78, 0x0B, 0x32, 0xDF, 0x57, 0xF9, 0x00, 0x03, 0x5B, 0x37, 0x32, 0x27, 0x6E, 0x26, 0xB1, 0x33, 0xB7, 0x50, 0x41, 0x83, 0x2F, 0x71, 0x02, 0xF2, 0xE3, 0x73, 0xDB, 0x01, 0x13, 0x07, 0x50, 0x5F, 0x33, 0x00, 0x80, 0x67, 0x77, 0xB8, 0x60, 0x67, 0x20, 0xAA, 0x43, 0xC7, 0x38, 0x63, 0xC7, 0x30, 0x6F, 0x93, 0x9F, 0x53, 0xA0, 0x24, 0x0A, 0xA0, 0x27, 0xA5, 0x80, 0xBE, 0x24, 0x39, 0x23, 0x16, 0x23, 0xA0, 0xC0, 0x48, 0x79, 0x06, 0x02, 0x00, 0x30, 0x37, 0x70, 0x23, 0x00, 0x33, 0x33, 0xB3, 0x3F, 0xFC, 0x2D, 0xEC, 0xBC, 0x8C, 0x70, 0x0B, 0x0A, 0xD7, 0x23, 0x20, 0x0B, 0x30, 0x2F, 0x80, 0x3F, 0xBC, 0x48, 0xA9, 0x07, 0x01, 0xD0, 0x2F, 0xF4, 0x2F, 0xB0, 0x6B, 0x39, 0x0C, 0x40, 0x9D, 0x6F, 0x1C, 0x30, 0x97, 0x61, 0xEF, 0xCC, 0x28, 0x45, 0x30, 0x9B, 0x70, 0xF3, 0x24, 0x87, 0xDC, 0xE4, 0xB9, 0x71, 0x5B, 0x50, 0xF1, 0x5B, 0x29, 0x39, 0xD4, 0x8B, 0x70, 0xC1, 0x14, 0xCD, 0xCC, 0xCC, 0x20, 0xEB, 0x20, 0x24, 0x02, 0xF8, 0xC1, 0xF9, 0x00, 0x31, 0x67, 0x00, 0x14, 0xBB, 0x30, 0x7F, 0x30, 0x2F, 0x71, 0x73, 0x44, 0x73, 0xF5, 0x38, 0x94, 0x01, 0x90, 0x7F, 0x3C, 0xC2, 0x20, 0x7F, 0x3E, 0x02, 0xD0, 0x7F, 0x52, 0x65, 0x70, 0x64, 0x01, 0x70, 0xFF, 0xC1, 0xEB, 0x45, 0x43, 0xC2, 0xB7, 0x6D, 0xDB, 0xAE, 0x20, 0x7F, 0x8C, 0x25, 0x02, 0x20, 0x00, 0x40, 0xFF, 0x91, 0xF7, 0xB6, 0x1B, 0x5C, 0xA0, 0xA4, 0x87, 0xB4, 0x35, 0x3E, 0x00, 0x92, 0x24, 0xE9, 0xC0, 0xBD, 0x60, 0x8B, 0x31, 0x00, 0x32, 0xE7, 0x35, 0x33, 0xF2, 0xE7, 0x92, 0x53, 0xF0, 0x26, 0x82, 0x7F, 0x20, 0x25, 0x1B, 0x90, 0x0B, 0x50, 0x97, 0x34, 0xEF, 0x00, 0x50, 0x97, 0x96, 0x53, 0x30, 0x3B, 0xAF, 0x77, 0x04, 0xAA, 0xF1, 0x17, 0x32, 0x02, 0x13, 0x73, 0x32, 0xF7, 0x50, 0x8B, 0x00, 0x96, 0x7B, 0xF9, 0x33, 0x1B, 0x57, 0x78, 0x00, 0x31, 0x7F, 0x3B, 0x90, 0x57, 0x07, 0x54, 0x53, 0x2A, 0xEE, 0xB7, 0x47, 0x73, 0x4C, 0x37, 0x8B, 0x2B, 0xD9, 0x94, 0x63, 0x8B, 0x3B, 0x87, 0x3B, 0xD1, 0x61, 0x24, 0x4B, 0xD5, 0x00, 0x01, 0x97, 0xC0, 0x39, 0x8E, 0xE3, 0x2B, 0x39, 0xE8, 0x51, 0x5B, 0x5B, 0xFD, 0x01, 0x20, 0x23, 0x03, 0xF7, 0x6B, 0x40, 0x41, 0x9E, 0x11, 0x15, 0x8D, 0xBD, 0x31, 0xA3, 0x9A, 0x99, 0xB9, 0x2B, 0x7D, 0xAA, 0x2C, 0x5A, 0x04, 0x01, 0x10, 0x23, 0x01, 0x01, 0x20, 0x8F, 0x01, 0x01, 0x20, 0x8F, 0x01, 0xAD, 0x00, 0x10, 0x8F, 0xC1, 0x20, 0x8F, 0x3D, 0x60, 0x8F, 0x44, 0x5B, 0x01, 0x00, 0x10, 0x8F, 0x82, 0x00, 0x00, 0x23, 0x50, 0x5F, 0x42, 0x6C, 0x6B, 0x03, 0xE1, 0x63, 0x9A, 0x03, 0x99, 0x19, 0xC0, 0x0E, 0x74, 0xDA, 0x00, 0xC1, 0x63, 0x00, 0x40, 0x23, 0xFE, 0x04, 0x61, 0x63, 0x00, 0x30, 0x8F, 0xF1, 0x63, 0x00, 0x40, 0x23, 0x03, 0x81, 0x63, 0x00, 0x13, 0xBB, 0x00, 0x32, 0xC7, 0x58, 0xAA, 0x2E, 0x9D, 0x88, 0x66, 0x4F, 0xE8, 0x2E, 0xA9, 0x18, 0x2E, 0x95, 0x48, 0xBE, 0x2E, 0x99, 0x78, 0x62, 0xC7, 0xE3, 0xD3, 0x56, 0x3F, 0x60, 0x0B, 0x52, 0xD3, 0xF0, 0xF7, 0xC2, 0xD3, 0x01, 0xC0, 0x2F, 0x22, 0xDF, 0xD4, 0x33, 0x40, 0x68, 0x03, 0x50, 0x0B, 0x52, 0xEB, 0x7F, 0xF0, 0xC2, 0xEB, 0x01, 0xB0, 0x2F, 0x32, 0xF7, 0x01, 0xB0, 0xBF, 0x33, 0x03, 0x01, 0xC0, 0x2F, 0x00, 0x10, 0xBF, 0xFF, 0x46, 0x73, 0x70, 0x0B, 0x53, 0x1B, 0x40, 0xBF, 0x83, 0x1B, 0x01, 0xB0, 0x2F, 0x63, 0x27, 0x03, 0xC1, 0xC3, 0xFF, 0x33, 0x27, 0x75, 0x97, 0x93, 0x33, 0x00, 0x91, 0xC3, 0x01, 0x00, 0x2F, 0x05, 0xE1, 0xC3, 0x00, 0xF0, 0xBF, 0xF1, 0xC3, 0xFF, 0x01, 0x00, 0x2F, 0x05, 0x01, 0xC3, 0x00, 0x16, 0xB7, 0x00, 0x36, 0x4F, 0x35, 0xCC, 0x33, 0x8B, 0x36, 0x53, 0x33, 0x8F, 0x5F, 0xA0, 0x69, 0xE3, 0xD0, 0x6D, 0xAB, 0xE6, 0xCF, 0x59, 0xC7, 0x26, 0x43, 0x00, 0x40, 0x17, 0xFF, 0x26, 0x37, 0xD6, 0xFF, 0x63, 0x57, 0x26, 0x2B, 0x00, 0x30, 0x17, 0x36, 0x1F, 0x00, 0x30, 0x5F, 0x36, 0x13, 0xFF, 0x00, 0x40, 0x17, 0x00, 0x10, 0x5F, 0x49, 0x6B, 0x35, 0xFB, 0x00, 0x30, 0x17, 0x65, 0xEF, 0x03, 0xC1, 0x03, 0x82, 0xC7, 0xFF, 0xE1, 0x03, 0x82, 0xDF, 0x02, 0xE1, 0x03, 0x73, 0x27, 0xF1, 0x03, 0x80, 0x17, 0x01, 0xE1, 0x03, 0x00, 0xFD, 0xDF, 0x6E, 0x54, 0xEC, 0xDF, 0x02, 0x7E, 0xDF, 0x04, 0x2F, 0xA4, 0x5F, 0x0D, 0xDC, 0xDF, 0x50, 0xAA, 0x2F, 0xCC, 0xB8, 0x2F, 0xD0, 0x20, 0x2F, 0xBC, 0xA0, 0x2F, 0xC0, 0x2C, 0xAA, 0x2C, 0xE3, 0xAC, 0x2C, 0xE7, 0x10, 0x2C, 0xDF, 0x74, 0x2C, 0xDF, 0xD8, 0x5F, 0x07, 0x2D, 0x17, 0x09, 0x2C, 0x31, 0x2C, 0xE3, 0x02, 0x9C, 0xDF, 0x3E, 0xCF, 0x00, 0xFC, 0xDF, 0xD7, 0x9E, 0xF3, 0x00, 0xBC, 0xDF, 0x7C, 0xEC, 0xDF, 0x2C, 0x6D, 0x5B, 0xDD, 0x4F, 0x4C, 0xE3, 0xFF, 0x8C, 0xD3, 0xD0, 0x17, 0x9C, 0xBB, 0x00, 0x30, 0x17, 0x00, 0x9F, 0xD3, 0x3F, 0xFD, 0x01, 0x1C, 0x8B, 0x00, 0x0D, 0xE7, 0xBF, 0xC3, 0xA3, 0xA0, 0x2C, 0x7B, 0x00, 0x5C, 0x7F, 0xD0, 0x23, 0x01, 0x1C, 0x73, 0x03, 0xD0, 0x67, 0x02, 0x7C, 0x5B, 0x6F, 0x70, 0x2B, 0xCB, 0x7C, 0x5B, 0x20, 0x3B, 0xDB, 0x00, 0xED, 0x5B, 0x3E, 0x67, 0x7F, 0xD7, 0xEF, 0x5B, 0xCF, 0x00, 0xFC, 0x4F, 0x00, 0xDC, 0xDB, 0xF0, 0x2D, 0xCB, 0x80, 0x7F, 0x01, 0x2C, 0xDB, 0x30, 0x2F, 0xBF, 0x7F, 0xFB, 0xC8, 0x2F, 0x03, 0x7C, 0xDB, 0x5B, 0xE7, 0xAC, 0xDB, 0x00, 0x4C, 0x4F, 0x03, 0x3D, 0x67, 0xFF, 0x00, 0x1E, 0x67, 0xDD, 0x67, 0x04, 0x1C, 0x67, 0x41, 0xA3, 0x79, 0x93, 0x9D, 0x43, 0xCC, 0x67, 0x00, 0x60, 0x23, 0xFF, 0xCC, 0x67, 0x31, 0xEB, 0x89, 0x7B, 0x28, 0x2F, 0x00, 0x3C, 0x67, 0x00, 0x50, 0x23, 0xDC, 0x67, 0x00, 0x50, 0x8F, 0xFF, 0xDC, 0x67, 0x00, 0x60, 0x23, 0x00, 0x10, 0x8F, 0x79, 0x4B, 0x30, 0x8F, 0x00, 0x2C, 0x67, 0x00, 0x50, 0x23, 0x04, 0x1C, 0x67, 0x3F, 0x70, 0xC2, 0xA9, 0x33, 0x00, 0x81, 0x63, 0x00, 0x40, 0x23, 0x04, 0x61, 0x63, 0x00, 0x30, 0x8F, 0xF1, 0x63, 0xFF, 0x00, 0x40, 0x23, 0x03, 0x81, 0x63, 0x01, 0x5C, 0x67, 0x01, 0x9F, 0x2F, 0x43, 0xEB, 0x6F, 0x2F, 0x9F, 0x97, 0xDF, 0x2F, 0xFF, 0x00, 0x60, 0x23, 0xCF, 0x2F, 0x34, 0x33, 0x7F, 0x2F, 0x38, 0x2F, 0x00, 0x3F, 0x2F, 0x00, 0x50, 0x23, 0xDF, 0x2F, 0xFF, 0x00, 0x50, 0x8F, 0xDF, 0x2F, 0x00, 0x60, 0x23, 0x00, 0x10, 0x8F, 0x6F, 0x2F, 0x40, 0x8F, 0x00, 0x2F, 0x2F, 0x00, 0x50, 0x23, 0xFF, 0x01, 0x7C, 0x07, 0x01, 0xB1, 0x63, 0x9F, 0x2F, 0x00, 0x91, 0x63, 0x00, 0x40, 0x23, 0x04, 0x61, 0x63, 0x00, 0x30, 0x8F, 0xF1, 0x63, 0xFF, 0x00, 0x40, 0x23, 0x03, 0x81, 0x63, 0x01, 0x5B, 0xA7, 0x01, 0x95, 0x8F, 0x46, 0x27, 0x6F, 0x23, 0x9F, 0xDF, 0xD5, 0x8F, 0xFF, 0x00, 0x60, 0x23, 0xC5, 0x8F, 0x4F, 0xDF, 0x6F, 0x0B, 0x4F, 0xDF, 0x00, 0x25, 0x8F, 0x00, 0x50, 0x23, 0xD5, 0x8F, 0xFF, 0x00, 0x50, 0x8F, 0xD5, 0x8F, 0x00, 0x60, 0x23, 0x00, 0x10, 0x8F, 0x6E, 0xDB, 0x40, 0x8F, 0x00, 0x25, 0x8F, 0x00, 0x50, 0x23, 0xFF, 0x01, 0x7C, 0x07, 0x01, 0xB1, 0x63, 0x9E, 0xC3, 0x00, 0x91, 0x63, 0x00, 0x40, 0x23, 0x04, 0x61, 0x63, 0x00, 0x30, 0x8F, 0xF1, 0x63, 0xEF, 0x00, 0x40, 0x23, 0x03, 0x61, 0x63, 0x00, 0x7C, 0x5F, 0x4C, 0x2C, 0x5C, 0xBC, 0x5F, 0x3E, 0x51, 0x7C, 0x5F, 0x18, 0x65, 0x00, 0x73, 0xCC, 0x5F, 0x3B, 0x9C, 0x47, 0x5F, 0x43, 0xAF, 0x00, 0x4C, 0x5F, 0xFC, 0x2E, 0xBB, 0x0F, 0x5B, 0x58, 0x4C, 0x5F, 0x3C, 0x82, 0x3B, 0xAB, 0x55, 0xBC, 0x2F, 0xFF, 0xFC, 0x2F, 0xF8, 0x3C, 0x2E, 0xDB, 0x7C, 0x2E, 0xDF, 0x7C, 0xBC, 0x2E, 0xE3, 0x00, 0x3C, 0x47, 0x3E, 0xC7, 0x5E, 0xFB, 0x00, 0x7A, 0x3B, 0x80, 0xBF, 0xBD, 0x9A, 0x3B, 0x60, 0x88, 0xC7, 0x00, 0x3C, 0x2B, 0x00, 0x70, 0x4B, 0x9F, 0x2B, 0xCA, 0x2A, 0xB7, 0xDF, 0x00, 0x9B, 0xCB, 0x01, 0x10, 0x3F, 0xF2, 0xA0, 0x3F, 0x00, 0x1B, 0xA3, 0x01, 0xD0, 0x3F, 0x00, 0x29, 0xEF, 0x01, 0x00, 0xBF, 0x7B, 0x8E, 0x2B, 0x77, 0x70, 0xFF, 0x00, 0x27, 0x67, 0x01, 0x00, 0xFF, 0x24, 0xF0, 0x3F, 0xDF, 0x83, 0xB2, 0x01, 0x01, 0x3F, 0x30, 0x00, 0x8E, 0xBB, 0x5E, 0xBF, 0x59, 0x54, 0x7E, 0xBF, 0xA8, 0xA1, 0x2E, 0xBC, 0x10, 0x2F, 0xFB, 0x6C, 0x79, 0x74, 0x31, 0x3E, 0xD1, 0x90, 0x4A, 0x9E, 0x00, 0xA0, 0x2D, 0xE7, 0x70, 0x43, 0x74, 0x78, 0x36, 0x6C, 0x31, 0x3F, 0xDF, 0x3F, 0xBF, 0x04, 0x2F, 0xFF, 0x2D, 0x55, 0x74, 0x00, 0x65, 0x6E, 0x64, 0x6F, 0x5F, 0x31, 0x32, 0x38, 0x00, 0x78, 0x36, 0x34, 0x2E, 0x62, 0x63, 0x6C, 0x69, 0x57, 0x6D, 0x2F, 0xFF, 0x6D, 0x2E, 0xFB, 0x60, 0x62, 0x23, 0x30, 0x4B, 0x4D, 0x7F, 0x08, 0x4C, 0x6F, 0x67, 0x6F, 0xAE, 0xE9, 0xFF, 0xFF, 0xFF, 0xDF, 0x30, 0x03, 0x00, 0x40, 0x02, 0x15, 0x5F, 0xE0, 0x30, 0x62, 0xAF, 0x72, 0x3E, 0x07, 0x5E, 0x0B, 0x08, 0x70, 0x61, 0x6E, 0x31, 0x3B, 0x4F, 0x01, 0x04, 0xFF, 0x00, 0x00, 0x52, 0x6F, 0x6F, 0x74, 0x50, 0x61, 0x6E, 0x70, 0x65, 0x00, 0xB0, 0xDF, 0x00, 0x50, 0x47, 0x50, 0xD3, 0x70, 0x61, 0x73, 0x31, 0xD3, 0x3B, 0xA3, 0x70, 0x53, 0x03, 0x20, 0x53, 0x4E, 0x5F, 0x30, 0x55, 0x00, 0x0F, 0x96, 0xD0, 0x01, 0x20, 0x53, 0x4C, 0xD3, 0x42, 0x80, 0x53, 0x69, 0x63, 0x31, 0x80, 0x9F, 0x3C, 0x0F, 0x07, 0xFF, 0x00, 0x41, 0x03, 0x7E, 0xE0, 0x2D, 0x5B, 0x82, 0x1F, 0x00, 0x20, 0xEF, 0x9E, 0x2F, 0x63, 0x80, 0x42, 0xF1, 0x2B, 0x71, 0x99, 0xCF, 0x23, 0xC1, 0x27, 0x80, 0x0C, 0x3F, 0x70, 0x61, 0x65, 0x60, 0xDB, 0x50, 0x07, 0x67, 0x72, 0x30, 0x70, 0x31, 0x3C, 0x97, 0x31, 0x33, 0x47, 0x72, 0x6F, 0x75, 0xCE, 0x3C, 0x8F, 0x7F, 0xE7, 0x67, 0x72, 0x51, 0x07, 0x30, 0x23, 0x34, 0x57, 0x47, 0x3F, 0x5F, 0x41, 0xCF, 0xD3, 0x3F, 0xCF, 0xF1, 0x17, 0xF1, 0xD7, 0x30, 0x5F, 0x3F, 0xDF, 0x1F, 0x47, 0x5F, 0x42, 0xCF, 0x6D, 0x3F, 0xE7, 0x00, 0x90, 0x2B, 0xD4, 0x9F, 0x3F, 0xFB, 0x9A, 0xF1, 0x7F, 0x67, 0x72, 0x50, 0xC7, 0x01, 0x32, 0xBF, 0x3C, 0x22, 0xBC, 0x22, 0xB5, 0x00, 0x02, 0xBF, 0xC8, 0x82, 0xBF, 0x3D, 0x7F, 0x07, 0x64, 0xBB, 0x2D, 0x2F, 0xFF, 0x9D, 0x2F, 0x08, 0x00, 0x4F, 0x34, 0xA1, 0x32, 0xF3, 0x2F, 0xFF, 0x78, 0x2F, 0xFB, 0x1D, 0x33, 0x64, 0x73, 0x62, 0xAD, 0x62, 0xD2, 0x80, 0x10, 0x31, 0xF0, 0x10, 0x53, 0x32, 0xF0, 0x21, 0x33, 0x63, 0x05, 0x4C, 0x54, 0x90, 0x3E, 0x30, 0x0B, 0x83, 0x70, 0x39, 0x4C, 0x54, 0x4D, 0x61, 0x73, 0x3C, 0xCC, 0x63, 0x2D, 0x82, 0x33, 0x2B, 0xA8, 0x06, 0x00, 0x00, 0x0B, 0x2F, 0xFC, 0x38, 0xAA, 0x2F, 0xFF, 0x88, 0x2F, 0xFB, 0x08, 0x2F, 0xF4, 0x88, 0x2F, 0xF8, 0x08, 0xAA, 0x2F, 0xD4, 0x88, 0x2F, 0xD8, 0x4C, 0x2F, 0x10, 0xE8, 0x2F, 0x14, 0xAC, 0x9D, 0x23, 0x74, 0x48, 0x05, 0x2F, 0xD7, 0x20, 0x2F, 0x00, 0x35, 0x47, 0xAA, 0x53, 0x57, 0xBB, 0x00, 0x83, 0x53, 0x02, 0x00, 0xA3, 0x53, 0x00, 0x35, 0x57, 0x00, 0xB3, 0xA3, 0xD5, 0x20, 0xB3, 0x00, 0xB3, 0xA3, 0x68, 0x40, 0x24, 0x20, 0x30, 0x03, 0x11, 0x2F, 0xFB, 0x74, 0x06, 0x00, 0x61, 0x04, 0x30, 0x03, 0x30, 0x0B, 0x65, 0x06, 0x00, 0x01, 0x30, 0x03, 0xE9, 0x30, 0x17, 0x31, 0x87, 0x5E, 0x03, 0x05, 0x2F, 0x47, 0x4F, 0x5F, 0x31, 0x32, 0xB0, 0x01, 0xB0, 0x7F, 0x01, 0x03, 0xE0, 0x7F, 0xD2, 0xB5, 0x00, 0x00, 0x8C, 0x8C, 0x77, 0x8C, 0x05, 0xA0, 0x7F, 0x00, 0x16, 0x97, 0x00, 0xF1, 0x7F, 0x03, 0x03, 0xC1, 0x7F, 0x00, 0x16, 0xD7, 0x45, 0xA7, 0xCB, 0x2F, 0xB4, 0x00, 0x25, 0xA7, 0x80, 0x3F, 0x23, 0x44, 0x04, 0x25, 0xA3, 0x30, 0x03, 0x00, 0x06, 0x00, 0x06, 0x06, 0x73, 0x09, 0xED, 0xBE, 0xC3, 0x30, 0x03, 0x3F, 0xFB, 0x3A, 0x57, 0xDE, 0x40, 0x30, 0x03, 0xE0, 0x13, 0x70, 0xC0, 0x30, 0x03, 0x00, 0x75, 0xD3, 0x78, 0x54, 0x10, 0x04, 0x00, 0x01, 0x0A, 0x10, 0x04, 0x10, 0x02, 0xA2, 0x37, 0x46, 0xF2, 0x37, 0x26, 0x0F, 0x06, 0x02, 0x05, 0x66, 0x23, 0x33, 0x00, 0x12, 0x43, 0x4D, 0x5F, 0x00, 0x35, 0xB6, 0x96, 0x00, 0x36, 0x6A, 0x80, 0xEA, 0x23, 0x77, 0x05, 0x26, 0x67, 0x30, 0x03, 0x30, 0x1F, 0x8E, 0xE3, 0xBE, 0x30, 0x03, 0xB0, 0xBF, 0xE0, 0x13, 0xC0, 0xBF, 0x50, 0xA7, 0x78, 0x00, 0x00, 0xC0, 0xA7, 0x00, 0x21, 0x5F, 0xCF, 0x5F, 0x01, 0xB1, 0x5F, 0x2F, 0xA1, 0xBD, 0x61, 0xBF, 0x30, 0x03, 0x3F, 0xF7, 0xCE, 0x95, 0x17, 0x41, 0x30, 0x03, 0xBC, 0xE0, 0x13, 0xC1, 0x30, 0x03, 0x05, 0x21, 0x5F, 0x00, 0x00, 0xC3, 0x01, 0x31, 0x5F, 0x61, 0x0B, 0x3F, 0xB6, 0xBF, 0x30, 0x03, 0xB0, 0xBF, 0xE0, 0x13, 0xC0, 0xBF, 0x02, 0x61, 0x5F, 0xCD, 0xF7, 0xFF, 0x01, 0xB2, 0xBF, 0x3F, 0xF7, 0x50, 0x03, 0x5D, 0xA7, 0x50, 0x03, 0xC0, 0x13, 0x4F, 0xF7, 0x05, 0x22, 0xBF, 0xFF, 0x00, 0x00, 0xC3, 0x01, 0x32, 0xBF, 0x3F, 0xE3, 0x3F, 0xE7, 0xB0, 0xBF, 0xE0, 0x13, 0xC0, 0xBF, 0x01, 0xF2, 0xBF, 0xFF, 0x03, 0x59, 0x73, 0x57, 0x87, 0x00, 0x59, 0x73, 0x00, 0x1C, 0x63, 0x99, 0x1F, 0x40, 0x03, 0x00, 0x79, 0x1F, 0x3F, 0x93, 0xAC, 0xC9, 0x73, 0xA0, 0x4E, 0xC7, 0xF5, 0x00, 0x84, 0xD3, 0x70, 0x53, 0x46, 0x31, 0x74, 0x32, 0x00, 0x63, 0x2F, 0x58, 0x2F, 0xFA, 0x9F, 0x05, 0x4F, 0xF1, 0x00, 0x00, 0x13, 0xCD, 0xCC, 0x4C, 0x28, 0x36, 0x80, 0x3F, 0x50, 0x07, 0x40, 0x0F, 0xFF, 0x4A, 0x9B, 0x50, 0x07, 0x9F, 0xEC, 0xE0, 0x0B, 0x40, 0x03, 0x00, 0x0A, 0x1B, 0x2A, 0xC3, 0x00, 0xA4, 0xB7, 0xBE, 0x60, 0xFB, 0x20, 0x01, 0xC0, 0xA7, 0x27, 0x62, 0x01, 0x00, 0xA7, 0x8A, 0x1B, 0x81, 0x2F, 0x91, 0xC5, 0x00, 0x84, 0xA3, 0x71, 0x83, 0x10, 0xF4, 0xBF, 0x01, 0xC1, 0x2F, 0x07, 0x04, 0xD1, 0x2F, 0xDF, 0x00, 0x80, 0xA7, 0x01, 0xF1, 0x2F, 0x08, 0x02, 0x41, 0x2F, 0x00, 0x5D, 0x8B, 0xE1, 0xB7, 0xEB, 0xD7, 0x00, 0xD2, 0x5F, 0x77, 0x09, 0x04, 0xD2, 0x5F, 0x00, 0xB0, 0xA7, 0x01, 0xC3, 0x07, 0x0A, 0x01, 0xE2, 0x5F, 0x92, 0xE7, 0x00, 0x1F, 0xFB, 0xAD, 0xDD, 0x07, 0xA0, 0x00, 0x68, 0x1F, 0x10, 0x43, 0xE3, 0x00, 0x5D, 0x03, 0x50, 0x43, 0x83, 0x1F, 0x00, 0x00, 0x44, 0x6D, 0x03, 0x30, 0x0F, 0x3E, 0x2B, 0x30, 0x0F, 0x2D, 0x03, 0x9B, 0x6D, 0x83, 0x03, 0x05, 0x6D, 0x83, 0x00, 0x4D, 0xD6, 0xF8, 0x3F, 0x1F, 0x00, 0xA0, 0x7F, 0x5E, 0x80, 0x00, 0x40, 0x7F, 0x01, 0x01, 0x4D, 0x83, 0x00, 0x3E, 0x0B, 0x00, 0x1A, 0xEB, 0x01, 0x94, 0xEB, 0x80, 0x7B, 0x41, 0xFF, 0x37, 0x5F, 0xE9, 0x00, 0xEE, 0x0B, 0x7E, 0x8B, 0x01, 0x50, 0x7F, 0xFA, 0xEB, 0xBD, 0x02, 0xB0, 0x7F, 0x03, 0x8E, 0x8B, 0x9E, 0x7B, 0xFE, 0x9B, 0x00, 0x02, 0x0F, 0x03, 0x3F, 0x13, 0xFD, 0x00, 0x1A, 0xF3, 0x01, 0x51, 0x8F, 0x3F, 0x13, 0x00, 0x12, 0x0F, 0x2A, 0x3A, 0x04, 0x8F, 0x13, 0xDC, 0x00, 0x2F, 0x13, 0x7F, 0x0C, 0x4F, 0x13, 0xD6, 0xB7, 0xFD, 0x97, 0xFD, 0x57, 0xFC, 0xE7, 0xFC, 0x77, 0xFC, 0x07, 0xFE, 0xFB, 0x97, 0xFA, 0xE3, 0xFA, 0x57, 0xF9, 0xA3, 0xF9, 0x17, 0xF8, 0x63, 0x3F, 0xEF, 0xCC, 0xEB, 0x00, 0x2F, 0xB3, 0x3E, 0x8F, 0x0A, 0x30, 0xCB, 0x2C, 0x41, 0xA7, 0x43, 0xCF, 0x1B, 0x37, 0x0D, 0x98, 0xF8, 0x5F, 0x67, 0x72, 0x56, 0xFF, 0x04, 0x40, 0x02, 0x60, 0x00, 0x5A, 0x3C, 0xF8, 0xFA, 0x34, 0x9F, 0x2C, 0x5F, 0x4F, 0x27, 0x00, 0x0C, 0x91, 0x82, 0xA0, 0x00, 0xFC, 0xFF, 0x5F, 0x00, 0xFE, 0x00, 0xFF, 0x5A, 0xC0, 0x48, 0xBE, 0xED, 0x75, 0xDF, 0x00, 0x28, 0xF9, 0x20, 0xFF, 0x4C, 0xFE, 0x3F, 0xDD, 0x1A, 0xEF, 0x68, 0x74, 0x08, 0x40, 0x04, 0x20, 0x50, 0x84, 0x3F, 0xFF, 0xF6, 0x40, 0xFF, 0x26, 0x2F, 0xFF, 0x63, 0x03, 0x00, 0x00, 0xC0, 0xF4, 0x00, 0x10, 0xFD, 0xFF, 0xE8, 0x80, 0x2F, 0xB8, 0xF5, 0xF0, 0xFF, 0xFF, 0x20, 0x50, 0xFF, 0x12, 0xFF, 0x30, 0x20, 0x58, 0x09, 0x9F, 0x6F, 0x9F, 0xD9, 0x4F, 0x04, 0x4F, 0x00, 0x00, 0x6F, 0xAF, 0xBF, 0xE9, 0xF2, 0x60, 0x86, 0x5F, 0xEF, 0x0E, 0x8F, 0xFF, 0xF9, 0x51, 0x37, 0x3F, 0xFF, 0xF5, 0x0C, 0xF2, 0xFF, 0xFF, 0xF2, 0x20, 0x03, 0x4F, 0xE3, 0x26, 0x00, 0x64, 0x22, 0x4F, 0xEB, 0x29, 0x82, 0xFF, 0x58, 0x28, 0xE3, 0x06, 0xFF, 0x06, 0xFF, 0x2F, 0x2F, 0x00, 0x00, 0x30, 0x03, 0x4F, 0xFF, 0x22, 0xA0, 0xA0, 0x1F, 0xC1, 0x50, 0x77, 0x0A, 0x0A, 0x00, 0x30, 0x0A, 0x14, 0x0A, 0xD0, 0xF3, 0x6F, 0xE6, 0x64, 0x4F, 0xEE, 0x46, 0x00, 0x20, 0x00, 0xE3, 0x24, 0x2A, 0x8F, 0x9F, 0x00, 0x66, 0xFF, 0x06, 0xF4, 0x2D, 0xDF, 0xC0, 0xC6, 0x90, 0x6F, 0xBF, 0xFB, 0x04, 0xC7, 0x01, 0x52, 0x1C, 0x20, 0xB7, 0x20, 0x00, 0xF0, 0x20, 0xEB, 0x30, 0x03, 0x2F, 0xB1, 0x60, 0xF8, 0x30, 0xCB, 0x20, 0xB6, 0xFF, 0x66, 0x00, 0x66, 0x00, 0x70, 0x02, 0x20, 0xE3, 0x20, 0x6E, 0x2F, 0xC7, 0x00, 0x04, 0xFF, 0xFF, 0x20, 0x0D, 0x8F, 0x7F, 0xDC, 0xF7, 0x30, 0xFF, 0xFD, 0x00, 0x00, 0x00, 0x30, 0x00, 0x19, 0xFF, 0x00, 0xAA, 0xE6, 0xC1, 0x61, 0xE7, 0x2E, 0xF9, 0xC0, 0xF5, 0xF9, 0xFF, 0xDF, 0x20, 0xD5, 0x00, 0x07, 0x10, 0xFC, 0xFC, 0xAF, 0xDF, 0xFF, 0x02, 0x21, 0x9F, 0x6F, 0x3A, 0x6E, 0x35, 0x00, 0xD7, 0x02, 0x30, 0x41, 0x18, 0x00, 0xFF, 0xFD, 0x40, 0xEF, 0x21, 0x3B, 0x00, 0x00, 0xF9, 0x00, 0xF2, 0x01, 0x0B, 0x20, 0x00, 0xCF, 0xFF, 0x21, 0x02, 0xFF, 0xA8, 0x00, 0x5A, 0x00, 0x20, 0x21, 0x63, 0x20, 0x80, 0x2F, 0xA5, 0x0B, 0x0A, 0xF6, 0xF6, 0x0A, 0x0C, 0xF6, 0x00, 0xF2, 0xF7, 0x8E, 0xFF, 0xFF, 0x21, 0x00, 0x69, 0x02, 0x00, 0x8F, 0xFF, 0xA2, 0x06, 0x12, 0x3F, 0xC3, 0x4F, 0x01, 0xFF, 0x00, 0xDF, 0xFF, 0x00, 0x1C, 0xFF, 0x3F, 0xCD, 0x00, 0x00, 0x00, 0xC3, 0xFF, 0xFC, 0xFF, 0xFF, 0x40, 0x00, 0x00, 0xA9, 0x00, 0xFB, 0xFF, 0xFF, 0x4F, 0x6A, 0xAB, 0x2F, 0x74, 0x0A, 0x3F, 0x7A, 0x09, 0x9F, 0xF9, 0x03, 0x04, 0x82, 0x70, 0xEF, 0xFE, 0x81, 0x7F, 0x9C, 0xF6, 0xFF, 0xFF, 0x10, 0x00, 0xC6, 0x3F, 0xA4, 0x1C, 0x6F, 0xFF, 0x00, 0xA0, 0x88, 0x05, 0x00, 0x8F, 0x01, 0x5C, 0xC1, 0x43, 0x4C, 0x04, 0x49, 0x4D, 0xFF, 0xFE, 0x14, 0x32, 0x7C, 0x02, 0x28, 0xC1, 0x29, 0xF7, 0x3B, 0x5D, 0x69, 0x6D, 0x61, 0x67, 0x10, 0x4B, 0xE4, 0x18, 0x20, 0x00, 0x0D, 0x42, 0x30, 0x07, 0x03, 0x8B, 0xA0, 0x00, 0x71, 0x05, 0x00, 0x88, 0xFE, 0xFF, 0xFF, 0x25, 0xCE, 0xC0, 0x2E, 0xCB, 0x01, 0xC0, 0xC0, 0xAF, 0x9F, 0x00, 0x00, 0x9F, 0x20, 0x03, 0x03, 0x00, 0x88, 0x00, 0x88, 0xFF, 0xCC, 0x20, 0x17, 0x70, 0x07, 0x81, 0xFF, 0xC0, 0x78, 0x00, 0x02, 0xFF, 0xFC, 0x3E, 0x4F, 0x9B, 0x87, 0x3F, 0xEE, 0x90, 0x90, 0xDF, 0xDF, 0x30, 0x03, 0x01, 0xA1, 0xD7, 0x40, 0x6F, 0xC8, 0x30, 0x6B, 0x40, 0x6F, 0x00, 0xB0, 0x43, 0xCF, 0xDF, 0xFF, 0x55, 0x0A, 0xFF, 0xD7, 0x00, 0xEE, 0x00, 0x0E, 0xCE, 0x55, 0x20, 0x17, 0xEE, 0xE8, 0x20, 0x17, 0x70, 0x07, 0x40, 0x6F, 0x80, 0x90, 0x6F, 0xC5, 0xFF, 0xCF, 0x1E, 0x7F, 0xDE, 0x00, 0x95, 0x9D, 0xC1, 0x7F, 0x2F, 0x63, 0xB1, 0x7F, 0x20, 0x3A, 0x00, 0x10, 0x51, 0x7F, 0xB9, 0x8A, 0x06, 0xC5, 0x30, 0x50, 0xA8, 0x42, 0x70, 0xC2, 0x28, 0x4E, 0x2E, 0x06, 0x10, 0x00, 0x43, 0xFD, 0x3A, 0xAF, 0x76, 0x24, 0x00, 0x98, 0x6F, 0xC8, 0xCE, 0x47, 0x2D, 0xA3, 0xFF, 0x7A, 0x22, 0xFF, 0x45, 0x4F, 0xFE, 0xAA, 0x00, 0xBB, 0x4A, 0xCF, 0xBB, 0x8A, 0x50, 0x07, 0xFF, 0x23, 0xEF, 0x3F, 0x96, 0x00, 0x23, 0xDE, 0x53, 0x8A, 0x4F, 0xFF, 0x9A, 0x00, 0x68, 0x4A, 0xEF, 0x24, 0x35, 0x23, 0xAD, 0x14, 0xFF, 0xFF, 0xC8, 0x3A, 0xFB, 0xB3, 0x33, 0x2F, 0xFF, 0xFF, 0x87, 0x66, 0x67, 0x27, 0xFF, 0x00, 0x4B, 0x83, 0x4F, 0x7B, 0xA7, 0x21, 0xD1, 0xE0, 0x2E, 0x0D, 0x37, 0xCA, 0x08, 0x46, 0x42, 0x21, 0xF6, 0x22, 0xFF, 0xF7, 0x44, 0xF7, 0x00, 0x15, 0x7F, 0x22, 0xFF, 0x32, 0x4F, 0x7A, 0x43, 0xFF, 0x60, 0x97, 0x6B, 0xE7, 0x36, 0x6A, 0x10, 0xE7, 0xF2, 0xF4, 0xFF, 0xF8, 0x34, 0xBA, 0x40, 0x03, 0x9F, 0x9B, 0x25, 0x32, 0x8F, 0xA8, 0x0C, 0x09, 0xFF, 0x40, 0x4F, 0x25, 0x2D, 0xAD, 0xBF, 0x04, 0x28, 0xFF, 0x00, 0x62, 0x6C, 0x06, 0xF4, 0x37, 0xFF, 0xCF, 0xD6, 0x00, 0xDD, 0x00, 0x4C, 0xFC, 0xDD, 0x35, 0x00, 0xEE, 0x34, 0x4A, 0x31, 0xEF, 0x54, 0x00, 0x45, 0x1F, 0xB8, 0x38, 0x0B, 0x48, 0x51, 0x41, 0xEF, 0xFD, 0xA0, 0x97, 0x1F, 0x10, 0x00, 0xFE, 0x18, 0xFC, 0x10, 0xA0, 0x23, 0xF1, 0x6F, 0xFF, 0x2C, 0xEF, 0x00, 0x05, 0x03, 0xFF, 0xFF, 0x1E, 0x8F, 0xA1, 0x1B, 0xEF, 0x21, 0x1F, 0xCE, 0xC7, 0x69, 0x04, 0xD8, 0x4B, 0xA1, 0xF2, 0x27, 0xCB, 0x20, 0x03, 0x7F, 0xD5, 0xE1, 0x84, 0x40, 0xD7, 0x37, 0xFF, 0x01, 0xBE, 0x4D, 0xAF, 0x58, 0x00, 0x44, 0x23, 0x4F, 0x7B, 0x64, 0xFF, 0xA7, 0x47, 0xEF, 0xDC, 0xFF, 0x59, 0xED, 0x79, 0xA0, 0xDE, 0x24, 0xA0, 0x37, 0x04, 0xCC, 0xFF, 0x50, 0xCF, 0x10, 0x21, 0x00, 0x32, 0x42, 0xDF, 0x33, 0x00, 0x33, 0x00, 0x13, 0x00, 0x73, 0x30, 0x26, 0xAA, 0xFF, 0xFF, 0x40, 0x04, 0x63, 0x3F, 0x9C, 0x46, 0xD7, 0x7A, 0xEF, 0x35, 0x94, 0x72, 0xEF, 0x26, 0xF5, 0x2F, 0x05, 0x5C, 0x4D, 0x2F, 0xCA, 0x6B, 0x03, 0xE4, 0x0C, 0x09, 0x55, 0xFF, 0x08, 0xC0, 0x7F, 0x91, 0x00, 0x01, 0x00, 0xF3, 0xF3, 0x00, 0x99, 0x00, 0x99, 0x5F, 0xB7, 0xFC, 0x20, 0x0B, 0x20, 0x0F, 0x8F, 0xC7, 0x70, 0x17, 0x60, 0x1F, 0x8F, 0xE7, 0x56, 0xFF, 0x34, 0x55, 0x01, 0x3A, 0xFD, 0x90, 0x1F, 0x78, 0x4F, 0xFF, 0x55, 0xFF, 0x46, 0x65, 0x4A, 0x2F, 0x97, 0xFF, 0xFC, 0x35, 0x5C, 0x29, 0x23, 0x02, 0xC6, 0x34, 0x07, 0x3F, 0xFF, 0x8B, 0xFF, 0x03, 0x44, 0x10, 0x28, 0x4D, 0xD4, 0xD0, 0x80, 0x5F, 0x3F, 0xF0, 0x17, 0x9F, 0xF5, 0xEF, 0xFF, 0x07, 0xCF, 0xD1, 0x45, 0x6F, 0x2B, 0x59, 0x3E, 0xFB, 0x5F, 0x30, 0x20, 0xFB, 0x5F, 0x87, 0x12, 0xF3, 0xF9, 0xF8, 0x90, 0x3F, 0x31, 0xB7, 0x22, 0x97, 0x00, 0x60, 0x75, 0x85, 0xBF, 0x2F, 0xFF, 0xAF, 0x0D, 0xFF, 0x9E, 0xFF, 0x45, 0x45, 0x4F, 0x79, 0xA9, 0x00, 0x9A, 0x45, 0xBF, 0x89, 0x55, 0xA7, 0x10, 0xFF, 0x22, 0xFF, 0x3E, 0x9F, 0x00, 0xFF, 0x75, 0xFF, 0x52, 0xDA, 0x4F, 0xE3, 0x23, 0x3A, 0xDB, 0x5E, 0x7F, 0x95, 0x9F, 0x7F, 0x3E, 0x8F, 0x52, 0x23, 0x0C, 0x0D, 0x92, 0x31, 0xE1, 0xEF, 0xE1, 0xFF, 0x7F, 0xC7, 0x02, 0xF8, 0x2C, 0xE9, 0x30, 0x03, 0xF0, 0x0F, 0x01, 0x1D, 0xA6, 0x45, 0x3E, 0x00, 0xFB, 0xB0, 0xB7, 0x3A, 0x47, 0xFE, 0x2B, 0x83, 0x8C, 0x57, 0xD0, 0x28, 0x8B, 0x25, 0x31, 0x77, 0x11, 0x60, 0x0D, 0xA5, 0x1B, 0x96, 0x44, 0x00, 0x00, 0xFC, 0xFB, 0x01, 0x30, 0x07, 0xF8, 0x3B, 0xB6, 0x56, 0x45, 0x0E, 0x50, 0x00, 0x2F, 0x51, 0x3F, 0x2C, 0x8F, 0xE5, 0x2C, 0xC3, 0x00, 0xFA, 0x30, 0x2B, 0xBB, 0x43, 0x6F, 0x76, 0x87, 0x18, 0xEF, 0x00, 0x38, 0x77, 0x77, 0xC2, 0x5F, 0x85, 0xAE, 0x4B, 0x6B, 0xFF, 0x01, 0xAF, 0x47, 0xCF, 0x15, 0x32, 0x6B, 0xDC, 0x26, 0x89, 0x00, 0x3E, 0x67, 0x3A, 0x0D, 0xB4, 0x2F, 0x04, 0xF1, 0xFF, 0x7F, 0xEB, 0xA0, 0xB0, 0xA1, 0x26, 0x73, 0xF4, 0x79, 0x7C, 0x00, 0x60, 0xFB, 0xFF, 0x28, 0x32, 0xC0, 0x00, 0xB0, 0x01, 0x24, 0x07, 0x4F, 0x08, 0x2F, 0x1F, 0x00, 0x00, 0x72, 0x0D, 0x3D, 0x04, 0x7D, 0xFB, 0xFE, 0xE5, 0x00, 0xD8, 0x5C, 0xA7, 0xA5, 0xB7, 0x3F, 0x93, 0x61, 0x00, 0x1C, 0xBF, 0x38, 0x93, 0x40, 0x96, 0x9E, 0x2A, 0x03, 0x4F, 0xF4, 0x4B, 0xFE, 0x28, 0xAF, 0xD0, 0xE1, 0x3E, 0xFF, 0x1A, 0x27, 0x87, 0x3E, 0x05, 0x18, 0x00, 0x00, 0x3F, 0x2B, 0x55, 0x98, 0xBE, 0x04, 0x07, 0xFF, 0x17, 0xFF, 0x08, 0x06, 0x0E, 0x53, 0xFF, 0x20, 0x4A, 0xBF, 0x3F, 0xB6, 0xB3, 0xFF, 0x1C, 0xDE, 0xFF, 0xDD, 0x55, 0x42, 0x60, 0x17, 0x7F, 0xD6, 0xDD, 0xFF, 0x03, 0xED, 0xFF, 0x00, 0x31, 0x00, 0x95, 0x7A, 0xDB, 0x60, 0x37, 0x24, 0xEE, 0x12, 0x2E, 0xBF, 0x50, 0xFD, 0xDA, 0x11, 0xFF, 0xBD, 0x24, 0xFF, 0x69, 0x45, 0xE7, 0x14, 0x6B, 0x9B, 0x0F, 0x8E, 0x5E, 0x34, 0x01, 0x9F, 0x6F, 0xD3, 0x00, 0x1C, 0x2A, 0x35, 0x2F, 0x8F, 0xFC, 0xFC, 0x09, 0x00, 0xAD, 0x00, 0x47, 0x5A, 0x6F, 0xFD, 0x74, 0x4E, 0x97, 0x1E, 0xB9, 0xFF, 0xDC, 0x4F, 0xFF, 0x2F, 0x44, 0x2B, 0xB2, 0x4F, 0x3C, 0xBB, 0x6A, 0xFF, 0x2B, 0xAA, 0x2D, 0x64, 0x10, 0x60, 0xB7, 0x32, 0x35, 0xDC, 0x10, 0x15, 0x00, 0xB4, 0xEC, 0x2F, 0xF3, 0xF9, 0x7B, 0x78, 0xEF, 0x68, 0x9F, 0x10, 0x8B, 0xFF, 0x25, 0xAB, 0x8F, 0x6D, 0xFF, 0x8F, 0x02, 0xBD, 0x4E, 0xDD, 0x6C, 0x01, 0xEC, 0xAC, 0xCE, 0x7F, 0x2D, 0x16, 0xBE, 0x7F, 0x80, 0x7E, 0x7F, 0xC0, 0x9D, 0x2E, 0x04, 0x0F, 0xBD, 0xFF, 0xEF, 0xEF, 0x8A, 0xAF, 0x8E, 0x00, 0x47, 0x24, 0xDD, 0x78, 0xDD, 0x77, 0x33, 0x11, 0x01, 0x33, 0x11, 0x8D, 0x7D, 0x13, 0x13, 0x7D, 0x20, 0x03, 0xBF, 0x8A, 0xBF, 0x77, 0x50, 0x17, 0x70, 0x07, 0xFF, 0xFF, 0x30, 0x2B, 0xB0, 0x2F, 0xF0, 0x0F, 0xB7, 0x01, 0xBF, 0x7F, 0xA8, 0x6D, 0xEA, 0x7F, 0x7F, 0x10, 0x6D, 0xFF, 0x27, 0xB0, 0x04, 0xA1, 0x00, 0xA0, 0x0D, 0xB0, 0x01, 0x02, 0x60, 0xED, 0x61, 0xFF, 0x2C, 0xFF, 0xBE, 0x35, 0xFF, 0xF0, 0x3E, 0xB7, 0x00, 0xC0, 0xFF, 0x19, 0x00, 0x21, 0x1D, 0x01, 0x28, 0xFA, 0x18, 0xCC, 0xFF, 0x3A, 0x3B, 0x92, 0x40, 0x3F, 0x17, 0xFF, 0x6B, 0x8D, 0x27, 0x4C, 0x84, 0xFF, 0xE9, 0xBD, 0xB9, 0x3C, 0xB8, 0xFD, 0x23, 0x34, 0xE2, 0x30, 0x62, 0x4C, 0xDA, 0x40, 0x13, 0xF7, 0xFF, 0xF1, 0x20, 0x03, 0xEF, 0x08, 0xFF, 0xE8, 0xFF, 0xE0, 0x20, 0x03, 0xE1, 0xFF, 0xD9, 0x38, 0xFF, 0xD3, 0x50, 0x95, 0x2E, 0xE1, 0x21, 0x87, 0x57, 0xFF, 0x86, 0x34, 0xFF, 0xD1, 0x93, 0x69, 0x30, 0x41, 0xFA, 0x31, 0x9F, 0xAC, 0xFF, 0x44, 0xC7, 0x41, 0xA5, 0xD9, 0xFF, 0xE4, 0x50, 0x5B, 0xF6, 0xFF, 0x41, 0xF2, 0x20, 0x5D, 0xFB, 0xFF, 0xEE, 0xFF, 0xEA, 0x2F, 0x23, 0x54, 0xF5, 0x20, 0x05, 0xE4, 0x23, 0x97, 0xEA, 0x2E, 0x2E, 0xDA, 0xFF, 0x10, 0xDB, 0xFF, 0xD5, 0x2C, 0x6E, 0xC6, 0xFF, 0xD0, 0xFF, 0x05, 0xCB, 0xFF, 0xC1, 0xFF, 0xBC, 0x27, 0xE7, 0xE1, 0x20, 0x11, 0x41, 0xD2, 0x24, 0x7A, 0xDA, 0xFF, 0xCD, 0xFF, 0xCA, 0x20, 0x19, 0x04, 0xC1, 0xFF, 0xB7, 0xFF, 0xB3, 0x20, 0xFE, 0xBA, 0xFF, 0x14, 0xAF, 0xFF, 0xAB, 0x05, 0xD1, 0xFF, 0x5F, 0x62, 0x6D, 0x03, 0xFF, 0x51, 0x29, 0x20, 0xC2, 0xC2, 0xE1, 0x7F, 0x05, 0xFF, 0x9F, 0x22, 0x8D, 0x5B, 0x07, 0x21, 0x61, 0xA7, 0x20, 0x9A, 0x5F, 0xE9, 0x8A, 0xD9, 0x48, 0x38, 0x99, 0x50, 0xFE, 0x41, 0x01, 0xF7, 0x25, 0x34, 0xEA, 0xFF, 0xE0, 0x8D, 0xA2, 0xD1, 0x73, 0xF6, 0x80, 0x27, 0xF7, 0xFF, 0xEB, 0x25, 0x56, 0xE0, 0xA8, 0x21, 0x1D, 0xE9, 0x21, 0x25, 0xDE, 0x2F, 0x4A, 0xD4, 0xFF, 0xD3, 0x28, 0xFF, 0xC9, 0x2F, 0x52, 0xD3, 0x2B, 0x3D, 0xC9, 0xFF, 0xC8, 0xA8, 0x25, 0x49, 0xBD, 0x21, 0x07, 0xFC, 0x27, 0x46, 0xF0, 0xFF, 0xE6, 0xA2, 0x20, 0x2D, 0xE2, 0x25, 0x9E, 0xD5, 0xFF, 0xE3, 0x21, 0x95, 0xD6, 0xAA, 0x2D, 0xB0, 0xD0, 0x20, 0x21, 0xC3, 0x25, 0x10, 0xDA, 0x21, 0x3B, 0xCD, 0x28, 0xFF, 0xC5, 0x21, 0x4D, 0xC4, 0x25, 0x7B, 0xB6, 0xFF, 0xBF, 0x80, 0x21, 0x3D, 0xB2, 0xFF, 0xAB, 0xFF, 0xB0, 0xFF, 0xA9, 0x0A, 0xFF, 0xA3, 0xFF, 0x9C, 0x21, 0x51, 0xC0, 0x22, 0x4A, 0xB5, 0xA8, 0x21, 0x53, 0xAE, 0x21, 0xA8, 0xA2, 0x21, 0x59, 0xAA, 0xFF, 0xA8, 0xA0, 0x20, 0xC8, 0xA0, 0x2F, 0xE0, 0x95, 0xFF, 0x8C, 0xFF, 0xA6, 0x80, 0x2B, 0x49, 0x9A, 0xFF, 0x92, 0xFF, 0x96, 0xFF, 0x8F, 0xA8, 0x25, 0x33, 0x84, 0x20, 0x05, 0x87, 0x22, 0x2C, 0x7C, 0xFF, 0x7F, 0x2A, 0xFF, 0x79, 0x2B, 0x43, 0x6E, 0x22, 0x86, 0xB8, 0x20, 0x4B, 0xAA, 0x8C, 0x20, 0x53, 0xAD, 0xFF, 0xA4, 0x21, 0x00, 0x30, 0x53, 0x96, 0xFF, 0x54, 0x90, 0x2B, 0xFF, 0x92, 0x20, 0xFC, 0x85, 0x20, 0x4B, 0xA4, 0xFF, 0x41, 0x9B, 0x20, 0x3D, 0xA0, 0xFF, 0x9D, 0xFF, 0x93, 0x20, 0x17, 0x04, 0x8D, 0xFF, 0x89, 0xFF, 0x80, 0x20, 0x3F, 0x86, 0xFF, 0x50, 0x83, 0x20, 0x41, 0x76, 0x20, 0x0D, 0x83, 0xFF, 0x7D, 0xFF, 0x14, 0x77, 0xFF, 0x7E, 0x20, 0x4F, 0x72, 0x25, 0x87, 0x72, 0xFF, 0x04, 0x6C, 0xFF, 0x67, 0xFF, 0x62, 0x40, 0x03, 0x5C, 0xFF, 0x00, 0x58, 0xFF, 0x74, 0xFF, 0x70, 0xFF, 0x69, 0xFF, 0x50, 0x65, 0x25, 0xA1, 0x6A, 0x20, 0x15, 0x5F, 0xFF, 0x5E, 0xFF, 0x04, 0x5A, 0xFF, 0x53, 0xFF, 0x50, 0x22, 0x6C, 0x54, 0xFF, 0x14, 0x4D, 0xFF, 0x4A, 0x00, 0xD3, 0xFF, 0x16, 0x24, 0x1D, 0x6B, 0xFF, 0x6A, 0x15, 0x22, 0x66, 0x72, 0xD7, 0x85, 0x2F, 0xA8, 0xED, 0x5F, 0x95, 0xF9, 0xAA, 0x2E, 0xCE, 0xF0, 0x94, 0x3F, 0x75, 0x21, 0xBF, 0xD5, 0x84, 0x4D, 0x3D, 0xD5, 0x2F, 0xD0, 0xAF, 0xAC, 0xFD, 0x22, 0xFB, 0xE8, 0x26, 0x3B, 0xE0, 0x2F, 0xE7, 0x55, 0xF8, 0x2F, 0x06, 0xF1, 0x23, 0x03, 0xD9, 0x23, 0x01, 0xD3, 0x21, 0x7F, 0x55, 0xF0, 0x28, 0xC8, 0xE6, 0x21, 0x77, 0xD6, 0x23, 0x0F, 0xCC, 0x22, 0xD1, 0x55, 0xDD, 0x21, 0x89, 0xD5, 0x22, 0xBF, 0xC3, 0x22, 0xF6, 0xBB, 0x22, 0xB1, 0x55, 0xBF, 0x21, 0x61, 0xB5, 0x22, 0xB1, 0xA8, 0x21, 0x59, 0x9F, 0x22, 0xBB, 0x55, 0xAB, 0x21, 0x69, 0xA2, 0x21, 0x5F, 0x95, 0x2D, 0x23, 0x8C, 0x42, 0xD5, 0x5A, 0xD2, 0x21, 0x9F, 0xBF, 0x21, 0x95, 0x30, 0x1B, 0xCB, 0x27, 0x21, 0xC4, 0xAA, 0x21, 0xA7, 0xB0, 0x21, 0x9D, 0xA9, 0x21, 0x9F, 0xA6, 0x21, 0x7D, 0x9E, 0xAB, 0x21, 0x7F, 0x8F, 0x23, 0xA2, 0x87, 0x21, 0x77, 0x96, 0x26, 0xB9, 0x30, 0x0B, 0x51, 0x7F, 0x21, 0x19, 0x78, 0x21, 0x7F, 0x9E, 0xFF, 0x94, 0x20, 0x01, 0x45, 0x8A, 0x26, 0xCF, 0x82, 0xFF, 0x81, 0x20, 0x11, 0x8B, 0x20, 0x05, 0x62, 0x82, 0x20, 0x19, 0x30, 0x1B, 0x6E, 0xFF, 0x66, 0x21, 0xA5, 0x71, 0xA2, 0x21, 0x41, 0x68, 0x21, 0x3D, 0x63, 0xFF, 0x60, 0x21, 0x3B, 0x66, 0xA0, 0x21, 0x41, 0x5E, 0x2D, 0xBD, 0x57, 0xFF, 0x51, 0xFF, 0x4F, 0x2A, 0xFF, 0x48, 0x21, 0xC5, 0x70, 0x20, 0x21, 0x68, 0x80, 0x17, 0x6A, 0xAA, 0x20, 0x25, 0x63, 0x21, 0x63, 0x57, 0x20, 0x1D, 0x50, 0x20, 0x1F, 0x55, 0x20, 0xFF, 0x4E, 0x20, 0x01, 0x46, 0xFF, 0x47, 0xFF, 0x40, 0x0A, 0xFF, 0x3F, 0xFF, 0x39, 0x20, 0x07, 0x3F, 0x20, 0x09, 0x39, 0xA2, 0x20, 0x09, 0x32, 0x20, 0x01, 0x2C, 0xFF, 0x5D, 0x23, 0xF6, 0x53, 0xC0, 0x20, 0x23, 0x30, 0x03, 0x49, 0xFF, 0x44, 0xFF, 0x4B, 0xFF, 0x10, 0x45, 0xFF, 0x42, 0x21, 0x3E, 0x40, 0xFF, 0x3C, 0xFF, 0x05, 0x38, 0xFF, 0x34, 0xFF, 0x4A, 0x20, 0x39, 0x41, 0x21, 0x4E, 0x50, 0x43, 0x20, 0x05, 0x3B, 0x20, 0x11, 0x38, 0xFF, 0x35, 0xFF, 0x00, 0x31, 0xFF, 0x2E, 0xFF, 0x33, 0xFF, 0x30, 0xFF, 0x55, 0x2B, 0x23, 0x46, 0x3B, 0x20, 0x0F, 0x33, 0x20, 0x0F, 0x30, 0x24, 0xD2, 0x05, 0x2A, 0xFF, 0x26, 0xFF, 0x2D, 0x2B, 0x94, 0x27, 0x2D, 0xA5, 0x01, 0x23, 0xFF, 0x20, 0xFF, 0x1E, 0xFF, 0x1B, 0x23, 0x68, 0x50, 0x26, 0x40, 0x0B, 0x24, 0x2D, 0xB9, 0x1E, 0xFF, 0x1C, 0xFF, 0x05, 0x1D, 0xFF, 0x1A, 0xFF, 0x18, 0x21, 0xD2, 0x18, 0x24, 0xB8, 0x15, 0x13, 0xFF, 0x12, 0x33, 0x8B, 0x00, 0x20, 0x36, 0xD2, 0x6B, 0xF6, 0xB5, 0x34, 0x85, 0x00, 0x24, 0x87, 0x64, 0xB3, 0xFA, 0x2C, 0x0B, 0xEA, 0x24, 0x61, 0x55, 0xE4, 0x24, 0x57, 0xCC, 0x24, 0x59, 0xC6, 0x27, 0xFF, 0xDF, 0x24, 0x75, 0x52, 0xDA, 0x24, 0x5F, 0xC1, 0x24, 0x4D, 0xBC, 0x00, 0x22, 0xB6, 0x00, 0xC5, 0x23, 0x56, 0x43, 0x7B, 0xFE, 0xFF, 0xF2, 0x34, 0xA5, 0x00, 0x23, 0x4E, 0xAA, 0x24, 0xFD, 0xED, 0x2A, 0x35, 0xEA, 0x2C, 0x67, 0xD5, 0x24, 0xE9, 0xD2, 0xAA, 0x24, 0x91, 0xB7, 0x24, 0x8F, 0xB2, 0x29, 0x02, 0xCD, 0x24, 0xA5, 0xCA, 0xAA, 0x25, 0x7E, 0xAF, 0x24, 0x81, 0xAB, 0x25, 0x86, 0xB0, 0x23, 0x01, 0xAA, 0xAA, 0x23, 0x4B, 0x96, 0x23, 0x4D, 0x90, 0x23, 0x5B, 0xA4, 0x23, 0x09, 0x9F, 0xAA, 0x2E, 0xFF, 0x8A, 0x23, 0x35, 0x85, 0x22, 0xED, 0x7D, 0x22, 0xE9, 0x77, 0xAA, 0x22, 0xDB, 0x67, 0x22, 0xD9, 0x62, 0x22, 0xE7, 0x72, 0x23, 0x39, 0x6D, 0xAA, 0x22, 0xE3, 0x5C, 0x22, 0xE5, 0x57, 0x23, 0x6B, 0x9A, 0x23, 0x37, 0x96, 0xAA, 0x23, 0x17, 0x80, 0x23, 0x19, 0x7C, 0x23, 0x77, 0x93, 0x23, 0x29, 0x90, 0xAA, 0x25, 0x4A, 0x79, 0x23, 0x21, 0x76, 0x22, 0xFF, 0x6A, 0x23, 0x01, 0x65, 0xAA, 0x24, 0x7A, 0x53, 0x22, 0xF9, 0x50, 0x28, 0xA9, 0x62, 0x23, 0x09, 0x5F, 0xAA, 0x25, 0x6C, 0x4D, 0x23, 0x01, 0x4A, 0x21, 0x7F, 0x53, 0x25, 0x78, 0x4E, 0xA2, 0x21, 0x77, 0x42, 0x21, 0x79, 0x3D, 0xFF, 0x52, 0x21, 0x85, 0x4E, 0xAA, 0x21, 0x87, 0x40, 0x21, 0x7D, 0x3C, 0x21, 0x7F, 0x3A, 0x21, 0x69, 0x35, 0xAA, 0x21, 0x6F, 0x2D, 0x25, 0xA4, 0x28, 0x2E, 0xFD, 0x30, 0x21, 0x65, 0x2C, 0xAA, 0x41, 0x53, 0x1E, 0x21, 0x61, 0x1B, 0x21, 0x9F, 0x41, 0x21, 0xDD, 0x3D, 0xAA, 0x21, 0xAB, 0x31, 0x21, 0x99, 0x2D, 0x21, 0xA7, 0x3B, 0x21, 0xAF, 0x38, 0xAA, 0x21, 0x9F, 0x2B, 0x21, 0xA1, 0x29, 0x24, 0xE8, 0x23, 0x21, 0x95, 0x20, 0xAA, 0x21, 0x77, 0x18, 0x21, 0x79, 0x15, 0x21, 0x87, 0x1E, 0x2F, 0x43, 0x1C, 0xAA, 0x21, 0x83, 0x13, 0x23, 0x5E, 0x12, 0x2F, 0x4F, 0x1D, 0x40, 0x1B, 0x19, 0x8A, 0x23, 0x6C, 0x14, 0xFF, 0x11, 0x26, 0x72, 0x14, 0x23, 0x76, 0x11, 0xA8, 0x20, 0x09, 0x0E, 0x20, 0x01, 0x0B, 0x21, 0xA3, 0x10, 0xFF, 0x0F, 0x20, 0xFF, 0x0D, 0x40, 0x03, 0x0B, 0xFF, 0x0A, 0xFF, 0x0C, 0xAA, 0x20, 0x03, 0x09, 0x25, 0x24, 0x08, 0x25, 0x28, 0x06, 0x25, 0x32, 0x13, 0xAA, 0x20, 0x1D, 0x10, 0x20, 0x1F, 0x0C, 0x20, 0x15, 0x0A, 0x25, 0x3C, 0x0F, 0xAB, 0x20, 0x33, 0x0D, 0x20, 0x27, 0x08, 0x20, 0x1D, 0x07, 0x25, 0x52, 0x50, 0x25, 0x56, 0x04, 0x20, 0x01, 0x03, 0x25, 0x76, 0x02, 0x80, 0x07, 0x50, 0x01, 0x01, 0xAC, 0x00, 0xB9, 0x7F, 0x20, 0x2C, 0x1D, 0x03, 0x04, 0xE9, 0x7F, 0x10, 0x04, 0x80, 0x59, 0x30, 0xC2, 0x30, 0xFE, 0xE2, 0x2C, 0x3A, 0x3A, 0x46, 0x20, 0xF8, 0xE9, 0xFF, 0x0C, 0x00, 0x95, 0x00, 0xDC, 0x00, 0x3E, 0x2E, 0x03, 0x1F, 0xA9, 0x80, 0xF2, 0x18, 0x00, 0x10, 0xF9, 0x38, 0xEF, 0x4F, 0x02, 0x50, 0x90, 0xFF, 0x1F, 0xFF, 0xC0, 0xD0, 0x00, 0xDE, 0x98, 0xBF, 0x1B, 0xCB, 0xFF, 0x2F, 0x23, 0xF2, 0x7F, 0xE2, 0x4B, 0xFF, 0x01, 0xCF, 0x27, 0x10, 0x5B, 0x20, 0x2B, 0x10, 0x00, 0x11, 0x49, 0x71, 0x11, 0xFD, 0x60, 0x07, 0xF9, 0x7E, 0x70, 0x17, 0x0B, 0x10, 0x1F, 0x3A, 0x6E, 0x0C, 0xBA, 0x5C, 0x40, 0x41, 0xCD, 0x00, 0x30, 0xE3, 0xFF, 0x20, 0xF9, 0xB7, 0xFF, 0xFF, 0x00, 0xAF, 0xCF, 0x03, 0xD1, 0xF8, 0xFF, 0x7F, 0xFE, 0x00, 0xFF, 0x0C, 0x05, 0x06, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x90, 0x80, 0xED, 0xFF, 0xEE, 0xFF, 0x79, 0x0A, 0x00, 0x77, 0x00, 0xEE, 0x20, 0x07, 0x77, 0x20, 0x07, 0x00, 0x01, 0xAA, 0x00, 0x07, 0xFF, 0xFF, 0x3F, 0xCF, 0x2B, 0x90, 0x06, 0xA5, 0x00, 0x00, 0xF9, 0xF8, 0x70, 0x17, 0x80, 0x1F, 0x4A, 0x00, 0x00, 0xA0, 0x6F, 0x6F, 0xF0, 0x30, 0x00, 0x9B, 0x07, 0x00, 0x00, 0xEF, 0x23, 0x00, 0x80, 0x37, 0x80, 0x3F, 0xEA, 0x05, 0x8B, 0x00, 0x30, 0x1F, 0x80, 0xE8, 0xAB, 0x59, 0xF7, 0x59, 0x00, 0x60, 0x3F, 0x3B, 0xFC, 0xE0, 0x6B, 0xF2, 0x00, 0xE0, 0x5F, 0x00, 0x90, 0x7F, 0xA2, 0x00, 0x6A, 0xF4, 0xF3, 0x2A, 0x9F, 0x8F, 0x90, 0xBF, 0xDE, 0x20, 0xDF, 0x97, 0xDC, 0x48, 0x01, 0x00, 0x20, 0x10, 0x7B, 0xFF, 0x02, 0xAF, 0xFC, 0x30, 0x20, 0xFF, 0xFA, 0x39, 0xE3, 0x4E, 0xFF, 0x00, 0x04, 0x00, 0x00, 0x65, 0x50, 0x01, 0xA9, 0x88, 0x05, 0x04, 0xFF, 0x02, 0xF6, 0x1E, 0x8F, 0xB0, 0x50, 0xEF, 0x00, 0x4A, 0x9F, 0x01, 0x4B, 0x05, 0x08, 0x3C, 0x8F, 0x70, 0x90, 0x21, 0xCF, 0x70, 0xA1, 0xFA, 0x41, 0x45, 0x0C, 0xFF, 0xEF, 0x02, 0x00, 0x20, 0xC5, 0x2A, 0xD9, 0x80, 0x80, 0x80, 0x30, 0x03, 0xFF, 0xFE, 0x05, 0x0B, 0xF8, 0xE1, 0x6F, 0x81, 0x2B, 0x97, 0x80, 0x90, 0x00, 0x05, 0x60, 0x00, 0x2F, 0x66, 0x00, 0xB6, 0xFF, 0xFF, 0x25, 0x06, 0x06, 0x1F, 0xF8, 0x08, 0xF8, 0x9F, 0xF8, 0xF7, 0x2F, 0x73, 0x06, 0x07, 0xBB, 0x90, 0x21, 0x06, 0xD0, 0x40, 0x3B, 0xBB, 0xB8, 0x00, 0x6F, 0x6F, 0x00, 0x70, 0xF0, 0x6F, 0x6F, 0xE0, 0xE0, 0x68, 0xDF, 0x02, 0x50, 0xA0, 0xDF, 0xDF, 0x90, 0x90, 0x20, 0x0B, 0xF0, 0x08, 0x4B, 0x00, 0xB0, 0x00, 0x20, 0x0B, 0xA0, 0x9B, 0x00, 0x00, 0x70, 0x00, 0x89, 0xFF, 0xB6, 0x8D, 0xFF, 0xFF, 0x00, 0x04, 0x03, 0xAD, 0xFD, 0x23, 0x2F, 0xF3, 0xF3, 0x24, 0x9F, 0xAF, 0x20, 0x0B, 0x04, 0xBB, 0x2D, 0xB5, 0xF3, 0xF3, 0x00, 0xAF, 0xBF, 0xB2, 0x00, 0x8B, 0x00, 0xE8, 0xB0, 0x00, 0xFF, 0xEF, 0xC0, 0xC0, 0xFF, 0xFF, 0x49, 0x10, 0x0C, 0x81, 0xFD, 0x90, 0xC1, 0x2B, 0x9B, 0x20, 0x0B, 0x90, 0x00, 0x00, 0xBB, 0x00, 0xA0, 0x21, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xB3, 0x00, 0xCC, 0x07, 0x7C, 0xFA, 0x68, 0xAC, 0x00, 0xFB, 0x89, 0x00, 0x0A, 0x95, 0xFA, 0x5F, 0x48, 0x00, 0xF8, 0xF9, 0x03, 0x06, 0x72, 0xFA, 0xBD, 0x21, 0x00, 0x9D, 0x01, 0x17, 0x0D, 0xF9, 0xF9, 0x01, 0x00, 0x00, 0xB7, 0x00, 0x58, 0xDF, 0xDB, 0xD8, 0x4F, 0x3F, 0x00, 0x30, 0x20, 0x3A, 0xFF, 0x00, 0x03, 0xFF, 0xFF, 0x01, 0x38, 0x6A, 0x4F, 0x4F, 0x20, 0x30, 0x3B, 0x2E, 0x19, 0x04, 0xFF, 0xFF, 0x4A, 0x0B, 0xBB, 0x2E, 0x18, 0x70, 0xFA, 0x00, 0xCC, 0x5E, 0xFF, 0xFF, 0x09, 0x08, 0xFA, 0xFA, 0x00, 0x8F, 0x8F, 0xF3, 0xF3, 0x8F, 0x7F, 0xFF, 0xFC, 0x00, 0x08, 0x3D, 0x91, 0x00, 0xDD, 0x00, 0xF3, 0xF9, 0x00, 0x8F, 0x9F, 0xBA, 0x00, 0x6B, 0x00, 0x00, 0xB0, 0x00, 0xC7, 0xAF, 0xF4, 0xF7, 0x6F, 0x7F, 0xDC, 0x70, 0x00, 0x19, 0xDF, 0x30, 0x30, 0xFF, 0xFF, 0xF5, 0xC0, 0x00, 0x6F, 0x9F, 0x10, 0x00, 0xD8, 0x00, 0x30, 0x60, 0x01, 0xFF, 0xEF, 0xDB, 0x12, 0x2A, 0x00, 0x31, 0x2E, 0x70, 0x20, 0x09, 0x0C, 0x34, 0xF2, 0xFF, 0xFE, 0x00, 0x10, 0xFD, 0x22, 0xFF, 0x09, 0x5B, 0xEB, 0x40, 0x40, 0xB0, 0x20, 0x2B, 0xFF, 0x0D, 0x9F, 0x1E, 0x08, 0x09, 0x20, 0x1F, 0xAE, 0x7F, 0x05, 0x09, 0xE4, 0x31, 0x08, 0x40, 0x00, 0xFF, 0xE4, 0x3F, 0x49, 0x9F, 0xFF, 0x02, 0x07, 0xBF, 0xFA, 0x30, 0xFF, 0xC8, 0x00, 0x0F, 0x49, 0x23, 0xFC, 0x24, 0x02, 0x7F, 0xEE, 0x24, 0x02, 0x40, 0x07, 0x00, 0x05, 0xFA, 0x60, 0x17, 0x70, 0x1F, 0x00, 0x05, 0xFA, 0x09, 0x90, 0x1F, 0x60, 0x87, 0xD0, 0xBF, 0x55, 0xFA, 0x20, 0xFB, 0xF9, 0xFF, 0xFF, 0x02, 0x8C, 0xAF, 0x03, 0xFF, 0x5E, 0x04, 0x10, 0x16, 0x4D, 0xE6, 0x43, 0x02, 0x4C, 0x49, 0x4D, 0xFF, 0xFE, 0x14, 0x2F, 0xFF, 0x02, 0x30, 0x02, 0x28, 0x26, 0xF7, 0x38, 0x90, 0x69, 0x6D, 0x61, 0x67, 0xA8, 0x37, 0x02, 0x80, 0x27, 0xBE, 0x0D, 0x67, 0x0F, 0x47, 0x06, 0x16, 0x00, 0x8A, 0x12, 0x3A, 0x5B, 0x41, 0xDB, 0x11, 0xF4, 0x00, 0xAC, 0xCF, 0xDD, 0x96, 0xDB, 0x5F, 0xAA, 0xAE, 0x00, 0x6D, 0x3C, 0x2E, 0x7F, 0x8B, 0x72, 0xA3, 0x56, 0x00, 0x47, 0x7A, 0xF8, 0x43, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned char Homebrew_LZ[0x2000] = +{ + 0x11, 0x9C, 0x21, 0x00, 0x00, 0x64, 0x61, 0x72, 0x63, 0xFF, 0xFE, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7C, 0x21, 0x00, 0x00, 0x83, 0x30, 0x09, 0x24, 0x03, 0x00, 0x00, 0x40, 0x20, 0x03, 0x30, 0x13, 0xAB, 0x30, 0x18, 0x0F, 0x20, 0x1D, 0x02, 0xA0, 0x0B, 0x06, 0x20, 0x2B, 0x30, 0x18, 0x5A, 0x05, 0x20, 0x35, 0x10, 0x20, 0x39, 0x30, 0x2B, 0xA4, 0x20, 0x20, 0x40, 0xEA, 0x30, 0x45, 0x20, 0x1C, 0x30, 0x0B, 0x70, 0x60, 0x23, 0x08, 0x20, 0x59, 0x7A, 0x85, 0x30, 0x5D, 0x09, 0x00, 0x00, 0x28, 0x20, 0x2C, 0xA2, 0x20, 0x69, 0x21, 0x80, 0x19, 0x20, 0x0B, 0x04, 0x00, 0x00, 0xC4, 0x60, 0x47, 0xA0, 0x30, 0x5F, 0xCE, 0x20, 0x81, 0xC0, 0x1D, 0x00, 0x00, 0x9C, 0xA5, 0x20, 0x89, 0x12, 0x20, 0x75, 0x60, 0x1E, 0x50, 0x0B, 0x56, 0x30, 0x81, 0x55, 0x1F, 0x50, 0x17, 0x9A, 0x20, 0x8D, 0xA0, 0x60, 0x0B, 0xDE, 0x20, 0x99, 0x2A, 0x40, 0x20, 0x50, 0x2F, 0x22, 0x20, 0x9C, 0xE0, 0x60, 0x0B, 0x00, 0x20, 0x00, 0x2E, 0x20, 0xCB, 0x62, 0x00, 0x6C, 0x00, 0x79, 0x20, 0x00, 0x74, 0x20, 0xD5, 0x4E, 0x00, 0x69, 0x00, 0x6E, 0xA0, 0x20, 0x09, 0x65, 0x20, 0x05, 0x64, 0x00, 0x6F, 0x00, 0x4C, 0xA2, 0x20, 0x03, 0x67, 0x20, 0x07, 0x5F, 0x00, 0x55, 0x20, 0x03, 0x30, 0xAA, 0x20, 0x01, 0x2E, 0x20, 0x2D, 0x63, 0x01, 0x20, 0x2F, 0x44, 0x00, 0x40, 0x2F, 0x74, 0xA3, 0x20, 0x5F, 0x6D, 0x20, 0x51, 0x00, 0x00, 0x68, 0x40, 0x75, 0x70, 0x5D, 0x57, 0x62, 0x20, 0x6B, 0x74, 0x20, 0x81, 0x6F, 0x20, 0x1D, 0x70, 0x61, 0x30, 0x29, 0xD7, 0xF0, 0x27, 0x30, 0x21, 0x70, 0xE0, 0x21, 0x61, 0x20, 0xB1, 0x50, 0x2B, 0x01, 0x10, 0x8D, 0x18, 0x5F, 0x00, 0x53, 0x20, 0xBD, 0x30, 0xDD, 0x65, 0x00, 0x4F, 0x2F, 0x00, 0x75, 0x20, 0xF3, 0x43, 0x80, 0xD1, 0x30, 0x47, 0x01, 0x31, 0x01, 0x00, 0x10, 0x43, 0x6F, 0x42, 0x01, 0x80, 0x43, 0x00, 0x90, 0x87, 0x41, 0x03, 0x20, 0x43, 0x01, 0x90, 0x87, 0x00, 0x90, 0xCB, 0x01, 0x90, 0x87, 0xE0, 0x00, 0x91, 0x0F, 0xF1, 0x53, 0x90, 0x02, 0x43, 0x4C, 0x59, 0x54, 0xFF, 0x2C, 0xFE, 0x14, 0x33, 0x21, 0x02, 0x33, 0x03, 0x33, 0x0F, 0x6C, 0x79, 0x31, 0x74, 0x31, 0x30, 0x11, 0x43, 0x3C, 0x00, 0xC8, 0x43, 0x23, 0x0D, 0x03, 0x43, 0x74, 0x78, 0x6C, 0x31, 0x24, 0x63, 0x50, 0x22, 0xFA, 0x00, 0x00, 0x68, 0x62, 0x6C, 0x6F, 0x67, 0x6F, 0x5F, 0x00, 0x74, 0x6F, 0x70, 0x2E, 0x62, 0x63, 0x6C, 0x69, 0x81, 0x32, 0x18, 0x00, 0x6D, 0x61, 0x74, 0x31, 0x60, 0x63, 0x74, 0x86, 0x33, 0x57, 0x48, 0x62, 0x4D, 0x61, 0x32, 0xC3, 0xB0, 0x70, 0xFF, 0x35, 0xFF, 0xFF, 0x30, 0x03, 0x00, 0x40, 0x02, 0x15, 0x43, 0xB2, 0x04, 0x30, 0x5E, 0x98, 0xA0, 0xA3, 0x80, 0x3F, 0x50, 0x03, 0x23, 0x93, 0x61, 0x6E, 0x31, 0x48, 0x4C, 0x33, 0xE8, 0x04, 0xFF, 0x20, 0x5B, 0x52, 0x6F, 0x6F, 0x07, 0x74, 0x50, 0x61, 0x6E, 0x65, 0xE0, 0x60, 0x00, 0x80, 0x0E, 0x70, 0x47, 0x86, 0x50, 0xCF, 0x70, 0x61, 0x73, 0x31, 0x33, 0xDB, 0x70, 0x53, 0x03, 0xB3, 0x80, 0x53, 0x30, 0x01, 0x70, 0x50, 0xA0, 0x9B, 0x20, 0x42, 0x30, 0x03, 0x80, 0x53, 0x0B, 0x69, 0x63, 0x31, 0x80, 0x34, 0x90, 0x07, 0x30, 0xA7, 0x00, 0x11, 0x03, 0xC7, 0x01, 0x50, 0xA7, 0x23, 0x08, 0x00, 0x80, 0x41, 0xF1, 0x2B, 0x71, 0x95, 0x91, 0x1B, 0x83, 0xF1, 0x27, 0x80, 0x3F, 0x70, 0x61, 0x65, 0x60, 0xDB, 0x50, 0x07, 0x0C, 0x67, 0x72, 0x70, 0x31, 0x35, 0x21, 0x51, 0x33, 0x47, 0x72, 0xCD, 0x24, 0xDB, 0x82, 0x03, 0x67, 0x72, 0x51, 0x07, 0x30, 0x23, 0x3C, 0x25, 0x45, 0x07, 0x47, 0x5F, 0x41, 0x5F, 0x30, 0xA1, 0x02, 0x25, 0x37, 0x00, 0x01, 0x17, 0xD7, 0xF1, 0xD7, 0x30, 0x5F, 0x2C, 0x40, 0x3B, 0x42, 0xC0, 0x3B, 0x35, 0x7C, 0x00, 0x90, 0x2B, 0x67, 0x43, 0x00, 0x20, 0x2B, 0xD1, 0x7F, 0x67, 0x72, 0x50, 0xC7, 0x00, 0xB1, 0xE1, 0x01, 0x12, 0xBF, 0x40, 0xA0, 0x00, 0xB2, 0xBF, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0xF2, 0x62, 0xC2, 0x09, 0x52, 0xBF, 0x50, 0xCF, 0x07, 0x52, 0xBF, 0xF0, 0xC2, 0x00, 0xE2, 0xBF, 0x42, 0xC3, 0x10, 0x00, 0xF2, 0xBF, 0x10, 0x76, 0x70, 0x1E, 0xF0, 0xF0, 0x0F, 0x0F, 0x30, 0x03, 0x02, 0x70, 0x1F, 0x9B, 0x4E, 0x44, 0xD7, 0x00, 0x2C, 0x8F, 0x00, 0x2D, 0x7F, 0x7D, 0x9D, 0xEE, 0x00, 0x4D, 0x9D, 0x11, 0x90, 0x00, 0xEF, 0x4E, 0x84, 0x06, 0x00, 0xA1, 0x00, 0x4D, 0xBD, 0x2B, 0xEE, 0x00, 0x00, 0xDB, 0xF6, 0xC5, 0x60, 0x77, 0x8D, 0x00, 0x30, 0x67, 0x3D, 0x23, 0xD5, 0x3D, 0x27, 0x40, 0x67, 0xFE, 0x00, 0x4E, 0x1D, 0x2A, 0x7A, 0xBE, 0x00, 0x00, 0x50, 0xFF, 0x57, 0xE9, 0x80, 0x17, 0xC5, 0x00, 0x4E, 0x5D, 0xFF, 0xA0, 0x79, 0x00, 0x50, 0x1F, 0x5F, 0x63, 0x1E, 0xD0, 0x00, 0xCF, 0x00, 0x7B, 0x38, 0x4F, 0x84, 0x00, 0xD1, 0x5D, 0x03, 0xF1, 0xEF, 0xFF, 0x1F, 0xF0, 0xF0, 0xFF, 0x81, 0xEF, 0x81, 0xFF, 0x3F, 0x8E, 0x21, 0xC6, 0x20, 0x1A, 0xE0, 0x20, 0x20, 0x92, 0x0F, 0x92, 0x1F, 0xC0, 0xF7, 0x00, 0x00, 0xFD, 0x02, 0xFF, 0xFF, 0x6E, 0xFF, 0x11, 0x06, 0x4E, 0x7A, 0xFF, 0x00, 0xFD, 0x00, 0x00, 0xF7, 0xC0, 0x01, 0x05, 0x00, 0x00, 0x00, 0x5E, 0xFF, 0x11, 0xFF, 0xFF, 0xF6, 0x0B, 0x08, 0x7F, 0x60, 0x10, 0xCF, 0x61, 0x73, 0xFF, 0xFF, 0x10, 0x01, 0x60, 0xFF, 0xCF, 0xE6, 0xFF, 0x7F, 0x0B, 0x71, 0x87, 0x04, 0xFA, 0x2C, 0xFF, 0x90, 0x03, 0x2D, 0x95, 0x4F, 0xFF, 0x04, 0xFC, 0x2C, 0xFF, 0xFF, 0x03, 0x00, 0x01, 0xA5, 0xFF, 0x90, 0x04, 0x5F, 0xCF, 0x10, 0x00, 0xEF, 0x2E, 0xD7, 0xA0, 0xF6, 0x18, 0x00, 0x00, 0xFC, 0x80, 0x47, 0x2D, 0x69, 0xFC, 0x00, 0x00, 0x01, 0xF6, 0x90, 0xFF, 0x5E, 0xFF, 0x01, 0x03, 0x3D, 0x72, 0x02, 0xC3, 0x0D, 0x8F, 0x20, 0xFF, 0xDF, 0x20, 0x42, 0xFF, 0x08, 0x00, 0x6E, 0xFF, 0x02, 0x40, 0xB3, 0x20, 0xFF, 0xC6, 0x83, 0x80, 0x77, 0xFA, 0x3E, 0xFF, 0x40, 0x04, 0x50, 0x9F, 0x41, 0xFF, 0x40, 0x01, 0x2D, 0xE4, 0x3E, 0xFA, 0x40, 0xFF, 0xEF, 0xFE, 0x04, 0x01, 0x08, 0xF5, 0xF1, 0x0D, 0x22, 0xEB, 0xAF, 0xEF, 0x00, 0xF0, 0xF0, 0x5F, 0x1F, 0xF1, 0xF5, 0x0F, 0x0D, 0x09, 0xFE, 0xEF, 0x08, 0x01, 0x72, 0xFF, 0xFF, 0x04, 0x53, 0x02, 0x07, 0xA0, 0xF6, 0xFF, 0x5E, 0xFC, 0x30, 0x5C, 0x7D, 0xE7, 0x20, 0x0C, 0x10, 0xF6, 0x90, 0x6E, 0x20, 0x48, 0xFF, 0xC3, 0x00, 0xFF, 0x01, 0x20, 0xFF, 0x0D, 0x8F, 0x00, 0x00, 0xDF, 0x32, 0x59, 0xB8, 0x22, 0xF2, 0x02, 0x20, 0x0F, 0x32, 0xF8, 0x30, 0x7D, 0xFC, 0xF8, 0x04, 0x00, 0x07, 0xF4, 0xF0, 0x0A, 0x0E, 0xF1, 0xF4, 0xFF, 0x00, 0xFD, 0xF8, 0xEC, 0xF5, 0xE0, 0xC0, 0x90, 0x2F, 0x00, 0x9F, 0x50, 0x10, 0xFF, 0xFF, 0xAF, 0x5F, 0xA0, 0x10, 0x50, 0x1F, 0x0D, 0x2E, 0x8D, 0x04, 0xF3, 0xF7, 0x08, 0x0C, 0x0C, 0xFA, 0xFD, 0x0C, 0x2F, 0x90, 0x3E, 0x77, 0x1F, 0x8F, 0x03, 0xCF, 0x8F, 0xFF, 0xFD, 0x5F, 0x1F, 0x06, 0x03, 0xEF, 0x00, 0xE2, 0x6C, 0xF4, 0x00, 0xF0, 0x1F, 0x02, 0x73, 0xEA, 0x01, 0x10, 0x1D, 0x00, 0xD0, 0x37, 0xA4, 0x63, 0xC2, 0xDF, 0x00, 0x6E, 0x28, 0x00, 0xC3, 0x6C, 0x00, 0x73, 0xEA, 0x2B, 0x01, 0x60, 0xFF, 0x30, 0x69, 0x00, 0x3F, 0x89, 0xFE, 0xFC, 0x20, 0x79, 0x3F, 0xFF, 0x00, 0x70, 0x7D, 0x23, 0x1B, 0x4F, 0xF5, 0x00, 0x7D, 0xE7, 0xF8, 0x00, 0x40, 0x0D, 0x10, 0x14, 0xCE, 0x01, 0x43, 0x4C, 0x49, 0x4D, 0xFF, 0xFE, 0x46, 0x14, 0x2F, 0xFF, 0x02, 0x02, 0x28, 0x24, 0x6E, 0x35, 0xA2, 0x69, 0x10, 0x6D, 0x61, 0x67, 0x24, 0x79, 0x00, 0x80, 0x00, 0x40, 0xFF, 0x52, 0x7D, 0x30, 0x0C, 0x07, 0x8F, 0xFF, 0x57, 0x9F, 0x38, 0x79, 0x00, 0x58, 0x7D, 0x00, 0xF0, 0x1F, 0x40, 0xD8, 0xB6, 0x53, 0x74, 0xDF, 0x00, 0x88, 0x27, 0x57, 0x5F, 0xFF, 0x28, 0x69, 0x00, 0x3F, 0xFF, 0xEF, 0xDD, 0x28, 0x79, 0x3F, 0xFF, 0xB2, 0x63, 0xDC, 0xFF, 0xFF, 0x98, 0x7D, 0xC7, 0x00, 0x67, 0xFF, 0x7F, 0x9D, 0x4F, 0xFF, 0x47, 0x0F, 0x48, 0xE5, 0x89, 0x47, 0x30, 0xD9, 0x50, 0xDD, 0x2F, 0xFF, 0x17, 0xE8, 0x00, 0xBF, 0x00, 0x4F, 0xFF, 0x02, 0x00, 0x10, 0x37, 0x00, 0x68, 0x7F, 0x01, 0x50, 0x7F, 0xB1, 0x00, 0x14, 0x92, 0x2F, 0x84, 0xBE, 0x00, 0x3F, 0xFF, 0x0E, 0x00, 0x20, 0x69, 0x9D, 0xF9, 0x05, 0x24, 0x9C, 0x61, 0xEF, 0x38, 0x67, 0x20, 0x04, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x38, 0x7F, 0x83, 0x28, 0x7D, 0x10, 0x90, 0x0F, 0x0F, 0xE0, 0x29, 0x17, 0x72, 0x1F, 0x50, 0xE0, 0x20, 0x0F, 0x10, 0x38, 0x79, 0x1B, 0xFF, 0xFF, 0x02, 0x02, 0x00, 0xFF, 0xB1, 0xDF, 0xFF, 0x20, 0x40, 0x2C, 0x02, 0x01, 0x1B, 0xFD, 0x80, 0xFF, 0xFD, 0x20, 0xB1, 0x20, 0x38, 0x00, 0xDF, 0xDF, 0x08, 0x01, 0x09, 0xF0, 0xF0, 0x0E, 0xD5, 0x2A, 0xC9, 0x28, 0x69, 0x1A, 0x30, 0x27, 0x0E, 0x20, 0x0F, 0x01, 0x2A, 0xDD, 0x90, 0x88, 0x5F, 0xF5, 0x7F, 0x38, 0xF7, 0xFF, 0xFE, 0x1A, 0xFF, 0x3A, 0xF5, 0x02, 0x2A, 0xF5, 0x30, 0x29, 0x50, 0x1D, 0xF0, 0x29, 0x83, 0xF0, 0x30, 0xF0, 0x7F, 0x25, 0xDE, 0x3A, 0x31, 0xD1, 0xF9, 0xFF, 0x7F, 0x60, 0xFE, 0x30, 0x8B, 0x6A, 0x3D, 0xFE, 0xF8, 0x2F, 0xCF, 0xC0, 0x44, 0x00, 0x28, 0x60, 0x01, 0xFF, 0xF5, 0x30, 0xA2, 0x2E, 0xAF, 0x80, 0x6A, 0x55, 0x03, 0x00, 0x30, 0xFF, 0xFE, 0xFF, 0xCF, 0x90, 0x32, 0x19, 0xFF, 0x38, 0x3A, 0x6D, 0xAF, 0xBF, 0xFF, 0xFF, 0x21, 0x2F, 0x0F, 0x29, 0x0A, 0xFB, 0x00, 0x00, 0xF2, 0x20, 0x87, 0x40, 0x2F, 0x20, 0x0F, 0xAF, 0x0F, 0x00, 0x11, 0xF2, 0xFB, 0x03, 0xFF, 0xE8, 0xFF, 0xBF, 0x1E, 0x9F, 0x22, 0x6E, 0x4A, 0x91, 0x01, 0xAF, 0xCF, 0xFF, 0xFF, 0x3F, 0x0F, 0xEF, 0x20, 0x0F, 0x67, 0x2E, 0x21, 0xFF, 0x72, 0x27, 0xFF, 0x03, 0x5B, 0x82, 0x70, 0x7F, 0x86, 0x77, 0x80, 0x01, 0x60, 0x7F, 0xF7, 0xF2, 0x7F, 0xBF, 0xD0, 0x70, 0xFF, 0x21, 0xFF, 0x20, 0x20, 0x03, 0xB1, 0xF5, 0xBF, 0x2F, 0x21, 0x23, 0x80, 0x39, 0x9F, 0xF2, 0xFD, 0xFF, 0x0B, 0x06, 0xFF, 0xDF, 0x00, 0x02, 0x00, 0x2F, 0x6F, 0x70, 0xB0, 0xBF, 0xFF, 0x40, 0xF1, 0x20, 0xC3, 0x07, 0x02, 0xDF, 0x7F, 0x00, 0x00, 0x02, 0xFB, 0xF6, 0xFD, 0xFF, 0xF2, 0xD0, 0x20, 0xB1, 0x0C, 0x1E, 0x00, 0x00, 0x07, 0x72, 0x87, 0x03, 0x74, 0x7F, 0x29, 0x73, 0xD4, 0x7F, 0x10, 0xE5, 0x56, 0xFD, 0x38, 0xF8, 0x00, 0x84, 0xBF, 0x41, 0x4E, 0x74, 0xBF, 0x9C, 0x63, 0x1A, 0x05, 0x70, 0x61, 0x74, 0x31, 0x3C, 0x63, 0x60, 0x1C, 0x67, 0x7B, 0x60, 0xF1, 0x22, 0x27, 0x3A, 0x7E, 0x53, 0x63, 0x65, 0x6E, 0x65, 0x08, 0x4F, 0x75, 0x74, 0x43, 0x2F, 0xFF, 0x47, 0x5F, 0x43, 0x10, 0x5F, 0x30, 0x30, 0xDF, 0xFF, 0x70, 0x61, 0x69, 0x31, 0x4D, 0x4C, 0x8B, 0x5A, 0x02, 0x00, 0x35, 0x19, 0x30, 0x43, 0x40, 0x2F, 0xFF, 0x04, 0x48, 0x62, 0x4D, 0x61, 0x74, 0x00, 0x2F, 0xFF, 0x48, 0x62, 0x0D, 0x52, 0x6F, 0x6F, 0x74, 0xE0, 0x48, 0x02, 0xE0, 0x9F, 0x42, 0x40, 0x9F, 0x56, 0x42, 0x08, 0x80, 0x9F, 0x41, 0x41, 0x3F, 0x41, 0x10, 0x0C, 0x81, 0x3F, 0x05, 0x33, 0x1F, 0x77, 0x00, 0xA2, 0x13, 0x49, 0x99, 0x58, 0x3D, 0x71, 0x8A, 0x00, 0x3A, 0x75, 0x0A, 0xEF, 0xE4, 0xC9, 0xFC, 0xB1, 0x00, 0x00, 0x99, 0x02, 0x63, 0xA9, 0x9B, 0x74, 0xE0, 0x00, 0x38, 0xD3, 0x33, 0xC0, 0x52, 0x6A, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; \ No newline at end of file From 6ab7c2a0dc487c84194ff8a106fe8d52e1cec0f6 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 26 Nov 2015 14:21:46 +0800 Subject: [PATCH 133/317] [ctrtool] Implemented --listromfs & --romfsdir to work directly with NCCH. Also implemented plainrgn extraction. --- ctrtool/ivfc.c | 60 +++++++++++++++++++++++++++++-------- ctrtool/ivfc.h | 22 +++++++++----- ctrtool/main.c | 13 ++++---- ctrtool/ncch.c | 38 +++++++++++++++++++++++- ctrtool/ncch.h | 5 ++++ ctrtool/romfs.c | 74 ++++++++++++++++++++++++++++++++++------------ ctrtool/romfs.h | 10 ++++++- ctrtool/settings.c | 13 ++++++++ ctrtool/settings.h | 3 ++ ctrtool/utils.c | 37 +++++++++++++++++++++++ ctrtool/utils.h | 4 +++ 11 files changed, 232 insertions(+), 47 deletions(-) diff --git a/ctrtool/ivfc.c b/ctrtool/ivfc.c index 90df3810..fb7a5f05 100644 --- a/ctrtool/ivfc.c +++ b/ctrtool/ivfc.c @@ -31,11 +31,47 @@ void ivfc_set_file(ivfc_context* ctx, FILE* file) ctx->file = file; } +void ivfc_set_encrypted(ivfc_context* ctx, u32 encrypted) +{ + ctx->encrypted = encrypted; +} + +void ivfc_set_key(ivfc_context* ctx, u8 key[16]) +{ + memcpy(ctx->key, key, 16); +} + +void ivfc_set_counter(ivfc_context* ctx, u8 counter[16]) +{ + memcpy(ctx->counter, counter, 16); +} + +void ivfc_fseek(ivfc_context* ctx, u64 offset) +{ + u64 data_pos = offset - ctx->offset; + fseeko64(ctx->file, offset, SEEK_SET); + ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); + ctr_add_counter(&ctx->aes, data_pos / 0x10); +} + +size_t ivfc_fread(ivfc_context* ctx, void* buffer, size_t size, size_t count) +{ + size_t read; + if ((read = fread(buffer, size, count, ctx->file)) != count) { + //printf("ivfc_fread() fail\n"); + return read; + } + if (ctx->encrypted) { + ctr_crypt_counter(&ctx->aes, buffer, buffer, size*read); + } + return read; +} + void ivfc_process(ivfc_context* ctx, u32 actions) { - fseeko64(ctx->file, ctx->offset, SEEK_SET); - fread(&ctx->header, 1, sizeof(ivfc_header), ctx->file); + ivfc_fseek(ctx, ctx->offset); + ivfc_fread(ctx, &ctx->header, 1, sizeof(ivfc_header)); if (getle32(ctx->header.magic) != MAGIC_IVFC) { @@ -45,25 +81,23 @@ void ivfc_process(ivfc_context* ctx, u32 actions) 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[2].hashblocksize = 1 << getle32(ctx->header.level3.blocksize); + ctx->level[1].hashblocksize = 1 << getle32(ctx->header.level2.blocksize); + ctx->level[0].hashblocksize = 1 << getle32(ctx->header.level1.blocksize); - ctx->bodyoffset = align64(IVFC_HEADER_SIZE + getle32(ctx->romfsheader.masterhashsize), ctx->level[2].hashblocksize); - ctx->bodysize = getle64(ctx->romfsheader.level3.hashdatasize); + ctx->bodyoffset = align64(IVFC_HEADER_SIZE + getle32(ctx->header.masterhashsize), ctx->level[2].hashblocksize); + ctx->bodysize = getle64(ctx->header.level3.hashdatasize); ctx->level[2].dataoffset = ctx->bodyoffset; ctx->level[2].datasize = align64(ctx->bodysize, ctx->level[2].hashblocksize); ctx->level[0].dataoffset = ctx->level[2].dataoffset + ctx->level[2].datasize; - ctx->level[0].datasize = align64(getle64(ctx->romfsheader.level1.hashdatasize), ctx->level[0].hashblocksize); + ctx->level[0].datasize = align64(getle64(ctx->header.level1.hashdatasize), ctx->level[0].hashblocksize); ctx->level[1].dataoffset = ctx->level[0].dataoffset + ctx->level[0].datasize; - ctx->level[1].datasize = align64(getle64(ctx->romfsheader.level2.hashdatasize), ctx->level[1].hashblocksize); + ctx->level[1].datasize = align64(getle64(ctx->header.level2.hashdatasize), ctx->level[1].hashblocksize); ctx->level[0].hashoffset = IVFC_HEADER_SIZE; ctx->level[1].hashoffset = ctx->level[0].dataoffset; @@ -125,8 +159,8 @@ void ivfc_read(ivfc_context* ctx, u64 offset, u64 size, u8* buffer) return; } - fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); - if (size != fread(buffer, 1, size, ctx->file)) + ivfc_fseek(ctx, ctx->offset + offset); + if (size != ivfc_fread(ctx, buffer, 1, size)) { fprintf(stderr, "Error, IVFC could not read file\n"); return; diff --git a/ctrtool/ivfc.h b/ctrtool/ivfc.h index 5c0aa1b1..9662f45e 100644 --- a/ctrtool/ivfc.h +++ b/ctrtool/ivfc.h @@ -2,18 +2,13 @@ #define __IVFC_H__ #include "types.h" +#include "ctr.h" #include "settings.h" #define IVFC_HEADER_SIZE 0x60 #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]; @@ -33,13 +28,15 @@ typedef struct typedef struct { + u8 magic[4]; + u8 id[4]; u8 masterhashsize[4]; ivfc_levelheader level1; ivfc_levelheader level2; ivfc_levelheader level3; u8 reserved[4]; u8 optionalsize[4]; -} ivfc_header_romfs; +} ivfc_header; typedef struct { @@ -47,9 +44,12 @@ typedef struct u64 offset; u64 size; settings* usersettings; + u8 counter[16]; + u8 key[16]; + ctr_aes_context aes; + int encrypted; ivfc_header header; - ivfc_header_romfs romfsheader; u32 levelcount; ivfc_level level[IVFC_MAX_LEVEL]; @@ -64,6 +64,12 @@ void ivfc_set_offset(ivfc_context* ctx, u64 offset); void ivfc_set_size(ivfc_context* ctx, u64 size); void ivfc_set_file(ivfc_context* ctx, FILE* file); void ivfc_set_usersettings(ivfc_context* ctx, settings* usersettings); +void ivfc_set_encrypted(ivfc_context* ctx, u32 encrypted); +void ivfc_set_key(ivfc_context* ctx, u8 key[16]); +void ivfc_set_counter(ivfc_context* ctx, u8 counter[16]); +void ivfc_fseek(ivfc_context* ctx, u64 offset); +size_t ivfc_fread(ivfc_context* ctx, void* buffer, size_t size, size_t count); + void ivfc_verify(ivfc_context* ctx, u32 flags); void ivfc_print(ivfc_context* ctx); diff --git a/ctrtool/main.c b/ctrtool/main.c index 25b688d5..4a7199be 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -64,11 +64,14 @@ static void usage(const char *argv0) " --lzssout=file Specify lzss output file\n" "CXI/CCI options:\n" " -n, --ncch=index Specify NCCH partition index.\n" + " --exheader=file Specify Extended Header file path.\n" + " --logo=file Specify Logo file path.\n" + " --plainrgn=file Specify Plain region file path" " --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" - " --logo=file Specify Logo file path.\n" + " --romfsdir=dir Specify RomFS directory path.\n" + " --listromfs List files in RomFS.\n" "CIA options:\n" " --certs=file Specify Certificate chain file path.\n" " --tik=file Specify Ticket file path.\n" @@ -83,9 +86,6 @@ static void usage(const char *argv0) "EXEFS options:\n" " --decompresscode Decompress .code section\n" " (only needed when using raw EXEFS file)\n" - "ROMFS options:\n" - " --romfsdir=dir Specify RomFS directory path.\n" - " --listromfs List files in RomFS.\n" "\n", __TIME__, __DATE__, argv0); exit(1); @@ -149,6 +149,7 @@ int main(int argc, char* argv[]) {"logo", 1, NULL, 20}, {"decompresscode", 0, NULL, 21}, {"titlekey", 1, NULL, 22}, + {"plainrgn", 1, NULL, 23}, {NULL}, }; @@ -237,6 +238,7 @@ int main(int argc, char* argv[]) case 20: settings_set_logo_path(&ctx.usersettings, optarg); break; case 21: ctx.actions |= DecompressCodeFlag; break; case 22: keyset_parse_titlekey(&tmpkeys, optarg, strlen(optarg)); break; + case 23: settings_set_plainrgn_path(&ctx.usersettings, optarg); break; default: usage(argv[0]); @@ -452,6 +454,7 @@ int main(int argc, char* argv[]) romfs_set_file(&romfsctx, ctx.infile); romfs_set_size(&romfsctx, ctx.infilesize); romfs_set_usersettings(&romfsctx, &ctx.usersettings); + romfs_set_encrypted(&romfsctx, 0); romfs_process(&romfsctx, ctx.actions); break; diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 991afc3a..7c06b231 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -116,6 +116,13 @@ int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags) } break; + case NCCHTYPE_PLAINRGN: + { + offset = ncch_get_plainrgn_offset(ctx); + size = ncch_get_plainrgn_size(ctx); + } + break; + default: { fprintf(stderr, "Error invalid NCCH type\n"); @@ -181,6 +188,7 @@ void ncch_save(ncch_context* ctx, u32 type, u32 flags) case NCCHTYPE_ROMFS: path = settings_get_romfs_path(ctx->usersettings); break; case NCCHTYPE_EXHEADER: path = settings_get_exheader_path(ctx->usersettings); break; case NCCHTYPE_LOGO: path = settings_get_logo_path(ctx->usersettings); break; + case NCCHTYPE_PLAINRGN: path = settings_get_plainrgn_path(ctx->usersettings); break; } if (path == 0 || path->valid == 0) @@ -199,13 +207,14 @@ void ncch_save(ncch_context* ctx, u32 type, u32 flags) case NCCHTYPE_ROMFS: fprintf(stdout, "Saving RomFS...\n"); break; case NCCHTYPE_EXHEADER: fprintf(stdout, "Saving Extended Header...\n"); break; case NCCHTYPE_LOGO: fprintf(stdout, "Saving Logo...\n"); break; + case NCCHTYPE_PLAINRGN: fprintf(stdout, "Saving Plain Region...\n"); break; } while(1) { u32 read_len; - if (0 == ncch_extract_buffer(ctx, buffer, sizeof(buffer), &read_len, type == NCCHTYPE_LOGO)) + if (0 == ncch_extract_buffer(ctx, buffer, sizeof(buffer), &read_len, type == NCCHTYPE_LOGO || type == NCCHTYPE_PLAINRGN)) goto clean; if (read_len == 0) @@ -304,6 +313,7 @@ void ncch_process(ncch_context* ctx, u32 actions) { u8 exheadercounter[16]; u8 exefscounter[16]; + u8 romfscounter[16]; int result = 1; @@ -320,6 +330,7 @@ void ncch_process(ncch_context* ctx, u32 actions) ncch_get_counter(ctx, exheadercounter, NCCHTYPE_EXHEADER); ncch_get_counter(ctx, exefscounter, NCCHTYPE_EXEFS); + ncch_get_counter(ctx, romfscounter, NCCHTYPE_ROMFS); exheader_set_file(&ctx->exheader, ctx->file); @@ -342,6 +353,14 @@ void ncch_process(ncch_context* ctx, u32 actions) exefs_set_key(&ctx->exefs, ctx->key); exefs_set_encrypted(&ctx->exefs, ctx->encrypted); + romfs_set_file(&ctx->romfs, ctx->file); + romfs_set_offset(&ctx->romfs, ncch_get_romfs_offset(ctx)); + romfs_set_size(&ctx->romfs, ncch_get_romfs_size(ctx)); + romfs_set_usersettings(&ctx->romfs, ctx->usersettings); + romfs_set_counter(&ctx->romfs, romfscounter); + romfs_set_key(&ctx->romfs, ctx->key); + romfs_set_encrypted(&ctx->romfs, ctx->encrypted); + exheader_read(&ctx->exheader, actions); @@ -357,6 +376,7 @@ void ncch_process(ncch_context* ctx, u32 actions) ncch_save(ctx, NCCHTYPE_ROMFS, actions); ncch_save(ctx, NCCHTYPE_EXHEADER, actions); ncch_save(ctx, NCCHTYPE_LOGO, actions); + ncch_save(ctx, NCCHTYPE_PLAINRGN, actions); } @@ -374,6 +394,11 @@ void ncch_process(ncch_context* ctx, u32 actions) exefs_set_compressedflag(&ctx->exefs, exheader_get_compressedflag(&ctx->exheader)); exefs_process(&ctx->exefs, actions); } + + if (result && ncch_get_romfs_size(ctx)) + { + romfs_process(&ctx->romfs, actions); + } } int ncch_signature_verify(ncch_context* ctx, rsakey2048* key) @@ -425,6 +450,17 @@ u64 ncch_get_logo_size(ncch_context* ctx) return getle32(ctx->header.logosize) * ncch_get_mediaunit_size(ctx); } +u64 ncch_get_plainrgn_offset(ncch_context* ctx) +{ + return ctx->offset + getle32(ctx->header.plainregionoffset) * ncch_get_mediaunit_size(ctx); +} + +u64 ncch_get_plainrgn_size(ncch_context* ctx) +{ + return getle32(ctx->header.plainregionsize) * ncch_get_mediaunit_size(ctx); +} + + u64 ncch_get_mediaunit_size(ncch_context* ctx) { unsigned int mediaunitsize = settings_get_mediaunit_size(ctx->usersettings); diff --git a/ctrtool/ncch.h b/ctrtool/ncch.h index 4bad2be1..64dc4901 100644 --- a/ctrtool/ncch.h +++ b/ctrtool/ncch.h @@ -7,6 +7,7 @@ #include "filepath.h" #include "ctr.h" #include "exefs.h" +#include "romfs.h" #include "exheader.h" #include "settings.h" @@ -16,6 +17,7 @@ typedef enum NCCHTYPE_EXEFS = 2, NCCHTYPE_ROMFS = 3, NCCHTYPE_LOGO = 4, + NCCHTYPE_PLAINRGN = 5, } ctr_ncchtypes; typedef struct @@ -63,6 +65,7 @@ typedef struct ctr_ncchheader header; ctr_aes_context aes; exefs_context exefs; + romfs_context romfs; exheader_context exheader; int exefshashcheck; int romfshashcheck; @@ -87,6 +90,8 @@ u64 ncch_get_exheader_offset(ncch_context* ctx); u64 ncch_get_exheader_size(ncch_context* ctx); u64 ncch_get_logo_offset(ncch_context* ctx); u64 ncch_get_logo_size(ncch_context* ctx); +u64 ncch_get_plainrgn_offset(ncch_context* ctx); +u64 ncch_get_plainrgn_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); diff --git a/ctrtool/romfs.c b/ctrtool/romfs.c index 05ce5c60..5889bdc4 100644 --- a/ctrtool/romfs.c +++ b/ctrtool/romfs.c @@ -30,7 +30,41 @@ void romfs_set_usersettings(romfs_context* ctx, settings* usersettings) ctx->usersettings = usersettings; } +void romfs_set_encrypted(romfs_context* ctx, u32 encrypted) +{ + ctx->encrypted = encrypted; +} + +void romfs_set_key(romfs_context* ctx, u8 key[16]) +{ + memcpy(ctx->key, key, 16); +} +void romfs_set_counter(romfs_context* ctx, u8 counter[16]) +{ + memcpy(ctx->counter, counter, 16); +} + +void romfs_fseek(romfs_context* ctx, u64 offset) +{ + u64 data_pos = offset - ctx->offset; + fseeko64(ctx->file, offset, SEEK_SET); + ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); + ctr_add_counter(&ctx->aes, data_pos / 0x10); +} + +size_t romfs_fread(romfs_context* ctx, void* buffer, size_t size, size_t count) +{ + size_t read; + if ((read = fread(buffer, size, count, ctx->file)) != count) { + //printf("romfs_fread() fail\n"); + return read; + } + if (ctx->encrypted) { + ctr_crypt_counter(&ctx->aes, buffer, buffer, size*read); + } + return read; +} void romfs_process(romfs_context* ctx, u32 actions) { @@ -39,15 +73,17 @@ void romfs_process(romfs_context* ctx, u32 actions) 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_set_counter(&ctx->ivfc, ctx->counter); + ivfc_set_key(&ctx->ivfc, ctx->key); + ivfc_set_encrypted(&ctx->ivfc, ctx->encrypted); ivfc_process(&ctx->ivfc, actions); - fseeko64(ctx->file, ctx->offset, SEEK_SET); - fread(&ctx->header, 1, sizeof(romfs_header), ctx->file); + romfs_fseek(ctx, ctx->offset); + romfs_fread(ctx, &ctx->header, 1, sizeof(romfs_header)); if (getle32(ctx->header.magic) != MAGIC_IVFC) { @@ -57,8 +93,8 @@ void romfs_process(romfs_context* ctx, u32 actions) ctx->infoblockoffset = ctx->offset + 0x1000; - fseeko64(ctx->file, ctx->infoblockoffset, SEEK_SET); - fread(&ctx->infoheader, 1, sizeof(romfs_infoheader), ctx->file); + romfs_fseek(ctx, ctx->infoblockoffset); + romfs_fread(ctx, &ctx->infoheader, 1, sizeof(romfs_infoheader)); if (getle32(ctx->infoheader.headersize) != sizeof(romfs_infoheader)) { @@ -71,24 +107,24 @@ void romfs_process(romfs_context* ctx, u32 actions) fileblockoffset = ctx->infoblockoffset + getle32(ctx->infoheader.section[3].offset); fileblocksize = getle32(ctx->infoheader.section[3].size); + u32 hdrsize = getle32(ctx->infoheader.dataoffset); + u8 *block = malloc(hdrsize); + romfs_fseek(ctx, ctx->infoblockoffset); + romfs_fread(ctx, block, hdrsize, 1); + ctx->dirblock = malloc(dirblocksize); ctx->dirblocksize = dirblocksize; + if(ctx->dirblock) + memcpy(ctx->dirblock, block + getle32(ctx->infoheader.section[1].offset), dirblocksize); + ctx->fileblock = malloc(fileblocksize); ctx->fileblocksize = fileblocksize; + if (ctx->fileblock) + memcpy(ctx->fileblock, block + getle32(ctx->infoheader.section[3].offset), fileblocksize); - ctx->datablockoffset = ctx->infoblockoffset + getle32(ctx->infoheader.dataoffset); - - if (ctx->dirblock) - { - fseeko64(ctx->file, dirblockoffset, SEEK_SET); - fread(ctx->dirblock, 1, dirblocksize, ctx->file); - } + free(block); - if (ctx->fileblock) - { - fseeko64(ctx->file, fileblockoffset, SEEK_SET); - fread(ctx->fileblock, 1, fileblocksize, ctx->file); - } + ctx->datablockoffset = ctx->infoblockoffset + getle32(ctx->infoheader.dataoffset); if (actions & InfoFlag) romfs_print(ctx); @@ -315,7 +351,7 @@ void romfs_extract_datafile(romfs_context* ctx, u64 offset, u64 size, const osch offset += ctx->datablockoffset; - fseeko64(ctx->file, offset, SEEK_SET); + romfs_fseek(ctx, offset); outfile = os_fopen(path, OS_MODE_WRITE); if (outfile == NULL) { @@ -329,7 +365,7 @@ void romfs_extract_datafile(romfs_context* ctx, u64 offset, u64 size, const osch if (max > size) max = size; - if (max != fread(buffer, 1, max, ctx->file)) + if (max != romfs_fread(ctx, buffer, 1, max)) { fprintf(stderr, "Error reading file\n"); goto clean; diff --git a/ctrtool/romfs.h b/ctrtool/romfs.h index c560ab4b..7706be59 100644 --- a/ctrtool/romfs.h +++ b/ctrtool/romfs.h @@ -57,6 +57,8 @@ typedef struct FILE* file; oschar_t* extractdir; settings* usersettings; + u8 counter[16]; + u8 key[16]; u64 offset; u64 size; romfs_header header; @@ -70,6 +72,8 @@ typedef struct romfs_direntry direntry; romfs_fileentry fileentry; ivfc_context ivfc; + ctr_aes_context aes; + int encrypted; } romfs_context; void romfs_init(romfs_context* ctx); @@ -77,7 +81,11 @@ void romfs_set_file(romfs_context* ctx, FILE* file); void romfs_set_offset(romfs_context* ctx, u64 offset); void romfs_set_size(romfs_context* ctx, u64 size); void romfs_set_usersettings(romfs_context* ctx, settings* usersettings); -void romfs_test(romfs_context* ctx); +void romfs_set_encrypted(romfs_context* ctx, u32 encrypted); +void romfs_set_key(romfs_context* ctx, u8 key[16]); +void romfs_set_counter(romfs_context* ctx, u8 counter[16]); +void romfs_fseek(romfs_context* ctx, u64 offset); +size_t romfs_fread(romfs_context* ctx, void* buffer, size_t size, size_t count); 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); diff --git a/ctrtool/settings.c b/ctrtool/settings.c index cbc7aec1..7888d545 100644 --- a/ctrtool/settings.c +++ b/ctrtool/settings.c @@ -120,6 +120,14 @@ filepath* settings_get_content_path(settings* usersettings) return 0; } +filepath* settings_get_plainrgn_path(settings* usersettings) +{ + if (usersettings) + return &usersettings->plainrgnpath; + else + return 0; +} + unsigned int settings_get_mediaunit_size(settings* usersettings) { if (usersettings) @@ -255,6 +263,11 @@ void settings_set_romfs_dir_path(settings* usersettings, const char* path) filepath_set(&usersettings->romfsdirpath, path); } +void settings_set_plainrgn_path(settings* usersettings, const char* path) +{ + filepath_set(&usersettings->plainrgnpath, path); +} + void settings_set_mediaunit_size(settings* usersettings, unsigned int size) { usersettings->mediaunitsize = size; diff --git a/ctrtool/settings.h b/ctrtool/settings.h index c75bb94b..30b42d22 100644 --- a/ctrtool/settings.h +++ b/ctrtool/settings.h @@ -15,6 +15,7 @@ typedef struct filepath romfsdirpath; filepath exheaderpath; filepath logopath; + filepath plainrgnpath; filepath certspath; filepath contentpath; filepath tikpath; @@ -43,6 +44,7 @@ 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); +filepath* settings_get_plainrgn_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); @@ -66,6 +68,7 @@ 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_plainrgn_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); diff --git a/ctrtool/utils.c b/ctrtool/utils.c index d2d4adb6..cfaf184b 100644 --- a/ctrtool/utils.c +++ b/ctrtool/utils.c @@ -84,6 +84,43 @@ void putle32(u8* p, u32 n) p[3] = n>>24; } +void putle64(u8* p, u64 n) +{ + p[0] = n; + p[1] = n >> 8; + p[2] = n >> 16; + p[3] = n >> 24; + p[4] = n >> 32; + p[5] = n >> 40; + p[6] = n >> 48; + p[7] = n >> 56; +} + +void putbe16(u8* p, u16 n) +{ + p[1] = n; + p[0] = n >> 8; +} + +void putbe32(u8* p, u32 n) +{ + p[3] = n; + p[2] = n >> 8; + p[1] = n >> 16; + p[0] = n >> 24; +} + +void putbe64(u8* p, u64 n) +{ + p[7] = n; + p[6] = n >> 8; + p[5] = n >> 16; + p[4] = n >> 24; + p[3] = n >> 32; + p[2] = n >> 40; + p[1] = n >> 48; + p[0] = n >> 56; +} void readkeyfile(u8* key, const char* keyfname) { diff --git a/ctrtool/utils.h b/ctrtool/utils.h index 519a95e5..4d766aac 100644 --- a/ctrtool/utils.h +++ b/ctrtool/utils.h @@ -27,6 +27,10 @@ u32 getbe32(const u8* p); u32 getbe16(const u8* p); void putle16(u8* p, u16 n); void putle32(u8* p, u32 n); +void putle64(u8* p, u64 n); +void putbe16(u8* p, u16 n); +void putbe32(u8* p, u32 n); +void putbe64(u8* p, u64 n); void readkeyfile(u8* key, const char* keyfname); void memdump(FILE* fout, const char* prefix, const u8* data, u32 size); From 2f81642a69b4f5cc0f7e5d312aadee3b19544e4b Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 26 Nov 2015 14:26:01 +0800 Subject: [PATCH 134/317] [ctrtool] Misc. --- ctrtool/utils.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ctrtool/utils.c b/ctrtool/utils.c index cfaf184b..1f21f073 100644 --- a/ctrtool/utils.c +++ b/ctrtool/utils.c @@ -1,4 +1,5 @@ #include +#include #include #ifdef _WIN32 #include From b17804b6857d94f885811f5bd494bf6cade6f5cc Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 26 Nov 2015 19:53:19 +0800 Subject: [PATCH 135/317] [ctrtool/makerom] Silenced warnings, fixed compiling on systems with libiconv installed. --- ctrtool/Makefile | 2 +- ctrtool/oschar.c | 1 + makerom/Makefile | 2 +- makerom/oschar.c | 3 +++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 165afc5e..73bd2051 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -5,7 +5,7 @@ OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) $(foreach # Compiler Settings LIBS = -static-libgcc -static-libstdc++ CXXFLAGS = -I. -CFLAGS = -O2 -flto -Wall -Wno-unused-variable -Wno-unused-but-set-variable -I. +CFLAGS = -O2 -flto -Wall -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-result -I. OUTPUT = ctrtool CC = gcc CXX = g++ diff --git a/ctrtool/oschar.c b/ctrtool/oschar.c index 1ef05ddb..715eab3d 100644 --- a/ctrtool/oschar.c +++ b/ctrtool/oschar.c @@ -1,5 +1,6 @@ #include #ifndef _WIN32 +#define LIBICONV_PLUG #include #endif #include "oschar.h" diff --git a/makerom/Makefile b/makerom/Makefile index e68a81c4..19d05055 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -5,7 +5,7 @@ OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) # Compiler Settings LIBS = -static-libgcc CXXFLAGS = -I. -CFLAGS = --std=c99 -O2 -flto -Wall -Wno-unused-but-set-variable -Wno-unused-value -I. $(MAKEROM_BUILD_FLAGS) +CFLAGS = --std=c99 -O2 -flto -Wall -Wno-unused-but-set-variable -Wno-unused-value -Wno-unused-result -I. $(MAKEROM_BUILD_FLAGS) CC = gcc CXX = g++ diff --git a/makerom/oschar.c b/makerom/oschar.c index 1ef05ddb..f850ef38 100644 --- a/makerom/oschar.c +++ b/makerom/oschar.c @@ -1,5 +1,6 @@ #include #ifndef _WIN32 +#define LIBICONV_PLUG #include #endif #include "oschar.h" @@ -134,6 +135,7 @@ utf16char_t* strcopy_UTF8toUTF16(const char *src) iconv_t cd = iconv_open("UTF-16LE", "UTF-8"); iconv(cd, &in, &in_bytes, &out, &out_bytes); + iconv_close(cd); return dst; } @@ -162,6 +164,7 @@ char* strcopy_UTF16toUTF8(const utf16char_t *src) iconv_t cd = iconv_open("UTF-8", "UTF-16LE"); iconv(cd, &in, &in_bytes, &out, &out_bytes); + iconv_close(cd); return dst; } #endif From acb8c37d36ee18ef2f5c2ca0b2ceb983ae0b1eed Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 26 Nov 2015 19:59:02 +0800 Subject: [PATCH 136/317] [ctrtool] Misc --- ctrtool/oschar.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ctrtool/oschar.c b/ctrtool/oschar.c index 715eab3d..f850ef38 100644 --- a/ctrtool/oschar.c +++ b/ctrtool/oschar.c @@ -135,6 +135,7 @@ utf16char_t* strcopy_UTF8toUTF16(const char *src) iconv_t cd = iconv_open("UTF-16LE", "UTF-8"); iconv(cd, &in, &in_bytes, &out, &out_bytes); + iconv_close(cd); return dst; } @@ -163,6 +164,7 @@ char* strcopy_UTF16toUTF8(const utf16char_t *src) iconv_t cd = iconv_open("UTF-8", "UTF-16LE"); iconv(cd, &in, &in_bytes, &out, &out_bytes); + iconv_close(cd); return dst; } #endif From 94c4ba7b93310ade282f214e8e0c289b93620858 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 1 Dec 2015 13:04:50 +0800 Subject: [PATCH 137/317] [makerom] Altered encryption preferences for different keychains. --- makerom/user_settings.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 8eada633..2d2753ca 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -175,16 +175,24 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - if (strcasecmp(argv[i + 1], "test") == 0 || strcasecmp(argv[i + 1], "t") == 0) + if (strcasecmp(argv[i + 1], "test") == 0 || strcasecmp(argv[i + 1], "t") == 0) { set->common.keys.keyset = pki_TEST; - //else if(strcasecmp(argv[i+1],"beta") == 0 || strcasecmp(argv[i+1],"b") == 0) + set->common.rsfSet.Option.EnableCrypt = false; // prefer unencrypted output + } + //else if(strcasecmp(argv[i+1],"beta") == 0 || strcasecmp(argv[i+1],"b") == 0) { // set->common.keys.keyset = pki_BETA; - else if (strcasecmp(argv[i + 1], "debug") == 0 || strcasecmp(argv[i + 1], "development") == 0 || strcasecmp(argv[i + 1], "d") == 0) + //} + else if (strcasecmp(argv[i + 1], "debug") == 0 || strcasecmp(argv[i + 1], "development") == 0 || strcasecmp(argv[i + 1], "d") == 0) { set->common.keys.keyset = pki_DEVELOPMENT; - else if (strcasecmp(argv[i + 1], "retail") == 0 || strcasecmp(argv[i + 1], "production") == 0 || strcasecmp(argv[i + 1], "p") == 0) + set->common.rsfSet.Option.EnableCrypt = true; // prefer encrypted output + } + else if (strcasecmp(argv[i + 1], "retail") == 0 || strcasecmp(argv[i + 1], "production") == 0 || strcasecmp(argv[i + 1], "p") == 0) { set->common.keys.keyset = pki_PRODUCTION; - //else if(strcasecmp(argv[i+1],"custom") == 0 || strcasecmp(argv[i+1],"c") == 0) + set->common.rsfSet.Option.EnableCrypt = true; // prefer encrypted output + } + //else if(strcasecmp(argv[i+1],"custom") == 0 || strcasecmp(argv[i+1],"c") == 0) { // set->common.keys.keyset = pki_CUSTOM; + //} else { fprintf(stderr, "[SETTING ERROR] Unrecognised target '%s'\n", argv[i + 1]); return USR_BAD_ARG; From 4d9f77b399ea27a881f6a30af788a644a0de8c53 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 17 Dec 2015 16:13:50 +0800 Subject: [PATCH 138/317] [makerom] misc --- makerom/makerom.sln | 2 +- makerom/makerom.vcxproj | 2 +- makerom/user_settings.c | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/makerom/makerom.sln b/makerom/makerom.sln index ff089e36..ab515c70 100644 --- a/makerom/makerom.sln +++ b/makerom/makerom.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "makerom", "makerom.vcxproj", "{21926330-F5A5-4643-AD32-D4F167CE226B}" EndProject diff --git a/makerom/makerom.vcxproj b/makerom/makerom.vcxproj index 896eb095..fa3547f7 100644 --- a/makerom/makerom.vcxproj +++ b/makerom/makerom.vcxproj @@ -67,7 +67,7 @@ make clean make rebuild WIN32;_DEBUG;$(NMakePreprocessorDefinitions) - C:\Program Files\mingw-w64\x86_64-4.9.2-win32-seh-rt_v4-rev2\mingw64\x86_64-w64-mingw32\include + C:\mingw\mingw64\x86_64-w64-mingw32\include make diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 2d2753ca..21a6eed4 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -49,14 +49,12 @@ int ParseArgs(int argc, char *argv[], user_settings *set) // Parsing Arguments int set_result; - int i = 1; - while (i < argc) { + for (int i = 1; i < argc; i += set_result) { set_result = SetArgument(argc, i, argv, set); if (set_result < 1) { fprintf(stderr, "[RESULT] Invalid arguments, see '%s -help'\n", argv[0]); return set_result; } - i += set_result; } // Checking arguments From 83d4a1fcf30d15547080a9a63c80e7d665604bc2 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 17 Dec 2015 16:20:03 +0800 Subject: [PATCH 139/317] [makerom] Fixed ELF related code. Addresses #14 --- makerom/code.c | 246 ++++++++++++++-------------------------- makerom/elf.c | 234 ++++++++++---------------------------- makerom/elf.h | 69 +++++------ makerom/rsf_settings.c | 4 - makerom/user_settings.h | 3 - 5 files changed, 165 insertions(+), 391 deletions(-) diff --git a/makerom/code.c b/makerom/code.c index 07ee74a7..22b64bf0 100644 --- a/makerom/code.c +++ b/makerom/code.c @@ -5,32 +5,33 @@ #include "exheader_read.h" #include "code.h" -const char *SDK_PLAINREGION_SEGMENT_NAME = ".module_id"; +const u32 CTR_PAGE_SIZE = 0x1000; +const u32 DEFAULT_STACK_SIZE = 0x4000; // 10KB typedef struct code_segment { u32 address; + u32 memSize; + u32 pageNum; + u32 size; - u32 maxPageNum; u8 *data; } code_segment; -u32 GetPageSize(ncch_settings *set) +u32 SizeToPage(u32 memorySize) { - if (set->rsfSet->Option.PageSize) - return strtoul(set->rsfSet->Option.PageSize, NULL, 10); - return 0x1000; + return align(memorySize, CTR_PAGE_SIZE) / CTR_PAGE_SIZE; } -u32 SizeToPage(u32 memorySize, elf_context *elf) +u32 PageToSize(u32 pageNum) { - return align(memorySize, elf->pageSize) / elf->pageSize; + return pageNum * CTR_PAGE_SIZE; } int ImportPlainRegionFromFile(ncch_settings *set) { set->sections.plainRegion.size = align(set->componentFilePtrs.plainregionSize, set->options.blockSize); - set->sections.plainRegion.buffer = malloc(set->sections.plainRegion.size); + set->sections.plainRegion.buffer = calloc(set->sections.plainRegion.size, 1); if (!set->sections.plainRegion.buffer) { fprintf(stderr, "[ELF ERROR] Not enough memory\n"); return MEM_ERROR; } ReadFile64(set->sections.plainRegion.buffer, set->componentFilePtrs.plainregionSize, 0, set->componentFilePtrs.plainregion); return 0; @@ -99,117 +100,61 @@ int ImportExeFsCodeBinaryFromFile(ncch_settings *set) return 0; } -int GetBSSFromElf(elf_context *elf, ncch_settings *set) +int ImportPlainRegionFromElf(elf_context *elf, ncch_settings *set) { - set->codeDetails.bssSize = 0; - - for (int i = 0; i < elf->sectionTableEntryCount; i++) { - if (IsBss(&elf->sections[i])) - set->codeDetails.bssSize = elf->sections[i].size; - } + elf_segment segment = elf_GetSegments(elf)[elf_SegmentNum(elf) - 1]; - return 0; -} - -int ImportPlainRegionFromElf(elf_context *elf, ncch_settings *set) // Doesn't work same as N makerom -{ - u64 size = 0; - u64 offset = 0; - for (u16 i = 0; i < elf->activeSegments; i++) { - if (strcmp(elf->segments[i].name, SDK_PLAINREGION_SEGMENT_NAME) == 0) { - size = elf->segments[i].header->sizeInFile; - offset = elf->segments[i].header->offsetInFile; - break; - } + /* Check last segment + If the last segment is RO segment, this must be an SDK .module_id segment */ + if (segment.flags != PF_RODATA) { + /* not a RO segment */ + return 0; } - - if (size > 0) { + if (segment.fileSize > 0) { /* Creating Output Buffer */ - set->sections.plainRegion.size = align(size, set->options.blockSize); - set->sections.plainRegion.buffer = malloc(set->sections.plainRegion.size); - if (!set->sections.plainRegion.buffer) { fprintf(stderr, "[ELF ERROR] Not enough memory\n"); return MEM_ERROR; } - memset(set->sections.plainRegion.buffer, 0, set->sections.plainRegion.size); + set->sections.plainRegion.size = align(segment.fileSize, set->options.blockSize); + set->sections.plainRegion.buffer = calloc(set->sections.plainRegion.size, 1); + if (!set->sections.plainRegion.buffer) { fprintf(stderr, "[CODE ERROR] Not enough memory\n"); return MEM_ERROR; } /* Copy Plain Region */ - memcpy(set->sections.plainRegion.buffer, elf->file + offset, size); + memcpy(set->sections.plainRegion.buffer, segment.ptr, segment.fileSize); } - return 0; } int CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u64 segment_flags) { - memset(out, 0, sizeof(code_segment)); - - u16 seg_num = 0; - elf_segment **seg = calloc(elf->activeSegments, sizeof(elf_segment*)); - - for (u16 i = 0; i < elf->activeSegments; i++) { - // Skip SDK ELF plain region - if (strcmp(elf->segments[i].name, SDK_PLAINREGION_SEGMENT_NAME) == 0) + u32 segmentNum = elf_SegmentNum(elf); + const elf_segment *segments = elf_GetSegments(elf); + + /* Find segment */ + for (u16 i = 0; i < segmentNum; i++) { + /* Skip SDK ELF plain region + The last segment should always be data in valid ELFs, + unless this is an SDK ELF with .module_id segment */ + if (i == segmentNum-1 && segments[i].flags == PF_RODATA) continue; - //printf("SegName: %s (flags: %x)\n", elf->segments[i].name, elf->segments[i].header->flags); - if ((elf->segments[i].header->flags & ~PF_CTRSDK) == segment_flags) { - if (seg_num == 0) { - seg[seg_num] = &elf->segments[i]; - seg_num++; - } - else if (elf->segments[i].vAddr == (u32)align(seg[seg_num - 1]->vAddr, seg[seg_num - 1]->header->alignment)) { - seg[seg_num] = &elf->segments[i]; - seg_num++; - } + /* Found segment */ + if ((segments[i].flags & ~PF_CTRSDK) == segment_flags) { + out->address = segments[i].vAddr; + out->memSize = segments[i].memSize; + out->pageNum = SizeToPage(out->memSize); + out->size = segments[i].fileSize; + out->data = malloc(out->size); + if (!out->data) { fprintf(stderr, "[CODE ERROR] Not enough memory\n"); return MEM_ERROR; } + memcpy(out->data, segments[i].ptr, out->size); + return 0; } } - /* Return if there are no applicable segment */ - if (seg_num == 0) - return 0; - - /* Getting Segment Size/Settings */ - u32 vAddr = 0; - u32 memorySize = 0; - for (u16 i = 0; i < seg_num; i++) { - if (i == 0) { - vAddr = seg[i]->vAddr; - } - else { // Add rounded size from previous segment - u32 padding = seg[i]->vAddr - (vAddr + memorySize); - memorySize += padding; - } - - memorySize += seg[i]->header->sizeInMemory; - - if (IsBss(&seg[i]->sections[seg[i]->sectionNum - 1])) - memorySize -= seg[i]->sections[seg[i]->sectionNum - 1].size; - } - - // For Check -#ifdef DEBUG - printf("Address: 0x%x\n", vAddr); - printf("Size: 0x%x\n", memorySize); -#endif - - out->address = vAddr; - out->size = memorySize; - out->maxPageNum = SizeToPage(memorySize, elf); - out->data = malloc(memorySize); - - /* Writing Segment to Buffer */ - for (int i = 0; i < seg_num; i++) { - - for (int j = 0; j < seg[i]->sectionNum; j++) { - elf_section_entry *section = &seg[i]->sections[j]; - if (!IsBss(section)) { - u8 *pos = (out->data + (section->address - seg[i]->vAddr)); - memcpy(pos, section->ptr, section->size); - //size += section->size; - } - } - } - - free(seg); + /* Stub struct data */ + out->address = 0; + out->memSize = 0; + out->pageNum = 0; + out->size = 0; + out->data = NULL; return 0; } @@ -217,37 +162,33 @@ int CreateExeFsCode(elf_context *elf, ncch_settings *set) { /* Getting Code Segments */ code_segment text; - memset(&text, 0, sizeof(code_segment)); code_segment rodata; - memset(&rodata, 0, sizeof(code_segment)); code_segment rwdata; - memset(&rwdata, 0, sizeof(code_segment)); - int result = CreateCodeSegmentFromElf(&text, elf, PF_TEXT); - if (result) return result; - result = CreateCodeSegmentFromElf(&rodata, elf, PF_RODATA); - if (result) return result; - result = CreateCodeSegmentFromElf(&rwdata, elf, PF_DATA); - if (result) return result; + if (CreateCodeSegmentFromElf(&text, elf, PF_TEXT) == MEM_ERROR || CreateCodeSegmentFromElf(&rodata, elf, PF_RODATA) == MEM_ERROR || CreateCodeSegmentFromElf(&rwdata, elf, PF_DATA) == MEM_ERROR) + return MEM_ERROR; /* Checking the existence of essential ELF Segments */ if (!text.size) return NOT_FIND_TEXT_SEGMENT; if (!rwdata.size) return NOT_FIND_DATA_SEGMENT; + /* Calculateing BSS size */ + set->codeDetails.bssSize = rwdata.memSize - rwdata.size; + /* Allocating Buffer for ExeFs Code */ - u32 size = (text.maxPageNum + rodata.maxPageNum + rwdata.maxPageNum)*elf->pageSize; + u32 size = PageToSize(text.pageNum + rodata.pageNum + rwdata.pageNum); u8 *code = calloc(1, size); /* Writing Code into Buffer */ - u8 *textPos = (code + 0); - u8 *rodataPos = (code + text.maxPageNum*elf->pageSize); - u8 *rwdataPos = (code + (text.maxPageNum + rodata.maxPageNum)*elf->pageSize); + u8 *textPos = (code + text.address - text.address); + u8 *rodataPos = (code + rodata.address - text.address); + u8 *rwdataPos = (code + rwdata.address - text.address); if (text.size) memcpy(textPos, text.data, text.size); if (rodata.size) memcpy(rodataPos, rodata.data, rodata.size); if (rwdata.size) memcpy(rwdataPos, rwdata.data, rwdata.size); - /* Compressing If needed */ + /* Compressing if needed */ if (set->options.CompressCode) { if (set->options.verbose) printf("[CODE] Compressing code... "); @@ -265,31 +206,32 @@ int CreateExeFsCode(elf_context *elf, ncch_settings *set) /* Setting code_segment data and freeing original buffers */ set->codeDetails.textAddress = text.address; - set->codeDetails.textMaxPages = text.maxPageNum; - set->codeDetails.textSize = text.size; + set->codeDetails.textMaxPages = text.pageNum; + set->codeDetails.textSize = text.memSize; if (text.size) free(text.data); set->codeDetails.roAddress = rodata.address; - set->codeDetails.roMaxPages = rodata.maxPageNum; - set->codeDetails.roSize = rodata.size; + set->codeDetails.roMaxPages = rodata.pageNum; + set->codeDetails.roSize = rodata.memSize; if (rodata.size) free(rodata.data); set->codeDetails.rwAddress = rwdata.address; - set->codeDetails.rwMaxPages = rwdata.maxPageNum; - set->codeDetails.rwSize = rwdata.size; + set->codeDetails.rwMaxPages = rwdata.pageNum; + set->codeDetails.rwSize = rwdata.memSize; if (rwdata.size) free(rwdata.data); if (set->rsfSet->SystemControlInfo.StackSize) set->codeDetails.stackSize = strtoul(set->rsfSet->SystemControlInfo.StackSize, NULL, 0); else { - fprintf(stderr, "[CODE ERROR] RSF Parameter Not Found: \"SystemControlInfo/StackSize\"\n"); - return 1; + set->codeDetails.stackSize = DEFAULT_STACK_SIZE; + fprintf(stderr, "[CODE WARNING] \"SystemControlInfo/StackSize\" not specified, defaulting to 0x%x bytes\n", DEFAULT_STACK_SIZE); } /* Return */ return 0; } +/* void PrintElfContext(elf_context *elf) { printf("[ELF] Program Table Data\n"); @@ -303,27 +245,17 @@ void PrintElfContext(elf_context *elf) printf(" Label index: 0x%x\n", elf->sectionHeaderNameEntryIndex); for (int i = 0; i < elf->activeSegments; i++) { printf(" Segment [%d][%s]\n", i, elf->segments[i].name); - printf(" > Size : 0x%x\n", elf->segments[i].header->sizeInFile); - printf(" > Address : 0x%x\n", elf->segments[i].vAddr); - printf(" > Flags: 0x%x\n", elf->segments[i].header->flags); - printf(" > Type: 0x%x\n", elf->segments[i].header->type); - printf(" > Sections : %d\n", elf->segments[i].sectionNum); + printf(" > Size(Memory): 0x%x\n", elf->segments[i].header->sizeInMemory); + printf(" > Size(File): 0x%x\n", elf->segments[i].header->sizeInFile); + printf(" > Address: 0x%x\n", elf->segments[i].vAddr); + printf(" > Flags: 0x%x\n", elf->segments[i].header->flags); + printf(" > Type: 0x%x\n", elf->segments[i].header->type); + printf(" > Sections: %d\n", elf->segments[i].sectionNum); for (int j = 0; j < elf->segments[i].sectionNum; j++) printf(" > Section [%d][%s][0x%x][0x%x]\n", j, elf->segments[i].sections[j].name, elf->segments[i].sections[j].flags, elf->segments[i].sections[j].type); - - /* - char outpath[100]; - memset(&outpath,0,100); - sprintf(outpath,"%s.bin",elf->sections[i].name); - chdir("elfsections"); - FILE *tmp = fopen(outpath,"wb"); - WriteBuffer(elf->sections[i].ptr,elf->sections[i].size,0,tmp); - fclose(tmp); - chdir(".."); - */ } - } +*/ int BuildExeFsCode(ncch_settings *set) { @@ -331,10 +263,11 @@ int BuildExeFsCode(ncch_settings *set) if (set->options.IsCfa) return result; - if (set->componentFilePtrs.plainregion) // Import PlainRegion from file - if ((result = ImportPlainRegionFromFile(set))) return result; - if (!set->options.IsBuildingCodeSection) // Import ExeFs Code from file and return + if (!set->options.IsBuildingCodeSection) { // Import ExeFs Code from file and return + if (set->componentFilePtrs.plainregion) // Import PlainRegion from file + if ((result = ImportPlainRegionFromFile(set))) return result; return ImportExeFsCodeBinaryFromFile(set); + } /* Import ELF */ u8 *elfFile = malloc(set->componentFilePtrs.elfSize); @@ -345,26 +278,12 @@ int BuildExeFsCode(ncch_settings *set) ReadFile64(elfFile, set->componentFilePtrs.elfSize, 0, set->componentFilePtrs.elf); /* Create ELF Context */ - elf_context *elf = calloc(1, sizeof(elf_context)); - if (!elf) { - fprintf(stderr, "[CODE ERROR] Not enough memory\n"); - free(elfFile); - return MEM_ERROR; - } - - if ((result = GetElfContext(elf, elfFile))) goto finish; - - /* Setting Page Size */ - elf->pageSize = GetPageSize(set); - - if (!set->componentFilePtrs.plainregion) - if ((result = ImportPlainRegionFromElf(elf, set))) goto finish; + elf_context elf; - if (set->options.verbose) - PrintElfContext(elf); + if ((result = elf_Init(&elf, elfFile))) goto finish; - if ((result = CreateExeFsCode(elf, set))) goto finish; - if ((result = GetBSSFromElf(elf, set))) goto finish; + if ((result = ImportPlainRegionFromElf(&elf, set))) goto finish; + if ((result = CreateExeFsCode(&elf, set))) goto finish; finish: switch (result) { @@ -389,8 +308,7 @@ int BuildExeFsCode(ncch_settings *set) fprintf(stderr, "[CODE ERROR] Failed to process ELF file (%d)\n", result); } - FreeElfContext(elf); + elf_Free(&elf); free(elfFile); - free(elf); return result; } diff --git a/makerom/elf.c b/makerom/elf.c index 6aad3ef1..a934817f 100644 --- a/makerom/elf.c +++ b/makerom/elf.c @@ -82,43 +82,12 @@ typedef struct elf_phdr } elf_phdr; // ELF Functions -int GetElfSectionEntries(elf_context *elf); -int GetElfProgramEntries(elf_context *elf); -void PrintElfContext(elf_context *elf); -int ReadElfHdr(elf_context *elf); -int CreateElfSegments(elf_context *elf); -bool IsIgnoreSection(elf_section_entry info); - -// ELF Functions - -int GetElfContext(elf_context *elf, const u8 *elfFile) -{ - elf->file = elfFile; - - int result; - - if((result = ReadElfHdr(elf))) return result; - if((result = GetElfSectionEntries(elf))) return result; - if((result = GetElfProgramEntries(elf))) return result; - if((result = CreateElfSegments(elf))) return result; - - return 0; -} - -void FreeElfContext(elf_context *elf) -{ - for (int i = 0; i < elf->activeSegments; i++) - free(elf->segments[i].sections); - free(elf->sections); - free(elf->programHeaders); - free(elf->segments); -} - -int ReadElfHdr(elf_context *elf) +int elf_ProcessHeader(elf_context *elf) { const elf_hdr *hdr = (const elf_hdr*)elf->file; + /* Check conditions for valid CTR ELF */ if (u8_to_u32(hdr->magic, BE) != ELF_MAGIC) return NOT_ELF_FILE; if (hdr->bitFormat != elf_32_bit) @@ -130,185 +99,96 @@ int ReadElfHdr(elf_context *elf) if (u8_to_u16(hdr->type, LE) != elf_executeable) return NON_EXECUTABLE_ELF; - elf->programTableOffset = u8_to_u32(hdr->programHeaderTableOffset, LE); - elf->programTableEntrySize = u8_to_u16(hdr->programHeaderEntrySize, LE); - elf->programTableEntryCount = u8_to_u16(hdr->programHeaderEntryCount, LE); - - elf->sectionTableOffset = u8_to_u32(hdr->sectionHeaderTableOffset, LE); - elf->sectionTableEntrySize = u8_to_u16(hdr->sectionTableEntrySize, LE); - elf->sectionTableEntryCount = u8_to_u16(hdr->sectionHeaderEntryCount, LE); + elf->phdrOffset = u8_to_u32(hdr->programHeaderTableOffset, LE); + elf->segmentNum = u8_to_u16(hdr->programHeaderEntryCount, LE); + elf->segments = calloc(elf->segmentNum, sizeof(elf_segment)); + if (!elf->segments) { + fprintf(stderr, "[ELF ERROR] Not enough memory\n"); + return MEM_ERROR; + } - elf->sectionHeaderNameEntryIndex = u8_to_u16(hdr->sectionHeaderNameEntryIndex, LE); + elf->shdrOffset = u8_to_u32(hdr->sectionHeaderTableOffset, LE); + elf->shdrNameIndex = u8_to_u16(hdr->sectionHeaderNameEntryIndex, LE); + elf->sectionNum = u8_to_u16(hdr->sectionHeaderEntryCount, LE); + elf->sections = calloc(elf->sectionNum, sizeof(elf_section)); + if (!elf->sections) { + fprintf(stderr, "[ELF ERROR] Not enough memory\n"); + return MEM_ERROR; + } return 0; } -int GetElfSectionEntries(elf_context *elf) +void elf_PopulateSections(elf_context *elf) { - elf->sections = calloc(elf->sectionTableEntryCount,sizeof(elf_section_entry)); - if(!elf->sections) { - fprintf(stderr,"[ELF ERROR] Not enough memory\n"); - return MEM_ERROR; - } - - const elf_shdr *shdr = (const elf_shdr *)(elf->file + elf->sectionTableOffset); - const char *nameTable = (const char*)(elf->file + u8_to_u32(shdr[elf->sectionHeaderNameEntryIndex].offset, LE)); + const elf_shdr *shdr = (const elf_shdr *)(elf->file + elf->shdrOffset); + const char *nameTable = (const char*)(elf->file + u8_to_u32(shdr[elf->shdrNameIndex].offset, LE)); - for(int i = 0; i < elf->sectionTableEntryCount; i++){ + for (int i = 0; i < elf->sectionNum; i++) { elf->sections[i].name = nameTable + u8_to_u32(shdr[i].name, LE); elf->sections[i].type = u8_to_u32(shdr[i].type, LE); elf->sections[i].flags = u8_to_u32(shdr[i].flags, LE); - elf->sections[i].offsetInFile = u8_to_u32(shdr[i].offset, LE); + elf->sections[i].fileOffset = u8_to_u32(shdr[i].offset, LE); elf->sections[i].size = u8_to_u32(shdr[i].size, LE); - elf->sections[i].ptr = elf->file + elf->sections[i].offsetInFile; - elf->sections[i].address = u8_to_u32(shdr[i].addr, LE); + elf->sections[i].ptr = elf->file + elf->sections[i].fileOffset; + elf->sections[i].vAddr = u8_to_u32(shdr[i].addr, LE); elf->sections[i].alignment = u8_to_u32(shdr[i].addralign, LE); } - return 0; } -int GetElfProgramEntries(elf_context *elf) +void elf_PopulateSegments(elf_context *elf) { - elf->programHeaders = calloc(elf->programTableEntryCount,sizeof(elf_program_entry)); - if(!elf->programHeaders) { - fprintf(stderr,"[ELF ERROR] Not enough memory\n"); - return MEM_ERROR; - } - - const elf_phdr *phdr = (const elf_phdr*)(elf->file + elf->programTableOffset); + const elf_phdr *phdr = (const elf_phdr *)(elf->file + elf->phdrOffset); - for(int i = 0; i < elf->programTableEntryCount; i++){ - elf->programHeaders[i].type = u8_to_u32(phdr[i].type, LE); - elf->programHeaders[i].flags = u8_to_u32(phdr[i].flags, LE); - elf->programHeaders[i].offsetInFile = u8_to_u32(phdr[i].offset, LE); - elf->programHeaders[i].sizeInFile = u8_to_u32(phdr[i].filesz, LE); - elf->programHeaders[i].ptr = elf->file + elf->programHeaders[i].offsetInFile; - elf->programHeaders[i].physicalAddress = u8_to_u32(phdr[i].paddr, LE); - elf->programHeaders[i].virtualAddress = u8_to_u32(phdr[i].vaddr, LE); - elf->programHeaders[i].sizeInMemory = u8_to_u32(phdr[i].memsz, LE); - elf->programHeaders[i].alignment = u8_to_u32(phdr[i].align, LE); + for (int i = 0; i < elf->segmentNum; i++) { + elf->segments[i].type = u8_to_u32(phdr[i].type, LE); + elf->segments[i].flags = u8_to_u32(phdr[i].flags, LE); + elf->segments[i].fileOffset = u8_to_u32(phdr[i].offset, LE); + elf->segments[i].fileSize = u8_to_u32(phdr[i].filesz, LE); + elf->segments[i].ptr = elf->file + elf->segments[i].fileOffset; + elf->segments[i].pAddr = u8_to_u32(phdr[i].paddr, LE); + elf->segments[i].vAddr = u8_to_u32(phdr[i].vaddr, LE); + elf->segments[i].memSize = u8_to_u32(phdr[i].memsz, LE); + elf->segments[i].alignment = u8_to_u32(phdr[i].align, LE); } - - return 0; } -/* Section Hdr Functions */ - -bool IsBss(elf_section_entry *section) +int elf_Init(elf_context *elf, const u8 *elfFile) { - return (section->type == SHT_NOBITS && section->flags == (SHF_WRITE | SHF_ALLOC)); -} + elf->file = elfFile; + + int result; -bool IsData(elf_section_entry *section) -{ - return (section->type == SHT_PROGBITS && section->flags == (SHF_WRITE | SHF_ALLOC)); -} + if((result = elf_ProcessHeader(elf))) return result; -bool IsRoData(elf_section_entry *section) -{ - return (section->type == SHT_PROGBITS && section->flags == SHF_ALLOC); + elf_PopulateSections(elf); + elf_PopulateSegments(elf); + return 0; } -bool IsText(elf_section_entry *section) +void elf_Free(elf_context *elf) { - return (section->type == SHT_PROGBITS && section->flags == (SHF_ALLOC | SHF_EXECINSTR)); + free(elf->sections); + free(elf->segments); + memset(elf, 0, sizeof(elf_context)); } -/* Program Segment Functions */ -void InitSegment(elf_segment *segment) +u16 elf_SectionNum(elf_context *ctx) { - memset(segment, 0, sizeof(elf_segment)); - - segment->sectionNumMax = 10; - segment->sectionNum = 0; - segment->sections = calloc(segment->sectionNumMax, sizeof(elf_section_entry)); + return ctx->sectionNum; } -void AddSegmentSection(elf_segment *segment, elf_section_entry *section) +const elf_section* elf_GetSections(elf_context *ctx) { - if (segment->sectionNum < segment->sectionNumMax) - memcpy(&segment->sections[segment->sectionNum], section, sizeof(elf_section_entry)); - else { - segment->sectionNumMax *= 2; - elf_section_entry *tmp = calloc(segment->sectionNumMax, sizeof(elf_section_entry)); - for (int k = 0; k < segment->sectionNum; k++) - memcpy(&tmp[k], &segment->sections[k], sizeof(elf_section_entry)); - free(segment->sections); - segment->sections = tmp; - memcpy(&segment->sections[segment->sectionNum], section, sizeof(elf_section_entry)); - } - - segment->sectionNum++; + return ctx->sections; } -bool IsIgnoreSection(elf_section_entry info) +u16 elf_SegmentNum(elf_context *ctx) { - return !(info.flags & SHF_ALLOC);//(info.type != SHT_PROGBITS && info.type != SHT_NOBITS && info.type != SHT_INIT_ARRAY && info.type != SHT_FINI_ARRAY && info.type != SHT_ARM_EXIDX); + return ctx->segmentNum; } -int CreateElfSegments(elf_context *elf) +const elf_segment* elf_GetSegments(elf_context *ctx) { - // Interate through Each Program Header - elf->activeSegments = 0; - elf->segments = calloc(elf->programTableEntryCount,sizeof(elf_segment)); - - elf_segment segment; - - bool foundFirstSection = false; - int curr, prev; - u32 padding, size, sizeInMemory; - - for (int i = 0; i < elf->programTableEntryCount; i++){ - if (elf->programHeaders[i].sizeInMemory != 0 && elf->programHeaders[i].type == PF_X){ - InitSegment(&segment); - - foundFirstSection = false; - size = 0; - sizeInMemory = elf->programHeaders[i].sizeInMemory; - - // Itterate Through Section Headers - for (curr = 0; curr < elf->sectionTableEntryCount && size != sizeInMemory; curr++){ - // Skip irrelevant sections - if (IsIgnoreSection(elf->sections[curr])) - continue; - - - if (!foundFirstSection) { - if (elf->sections[curr].address != elf->programHeaders[i].virtualAddress) - continue; - - foundFirstSection = true; - segment.vAddr = elf->sections[curr].address; - segment.name = elf->sections[curr].name; - - AddSegmentSection(&segment, &elf->sections[curr]); - size = elf->sections[curr].size; - } - else { - AddSegmentSection(&segment, &elf->sections[curr]); - padding = elf->sections[curr].address - (elf->sections[prev].address + elf->sections[prev].size); - size += padding + elf->sections[curr].size; - } - prev = curr; - - // Catch section parsing fails - if (size > sizeInMemory){ - fprintf(stderr,"[ELF ERROR] Too large section size.\n Segment size = 0x%x\n Section Size = 0x%x\n", sizeInMemory, size); - return ELF_SEGMENT_SECTION_SIZE_MISMATCH; - } - } - if(segment.sectionNum){ - segment.header = &elf->programHeaders[i]; - memcpy(&elf->segments[elf->activeSegments],&segment,sizeof(elf_segment)); - elf->activeSegments++; - } - else{ - free(segment.sections); - fprintf(stderr,"[ELF ERROR] Program Header Has no corresponding Sections, ELF Cannot be proccessed\n"); - return ELF_SEGMENTS_NOT_FOUND; - } - } - } - - return 0; -} \ No newline at end of file + return ctx->segments; +} diff --git a/makerom/elf.h b/makerom/elf.h index c568a5b5..71466f63 100644 --- a/makerom/elf.h +++ b/makerom/elf.h @@ -56,17 +56,17 @@ typedef enum elf_section_flag SHF_TLS = 0x400 } elf_section_flag; -typedef struct elf_section_entry +typedef struct elf_section { const char *name; u32 type; u32 flags; const u8 *ptr; - u32 offsetInFile; + u32 fileOffset; u32 size; - u32 address; + u32 vAddr; u32 alignment; -} elf_section_entry; +} elf_section; typedef enum elf_program_type { @@ -91,57 +91,40 @@ typedef enum elf_program_flag PF_RODATA = PF_R } elf_program_flag; -typedef struct elf_program_entry +typedef struct elf_segment { u32 type; u32 flags; const u8 *ptr; - u32 offsetInFile; - u32 sizeInFile; - u32 virtualAddress; - u32 physicalAddress; - u32 sizeInMemory; - u32 alignment; -} elf_program_entry; - -typedef struct elf_segment -{ - const char *name; + u32 fileOffset; + u32 fileSize; + u32 memSize; u32 vAddr; - - elf_program_entry *header; - u32 sectionNum; - u32 sectionNumMax; - elf_section_entry *sections; + u32 pAddr; + u32 alignment; } elf_segment; typedef struct elf_context { const u8 *file; - u32 pageSize; - - u32 programTableOffset; - u16 programTableEntrySize; - u16 programTableEntryCount; - - u32 sectionTableOffset; - u16 sectionTableEntrySize; - u16 sectionTableEntryCount; - - u16 sectionHeaderNameEntryIndex; - - elf_section_entry *sections; - elf_program_entry *programHeaders; - - u16 activeSegments; + u32 shdrOffset; + u16 shdrNameIndex; + u32 phdrOffset; + + u16 sectionNum; + elf_section *sections; + + u16 segmentNum; elf_segment *segments; } elf_context; -bool IsBss(elf_section_entry *section); -bool IsData(elf_section_entry *section); -bool IsRoData(elf_section_entry *section); -bool IsText(elf_section_entry *section); -int GetElfContext(elf_context *elf, const u8 *elfFile); -void FreeElfContext(elf_context *elf); \ No newline at end of file +int elf_Init(elf_context *ctx, const u8 *fp); +void elf_Free(elf_context *ctx); + +u16 elf_SectionNum(elf_context *ctx); +const elf_section* elf_GetSections(elf_context *ctx); + +u16 elf_SegmentNum(elf_context *ctx); +const elf_segment* elf_GetSegments(elf_context *ctx); \ No newline at end of file diff --git a/makerom/rsf_settings.c b/makerom/rsf_settings.c index 66fcc0f2..99c89ef0 100644 --- a/makerom/rsf_settings.c +++ b/makerom/rsf_settings.c @@ -64,7 +64,6 @@ void GET_Option(ctr_yaml_context *ctx, rsf_settings *rsf) else if(cmpYamlValue("EnableCompress",ctx)) SetBoolYAMLValue(&rsf->Option.EnableCompress,"EnableCompress",ctx); else if(cmpYamlValue("FreeProductCode",ctx)) SetBoolYAMLValue(&rsf->Option.FreeProductCode,"FreeProductCode",ctx); else if(cmpYamlValue("UseOnSD",ctx)) SetBoolYAMLValue(&rsf->Option.UseOnSD,"UseOnSD",ctx); - else if(cmpYamlValue("PageSize",ctx)) SetSimpleYAMLValue(&rsf->Option.PageSize,"PageSize",ctx,0); else{ fprintf(stderr,"[RSF ERROR] Unrecognised key '%s'\n",GetYamlString(ctx)); ctx->error = YAML_UNKNOWN_KEY; @@ -343,9 +342,6 @@ void GET_CommonHeaderKey(ctr_yaml_context *ctx, rsf_settings *rsf) void free_RsfSettings(rsf_settings *set) { - //Option - free(set->Option.PageSize); - //AccessControlInfo free(set->AccessControlInfo.IdealProcessor); free(set->AccessControlInfo.Priority); diff --git a/makerom/user_settings.h b/makerom/user_settings.h index f06f95ff..ea0f008d 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -70,9 +70,6 @@ typedef struct bool EnableCompress; bool FreeProductCode; bool UseOnSD; - - // Strings - char *PageSize; } Option; struct{ From 59c5df9d8c5fd833dc87bd10afaaade6d5895855 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 17 Dec 2015 20:36:34 +0800 Subject: [PATCH 140/317] [makerom] misc --- makerom/code.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/makerom/code.c b/makerom/code.c index 22b64bf0..cc79cd6c 100644 --- a/makerom/code.c +++ b/makerom/code.c @@ -123,39 +123,36 @@ int ImportPlainRegionFromElf(elf_context *elf, ncch_settings *set) return 0; } -int CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u64 segment_flags) +void CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u64 segment_flags) { u32 segmentNum = elf_SegmentNum(elf); const elf_segment *segments = elf_GetSegments(elf); + /* Initialise struct data */ + out->address = 0; + out->memSize = 0; + out->pageNum = 0; + out->size = 0; + out->data = NULL; + /* Find segment */ for (u16 i = 0; i < segmentNum; i++) { - /* Skip SDK ELF plain region + /* Skip SDK ELF .module_id segment The last segment should always be data in valid ELFs, unless this is an SDK ELF with .module_id segment */ if (i == segmentNum-1 && segments[i].flags == PF_RODATA) continue; /* Found segment */ - if ((segments[i].flags & ~PF_CTRSDK) == segment_flags) { + if ((segments[i].flags & ~PF_CTRSDK) == segment_flags && segments[i].type == PT_LOAD) { out->address = segments[i].vAddr; out->memSize = segments[i].memSize; out->pageNum = SizeToPage(out->memSize); out->size = segments[i].fileSize; - out->data = malloc(out->size); - if (!out->data) { fprintf(stderr, "[CODE ERROR] Not enough memory\n"); return MEM_ERROR; } - memcpy(out->data, segments[i].ptr, out->size); - return 0; + out->data = segments[i].ptr; + break; } } - - /* Stub struct data */ - out->address = 0; - out->memSize = 0; - out->pageNum = 0; - out->size = 0; - out->data = NULL; - return 0; } int CreateExeFsCode(elf_context *elf, ncch_settings *set) @@ -165,14 +162,15 @@ int CreateExeFsCode(elf_context *elf, ncch_settings *set) code_segment rodata; code_segment rwdata; - if (CreateCodeSegmentFromElf(&text, elf, PF_TEXT) == MEM_ERROR || CreateCodeSegmentFromElf(&rodata, elf, PF_RODATA) == MEM_ERROR || CreateCodeSegmentFromElf(&rwdata, elf, PF_DATA) == MEM_ERROR) - return MEM_ERROR; + CreateCodeSegmentFromElf(&text, elf, PF_TEXT); + CreateCodeSegmentFromElf(&rodata, elf, PF_RODATA); + CreateCodeSegmentFromElf(&rwdata, elf, PF_DATA); /* Checking the existence of essential ELF Segments */ if (!text.size) return NOT_FIND_TEXT_SEGMENT; if (!rwdata.size) return NOT_FIND_DATA_SEGMENT; - /* Calculateing BSS size */ + /* Calculating BSS size */ set->codeDetails.bssSize = rwdata.memSize - rwdata.size; /* Allocating Buffer for ExeFs Code */ @@ -208,17 +206,14 @@ int CreateExeFsCode(elf_context *elf, ncch_settings *set) set->codeDetails.textAddress = text.address; set->codeDetails.textMaxPages = text.pageNum; set->codeDetails.textSize = text.memSize; - if (text.size) free(text.data); set->codeDetails.roAddress = rodata.address; set->codeDetails.roMaxPages = rodata.pageNum; set->codeDetails.roSize = rodata.memSize; - if (rodata.size) free(rodata.data); set->codeDetails.rwAddress = rwdata.address; set->codeDetails.rwMaxPages = rwdata.pageNum; set->codeDetails.rwSize = rwdata.memSize; - if (rwdata.size) free(rwdata.data); if (set->rsfSet->SystemControlInfo.StackSize) set->codeDetails.stackSize = strtoul(set->rsfSet->SystemControlInfo.StackSize, NULL, 0); From 103fc09d72bdd466eeb5ff68259cd7b7e1fe59fa Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 23 Dec 2015 14:49:19 +0800 Subject: [PATCH 141/317] [makerom] Cleaned up "desc" presets --- makerom/accessdesc.c | 312 +++++------------------------- makerom/code.c | 2 +- makerom/desc/desc.h | 33 ++++ makerom/desc/dev_sigdata.h | 257 ++++++------------------- makerom/desc/presets.h | 324 +++++++++++++------------------- makerom/desc/prod_sigdata.h | 134 ------------- makerom/exheader.c | 2 +- makerom/keyset.h | 13 +- makerom/makerom.vcxproj | 2 +- makerom/makerom.vcxproj.filters | 6 +- makerom/user_settings.c | 14 +- 11 files changed, 278 insertions(+), 821 deletions(-) create mode 100644 makerom/desc/desc.h delete mode 100644 makerom/desc/prod_sigdata.h diff --git a/makerom/accessdesc.c b/makerom/accessdesc.c index dde1b58b..1b8a3ef9 100644 --- a/makerom/accessdesc.c +++ b/makerom/accessdesc.c @@ -5,7 +5,6 @@ #include "desc/presets.h" #include "desc/dev_sigdata.h" -#include "desc/prod_sigdata.h" const int RSF_RSA_DATA_LEN = 344; const int RSF_DESC_DATA_LEN = 684; @@ -14,8 +13,9 @@ const int RSF_DESC_DATA_LEN = 684; int accessdesc_SignWithKey(exheader_settings *exhdrset); int accessdesc_GetSignFromRsf(exheader_settings *exhdrset); int accessdesc_GetSignFromPreset(exheader_settings *exhdrset); -void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, keys_struct *keys); -void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, keys_struct *keys); +const CtrSdkDesc* accessdesc_GetPresetData(keys_struct *keys); +const CtrSdkDescSignData* accessdesc_GetPresetSignData(keys_struct *keys); +const CtrSdkDepList* accessdesc_GetPresetDependencyList(keys_struct *keys); int set_AccessDesc(exheader_settings *exhdrset) { @@ -121,48 +121,41 @@ int accessdesc_GetSignFromRsf(exheader_settings *exhdrset) int accessdesc_GetSignFromPreset(exheader_settings *exhdrset) { - u8 *desc = NULL; - u8 *accessDesc = NULL; - u8 *depList = NULL; + const CtrSdkDesc *desc = accessdesc_GetPresetData(exhdrset->keys); + const CtrSdkDescSignData *pre_sign = accessdesc_GetPresetSignData(exhdrset->keys); + const CtrSdkDepList *dependency_list = accessdesc_GetPresetDependencyList(exhdrset->keys); - u8 *accessDescSig = NULL; - u8 *cxiPubk = NULL; - u8 *cxiPvtk = NULL; - - accessdesc_GetPresetData(&desc,&accessDesc,&depList,exhdrset->keys); - accessdesc_GetPresetSigData(&accessDescSig,&cxiPubk,&cxiPvtk,exhdrset->keys); // Error Checking - if(!desc || !depList){ + if(!desc || !dependency_list){ fprintf(stderr,"[ACEXDESC ERROR] AccessDesc template is unavailable, please configure RSF file\n"); return CANNOT_SIGN_ACCESSDESC; } - if((!cxiPubk || !cxiPvtk || !accessDesc || !accessDescSig) && exhdrset->keys->rsa.requiresPresignedDesc){ + if(!pre_sign->modulus && exhdrset->keys->rsa.requiresPresignedDesc){ fprintf(stderr,"[ACEXDESC ERROR] This AccessDesc template needs to be signed, the current keyset is incapable of doing so. Please configure RSF file with the appropriate signature data.\n"); return CANNOT_SIGN_ACCESSDESC; } - // Setting data in Exheader // Dependency List - memcpy(exhdrset->exHdr->dependencyList,depList,0x180); + memcpy(exhdrset->exHdr->dependencyList, dependency_list->dependency, 0x180); // Backing Up Non Preset Data u8 ProgramID[8]; exhdr_StorageInfo StorageInfoBackup; exhdr_ARM9AccessControlInfo Arm9Desc; - memcpy(ProgramID,exhdrset->exHdr->arm11SystemLocalCapabilities.programId,8); - memcpy(&StorageInfoBackup,&exhdrset->exHdr->arm11SystemLocalCapabilities.storageInfo,sizeof(exhdr_StorageInfo)); - memcpy(&Arm9Desc,&exhdrset->exHdr->arm9AccessControlInfo,sizeof(exhdr_ARM9AccessControlInfo)); + memcpy(ProgramID, exhdrset->exHdr->arm11SystemLocalCapabilities.programId, 8); + memcpy(&StorageInfoBackup, &exhdrset->exHdr->arm11SystemLocalCapabilities.storageInfo, sizeof(exhdr_StorageInfo)); + memcpy(&Arm9Desc, &exhdrset->exHdr->arm9AccessControlInfo, sizeof(exhdr_ARM9AccessControlInfo)); // Setting Preset Data - memcpy(&exhdrset->exHdr->arm11SystemLocalCapabilities,desc,0x200); + memcpy(&exhdrset->exHdr->arm11SystemLocalCapabilities, desc->exheader_desc, 0x200); // Restoring Non Preset Data - memcpy(exhdrset->exHdr->arm11SystemLocalCapabilities.programId,ProgramID,8); - memcpy(&exhdrset->exHdr->arm11SystemLocalCapabilities.storageInfo,&StorageInfoBackup,sizeof(exhdr_StorageInfo)); - memcpy(&exhdrset->exHdr->arm9AccessControlInfo,&Arm9Desc,sizeof(exhdr_ARM9AccessControlInfo)); + memcpy(exhdrset->exHdr->arm11SystemLocalCapabilities.programId, ProgramID, 8); + memcpy(&exhdrset->exHdr->arm11SystemLocalCapabilities.storageInfo, &StorageInfoBackup, sizeof(exhdr_StorageInfo)); + memcpy(&exhdrset->exHdr->arm9AccessControlInfo, &Arm9Desc, sizeof(exhdr_ARM9AccessControlInfo)); // Setting AccessDesc Area @@ -171,265 +164,46 @@ int accessdesc_GetSignFromPreset(exheader_settings *exhdrset) return accessdesc_SignWithKey(exhdrset); // Otherwise set static data & ncch hdr sig info - memcpy(exhdrset->keys->rsa.cxiHdrPub,cxiPubk,0x100); - memcpy(exhdrset->keys->rsa.cxiHdrPvt,cxiPvtk,0x100); - memcpy(&exhdrset->acexDesc->signature,accessDescSig,0x100); - memcpy(&exhdrset->acexDesc->ncchRsaPubKey,cxiPubk,0x100); - memcpy(&exhdrset->acexDesc->arm11SystemLocalCapabilities,accessDesc,0x200); + memcpy(exhdrset->keys->rsa.cxiHdrPub, pre_sign->modulus, 0x100); + memcpy(exhdrset->keys->rsa.cxiHdrPvt, pre_sign->priv_exponent, 0x100); + memcpy(&exhdrset->acexDesc->signature, pre_sign->access_desc_signature, 0x100); + memcpy(&exhdrset->acexDesc->ncchRsaPubKey, pre_sign->modulus, 0x100); + memcpy(&exhdrset->acexDesc->arm11SystemLocalCapabilities, desc->signed_desc, 0x200); return 0; } -void accessdesc_GetPresetData(u8 **desc, u8 **accessDesc, u8 **depList, keys_struct *keys) +const CtrSdkDesc* accessdesc_GetPresetData(keys_struct *keys) { - if(keys->accessDescSign.presetType == desc_preset_APP){ - switch(keys->accessDescSign.targetFirmware){ - case 0x1B: - case 0x1C: - *desc = (u8*)app_fw1B_desc_data; - *accessDesc = (u8*)app_fw1B_acex_data; - *depList = (u8*)fw1B_dep_list; - break; - case 0x1D: - *desc = (u8*)app_fw1D_desc_data; - *accessDesc = (u8*)app_fw1D_acex_data; - *depList = (u8*)fw1D_dep_list; - break; - case 0x1E: - *desc = (u8*)app_fw1E_desc_data; - *accessDesc = (u8*)app_fw1E_acex_data; - *depList = (u8*)fw1D_dep_list; - break; - case 0x20: - *desc = (u8*)app_fw20_desc_data; - *accessDesc = (u8*)app_fw20_acex_data; - *depList = (u8*)fw20_dep_list; - break; - case 0x21: - *desc = (u8*)app_fw21_desc_data; - *accessDesc = (u8*)app_fw21_acex_data; - *depList = (u8*)fw21_dep_list; - break; - case 0x23: - *desc = (u8*)app_fw23_desc_data; - *accessDesc = (u8*)app_fw23_acex_data; - *depList = (u8*)fw23_dep_list; - break; - case 0x27: - *desc = (u8*)app_fw27_desc_data; - *accessDesc = (u8*)app_fw27_acex_data; - *depList = (u8*)fw27_dep_list; - break; - - } - } - else if(keys->accessDescSign.presetType == desc_preset_EC_APP){ - switch(keys->accessDescSign.targetFirmware){ - case 0x20: - *desc = (u8*)ecapp_fw20_desc_data; - *accessDesc = (u8*)ecapp_fw20_acex_data; - *depList = (u8*)fw20_dep_list; - break; - case 0x23: - *desc = (u8*)ecapp_fw23_desc_data; - *accessDesc = (u8*)ecapp_fw23_acex_data; - *depList = (u8*)fw23_dep_list; - break; - } - } - else if(keys->accessDescSign.presetType == desc_preset_DLP){ - switch(keys->accessDescSign.targetFirmware){ - case 0x1B: - case 0x1C: - *desc = (u8*)dlp_fw1B_desc_data; - *accessDesc = (u8*)dlp_fw1B_acex_data; - *depList = (u8*)fw1B_dep_list; - break; - case 0x1D: - *desc = (u8*)dlp_fw1D_desc_data; - *accessDesc = (u8*)dlp_fw1D_acex_data; - *depList = (u8*)fw1D_dep_list; - break; - case 0x21: - *desc = (u8*)dlp_fw21_desc_data; - *accessDesc = (u8*)dlp_fw21_acex_data; - *depList = (u8*)fw21_dep_list; - break; - } - } - else if(keys->accessDescSign.presetType == desc_preset_DEMO){ - switch(keys->accessDescSign.targetFirmware){ - case 0x1E: - *desc = (u8*)demo_fw1E_desc_data; - *accessDesc = (u8*)demo_fw1E_acex_data; - *depList = (u8*)fw1D_dep_list; - break; - case 0x21: - *desc = (u8*)demo_fw21_desc_data; - *accessDesc = (u8*)demo_fw21_acex_data; - *depList = (u8*)fw21_dep_list; - break; - } - } - else if(keys->accessDescSign.presetType == desc_preset_FIRM){ - switch(keys->accessDescSign.targetFirmware){ - default: - *desc = (u8*)firm_fw26_desc_data; - *accessDesc = (u8*)firm_fw26_acex_data; - *depList = (u8*)firm_fwXX_dep_list; - break; + for (int i = 0; i < sizeof(kDescPresets) / sizeof(CtrSdkDesc); i++) { + if (kDescPresets[i].type == keys->accessDescSign.presetType && kDescPresets[i].fw_minor == keys->accessDescSign.targetFirmware) { + return &kDescPresets[i]; } } + return NULL; } -void accessdesc_GetPresetSigData(u8 **accessDescSig, u8 **cxiPubk, u8 **cxiPvtk, keys_struct *keys) +const CtrSdkDescSignData* accessdesc_GetPresetSignData(keys_struct *keys) { - if(keys->accessDescSign.presetType == desc_preset_APP){ - switch(keys->accessDescSign.targetFirmware){ - case 0x1B: - case 0x1C: - if(keys->keyset == pki_DEVELOPMENT){ - *accessDescSig = (u8*)app_fw1B_dev_acexsig; - *cxiPubk = (u8*)app_fw1B_dev_hdrpub; - *cxiPvtk = (u8*)app_fw1B_dev_hdrpvt; - } - break; - case 0x1D: - if(keys->keyset == pki_DEVELOPMENT){ - *accessDescSig = (u8*)app_fw1D_dev_acexsig; - *cxiPubk = (u8*)app_fw1D_dev_hdrpub; - *cxiPvtk = (u8*)app_fw1D_dev_hdrpvt; - } - if(keys->keyset == pki_PRODUCTION){ - *accessDescSig = (u8*)app_fw1D_prod_acexsig; - *cxiPubk = (u8*)app_fw1D_prod_hdrpub; - *cxiPvtk = NULL; - } - break; - case 0x1E: - if(keys->keyset == pki_PRODUCTION){ - *accessDescSig = (u8*)app_fw1E_prod_acexsig; - *cxiPubk = (u8*)app_fw1E_prod_hdrpub; - *cxiPvtk = NULL; - } - break; - case 0x20: - if(keys->keyset == pki_DEVELOPMENT){ - *accessDescSig = (u8*)app_fw20_dev_acexsig; - *cxiPubk = (u8*)app_fw20_dev_hdrpub; - *cxiPvtk = NULL; - } - if(keys->keyset == pki_PRODUCTION){ - *accessDescSig = (u8*)app_fw20_prod_acexsig; - *cxiPubk = (u8*)app_fw20_prod_hdrpub; - *cxiPvtk = NULL; - } - break; - case 0x21: - if(keys->keyset == pki_DEVELOPMENT){ - *accessDescSig = (u8*)app_fw21_dev_acexsig; - *cxiPubk = (u8*)app_fw21_dev_hdrpub; - *cxiPvtk = (u8*)app_fw21_dev_hdrpvt; - } - else if(keys->keyset == pki_PRODUCTION){ - *accessDescSig = (u8*)app_fw21_prod_acexsig; - *cxiPubk = (u8*)app_fw21_prod_hdrpub; - *cxiPvtk = NULL; - } - break; - case 0x23: - if(keys->keyset == pki_DEVELOPMENT){ - *accessDescSig = (u8*)app_fw23_dev_acexsig; - *cxiPubk = (u8*)app_fw23_dev_hdrpub; - *cxiPvtk = NULL; - } - else if(keys->keyset == pki_PRODUCTION){ - *accessDescSig = (u8*)app_fw23_prod_acexsig; - *cxiPubk = (u8*)app_fw23_prod_hdrpub; - *cxiPvtk = NULL; - } - break; - case 0x27: - if(keys->keyset == pki_PRODUCTION){ - *accessDescSig = (u8*)app_fw27_prod_acexsig; - *cxiPubk = (u8*)app_fw27_prod_hdrpub; - *cxiPvtk = NULL; - } - break; - - } - } - else if(keys->accessDescSign.presetType == desc_preset_EC_APP){ - switch(keys->accessDescSign.targetFirmware){ - case 0x20: - if(keys->keyset == pki_PRODUCTION){ - *accessDescSig = (u8*)ecapp_fw20_prod_acexsig; - *cxiPubk = (u8*)ecapp_fw20_prod_hdrpub; - *cxiPvtk = NULL; - } - break; - case 0x23: - if(keys->keyset == pki_PRODUCTION){ - *accessDescSig = (u8*)ecapp_fw23_prod_acexsig; - *cxiPubk = (u8*)ecapp_fw23_prod_hdrpub; - *cxiPvtk = NULL; - } - break; - } - } - else if(keys->accessDescSign.presetType == desc_preset_DLP){ - switch(keys->accessDescSign.targetFirmware){ - case 0x1B: - case 0x1C: - if(keys->keyset == pki_DEVELOPMENT){ - *accessDescSig = (u8*)dlp_fw1B_dev_acexsig; - *cxiPubk = (u8*)dlp_fw1B_dev_hdrpub; - *cxiPvtk = (u8*)dlp_fw1B_dev_hdrpvt; - } - break; - case 0x1D: - if(keys->keyset == pki_DEVELOPMENT){ - *accessDescSig = (u8*)dlp_fw1D_dev_acexsig; - *cxiPubk = (u8*)dlp_fw1D_dev_hdrpub; - *cxiPvtk = (u8*)dlp_fw1D_dev_hdrpvt; - } - break; - case 0x21: - if(keys->keyset == pki_DEVELOPMENT){ - *accessDescSig = (u8*)dlp_fw21_dev_acexsig; - *cxiPubk = (u8*)dlp_fw21_dev_hdrpub; - *cxiPvtk = (u8*)dlp_fw21_dev_hdrpvt; - } - break; - } + if (keys->keyset != pki_DEVELOPMENT) { + return NULL; } - else if(keys->accessDescSign.presetType == desc_preset_DEMO){ - switch(keys->accessDescSign.targetFirmware){ - case 0x1E: - if(keys->keyset == pki_DEVELOPMENT){ - *accessDescSig = (u8*)demo_fw1E_dev_acexsig; - *cxiPubk = (u8*)demo_fw1E_dev_hdrpub; - *cxiPvtk = NULL; - } - break; - case 0x21: - if(keys->keyset == pki_DEVELOPMENT){ - *accessDescSig = (u8*)demo_fw21_dev_acexsig; - *cxiPubk = (u8*)demo_fw21_dev_hdrpub; - *cxiPvtk = (u8*)demo_fw21_dev_hdrpvt; - } - break; + + for (int i = 0; i < sizeof(kDevDescSignData) / sizeof(CtrSdkDescSignData); i++) { + if (kDevDescSignData[i].type == keys->accessDescSign.presetType && kDevDescSignData[i].fw_minor == keys->accessDescSign.targetFirmware) { + return &kDevDescSignData[i]; } } - else if(keys->accessDescSign.presetType == desc_preset_FIRM){ - switch(keys->accessDescSign.targetFirmware){ - case 0x26: - if(keys->keyset == pki_DEVELOPMENT){ - *accessDescSig = (u8*)firm_fw26_dev_acexsig; - *cxiPubk = (u8*)firm_fw26_dev_hdrpub; - *cxiPvtk = NULL; - } - break; + + return NULL; +} + +const CtrSdkDepList* accessdesc_GetPresetDependencyList(keys_struct *keys) +{ + for (int i = 0; i < sizeof(kExheaderDependencyLists) / sizeof(CtrSdkDepList); i++) { + if (kExheaderDependencyLists[i].fw_minor == keys->accessDescSign.targetFirmware) { + return &kExheaderDependencyLists[i]; } } + return NULL; } \ No newline at end of file diff --git a/makerom/code.c b/makerom/code.c index cc79cd6c..483dfa41 100644 --- a/makerom/code.c +++ b/makerom/code.c @@ -15,7 +15,7 @@ typedef struct code_segment u32 pageNum; u32 size; - u8 *data; + const u8 *data; } code_segment; u32 SizeToPage(u32 memorySize) diff --git a/makerom/desc/desc.h b/makerom/desc/desc.h new file mode 100644 index 00000000..441d1a8b --- /dev/null +++ b/makerom/desc/desc.h @@ -0,0 +1,33 @@ +#pragma once +#include + +enum SdkAppTypes { + desc_NotSpecified, + desc_Application, + desc_DlpChild, + desc_Demo, + desc_EcApplication, // intergrated in (Ext)Application since SDK 7 + desc_ExtApplication, // Snake equivalent of desc_Application (128MB/804MHz/L2 Cache) + desc_ExtDlpChild, // Snake equivalent of desc_DlpChild (128MB/804MHz/L2 Cache) + desc_ExtDemo // Snake equivalent of desc_Demo (128MB/804MHz/L2 Cache) +}; + +typedef struct CtrSdkDepList { + uint32_t fw_minor; + uint8_t dependency[0x180]; +} CtrSdkDepList; + +typedef struct CtrSdkDesc { + uint32_t type; + uint32_t fw_minor; + uint8_t exheader_desc[0x200]; + uint8_t signed_desc[0x200]; +} CtrSdkDesc; + +typedef struct CtrSdkDescSignData { + uint32_t type; + uint32_t fw_minor; + uint8_t modulus[0x100]; + uint8_t priv_exponent[0x100]; + uint8_t access_desc_signature[0x100]; +} CtrSdkDescSignData; \ No newline at end of file diff --git a/makerom/desc/dev_sigdata.h b/makerom/desc/dev_sigdata.h index a8728e82..267a6f0f 100644 --- a/makerom/desc/dev_sigdata.h +++ b/makerom/desc/dev_sigdata.h @@ -1,203 +1,58 @@ #pragma once - -/* CTR_SDK 1 (1.2.0) */ -// APP -static const unsigned char app_fw1B_dev_hdrpub[0x100] = -{ - 0x9B, 0x82, 0x9C, 0x19, 0x01, 0x73, 0x23, 0x50, 0x89, 0xE3, 0x0B, 0xC8, 0x8F, 0xFB, 0xA5, 0xE4, 0xFD, 0x44, 0xAE, 0x49, 0x32, 0xC7, 0xEF, 0x0A, 0x7E, 0x93, 0x95, 0xBA, 0xA2, 0x48, 0x4D, 0x8E, 0x18, 0x82, 0x34, 0x66, 0xFE, 0xDA, 0x7A, 0x45, 0x4A, 0x7D, 0xD5, 0x3C, 0xC6, 0x5C, 0xE0, 0x71, 0xFC, 0xD0, 0x82, 0xCC, 0xB2, 0xCF, 0x77, 0x0E, 0xAD, 0xCD, 0xD2, 0xAC, 0x1D, 0x66, 0x1B, 0xC1, 0xEA, 0xAC, 0x39, 0x96, 0x2C, 0x52, 0xDE, 0xFF, 0xF2, 0x74, 0xF6, 0xC5, 0xCA, 0x99, 0x31, 0x95, 0x13, 0xBB, 0x3F, 0xED, 0x30, 0xAE, 0x0A, 0xD3, 0x86, 0x0D, 0x0B, 0xF5, 0x56, 0x7D, 0x33, 0xBA, 0xA1, 0x39, 0xA2, 0xF5, 0xB6, 0x47, 0x78, 0x1F, 0xFD, 0x04, 0xA3, 0x49, 0xA9, 0x10, 0xD3, 0x41, 0x2E, 0x92, 0x7D, 0xE1, 0xA4, 0xA8, 0x02, 0x18, 0x01, 0x2C, 0xC2, 0x61, 0xB1, 0xAC, 0xCB, 0xB3, 0x7A, 0x64, 0xB1, 0xC3, 0xE6, 0x3B, 0x50, 0xAD, 0xDD, 0x55, 0xAB, 0x28, 0xDA, 0x59, 0xE7, 0x57, 0x6A, 0x76, 0x4F, 0x6B, 0x08, 0xD1, 0x61, 0x7D, 0x28, 0xD5, 0x88, 0x7B, 0x8E, 0x80, 0x78, 0xF3, 0xFF, 0x50, 0x00, 0xBD, 0x73, 0xFB, 0x62, 0xB3, 0xCA, 0xA8, 0x05, 0x48, 0xE6, 0xE6, 0x71, 0xB5, 0xAC, 0xDA, 0xE9, 0xC6, 0x1F, 0x9B, 0x72, 0x98, 0x2E, 0xFF, 0xB2, 0x9C, 0x36, 0x9F, 0x7E, 0x7D, 0x25, 0x65, 0x6F, 0xB0, 0x60, 0xB1, 0xB5, 0x5F, 0xCF, 0x74, 0x29, 0x91, 0x9D, 0xAB, 0x84, 0x97, 0x1A, 0x27, 0x5D, 0x69, 0x49, 0x16, 0xF8, 0x77, 0x31, 0x26, 0x3E, 0x6F, 0x97, 0x41, 0x4A, 0x26, 0xFD, 0x5B, 0xAB, 0x65, 0x77, 0x45, 0x1C, 0x76, 0x15, 0xDC, 0x1A, 0x63, 0x0F, 0x51, 0x2D, 0xA0, 0x07, 0x6E, 0xE5, 0x29, 0xD3, 0x37, 0x4B, 0xE5, 0x7F, 0xBB, 0x23, 0xD0, 0x2B, 0xB9, 0x76, 0x1F -}; - -static const unsigned char app_fw1B_dev_hdrpvt[0x100] = -{ - 0x66, 0x9F, 0xB5, 0xC5, 0xA6, 0xB8, 0x45, 0xD8, 0xD3, 0x75, 0xFB, 0x03, 0xBB, 0x48, 0xF5, 0x7C, 0x7D, 0x4B, 0x02, 0xBD, 0x19, 0x7E, 0xE9, 0x98, 0x02, 0x5A, 0x00, 0xD8, 0x6E, 0x59, 0xCA, 0x9C, 0x78, 0x3E, 0x0C, 0xB8, 0xDF, 0x7C, 0x6C, 0x6E, 0x27, 0xAF, 0x8C, 0xB6, 0x13, 0xAD, 0x9D, 0x0C, 0x7C, 0x2B, 0x59, 0xF6, 0x1E, 0x16, 0x5D, 0x5A, 0x59, 0x86, 0x57, 0x7D, 0xEF, 0xD4, 0xBF, 0x82, 0xA4, 0x0C, 0x4D, 0xE0, 0x75, 0x95, 0xA6, 0xC6, 0x3F, 0x49, 0xC2, 0xC4, 0x5A, 0x63, 0xE8, 0x5D, 0x99, 0xEC, 0xDB, 0x4D, 0xFA, 0xEF, 0x10, 0x03, 0xF1, 0x15, 0xD1, 0x0B, 0x71, 0xAD, 0x24, 0x23, 0x08, 0x5C, 0x91, 0xD7, 0x17, 0x18, 0x69, 0x04, 0xAB, 0x23, 0x91, 0x62, 0x7D, 0xE8, 0xB5, 0x90, 0xF1, 0x5C, 0x09, 0x28, 0x8C, 0x51, 0xB7, 0x38, 0x02, 0x26, 0x78, 0x8C, 0xA2, 0x05, 0x07, 0x53, 0x7D, 0x99, 0x46, 0xFD, 0x12, 0x77, 0x32, 0x0E, 0xA8, 0x54, 0xE3, 0x32, 0x0E, 0x93, 0x05, 0x10, 0xFA, 0x59, 0x7A, 0x5D, 0x2E, 0xDE, 0x32, 0xE8, 0xE9, 0xF0, 0x27, 0x4E, 0x08, 0x83, 0x08, 0xD4, 0x92, 0x58, 0x4D, 0x6D, 0x34, 0x9F, 0xD9, 0xAF, 0xA9, 0x01, 0x62, 0xB5, 0x1A, 0x1D, 0x7E, 0x8D, 0xBB, 0x8A, 0x58, 0xBA, 0xFF, 0xB4, 0xD3, 0x75, 0xF3, 0x44, 0xE7, 0x13, 0x05, 0xC6, 0xC5, 0xA4, 0xD2, 0x6B, 0x18, 0x31, 0x9F, 0xBE, 0x42, 0x45, 0x82, 0x2E, 0x47, 0x9B, 0x26, 0x73, 0x28, 0xD7, 0x9A, 0x64, 0xA6, 0x3D, 0x38, 0x9C, 0x11, 0x36, 0x01, 0x5B, 0x82, 0x2F, 0x7E, 0xA8, 0xC4, 0x82, 0x15, 0x6C, 0x0B, 0xBA, 0x1C, 0x58, 0xF5, 0x81, 0xF9, 0x45, 0xA9, 0xC6, 0x05, 0x6A, 0x2C, 0xA6, 0xCF, 0xF5, 0xDF, 0xEB, 0xBB, 0xC0, 0x92, 0xE3, 0xA6, 0x9D, 0x23, 0x09, 0x09, 0x0E, 0x98, 0x39 -}; - -static const unsigned char app_fw1B_dev_acexsig[0x100] = -{ - 0x05, 0x90, 0xAF, 0x65, 0x16, 0x9F, 0x18, 0x2C, 0x17, 0x78, 0x9F, 0xDF, 0xB6, 0x37, 0xCF, 0x26, 0x9B, 0x1B, 0x75, 0x51, 0xB8, 0x57, 0xA3, 0x8F, 0xD7, 0x93, 0x19, 0x61, 0x81, 0x0D, 0x3D, 0xBC, 0x36, 0x50, 0x53, 0xDA, 0x7D, 0xA9, 0x7F, 0xAA, 0x3E, 0x51, 0x2C, 0x75, 0xA1, 0xB9, 0xB1, 0x56, 0xEB, 0x2A, 0x46, 0x21, 0xEC, 0x4F, 0xA7, 0x0C, 0xA1, 0xA8, 0xFD, 0xEE, 0xA3, 0x4A, 0xFD, 0x54, 0xB0, 0x3A, 0x49, 0x5C, 0x8F, 0x8D, 0xB2, 0xBC, 0x32, 0x50, 0x7E, 0x2C, 0x50, 0xD2, 0x1A, 0x6B, 0x61, 0xCB, 0x2A, 0xC9, 0x7E, 0x6E, 0x6A, 0xC8, 0xD6, 0x9B, 0x21, 0xE7, 0x3B, 0xB8, 0x39, 0x1C, 0xD7, 0xEB, 0x69, 0x35, 0xF5, 0xBC, 0xB5, 0x23, 0x54, 0x81, 0x4F, 0x73, 0xAB, 0x9C, 0x55, 0xF0, 0x04, 0x0B, 0x4A, 0xEA, 0x54, 0x08, 0xBF, 0x36, 0x28, 0x12, 0x5E, 0x44, 0x41, 0xF5, 0x3D, 0xFE, 0xA7, 0x6B, 0x35, 0xF2, 0x9A, 0xF2, 0x88, 0xCD, 0xD6, 0x2E, 0x7B, 0xF3, 0xF5, 0x0D, 0x06, 0x2E, 0x13, 0x4F, 0x78, 0xEE, 0x27, 0xAB, 0x31, 0x06, 0x62, 0xE9, 0xB2, 0x3E, 0xC6, 0x99, 0xD7, 0xA9, 0xCC, 0x21, 0x70, 0xD7, 0xCD, 0x9F, 0x03, 0x66, 0x91, 0x7E, 0xBD, 0x3E, 0x83, 0xC4, 0xFF, 0xC9, 0xAA, 0x7E, 0x27, 0xAE, 0x5C, 0x37, 0x7D, 0x93, 0x57, 0x60, 0xB9, 0x0B, 0x71, 0x43, 0x8A, 0x2F, 0x43, 0x50, 0x94, 0xF8, 0x0D, 0x40, 0xCC, 0x64, 0x16, 0x09, 0x70, 0xCD, 0x03, 0xCA, 0x95, 0x30, 0x20, 0xE2, 0x85, 0x2F, 0x2A, 0xCF, 0x65, 0xAA, 0xE9, 0xCF, 0x1F, 0x57, 0xC1, 0x8F, 0xD5, 0x46, 0x51, 0x5A, 0x99, 0x60, 0x65, 0x93, 0xA9, 0xBB, 0xEA, 0x5F, 0xA0, 0x47, 0xD7, 0x11, 0x04, 0xC7, 0xB4, 0x82, 0x66, 0x24, 0x17, 0x17, 0x5E, 0x9D, 0xC8, 0x50, 0x80, 0x63, 0x28, 0xB3, 0xF8, 0xE3 -}; - -// DLP -static const unsigned char dlp_fw1B_dev_hdrpub[0x100] = -{ - 0xD9, 0xF0, 0xC9, 0x35, 0x1E, 0x55, 0xD8, 0x7E, 0x96, 0x53, 0x33, 0x34, 0xBB, 0x8A, 0xAE, 0x03, 0x92, 0x35, 0xE2, 0x05, 0x58, 0x7C, 0xCC, 0x08, 0xB2, 0xDF, 0x43, 0x41, 0xB7, 0x7A, 0xB5, 0x29, 0xEE, 0x4E, 0xF3, 0xC7, 0x35, 0x3B, 0x7E, 0xC5, 0xEE, 0x74, 0xF0, 0xAA, 0x7E, 0x60, 0xF1, 0x28, 0x35, 0x17, 0xD6, 0xC9, 0x9A, 0xF2, 0x84, 0xFE, 0xC8, 0x93, 0x86, 0xF7, 0xA7, 0x36, 0xA6, 0xB0, 0x28, 0xDC, 0xE8, 0x38, 0x0B, 0x54, 0x42, 0x8D, 0x4E, 0x4B, 0x55, 0x0F, 0x4A, 0x0D, 0x72, 0xA0, 0x23, 0xC9, 0x68, 0x22, 0x37, 0x31, 0x88, 0x2C, 0x05, 0x49, 0x86, 0x80, 0x9A, 0xFC, 0x1D, 0x02, 0xE3, 0x20, 0x15, 0x0C, 0x7E, 0x28, 0x40, 0x57, 0xEF, 0xA7, 0xBC, 0xAA, 0xC5, 0xD6, 0xD7, 0x6F, 0xF9, 0x26, 0x9A, 0x32, 0xB2, 0x9E, 0x10, 0x5F, 0x93, 0xE6, 0xB2, 0xC6, 0xB2, 0x62, 0x34, 0x6A, 0xB0, 0xD9, 0x71, 0x3B, 0x0F, 0x34, 0x6C, 0xB1, 0xFE, 0x3A, 0x39, 0xDE, 0x3D, 0x6A, 0xCB, 0x32, 0x95, 0xFA, 0x18, 0x4F, 0xF4, 0xEB, 0x5F, 0x20, 0xE4, 0xEF, 0x64, 0xC5, 0x06, 0x27, 0xC3, 0x44, 0x2A, 0x39, 0x35, 0xD8, 0x00, 0xDF, 0x00, 0xAD, 0xC4, 0x98, 0x06, 0x52, 0xD8, 0x4A, 0xC5, 0x2A, 0x7F, 0x77, 0x50, 0x62, 0x7E, 0x05, 0x3E, 0x8C, 0x28, 0x0A, 0x26, 0xD2, 0x6C, 0x9B, 0x27, 0x65, 0xE9, 0x77, 0x68, 0xE9, 0xE6, 0xAA, 0xBA, 0xF5, 0x85, 0xFC, 0x75, 0x07, 0x84, 0xB2, 0xCA, 0x35, 0x85, 0x52, 0x10, 0x08, 0xEF, 0x85, 0xD3, 0x70, 0x17, 0x31, 0xE1, 0x44, 0xF6, 0x34, 0xDF, 0x7C, 0x42, 0xF7, 0x74, 0xAA, 0xFC, 0xC3, 0xE4, 0x84, 0x2D, 0xBF, 0x15, 0x1E, 0x84, 0x00, 0xE3, 0x80, 0xD7, 0x89, 0x56, 0xEE, 0x60, 0x09, 0x1F, 0xD3, 0xBF, 0xBF, 0x50, 0x8F, 0xA3, 0x0C, 0x72, 0x3F -}; - -static const unsigned char dlp_fw1B_dev_hdrpvt[0x100] = -{ - 0x10, 0x19, 0x3A, 0x33, 0xA3, 0x47, 0x02, 0x13, 0xEF, 0xB4, 0xBB, 0x9E, 0x94, 0x8F, 0xDC, 0xE4, 0xC4, 0xA3, 0x18, 0x4B, 0xFE, 0xCA, 0x51, 0x23, 0xFF, 0x5A, 0x80, 0x94, 0x55, 0x22, 0x4A, 0x49, 0x8B, 0xA1, 0xE7, 0x5D, 0xFA, 0xAF, 0xA7, 0x60, 0xA5, 0x89, 0x9B, 0xD1, 0x6C, 0x3E, 0x6A, 0xF1, 0xE6, 0x62, 0x19, 0x6A, 0x90, 0xF8, 0x83, 0x1C, 0x72, 0xE2, 0x7A, 0xE0, 0xC6, 0x48, 0x42, 0x2D, 0xD7, 0x06, 0xE2, 0x5C, 0x69, 0x71, 0xD2, 0xEC, 0xAF, 0x30, 0xDF, 0x5A, 0x9E, 0xC4, 0xB9, 0x87, 0xDC, 0xBC, 0xDE, 0xE5, 0x50, 0x20, 0x67, 0x87, 0xA0, 0xE8, 0x5A, 0x78, 0x1B, 0x7A, 0xAE, 0x05, 0xED, 0x93, 0x0C, 0x1A, 0xFD, 0x22, 0xAA, 0x06, 0x14, 0xDC, 0xD6, 0x11, 0xE3, 0x45, 0x48, 0x6A, 0xAC, 0x03, 0xCE, 0xF6, 0x19, 0xBD, 0x95, 0x46, 0x0A, 0x1D, 0xCB, 0x6C, 0xE3, 0xF6, 0x5F, 0x1A, 0xB3, 0x81, 0xC7, 0xE2, 0xAB, 0xFE, 0xEF, 0xFB, 0xEC, 0xFE, 0x88, 0x36, 0x26, 0x60, 0x47, 0x43, 0x78, 0x36, 0xA7, 0xC8, 0xC9, 0x40, 0x98, 0x2E, 0xF2, 0x7E, 0xE4, 0x0D, 0x6C, 0x45, 0x88, 0x2A, 0x32, 0x9B, 0xA2, 0x7C, 0x39, 0x20, 0xAA, 0x6B, 0x64, 0x35, 0xC6, 0xA9, 0x20, 0x71, 0x4A, 0x78, 0x6E, 0x55, 0x3C, 0x9B, 0xEA, 0x10, 0x73, 0xBB, 0xA7, 0xD8, 0xFE, 0x69, 0x42, 0xB8, 0xE7, 0xA1, 0xE5, 0xDF, 0x8A, 0xDE, 0x4C, 0x2B, 0x3A, 0x92, 0xB8, 0x3E, 0x5E, 0x2C, 0x29, 0x0D, 0xC1, 0x3D, 0x10, 0x65, 0x1E, 0xF1, 0x95, 0xE5, 0xF6, 0x45, 0x15, 0xBF, 0xE2, 0x30, 0xA6, 0x70, 0x19, 0xA4, 0x11, 0x57, 0x12, 0x1C, 0x81, 0x4B, 0x54, 0x04, 0xBE, 0x67, 0xF5, 0x00, 0x22, 0x06, 0xDA, 0x6B, 0xCE, 0x23, 0x3F, 0x86, 0xE4, 0x70, 0x6A, 0xD1, 0x3E, 0xE5, 0x74, 0x44, 0x86, 0xDA, 0xBE, 0x79 -}; - -static const unsigned char dlp_fw1B_dev_acexsig[0x100] = -{ - 0x13, 0xB2, 0xF4, 0x89, 0xBB, 0xB4, 0xFF, 0x55, 0x7E, 0x3E, 0x9B, 0x90, 0x44, 0x45, 0xC6, 0x8A, 0x17, 0x32, 0x91, 0x77, 0x7D, 0xCB, 0x4A, 0x26, 0x3D, 0xCA, 0xD8, 0x6E, 0x72, 0x27, 0x93, 0x9C, 0xE4, 0x27, 0xFE, 0x54, 0xDC, 0xE7, 0xDC, 0x02, 0x17, 0x9C, 0x82, 0xA8, 0xB0, 0xE8, 0x69, 0x07, 0xE3, 0x34, 0x0E, 0xBF, 0x17, 0x05, 0x58, 0x23, 0x5F, 0x6D, 0xDA, 0xE8, 0xC6, 0xEE, 0x49, 0x29, 0xEF, 0xD7, 0x48, 0x48, 0x41, 0xE2, 0x2A, 0x57, 0x2B, 0x21, 0x13, 0x64, 0xD4, 0x79, 0x5A, 0xE3, 0x84, 0xDA, 0x63, 0x9A, 0x07, 0x39, 0xE2, 0x7E, 0xA7, 0x56, 0x34, 0x1C, 0xE2, 0xAF, 0xCD, 0x65, 0xD1, 0xC0, 0x1B, 0x72, 0xB1, 0x1D, 0xFC, 0xE2, 0x6E, 0x0F, 0xC7, 0xB0, 0xF5, 0x84, 0x0F, 0xA5, 0x2B, 0xDF, 0x3A, 0xCF, 0x43, 0xE8, 0xE4, 0x07, 0x29, 0xC6, 0x41, 0x0E, 0x7B, 0xEE, 0x35, 0xF9, 0xFC, 0xE7, 0x15, 0xF3, 0x8C, 0x3A, 0xB4, 0x77, 0xBD, 0x88, 0x70, 0x83, 0x4A, 0x03, 0x9E, 0x01, 0x9B, 0xBB, 0xD2, 0xE4, 0xA5, 0xBE, 0xF7, 0x80, 0x92, 0x5A, 0x9F, 0x14, 0x9B, 0x49, 0x5D, 0x3D, 0xDC, 0x32, 0x34, 0x7E, 0xC0, 0xD4, 0xAC, 0xDA, 0x8C, 0xF7, 0xA1, 0xE1, 0xCA, 0x29, 0xF5, 0x58, 0x5E, 0xEB, 0x02, 0xB6, 0x67, 0x42, 0x89, 0x46, 0x4C, 0xA6, 0xA8, 0xBD, 0xEB, 0xEB, 0xDD, 0x8F, 0x2E, 0x72, 0x26, 0x49, 0x70, 0x01, 0x7D, 0x5B, 0x03, 0x06, 0x2C, 0x92, 0x1E, 0x55, 0xB2, 0xDA, 0xD7, 0x0D, 0x70, 0x74, 0x42, 0xD5, 0x62, 0x42, 0x3C, 0xC1, 0x70, 0x8E, 0x67, 0xB1, 0xD9, 0xF3, 0x7E, 0xB2, 0xBA, 0x6D, 0x0F, 0x42, 0x65, 0x24, 0x79, 0xE8, 0x8F, 0xB4, 0x4B, 0x32, 0x35, 0xCA, 0x39, 0x70, 0xFE, 0x8E, 0x81, 0x49, 0x73, 0x8D, 0x3C, 0xCD, 0x60, 0xAF, 0xA3, 0x52, 0x45, 0xEE -}; - -/* CTR_SDK 2 (2.3.4) */ -// APP -static const unsigned char app_fw1D_dev_hdrpub[0x100] = -{ - 0xE9, 0x45, 0xF0, 0xC6, 0x96, 0xD5, 0x6F, 0x7E, 0xAE, 0x03, 0x92, 0x2E, 0xEA, 0xCB, 0xFD, 0xEA, 0xA4, 0x7A, 0x9F, 0x12, 0xDA, 0x4C, 0x10, 0x0A, 0xBE, 0x08, 0x9D, 0x87, 0xE0, 0x14, 0xAC, 0x7F, 0x39, 0xD2, 0xFE, 0x9D, 0x88, 0xB2, 0x81, 0xF6, 0x1A, 0x9E, 0x15, 0x57, 0xD4, 0xE2, 0x31, 0x08, 0x07, 0xEC, 0x4F, 0x10, 0x00, 0xDE, 0xEF, 0x8B, 0x6F, 0xCF, 0x84, 0xE7, 0x3B, 0x41, 0x08, 0x64, 0x3B, 0x1C, 0x00, 0x7C, 0x73, 0xBB, 0x59, 0x4D, 0xD8, 0xD6, 0xE7, 0x7B, 0xBE, 0xDD, 0x50, 0x98, 0xA1, 0x1A, 0xD5, 0xAA, 0x37, 0x69, 0xB8, 0x25, 0xCB, 0x7B, 0x03, 0x00, 0x90, 0x25, 0xF3, 0x7E, 0x9A, 0x0F, 0xA3, 0xAA, 0xC4, 0xB9, 0x3B, 0x3A, 0x18, 0x2B, 0xBC, 0x9C, 0x11, 0x04, 0x92, 0x16, 0x6E, 0xC3, 0xFA, 0x01, 0xD3, 0x00, 0x02, 0xF3, 0x2E, 0xD5, 0x60, 0xA8, 0xAF, 0xAB, 0xEE, 0x2F, 0x9D, 0x30, 0x3E, 0x0E, 0xDC, 0xB8, 0xEC, 0x87, 0x9E, 0x4A, 0xA9, 0x01, 0x34, 0x69, 0x2C, 0x4C, 0x34, 0xB7, 0x7D, 0xB9, 0x7A, 0x17, 0x74, 0x31, 0xB0, 0x29, 0xC4, 0x7D, 0x27, 0x1F, 0xBA, 0xBA, 0x3F, 0x5B, 0x62, 0xF6, 0x90, 0xB8, 0x37, 0x33, 0xFC, 0x73, 0xD6, 0x19, 0x11, 0xCA, 0x83, 0x2A, 0x58, 0x62, 0x9C, 0xB1, 0x83, 0x43, 0x1D, 0x2C, 0x00, 0xA2, 0xE5, 0x87, 0x97, 0x12, 0x63, 0x31, 0x83, 0x0E, 0xB1, 0x1E, 0x69, 0x99, 0x02, 0xAF, 0xDF, 0xFF, 0x0F, 0xA9, 0x7C, 0x1B, 0x33, 0x9E, 0xFF, 0x9C, 0x14, 0x19, 0xA6, 0xCA, 0xFD, 0xB9, 0x17, 0xE0, 0x22, 0xCF, 0xB5, 0x00, 0x77, 0x2E, 0x31, 0xAD, 0xF7, 0xE5, 0xAD, 0x98, 0x14, 0xDF, 0x19, 0xF0, 0xC9, 0xBE, 0x37, 0xF6, 0xF0, 0x23, 0x66, 0xCF, 0x34, 0xE3, 0xD5, 0x8F, 0xD4, 0x07, 0xBA, 0x06, 0x56, 0x00, 0x66, 0x9A, 0xEB, 0x93 -}; - -static const unsigned char app_fw1D_dev_hdrpvt[0x100] = -{ - 0xBC, 0x49, 0x29, 0xB9, 0x01, 0x52, 0x31, 0x76, 0x4C, 0xBA, 0xB1, 0x29, 0x91, 0x77, 0x29, 0xF2, 0x54, 0xE4, 0x6C, 0xB5, 0x68, 0xE1, 0xF0, 0x28, 0xDB, 0x8E, 0x54, 0xA8, 0xB1, 0xA3, 0xBE, 0x3F, 0xCA, 0xCA, 0x95, 0x9D, 0x4E, 0x12, 0xD7, 0x77, 0x6F, 0xB0, 0x9D, 0x85, 0x91, 0x5D, 0x29, 0x3A, 0x54, 0x3A, 0xD6, 0xEE, 0x11, 0xE5, 0xDF, 0xEF, 0xEA, 0x45, 0xD3, 0xFE, 0x58, 0x03, 0x7B, 0xE4, 0x7B, 0x19, 0x75, 0x02, 0xFE, 0xDE, 0xFF, 0x8C, 0x28, 0x33, 0xFE, 0x10, 0x11, 0xD4, 0xCD, 0x13, 0x05, 0x26, 0x85, 0xC3, 0xA8, 0x8A, 0x7A, 0x8A, 0x77, 0x1D, 0x49, 0x25, 0x11, 0x34, 0xB0, 0xBF, 0x45, 0x56, 0xCE, 0x42, 0x2E, 0x1B, 0x5C, 0xC4, 0xDD, 0x71, 0xA0, 0x01, 0x50, 0x73, 0x21, 0xFF, 0x5D, 0x54, 0x6D, 0xDD, 0x3F, 0x14, 0x49, 0x4D, 0x44, 0x46, 0x12, 0x88, 0xD5, 0x92, 0xAE, 0xE2, 0xD0, 0xF6, 0x2C, 0x10, 0xD5, 0x67, 0x61, 0x87, 0x7F, 0x2A, 0x17, 0x9D, 0x4F, 0xC6, 0x79, 0xC3, 0xAF, 0x4D, 0x6F, 0xFB, 0x0F, 0x3B, 0x48, 0x5D, 0x46, 0x9A, 0xE8, 0x53, 0xB7, 0xC5, 0x69, 0xEC, 0x31, 0x25, 0xD1, 0xDC, 0x93, 0xAB, 0x2E, 0x53, 0x3B, 0x8E, 0x96, 0x27, 0x59, 0xD4, 0xF7, 0xB3, 0xAB, 0x51, 0x59, 0xAE, 0x6E, 0x26, 0x4F, 0xC2, 0x95, 0xCE, 0x42, 0xC6, 0xAF, 0x46, 0xC6, 0x2E, 0x32, 0x09, 0x7B, 0xAF, 0x67, 0x4E, 0x57, 0xC8, 0x93, 0x5F, 0x8C, 0xD5, 0x66, 0x7B, 0xCC, 0xE9, 0xBE, 0x86, 0xB9, 0xBB, 0xD0, 0xC8, 0xD2, 0xDC, 0x5F, 0x95, 0x83, 0x28, 0x55, 0x21, 0x1E, 0xEE, 0xCF, 0x23, 0xB7, 0x6D, 0xE0, 0x9A, 0x87, 0x99, 0xFB, 0x82, 0x50, 0xD0, 0x2D, 0xC4, 0xFB, 0xA0, 0x11, 0x2F, 0xDD, 0x05, 0x7E, 0x1C, 0xE3, 0xFB, 0x98, 0x69, 0xD4, 0x49, 0x2F, 0x0D, 0xF6, 0x61 -}; - -static const unsigned char app_fw1D_dev_acexsig[0x100] = -{ - 0x62, 0xFE, 0xD9, 0x12, 0x3D, 0x99, 0x53, 0xC4, 0x20, 0x25, 0xDE, 0x59, 0xEA, 0x6E, 0xF3, 0x16, 0x5B, 0x36, 0xBA, 0x1C, 0xB3, 0xB5, 0x48, 0x37, 0xD2, 0xA4, 0x04, 0xE5, 0x14, 0xC6, 0xE7, 0x22, 0x14, 0x40, 0x6F, 0x92, 0x6A, 0x9B, 0xDF, 0xDE, 0xFA, 0xCE, 0x3C, 0xBB, 0x4B, 0xC4, 0x66, 0xA8, 0x86, 0x58, 0xAC, 0xEB, 0x2F, 0xB7, 0xA3, 0xEC, 0xEA, 0x31, 0x23, 0x61, 0xF6, 0x72, 0x1E, 0x26, 0x8A, 0x1D, 0x68, 0x2A, 0x2A, 0x21, 0x5A, 0xA2, 0x6A, 0xBD, 0xCE, 0xC0, 0x19, 0x08, 0x61, 0x64, 0xB3, 0xF6, 0x90, 0xB1, 0x34, 0xF8, 0x50, 0x6F, 0x83, 0xB6, 0x8D, 0x35, 0x12, 0x7F, 0x9C, 0x7B, 0x6E, 0x3C, 0x4E, 0xD1, 0xFD, 0xC3, 0x30, 0xD2, 0xE8, 0x7E, 0x15, 0x1F, 0xAD, 0xDB, 0x1D, 0x92, 0xDA, 0x8C, 0x4E, 0xE9, 0x84, 0x83, 0xFF, 0x1A, 0x09, 0x77, 0x05, 0x5A, 0xCF, 0x5C, 0x8B, 0x4F, 0x68, 0x36, 0xC8, 0xDA, 0x5B, 0x1A, 0x5A, 0x49, 0xF9, 0xA1, 0xF2, 0xC8, 0x02, 0xFD, 0x69, 0x1F, 0x1D, 0xB3, 0xE8, 0xF8, 0xE1, 0x6B, 0x15, 0x9A, 0x5E, 0x41, 0x84, 0x06, 0x1F, 0x2A, 0xB3, 0xB2, 0xA1, 0xDC, 0x63, 0x81, 0xB3, 0x6B, 0x4B, 0x21, 0x67, 0x19, 0x82, 0x52, 0xFE, 0x75, 0x96, 0xA1, 0xDF, 0x02, 0xD4, 0x07, 0x1F, 0x1B, 0x88, 0x12, 0x5A, 0x76, 0x54, 0xC4, 0x06, 0x2D, 0xB1, 0xAA, 0x41, 0x3C, 0x9F, 0x43, 0xA2, 0x75, 0x20, 0x39, 0xB6, 0x06, 0xF9, 0x9C, 0xFC, 0x00, 0xC5, 0xBC, 0x84, 0x13, 0x80, 0xE4, 0x10, 0x1A, 0xCD, 0x95, 0xBB, 0xF2, 0xDC, 0x57, 0x7B, 0xBA, 0x87, 0x05, 0x0B, 0x96, 0xC1, 0xCD, 0x60, 0xC7, 0x10, 0x44, 0x78, 0x0E, 0x0F, 0x2F, 0x91, 0x54, 0x6C, 0xDE, 0xB8, 0x14, 0x46, 0xF3, 0x9C, 0xAC, 0x7B, 0xAA, 0xE7, 0x1B, 0x52, 0xD6, 0xBE, 0x71, 0x97, 0x22 -}; - -// DLP -static const unsigned char dlp_fw1D_dev_hdrpub[0x100] = -{ - 0xB9, 0xDE, 0x3D, 0xC0, 0x55, 0xB9, 0xCC, 0x3F, 0x55, 0xE0, 0x61, 0x1D, 0x6F, 0xCF, 0x3E, 0x7F, 0xE2, 0xF7, 0xF5, 0xAD, 0x5C, 0x02, 0x7F, 0x17, 0x5B, 0x44, 0x2F, 0x2D, 0xDC, 0xD4, 0xA6, 0x63, 0xD2, 0xA7, 0x82, 0xD3, 0x00, 0x77, 0xC8, 0x0B, 0x28, 0x09, 0x3D, 0x81, 0x86, 0x93, 0xF5, 0xF6, 0xE4, 0x69, 0x3B, 0x60, 0x4C, 0x7F, 0x8D, 0x72, 0xA3, 0x22, 0x42, 0x86, 0x87, 0x06, 0xD8, 0x29, 0x89, 0x8A, 0x9F, 0x5F, 0x6C, 0x06, 0x0C, 0x96, 0x84, 0x00, 0x24, 0x5D, 0x0B, 0xEA, 0x15, 0xEC, 0xAD, 0x90, 0xA4, 0x0C, 0x7B, 0xAE, 0x0E, 0x85, 0x3E, 0xA2, 0x20, 0x04, 0xE8, 0xD9, 0x59, 0x0F, 0x31, 0x0E, 0xD4, 0x5D, 0xC1, 0x18, 0xED, 0x0E, 0xB4, 0xD2, 0x5E, 0x65, 0xA2, 0x78, 0x0C, 0x76, 0x03, 0x3A, 0x71, 0x18, 0xE4, 0x38, 0x44, 0x14, 0xE0, 0x93, 0x84, 0xFE, 0x34, 0x82, 0xCA, 0x0B, 0xB8, 0xF2, 0x41, 0xAB, 0x63, 0xF3, 0xDE, 0xAE, 0xF4, 0x36, 0x81, 0xA4, 0x78, 0x7B, 0xF9, 0xA8, 0xFB, 0xC9, 0xA7, 0x6E, 0xA4, 0xD5, 0xE2, 0xA9, 0xD8, 0xD9, 0xE8, 0x98, 0x1B, 0x25, 0x75, 0x00, 0x11, 0x51, 0x97, 0x62, 0x0D, 0xF0, 0x0C, 0xE9, 0x6B, 0x0C, 0xEE, 0xCE, 0x25, 0x2C, 0x3F, 0xDF, 0xBE, 0x54, 0xD5, 0xD6, 0x5E, 0xEE, 0x1F, 0x73, 0xFC, 0xE8, 0xEC, 0xB3, 0x8A, 0x48, 0x9F, 0x6A, 0xC1, 0x63, 0x85, 0xE4, 0x94, 0x85, 0x8F, 0x3D, 0x9D, 0x43, 0xB4, 0xA7, 0x4C, 0x82, 0xA3, 0x0B, 0x67, 0x43, 0x12, 0x31, 0x77, 0x89, 0xB0, 0xD5, 0x00, 0x1B, 0x52, 0x29, 0xCE, 0x54, 0xC7, 0xC4, 0x7D, 0xB6, 0x69, 0x7B, 0xFE, 0xDC, 0xDB, 0x4E, 0xD8, 0x58, 0x42, 0x14, 0x34, 0x72, 0x64, 0xBC, 0x09, 0x6D, 0xAC, 0xD3, 0xC4, 0x1B, 0x5C, 0x8E, 0xF9, 0xBE, 0x84, 0xCD, 0x9A, 0x86, 0x4B, 0x17 -}; - -static const unsigned char dlp_fw1D_dev_hdrpvt[0x100] = -{ - 0xAA, 0x51, 0x62, 0x58, 0x9A, 0xB5, 0x74, 0xDA, 0x1C, 0xC1, 0x4D, 0x7C, 0x81, 0xF6, 0x70, 0x99, 0x13, 0xCC, 0x90, 0x0D, 0xD9, 0xA0, 0x58, 0x01, 0x79, 0x1A, 0x53, 0xF9, 0x3C, 0xC0, 0x87, 0xF0, 0x35, 0x1A, 0x56, 0xA1, 0x2F, 0x6E, 0x93, 0x9A, 0xD5, 0x87, 0x12, 0x1B, 0x5C, 0xCC, 0xBC, 0xB9, 0x0E, 0xB8, 0xF7, 0x35, 0xD9, 0x23, 0x90, 0xE4, 0x19, 0x64, 0xCD, 0x7D, 0x24, 0xC2, 0x3A, 0xD6, 0x65, 0x38, 0xE7, 0xAD, 0xB2, 0xF9, 0x20, 0x13, 0xD4, 0xC5, 0xA4, 0x8C, 0xB6, 0xDC, 0x3C, 0x56, 0xF2, 0xFC, 0xF5, 0xB6, 0x92, 0xA6, 0xFE, 0x9B, 0x4E, 0xB7, 0x95, 0x8B, 0xAA, 0x2B, 0x70, 0x96, 0xA1, 0x27, 0xAB, 0xA6, 0x75, 0xC9, 0x77, 0x80, 0xE0, 0x65, 0x5D, 0x26, 0xD8, 0xE8, 0x14, 0xD3, 0x17, 0x46, 0x38, 0x58, 0xCC, 0xD8, 0x5A, 0x5A, 0x9F, 0x27, 0xCE, 0xD8, 0x7A, 0x19, 0xD7, 0x35, 0xB2, 0x32, 0xAF, 0x47, 0x2E, 0x9F, 0x4B, 0x64, 0xEC, 0x1F, 0xC6, 0x40, 0xD0, 0x2C, 0x47, 0xD1, 0xEA, 0x33, 0xE5, 0x0E, 0x80, 0xFC, 0x68, 0xEC, 0x8C, 0x12, 0x33, 0xCE, 0x34, 0x28, 0x79, 0xFA, 0x05, 0x5D, 0x70, 0x15, 0xDE, 0xB1, 0x22, 0x85, 0x18, 0x63, 0x15, 0x35, 0x57, 0x04, 0x17, 0x64, 0x20, 0xC8, 0x52, 0x44, 0x64, 0x5E, 0x47, 0x4E, 0x5F, 0x80, 0x21, 0x16, 0x94, 0x4B, 0x18, 0x11, 0x36, 0x67, 0x3B, 0x6C, 0x69, 0x19, 0xCF, 0xC9, 0x05, 0x85, 0x9B, 0x3A, 0xDE, 0x12, 0x1E, 0x0A, 0xC6, 0x22, 0xA8, 0xC7, 0x9A, 0x34, 0x14, 0x98, 0xFD, 0xD9, 0x0F, 0xE8, 0x64, 0xE6, 0x89, 0x63, 0x6E, 0x17, 0x76, 0xD7, 0x1B, 0x6F, 0x92, 0x00, 0xD8, 0xBB, 0xF6, 0xA0, 0x65, 0x9D, 0xAA, 0x7A, 0x0E, 0x4B, 0x56, 0xA5, 0x33, 0xDA, 0x3F, 0x5D, 0xFE, 0xD3, 0xAD, 0x6E, 0x0E, 0xB3, 0xD4, 0x41 -}; - -static const unsigned char dlp_fw1D_dev_acexsig[0x100] = -{ - 0x97, 0x84, 0x97, 0xEE, 0x4F, 0x35, 0xCC, 0xBE, 0x08, 0xB4, 0x5D, 0x7E, 0x17, 0xC3, 0x94, 0x2B, 0x4D, 0x3A, 0xA5, 0xB5, 0x01, 0xD4, 0xAE, 0x2A, 0x90, 0x26, 0x21, 0x8F, 0x56, 0x05, 0xB9, 0xA2, 0x5E, 0xCE, 0x73, 0xC7, 0x42, 0xDC, 0x99, 0xD2, 0x7C, 0x08, 0x62, 0xBF, 0x10, 0x7A, 0xC1, 0x5D, 0x22, 0x53, 0x8F, 0x63, 0x2D, 0x73, 0xF3, 0x05, 0xDA, 0x9D, 0x6A, 0xF8, 0xB9, 0x5B, 0x80, 0xB4, 0x30, 0xB3, 0x11, 0xF7, 0x96, 0x8A, 0xCF, 0x70, 0xD7, 0x62, 0x6E, 0x99, 0x32, 0xFD, 0x74, 0x34, 0x16, 0xFD, 0x17, 0x1F, 0xB1, 0xEC, 0xA4, 0x0F, 0x52, 0x13, 0x9F, 0x62, 0x0D, 0xE0, 0x50, 0xA6, 0xA0, 0x7B, 0x69, 0x95, 0xE0, 0xE9, 0xBB, 0x38, 0x0C, 0x62, 0xE0, 0xE3, 0xCE, 0x82, 0xE0, 0xB9, 0xE0, 0xF6, 0x61, 0x50, 0xBF, 0xA8, 0x18, 0x15, 0x38, 0xFE, 0xFA, 0x8C, 0xBA, 0xA5, 0xB9, 0x9C, 0x05, 0xA6, 0x91, 0x5C, 0xA7, 0x13, 0x6F, 0x13, 0x3F, 0xF1, 0xF6, 0x68, 0xAF, 0x40, 0xEC, 0x27, 0xE0, 0x33, 0x6B, 0xCF, 0x26, 0x06, 0xF8, 0x6A, 0x13, 0x6C, 0xBC, 0xDB, 0xAF, 0x6F, 0x78, 0xA0, 0x80, 0x10, 0x8F, 0xB6, 0x91, 0x5A, 0x43, 0x2C, 0x5F, 0x1D, 0xBA, 0xB4, 0x5E, 0xBE, 0xAE, 0x53, 0x09, 0x17, 0x5B, 0x6C, 0xC1, 0x5E, 0x0F, 0x72, 0x6E, 0xD6, 0x10, 0x0B, 0xC3, 0x26, 0xDC, 0xAF, 0xCA, 0x28, 0xAB, 0x00, 0x67, 0x04, 0xE3, 0x54, 0xE8, 0x95, 0xC6, 0x23, 0xB6, 0x79, 0x70, 0xA4, 0x87, 0x6D, 0x12, 0x48, 0xCC, 0x11, 0x86, 0xEC, 0x82, 0xF4, 0x30, 0xC9, 0xB1, 0x6D, 0x08, 0xA7, 0xEA, 0x8C, 0x6A, 0x97, 0xAA, 0x89, 0xD5, 0xC5, 0x07, 0xA9, 0xD5, 0xCF, 0x09, 0x08, 0xBC, 0x56, 0x63, 0x8D, 0x70, 0x2F, 0x64, 0xAF, 0x51, 0x9E, 0x22, 0xA4, 0x88, 0xF0, 0xDC, 0x56, 0x72, 0x28 -}; - -// DEMO -static const unsigned char demo_fw1E_dev_hdrpub[0x100] = -{ - 0xC0, 0xBE, 0x2D, 0xAC, 0x4A, 0x1D, 0xCB, 0xDE, 0x84, 0x61, 0x0C, 0x29, 0x50, 0xEC, 0x7A, 0xF9, 0xA4, 0x96, 0x3B, 0x6E, 0xF7, 0xEC, 0x38, 0x25, 0x52, 0xB0, 0x6D, 0x71, 0xA4, 0x55, 0x61, 0x7C, 0xB4, 0xCA, 0x7F, 0x4D, 0xB0, 0xF2, 0x26, 0xE8, 0xDE, 0x11, 0x01, 0x3C, 0xFF, 0x11, 0xBE, 0x42, 0x7D, 0x80, 0xD6, 0xF0, 0xEB, 0x1E, 0xF5, 0x68, 0x48, 0x24, 0x65, 0x09, 0xA0, 0x29, 0x9B, 0xC3, 0xBB, 0x45, 0x7E, 0x49, 0xE7, 0x98, 0x0E, 0x5F, 0x3C, 0xCC, 0xA6, 0xC6, 0x13, 0xC5, 0xC6, 0x8C, 0x82, 0x8F, 0xEC, 0xF1, 0x6D, 0xDB, 0x9C, 0xE6, 0xAD, 0xB7, 0x3E, 0xD7, 0x29, 0xFC, 0x3B, 0x50, 0x1F, 0x92, 0x0B, 0x25, 0x91, 0x41, 0x32, 0xFE, 0xCE, 0xAF, 0x0D, 0x34, 0xC5, 0xA3, 0x2E, 0x1A, 0x41, 0xA1, 0xC4, 0x86, 0x8D, 0x94, 0x1E, 0x80, 0x58, 0x3E, 0x35, 0x58, 0x2D, 0x0C, 0xD1, 0x0E, 0x3E, 0x6C, 0xE3, 0xDC, 0x2B, 0x97, 0xDC, 0xEC, 0x3A, 0x0D, 0xDA, 0x8E, 0x14, 0x5F, 0x12, 0xDC, 0xD7, 0x19, 0xD1, 0xE9, 0xDA, 0xD9, 0x6D, 0x02, 0x4E, 0xFC, 0x9B, 0x41, 0x3E, 0x4A, 0x70, 0xD6, 0x81, 0x41, 0xF3, 0x7F, 0xC8, 0x6E, 0xAD, 0x58, 0x25, 0xC8, 0x92, 0xD6, 0x37, 0x10, 0x93, 0xEB, 0x1D, 0xCD, 0x80, 0x05, 0x9F, 0x85, 0x05, 0x75, 0x47, 0xFC, 0x3C, 0xA7, 0x6A, 0xF4, 0x32, 0x1D, 0x49, 0xF3, 0xAA, 0x26, 0xEB, 0x78, 0x02, 0xE6, 0xA7, 0x15, 0xC0, 0xE1, 0x76, 0x19, 0x61, 0x42, 0xEC, 0x58, 0x38, 0x5F, 0x6F, 0xA6, 0x61, 0x6D, 0x49, 0x07, 0x70, 0xFD, 0x08, 0x28, 0x41, 0xB7, 0xA7, 0x96, 0x0E, 0x94, 0x2E, 0xF9, 0x25, 0x29, 0x15, 0x92, 0x13, 0xFC, 0xA6, 0x0D, 0x40, 0x54, 0x2D, 0x9D, 0x4E, 0x19, 0x77, 0xB1, 0xFC, 0x27, 0x95, 0x8F, 0xDA, 0x99, 0xED, 0x2C, 0xD7, 0xE1 -}; - -static const unsigned char demo_fw1E_dev_acexsig[0x100] = -{ - 0x67, 0xC0, 0xA1, 0x00, 0x1D, 0xE0, 0x35, 0x62, 0x0F, 0xA7, 0xFE, 0xE7, 0xC5, 0x8B, 0x44, 0xFE, 0x1F, 0x61, 0x82, 0xFD, 0x42, 0xAF, 0x88, 0xAD, 0x44, 0xE2, 0x26, 0xFB, 0xFD, 0xA1, 0xFC, 0x3A, 0x9D, 0xCD, 0x1B, 0x2D, 0xD2, 0x40, 0x13, 0x85, 0x1C, 0x66, 0xCF, 0xF1, 0xF5, 0x1D, 0xF0, 0x5E, 0x54, 0xB5, 0xB9, 0x8A, 0xB2, 0x72, 0x91, 0xF5, 0x23, 0xF0, 0x58, 0xBA, 0xC0, 0x4D, 0x84, 0x77, 0x5A, 0x4D, 0x41, 0xBD, 0xBD, 0x07, 0x38, 0x3E, 0x42, 0x40, 0x94, 0x55, 0x21, 0xCF, 0x99, 0x89, 0xA7, 0xEC, 0xBA, 0x6D, 0x72, 0x3B, 0x98, 0xE1, 0x5A, 0x21, 0x57, 0x79, 0xBF, 0xC4, 0xC5, 0x0D, 0xBF, 0x76, 0x44, 0x88, 0xFE, 0x3D, 0x8A, 0x8A, 0x05, 0x85, 0x61, 0xBB, 0xB6, 0x76, 0x22, 0xF6, 0x59, 0x9E, 0x3B, 0x21, 0xF3, 0x23, 0xC7, 0x20, 0x34, 0x8E, 0xB7, 0xAB, 0xD0, 0x06, 0x56, 0x11, 0x92, 0x02, 0xF0, 0xB7, 0x1A, 0x4D, 0x85, 0xD5, 0x7A, 0x71, 0x75, 0x5D, 0x3A, 0x7D, 0x6F, 0xB7, 0xC2, 0xA6, 0xAF, 0xCF, 0x1C, 0x44, 0x61, 0xBB, 0xE7, 0x03, 0x7D, 0x2E, 0xE9, 0xC2, 0x0D, 0x89, 0xD9, 0x4B, 0x4D, 0x88, 0xB8, 0x81, 0xB5, 0x86, 0x45, 0xAE, 0x59, 0xA5, 0x8D, 0x68, 0x80, 0x00, 0x05, 0x53, 0x27, 0x89, 0xC9, 0x20, 0x1E, 0x9F, 0xDE, 0xAE, 0x7C, 0x98, 0xAC, 0xAC, 0x78, 0x80, 0x7B, 0x3A, 0x1E, 0xA6, 0x4E, 0x03, 0xD3, 0x00, 0xDD, 0x1E, 0xEB, 0x01, 0x9A, 0xDE, 0xAE, 0xF4, 0x84, 0x44, 0xBC, 0x4B, 0xD5, 0xDE, 0x23, 0xF0, 0x3C, 0xA4, 0x57, 0x70, 0x25, 0x2C, 0x93, 0x3D, 0xA5, 0x17, 0xCF, 0x1B, 0x0A, 0xDF, 0x4F, 0x21, 0x71, 0x05, 0xF4, 0xE4, 0x6F, 0xE2, 0xC3, 0x50, 0x74, 0xA9, 0x09, 0x61, 0x1D, 0x81, 0xC9, 0xA3, 0x1F, 0x82, 0x2D, 0x8F, 0xDB, 0x5A, 0x62, 0xB0 -}; - -/* CTR_SDK 3 (3.2.5) */ -// APP -static const unsigned char app_fw20_dev_hdrpub[0x100] = -{ - 0xC4, 0xF6, 0x84, 0x47, 0xF0, 0xF0, 0x5D, 0x2B, 0x06, 0xE0, 0xC6, 0x73, 0xE2, 0x43, 0x9F, 0x57, 0xAA, 0x48, 0xCE, 0xB8, 0xEA, 0xBD, 0xA1, 0x84, 0x06, 0xFC, 0xF5, 0x7D, 0xA0, 0x37, 0x79, 0xA6, 0x27, 0xF0, 0xFE, 0xE8, 0x7E, 0xF1, 0x9E, 0xC0, 0x7A, 0x0A, 0x40, 0x86, 0x20, 0x52, 0x61, 0x4D, 0xAB, 0x50, 0xC5, 0x93, 0xEB, 0x2C, 0x36, 0x10, 0xAA, 0xEF, 0x32, 0x28, 0x5C, 0x82, 0xF7, 0x58, 0xE5, 0x2E, 0xC0, 0xEA, 0x0F, 0xB9, 0x90, 0x64, 0x0D, 0x44, 0x39, 0x36, 0x4E, 0x0D, 0x8A, 0xBF, 0xEB, 0x95, 0x98, 0x29, 0x27, 0xCF, 0xDC, 0x66, 0xE4, 0xCD, 0xE9, 0xC7, 0x9F, 0xF8, 0x1F, 0x64, 0xB0, 0x47, 0x91, 0x82, 0x54, 0x8D, 0x87, 0xD3, 0xE0, 0xA9, 0xFF, 0xE5, 0x78, 0xE9, 0x35, 0x55, 0x57, 0x28, 0x8C, 0x8B, 0x54, 0x63, 0xB7, 0x36, 0xFB, 0x85, 0xDD, 0xB2, 0x54, 0x14, 0xAD, 0x39, 0xDE, 0x88, 0x91, 0xCB, 0xD6, 0xF0, 0x0C, 0xB7, 0xEC, 0x91, 0x48, 0x1C, 0xB9, 0xB3, 0xEB, 0x90, 0xBC, 0xAC, 0x00, 0x40, 0x35, 0x45, 0xD4, 0x71, 0x6A, 0x14, 0x65, 0x3E, 0x3C, 0xEA, 0xAE, 0xE0, 0xFE, 0xE6, 0x71, 0xA2, 0x51, 0x2A, 0x15, 0x70, 0xC1, 0x3D, 0x70, 0x29, 0xAE, 0xFC, 0x21, 0x66, 0x06, 0x8B, 0x66, 0xE4, 0xE9, 0x4D, 0x6A, 0x9D, 0x66, 0xDA, 0x64, 0x2B, 0xE8, 0xF3, 0x8E, 0x31, 0xF0, 0xC2, 0x12, 0x02, 0x39, 0x73, 0x41, 0x4E, 0xF5, 0x91, 0x43, 0x9D, 0x1E, 0x51, 0xE6, 0x2D, 0x4C, 0x8F, 0xA0, 0x21, 0x2A, 0x38, 0x69, 0x3C, 0xB6, 0x9F, 0xF9, 0xBB, 0x8B, 0xFF, 0x72, 0x3A, 0x74, 0x7C, 0xCC, 0x49, 0x5B, 0x40, 0x25, 0x41, 0xE2, 0x2F, 0xEB, 0x3B, 0xD4, 0xE5, 0x14, 0x0F, 0x83, 0x43, 0x63, 0x30, 0x75, 0x5E, 0x4B, 0x29, 0x94, 0x65, 0xF8, 0xDC, 0x6D, 0x5B, 0xC1, 0xB1 -}; - -static const unsigned char app_fw20_dev_acexsig[0x100] = -{ - 0x48, 0xAB, 0x7A, 0x3E, 0x55, 0x05, 0x84, 0x09, 0x4E, 0x70, 0xD2, 0x42, 0x3C, 0xBA, 0x32, 0x8B, 0xB9, 0x9F, 0x9D, 0x16, 0x95, 0x19, 0xE7, 0xC7, 0x8E, 0x5B, 0x1D, 0xA3, 0x67, 0xC8, 0x9D, 0xD3, 0xCD, 0x82, 0x5A, 0x3D, 0xC0, 0xD6, 0xB9, 0x13, 0xBE, 0x3F, 0xE7, 0x5F, 0x36, 0x73, 0x71, 0x77, 0xCD, 0x7B, 0x40, 0x33, 0x25, 0xA6, 0xA5, 0xA2, 0x51, 0xC8, 0x40, 0x88, 0xC3, 0x7C, 0x8D, 0x29, 0xDE, 0xEB, 0xCB, 0x9B, 0x5D, 0xE3, 0xDA, 0x3D, 0x74, 0x4A, 0xDE, 0xE7, 0xE7, 0xC0, 0x85, 0x2E, 0x5E, 0x84, 0xE3, 0x1B, 0x24, 0x0B, 0x82, 0x79, 0xEF, 0x1B, 0xAD, 0xD2, 0x6C, 0xE3, 0xDD, 0xC0, 0x03, 0xFD, 0x5E, 0xD9, 0x11, 0xE5, 0x65, 0x05, 0xED, 0x65, 0x79, 0x03, 0x59, 0xE7, 0x09, 0x63, 0x39, 0x9D, 0x51, 0x00, 0x2F, 0x5E, 0x65, 0xB1, 0xA7, 0x73, 0x1B, 0x1E, 0xC9, 0x27, 0x75, 0xB3, 0xF6, 0x7B, 0x36, 0x60, 0x57, 0x3F, 0xF7, 0x45, 0x83, 0x5D, 0x01, 0xD4, 0x02, 0xA7, 0xE5, 0xCE, 0x06, 0x63, 0xF9, 0x27, 0x9A, 0xF7, 0xA6, 0xD8, 0x5A, 0x72, 0xD1, 0x7F, 0x03, 0xE0, 0xA7, 0xD3, 0x47, 0xE9, 0xC2, 0xB3, 0x1C, 0xAE, 0x99, 0xD7, 0x08, 0x13, 0xE7, 0x5F, 0x8B, 0x8D, 0xD6, 0x71, 0xF4, 0x66, 0x0E, 0x71, 0x5E, 0xE0, 0x08, 0xA1, 0x19, 0x9A, 0x39, 0xBD, 0x29, 0x78, 0x75, 0x9F, 0xFE, 0x62, 0xD4, 0x52, 0xA0, 0x01, 0x7B, 0x16, 0x9C, 0x33, 0xFD, 0xBC, 0x05, 0xFB, 0xC4, 0xC9, 0x67, 0xF6, 0xE1, 0xE0, 0x15, 0xFF, 0x13, 0x78, 0x09, 0xDF, 0x1B, 0x6C, 0x29, 0x9F, 0xA7, 0x8D, 0x3D, 0x36, 0xEB, 0x28, 0x2E, 0x85, 0xF1, 0x99, 0x5B, 0x94, 0xB4, 0xB4, 0x47, 0x78, 0xE9, 0x74, 0xDA, 0x71, 0xA0, 0x72, 0xDC, 0x32, 0x55, 0x14, 0xE8, 0x63, 0xDA, 0xB4, 0x77, 0x58, 0x14, 0xC7 -}; - -/* CTR_SDK 4 (4.2.8) */ -// APP -static const unsigned char app_fw21_dev_hdrpub[0x100] = -{ - 0xCF, 0xEC, 0xB2, 0x48, 0x03, 0x6D, 0xB8, 0x09, 0xE3, 0x5C, 0x6C, 0x62, 0x2C, 0xA9, 0x49, 0xE1, 0xF4, 0xF4, 0x0C, 0x6C, 0xC3, 0xE5, 0x2F, 0x9D, 0x50, 0xA0, 0x2B, 0x5A, 0x00, 0xC6, 0x72, 0x00, 0x0B, 0xA3, 0x04, 0x5D, 0x94, 0x46, 0xE7, 0x00, 0x1B, 0x48, 0x85, 0xB5, 0x61, 0x2C, 0xC9, 0x74, 0xCA, 0x2B, 0x43, 0x13, 0xC1, 0x78, 0x97, 0x5C, 0x33, 0x2F, 0x07, 0xC7, 0x85, 0xF0, 0xDA, 0xDB, 0x60, 0x96, 0x50, 0x0F, 0x7C, 0x4B, 0x7A, 0xD7, 0x17, 0x9D, 0xE4, 0xE5, 0xC3, 0xAB, 0x6F, 0x5D, 0xA5, 0x78, 0x32, 0xAD, 0x04, 0xDD, 0x96, 0x6E, 0xDC, 0x75, 0xFF, 0xC2, 0x2F, 0xFA, 0xA2, 0xEE, 0x46, 0x89, 0xCD, 0xAE, 0x69, 0x92, 0xA4, 0x48, 0xBC, 0x46, 0x47, 0xC4, 0x8C, 0x89, 0x63, 0xE1, 0x0A, 0x4D, 0x1C, 0xDC, 0x46, 0x2F, 0x5B, 0x70, 0x8A, 0x7C, 0xE9, 0x22, 0x9C, 0x09, 0x0B, 0xA8, 0x97, 0x40, 0xCA, 0x2A, 0x7D, 0x84, 0xA1, 0x04, 0x4A, 0x2E, 0xDB, 0xD7, 0xD0, 0x64, 0x43, 0x9C, 0xD0, 0x78, 0x11, 0x41, 0x88, 0x33, 0xDD, 0x31, 0x62, 0x90, 0x2D, 0x17, 0xF2, 0xC6, 0xA9, 0x2B, 0x9C, 0x70, 0xAB, 0xDC, 0xD3, 0xAB, 0x5D, 0xDA, 0xEE, 0x3D, 0x6C, 0x0E, 0x81, 0xFF, 0xF6, 0x67, 0x5A, 0x44, 0xF9, 0xAC, 0x07, 0x3D, 0x23, 0x94, 0x75, 0x65, 0x93, 0x20, 0x0C, 0xC5, 0x76, 0x1D, 0x0F, 0x65, 0x06, 0x3D, 0x21, 0xA2, 0xF0, 0x96, 0x80, 0xB7, 0x0A, 0x49, 0x53, 0x38, 0xA3, 0x5D, 0xC0, 0x74, 0x3C, 0xA4, 0xD9, 0x40, 0x36, 0x85, 0x1F, 0x8C, 0xD1, 0x2D, 0x15, 0xF9, 0xEF, 0x24, 0xA9, 0x7E, 0x9D, 0xB2, 0x1E, 0xF8, 0xA0, 0x72, 0x81, 0x17, 0x77, 0x73, 0xB1, 0x56, 0x7F, 0xAD, 0x05, 0xA2, 0xD2, 0x30, 0x5A, 0xF5, 0xD3, 0xAF, 0x0F, 0x10, 0x4A, 0x52, 0xD8, 0x09, 0x47, 0x97 -}; - -static const unsigned char app_fw21_dev_hdrpvt[0x100] = -{ - 0x8C, 0xBD, 0xB2, 0x3B, 0xCE, 0x9E, 0x51, 0x09, 0xD8, 0x6D, 0x72, 0x2B, 0xCE, 0x01, 0x55, 0x32, 0x6E, 0xC5, 0x57, 0x37, 0xB4, 0x2E, 0x09, 0x59, 0xD9, 0xFE, 0x60, 0xF9, 0xCE, 0x36, 0x85, 0x6A, 0x04, 0x76, 0x76, 0xF9, 0x04, 0xEA, 0x2D, 0x68, 0xC4, 0x0F, 0x05, 0xFA, 0xAD, 0x69, 0x4C, 0x80, 0x12, 0x6C, 0xD0, 0x3D, 0xAA, 0x22, 0xFF, 0x89, 0x78, 0x57, 0xE8, 0x53, 0x25, 0x15, 0xD0, 0x7E, 0xD8, 0x55, 0x46, 0xA2, 0x04, 0xC7, 0x6E, 0xC1, 0xF3, 0x89, 0x7C, 0x2C, 0x0E, 0x93, 0x97, 0x91, 0x72, 0xF4, 0xF6, 0x90, 0x69, 0x0F, 0xB8, 0xC9, 0x17, 0xCF, 0x83, 0xAC, 0xA5, 0x1F, 0x69, 0x74, 0x12, 0x29, 0x2B, 0x21, 0x58, 0xF2, 0xDA, 0xE3, 0x25, 0x16, 0x09, 0x74, 0x40, 0x90, 0xAB, 0x1B, 0xE4, 0x06, 0x28, 0x77, 0xED, 0xC6, 0x16, 0x86, 0x0A, 0x27, 0xDD, 0x03, 0x01, 0x4D, 0x9A, 0x26, 0x6E, 0xC8, 0x9F, 0xD3, 0x9A, 0x4B, 0x59, 0xD1, 0x10, 0x9B, 0xEB, 0xA9, 0x58, 0x72, 0xBD, 0xA1, 0xFE, 0x9D, 0x86, 0xED, 0x29, 0xE9, 0x29, 0x49, 0x62, 0x4B, 0xD8, 0x7D, 0x2A, 0x7A, 0x66, 0x1B, 0xE5, 0x04, 0x81, 0x56, 0x10, 0x50, 0xAF, 0xB8, 0x48, 0x27, 0xC1, 0xC9, 0x46, 0xBD, 0x3F, 0x16, 0x06, 0xA5, 0x3D, 0x04, 0x9F, 0x0D, 0x54, 0x71, 0x1C, 0xF4, 0x82, 0xC0, 0x66, 0x74, 0xEA, 0x9C, 0x83, 0x3C, 0x27, 0x01, 0xDF, 0x6F, 0x56, 0xA8, 0x1B, 0xE3, 0x68, 0x55, 0x9F, 0xAB, 0x90, 0x67, 0x20, 0x25, 0xFA, 0x3D, 0x51, 0x2A, 0x23, 0x16, 0xCB, 0x06, 0x5A, 0xAD, 0xAC, 0xC8, 0x47, 0xF9, 0x39, 0x2E, 0x6A, 0xF8, 0xFA, 0x0A, 0xE8, 0x8A, 0x64, 0x84, 0x6B, 0xED, 0xDA, 0x8F, 0x2A, 0x08, 0x86, 0x8F, 0x56, 0x69, 0x64, 0xC3, 0x98, 0x55, 0x37, 0x9A, 0x48, 0x40, 0xDA, 0xD5, 0x03, 0x21 -}; - -static const unsigned char app_fw21_dev_acexsig[0x100] = -{ - 0xDF, 0x1C, 0x8B, 0x98, 0xE4, 0x6F, 0xA2, 0x35, 0x6C, 0xC3, 0x18, 0x17, 0x98, 0xF3, 0xCE, 0x54, 0x7E, 0x14, 0x2E, 0x7F, 0x1E, 0xD8, 0x6D, 0xCF, 0xBC, 0x29, 0x4E, 0xFE, 0x32, 0x2E, 0xC1, 0x11, 0xAD, 0x46, 0x9A, 0xC6, 0x70, 0xEA, 0xEE, 0x28, 0x55, 0x22, 0xE1, 0x36, 0x05, 0x1C, 0x04, 0x8A, 0xCE, 0x0F, 0x0C, 0x83, 0x8F, 0xC8, 0xD6, 0xDE, 0x11, 0x8E, 0xEA, 0xCF, 0xAD, 0x9B, 0xCF, 0x81, 0x0D, 0xEB, 0x71, 0x13, 0xB3, 0xD3, 0xAE, 0x83, 0x02, 0x4C, 0x0E, 0x10, 0x50, 0x59, 0x3C, 0xEE, 0x60, 0x06, 0xFB, 0x8C, 0x7F, 0xC2, 0x20, 0x24, 0x01, 0x62, 0x55, 0x87, 0x60, 0x0F, 0xAD, 0xFA, 0x73, 0x2E, 0xF6, 0x65, 0x62, 0xD2, 0xE5, 0x10, 0x45, 0x69, 0x70, 0x39, 0x03, 0xD1, 0x39, 0xEC, 0x50, 0xC1, 0xD4, 0x25, 0x39, 0xB2, 0x90, 0x11, 0x4E, 0x95, 0xCB, 0x19, 0xEB, 0xCA, 0x0F, 0xB5, 0xFA, 0xC7, 0xB0, 0xE2, 0xD7, 0xE0, 0x71, 0xC3, 0xE5, 0x55, 0x33, 0x9E, 0x5C, 0xDC, 0x4D, 0x3B, 0x51, 0x11, 0x0D, 0x31, 0x78, 0x96, 0xCA, 0xD7, 0x18, 0x58, 0xEE, 0x00, 0xE9, 0x28, 0xF2, 0x68, 0x76, 0xD4, 0x57, 0xFE, 0x65, 0xB1, 0x4B, 0x49, 0x3F, 0xF6, 0xA6, 0x58, 0x4A, 0xC7, 0xFC, 0xC4, 0xBB, 0x61, 0xBC, 0x58, 0x8D, 0x55, 0x65, 0xE6, 0x0A, 0x79, 0x39, 0x41, 0xB8, 0x80, 0x61, 0xF7, 0x05, 0xC3, 0xFE, 0xD6, 0x8B, 0x09, 0x82, 0xC2, 0x5F, 0xA6, 0x56, 0xF9, 0xEE, 0x1D, 0x0E, 0x06, 0x3E, 0x9F, 0x3F, 0xF1, 0x93, 0x9A, 0x4F, 0xA2, 0xD5, 0x91, 0x87, 0x8A, 0xFE, 0xCF, 0xC3, 0xFC, 0x8A, 0xB1, 0xC4, 0x78, 0xE9, 0xD1, 0x1A, 0xF7, 0xB1, 0xD3, 0x20, 0xCB, 0x83, 0xBE, 0x03, 0xD5, 0xCA, 0xA5, 0x5E, 0x17, 0xA6, 0x91, 0x10, 0xD4, 0xBE, 0x23, 0xD6, 0x4B, 0x4F, 0x03, 0xA9, 0xAE -}; - -// DEMO -static const unsigned char demo_fw21_dev_hdrpub[0x100] = -{ - 0xB5, 0x11, 0x8D, 0x9E, 0x2D, 0xDB, 0x70, 0x6D, 0x6E, 0xEE, 0xAA, 0x21, 0xE0, 0x4E, 0x80, 0x0A, 0x96, 0x4A, 0x10, 0xD0, 0x9C, 0xD7, 0xD9, 0xD4, 0x94, 0x87, 0x72, 0xA2, 0xAF, 0x02, 0xA0, 0x05, 0x2E, 0xBF, 0x17, 0xEB, 0xFE, 0x5B, 0x9F, 0xB7, 0x0B, 0x1E, 0x3E, 0xF9, 0xAC, 0xBC, 0x7B, 0xB1, 0x56, 0x10, 0x24, 0x5F, 0x57, 0x2C, 0x08, 0xD0, 0x14, 0x79, 0x83, 0x84, 0x6A, 0x45, 0x25, 0xEB, 0xD9, 0xBE, 0x02, 0x21, 0xF7, 0x35, 0xC2, 0x74, 0x57, 0xC5, 0xAC, 0x34, 0x05, 0xC6, 0x9E, 0x82, 0xB8, 0xED, 0x78, 0xC4, 0x3B, 0xFD, 0x23, 0x59, 0x54, 0xD2, 0x0A, 0x0B, 0x5B, 0x25, 0xC0, 0x71, 0xC3, 0x84, 0x3A, 0xA7, 0xF9, 0x99, 0x86, 0xD8, 0xFE, 0x60, 0x10, 0x85, 0x77, 0x57, 0x76, 0x0C, 0x25, 0xE1, 0x18, 0x18, 0x3B, 0x83, 0xFD, 0x36, 0x7C, 0x84, 0x58, 0xC2, 0xC4, 0x68, 0x4F, 0xD1, 0xD7, 0x0A, 0x88, 0xFD, 0xCA, 0x97, 0xA1, 0xE5, 0xCE, 0x72, 0x63, 0xCF, 0x74, 0xD0, 0x20, 0xD9, 0xDE, 0x3F, 0xBB, 0x11, 0xF9, 0x21, 0xAB, 0x3F, 0x54, 0x41, 0xA7, 0xAA, 0xCA, 0xFC, 0xE1, 0x1A, 0x8C, 0x12, 0xC9, 0x39, 0x13, 0x5A, 0x81, 0x29, 0x49, 0xE8, 0xFB, 0x48, 0xC9, 0x4D, 0x50, 0x87, 0xAE, 0x51, 0xFB, 0x94, 0xFC, 0xF0, 0x9C, 0x70, 0x1C, 0xE8, 0x6E, 0x44, 0x53, 0x1E, 0x2F, 0x27, 0x5C, 0xB8, 0xEC, 0xBE, 0xFC, 0xD9, 0x98, 0x6A, 0x08, 0xD0, 0x5C, 0x4D, 0x78, 0x2D, 0x4D, 0x07, 0xAD, 0x5E, 0xB8, 0x51, 0x40, 0xE2, 0x2A, 0x7F, 0xB1, 0x54, 0x47, 0x5C, 0x99, 0x12, 0xC2, 0x6D, 0x5E, 0xED, 0x25, 0x30, 0x6A, 0x99, 0xC5, 0x0D, 0x65, 0x83, 0x68, 0x3A, 0xFD, 0x82, 0x59, 0x0D, 0xCE, 0x0B, 0x49, 0xBE, 0x17, 0x46, 0x51, 0xA9, 0xB6, 0x54, 0xE1, 0x18, 0xBD, 0x49, 0xE6, 0x7F -}; - -static const unsigned char demo_fw21_dev_hdrpvt[0x100] = -{ - 0x1D, 0x7B, 0x79, 0x32, 0xAB, 0x46, 0xD2, 0xBC, 0x8E, 0xD6, 0x7F, 0x8F, 0x3A, 0x85, 0xAD, 0xA5, 0x8B, 0xA9, 0x0D, 0xA9, 0xDA, 0x0F, 0xEF, 0x61, 0x04, 0xBA, 0x35, 0x39, 0x36, 0x03, 0xD8, 0x68, 0x5F, 0x9F, 0x2F, 0xD6, 0xF6, 0x38, 0x96, 0xFD, 0xE7, 0xEA, 0x89, 0xD8, 0x7F, 0x7E, 0xC5, 0x29, 0x2F, 0xD9, 0x3B, 0x02, 0xE7, 0x1F, 0xBD, 0x63, 0x9C, 0x21, 0xD8, 0xFF, 0x43, 0x8A, 0x74, 0xCD, 0x3D, 0x4C, 0x09, 0xEE, 0xDB, 0xE0, 0xBE, 0x03, 0xD1, 0x92, 0xD7, 0x22, 0x35, 0x5A, 0x8C, 0xCE, 0xBE, 0x2B, 0xB4, 0x81, 0x47, 0x3F, 0x45, 0x75, 0x33, 0x31, 0x6B, 0xFF, 0x43, 0x5D, 0x17, 0x43, 0xAE, 0xD1, 0x25, 0xF7, 0xD9, 0xD5, 0x5C, 0xB6, 0x92, 0x5C, 0xB3, 0xF3, 0xF7, 0x65, 0x9F, 0x4C, 0x05, 0x12, 0xEC, 0xA8, 0x6D, 0x70, 0x65, 0x57, 0x6C, 0xD8, 0xE3, 0xD6, 0xFA, 0xC1, 0xFD, 0x54, 0xE8, 0x34, 0x67, 0x4D, 0x0A, 0x14, 0x2F, 0xA3, 0xD4, 0x81, 0x8C, 0xC3, 0xD0, 0x8B, 0x09, 0x08, 0x90, 0x70, 0x68, 0xA0, 0x0E, 0xD1, 0x0B, 0xAA, 0x71, 0xEC, 0x9A, 0x1A, 0x83, 0xFF, 0xA1, 0x70, 0xEB, 0xAC, 0xF2, 0xE9, 0x80, 0xA1, 0xB8, 0x20, 0x31, 0x83, 0xF5, 0x37, 0x01, 0x72, 0x06, 0x50, 0x05, 0x3F, 0x14, 0xF9, 0x29, 0x48, 0x84, 0xA0, 0x0E, 0xF7, 0xB8, 0x1D, 0xA3, 0x36, 0x5A, 0x78, 0x6D, 0x83, 0x90, 0x27, 0xE3, 0x50, 0x49, 0x2F, 0x65, 0xE5, 0x61, 0xED, 0x65, 0xBE, 0xEA, 0x34, 0xA6, 0x6A, 0xEF, 0x49, 0xB4, 0xE0, 0xBC, 0xC2, 0xA5, 0xB8, 0xEB, 0xA9, 0x2F, 0xBA, 0x26, 0x76, 0xB2, 0x5A, 0x3A, 0x3B, 0xFD, 0xAD, 0xFB, 0xE4, 0x79, 0xE2, 0x85, 0x54, 0x5B, 0xAB, 0x1F, 0x0A, 0xE5, 0x8B, 0x77, 0x3A, 0x10, 0x98, 0x26, 0x74, 0xC8, 0xB0, 0x82, 0xB1, 0xF9, 0x8F, 0x68, 0x59 -}; - -static const unsigned char demo_fw21_dev_acexsig[0x100] = -{ - 0xD3, 0x7D, 0x42, 0xBA, 0x6A, 0x1E, 0xD8, 0x07, 0x3C, 0x4A, 0xC4, 0xCD, 0x8C, 0x68, 0x3D, 0xCD, 0xCD, 0xBD, 0x9D, 0xCE, 0xB5, 0x2A, 0xF9, 0x63, 0x3D, 0xA9, 0x54, 0x0A, 0x2E, 0x4C, 0xE1, 0x60, 0x4B, 0xD0, 0xC9, 0xEB, 0xEF, 0x31, 0x65, 0x70, 0xB9, 0x0E, 0x06, 0x3B, 0x3D, 0x42, 0x4C, 0x6E, 0x8D, 0x2C, 0xD4, 0x71, 0x29, 0x76, 0xB7, 0xDD, 0x8C, 0xDA, 0xE7, 0xE3, 0x96, 0xA7, 0xAA, 0xF8, 0xCA, 0x05, 0xE8, 0xA7, 0x0A, 0xDD, 0x01, 0x49, 0xBD, 0xF1, 0xA5, 0xE8, 0x16, 0x22, 0xEE, 0x47, 0x1F, 0xEF, 0x28, 0x48, 0x87, 0xA9, 0x2D, 0xFC, 0x4E, 0xD5, 0xA5, 0x98, 0xB1, 0xFE, 0x1B, 0xEB, 0xA9, 0x06, 0x3C, 0x76, 0xD9, 0xAA, 0x0E, 0x9C, 0x60, 0xFC, 0xE9, 0x77, 0x9D, 0x7F, 0x67, 0xAC, 0xF5, 0xC7, 0x49, 0x12, 0xFD, 0x76, 0xAC, 0xD2, 0x54, 0xDB, 0x73, 0x41, 0x10, 0x1F, 0x04, 0x3F, 0xD0, 0x6F, 0xE0, 0x80, 0x24, 0xCC, 0xEE, 0xBF, 0x25, 0x9D, 0x0D, 0x5A, 0x2A, 0x1C, 0xC5, 0xD4, 0xE3, 0x5D, 0x3A, 0xC1, 0x86, 0xD3, 0xD4, 0x52, 0x1C, 0x4C, 0xBF, 0x31, 0xEB, 0x54, 0xCA, 0x4C, 0x06, 0x50, 0x52, 0x87, 0xD4, 0x9D, 0x4A, 0x4B, 0x22, 0xE1, 0x4A, 0xE9, 0x4D, 0x05, 0xA8, 0x57, 0xEC, 0xF8, 0x90, 0xF8, 0x58, 0xC3, 0x8B, 0x3A, 0x0F, 0x88, 0x36, 0xF4, 0xE5, 0x44, 0x10, 0x80, 0x68, 0x86, 0x1D, 0xAE, 0x90, 0x20, 0x03, 0x22, 0x2D, 0x44, 0xBF, 0xAB, 0x2B, 0xA1, 0x14, 0xAD, 0x6B, 0x40, 0x57, 0xDB, 0xBB, 0xDA, 0x09, 0x4C, 0x51, 0x26, 0x9B, 0xE3, 0xD9, 0xF9, 0xE1, 0xBC, 0xF1, 0xF1, 0xCD, 0x30, 0xB4, 0xF5, 0x39, 0xD0, 0xBC, 0xF7, 0x98, 0x05, 0xAF, 0xA8, 0x33, 0x4B, 0xC1, 0x16, 0x0F, 0xF2, 0xC2, 0x79, 0x96, 0xEC, 0xBE, 0xA9, 0xF5, 0x55, 0x7C, 0x82, 0x95, 0x73 -}; - -// DLP -static const unsigned char dlp_fw21_dev_hdrpub[0x100] = -{ - 0xB3, 0x16, 0x68, 0xF1, 0xED, 0x59, 0xC8, 0x7F, 0xC6, 0x50, 0x21, 0xFE, 0x36, 0x7C, 0x55, 0xE7, 0x07, 0xF9, 0x1D, 0x1B, 0xF5, 0xB1, 0x2A, 0x6B, 0x3A, 0xDE, 0x2D, 0x4C, 0x51, 0xCD, 0x4C, 0x9F, 0xEE, 0x1D, 0xE4, 0xE8, 0xF0, 0xFD, 0x09, 0x8E, 0x0F, 0x92, 0x5F, 0xDB, 0x9C, 0x5C, 0x15, 0x55, 0x1A, 0x4D, 0x04, 0x8C, 0xB0, 0xA4, 0x88, 0x97, 0xC4, 0xD5, 0x92, 0x04, 0x42, 0x33, 0x84, 0x81, 0x06, 0xD6, 0xF2, 0x17, 0xDE, 0x83, 0x17, 0x50, 0xD0, 0x47, 0x61, 0x14, 0x0D, 0xB7, 0xC7, 0xA0, 0xC1, 0x8B, 0x82, 0x47, 0x13, 0xEE, 0x76, 0xA2, 0xA3, 0x8D, 0xCE, 0x55, 0xC1, 0xF3, 0x7A, 0xEA, 0x91, 0xE1, 0xB9, 0x2F, 0x8F, 0x9B, 0xC3, 0x7B, 0x51, 0x2F, 0xE7, 0xAD, 0x93, 0x9C, 0xFD, 0xDF, 0x19, 0xC8, 0x6C, 0x24, 0xC2, 0xE2, 0x91, 0x97, 0x1F, 0xEB, 0x4B, 0xD4, 0x46, 0x6C, 0x06, 0x93, 0xAF, 0xF5, 0x5E, 0x8F, 0x77, 0x25, 0xC4, 0x28, 0xC0, 0x82, 0x4A, 0x78, 0xE9, 0x14, 0x08, 0xC3, 0xC3, 0x58, 0x24, 0x44, 0x2D, 0x2B, 0xA7, 0xEE, 0x28, 0xEF, 0x1B, 0x6D, 0xAA, 0x9C, 0xED, 0x7F, 0x35, 0xCE, 0x86, 0x5C, 0x6B, 0x8A, 0x23, 0xD3, 0x9D, 0x05, 0x8F, 0xD2, 0x41, 0x93, 0x1D, 0x1D, 0x7E, 0xB0, 0x46, 0x23, 0x63, 0x07, 0xEA, 0x5F, 0x26, 0xE3, 0x81, 0x27, 0xB3, 0x95, 0xB1, 0x93, 0x59, 0xD4, 0x1A, 0xB8, 0x73, 0xD0, 0x09, 0x95, 0x2B, 0xE8, 0x8B, 0xE2, 0x73, 0x5F, 0x34, 0xB9, 0x98, 0x82, 0xF0, 0x11, 0xC6, 0x8F, 0x12, 0x4D, 0x09, 0x57, 0x10, 0x97, 0x22, 0x0E, 0xC8, 0x7D, 0x40, 0xC1, 0x9D, 0x12, 0x1F, 0x71, 0xFE, 0x1E, 0x1A, 0x8C, 0x3F, 0x56, 0xAC, 0x43, 0xC3, 0x66, 0x0C, 0x81, 0xAE, 0xC1, 0x8F, 0x68, 0xFF, 0x87, 0x07, 0x3C, 0xCD, 0x0A, 0x23, 0xDE, 0xBA, 0x9B -}; - -static const unsigned char dlp_fw21_dev_hdrpvt[0x100] = -{ - 0x77, 0xC2, 0x7A, 0xB7, 0x9E, 0x13, 0xB6, 0x62, 0xCC, 0x09, 0x76, 0x51, 0xFB, 0xB9, 0xB5, 0xF0, 0x63, 0x82, 0x91, 0x96, 0xCA, 0xFC, 0x88, 0xF3, 0x60, 0x50, 0x87, 0x56, 0x4C, 0x35, 0xD0, 0x11, 0xFB, 0x38, 0x7E, 0x85, 0xCF, 0xF2, 0x46, 0xDB, 0x7B, 0x4A, 0x55, 0x54, 0x15, 0x01, 0xF7, 0x3A, 0x0B, 0xF6, 0x89, 0x1E, 0x54, 0x5A, 0x13, 0x05, 0xFB, 0x19, 0x1F, 0x26, 0x3D, 0xE7, 0x19, 0xAA, 0xF7, 0x19, 0xF2, 0x97, 0x47, 0xB3, 0xBE, 0x79, 0xCA, 0x6E, 0x91, 0x5A, 0xC9, 0xB9, 0xA6, 0x83, 0xB8, 0x2A, 0x45, 0x1A, 0xA7, 0x17, 0x86, 0xBA, 0x48, 0x49, 0x62, 0x3C, 0x33, 0x11, 0x51, 0x97, 0x5F, 0xAA, 0xE5, 0x1E, 0x0B, 0x19, 0x0C, 0xE6, 0x80, 0x6A, 0x5A, 0xB1, 0xD6, 0xCE, 0xDB, 0x6E, 0xC0, 0x5D, 0x29, 0x04, 0x84, 0x56, 0xE3, 0x29, 0x7E, 0xAC, 0xE8, 0xEE, 0xB1, 0x91, 0x37, 0xEB, 0x98, 0x9C, 0xBD, 0x02, 0x6A, 0x78, 0x61, 0xB0, 0x79, 0x1A, 0x9F, 0x30, 0x86, 0xF6, 0x71, 0x5A, 0x5A, 0x12, 0xA1, 0x9E, 0xA1, 0x68, 0x03, 0xE5, 0x95, 0xA8, 0x38, 0x58, 0x87, 0x08, 0x57, 0x35, 0x32, 0x47, 0x3B, 0xFC, 0x02, 0x6F, 0xCE, 0x55, 0x61, 0xA3, 0x2A, 0x6B, 0x2F, 0xF8, 0xEE, 0x8D, 0xFA, 0x43, 0x33, 0x02, 0x63, 0x47, 0x02, 0x78, 0x5A, 0x7F, 0x64, 0x07, 0x92, 0xB7, 0x7C, 0x09, 0x7C, 0xFE, 0x2D, 0x1C, 0xFC, 0x77, 0x9F, 0x19, 0x20, 0xDD, 0x6D, 0x4C, 0xFE, 0x49, 0x09, 0x47, 0xCA, 0x9B, 0x1C, 0x8C, 0x1F, 0x37, 0xAC, 0x14, 0x85, 0x56, 0xC0, 0xFD, 0xD6, 0x01, 0xB3, 0x40, 0xA3, 0x1A, 0x32, 0x78, 0xA0, 0xDD, 0x21, 0x75, 0xBF, 0x24, 0xD2, 0x93, 0x85, 0xED, 0x22, 0xAD, 0x99, 0x91, 0x87, 0x4A, 0xEC, 0xC0, 0x6C, 0x71, 0x00, 0x76, 0x08, 0x23, 0xA2, 0xF3, 0xCF, 0x61 -}; - -static const unsigned char dlp_fw21_dev_acexsig[0x100] = -{ - 0xAC, 0xE2, 0xA7, 0xC3, 0x00, 0xDE, 0xE8, 0xE9, 0xE0, 0x03, 0xB3, 0x54, 0x08, 0xA8, 0xF8, 0x3A, 0x2E, 0xD8, 0x10, 0x6B, 0xEC, 0xDC, 0x4E, 0xEE, 0x62, 0x10, 0x71, 0x49, 0xD4, 0x43, 0xB1, 0x0E, 0x6B, 0x8C, 0xD7, 0x54, 0xD5, 0x62, 0x28, 0x3F, 0xAA, 0xDE, 0xA9, 0x7D, 0xED, 0x37, 0x7C, 0xE7, 0x89, 0x0B, 0x02, 0xB2, 0x72, 0x4B, 0x17, 0xDB, 0xE2, 0xD3, 0x7C, 0x94, 0x12, 0x3F, 0x2E, 0xA1, 0x08, 0x99, 0xCC, 0x7F, 0x93, 0xE6, 0x38, 0xC9, 0x37, 0x84, 0xD7, 0x11, 0x9D, 0x02, 0x4D, 0x66, 0xB4, 0x70, 0x9F, 0xD8, 0xC6, 0xDD, 0xD5, 0x13, 0x52, 0xF0, 0xA6, 0x78, 0x8C, 0x8E, 0x15, 0xA0, 0xA1, 0xF3, 0xC4, 0xC3, 0x48, 0x45, 0xA5, 0xBE, 0xC9, 0x7A, 0x8B, 0xD3, 0x95, 0xA5, 0x4C, 0xF1, 0xB3, 0x0C, 0x6C, 0x76, 0xA7, 0x57, 0xA1, 0x77, 0xDF, 0x2F, 0xC8, 0x06, 0xA6, 0x0D, 0x1A, 0x09, 0xE4, 0x38, 0x64, 0x07, 0xBE, 0x6A, 0xD2, 0xA0, 0xC0, 0xEC, 0x09, 0x64, 0x9F, 0x0D, 0x93, 0x0C, 0x89, 0xA2, 0x71, 0xD6, 0xC6, 0xC2, 0x54, 0x79, 0x2A, 0xA4, 0x31, 0x28, 0x24, 0x1A, 0xF3, 0x56, 0x78, 0x63, 0x99, 0x97, 0xA5, 0xCE, 0x8F, 0x52, 0x7A, 0x79, 0x51, 0xEE, 0x4C, 0x8B, 0x00, 0x9D, 0x5C, 0x3E, 0xD5, 0xAA, 0x24, 0x9C, 0x94, 0xC6, 0xA3, 0x99, 0x1B, 0x2D, 0xD4, 0xFF, 0xB4, 0x25, 0x73, 0x13, 0x33, 0x9F, 0x03, 0x6F, 0x1E, 0x75, 0xC4, 0x70, 0xF4, 0x07, 0x4F, 0x18, 0xFE, 0xBD, 0x8F, 0x2C, 0x9B, 0x33, 0xD4, 0x30, 0xA7, 0x18, 0x4A, 0xF1, 0xA4, 0xDD, 0x78, 0x41, 0xA0, 0xB8, 0x02, 0x8D, 0x51, 0x96, 0xBE, 0xE7, 0x17, 0x94, 0x66, 0x65, 0x27, 0xF7, 0x69, 0x48, 0x7E, 0xA9, 0x08, 0x71, 0x20, 0x76, 0xB7, 0x8E, 0xD2, 0xBF, 0x5C, 0x7E, 0x5E, 0x06, 0x45, 0xAB, 0x7E, 0x2E -}; - -/* CTR_SDK 5 (5.2.2) */ -// APP -static const unsigned char app_fw23_dev_hdrpub[0x100] = -{ - 0xED, 0xD8, 0x3B, 0x9E, 0x78, 0x26, 0xD2, 0x96, 0x73, 0x6B, 0x7D, 0xB0, 0x5E, 0x01, 0x51, 0x63, 0x0F, 0x4B, 0xE6, 0x87, 0xCC, 0x4A, 0x03, 0x84, 0xB2, 0x65, 0x77, 0xB2, 0xA2, 0x6F, 0x20, 0x7A, 0x3B, 0xC3, 0xF3, 0xCC, 0x37, 0x7C, 0x1F, 0x77, 0xC8, 0x3D, 0xE2, 0xA9, 0x19, 0x4E, 0x7C, 0xBB, 0xAC, 0x2D, 0x7D, 0x7D, 0xE1, 0x35, 0xF8, 0x44, 0x2E, 0xC5, 0xB7, 0x50, 0x8D, 0xA1, 0x18, 0x34, 0x45, 0x20, 0xD4, 0x29, 0x0E, 0x79, 0xA7, 0xE1, 0x97, 0x1A, 0xE0, 0x39, 0x1D, 0x55, 0xB4, 0x2A, 0xDB, 0xD4, 0x77, 0x56, 0xC8, 0x73, 0xC8, 0x63, 0x04, 0xBA, 0x4C, 0x61, 0x48, 0x2E, 0x52, 0xD5, 0xC5, 0x21, 0x83, 0x4C, 0xBE, 0xE4, 0x38, 0x44, 0xD4, 0x7D, 0x8E, 0xDE, 0x17, 0x4A, 0x6F, 0xBD, 0xB0, 0x2B, 0xF7, 0xA8, 0xF3, 0xD3, 0xB7, 0xED, 0xF2, 0xE6, 0x4C, 0x5B, 0x83, 0x3E, 0x68, 0x67, 0xAF, 0x45, 0xE1, 0xF3, 0xAA, 0xFF, 0x5E, 0xB0, 0x0B, 0x39, 0x8E, 0xCF, 0xA7, 0x28, 0x88, 0xEA, 0x8B, 0x6F, 0x39, 0x39, 0x9F, 0xB1, 0x51, 0xCA, 0xC3, 0xFD, 0x46, 0x02, 0x92, 0x62, 0xB9, 0x98, 0x95, 0xAA, 0xCA, 0x76, 0x75, 0xE7, 0xE8, 0x88, 0x1C, 0x59, 0x9F, 0xD8, 0xCA, 0x99, 0x76, 0x3D, 0x3B, 0x56, 0xFA, 0xE4, 0x5A, 0x2F, 0x12, 0x07, 0x14, 0xA7, 0x81, 0x3D, 0x3B, 0xC0, 0xA7, 0x10, 0xE8, 0x9E, 0x69, 0x41, 0x55, 0x3F, 0xD8, 0x2B, 0x9B, 0x09, 0x1C, 0xE3, 0x94, 0x29, 0xD5, 0x35, 0xB2, 0xC7, 0x04, 0x16, 0x8A, 0xBA, 0x0C, 0x77, 0x78, 0x69, 0xAC, 0x3D, 0xE8, 0x92, 0xD2, 0x78, 0x88, 0x51, 0xAC, 0x80, 0x03, 0xC1, 0x23, 0xEC, 0xDF, 0x0C, 0x80, 0x09, 0x2D, 0xBC, 0x74, 0x22, 0x3F, 0xF2, 0xE3, 0xF0, 0x09, 0x67, 0xA7, 0xD6, 0x36, 0xA8, 0xE4, 0x3F, 0xF5, 0xEC, 0x3D, 0x8B -}; - -static const unsigned char app_fw23_dev_acexsig[0x100] = -{ - 0x1C, 0x2A, 0x20, 0x20, 0xE1, 0x8C, 0x57, 0x67, 0xA7, 0x25, 0x6C, 0x77, 0x72, 0xAE, 0xC9, 0x6D, 0xCD, 0xB3, 0xA4, 0x0E, 0xCE, 0x8A, 0x62, 0x46, 0xB7, 0x99, 0xEB, 0x87, 0xDC, 0xCD, 0x3A, 0x1C, 0x10, 0x18, 0x2C, 0x59, 0x23, 0x81, 0x8A, 0xB9, 0x66, 0x9A, 0x20, 0x42, 0x5E, 0x38, 0xE2, 0x53, 0x92, 0xB1, 0xDD, 0x38, 0x6E, 0x01, 0xF4, 0x56, 0xEC, 0x43, 0x22, 0x9B, 0x15, 0x36, 0xE4, 0x2C, 0x3E, 0x6D, 0x1B, 0xAE, 0xAA, 0x71, 0xA8, 0x0C, 0x9A, 0xC4, 0x5E, 0x19, 0x93, 0xD6, 0x59, 0x23, 0xCD, 0xAD, 0xA5, 0xEF, 0xFD, 0x7D, 0x05, 0x73, 0xAF, 0x29, 0xC4, 0x85, 0x0B, 0x33, 0x9B, 0xF4, 0x5D, 0x31, 0x8E, 0xE8, 0x6C, 0xD1, 0x39, 0xBB, 0x03, 0x8B, 0xD9, 0x77, 0x30, 0xA4, 0x1A, 0x63, 0x50, 0xA0, 0xB9, 0x7B, 0x46, 0x10, 0x6A, 0x9A, 0x31, 0x71, 0x45, 0x72, 0x8C, 0x10, 0x6B, 0xE9, 0xBE, 0xC7, 0x0E, 0x2B, 0x4A, 0xF4, 0x29, 0x0B, 0xAA, 0x91, 0x76, 0xF8, 0xB3, 0x74, 0x57, 0xF8, 0x9B, 0xF4, 0xBE, 0x50, 0x22, 0x46, 0x93, 0xF1, 0x80, 0x64, 0xC1, 0x50, 0x77, 0x2A, 0x2A, 0x70, 0x74, 0x0C, 0xB2, 0xDC, 0x77, 0x34, 0x84, 0x83, 0x4E, 0x5B, 0x47, 0x30, 0x52, 0xF3, 0xEC, 0xC6, 0xB5, 0x93, 0xF3, 0x77, 0x0D, 0x10, 0x86, 0x65, 0x35, 0xC9, 0x84, 0x07, 0x43, 0x51, 0x00, 0x92, 0x2F, 0xA7, 0x75, 0x02, 0x23, 0xEE, 0x0F, 0x9E, 0x69, 0x5A, 0xF9, 0x9C, 0x0E, 0x17, 0x05, 0x94, 0x2A, 0xE9, 0x79, 0x82, 0x2C, 0x68, 0x3E, 0xCD, 0x26, 0xE6, 0x9E, 0x18, 0x99, 0x9A, 0xA0, 0xA7, 0x95, 0xA3, 0xBB, 0xB5, 0x9D, 0x86, 0x6E, 0x99, 0xFD, 0xC4, 0x1F, 0x49, 0x78, 0x2D, 0x4A, 0x8F, 0xAA, 0x77, 0x48, 0x6F, 0x69, 0x6A, 0x71, 0xA7, 0x19, 0x67, 0x56, 0x5B, 0x10, 0x27, 0x07, 0x7F -}; - -/* CTR_SDK 6 (6.?.?) */ -// FIRM -static const unsigned char firm_fw26_dev_hdrpub[0x100] = -{ - 0xC9, 0x3C, 0x30, 0xE3, 0x64, 0xFD, 0x70, 0xBB, 0x98, 0xB0, 0x7B, 0x59, 0x4F, 0x6A, 0xC5, 0x1A, 0x6E, 0xF7, 0x89, 0x6E, 0xF9, 0x9C, 0x58, 0xC4, 0x34, 0x51, 0xAF, 0x0B, 0x41, 0x0D, 0x76, 0x7D, 0xE5, 0xF8, 0x9C, 0x2F, 0x08, 0x52, 0x75, 0xE2, 0x83, 0x2D, 0x6B, 0x6F, 0x1D, 0xC6, 0x13, 0x41, 0xD6, 0x91, 0x78, 0xF8, 0xD6, 0xC6, 0x57, 0x58, 0xC4, 0xE4, 0x3D, 0x3C, 0x19, 0xEB, 0x81, 0x77, 0x6C, 0xCF, 0x4A, 0xCD, 0x87, 0x9A, 0x2A, 0x82, 0xCB, 0x94, 0xE0, 0xAB, 0x93, 0x49, 0x00, 0x48, 0x1B, 0x6B, 0x0E, 0x62, 0x94, 0xE4, 0x70, 0xAF, 0x16, 0x0C, 0x93, 0x3A, 0x6B, 0x16, 0x20, 0x93, 0xDF, 0x84, 0x26, 0x88, 0x1B, 0x61, 0x47, 0x2C, 0xAE, 0x58, 0x07, 0x5A, 0xE1, 0xED, 0x61, 0x99, 0x15, 0x47, 0x0F, 0x83, 0x69, 0xAF, 0x89, 0xBB, 0x18, 0xC7, 0x56, 0x9C, 0xF5, 0x00, 0xD8, 0x76, 0xFC, 0x2F, 0x56, 0x6D, 0xD7, 0xD2, 0x0F, 0x41, 0xF4, 0xB6, 0xC3, 0x46, 0x37, 0x24, 0xAE, 0x9B, 0x1A, 0xD7, 0xA0, 0x29, 0x48, 0xD0, 0x6C, 0x7F, 0x54, 0x7D, 0x0E, 0xA7, 0xDA, 0x7E, 0x39, 0xF7, 0xCA, 0xB6, 0x51, 0x37, 0x9F, 0x34, 0x84, 0x2F, 0x41, 0xF3, 0x4A, 0x6F, 0xDC, 0x75, 0x83, 0x74, 0x8C, 0x16, 0x28, 0xB2, 0x3D, 0x7B, 0xF1, 0x91, 0xEC, 0x09, 0x25, 0x5F, 0x89, 0x4F, 0x81, 0xA4, 0xD5, 0x9F, 0xD1, 0xD4, 0xDC, 0x0D, 0x96, 0x85, 0x34, 0x01, 0x4C, 0xFE, 0x5F, 0x1A, 0xA3, 0x0B, 0x5B, 0xCF, 0xCF, 0xE1, 0x50, 0x9C, 0x89, 0x2E, 0x06, 0xB4, 0x1A, 0xED, 0x22, 0x76, 0xDA, 0x3D, 0xB5, 0x3E, 0xF5, 0x9B, 0xA7, 0xE2, 0x0E, 0x9D, 0xBD, 0xE0, 0x68, 0xB5, 0xB5, 0x8A, 0xAA, 0x98, 0xF9, 0x07, 0xF1, 0xB4, 0x96, 0xC0, 0xA1, 0xB9, 0xE8, 0x56, 0xAC, 0xB7, 0x39, 0x11, 0x14, 0x0B -}; - -static const unsigned char firm_fw26_dev_acexsig[0x100] = -{ - 0xB0, 0xEB, 0x0B, 0xE4, 0x81, 0x5F, 0x4C, 0x41, 0x74, 0x13, 0x8D, 0x8C, 0x2D, 0x07, 0xDB, 0x67, 0xDB, 0x0A, 0xD3, 0xE9, 0xD9, 0x24, 0x35, 0xA7, 0x2A, 0x7A, 0x7D, 0x22, 0xAD, 0xE5, 0x5B, 0xAC, 0x6B, 0x08, 0x9E, 0xC3, 0x19, 0x5D, 0x28, 0x13, 0x71, 0xE0, 0x4E, 0xF8, 0xA4, 0x2F, 0xF9, 0x41, 0xB4, 0x9F, 0x02, 0x5B, 0xBD, 0xBB, 0x33, 0xFD, 0xF8, 0x57, 0xE5, 0x36, 0x63, 0xB4, 0x8D, 0x05, 0x8F, 0xA5, 0x87, 0x54, 0x62, 0xE5, 0x9E, 0x31, 0x61, 0xD4, 0x3C, 0xFB, 0xB5, 0xE9, 0x76, 0x65, 0x4E, 0xF2, 0x08, 0x44, 0x6D, 0x2B, 0x54, 0x6E, 0x4E, 0xAB, 0xF3, 0xA4, 0xF1, 0xA5, 0xEF, 0x43, 0xFD, 0x02, 0x09, 0xE5, 0x11, 0x2B, 0x8A, 0xB5, 0xEC, 0x20, 0xDE, 0x28, 0xA7, 0x5C, 0x16, 0x71, 0x1E, 0x0A, 0x1A, 0x1B, 0xED, 0x4B, 0x32, 0x0E, 0x3E, 0x86, 0x37, 0x86, 0x64, 0x10, 0xE7, 0x08, 0x18, 0x06, 0x58, 0x96, 0xFB, 0x84, 0x21, 0xA4, 0x46, 0x7A, 0x59, 0x7A, 0x1E, 0x08, 0x1D, 0x38, 0x28, 0xAF, 0xB2, 0x26, 0xB3, 0xAB, 0x00, 0x2C, 0x85, 0x3A, 0xFA, 0x5F, 0x3B, 0xC4, 0xA6, 0x22, 0xBB, 0x79, 0x74, 0x5A, 0x44, 0xCB, 0x9E, 0x8D, 0x6D, 0x18, 0xD3, 0xC1, 0x74, 0x6D, 0xA1, 0x8B, 0xAC, 0xF4, 0x15, 0x9B, 0x07, 0x52, 0xF5, 0x04, 0x26, 0x58, 0x82, 0x05, 0xF4, 0xE9, 0x54, 0xAD, 0x58, 0xF1, 0xE1, 0x02, 0x9B, 0xE2, 0x15, 0x67, 0x62, 0x3B, 0x95, 0xF2, 0xB7, 0xDC, 0x27, 0x94, 0x10, 0xAC, 0x5B, 0xC9, 0x01, 0xBE, 0x8F, 0x46, 0x92, 0x51, 0x22, 0x89, 0x06, 0xB5, 0x78, 0x52, 0x77, 0x83, 0x79, 0x22, 0xB8, 0x7B, 0x10, 0x7E, 0xF9, 0x7F, 0xF2, 0xA2, 0x53, 0x96, 0x89, 0x05, 0x60, 0x9C, 0x7C, 0xAF, 0x02, 0x29, 0x71, 0xB9, 0xEC, 0x62, 0x38, 0x2D, 0xA4, 0x99, 0x04, 0x78 -}; - -static const unsigned char firm_fw26_2_dev_hdrpub[0x100] = -{ - 0xB8, 0xF9, 0x95, 0xFA, 0xC9, 0xB4, 0x07, 0x9F, 0xCC, 0xF1, 0xAA, 0x82, 0xBB, 0xE3, 0x29, 0x23, 0xA8, 0xF0, 0xB9, 0x9A, 0x2B, 0x27, 0xCA, 0x65, 0x51, 0xAA, 0x05, 0x40, 0x0F, 0x9E, 0x46, 0xBB, 0xE2, 0xF2, 0x72, 0xA6, 0x76, 0xBE, 0x3D, 0xA8, 0xDA, 0x53, 0x4D, 0xD3, 0x95, 0xC4, 0x01, 0x19, 0xD5, 0xC7, 0x3B, 0xF6, 0x12, 0xE7, 0x08, 0x32, 0x2C, 0x66, 0xD9, 0xC1, 0xB6, 0x7D, 0x03, 0x99, 0xA2, 0x89, 0x40, 0x7E, 0x77, 0x0C, 0xF7, 0xD9, 0x29, 0x50, 0x9C, 0x12, 0xB5, 0xBD, 0xED, 0x74, 0x70, 0x30, 0x39, 0x77, 0x49, 0xCE, 0x4F, 0x4F, 0x42, 0xCE, 0xD3, 0xC9, 0xD0, 0xE3, 0x14, 0x13, 0x6E, 0xE3, 0x59, 0x7A, 0x13, 0xE6, 0xEA, 0x57, 0xA1, 0xA3, 0xEC, 0xF4, 0x03, 0x18, 0x07, 0x0D, 0x63, 0xD5, 0x4B, 0x4E, 0xE7, 0x7A, 0x1E, 0x5E, 0x07, 0x0E, 0x4E, 0xAD, 0x23, 0xCD, 0x38, 0x0B, 0xC0, 0x5E, 0x20, 0xD5, 0x98, 0x57, 0xD5, 0xD8, 0xFD, 0x9D, 0xFF, 0x9F, 0xB0, 0xF7, 0xD8, 0xE5, 0xEB, 0xA5, 0xA2, 0xD9, 0xBF, 0x9E, 0x0B, 0xD5, 0xD5, 0xD9, 0xCE, 0x12, 0xAE, 0x8F, 0xD1, 0xCC, 0xD1, 0x8A, 0x04, 0xA1, 0x06, 0x0A, 0x55, 0xDE, 0x0B, 0xCE, 0x7B, 0x11, 0xD7, 0x0F, 0xD6, 0xD9, 0xDF, 0x54, 0x6D, 0xDF, 0x76, 0x2C, 0xB8, 0xCF, 0x85, 0x54, 0xDB, 0xDB, 0x47, 0x35, 0x28, 0x90, 0x7C, 0x8E, 0x59, 0x7D, 0x25, 0x7C, 0x95, 0xA8, 0x9A, 0xF0, 0x4D, 0xE3, 0x5E, 0x0B, 0xE0, 0x7E, 0xAF, 0x0F, 0xEA, 0xC9, 0x06, 0x81, 0x90, 0xE3, 0xDE, 0x42, 0x9C, 0x99, 0x8F, 0x7A, 0x31, 0xFB, 0x83, 0x05, 0x8B, 0xBB, 0xE9, 0x88, 0x0B, 0x31, 0x44, 0x1C, 0x21, 0x02, 0x66, 0xA2, 0xC4, 0x12, 0xAF, 0x4A, 0xB8, 0x01, 0xB6, 0xE1, 0x3B, 0x73, 0x29, 0x78, 0x6E, 0xB5, 0x39, 0x41, 0x37, 0x07 -}; - -static const unsigned char firm_fw26_2_dev_acexsig[0x100] = -{ - 0x57, 0x9D, 0xD4, 0xA1, 0xE8, 0x8C, 0x59, 0x0C, 0xE1, 0xB6, 0x9E, 0x38, 0xD0, 0x45, 0xF7, 0x6F, 0xDD, 0xAE, 0xC6, 0xD1, 0xB1, 0xDE, 0x45, 0xBA, 0xD5, 0x69, 0xF3, 0x8B, 0x54, 0xB0, 0xAA, 0x43, 0x1C, 0x82, 0x58, 0x1D, 0x59, 0xB5, 0xC8, 0xF7, 0xB1, 0x80, 0x3A, 0x99, 0x3E, 0x72, 0xCB, 0x43, 0x5F, 0xE5, 0x96, 0x9C, 0x5B, 0xAE, 0x8C, 0xA3, 0x78, 0x6B, 0xC2, 0xC1, 0xC7, 0x50, 0x36, 0xCB, 0x87, 0xB5, 0x7D, 0x04, 0x74, 0x92, 0xE4, 0x92, 0x94, 0x77, 0x4F, 0x2B, 0x64, 0xDB, 0x6F, 0xE0, 0x75, 0x5A, 0xB5, 0x7B, 0x19, 0xE5, 0x57, 0x4B, 0x9C, 0x0D, 0x80, 0x64, 0xC9, 0x96, 0x1A, 0x5D, 0xAB, 0xEE, 0xB9, 0xAC, 0x3D, 0x2F, 0x60, 0x6F, 0xA2, 0xBC, 0xF9, 0xDD, 0x23, 0x7F, 0xA3, 0x3E, 0xE1, 0x33, 0x31, 0x60, 0x40, 0xF4, 0x26, 0x77, 0x6E, 0xDD, 0xD8, 0x98, 0xD0, 0xE7, 0x01, 0x13, 0x08, 0xC6, 0x0F, 0xB9, 0xF5, 0x52, 0x7A, 0xE4, 0x48, 0xCF, 0x3F, 0x0C, 0x37, 0x38, 0x69, 0x93, 0xFF, 0x12, 0x5B, 0x35, 0x9F, 0x4B, 0x1D, 0x36, 0x53, 0x0C, 0xE6, 0x66, 0xF3, 0xBB, 0xBC, 0x32, 0xF5, 0x8D, 0x74, 0x35, 0x2E, 0xCB, 0xFC, 0x03, 0x02, 0xED, 0x9B, 0xDD, 0xBE, 0x3F, 0xF3, 0xA7, 0xB5, 0xFF, 0x38, 0x43, 0x89, 0xDD, 0x93, 0x43, 0x7A, 0x6E, 0xE0, 0xCF, 0x5E, 0xBF, 0x0C, 0xD5, 0x25, 0x71, 0x98, 0xB2, 0x76, 0xC7, 0x5B, 0x18, 0xC0, 0x71, 0xCB, 0x0C, 0xDB, 0x26, 0x52, 0xB1, 0x55, 0xC3, 0x97, 0x43, 0x1D, 0x33, 0x41, 0x32, 0x2A, 0xD8, 0xB6, 0x37, 0xEA, 0x96, 0x2B, 0xBA, 0x05, 0xA8, 0xA6, 0x3F, 0x47, 0x82, 0xBF, 0x85, 0x3E, 0xDF, 0x8F, 0xEE, 0xFA, 0xD3, 0x68, 0xEE, 0xAE, 0xEB, 0x0E, 0x0D, 0x5D, 0xB1, 0x99, 0xE8, 0x76, 0x70, 0xCF, 0xA4, 0x7E, 0x1E, 0xE6, 0x65 -}; - -static const unsigned char firm_fw26_3_dev_hdrpub[0x100] = -{ - 0xAF, 0xC4, 0x87, 0x3F, 0x38, 0xFF, 0x0D, 0x27, 0xEF, 0x59, 0xB7, 0x5D, 0x71, 0xD4, 0x17, 0x5E, 0x37, 0xB9, 0xEC, 0xD9, 0x2B, 0x94, 0xCD, 0x3F, 0xD3, 0x01, 0x24, 0xC6, 0x67, 0x32, 0x67, 0xAA, 0x09, 0x12, 0xFF, 0x72, 0x74, 0x35, 0x6B, 0x41, 0xF4, 0xB5, 0xDD, 0x27, 0x8B, 0xB3, 0xA7, 0xA8, 0x20, 0x5C, 0x3F, 0x33, 0x10, 0xFF, 0x19, 0x60, 0xB0, 0x04, 0x31, 0x9F, 0xD6, 0x57, 0xB3, 0x41, 0xBC, 0x4C, 0xAC, 0xD8, 0xB4, 0x5B, 0x90, 0xDC, 0x85, 0xED, 0x2C, 0x75, 0x06, 0xD4, 0xA0, 0x0F, 0x90, 0xBB, 0x42, 0xA6, 0x17, 0x0D, 0xEB, 0x15, 0x53, 0xFE, 0x7A, 0x10, 0x57, 0x09, 0x9A, 0x5F, 0x9F, 0x94, 0x91, 0xCA, 0x7B, 0x55, 0x7D, 0xED, 0x3B, 0x80, 0x00, 0xD3, 0x5D, 0xC3, 0x0A, 0x9A, 0x5A, 0xD3, 0x80, 0xAD, 0x03, 0xB3, 0x81, 0x1C, 0xE3, 0xBC, 0x0D, 0x7C, 0x06, 0x8F, 0xE8, 0xEB, 0xCE, 0x96, 0x19, 0xA1, 0xF5, 0x1B, 0xE0, 0x87, 0x17, 0xA8, 0x5C, 0x76, 0x0D, 0xC3, 0xD2, 0xBC, 0x8F, 0x57, 0x02, 0xE1, 0x63, 0xFC, 0x14, 0x98, 0x3F, 0xFC, 0x67, 0x09, 0xB0, 0x99, 0xB6, 0x7D, 0x84, 0xD1, 0x39, 0xE3, 0xCE, 0x54, 0x00, 0xBF, 0x33, 0x78, 0xB2, 0xC9, 0x91, 0xAB, 0xF1, 0x05, 0x88, 0xFF, 0x6E, 0x28, 0xFB, 0x18, 0x08, 0x61, 0x9E, 0x37, 0xED, 0x21, 0x8F, 0x73, 0x82, 0x4D, 0x6C, 0x57, 0xEC, 0x85, 0xE4, 0x8B, 0xB9, 0x89, 0x6B, 0x2B, 0x70, 0x25, 0xE9, 0x00, 0x9A, 0x46, 0x00, 0x22, 0x9C, 0xC0, 0x1C, 0x7F, 0xAC, 0x92, 0x1B, 0x17, 0x3B, 0xDA, 0xF3, 0xA3, 0x60, 0x99, 0xF2, 0x75, 0x7C, 0x52, 0xC5, 0xAE, 0xF7, 0x45, 0xE3, 0xE5, 0x5B, 0x6D, 0x2D, 0x47, 0x39, 0x97, 0xFE, 0x2B, 0x6D, 0x7D, 0xBD, 0x35, 0xC8, 0xB6, 0x23, 0x7B, 0x2E, 0xB2, 0x00, 0xD2, 0x61, 0xC5 -}; - -static const unsigned char firm_fw26_3_dev_acexsig[0x100] = -{ - 0x85, 0x2B, 0xF3, 0xF5, 0x26, 0x11, 0xFA, 0x95, 0xF8, 0x32, 0x2A, 0xAE, 0xCB, 0x96, 0x71, 0x92, 0xCF, 0x81, 0xE4, 0xF7, 0xC9, 0xBB, 0x27, 0x14, 0x21, 0x08, 0x5B, 0x93, 0xA2, 0x92, 0x86, 0x7C, 0x1F, 0xF8, 0x22, 0x1B, 0x33, 0x10, 0x56, 0xF4, 0xC9, 0x58, 0xDB, 0x0F, 0xEC, 0x0B, 0x8C, 0x31, 0xE7, 0xE8, 0x80, 0x69, 0x57, 0x2B, 0xEE, 0xF9, 0x39, 0xBF, 0xEF, 0x56, 0x1B, 0xE1, 0xFF, 0x44, 0xD7, 0xEE, 0x3E, 0x6C, 0xAA, 0x2C, 0xB2, 0x17, 0xE0, 0xA8, 0xEA, 0xAA, 0x93, 0xE3, 0xBF, 0x63, 0xEE, 0xB4, 0xD5, 0x5F, 0x1E, 0x4A, 0x13, 0x22, 0x7D, 0x36, 0xE1, 0xC3, 0xF3, 0xC6, 0x3A, 0xEC, 0x1A, 0x02, 0xA3, 0x3E, 0xC5, 0x5E, 0xFD, 0xD3, 0x09, 0x1D, 0xAF, 0x7C, 0x1A, 0x75, 0x68, 0x58, 0x93, 0x91, 0x92, 0x27, 0xED, 0x0F, 0xCB, 0x98, 0x0E, 0xEA, 0xDF, 0xA5, 0x28, 0x2E, 0x4D, 0x84, 0x7A, 0xE6, 0xC1, 0xBD, 0x50, 0x8A, 0x6F, 0xAC, 0xEC, 0x64, 0xF4, 0xD2, 0x3B, 0x54, 0xFA, 0x07, 0x06, 0xE9, 0xFE, 0xB0, 0x93, 0x9A, 0x8E, 0x46, 0x95, 0xC5, 0xC5, 0xB5, 0x72, 0x0D, 0xD1, 0x79, 0x1A, 0x80, 0xA0, 0x0B, 0x1F, 0x5E, 0x49, 0x9D, 0x50, 0x57, 0xE5, 0x92, 0x6A, 0xF3, 0x16, 0x74, 0x7B, 0xE9, 0x0F, 0x29, 0xCF, 0x2B, 0x3D, 0x52, 0x10, 0xFC, 0x2F, 0x71, 0x5E, 0xDF, 0xAD, 0xC3, 0xCE, 0x69, 0x25, 0xD5, 0xF2, 0x97, 0x0B, 0xCA, 0xB1, 0x0A, 0x9B, 0xF8, 0x85, 0xB6, 0xDC, 0x4F, 0xB1, 0xCB, 0x03, 0xC4, 0xF8, 0x50, 0x63, 0x15, 0xE0, 0x1F, 0x5A, 0xB7, 0x07, 0x45, 0x0B, 0xD6, 0x9E, 0xCD, 0xBF, 0x2F, 0x1C, 0x99, 0x20, 0x69, 0x76, 0x5C, 0xFC, 0x31, 0x18, 0x9A, 0xB9, 0xAC, 0x77, 0xA6, 0xFB, 0xBD, 0xB9, 0x4D, 0x04, 0x45, 0xED, 0x9E, 0x19, 0x56, 0x18, 0xC7, 0x87, 0xFC -}; - -static const unsigned char firm_fw26_4_dev_hdrpub[0x100] = -{ - 0xC9, 0x83, 0x86, 0x8C, 0x40, 0x07, 0xF3, 0x48, 0xD1, 0xE9, 0xBC, 0x27, 0xAB, 0x11, 0xE4, 0x10, 0x1C, 0x65, 0x1C, 0xA1, 0xE5, 0x9A, 0xEB, 0xB5, 0x22, 0x1F, 0x78, 0x84, 0xC9, 0x9A, 0x7B, 0x45, 0x70, 0x7C, 0x80, 0xDD, 0xC1, 0xF2, 0xF6, 0x0D, 0x91, 0x90, 0x5C, 0xCB, 0x95, 0x1E, 0xC4, 0x31, 0x0D, 0x15, 0xA9, 0x2E, 0x22, 0x94, 0x3F, 0x97, 0x5C, 0x7E, 0x70, 0x5F, 0x5F, 0xF3, 0x12, 0x2D, 0x27, 0x91, 0x53, 0x31, 0xD8, 0x4D, 0xB8, 0x76, 0x74, 0xE8, 0xE1, 0x76, 0xE1, 0xA1, 0x84, 0xA9, 0xF8, 0x62, 0x4F, 0xED, 0x22, 0xFB, 0x82, 0xA5, 0x23, 0x36, 0x2E, 0x08, 0xC8, 0xC1, 0xBA, 0x46, 0x27, 0x10, 0x2E, 0x94, 0x47, 0x62, 0x01, 0x04, 0x4C, 0x84, 0x18, 0xC3, 0xCC, 0xF9, 0xD0, 0x1A, 0x89, 0xC7, 0x47, 0x5C, 0x62, 0x6F, 0x45, 0xF9, 0x18, 0x88, 0x80, 0x3F, 0x65, 0xB5, 0x4B, 0x07, 0x25, 0x4A, 0x97, 0x19, 0xBF, 0x13, 0x44, 0x28, 0x00, 0x38, 0xA7, 0x2E, 0x12, 0x32, 0x5B, 0xB4, 0x3A, 0x96, 0x64, 0x90, 0x77, 0x0F, 0x72, 0xB8, 0x79, 0x97, 0x59, 0x8E, 0x1C, 0xF0, 0xBC, 0x58, 0xD1, 0x26, 0x88, 0x3B, 0x25, 0x65, 0x7E, 0xC6, 0xF5, 0xF5, 0x24, 0x35, 0xC6, 0x65, 0xEF, 0x4C, 0x53, 0xC5, 0x56, 0x9A, 0x1A, 0x5F, 0x15, 0x29, 0xE7, 0x85, 0x1D, 0x35, 0xA1, 0x5C, 0x4B, 0xD6, 0xF2, 0x81, 0x24, 0x5F, 0x43, 0x05, 0xEA, 0xB0, 0x63, 0x0D, 0x78, 0xF7, 0xB5, 0x30, 0x20, 0xF2, 0xC9, 0xE8, 0x91, 0x8F, 0xBF, 0x20, 0x99, 0x95, 0x43, 0xAC, 0xB2, 0xF6, 0x82, 0xF0, 0xB1, 0x33, 0x1E, 0xA9, 0xB3, 0xF5, 0x61, 0xDC, 0x37, 0x81, 0xB3, 0x1F, 0xFE, 0xFF, 0x10, 0x9C, 0x12, 0x2A, 0x80, 0x1F, 0xE3, 0x9C, 0x86, 0x90, 0x47, 0x59, 0x29, 0xA9, 0xE2, 0xD7, 0x6F, 0x89, 0x5C, 0x7D -}; - -static const unsigned char firm_fw26_4_dev_acexsig[0x100] = -{ - 0x1B, 0x67, 0xA7, 0x4F, 0xF7, 0x55, 0x10, 0x85, 0xA9, 0x40, 0x89, 0xDC, 0xFF, 0xED, 0x4D, 0xC9, 0x71, 0x6E, 0x46, 0x92, 0xB2, 0x9E, 0xEA, 0xB9, 0x7C, 0xC6, 0xAB, 0xCD, 0xDD, 0x6B, 0x4A, 0x27, 0x31, 0xF3, 0xFA, 0x26, 0x2F, 0x99, 0x46, 0x32, 0xBD, 0xDE, 0xA3, 0x09, 0xC9, 0x44, 0x6D, 0xF8, 0x6E, 0x36, 0xB1, 0x26, 0x21, 0x02, 0x0E, 0x6B, 0x3F, 0x3C, 0xD6, 0xA5, 0x56, 0xD9, 0x19, 0x68, 0x73, 0xB7, 0x98, 0x08, 0x46, 0x5E, 0x7C, 0xDC, 0x61, 0x69, 0x06, 0x9C, 0x8C, 0x09, 0x4D, 0x54, 0x88, 0x22, 0x98, 0x85, 0x03, 0x07, 0xA7, 0xB7, 0x9F, 0x81, 0xFA, 0x9F, 0xF5, 0xF6, 0x0D, 0x2E, 0xE7, 0x8B, 0x09, 0xB2, 0xB4, 0x7F, 0xB5, 0xBB, 0x57, 0x7A, 0xEB, 0x92, 0x24, 0xAF, 0xFA, 0x24, 0x69, 0x33, 0x55, 0x9D, 0xF6, 0xAC, 0x78, 0xB9, 0xCF, 0x16, 0x88, 0x85, 0xDD, 0xBA, 0x12, 0xB9, 0x50, 0xA9, 0x81, 0x7D, 0x03, 0x83, 0x4F, 0x7F, 0x25, 0xBD, 0xE4, 0x5F, 0x24, 0x85, 0xCB, 0x3A, 0x46, 0x5A, 0x83, 0xEC, 0x1A, 0x85, 0xE1, 0x38, 0xAB, 0xC5, 0x54, 0xE7, 0x14, 0xD1, 0xC4, 0x77, 0x19, 0xAB, 0xAD, 0x75, 0x11, 0xCF, 0xB4, 0xFE, 0xAB, 0x85, 0xF8, 0xAC, 0x9C, 0x08, 0x93, 0xED, 0x10, 0x60, 0xB2, 0x3F, 0x5A, 0x7B, 0xA4, 0x30, 0xA0, 0x72, 0xC2, 0x33, 0xF3, 0xDC, 0x38, 0xEB, 0x58, 0x7A, 0x9C, 0xF5, 0x4F, 0xB4, 0x92, 0x57, 0x07, 0x7C, 0xC8, 0xD7, 0xE3, 0x28, 0xBA, 0x9B, 0x85, 0x7E, 0x8A, 0xB5, 0xF3, 0x18, 0x58, 0x27, 0x81, 0x37, 0x11, 0x9A, 0x8A, 0x97, 0x33, 0x1A, 0x46, 0x90, 0x0F, 0x1E, 0x37, 0xC9, 0x86, 0xF0, 0xF7, 0xA6, 0xBD, 0xCA, 0x65, 0x3B, 0x3B, 0xDC, 0x80, 0x02, 0x44, 0x84, 0x15, 0x12, 0x31, 0x14, 0xB8, 0x76, 0x9B, 0xAF, 0xB4, 0xCD, 0x2D, 0xC1 -}; - -static const unsigned char firm_fw26_5_dev_hdrpub[0x100] = -{ - 0xD2, 0x81, 0x84, 0x0D, 0x64, 0x18, 0x87, 0x0F, 0xAC, 0x05, 0x8A, 0xF1, 0x9D, 0x0F, 0x08, 0xC0, 0xC2, 0xD4, 0xE6, 0xEC, 0x7B, 0x96, 0xA1, 0xD0, 0xD7, 0x47, 0x01, 0x03, 0x3F, 0x6F, 0xCB, 0x81, 0xE8, 0x55, 0x82, 0xFE, 0xBC, 0x19, 0x07, 0x05, 0x3D, 0x80, 0x8B, 0x85, 0x44, 0xD4, 0x2D, 0x68, 0xAE, 0x6F, 0xC7, 0x0A, 0xBC, 0xA0, 0xB4, 0x83, 0x7D, 0x00, 0x6F, 0x34, 0x2C, 0xF5, 0x1A, 0x79, 0x59, 0x34, 0x5F, 0x94, 0x40, 0x36, 0x19, 0x90, 0x09, 0x45, 0x30, 0xA7, 0xED, 0x8E, 0x47, 0x9F, 0x6B, 0x31, 0xD2, 0x13, 0x53, 0xEE, 0xF8, 0xC1, 0x00, 0xBD, 0x31, 0x19, 0xB2, 0x5B, 0x9C, 0x44, 0xE3, 0x14, 0x36, 0x31, 0xB4, 0x6F, 0x45, 0x33, 0xCC, 0xD5, 0x5D, 0xD0, 0x54, 0xFC, 0x57, 0xC0, 0xE2, 0x5C, 0xC1, 0x7E, 0xE4, 0x98, 0xBF, 0x61, 0xC6, 0x5F, 0x11, 0xBC, 0xED, 0x7E, 0x2B, 0xB1, 0x31, 0xD3, 0xF6, 0xC7, 0x5C, 0xC9, 0x35, 0x41, 0x82, 0x01, 0xE3, 0xF3, 0x02, 0xCC, 0x09, 0x65, 0x5C, 0xBB, 0xC0, 0x41, 0x47, 0xDC, 0xDC, 0xB9, 0x57, 0xEC, 0x39, 0xB9, 0xE7, 0x30, 0x29, 0x26, 0xA0, 0xE8, 0xD2, 0x3E, 0xAF, 0x46, 0x49, 0xBF, 0xC4, 0x9D, 0xB2, 0xD7, 0x16, 0x77, 0xD3, 0xBA, 0xF4, 0x7C, 0x7B, 0x7B, 0x4C, 0xCD, 0x0E, 0x48, 0xCD, 0x92, 0x1F, 0x2D, 0x7F, 0x00, 0xE8, 0x75, 0xC1, 0xC5, 0x64, 0x53, 0x2B, 0x6F, 0xE0, 0xA8, 0x42, 0xC6, 0x55, 0x66, 0x62, 0x94, 0xEA, 0xCC, 0xAC, 0xE6, 0x75, 0x55, 0xBC, 0xB8, 0x88, 0x18, 0x5B, 0x1F, 0x12, 0xE6, 0x9C, 0x12, 0x1E, 0x29, 0xBD, 0x4F, 0xF0, 0x83, 0xCE, 0xEE, 0xF2, 0x3B, 0x87, 0x28, 0x49, 0x96, 0x31, 0x4F, 0x8E, 0x2A, 0x17, 0x31, 0x46, 0x73, 0x7E, 0xE8, 0x3C, 0x60, 0x82, 0xB7, 0x45, 0x63, 0x50, 0x96, 0xB6, 0xED -}; - -static const unsigned char firm_fw26_5_dev_acexsig[0x100] = -{ - 0x19, 0x92, 0x44, 0x57, 0xF2, 0xEE, 0x7E, 0xA6, 0x1D, 0xB7, 0xF0, 0x57, 0xE7, 0x35, 0x0F, 0x9B, 0xAE, 0xC3, 0x80, 0x3F, 0x8D, 0xFD, 0x4B, 0xBB, 0xCF, 0x0C, 0x02, 0x31, 0x04, 0x2C, 0xB8, 0xA3, 0xB9, 0x7B, 0x87, 0x74, 0x39, 0x86, 0x61, 0xE7, 0x69, 0xE6, 0xD6, 0x01, 0xCE, 0xCE, 0xAB, 0xD6, 0xE7, 0xDE, 0xF3, 0xFF, 0x4B, 0x20, 0xB0, 0x41, 0xCE, 0xE4, 0x02, 0xAA, 0x50, 0x3F, 0xF4, 0xE1, 0xE0, 0x57, 0x6C, 0xCD, 0xC4, 0x70, 0x59, 0x55, 0x80, 0xDB, 0x9F, 0x7B, 0xB3, 0x27, 0xEA, 0x29, 0xA5, 0xB4, 0x98, 0xEE, 0xDB, 0x6C, 0x7D, 0x50, 0x1E, 0xE7, 0xF9, 0xC0, 0x0F, 0x2F, 0x65, 0x0B, 0x37, 0x1C, 0xD2, 0x8B, 0x94, 0x1B, 0x1A, 0xC7, 0x23, 0x85, 0x56, 0x6C, 0x1A, 0xF3, 0x6D, 0xC2, 0xB2, 0xF1, 0x0F, 0xDF, 0x30, 0x65, 0x84, 0xF1, 0xEB, 0xD3, 0x6D, 0xA6, 0xA0, 0x65, 0x3B, 0xA9, 0x07, 0x20, 0x69, 0x7B, 0x63, 0x63, 0xF9, 0xCF, 0x97, 0x46, 0xD0, 0xD1, 0x7D, 0x9F, 0x87, 0x45, 0xC1, 0xED, 0xE8, 0x3C, 0x62, 0x5F, 0x16, 0x35, 0xB2, 0x2E, 0x1C, 0x16, 0x89, 0x2D, 0x93, 0xD9, 0x98, 0x4F, 0x42, 0xDF, 0x63, 0xA5, 0xB9, 0xBB, 0x48, 0x6F, 0xA2, 0xE3, 0x6C, 0xB5, 0xB6, 0x6F, 0x37, 0x37, 0x6E, 0x15, 0xD5, 0x32, 0x75, 0x2E, 0x39, 0x34, 0xCE, 0x1A, 0x81, 0x9E, 0xE8, 0x0B, 0xCA, 0xCB, 0xB0, 0x69, 0xA3, 0xE2, 0xBE, 0x8A, 0xC1, 0xE3, 0xBC, 0xAD, 0x25, 0x6F, 0xCA, 0x80, 0xB0, 0xEC, 0x1B, 0x0F, 0x5C, 0x6B, 0x92, 0xA0, 0xCE, 0x61, 0x04, 0x86, 0x51, 0x57, 0x9C, 0x0F, 0xB9, 0xD5, 0x80, 0x16, 0x30, 0xE1, 0x3A, 0x25, 0x4D, 0xEF, 0x74, 0xC4, 0x94, 0x78, 0xB6, 0x84, 0xDF, 0xC0, 0xE5, 0x62, 0x5B, 0x16, 0x4D, 0xDA, 0x75, 0x71, 0xA9, 0xB0, 0x58, 0x7E, 0x95, 0x83 +#include "desc/desc.h" + +static const CtrSdkDescSignData kDevDescSignData[] = +{ + /* CTR_SDK 1 (1.2.0) */ + { + .type = desc_Application, + .fw_minor = 27, + .modulus = { 0x9B, 0x82, 0x9C, 0x19, 0x01, 0x73, 0x23, 0x50, 0x89, 0xE3, 0x0B, 0xC8, 0x8F, 0xFB, 0xA5, 0xE4, 0xFD, 0x44, 0xAE, 0x49, 0x32, 0xC7, 0xEF, 0x0A, 0x7E, 0x93, 0x95, 0xBA, 0xA2, 0x48, 0x4D, 0x8E, 0x18, 0x82, 0x34, 0x66, 0xFE, 0xDA, 0x7A, 0x45, 0x4A, 0x7D, 0xD5, 0x3C, 0xC6, 0x5C, 0xE0, 0x71, 0xFC, 0xD0, 0x82, 0xCC, 0xB2, 0xCF, 0x77, 0x0E, 0xAD, 0xCD, 0xD2, 0xAC, 0x1D, 0x66, 0x1B, 0xC1, 0xEA, 0xAC, 0x39, 0x96, 0x2C, 0x52, 0xDE, 0xFF, 0xF2, 0x74, 0xF6, 0xC5, 0xCA, 0x99, 0x31, 0x95, 0x13, 0xBB, 0x3F, 0xED, 0x30, 0xAE, 0x0A, 0xD3, 0x86, 0x0D, 0x0B, 0xF5, 0x56, 0x7D, 0x33, 0xBA, 0xA1, 0x39, 0xA2, 0xF5, 0xB6, 0x47, 0x78, 0x1F, 0xFD, 0x04, 0xA3, 0x49, 0xA9, 0x10, 0xD3, 0x41, 0x2E, 0x92, 0x7D, 0xE1, 0xA4, 0xA8, 0x02, 0x18, 0x01, 0x2C, 0xC2, 0x61, 0xB1, 0xAC, 0xCB, 0xB3, 0x7A, 0x64, 0xB1, 0xC3, 0xE6, 0x3B, 0x50, 0xAD, 0xDD, 0x55, 0xAB, 0x28, 0xDA, 0x59, 0xE7, 0x57, 0x6A, 0x76, 0x4F, 0x6B, 0x08, 0xD1, 0x61, 0x7D, 0x28, 0xD5, 0x88, 0x7B, 0x8E, 0x80, 0x78, 0xF3, 0xFF, 0x50, 0x00, 0xBD, 0x73, 0xFB, 0x62, 0xB3, 0xCA, 0xA8, 0x05, 0x48, 0xE6, 0xE6, 0x71, 0xB5, 0xAC, 0xDA, 0xE9, 0xC6, 0x1F, 0x9B, 0x72, 0x98, 0x2E, 0xFF, 0xB2, 0x9C, 0x36, 0x9F, 0x7E, 0x7D, 0x25, 0x65, 0x6F, 0xB0, 0x60, 0xB1, 0xB5, 0x5F, 0xCF, 0x74, 0x29, 0x91, 0x9D, 0xAB, 0x84, 0x97, 0x1A, 0x27, 0x5D, 0x69, 0x49, 0x16, 0xF8, 0x77, 0x31, 0x26, 0x3E, 0x6F, 0x97, 0x41, 0x4A, 0x26, 0xFD, 0x5B, 0xAB, 0x65, 0x77, 0x45, 0x1C, 0x76, 0x15, 0xDC, 0x1A, 0x63, 0x0F, 0x51, 0x2D, 0xA0, 0x07, 0x6E, 0xE5, 0x29, 0xD3, 0x37, 0x4B, 0xE5, 0x7F, 0xBB, 0x23, 0xD0, 0x2B, 0xB9, 0x76, 0x1F }, + .priv_exponent = { 0x66, 0x9F, 0xB5, 0xC5, 0xA6, 0xB8, 0x45, 0xD8, 0xD3, 0x75, 0xFB, 0x03, 0xBB, 0x48, 0xF5, 0x7C, 0x7D, 0x4B, 0x02, 0xBD, 0x19, 0x7E, 0xE9, 0x98, 0x02, 0x5A, 0x00, 0xD8, 0x6E, 0x59, 0xCA, 0x9C, 0x78, 0x3E, 0x0C, 0xB8, 0xDF, 0x7C, 0x6C, 0x6E, 0x27, 0xAF, 0x8C, 0xB6, 0x13, 0xAD, 0x9D, 0x0C, 0x7C, 0x2B, 0x59, 0xF6, 0x1E, 0x16, 0x5D, 0x5A, 0x59, 0x86, 0x57, 0x7D, 0xEF, 0xD4, 0xBF, 0x82, 0xA4, 0x0C, 0x4D, 0xE0, 0x75, 0x95, 0xA6, 0xC6, 0x3F, 0x49, 0xC2, 0xC4, 0x5A, 0x63, 0xE8, 0x5D, 0x99, 0xEC, 0xDB, 0x4D, 0xFA, 0xEF, 0x10, 0x03, 0xF1, 0x15, 0xD1, 0x0B, 0x71, 0xAD, 0x24, 0x23, 0x08, 0x5C, 0x91, 0xD7, 0x17, 0x18, 0x69, 0x04, 0xAB, 0x23, 0x91, 0x62, 0x7D, 0xE8, 0xB5, 0x90, 0xF1, 0x5C, 0x09, 0x28, 0x8C, 0x51, 0xB7, 0x38, 0x02, 0x26, 0x78, 0x8C, 0xA2, 0x05, 0x07, 0x53, 0x7D, 0x99, 0x46, 0xFD, 0x12, 0x77, 0x32, 0x0E, 0xA8, 0x54, 0xE3, 0x32, 0x0E, 0x93, 0x05, 0x10, 0xFA, 0x59, 0x7A, 0x5D, 0x2E, 0xDE, 0x32, 0xE8, 0xE9, 0xF0, 0x27, 0x4E, 0x08, 0x83, 0x08, 0xD4, 0x92, 0x58, 0x4D, 0x6D, 0x34, 0x9F, 0xD9, 0xAF, 0xA9, 0x01, 0x62, 0xB5, 0x1A, 0x1D, 0x7E, 0x8D, 0xBB, 0x8A, 0x58, 0xBA, 0xFF, 0xB4, 0xD3, 0x75, 0xF3, 0x44, 0xE7, 0x13, 0x05, 0xC6, 0xC5, 0xA4, 0xD2, 0x6B, 0x18, 0x31, 0x9F, 0xBE, 0x42, 0x45, 0x82, 0x2E, 0x47, 0x9B, 0x26, 0x73, 0x28, 0xD7, 0x9A, 0x64, 0xA6, 0x3D, 0x38, 0x9C, 0x11, 0x36, 0x01, 0x5B, 0x82, 0x2F, 0x7E, 0xA8, 0xC4, 0x82, 0x15, 0x6C, 0x0B, 0xBA, 0x1C, 0x58, 0xF5, 0x81, 0xF9, 0x45, 0xA9, 0xC6, 0x05, 0x6A, 0x2C, 0xA6, 0xCF, 0xF5, 0xDF, 0xEB, 0xBB, 0xC0, 0x92, 0xE3, 0xA6, 0x9D, 0x23, 0x09, 0x09, 0x0E, 0x98, 0x39 }, + .access_desc_signature = { 0x05, 0x90, 0xAF, 0x65, 0x16, 0x9F, 0x18, 0x2C, 0x17, 0x78, 0x9F, 0xDF, 0xB6, 0x37, 0xCF, 0x26, 0x9B, 0x1B, 0x75, 0x51, 0xB8, 0x57, 0xA3, 0x8F, 0xD7, 0x93, 0x19, 0x61, 0x81, 0x0D, 0x3D, 0xBC, 0x36, 0x50, 0x53, 0xDA, 0x7D, 0xA9, 0x7F, 0xAA, 0x3E, 0x51, 0x2C, 0x75, 0xA1, 0xB9, 0xB1, 0x56, 0xEB, 0x2A, 0x46, 0x21, 0xEC, 0x4F, 0xA7, 0x0C, 0xA1, 0xA8, 0xFD, 0xEE, 0xA3, 0x4A, 0xFD, 0x54, 0xB0, 0x3A, 0x49, 0x5C, 0x8F, 0x8D, 0xB2, 0xBC, 0x32, 0x50, 0x7E, 0x2C, 0x50, 0xD2, 0x1A, 0x6B, 0x61, 0xCB, 0x2A, 0xC9, 0x7E, 0x6E, 0x6A, 0xC8, 0xD6, 0x9B, 0x21, 0xE7, 0x3B, 0xB8, 0x39, 0x1C, 0xD7, 0xEB, 0x69, 0x35, 0xF5, 0xBC, 0xB5, 0x23, 0x54, 0x81, 0x4F, 0x73, 0xAB, 0x9C, 0x55, 0xF0, 0x04, 0x0B, 0x4A, 0xEA, 0x54, 0x08, 0xBF, 0x36, 0x28, 0x12, 0x5E, 0x44, 0x41, 0xF5, 0x3D, 0xFE, 0xA7, 0x6B, 0x35, 0xF2, 0x9A, 0xF2, 0x88, 0xCD, 0xD6, 0x2E, 0x7B, 0xF3, 0xF5, 0x0D, 0x06, 0x2E, 0x13, 0x4F, 0x78, 0xEE, 0x27, 0xAB, 0x31, 0x06, 0x62, 0xE9, 0xB2, 0x3E, 0xC6, 0x99, 0xD7, 0xA9, 0xCC, 0x21, 0x70, 0xD7, 0xCD, 0x9F, 0x03, 0x66, 0x91, 0x7E, 0xBD, 0x3E, 0x83, 0xC4, 0xFF, 0xC9, 0xAA, 0x7E, 0x27, 0xAE, 0x5C, 0x37, 0x7D, 0x93, 0x57, 0x60, 0xB9, 0x0B, 0x71, 0x43, 0x8A, 0x2F, 0x43, 0x50, 0x94, 0xF8, 0x0D, 0x40, 0xCC, 0x64, 0x16, 0x09, 0x70, 0xCD, 0x03, 0xCA, 0x95, 0x30, 0x20, 0xE2, 0x85, 0x2F, 0x2A, 0xCF, 0x65, 0xAA, 0xE9, 0xCF, 0x1F, 0x57, 0xC1, 0x8F, 0xD5, 0x46, 0x51, 0x5A, 0x99, 0x60, 0x65, 0x93, 0xA9, 0xBB, 0xEA, 0x5F, 0xA0, 0x47, 0xD7, 0x11, 0x04, 0xC7, 0xB4, 0x82, 0x66, 0x24, 0x17, 0x17, 0x5E, 0x9D, 0xC8, 0x50, 0x80, 0x63, 0x28, 0xB3, 0xF8, 0xE3 } + }, + { + .type = desc_DlpChild, + .fw_minor = 27, + .modulus = { 0xD9, 0xF0, 0xC9, 0x35, 0x1E, 0x55, 0xD8, 0x7E, 0x96, 0x53, 0x33, 0x34, 0xBB, 0x8A, 0xAE, 0x03, 0x92, 0x35, 0xE2, 0x05, 0x58, 0x7C, 0xCC, 0x08, 0xB2, 0xDF, 0x43, 0x41, 0xB7, 0x7A, 0xB5, 0x29, 0xEE, 0x4E, 0xF3, 0xC7, 0x35, 0x3B, 0x7E, 0xC5, 0xEE, 0x74, 0xF0, 0xAA, 0x7E, 0x60, 0xF1, 0x28, 0x35, 0x17, 0xD6, 0xC9, 0x9A, 0xF2, 0x84, 0xFE, 0xC8, 0x93, 0x86, 0xF7, 0xA7, 0x36, 0xA6, 0xB0, 0x28, 0xDC, 0xE8, 0x38, 0x0B, 0x54, 0x42, 0x8D, 0x4E, 0x4B, 0x55, 0x0F, 0x4A, 0x0D, 0x72, 0xA0, 0x23, 0xC9, 0x68, 0x22, 0x37, 0x31, 0x88, 0x2C, 0x05, 0x49, 0x86, 0x80, 0x9A, 0xFC, 0x1D, 0x02, 0xE3, 0x20, 0x15, 0x0C, 0x7E, 0x28, 0x40, 0x57, 0xEF, 0xA7, 0xBC, 0xAA, 0xC5, 0xD6, 0xD7, 0x6F, 0xF9, 0x26, 0x9A, 0x32, 0xB2, 0x9E, 0x10, 0x5F, 0x93, 0xE6, 0xB2, 0xC6, 0xB2, 0x62, 0x34, 0x6A, 0xB0, 0xD9, 0x71, 0x3B, 0x0F, 0x34, 0x6C, 0xB1, 0xFE, 0x3A, 0x39, 0xDE, 0x3D, 0x6A, 0xCB, 0x32, 0x95, 0xFA, 0x18, 0x4F, 0xF4, 0xEB, 0x5F, 0x20, 0xE4, 0xEF, 0x64, 0xC5, 0x06, 0x27, 0xC3, 0x44, 0x2A, 0x39, 0x35, 0xD8, 0x00, 0xDF, 0x00, 0xAD, 0xC4, 0x98, 0x06, 0x52, 0xD8, 0x4A, 0xC5, 0x2A, 0x7F, 0x77, 0x50, 0x62, 0x7E, 0x05, 0x3E, 0x8C, 0x28, 0x0A, 0x26, 0xD2, 0x6C, 0x9B, 0x27, 0x65, 0xE9, 0x77, 0x68, 0xE9, 0xE6, 0xAA, 0xBA, 0xF5, 0x85, 0xFC, 0x75, 0x07, 0x84, 0xB2, 0xCA, 0x35, 0x85, 0x52, 0x10, 0x08, 0xEF, 0x85, 0xD3, 0x70, 0x17, 0x31, 0xE1, 0x44, 0xF6, 0x34, 0xDF, 0x7C, 0x42, 0xF7, 0x74, 0xAA, 0xFC, 0xC3, 0xE4, 0x84, 0x2D, 0xBF, 0x15, 0x1E, 0x84, 0x00, 0xE3, 0x80, 0xD7, 0x89, 0x56, 0xEE, 0x60, 0x09, 0x1F, 0xD3, 0xBF, 0xBF, 0x50, 0x8F, 0xA3, 0x0C, 0x72, 0x3F }, + .priv_exponent = { 0x10, 0x19, 0x3A, 0x33, 0xA3, 0x47, 0x02, 0x13, 0xEF, 0xB4, 0xBB, 0x9E, 0x94, 0x8F, 0xDC, 0xE4, 0xC4, 0xA3, 0x18, 0x4B, 0xFE, 0xCA, 0x51, 0x23, 0xFF, 0x5A, 0x80, 0x94, 0x55, 0x22, 0x4A, 0x49, 0x8B, 0xA1, 0xE7, 0x5D, 0xFA, 0xAF, 0xA7, 0x60, 0xA5, 0x89, 0x9B, 0xD1, 0x6C, 0x3E, 0x6A, 0xF1, 0xE6, 0x62, 0x19, 0x6A, 0x90, 0xF8, 0x83, 0x1C, 0x72, 0xE2, 0x7A, 0xE0, 0xC6, 0x48, 0x42, 0x2D, 0xD7, 0x06, 0xE2, 0x5C, 0x69, 0x71, 0xD2, 0xEC, 0xAF, 0x30, 0xDF, 0x5A, 0x9E, 0xC4, 0xB9, 0x87, 0xDC, 0xBC, 0xDE, 0xE5, 0x50, 0x20, 0x67, 0x87, 0xA0, 0xE8, 0x5A, 0x78, 0x1B, 0x7A, 0xAE, 0x05, 0xED, 0x93, 0x0C, 0x1A, 0xFD, 0x22, 0xAA, 0x06, 0x14, 0xDC, 0xD6, 0x11, 0xE3, 0x45, 0x48, 0x6A, 0xAC, 0x03, 0xCE, 0xF6, 0x19, 0xBD, 0x95, 0x46, 0x0A, 0x1D, 0xCB, 0x6C, 0xE3, 0xF6, 0x5F, 0x1A, 0xB3, 0x81, 0xC7, 0xE2, 0xAB, 0xFE, 0xEF, 0xFB, 0xEC, 0xFE, 0x88, 0x36, 0x26, 0x60, 0x47, 0x43, 0x78, 0x36, 0xA7, 0xC8, 0xC9, 0x40, 0x98, 0x2E, 0xF2, 0x7E, 0xE4, 0x0D, 0x6C, 0x45, 0x88, 0x2A, 0x32, 0x9B, 0xA2, 0x7C, 0x39, 0x20, 0xAA, 0x6B, 0x64, 0x35, 0xC6, 0xA9, 0x20, 0x71, 0x4A, 0x78, 0x6E, 0x55, 0x3C, 0x9B, 0xEA, 0x10, 0x73, 0xBB, 0xA7, 0xD8, 0xFE, 0x69, 0x42, 0xB8, 0xE7, 0xA1, 0xE5, 0xDF, 0x8A, 0xDE, 0x4C, 0x2B, 0x3A, 0x92, 0xB8, 0x3E, 0x5E, 0x2C, 0x29, 0x0D, 0xC1, 0x3D, 0x10, 0x65, 0x1E, 0xF1, 0x95, 0xE5, 0xF6, 0x45, 0x15, 0xBF, 0xE2, 0x30, 0xA6, 0x70, 0x19, 0xA4, 0x11, 0x57, 0x12, 0x1C, 0x81, 0x4B, 0x54, 0x04, 0xBE, 0x67, 0xF5, 0x00, 0x22, 0x06, 0xDA, 0x6B, 0xCE, 0x23, 0x3F, 0x86, 0xE4, 0x70, 0x6A, 0xD1, 0x3E, 0xE5, 0x74, 0x44, 0x86, 0xDA, 0xBE, 0x79 }, + .access_desc_signature = { 0x10, 0x19, 0x3A, 0x33, 0xA3, 0x47, 0x02, 0x13, 0xEF, 0xB4, 0xBB, 0x9E, 0x94, 0x8F, 0xDC, 0xE4, 0xC4, 0xA3, 0x18, 0x4B, 0xFE, 0xCA, 0x51, 0x23, 0xFF, 0x5A, 0x80, 0x94, 0x55, 0x22, 0x4A, 0x49, 0x8B, 0xA1, 0xE7, 0x5D, 0xFA, 0xAF, 0xA7, 0x60, 0xA5, 0x89, 0x9B, 0xD1, 0x6C, 0x3E, 0x6A, 0xF1, 0xE6, 0x62, 0x19, 0x6A, 0x90, 0xF8, 0x83, 0x1C, 0x72, 0xE2, 0x7A, 0xE0, 0xC6, 0x48, 0x42, 0x2D, 0xD7, 0x06, 0xE2, 0x5C, 0x69, 0x71, 0xD2, 0xEC, 0xAF, 0x30, 0xDF, 0x5A, 0x9E, 0xC4, 0xB9, 0x87, 0xDC, 0xBC, 0xDE, 0xE5, 0x50, 0x20, 0x67, 0x87, 0xA0, 0xE8, 0x5A, 0x78, 0x1B, 0x7A, 0xAE, 0x05, 0xED, 0x93, 0x0C, 0x1A, 0xFD, 0x22, 0xAA, 0x06, 0x14, 0xDC, 0xD6, 0x11, 0xE3, 0x45, 0x48, 0x6A, 0xAC, 0x03, 0xCE, 0xF6, 0x19, 0xBD, 0x95, 0x46, 0x0A, 0x1D, 0xCB, 0x6C, 0xE3, 0xF6, 0x5F, 0x1A, 0xB3, 0x81, 0xC7, 0xE2, 0xAB, 0xFE, 0xEF, 0xFB, 0xEC, 0xFE, 0x88, 0x36, 0x26, 0x60, 0x47, 0x43, 0x78, 0x36, 0xA7, 0xC8, 0xC9, 0x40, 0x98, 0x2E, 0xF2, 0x7E, 0xE4, 0x0D, 0x6C, 0x45, 0x88, 0x2A, 0x32, 0x9B, 0xA2, 0x7C, 0x39, 0x20, 0xAA, 0x6B, 0x64, 0x35, 0xC6, 0xA9, 0x20, 0x71, 0x4A, 0x78, 0x6E, 0x55, 0x3C, 0x9B, 0xEA, 0x10, 0x73, 0xBB, 0xA7, 0xD8, 0xFE, 0x69, 0x42, 0xB8, 0xE7, 0xA1, 0xE5, 0xDF, 0x8A, 0xDE, 0x4C, 0x2B, 0x3A, 0x92, 0xB8, 0x3E, 0x5E, 0x2C, 0x29, 0x0D, 0xC1, 0x3D, 0x10, 0x65, 0x1E, 0xF1, 0x95, 0xE5, 0xF6, 0x45, 0x15, 0xBF, 0xE2, 0x30, 0xA6, 0x70, 0x19, 0xA4, 0x11, 0x57, 0x12, 0x1C, 0x81, 0x4B, 0x54, 0x04, 0xBE, 0x67, 0xF5, 0x00, 0x22, 0x06, 0xDA, 0x6B, 0xCE, 0x23, 0x3F, 0x86, 0xE4, 0x70, 0x6A, 0xD1, 0x3E, 0xE5, 0x74, 0x44, 0x86, 0xDA, 0xBE, 0x79 } + }, + /* CTR_SDK 2 (2.3.4) */ + { + .type = desc_Application, + .fw_minor = 29, + .modulus = { 0xE9, 0x45, 0xF0, 0xC6, 0x96, 0xD5, 0x6F, 0x7E, 0xAE, 0x03, 0x92, 0x2E, 0xEA, 0xCB, 0xFD, 0xEA, 0xA4, 0x7A, 0x9F, 0x12, 0xDA, 0x4C, 0x10, 0x0A, 0xBE, 0x08, 0x9D, 0x87, 0xE0, 0x14, 0xAC, 0x7F, 0x39, 0xD2, 0xFE, 0x9D, 0x88, 0xB2, 0x81, 0xF6, 0x1A, 0x9E, 0x15, 0x57, 0xD4, 0xE2, 0x31, 0x08, 0x07, 0xEC, 0x4F, 0x10, 0x00, 0xDE, 0xEF, 0x8B, 0x6F, 0xCF, 0x84, 0xE7, 0x3B, 0x41, 0x08, 0x64, 0x3B, 0x1C, 0x00, 0x7C, 0x73, 0xBB, 0x59, 0x4D, 0xD8, 0xD6, 0xE7, 0x7B, 0xBE, 0xDD, 0x50, 0x98, 0xA1, 0x1A, 0xD5, 0xAA, 0x37, 0x69, 0xB8, 0x25, 0xCB, 0x7B, 0x03, 0x00, 0x90, 0x25, 0xF3, 0x7E, 0x9A, 0x0F, 0xA3, 0xAA, 0xC4, 0xB9, 0x3B, 0x3A, 0x18, 0x2B, 0xBC, 0x9C, 0x11, 0x04, 0x92, 0x16, 0x6E, 0xC3, 0xFA, 0x01, 0xD3, 0x00, 0x02, 0xF3, 0x2E, 0xD5, 0x60, 0xA8, 0xAF, 0xAB, 0xEE, 0x2F, 0x9D, 0x30, 0x3E, 0x0E, 0xDC, 0xB8, 0xEC, 0x87, 0x9E, 0x4A, 0xA9, 0x01, 0x34, 0x69, 0x2C, 0x4C, 0x34, 0xB7, 0x7D, 0xB9, 0x7A, 0x17, 0x74, 0x31, 0xB0, 0x29, 0xC4, 0x7D, 0x27, 0x1F, 0xBA, 0xBA, 0x3F, 0x5B, 0x62, 0xF6, 0x90, 0xB8, 0x37, 0x33, 0xFC, 0x73, 0xD6, 0x19, 0x11, 0xCA, 0x83, 0x2A, 0x58, 0x62, 0x9C, 0xB1, 0x83, 0x43, 0x1D, 0x2C, 0x00, 0xA2, 0xE5, 0x87, 0x97, 0x12, 0x63, 0x31, 0x83, 0x0E, 0xB1, 0x1E, 0x69, 0x99, 0x02, 0xAF, 0xDF, 0xFF, 0x0F, 0xA9, 0x7C, 0x1B, 0x33, 0x9E, 0xFF, 0x9C, 0x14, 0x19, 0xA6, 0xCA, 0xFD, 0xB9, 0x17, 0xE0, 0x22, 0xCF, 0xB5, 0x00, 0x77, 0x2E, 0x31, 0xAD, 0xF7, 0xE5, 0xAD, 0x98, 0x14, 0xDF, 0x19, 0xF0, 0xC9, 0xBE, 0x37, 0xF6, 0xF0, 0x23, 0x66, 0xCF, 0x34, 0xE3, 0xD5, 0x8F, 0xD4, 0x07, 0xBA, 0x06, 0x56, 0x00, 0x66, 0x9A, 0xEB, 0x93 }, + .priv_exponent = { 0xBC, 0x49, 0x29, 0xB9, 0x01, 0x52, 0x31, 0x76, 0x4C, 0xBA, 0xB1, 0x29, 0x91, 0x77, 0x29, 0xF2, 0x54, 0xE4, 0x6C, 0xB5, 0x68, 0xE1, 0xF0, 0x28, 0xDB, 0x8E, 0x54, 0xA8, 0xB1, 0xA3, 0xBE, 0x3F, 0xCA, 0xCA, 0x95, 0x9D, 0x4E, 0x12, 0xD7, 0x77, 0x6F, 0xB0, 0x9D, 0x85, 0x91, 0x5D, 0x29, 0x3A, 0x54, 0x3A, 0xD6, 0xEE, 0x11, 0xE5, 0xDF, 0xEF, 0xEA, 0x45, 0xD3, 0xFE, 0x58, 0x03, 0x7B, 0xE4, 0x7B, 0x19, 0x75, 0x02, 0xFE, 0xDE, 0xFF, 0x8C, 0x28, 0x33, 0xFE, 0x10, 0x11, 0xD4, 0xCD, 0x13, 0x05, 0x26, 0x85, 0xC3, 0xA8, 0x8A, 0x7A, 0x8A, 0x77, 0x1D, 0x49, 0x25, 0x11, 0x34, 0xB0, 0xBF, 0x45, 0x56, 0xCE, 0x42, 0x2E, 0x1B, 0x5C, 0xC4, 0xDD, 0x71, 0xA0, 0x01, 0x50, 0x73, 0x21, 0xFF, 0x5D, 0x54, 0x6D, 0xDD, 0x3F, 0x14, 0x49, 0x4D, 0x44, 0x46, 0x12, 0x88, 0xD5, 0x92, 0xAE, 0xE2, 0xD0, 0xF6, 0x2C, 0x10, 0xD5, 0x67, 0x61, 0x87, 0x7F, 0x2A, 0x17, 0x9D, 0x4F, 0xC6, 0x79, 0xC3, 0xAF, 0x4D, 0x6F, 0xFB, 0x0F, 0x3B, 0x48, 0x5D, 0x46, 0x9A, 0xE8, 0x53, 0xB7, 0xC5, 0x69, 0xEC, 0x31, 0x25, 0xD1, 0xDC, 0x93, 0xAB, 0x2E, 0x53, 0x3B, 0x8E, 0x96, 0x27, 0x59, 0xD4, 0xF7, 0xB3, 0xAB, 0x51, 0x59, 0xAE, 0x6E, 0x26, 0x4F, 0xC2, 0x95, 0xCE, 0x42, 0xC6, 0xAF, 0x46, 0xC6, 0x2E, 0x32, 0x09, 0x7B, 0xAF, 0x67, 0x4E, 0x57, 0xC8, 0x93, 0x5F, 0x8C, 0xD5, 0x66, 0x7B, 0xCC, 0xE9, 0xBE, 0x86, 0xB9, 0xBB, 0xD0, 0xC8, 0xD2, 0xDC, 0x5F, 0x95, 0x83, 0x28, 0x55, 0x21, 0x1E, 0xEE, 0xCF, 0x23, 0xB7, 0x6D, 0xE0, 0x9A, 0x87, 0x99, 0xFB, 0x82, 0x50, 0xD0, 0x2D, 0xC4, 0xFB, 0xA0, 0x11, 0x2F, 0xDD, 0x05, 0x7E, 0x1C, 0xE3, 0xFB, 0x98, 0x69, 0xD4, 0x49, 0x2F, 0x0D, 0xF6, 0x61 }, + .access_desc_signature = { 0x62, 0xFE, 0xD9, 0x12, 0x3D, 0x99, 0x53, 0xC4, 0x20, 0x25, 0xDE, 0x59, 0xEA, 0x6E, 0xF3, 0x16, 0x5B, 0x36, 0xBA, 0x1C, 0xB3, 0xB5, 0x48, 0x37, 0xD2, 0xA4, 0x04, 0xE5, 0x14, 0xC6, 0xE7, 0x22, 0x14, 0x40, 0x6F, 0x92, 0x6A, 0x9B, 0xDF, 0xDE, 0xFA, 0xCE, 0x3C, 0xBB, 0x4B, 0xC4, 0x66, 0xA8, 0x86, 0x58, 0xAC, 0xEB, 0x2F, 0xB7, 0xA3, 0xEC, 0xEA, 0x31, 0x23, 0x61, 0xF6, 0x72, 0x1E, 0x26, 0x8A, 0x1D, 0x68, 0x2A, 0x2A, 0x21, 0x5A, 0xA2, 0x6A, 0xBD, 0xCE, 0xC0, 0x19, 0x08, 0x61, 0x64, 0xB3, 0xF6, 0x90, 0xB1, 0x34, 0xF8, 0x50, 0x6F, 0x83, 0xB6, 0x8D, 0x35, 0x12, 0x7F, 0x9C, 0x7B, 0x6E, 0x3C, 0x4E, 0xD1, 0xFD, 0xC3, 0x30, 0xD2, 0xE8, 0x7E, 0x15, 0x1F, 0xAD, 0xDB, 0x1D, 0x92, 0xDA, 0x8C, 0x4E, 0xE9, 0x84, 0x83, 0xFF, 0x1A, 0x09, 0x77, 0x05, 0x5A, 0xCF, 0x5C, 0x8B, 0x4F, 0x68, 0x36, 0xC8, 0xDA, 0x5B, 0x1A, 0x5A, 0x49, 0xF9, 0xA1, 0xF2, 0xC8, 0x02, 0xFD, 0x69, 0x1F, 0x1D, 0xB3, 0xE8, 0xF8, 0xE1, 0x6B, 0x15, 0x9A, 0x5E, 0x41, 0x84, 0x06, 0x1F, 0x2A, 0xB3, 0xB2, 0xA1, 0xDC, 0x63, 0x81, 0xB3, 0x6B, 0x4B, 0x21, 0x67, 0x19, 0x82, 0x52, 0xFE, 0x75, 0x96, 0xA1, 0xDF, 0x02, 0xD4, 0x07, 0x1F, 0x1B, 0x88, 0x12, 0x5A, 0x76, 0x54, 0xC4, 0x06, 0x2D, 0xB1, 0xAA, 0x41, 0x3C, 0x9F, 0x43, 0xA2, 0x75, 0x20, 0x39, 0xB6, 0x06, 0xF9, 0x9C, 0xFC, 0x00, 0xC5, 0xBC, 0x84, 0x13, 0x80, 0xE4, 0x10, 0x1A, 0xCD, 0x95, 0xBB, 0xF2, 0xDC, 0x57, 0x7B, 0xBA, 0x87, 0x05, 0x0B, 0x96, 0xC1, 0xCD, 0x60, 0xC7, 0x10, 0x44, 0x78, 0x0E, 0x0F, 0x2F, 0x91, 0x54, 0x6C, 0xDE, 0xB8, 0x14, 0x46, 0xF3, 0x9C, 0xAC, 0x7B, 0xAA, 0xE7, 0x1B, 0x52, 0xD6, 0xBE, 0x71, 0x97, 0x22 } + }, + { + .type = desc_DlpChild, + .fw_minor = 29, + .modulus = { 0xB9, 0xDE, 0x3D, 0xC0, 0x55, 0xB9, 0xCC, 0x3F, 0x55, 0xE0, 0x61, 0x1D, 0x6F, 0xCF, 0x3E, 0x7F, 0xE2, 0xF7, 0xF5, 0xAD, 0x5C, 0x02, 0x7F, 0x17, 0x5B, 0x44, 0x2F, 0x2D, 0xDC, 0xD4, 0xA6, 0x63, 0xD2, 0xA7, 0x82, 0xD3, 0x00, 0x77, 0xC8, 0x0B, 0x28, 0x09, 0x3D, 0x81, 0x86, 0x93, 0xF5, 0xF6, 0xE4, 0x69, 0x3B, 0x60, 0x4C, 0x7F, 0x8D, 0x72, 0xA3, 0x22, 0x42, 0x86, 0x87, 0x06, 0xD8, 0x29, 0x89, 0x8A, 0x9F, 0x5F, 0x6C, 0x06, 0x0C, 0x96, 0x84, 0x00, 0x24, 0x5D, 0x0B, 0xEA, 0x15, 0xEC, 0xAD, 0x90, 0xA4, 0x0C, 0x7B, 0xAE, 0x0E, 0x85, 0x3E, 0xA2, 0x20, 0x04, 0xE8, 0xD9, 0x59, 0x0F, 0x31, 0x0E, 0xD4, 0x5D, 0xC1, 0x18, 0xED, 0x0E, 0xB4, 0xD2, 0x5E, 0x65, 0xA2, 0x78, 0x0C, 0x76, 0x03, 0x3A, 0x71, 0x18, 0xE4, 0x38, 0x44, 0x14, 0xE0, 0x93, 0x84, 0xFE, 0x34, 0x82, 0xCA, 0x0B, 0xB8, 0xF2, 0x41, 0xAB, 0x63, 0xF3, 0xDE, 0xAE, 0xF4, 0x36, 0x81, 0xA4, 0x78, 0x7B, 0xF9, 0xA8, 0xFB, 0xC9, 0xA7, 0x6E, 0xA4, 0xD5, 0xE2, 0xA9, 0xD8, 0xD9, 0xE8, 0x98, 0x1B, 0x25, 0x75, 0x00, 0x11, 0x51, 0x97, 0x62, 0x0D, 0xF0, 0x0C, 0xE9, 0x6B, 0x0C, 0xEE, 0xCE, 0x25, 0x2C, 0x3F, 0xDF, 0xBE, 0x54, 0xD5, 0xD6, 0x5E, 0xEE, 0x1F, 0x73, 0xFC, 0xE8, 0xEC, 0xB3, 0x8A, 0x48, 0x9F, 0x6A, 0xC1, 0x63, 0x85, 0xE4, 0x94, 0x85, 0x8F, 0x3D, 0x9D, 0x43, 0xB4, 0xA7, 0x4C, 0x82, 0xA3, 0x0B, 0x67, 0x43, 0x12, 0x31, 0x77, 0x89, 0xB0, 0xD5, 0x00, 0x1B, 0x52, 0x29, 0xCE, 0x54, 0xC7, 0xC4, 0x7D, 0xB6, 0x69, 0x7B, 0xFE, 0xDC, 0xDB, 0x4E, 0xD8, 0x58, 0x42, 0x14, 0x34, 0x72, 0x64, 0xBC, 0x09, 0x6D, 0xAC, 0xD3, 0xC4, 0x1B, 0x5C, 0x8E, 0xF9, 0xBE, 0x84, 0xCD, 0x9A, 0x86, 0x4B, 0x17 }, + .priv_exponent = { 0xAA, 0x51, 0x62, 0x58, 0x9A, 0xB5, 0x74, 0xDA, 0x1C, 0xC1, 0x4D, 0x7C, 0x81, 0xF6, 0x70, 0x99, 0x13, 0xCC, 0x90, 0x0D, 0xD9, 0xA0, 0x58, 0x01, 0x79, 0x1A, 0x53, 0xF9, 0x3C, 0xC0, 0x87, 0xF0, 0x35, 0x1A, 0x56, 0xA1, 0x2F, 0x6E, 0x93, 0x9A, 0xD5, 0x87, 0x12, 0x1B, 0x5C, 0xCC, 0xBC, 0xB9, 0x0E, 0xB8, 0xF7, 0x35, 0xD9, 0x23, 0x90, 0xE4, 0x19, 0x64, 0xCD, 0x7D, 0x24, 0xC2, 0x3A, 0xD6, 0x65, 0x38, 0xE7, 0xAD, 0xB2, 0xF9, 0x20, 0x13, 0xD4, 0xC5, 0xA4, 0x8C, 0xB6, 0xDC, 0x3C, 0x56, 0xF2, 0xFC, 0xF5, 0xB6, 0x92, 0xA6, 0xFE, 0x9B, 0x4E, 0xB7, 0x95, 0x8B, 0xAA, 0x2B, 0x70, 0x96, 0xA1, 0x27, 0xAB, 0xA6, 0x75, 0xC9, 0x77, 0x80, 0xE0, 0x65, 0x5D, 0x26, 0xD8, 0xE8, 0x14, 0xD3, 0x17, 0x46, 0x38, 0x58, 0xCC, 0xD8, 0x5A, 0x5A, 0x9F, 0x27, 0xCE, 0xD8, 0x7A, 0x19, 0xD7, 0x35, 0xB2, 0x32, 0xAF, 0x47, 0x2E, 0x9F, 0x4B, 0x64, 0xEC, 0x1F, 0xC6, 0x40, 0xD0, 0x2C, 0x47, 0xD1, 0xEA, 0x33, 0xE5, 0x0E, 0x80, 0xFC, 0x68, 0xEC, 0x8C, 0x12, 0x33, 0xCE, 0x34, 0x28, 0x79, 0xFA, 0x05, 0x5D, 0x70, 0x15, 0xDE, 0xB1, 0x22, 0x85, 0x18, 0x63, 0x15, 0x35, 0x57, 0x04, 0x17, 0x64, 0x20, 0xC8, 0x52, 0x44, 0x64, 0x5E, 0x47, 0x4E, 0x5F, 0x80, 0x21, 0x16, 0x94, 0x4B, 0x18, 0x11, 0x36, 0x67, 0x3B, 0x6C, 0x69, 0x19, 0xCF, 0xC9, 0x05, 0x85, 0x9B, 0x3A, 0xDE, 0x12, 0x1E, 0x0A, 0xC6, 0x22, 0xA8, 0xC7, 0x9A, 0x34, 0x14, 0x98, 0xFD, 0xD9, 0x0F, 0xE8, 0x64, 0xE6, 0x89, 0x63, 0x6E, 0x17, 0x76, 0xD7, 0x1B, 0x6F, 0x92, 0x00, 0xD8, 0xBB, 0xF6, 0xA0, 0x65, 0x9D, 0xAA, 0x7A, 0x0E, 0x4B, 0x56, 0xA5, 0x33, 0xDA, 0x3F, 0x5D, 0xFE, 0xD3, 0xAD, 0x6E, 0x0E, 0xB3, 0xD4, 0x41 }, + .access_desc_signature = { 0x97, 0x84, 0x97, 0xEE, 0x4F, 0x35, 0xCC, 0xBE, 0x08, 0xB4, 0x5D, 0x7E, 0x17, 0xC3, 0x94, 0x2B, 0x4D, 0x3A, 0xA5, 0xB5, 0x01, 0xD4, 0xAE, 0x2A, 0x90, 0x26, 0x21, 0x8F, 0x56, 0x05, 0xB9, 0xA2, 0x5E, 0xCE, 0x73, 0xC7, 0x42, 0xDC, 0x99, 0xD2, 0x7C, 0x08, 0x62, 0xBF, 0x10, 0x7A, 0xC1, 0x5D, 0x22, 0x53, 0x8F, 0x63, 0x2D, 0x73, 0xF3, 0x05, 0xDA, 0x9D, 0x6A, 0xF8, 0xB9, 0x5B, 0x80, 0xB4, 0x30, 0xB3, 0x11, 0xF7, 0x96, 0x8A, 0xCF, 0x70, 0xD7, 0x62, 0x6E, 0x99, 0x32, 0xFD, 0x74, 0x34, 0x16, 0xFD, 0x17, 0x1F, 0xB1, 0xEC, 0xA4, 0x0F, 0x52, 0x13, 0x9F, 0x62, 0x0D, 0xE0, 0x50, 0xA6, 0xA0, 0x7B, 0x69, 0x95, 0xE0, 0xE9, 0xBB, 0x38, 0x0C, 0x62, 0xE0, 0xE3, 0xCE, 0x82, 0xE0, 0xB9, 0xE0, 0xF6, 0x61, 0x50, 0xBF, 0xA8, 0x18, 0x15, 0x38, 0xFE, 0xFA, 0x8C, 0xBA, 0xA5, 0xB9, 0x9C, 0x05, 0xA6, 0x91, 0x5C, 0xA7, 0x13, 0x6F, 0x13, 0x3F, 0xF1, 0xF6, 0x68, 0xAF, 0x40, 0xEC, 0x27, 0xE0, 0x33, 0x6B, 0xCF, 0x26, 0x06, 0xF8, 0x6A, 0x13, 0x6C, 0xBC, 0xDB, 0xAF, 0x6F, 0x78, 0xA0, 0x80, 0x10, 0x8F, 0xB6, 0x91, 0x5A, 0x43, 0x2C, 0x5F, 0x1D, 0xBA, 0xB4, 0x5E, 0xBE, 0xAE, 0x53, 0x09, 0x17, 0x5B, 0x6C, 0xC1, 0x5E, 0x0F, 0x72, 0x6E, 0xD6, 0x10, 0x0B, 0xC3, 0x26, 0xDC, 0xAF, 0xCA, 0x28, 0xAB, 0x00, 0x67, 0x04, 0xE3, 0x54, 0xE8, 0x95, 0xC6, 0x23, 0xB6, 0x79, 0x70, 0xA4, 0x87, 0x6D, 0x12, 0x48, 0xCC, 0x11, 0x86, 0xEC, 0x82, 0xF4, 0x30, 0xC9, 0xB1, 0x6D, 0x08, 0xA7, 0xEA, 0x8C, 0x6A, 0x97, 0xAA, 0x89, 0xD5, 0xC5, 0x07, 0xA9, 0xD5, 0xCF, 0x09, 0x08, 0xBC, 0x56, 0x63, 0x8D, 0x70, 0x2F, 0x64, 0xAF, 0x51, 0x9E, 0x22, 0xA4, 0x88, 0xF0, 0xDC, 0x56, 0x72, 0x28 } + }, + /* CTR_SDK 4 (4.2.8) */ + { + .type = desc_Application, + .fw_minor = 33, + .modulus = { 0xCF, 0xEC, 0xB2, 0x48, 0x03, 0x6D, 0xB8, 0x09, 0xE3, 0x5C, 0x6C, 0x62, 0x2C, 0xA9, 0x49, 0xE1, 0xF4, 0xF4, 0x0C, 0x6C, 0xC3, 0xE5, 0x2F, 0x9D, 0x50, 0xA0, 0x2B, 0x5A, 0x00, 0xC6, 0x72, 0x00, 0x0B, 0xA3, 0x04, 0x5D, 0x94, 0x46, 0xE7, 0x00, 0x1B, 0x48, 0x85, 0xB5, 0x61, 0x2C, 0xC9, 0x74, 0xCA, 0x2B, 0x43, 0x13, 0xC1, 0x78, 0x97, 0x5C, 0x33, 0x2F, 0x07, 0xC7, 0x85, 0xF0, 0xDA, 0xDB, 0x60, 0x96, 0x50, 0x0F, 0x7C, 0x4B, 0x7A, 0xD7, 0x17, 0x9D, 0xE4, 0xE5, 0xC3, 0xAB, 0x6F, 0x5D, 0xA5, 0x78, 0x32, 0xAD, 0x04, 0xDD, 0x96, 0x6E, 0xDC, 0x75, 0xFF, 0xC2, 0x2F, 0xFA, 0xA2, 0xEE, 0x46, 0x89, 0xCD, 0xAE, 0x69, 0x92, 0xA4, 0x48, 0xBC, 0x46, 0x47, 0xC4, 0x8C, 0x89, 0x63, 0xE1, 0x0A, 0x4D, 0x1C, 0xDC, 0x46, 0x2F, 0x5B, 0x70, 0x8A, 0x7C, 0xE9, 0x22, 0x9C, 0x09, 0x0B, 0xA8, 0x97, 0x40, 0xCA, 0x2A, 0x7D, 0x84, 0xA1, 0x04, 0x4A, 0x2E, 0xDB, 0xD7, 0xD0, 0x64, 0x43, 0x9C, 0xD0, 0x78, 0x11, 0x41, 0x88, 0x33, 0xDD, 0x31, 0x62, 0x90, 0x2D, 0x17, 0xF2, 0xC6, 0xA9, 0x2B, 0x9C, 0x70, 0xAB, 0xDC, 0xD3, 0xAB, 0x5D, 0xDA, 0xEE, 0x3D, 0x6C, 0x0E, 0x81, 0xFF, 0xF6, 0x67, 0x5A, 0x44, 0xF9, 0xAC, 0x07, 0x3D, 0x23, 0x94, 0x75, 0x65, 0x93, 0x20, 0x0C, 0xC5, 0x76, 0x1D, 0x0F, 0x65, 0x06, 0x3D, 0x21, 0xA2, 0xF0, 0x96, 0x80, 0xB7, 0x0A, 0x49, 0x53, 0x38, 0xA3, 0x5D, 0xC0, 0x74, 0x3C, 0xA4, 0xD9, 0x40, 0x36, 0x85, 0x1F, 0x8C, 0xD1, 0x2D, 0x15, 0xF9, 0xEF, 0x24, 0xA9, 0x7E, 0x9D, 0xB2, 0x1E, 0xF8, 0xA0, 0x72, 0x81, 0x17, 0x77, 0x73, 0xB1, 0x56, 0x7F, 0xAD, 0x05, 0xA2, 0xD2, 0x30, 0x5A, 0xF5, 0xD3, 0xAF, 0x0F, 0x10, 0x4A, 0x52, 0xD8, 0x09, 0x47, 0x97 }, + .priv_exponent = { 0x8C, 0xBD, 0xB2, 0x3B, 0xCE, 0x9E, 0x51, 0x09, 0xD8, 0x6D, 0x72, 0x2B, 0xCE, 0x01, 0x55, 0x32, 0x6E, 0xC5, 0x57, 0x37, 0xB4, 0x2E, 0x09, 0x59, 0xD9, 0xFE, 0x60, 0xF9, 0xCE, 0x36, 0x85, 0x6A, 0x04, 0x76, 0x76, 0xF9, 0x04, 0xEA, 0x2D, 0x68, 0xC4, 0x0F, 0x05, 0xFA, 0xAD, 0x69, 0x4C, 0x80, 0x12, 0x6C, 0xD0, 0x3D, 0xAA, 0x22, 0xFF, 0x89, 0x78, 0x57, 0xE8, 0x53, 0x25, 0x15, 0xD0, 0x7E, 0xD8, 0x55, 0x46, 0xA2, 0x04, 0xC7, 0x6E, 0xC1, 0xF3, 0x89, 0x7C, 0x2C, 0x0E, 0x93, 0x97, 0x91, 0x72, 0xF4, 0xF6, 0x90, 0x69, 0x0F, 0xB8, 0xC9, 0x17, 0xCF, 0x83, 0xAC, 0xA5, 0x1F, 0x69, 0x74, 0x12, 0x29, 0x2B, 0x21, 0x58, 0xF2, 0xDA, 0xE3, 0x25, 0x16, 0x09, 0x74, 0x40, 0x90, 0xAB, 0x1B, 0xE4, 0x06, 0x28, 0x77, 0xED, 0xC6, 0x16, 0x86, 0x0A, 0x27, 0xDD, 0x03, 0x01, 0x4D, 0x9A, 0x26, 0x6E, 0xC8, 0x9F, 0xD3, 0x9A, 0x4B, 0x59, 0xD1, 0x10, 0x9B, 0xEB, 0xA9, 0x58, 0x72, 0xBD, 0xA1, 0xFE, 0x9D, 0x86, 0xED, 0x29, 0xE9, 0x29, 0x49, 0x62, 0x4B, 0xD8, 0x7D, 0x2A, 0x7A, 0x66, 0x1B, 0xE5, 0x04, 0x81, 0x56, 0x10, 0x50, 0xAF, 0xB8, 0x48, 0x27, 0xC1, 0xC9, 0x46, 0xBD, 0x3F, 0x16, 0x06, 0xA5, 0x3D, 0x04, 0x9F, 0x0D, 0x54, 0x71, 0x1C, 0xF4, 0x82, 0xC0, 0x66, 0x74, 0xEA, 0x9C, 0x83, 0x3C, 0x27, 0x01, 0xDF, 0x6F, 0x56, 0xA8, 0x1B, 0xE3, 0x68, 0x55, 0x9F, 0xAB, 0x90, 0x67, 0x20, 0x25, 0xFA, 0x3D, 0x51, 0x2A, 0x23, 0x16, 0xCB, 0x06, 0x5A, 0xAD, 0xAC, 0xC8, 0x47, 0xF9, 0x39, 0x2E, 0x6A, 0xF8, 0xFA, 0x0A, 0xE8, 0x8A, 0x64, 0x84, 0x6B, 0xED, 0xDA, 0x8F, 0x2A, 0x08, 0x86, 0x8F, 0x56, 0x69, 0x64, 0xC3, 0x98, 0x55, 0x37, 0x9A, 0x48, 0x40, 0xDA, 0xD5, 0x03, 0x21 }, + .access_desc_signature = { 0xDF, 0x1C, 0x8B, 0x98, 0xE4, 0x6F, 0xA2, 0x35, 0x6C, 0xC3, 0x18, 0x17, 0x98, 0xF3, 0xCE, 0x54, 0x7E, 0x14, 0x2E, 0x7F, 0x1E, 0xD8, 0x6D, 0xCF, 0xBC, 0x29, 0x4E, 0xFE, 0x32, 0x2E, 0xC1, 0x11, 0xAD, 0x46, 0x9A, 0xC6, 0x70, 0xEA, 0xEE, 0x28, 0x55, 0x22, 0xE1, 0x36, 0x05, 0x1C, 0x04, 0x8A, 0xCE, 0x0F, 0x0C, 0x83, 0x8F, 0xC8, 0xD6, 0xDE, 0x11, 0x8E, 0xEA, 0xCF, 0xAD, 0x9B, 0xCF, 0x81, 0x0D, 0xEB, 0x71, 0x13, 0xB3, 0xD3, 0xAE, 0x83, 0x02, 0x4C, 0x0E, 0x10, 0x50, 0x59, 0x3C, 0xEE, 0x60, 0x06, 0xFB, 0x8C, 0x7F, 0xC2, 0x20, 0x24, 0x01, 0x62, 0x55, 0x87, 0x60, 0x0F, 0xAD, 0xFA, 0x73, 0x2E, 0xF6, 0x65, 0x62, 0xD2, 0xE5, 0x10, 0x45, 0x69, 0x70, 0x39, 0x03, 0xD1, 0x39, 0xEC, 0x50, 0xC1, 0xD4, 0x25, 0x39, 0xB2, 0x90, 0x11, 0x4E, 0x95, 0xCB, 0x19, 0xEB, 0xCA, 0x0F, 0xB5, 0xFA, 0xC7, 0xB0, 0xE2, 0xD7, 0xE0, 0x71, 0xC3, 0xE5, 0x55, 0x33, 0x9E, 0x5C, 0xDC, 0x4D, 0x3B, 0x51, 0x11, 0x0D, 0x31, 0x78, 0x96, 0xCA, 0xD7, 0x18, 0x58, 0xEE, 0x00, 0xE9, 0x28, 0xF2, 0x68, 0x76, 0xD4, 0x57, 0xFE, 0x65, 0xB1, 0x4B, 0x49, 0x3F, 0xF6, 0xA6, 0x58, 0x4A, 0xC7, 0xFC, 0xC4, 0xBB, 0x61, 0xBC, 0x58, 0x8D, 0x55, 0x65, 0xE6, 0x0A, 0x79, 0x39, 0x41, 0xB8, 0x80, 0x61, 0xF7, 0x05, 0xC3, 0xFE, 0xD6, 0x8B, 0x09, 0x82, 0xC2, 0x5F, 0xA6, 0x56, 0xF9, 0xEE, 0x1D, 0x0E, 0x06, 0x3E, 0x9F, 0x3F, 0xF1, 0x93, 0x9A, 0x4F, 0xA2, 0xD5, 0x91, 0x87, 0x8A, 0xFE, 0xCF, 0xC3, 0xFC, 0x8A, 0xB1, 0xC4, 0x78, 0xE9, 0xD1, 0x1A, 0xF7, 0xB1, 0xD3, 0x20, 0xCB, 0x83, 0xBE, 0x03, 0xD5, 0xCA, 0xA5, 0x5E, 0x17, 0xA6, 0x91, 0x10, 0xD4, 0xBE, 0x23, 0xD6, 0x4B, 0x4F, 0x03, 0xA9, 0xAE } + }, + { + .type = desc_DlpChild, + .fw_minor = 33, + .modulus = { 0xB3, 0x16, 0x68, 0xF1, 0xED, 0x59, 0xC8, 0x7F, 0xC6, 0x50, 0x21, 0xFE, 0x36, 0x7C, 0x55, 0xE7, 0x07, 0xF9, 0x1D, 0x1B, 0xF5, 0xB1, 0x2A, 0x6B, 0x3A, 0xDE, 0x2D, 0x4C, 0x51, 0xCD, 0x4C, 0x9F, 0xEE, 0x1D, 0xE4, 0xE8, 0xF0, 0xFD, 0x09, 0x8E, 0x0F, 0x92, 0x5F, 0xDB, 0x9C, 0x5C, 0x15, 0x55, 0x1A, 0x4D, 0x04, 0x8C, 0xB0, 0xA4, 0x88, 0x97, 0xC4, 0xD5, 0x92, 0x04, 0x42, 0x33, 0x84, 0x81, 0x06, 0xD6, 0xF2, 0x17, 0xDE, 0x83, 0x17, 0x50, 0xD0, 0x47, 0x61, 0x14, 0x0D, 0xB7, 0xC7, 0xA0, 0xC1, 0x8B, 0x82, 0x47, 0x13, 0xEE, 0x76, 0xA2, 0xA3, 0x8D, 0xCE, 0x55, 0xC1, 0xF3, 0x7A, 0xEA, 0x91, 0xE1, 0xB9, 0x2F, 0x8F, 0x9B, 0xC3, 0x7B, 0x51, 0x2F, 0xE7, 0xAD, 0x93, 0x9C, 0xFD, 0xDF, 0x19, 0xC8, 0x6C, 0x24, 0xC2, 0xE2, 0x91, 0x97, 0x1F, 0xEB, 0x4B, 0xD4, 0x46, 0x6C, 0x06, 0x93, 0xAF, 0xF5, 0x5E, 0x8F, 0x77, 0x25, 0xC4, 0x28, 0xC0, 0x82, 0x4A, 0x78, 0xE9, 0x14, 0x08, 0xC3, 0xC3, 0x58, 0x24, 0x44, 0x2D, 0x2B, 0xA7, 0xEE, 0x28, 0xEF, 0x1B, 0x6D, 0xAA, 0x9C, 0xED, 0x7F, 0x35, 0xCE, 0x86, 0x5C, 0x6B, 0x8A, 0x23, 0xD3, 0x9D, 0x05, 0x8F, 0xD2, 0x41, 0x93, 0x1D, 0x1D, 0x7E, 0xB0, 0x46, 0x23, 0x63, 0x07, 0xEA, 0x5F, 0x26, 0xE3, 0x81, 0x27, 0xB3, 0x95, 0xB1, 0x93, 0x59, 0xD4, 0x1A, 0xB8, 0x73, 0xD0, 0x09, 0x95, 0x2B, 0xE8, 0x8B, 0xE2, 0x73, 0x5F, 0x34, 0xB9, 0x98, 0x82, 0xF0, 0x11, 0xC6, 0x8F, 0x12, 0x4D, 0x09, 0x57, 0x10, 0x97, 0x22, 0x0E, 0xC8, 0x7D, 0x40, 0xC1, 0x9D, 0x12, 0x1F, 0x71, 0xFE, 0x1E, 0x1A, 0x8C, 0x3F, 0x56, 0xAC, 0x43, 0xC3, 0x66, 0x0C, 0x81, 0xAE, 0xC1, 0x8F, 0x68, 0xFF, 0x87, 0x07, 0x3C, 0xCD, 0x0A, 0x23, 0xDE, 0xBA, 0x9B }, + .priv_exponent = { 0x77, 0xC2, 0x7A, 0xB7, 0x9E, 0x13, 0xB6, 0x62, 0xCC, 0x09, 0x76, 0x51, 0xFB, 0xB9, 0xB5, 0xF0, 0x63, 0x82, 0x91, 0x96, 0xCA, 0xFC, 0x88, 0xF3, 0x60, 0x50, 0x87, 0x56, 0x4C, 0x35, 0xD0, 0x11, 0xFB, 0x38, 0x7E, 0x85, 0xCF, 0xF2, 0x46, 0xDB, 0x7B, 0x4A, 0x55, 0x54, 0x15, 0x01, 0xF7, 0x3A, 0x0B, 0xF6, 0x89, 0x1E, 0x54, 0x5A, 0x13, 0x05, 0xFB, 0x19, 0x1F, 0x26, 0x3D, 0xE7, 0x19, 0xAA, 0xF7, 0x19, 0xF2, 0x97, 0x47, 0xB3, 0xBE, 0x79, 0xCA, 0x6E, 0x91, 0x5A, 0xC9, 0xB9, 0xA6, 0x83, 0xB8, 0x2A, 0x45, 0x1A, 0xA7, 0x17, 0x86, 0xBA, 0x48, 0x49, 0x62, 0x3C, 0x33, 0x11, 0x51, 0x97, 0x5F, 0xAA, 0xE5, 0x1E, 0x0B, 0x19, 0x0C, 0xE6, 0x80, 0x6A, 0x5A, 0xB1, 0xD6, 0xCE, 0xDB, 0x6E, 0xC0, 0x5D, 0x29, 0x04, 0x84, 0x56, 0xE3, 0x29, 0x7E, 0xAC, 0xE8, 0xEE, 0xB1, 0x91, 0x37, 0xEB, 0x98, 0x9C, 0xBD, 0x02, 0x6A, 0x78, 0x61, 0xB0, 0x79, 0x1A, 0x9F, 0x30, 0x86, 0xF6, 0x71, 0x5A, 0x5A, 0x12, 0xA1, 0x9E, 0xA1, 0x68, 0x03, 0xE5, 0x95, 0xA8, 0x38, 0x58, 0x87, 0x08, 0x57, 0x35, 0x32, 0x47, 0x3B, 0xFC, 0x02, 0x6F, 0xCE, 0x55, 0x61, 0xA3, 0x2A, 0x6B, 0x2F, 0xF8, 0xEE, 0x8D, 0xFA, 0x43, 0x33, 0x02, 0x63, 0x47, 0x02, 0x78, 0x5A, 0x7F, 0x64, 0x07, 0x92, 0xB7, 0x7C, 0x09, 0x7C, 0xFE, 0x2D, 0x1C, 0xFC, 0x77, 0x9F, 0x19, 0x20, 0xDD, 0x6D, 0x4C, 0xFE, 0x49, 0x09, 0x47, 0xCA, 0x9B, 0x1C, 0x8C, 0x1F, 0x37, 0xAC, 0x14, 0x85, 0x56, 0xC0, 0xFD, 0xD6, 0x01, 0xB3, 0x40, 0xA3, 0x1A, 0x32, 0x78, 0xA0, 0xDD, 0x21, 0x75, 0xBF, 0x24, 0xD2, 0x93, 0x85, 0xED, 0x22, 0xAD, 0x99, 0x91, 0x87, 0x4A, 0xEC, 0xC0, 0x6C, 0x71, 0x00, 0x76, 0x08, 0x23, 0xA2, 0xF3, 0xCF, 0x61 }, + .access_desc_signature = { 0xAC, 0xE2, 0xA7, 0xC3, 0x00, 0xDE, 0xE8, 0xE9, 0xE0, 0x03, 0xB3, 0x54, 0x08, 0xA8, 0xF8, 0x3A, 0x2E, 0xD8, 0x10, 0x6B, 0xEC, 0xDC, 0x4E, 0xEE, 0x62, 0x10, 0x71, 0x49, 0xD4, 0x43, 0xB1, 0x0E, 0x6B, 0x8C, 0xD7, 0x54, 0xD5, 0x62, 0x28, 0x3F, 0xAA, 0xDE, 0xA9, 0x7D, 0xED, 0x37, 0x7C, 0xE7, 0x89, 0x0B, 0x02, 0xB2, 0x72, 0x4B, 0x17, 0xDB, 0xE2, 0xD3, 0x7C, 0x94, 0x12, 0x3F, 0x2E, 0xA1, 0x08, 0x99, 0xCC, 0x7F, 0x93, 0xE6, 0x38, 0xC9, 0x37, 0x84, 0xD7, 0x11, 0x9D, 0x02, 0x4D, 0x66, 0xB4, 0x70, 0x9F, 0xD8, 0xC6, 0xDD, 0xD5, 0x13, 0x52, 0xF0, 0xA6, 0x78, 0x8C, 0x8E, 0x15, 0xA0, 0xA1, 0xF3, 0xC4, 0xC3, 0x48, 0x45, 0xA5, 0xBE, 0xC9, 0x7A, 0x8B, 0xD3, 0x95, 0xA5, 0x4C, 0xF1, 0xB3, 0x0C, 0x6C, 0x76, 0xA7, 0x57, 0xA1, 0x77, 0xDF, 0x2F, 0xC8, 0x06, 0xA6, 0x0D, 0x1A, 0x09, 0xE4, 0x38, 0x64, 0x07, 0xBE, 0x6A, 0xD2, 0xA0, 0xC0, 0xEC, 0x09, 0x64, 0x9F, 0x0D, 0x93, 0x0C, 0x89, 0xA2, 0x71, 0xD6, 0xC6, 0xC2, 0x54, 0x79, 0x2A, 0xA4, 0x31, 0x28, 0x24, 0x1A, 0xF3, 0x56, 0x78, 0x63, 0x99, 0x97, 0xA5, 0xCE, 0x8F, 0x52, 0x7A, 0x79, 0x51, 0xEE, 0x4C, 0x8B, 0x00, 0x9D, 0x5C, 0x3E, 0xD5, 0xAA, 0x24, 0x9C, 0x94, 0xC6, 0xA3, 0x99, 0x1B, 0x2D, 0xD4, 0xFF, 0xB4, 0x25, 0x73, 0x13, 0x33, 0x9F, 0x03, 0x6F, 0x1E, 0x75, 0xC4, 0x70, 0xF4, 0x07, 0x4F, 0x18, 0xFE, 0xBD, 0x8F, 0x2C, 0x9B, 0x33, 0xD4, 0x30, 0xA7, 0x18, 0x4A, 0xF1, 0xA4, 0xDD, 0x78, 0x41, 0xA0, 0xB8, 0x02, 0x8D, 0x51, 0x96, 0xBE, 0xE7, 0x17, 0x94, 0x66, 0x65, 0x27, 0xF7, 0x69, 0x48, 0x7E, 0xA9, 0x08, 0x71, 0x20, 0x76, 0xB7, 0x8E, 0xD2, 0xBF, 0x5C, 0x7E, 0x5E, 0x06, 0x45, 0xAB, 0x7E, 0x2E } + }, + { + .type = desc_Demo, + .fw_minor = 33, + .modulus = { 0xB5, 0x11, 0x8D, 0x9E, 0x2D, 0xDB, 0x70, 0x6D, 0x6E, 0xEE, 0xAA, 0x21, 0xE0, 0x4E, 0x80, 0x0A, 0x96, 0x4A, 0x10, 0xD0, 0x9C, 0xD7, 0xD9, 0xD4, 0x94, 0x87, 0x72, 0xA2, 0xAF, 0x02, 0xA0, 0x05, 0x2E, 0xBF, 0x17, 0xEB, 0xFE, 0x5B, 0x9F, 0xB7, 0x0B, 0x1E, 0x3E, 0xF9, 0xAC, 0xBC, 0x7B, 0xB1, 0x56, 0x10, 0x24, 0x5F, 0x57, 0x2C, 0x08, 0xD0, 0x14, 0x79, 0x83, 0x84, 0x6A, 0x45, 0x25, 0xEB, 0xD9, 0xBE, 0x02, 0x21, 0xF7, 0x35, 0xC2, 0x74, 0x57, 0xC5, 0xAC, 0x34, 0x05, 0xC6, 0x9E, 0x82, 0xB8, 0xED, 0x78, 0xC4, 0x3B, 0xFD, 0x23, 0x59, 0x54, 0xD2, 0x0A, 0x0B, 0x5B, 0x25, 0xC0, 0x71, 0xC3, 0x84, 0x3A, 0xA7, 0xF9, 0x99, 0x86, 0xD8, 0xFE, 0x60, 0x10, 0x85, 0x77, 0x57, 0x76, 0x0C, 0x25, 0xE1, 0x18, 0x18, 0x3B, 0x83, 0xFD, 0x36, 0x7C, 0x84, 0x58, 0xC2, 0xC4, 0x68, 0x4F, 0xD1, 0xD7, 0x0A, 0x88, 0xFD, 0xCA, 0x97, 0xA1, 0xE5, 0xCE, 0x72, 0x63, 0xCF, 0x74, 0xD0, 0x20, 0xD9, 0xDE, 0x3F, 0xBB, 0x11, 0xF9, 0x21, 0xAB, 0x3F, 0x54, 0x41, 0xA7, 0xAA, 0xCA, 0xFC, 0xE1, 0x1A, 0x8C, 0x12, 0xC9, 0x39, 0x13, 0x5A, 0x81, 0x29, 0x49, 0xE8, 0xFB, 0x48, 0xC9, 0x4D, 0x50, 0x87, 0xAE, 0x51, 0xFB, 0x94, 0xFC, 0xF0, 0x9C, 0x70, 0x1C, 0xE8, 0x6E, 0x44, 0x53, 0x1E, 0x2F, 0x27, 0x5C, 0xB8, 0xEC, 0xBE, 0xFC, 0xD9, 0x98, 0x6A, 0x08, 0xD0, 0x5C, 0x4D, 0x78, 0x2D, 0x4D, 0x07, 0xAD, 0x5E, 0xB8, 0x51, 0x40, 0xE2, 0x2A, 0x7F, 0xB1, 0x54, 0x47, 0x5C, 0x99, 0x12, 0xC2, 0x6D, 0x5E, 0xED, 0x25, 0x30, 0x6A, 0x99, 0xC5, 0x0D, 0x65, 0x83, 0x68, 0x3A, 0xFD, 0x82, 0x59, 0x0D, 0xCE, 0x0B, 0x49, 0xBE, 0x17, 0x46, 0x51, 0xA9, 0xB6, 0x54, 0xE1, 0x18, 0xBD, 0x49, 0xE6, 0x7F }, + .priv_exponent = { 0x1D, 0x7B, 0x79, 0x32, 0xAB, 0x46, 0xD2, 0xBC, 0x8E, 0xD6, 0x7F, 0x8F, 0x3A, 0x85, 0xAD, 0xA5, 0x8B, 0xA9, 0x0D, 0xA9, 0xDA, 0x0F, 0xEF, 0x61, 0x04, 0xBA, 0x35, 0x39, 0x36, 0x03, 0xD8, 0x68, 0x5F, 0x9F, 0x2F, 0xD6, 0xF6, 0x38, 0x96, 0xFD, 0xE7, 0xEA, 0x89, 0xD8, 0x7F, 0x7E, 0xC5, 0x29, 0x2F, 0xD9, 0x3B, 0x02, 0xE7, 0x1F, 0xBD, 0x63, 0x9C, 0x21, 0xD8, 0xFF, 0x43, 0x8A, 0x74, 0xCD, 0x3D, 0x4C, 0x09, 0xEE, 0xDB, 0xE0, 0xBE, 0x03, 0xD1, 0x92, 0xD7, 0x22, 0x35, 0x5A, 0x8C, 0xCE, 0xBE, 0x2B, 0xB4, 0x81, 0x47, 0x3F, 0x45, 0x75, 0x33, 0x31, 0x6B, 0xFF, 0x43, 0x5D, 0x17, 0x43, 0xAE, 0xD1, 0x25, 0xF7, 0xD9, 0xD5, 0x5C, 0xB6, 0x92, 0x5C, 0xB3, 0xF3, 0xF7, 0x65, 0x9F, 0x4C, 0x05, 0x12, 0xEC, 0xA8, 0x6D, 0x70, 0x65, 0x57, 0x6C, 0xD8, 0xE3, 0xD6, 0xFA, 0xC1, 0xFD, 0x54, 0xE8, 0x34, 0x67, 0x4D, 0x0A, 0x14, 0x2F, 0xA3, 0xD4, 0x81, 0x8C, 0xC3, 0xD0, 0x8B, 0x09, 0x08, 0x90, 0x70, 0x68, 0xA0, 0x0E, 0xD1, 0x0B, 0xAA, 0x71, 0xEC, 0x9A, 0x1A, 0x83, 0xFF, 0xA1, 0x70, 0xEB, 0xAC, 0xF2, 0xE9, 0x80, 0xA1, 0xB8, 0x20, 0x31, 0x83, 0xF5, 0x37, 0x01, 0x72, 0x06, 0x50, 0x05, 0x3F, 0x14, 0xF9, 0x29, 0x48, 0x84, 0xA0, 0x0E, 0xF7, 0xB8, 0x1D, 0xA3, 0x36, 0x5A, 0x78, 0x6D, 0x83, 0x90, 0x27, 0xE3, 0x50, 0x49, 0x2F, 0x65, 0xE5, 0x61, 0xED, 0x65, 0xBE, 0xEA, 0x34, 0xA6, 0x6A, 0xEF, 0x49, 0xB4, 0xE0, 0xBC, 0xC2, 0xA5, 0xB8, 0xEB, 0xA9, 0x2F, 0xBA, 0x26, 0x76, 0xB2, 0x5A, 0x3A, 0x3B, 0xFD, 0xAD, 0xFB, 0xE4, 0x79, 0xE2, 0x85, 0x54, 0x5B, 0xAB, 0x1F, 0x0A, 0xE5, 0x8B, 0x77, 0x3A, 0x10, 0x98, 0x26, 0x74, 0xC8, 0xB0, 0x82, 0xB1, 0xF9, 0x8F, 0x68, 0x59 }, + .access_desc_signature = { 0xD3, 0x7D, 0x42, 0xBA, 0x6A, 0x1E, 0xD8, 0x07, 0x3C, 0x4A, 0xC4, 0xCD, 0x8C, 0x68, 0x3D, 0xCD, 0xCD, 0xBD, 0x9D, 0xCE, 0xB5, 0x2A, 0xF9, 0x63, 0x3D, 0xA9, 0x54, 0x0A, 0x2E, 0x4C, 0xE1, 0x60, 0x4B, 0xD0, 0xC9, 0xEB, 0xEF, 0x31, 0x65, 0x70, 0xB9, 0x0E, 0x06, 0x3B, 0x3D, 0x42, 0x4C, 0x6E, 0x8D, 0x2C, 0xD4, 0x71, 0x29, 0x76, 0xB7, 0xDD, 0x8C, 0xDA, 0xE7, 0xE3, 0x96, 0xA7, 0xAA, 0xF8, 0xCA, 0x05, 0xE8, 0xA7, 0x0A, 0xDD, 0x01, 0x49, 0xBD, 0xF1, 0xA5, 0xE8, 0x16, 0x22, 0xEE, 0x47, 0x1F, 0xEF, 0x28, 0x48, 0x87, 0xA9, 0x2D, 0xFC, 0x4E, 0xD5, 0xA5, 0x98, 0xB1, 0xFE, 0x1B, 0xEB, 0xA9, 0x06, 0x3C, 0x76, 0xD9, 0xAA, 0x0E, 0x9C, 0x60, 0xFC, 0xE9, 0x77, 0x9D, 0x7F, 0x67, 0xAC, 0xF5, 0xC7, 0x49, 0x12, 0xFD, 0x76, 0xAC, 0xD2, 0x54, 0xDB, 0x73, 0x41, 0x10, 0x1F, 0x04, 0x3F, 0xD0, 0x6F, 0xE0, 0x80, 0x24, 0xCC, 0xEE, 0xBF, 0x25, 0x9D, 0x0D, 0x5A, 0x2A, 0x1C, 0xC5, 0xD4, 0xE3, 0x5D, 0x3A, 0xC1, 0x86, 0xD3, 0xD4, 0x52, 0x1C, 0x4C, 0xBF, 0x31, 0xEB, 0x54, 0xCA, 0x4C, 0x06, 0x50, 0x52, 0x87, 0xD4, 0x9D, 0x4A, 0x4B, 0x22, 0xE1, 0x4A, 0xE9, 0x4D, 0x05, 0xA8, 0x57, 0xEC, 0xF8, 0x90, 0xF8, 0x58, 0xC3, 0x8B, 0x3A, 0x0F, 0x88, 0x36, 0xF4, 0xE5, 0x44, 0x10, 0x80, 0x68, 0x86, 0x1D, 0xAE, 0x90, 0x20, 0x03, 0x22, 0x2D, 0x44, 0xBF, 0xAB, 0x2B, 0xA1, 0x14, 0xAD, 0x6B, 0x40, 0x57, 0xDB, 0xBB, 0xDA, 0x09, 0x4C, 0x51, 0x26, 0x9B, 0xE3, 0xD9, 0xF9, 0xE1, 0xBC, 0xF1, 0xF1, 0xCD, 0x30, 0xB4, 0xF5, 0x39, 0xD0, 0xBC, 0xF7, 0x98, 0x05, 0xAF, 0xA8, 0x33, 0x4B, 0xC1, 0x16, 0x0F, 0xF2, 0xC2, 0x79, 0x96, 0xEC, 0xBE, 0xA9, 0xF5, 0x55, 0x7C, 0x82, 0x95, 0x73 } + }, }; \ No newline at end of file diff --git a/makerom/desc/presets.h b/makerom/desc/presets.h index fd15473f..83c71ab3 100644 --- a/makerom/desc/presets.h +++ b/makerom/desc/presets.h @@ -1,196 +1,132 @@ #pragma once - -/* CTR_SDK 1 (1.2.0) (2.27 - 2.28) */ -// DependencyList -static const unsigned char fw1B_dep_list[0x180] = -{ - 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -// APP -static const unsigned char app_fw1B_desc_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char app_fw1B_acex_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -// DLP -static const unsigned char dlp_fw1B_desc_data[0x200] = -{ - 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char dlp_fw1B_acex_data[0x200] = -{ - 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -/* CTR_SDK 2 (2.3.4) */ -static const unsigned char fw1D_dep_list[0x180] = -{ - 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -// APP (2.29) -static const unsigned char app_fw1D_desc_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1D, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char app_fw1D_acex_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1D, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -// APP (2.30) -static const unsigned char app_fw1E_desc_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1E, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char app_fw1E_acex_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1E, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -// DLP (2.29) -static const unsigned char dlp_fw1D_desc_data[0x200] = -{ - 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1D, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char dlp_fw1D_acex_data[0x200] = -{ - 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1D, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -// DEMO (2.30) -static const unsigned char demo_fw1E_desc_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1E, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char demo_fw1E_acex_data[0x200] = -{ - 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1E, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -/* CTR_SDK 3 (3.2.5) (2.32) */ -static const unsigned char fw20_dep_list[0x180] = -{ - 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x33, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x37, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -// APP -static const unsigned char app_fw20_desc_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x20, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char app_fw20_acex_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x20, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -// EC App -static const unsigned char ecapp_fw20_desc_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6E, 0x69, 0x6D, 0x3A, 0x61, 0x6F, 0x63, 0x00, 0x61, 0x6D, 0x3A, 0x61, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x20, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char ecapp_fw20_acex_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6E, 0x69, 0x6D, 0x3A, 0x61, 0x6F, 0x63, 0x00, 0x61, 0x6D, 0x3A, 0x61, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x20, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -/* CTR_SDK 4 (4.2.8) (2.33) */ -// DependencyList -static const unsigned char fw21_dep_list[0x180] = -{ - 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x33, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x37, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -// APP -static const unsigned char app_fw21_desc_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x00, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x21, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -static const unsigned char app_fw21_acex_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x21, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -// DEMO -static const unsigned char demo_fw21_desc_data[0x200] = -{ - 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x00, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x21, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char demo_fw21_acex_data[0x200] = -{ - 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x21, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -// DLP -static const unsigned char dlp_fw21_desc_data[0x200] = -{ - 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x00, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x21, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char dlp_fw21_acex_data[0x200] = -{ - 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x21, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -/* CTR_SDK 5 (5.2.3) (2.35) */ -static const unsigned char fw23_dep_list[0x180] = -{ - 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x33, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x37, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -// APP -static const unsigned char app_fw23_desc_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x00, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x23, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char app_fw23_acex_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x23, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -// EC App -static const unsigned char ecapp_fw23_desc_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6E, 0x69, 0x6D, 0x3A, 0x61, 0x6F, 0x63, 0x00, 0x61, 0x6D, 0x3A, 0x61, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x23, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char ecapp_fw23_acex_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6E, 0x69, 0x6D, 0x3A, 0x61, 0x6F, 0x63, 0x00, 0x61, 0x6D, 0x3A, 0x61, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x23, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -/* SDK 7 (7.1.0) (2.39) */ -static const unsigned char fw27_dep_list[0x180] = -{ - 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x38, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x33, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x37, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -// APP -static const unsigned char app_fw27_desc_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x41, 0x00, 0x00, 0x00, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x74, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x61, 0x6D, 0x3A, 0x61, 0x70, 0x70, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x69, 0x6D, 0x3A, 0x61, 0x6F, 0x63, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x27, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char app_fw27_acex_data[0x200] = -{ - 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x41, 0x00, 0x00, 0x00, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x74, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x61, 0x6D, 0x3A, 0x61, 0x70, 0x70, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x69, 0x6D, 0x3A, 0x61, 0x6F, 0x63, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x27, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; - -/* FIRM (sdk irrelevant) */ -static const unsigned char firm_fwXX_dep_list[0x180] = -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -// FIRM -static const unsigned char firm_fw26_desc_data[0x200] = -{ - 0xFF, 0xFF, 0xFF, 0xFF, 0x38, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xF1, 0x00, 0x01, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 -}; -static const unsigned char firm_fw26_acex_data[0x200] = -{ - 0xFF, 0xFF, 0xFF, 0xFF, 0x38, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x02, 0xF1, 0x00, 0x01, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 +#include "desc/desc.h" + +static const CtrSdkDesc kDescPresets[] = +{ + /* CTR_SDK 1 (1.2.0) (2.27 - 2.28) */ + { + .type = desc_Application, + .fw_minor = 27, + .exheader_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + { + .type = desc_DlpChild, + .fw_minor = 27, + .exheader_desc = { 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + /* CTR_SDK 2 (2.3.4) */ + { + .type = desc_Application, + .fw_minor = 29, + .exheader_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1D, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1D, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + { + .type = desc_DlpChild, + .fw_minor = 29, + .exheader_desc = { 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1D, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1D, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + { + .type = desc_Application, + .fw_minor = 30, + .exheader_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1E, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1E, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + { + .type = desc_Demo, + .fw_minor = 30, + .exheader_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1E, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFB, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x1E, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + /* CTR_SDK 3 (3.2.5) (2.32) */ + { + .type = desc_Application, + .fw_minor = 32, + .exheader_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x20, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x20, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + { + .type = desc_EcApplication, + .fw_minor = 32, + .exheader_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6E, 0x69, 0x6D, 0x3A, 0x61, 0x6F, 0x63, 0x00, 0x61, 0x6D, 0x3A, 0x61, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x20, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6E, 0x69, 0x6D, 0x3A, 0x61, 0x6F, 0x63, 0x00, 0x61, 0x6D, 0x3A, 0x61, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x20, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + /* CTR_SDK 4 (4.2.8) (2.33) */ + { + .type = desc_Application, + .fw_minor = 33, + .exheader_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x00, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x21, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x21, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + { + .type = desc_DlpChild, + .fw_minor = 33, + .exheader_desc = { 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x00, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x21, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x21, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + { + .type = desc_Demo, + .fw_minor = 33, + .exheader_desc = { 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x00, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x21, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x21, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + /* CTR_SDK 5 (5.2.3) (2.35) */ + { + .type = desc_Application, + .fw_minor = 35, + .exheader_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x00, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x23, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x23, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + { + .type = desc_EcApplication, + .fw_minor = 35, + .exheader_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6E, 0x69, 0x6D, 0x3A, 0x61, 0x6F, 0x63, 0x00, 0x61, 0x6D, 0x3A, 0x61, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x23, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6E, 0x69, 0x6D, 0x3A, 0x61, 0x6F, 0x63, 0x00, 0x61, 0x6D, 0x3A, 0x61, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x23, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, + /* SDK 7 (7.1.0) (2.39) */ + { + .type = desc_Application, + .fw_minor = 39, + .exheader_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x41, 0x00, 0x00, 0x00, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x74, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x61, 0x6D, 0x3A, 0x61, 0x70, 0x70, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x69, 0x6D, 0x3A, 0x61, 0x6F, 0x63, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x27, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, + .signed_desc = { 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x18, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x68, 0x69, 0x6F, 0x46, 0x49, 0x4F, 0x00, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x30, 0x24, 0x68, 0x6F, 0x73, 0x74, 0x69, 0x6F, 0x31, 0x63, 0x66, 0x67, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x66, 0x73, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x67, 0x73, 0x70, 0x3A, 0x3A, 0x47, 0x70, 0x75, 0x68, 0x69, 0x64, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x6E, 0x64, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x70, 0x78, 0x69, 0x3A, 0x64, 0x65, 0x76, 0x00, 0x41, 0x50, 0x54, 0x3A, 0x41, 0x00, 0x00, 0x00, 0x61, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x74, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x61, 0x6D, 0x3A, 0x61, 0x70, 0x70, 0x00, 0x00, 0x62, 0x6F, 0x73, 0x73, 0x3A, 0x55, 0x00, 0x00, 0x63, 0x61, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x63, 0x65, 0x63, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x64, 0x6C, 0x70, 0x3A, 0x46, 0x4B, 0x43, 0x4C, 0x64, 0x6C, 0x70, 0x3A, 0x53, 0x52, 0x56, 0x52, 0x64, 0x73, 0x70, 0x3A, 0x3A, 0x44, 0x53, 0x50, 0x66, 0x72, 0x64, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x43, 0x00, 0x00, 0x69, 0x72, 0x3A, 0x55, 0x53, 0x45, 0x52, 0x00, 0x6C, 0x64, 0x72, 0x3A, 0x72, 0x6F, 0x00, 0x00, 0x6D, 0x69, 0x63, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x6E, 0x65, 0x77, 0x73, 0x3A, 0x75, 0x00, 0x00, 0x6E, 0x69, 0x6D, 0x3A, 0x61, 0x6F, 0x63, 0x00, 0x6E, 0x77, 0x6D, 0x3A, 0x3A, 0x55, 0x44, 0x53, 0x70, 0x74, 0x6D, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x73, 0x6F, 0x63, 0x3A, 0x55, 0x00, 0x00, 0x00, 0x73, 0x73, 0x6C, 0x3A, 0x43, 0x00, 0x00, 0x00, 0x79, 0x32, 0x72, 0x3A, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x9F, 0xFA, 0xF0, 0xFF, 0xBF, 0xFF, 0xF1, 0xE7, 0x3F, 0x00, 0xF2, 0x00, 0xF0, 0x91, 0xFF, 0x00, 0xF6, 0x91, 0xFF, 0x50, 0xFF, 0x81, 0xFF, 0x58, 0xFF, 0x81, 0xFF, 0x70, 0xFF, 0x81, 0xFF, 0x78, 0xFF, 0x81, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0x00, 0x02, 0x00, 0xFE, 0x27, 0x02, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } + }, +}; + +static const CtrSdkDepList kExheaderDependencyLists[] = +{ + { + .fw_minor = 27, + .dependency = { 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, + { + .fw_minor = 28, + .dependency = { 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, + { + .fw_minor = 29, + .dependency = { 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, + { + .fw_minor = 30, + .dependency = { 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, + { + .fw_minor = 32, + .dependency = { 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x33, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x37, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, + { + .fw_minor = 33, + .dependency = { 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x33, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x37, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, + { + .fw_minor = 35, + .dependency = { 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x33, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x37, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, + { + .fw_minor = 39, + .dependency = { 0x02, 0x24, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x38, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x15, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x34, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x16, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x26, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x17, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x18, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x28, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1A, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x32, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x29, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x33, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x20, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2B, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x35, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2D, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x21, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x31, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x22, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x37, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2E, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x23, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x02, 0x2F, 0x00, 0x00, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + } }; \ No newline at end of file diff --git a/makerom/desc/prod_sigdata.h b/makerom/desc/prod_sigdata.h deleted file mode 100644 index b68ca75d..00000000 --- a/makerom/desc/prod_sigdata.h +++ /dev/null @@ -1,134 +0,0 @@ -#pragma once - -/* CTR_SDK 2 (2.3.4) */ -// APP -static const unsigned char app_fw1D_prod_hdrpub[0x100] = -{ - 0x9F, 0xA8, 0xD0, 0xBE, 0x14, 0x3F, 0x28, 0xE5, 0xBE, 0x0E, 0xEB, 0x5D, 0x95, 0xEE, 0xD4, 0x76, 0x7E, 0x1C, 0x11, 0x49, 0x13, 0x55, 0x87, 0x22, 0xCF, 0xA4, 0x2B, 0xDE, 0x59, 0x1A, 0xDB, 0xEB, 0x39, 0x14, 0xE0, 0x27, 0xFD, 0x0B, 0x6E, 0x5B, 0xD9, 0x0E, 0xCD, 0xCC, 0xAF, 0x8E, 0xEF, 0x92, 0xFC, 0x9A, 0x3B, 0x2E, 0xB8, 0x9C, 0xDA, 0x20, 0x22, 0x84, 0x0B, 0x7D, 0xFA, 0xCE, 0x4B, 0xB0, 0xC6, 0xAC, 0xC7, 0xB7, 0x44, 0x59, 0xB6, 0x94, 0xD2, 0xA6, 0x87, 0xEA, 0x36, 0xCB, 0xCB, 0x8E, 0x58, 0xB7, 0xE5, 0xD0, 0xDD, 0x62, 0x0E, 0x83, 0x40, 0x03, 0x21, 0x5B, 0x48, 0x9C, 0x6E, 0x4A, 0x6C, 0x1E, 0x10, 0x9D, 0x6B, 0x85, 0xC3, 0x00, 0x59, 0x3C, 0xE0, 0x35, 0x93, 0x87, 0x94, 0x9A, 0x7C, 0x2C, 0x3C, 0x44, 0xE3, 0x3B, 0x15, 0x20, 0x57, 0xAC, 0x86, 0x8B, 0xE8, 0x97, 0xBB, 0x1C, 0x84, 0x8D, 0x95, 0x83, 0xE7, 0x54, 0x39, 0x8F, 0x0B, 0x13, 0xC7, 0xD6, 0x26, 0x6D, 0xA8, 0xBF, 0x96, 0x79, 0x39, 0xF9, 0x11, 0x63, 0xB1, 0x9F, 0x8C, 0x5F, 0x7A, 0xA9, 0x7B, 0xCD, 0xDB, 0xEE, 0x44, 0x8C, 0xF2, 0x83, 0x7C, 0xA8, 0xD9, 0x8D, 0x4B, 0xB0, 0xD7, 0x86, 0x53, 0x1D, 0xD7, 0x94, 0xD1, 0xFA, 0x61, 0x8B, 0x94, 0x20, 0x4D, 0xD8, 0x5C, 0xB5, 0x2C, 0x6E, 0x7E, 0xAC, 0x85, 0xC7, 0x33, 0xBB, 0x4D, 0x34, 0xAB, 0xDC, 0xED, 0x3F, 0xA0, 0xA8, 0xC7, 0xC2, 0x18, 0x48, 0x70, 0x54, 0x06, 0x7E, 0xA9, 0x2E, 0x30, 0x92, 0x04, 0xBA, 0xFD, 0x2A, 0xF9, 0xEC, 0xD1, 0xD9, 0x5D, 0x45, 0xB3, 0x7E, 0x9A, 0x86, 0xB4, 0x7F, 0xD6, 0x7A, 0x7F, 0x02, 0xEE, 0xB1, 0x65, 0x77, 0x68, 0xCB, 0x3D, 0xB2, 0x6C, 0x70, 0xB1, 0xB6, 0x57, 0x1F, 0x67, 0xB3, 0x73, 0x31, 0x0D, 0x89, 0x83, 0x39 -}; - -static const unsigned char app_fw1D_prod_acexsig[0x100] = -{ - 0x05, 0x67, 0xFA, 0xAC, 0x18, 0x3A, 0xCC, 0xC9, 0x1B, 0xCD, 0x49, 0x7F, 0x28, 0x87, 0xF5, 0x8A, 0xD9, 0x86, 0x06, 0xAB, 0x1D, 0x36, 0x41, 0xDB, 0xFD, 0xE5, 0x97, 0x93, 0x1C, 0x9C, 0xAB, 0xB2, 0x62, 0xEB, 0x8B, 0xD6, 0x85, 0xCF, 0x06, 0xC4, 0xC5, 0xB9, 0x7F, 0x3B, 0x6F, 0x07, 0x83, 0x93, 0xBD, 0x26, 0x71, 0x8C, 0xEC, 0xBC, 0x0B, 0x3C, 0xD8, 0x42, 0xF3, 0x19, 0xBE, 0x91, 0xB9, 0xAB, 0x23, 0x39, 0x0F, 0x46, 0x8A, 0xCB, 0xC2, 0x34, 0xD2, 0x39, 0x63, 0xE3, 0x49, 0x59, 0xE0, 0x47, 0x73, 0x4F, 0x8E, 0xC8, 0x87, 0xE7, 0x54, 0xDD, 0xF9, 0x3B, 0x19, 0xD5, 0xB5, 0x78, 0xED, 0x47, 0x4F, 0x53, 0x8F, 0x6D, 0xB6, 0xA8, 0x3A, 0x06, 0x3C, 0x2D, 0x6B, 0xFE, 0x7C, 0x3C, 0x6B, 0x20, 0x47, 0xC9, 0xF5, 0xB7, 0x36, 0x8A, 0x92, 0x27, 0x1A, 0x41, 0x4E, 0xE6, 0x75, 0x6E, 0x88, 0x57, 0xAA, 0x01, 0x43, 0x93, 0x78, 0x5E, 0xE0, 0x3D, 0x59, 0x26, 0xFD, 0x46, 0x09, 0x02, 0xCA, 0x58, 0x45, 0x5A, 0xCB, 0xCC, 0x85, 0xBD, 0x0B, 0xDF, 0xAB, 0xE3, 0xDB, 0x35, 0x36, 0xCC, 0xCF, 0x57, 0x8B, 0x4A, 0x21, 0x1C, 0x3A, 0x2D, 0x90, 0xED, 0xA6, 0xCB, 0x20, 0x21, 0x4A, 0xE4, 0x08, 0xF6, 0xF3, 0x73, 0x5B, 0xA5, 0x73, 0x2B, 0x1C, 0x46, 0x3C, 0x77, 0xB6, 0x43, 0x33, 0xFF, 0xEA, 0xF7, 0xA3, 0xDC, 0x3B, 0xD0, 0xC0, 0xC9, 0x10, 0xF4, 0x86, 0xB5, 0xB1, 0x83, 0x71, 0xF5, 0x6E, 0x67, 0xCC, 0xCA, 0xEE, 0xC0, 0x53, 0xD2, 0x78, 0xA4, 0x2C, 0x86, 0xDF, 0x2F, 0xEF, 0x4D, 0x0C, 0x8B, 0x6B, 0xAE, 0x32, 0xAC, 0x07, 0x2D, 0x4D, 0x6B, 0x4A, 0xEF, 0x13, 0xCE, 0xA7, 0x7C, 0xC9, 0x30, 0x69, 0x88, 0x96, 0x95, 0x8E, 0x11, 0x5C, 0x2A, 0x6D, 0x84, 0x3E, 0x36, 0xDA, 0x9B, 0x2A, 0x20 -}; - -// APP -static const unsigned char app_fw1E_prod_hdrpub[0x100] = -{ - 0xC8, 0xC4, 0x8C, 0x65, 0xA1, 0x76, 0x9B, 0x68, 0x91, 0xF1, 0x2B, 0xDE, 0xDF, 0x0D, 0xE1, 0x44, 0x6B, 0xB2, 0xF9, 0xFA, 0x0C, 0xB8, 0x9A, 0xC7, 0x7E, 0x71, 0xA3, 0x27, 0x57, 0xF3, 0x27, 0x99, 0x15, 0xF2, 0x43, 0x6A, 0x1E, 0x75, 0x4C, 0xA2, 0x7A, 0x77, 0x71, 0x60, 0xFE, 0x14, 0xB6, 0xCB, 0xFC, 0x6E, 0x64, 0x2E, 0x86, 0x9B, 0xFD, 0x17, 0xB5, 0x89, 0xD1, 0xC2, 0x10, 0xFC, 0xCD, 0x81, 0x58, 0x8F, 0x58, 0x43, 0xEC, 0x52, 0x6D, 0xE8, 0x14, 0x32, 0x6E, 0xCD, 0x67, 0x35, 0xDC, 0xDC, 0x58, 0x97, 0xB0, 0xBF, 0xD3, 0xC0, 0x2C, 0xFE, 0x77, 0xEC, 0xE0, 0x33, 0x65, 0xA8, 0x56, 0x52, 0xFD, 0x5B, 0xAB, 0x09, 0xB9, 0xF4, 0x74, 0xB9, 0x98, 0xDA, 0x33, 0x2F, 0x79, 0xD6, 0x52, 0x5D, 0x5A, 0x72, 0xA2, 0xCE, 0x15, 0xC5, 0x4B, 0x0C, 0x92, 0xD6, 0xC9, 0x10, 0x75, 0xFF, 0x6C, 0x8D, 0x9B, 0xDC, 0x34, 0x7A, 0xAA, 0x66, 0x45, 0x60, 0xBC, 0xC6, 0xB6, 0x3B, 0x08, 0x4B, 0x62, 0x2C, 0x95, 0x29, 0xAD, 0xDC, 0xAF, 0xEB, 0x47, 0xB5, 0xF5, 0xD2, 0x1C, 0x6D, 0x23, 0xD4, 0xCA, 0xD8, 0xE5, 0x58, 0x5D, 0x0D, 0x56, 0x95, 0x36, 0x7C, 0x2D, 0xFF, 0x51, 0x8B, 0xA9, 0xC4, 0x2D, 0xA0, 0x60, 0x94, 0xE4, 0xBB, 0x05, 0xF9, 0xFA, 0x08, 0x8C, 0xC6, 0x64, 0x88, 0x07, 0x71, 0x4F, 0x91, 0xB7, 0x62, 0xF6, 0x12, 0xFC, 0xF4, 0xC8, 0x7F, 0x9A, 0x50, 0x7F, 0xFC, 0x73, 0x3B, 0xC4, 0xC6, 0x41, 0x3D, 0xA3, 0x5B, 0x03, 0x54, 0xF6, 0x6C, 0x8C, 0x66, 0xC8, 0xFE, 0xE8, 0x38, 0xA5, 0xC3, 0xDD, 0xC3, 0x9F, 0x25, 0x66, 0x77, 0x75, 0x77, 0x2E, 0xD9, 0xE5, 0x37, 0x15, 0xB6, 0x7D, 0x29, 0x5F, 0xC5, 0x94, 0x31, 0xE0, 0xB2, 0xBA, 0xF3, 0xF7, 0xB6, 0x41, 0x3F, 0xF1, 0x67, 0x03, 0xE3 -}; - -static const unsigned char app_fw1E_prod_acexsig[0x100] = -{ - 0x28, 0xBD, 0x6C, 0x5C, 0xE1, 0x57, 0xAB, 0x22, 0xEB, 0xBF, 0x76, 0xC3, 0x45, 0xDB, 0x13, 0x7E, 0x6F, 0x15, 0x7B, 0x13, 0x86, 0x1C, 0xFA, 0xEA, 0xAC, 0x58, 0x43, 0x48, 0xB1, 0xCD, 0x6D, 0xA8, 0xB9, 0x45, 0x9C, 0x03, 0xBE, 0xBA, 0xCA, 0xF9, 0x7D, 0xF5, 0x78, 0x35, 0x36, 0xB3, 0xFE, 0xD8, 0x07, 0x48, 0x46, 0x31, 0x8D, 0xB6, 0x61, 0x94, 0xF7, 0x27, 0xB5, 0x03, 0x5C, 0x01, 0x5A, 0xEC, 0x92, 0x88, 0x15, 0xEB, 0xC1, 0x19, 0xD1, 0x13, 0xF9, 0x69, 0x3F, 0x75, 0x24, 0x18, 0x36, 0xE0, 0x12, 0x1E, 0xE2, 0x34, 0xC0, 0x79, 0x85, 0x4D, 0xB0, 0xA1, 0x9B, 0x8C, 0x9D, 0x0F, 0xCC, 0x56, 0x45, 0xEB, 0x0E, 0x56, 0xA8, 0x1D, 0xA2, 0x81, 0x94, 0x45, 0x35, 0xE9, 0x4A, 0x5D, 0x29, 0x47, 0xBC, 0x40, 0x5B, 0xA8, 0x67, 0xE6, 0x7C, 0x51, 0x8D, 0xB7, 0x97, 0x4A, 0x88, 0xD2, 0x14, 0xFE, 0x20, 0xFF, 0x7A, 0x10, 0xB7, 0xAB, 0x92, 0x38, 0xF5, 0xAC, 0x12, 0xCB, 0x97, 0xAF, 0xFD, 0x78, 0x93, 0xC6, 0xCC, 0xFF, 0x4F, 0x8C, 0x7F, 0x2D, 0x87, 0xEC, 0x8B, 0x85, 0xAE, 0xB1, 0xBA, 0xF9, 0xE2, 0x88, 0xFA, 0x41, 0xCB, 0x27, 0xAA, 0x19, 0x41, 0xF4, 0x93, 0x90, 0x73, 0x1A, 0xFA, 0xC9, 0x15, 0x58, 0xC7, 0x51, 0x02, 0x83, 0xB4, 0xD2, 0xDD, 0x43, 0x3E, 0x2C, 0x11, 0xBB, 0x97, 0x02, 0xCA, 0x29, 0xD2, 0x28, 0x82, 0x4C, 0x5B, 0x7B, 0x94, 0xE4, 0x8C, 0x0B, 0x49, 0x8F, 0x0F, 0xFA, 0xDA, 0x43, 0xA6, 0x52, 0x81, 0xA2, 0x1F, 0x98, 0xB3, 0xB1, 0x8F, 0x3D, 0x64, 0x54, 0x2D, 0xA0, 0xCB, 0xA8, 0x0D, 0xC3, 0x9C, 0xB0, 0x2E, 0x38, 0xEF, 0x3A, 0x47, 0x28, 0xE9, 0x54, 0x95, 0x1A, 0x94, 0x86, 0x7D, 0x36, 0xB6, 0x4D, 0x90, 0x44, 0xF6, 0xC0, 0xA7, 0xC6, 0x31, 0x15, 0x99, 0x7A -}; - -/* CTR_SDK 3 (3.2.5) */ -// APP -static const unsigned char app_fw20_prod_hdrpub[0x100] = -{ - 0xC0, 0xDE, 0x75, 0x56, 0x5F, 0xC1, 0x95, 0x48, 0x9C, 0x1E, 0x00, 0x41, 0x49, 0x9F, 0xB2, 0x8C, 0x43, 0x30, 0xCA, 0x50, 0xC7, 0xCC, 0x7E, 0xF5, 0x56, 0xD0, 0x8F, 0x89, 0x6A, 0xC7, 0x15, 0x10, 0x70, 0x64, 0xB3, 0x29, 0x5B, 0xE8, 0x6B, 0xCA, 0xB1, 0xFE, 0x44, 0xEC, 0x4D, 0x5D, 0xB9, 0xFC, 0xA8, 0xE2, 0x95, 0xBC, 0xFA, 0x0F, 0x76, 0xA5, 0xE1, 0x07, 0xEA, 0x02, 0x13, 0x8C, 0xA6, 0x18, 0x55, 0xDA, 0xBD, 0x9C, 0xE3, 0x5F, 0xBD, 0x50, 0x1E, 0x89, 0xFC, 0x73, 0xC3, 0x37, 0x39, 0x05, 0xAF, 0xE4, 0xDA, 0xA8, 0x62, 0x9E, 0x58, 0x4B, 0x83, 0x9E, 0x5C, 0x26, 0xF2, 0x8B, 0x41, 0xB4, 0xB5, 0x89, 0xCD, 0x90, 0xC0, 0xB8, 0xA0, 0x9F, 0xFA, 0x83, 0x05, 0x11, 0x06, 0xD2, 0xED, 0x3A, 0xAB, 0xAF, 0x6D, 0x46, 0x8A, 0xE8, 0x0E, 0x39, 0xB1, 0xA3, 0xE6, 0x7A, 0x3E, 0x30, 0xE2, 0xA2, 0xDC, 0xC5, 0x4B, 0x8E, 0x3F, 0x9E, 0xB5, 0xC0, 0x9E, 0x05, 0x23, 0x72, 0x3F, 0x4E, 0xDD, 0x7C, 0xF4, 0x7D, 0xA6, 0x9B, 0x92, 0x45, 0x4B, 0x07, 0xD6, 0x19, 0x53, 0x1C, 0x98, 0x13, 0xB1, 0x50, 0x18, 0x6F, 0x74, 0xFD, 0x2D, 0x1C, 0xD5, 0x04, 0xBD, 0x59, 0x19, 0x05, 0xB0, 0xE8, 0x1E, 0x8A, 0xE8, 0x1C, 0xB0, 0x09, 0xB7, 0x82, 0x22, 0x77, 0x99, 0x93, 0xED, 0x33, 0x6D, 0xC8, 0x0C, 0x8F, 0x54, 0xA6, 0x51, 0x7C, 0x22, 0xC3, 0x13, 0x02, 0x5B, 0xC1, 0x44, 0xFE, 0x8E, 0x0D, 0x19, 0x8E, 0xD1, 0x0C, 0xB8, 0xED, 0xEF, 0xF4, 0x3D, 0x3B, 0x68, 0xA0, 0xA3, 0xB9, 0x52, 0x81, 0x98, 0xA9, 0xAE, 0xD0, 0xCD, 0xC2, 0x14, 0xF6, 0x67, 0x45, 0xC2, 0xC7, 0x68, 0x22, 0xB2, 0xC4, 0x20, 0xB8, 0x33, 0x48, 0x7B, 0xB6, 0x13, 0x2E, 0x7A, 0x3D, 0xC5, 0x5E, 0xB6, 0xAF, 0x2A, 0xF9, 0x7D, 0x53 -}; - -static const unsigned char app_fw20_prod_acexsig[0x100] = -{ - 0x19, 0xA5, 0x0A, 0x40, 0x4E, 0xF4, 0xDD, 0xE2, 0x8F, 0xA1, 0x50, 0xA5, 0x78, 0xFF, 0x1D, 0xBA, 0xE3, 0x2B, 0x9D, 0x7A, 0xB5, 0x7C, 0xC1, 0x59, 0x01, 0xB4, 0x2C, 0x46, 0xA8, 0x96, 0xDC, 0xD3, 0xD7, 0x41, 0x0D, 0x0F, 0xC8, 0x24, 0x0D, 0x20, 0xDA, 0xDF, 0x25, 0xE8, 0xC9, 0x88, 0x8C, 0x1B, 0xFF, 0x8B, 0x40, 0x89, 0x9C, 0x2F, 0x37, 0x43, 0x26, 0xDE, 0x18, 0xA3, 0xAB, 0x53, 0x5C, 0xB4, 0xFA, 0xD1, 0x80, 0x2E, 0x46, 0x57, 0x9A, 0x90, 0x49, 0x65, 0xCD, 0x66, 0xE5, 0xE6, 0xB8, 0xF5, 0x3C, 0x70, 0xAE, 0x57, 0xF3, 0x71, 0x31, 0xBA, 0x04, 0x3F, 0x9C, 0x2C, 0x28, 0x9A, 0x32, 0xB8, 0xCD, 0x22, 0x4A, 0x38, 0x7B, 0x91, 0xDA, 0xCC, 0x4D, 0x3A, 0x7D, 0x5C, 0xDC, 0x49, 0x85, 0x37, 0x7A, 0xFF, 0x20, 0xEA, 0x1D, 0x74, 0x2F, 0x83, 0xD8, 0x3C, 0x2E, 0x71, 0xBD, 0x0F, 0xFF, 0x90, 0x8F, 0x49, 0x1C, 0x1F, 0x2A, 0x34, 0xB0, 0x2E, 0x60, 0x78, 0x4B, 0x46, 0xDF, 0x27, 0xEC, 0x37, 0x36, 0xEA, 0xE9, 0xA0, 0xC5, 0x4A, 0x12, 0x1F, 0x36, 0x18, 0x07, 0x97, 0x52, 0xF6, 0xF3, 0xC2, 0x78, 0xA4, 0x70, 0xF2, 0xC3, 0xB6, 0xD8, 0xF1, 0xC9, 0x74, 0x2C, 0x11, 0x21, 0x1A, 0x54, 0xD9, 0x27, 0x6C, 0xB1, 0x24, 0xBD, 0xC8, 0x9B, 0xEC, 0x69, 0xD6, 0xAE, 0x16, 0x90, 0x43, 0x64, 0x58, 0x6F, 0xF8, 0x56, 0x29, 0x04, 0xDF, 0x2F, 0x4C, 0xEE, 0xCC, 0x4D, 0x22, 0x2D, 0xEB, 0x60, 0x35, 0x07, 0x20, 0xF5, 0x75, 0x69, 0x54, 0x26, 0x20, 0x02, 0xC2, 0x1A, 0xBD, 0xA5, 0xDE, 0xC8, 0x38, 0xCD, 0xDB, 0x32, 0x53, 0x5D, 0x08, 0x3F, 0x7D, 0xED, 0x7F, 0x6A, 0x6F, 0x29, 0xEB, 0x11, 0x8A, 0x9A, 0xEE, 0x9C, 0x3C, 0x87, 0x3F, 0xB7, 0x2B, 0x84, 0x7A, 0x74, 0x70, 0xE2, 0x90, 0x33, 0x29 -}; - -static const unsigned char app_fw20_2_prod_hdrpub[0x100] = -{ - 0xC7, 0x35, 0xC3, 0x09, 0x60, 0x43, 0xCD, 0xE6, 0x11, 0x07, 0x52, 0xCA, 0x8B, 0x6A, 0xF2, 0x5A, 0x72, 0xC9, 0xAA, 0xDF, 0x63, 0xF3, 0x1F, 0x2A, 0xDE, 0xE3, 0x56, 0x24, 0xE8, 0x95, 0x8D, 0xBA, 0x5D, 0x9F, 0xF7, 0x03, 0xD9, 0x3C, 0xC8, 0x58, 0x47, 0x00, 0x35, 0x1B, 0xDB, 0x16, 0x7B, 0xA7, 0x2B, 0x82, 0x38, 0xBD, 0x20, 0xA9, 0xB7, 0x1E, 0xF4, 0x78, 0xFC, 0x59, 0x28, 0x61, 0x29, 0x74, 0xE5, 0x63, 0x6F, 0xB2, 0x9E, 0x36, 0x2A, 0xA0, 0x5B, 0x50, 0x89, 0x3F, 0xE9, 0x0C, 0x76, 0x05, 0x41, 0xF3, 0x23, 0x9C, 0xCD, 0xA9, 0xC1, 0x3C, 0x0B, 0x48, 0xAC, 0x11, 0x17, 0xA9, 0x46, 0x4D, 0x64, 0xD9, 0xBF, 0x63, 0x13, 0xBF, 0x8A, 0xFF, 0xED, 0xF0, 0x24, 0x71, 0xD6, 0xA9, 0x50, 0x9D, 0x00, 0xFC, 0x9B, 0x33, 0xFF, 0x0F, 0x68, 0x22, 0x25, 0xE8, 0x49, 0x68, 0x76, 0xF4, 0x8D, 0x3B, 0x67, 0xF0, 0xD7, 0x49, 0x41, 0xDE, 0xBF, 0x55, 0xC3, 0x8F, 0xD9, 0xF6, 0x30, 0x39, 0xF3, 0x15, 0x2B, 0x79, 0x6D, 0x71, 0xAC, 0xE9, 0x93, 0x88, 0xBD, 0xDB, 0x24, 0x75, 0xB1, 0x19, 0x84, 0x51, 0xF8, 0xD3, 0xBB, 0xB1, 0xBC, 0xE4, 0x53, 0x51, 0x4E, 0x00, 0x29, 0x66, 0x05, 0xBC, 0x8E, 0x37, 0x80, 0xCD, 0xD3, 0x40, 0xF7, 0x4F, 0xE0, 0x6B, 0x88, 0xDB, 0xBD, 0xA6, 0xF2, 0x11, 0x9C, 0x26, 0xF8, 0xED, 0xB3, 0x37, 0xB0, 0x77, 0x1D, 0xF0, 0x63, 0x8F, 0x81, 0x07, 0xC0, 0x66, 0x91, 0xA1, 0x8D, 0x06, 0xC1, 0xE9, 0xD4, 0xEA, 0xD7, 0xA0, 0xCB, 0x6F, 0x20, 0x7D, 0xC4, 0xCB, 0x0B, 0x9E, 0x80, 0xB5, 0x3A, 0xDA, 0x57, 0xF5, 0x0E, 0x07, 0x9F, 0x2E, 0x92, 0xC7, 0xBC, 0x6E, 0x85, 0x34, 0xB5, 0x95, 0x15, 0xCA, 0x69, 0x37, 0x7B, 0x2C, 0x1D, 0x5A, 0x13, 0x81, 0x74, 0xA3, 0x3E, 0x9D -}; - -static const unsigned char app_fw20_2_prod_acexsig[0x100] = -{ - 0x33, 0x38, 0x2A, 0x9D, 0xBD, 0xBB, 0x2A, 0xE0, 0x63, 0x1E, 0xAA, 0xA5, 0x5D, 0x43, 0xD5, 0x95, 0xD7, 0xA7, 0xFF, 0x28, 0xA1, 0xD4, 0x2A, 0x70, 0x7E, 0x46, 0x0F, 0x8B, 0x00, 0xA0, 0x7E, 0x74, 0x5D, 0x1C, 0x96, 0x89, 0x5C, 0x38, 0xAF, 0x19, 0xF0, 0x2D, 0x88, 0x28, 0x8B, 0x6D, 0xE4, 0xC9, 0xF5, 0x2F, 0xCD, 0x94, 0xDB, 0xF4, 0x2E, 0x1E, 0xF1, 0x17, 0x2D, 0xB6, 0x1E, 0x21, 0x30, 0x28, 0xCE, 0xEA, 0x2E, 0xED, 0x6F, 0x95, 0xC8, 0x9E, 0xF5, 0x1E, 0x91, 0xC5, 0x38, 0x68, 0x1A, 0x4D, 0x74, 0x91, 0x2C, 0xDA, 0x72, 0xE1, 0xCE, 0x58, 0x2D, 0x4C, 0xD9, 0x9F, 0x24, 0xC1, 0xA2, 0xFA, 0xBD, 0x96, 0x95, 0x95, 0x29, 0x25, 0x58, 0x62, 0x39, 0xDD, 0xE7, 0xD7, 0xD3, 0xBF, 0xDB, 0x35, 0x0C, 0x26, 0xAE, 0x7C, 0xA5, 0x5D, 0x0E, 0x0B, 0x6D, 0xBF, 0x87, 0x72, 0xAA, 0x40, 0xA2, 0x55, 0x89, 0xDF, 0x1B, 0xBA, 0x2D, 0x5E, 0xDC, 0xB3, 0x40, 0x0F, 0x8C, 0xBA, 0xDF, 0xF5, 0xB7, 0x50, 0xC2, 0x94, 0x02, 0x4E, 0xFB, 0x8F, 0x5F, 0x8C, 0xE6, 0xE8, 0x38, 0x28, 0x5F, 0xB6, 0xA2, 0x66, 0x67, 0x73, 0x7F, 0xF8, 0x70, 0xFF, 0xE1, 0x30, 0xEC, 0xCE, 0x0D, 0x61, 0x9A, 0xE2, 0xD9, 0x4B, 0x40, 0xB7, 0xE2, 0x31, 0xC5, 0x6F, 0x06, 0x5C, 0x9A, 0x61, 0x5A, 0x14, 0xF6, 0x36, 0x44, 0xF5, 0x93, 0x30, 0x53, 0xB1, 0x97, 0x44, 0x50, 0x79, 0x9F, 0x3A, 0xFB, 0x2D, 0x42, 0xF1, 0x6C, 0x14, 0x8D, 0x35, 0xD6, 0x16, 0x22, 0xC3, 0x8B, 0x79, 0xFD, 0x26, 0xF8, 0x64, 0x76, 0x53, 0xA1, 0x32, 0x90, 0xBE, 0xA3, 0x9A, 0x6B, 0x5A, 0xCF, 0xC0, 0x34, 0xDE, 0x2F, 0xDC, 0x39, 0x14, 0x8F, 0x98, 0x01, 0x45, 0x57, 0x9E, 0x02, 0x05, 0xEB, 0x8E, 0x6D, 0x16, 0x0B, 0xCF, 0x43, 0xBF, 0xE5, 0xE5 -}; - -static const unsigned char app_fw20_3_prod_hdrpub[0x100] = -{ - 0xBF, 0x3C, 0x41, 0xF1, 0xD2, 0xDC, 0x47, 0xBE, 0xF2, 0xAE, 0x18, 0xA0, 0xCD, 0xAD, 0x0B, 0xEA, 0xB0, 0x8B, 0x69, 0x4E, 0x58, 0x36, 0x59, 0x59, 0x18, 0xA9, 0x02, 0x0C, 0xE6, 0xA9, 0x9D, 0xDA, 0x0A, 0x1E, 0xDA, 0x51, 0x08, 0xD9, 0xC3, 0x1F, 0xA3, 0x36, 0x55, 0xDE, 0xC3, 0xA0, 0x24, 0x1C, 0xB8, 0x67, 0x71, 0x98, 0xC0, 0x60, 0x15, 0x72, 0x47, 0x7E, 0xDC, 0x7E, 0xEB, 0x43, 0x5E, 0x35, 0x23, 0x95, 0xB2, 0x34, 0x1F, 0x31, 0xE8, 0xC7, 0x1E, 0xA4, 0x9B, 0x88, 0x51, 0x39, 0x75, 0xEE, 0x92, 0xA6, 0xF9, 0xD5, 0x5C, 0xFE, 0x84, 0xED, 0x5B, 0xE8, 0x3F, 0xC0, 0xBE, 0xA7, 0x49, 0x63, 0x5D, 0xF1, 0x89, 0xDC, 0x79, 0x75, 0xBC, 0xB1, 0xF5, 0x20, 0x2A, 0x50, 0xBC, 0x8D, 0x2F, 0xC0, 0x27, 0x14, 0x85, 0x58, 0x8B, 0x11, 0xE0, 0xBA, 0xDE, 0x5F, 0x41, 0x44, 0xEE, 0x4F, 0x3D, 0xB6, 0x8D, 0x7A, 0x2F, 0x9C, 0xE3, 0x2A, 0xC1, 0x5E, 0x82, 0x04, 0x3C, 0xFD, 0x68, 0xFD, 0x0A, 0x14, 0x91, 0xF8, 0x5E, 0xBA, 0xD1, 0x80, 0xF6, 0xF5, 0x89, 0xE2, 0x12, 0xE1, 0x86, 0xE7, 0xF1, 0x78, 0x6F, 0x43, 0x5C, 0xF7, 0xD1, 0x57, 0xDA, 0x83, 0x1E, 0x6E, 0xEF, 0x3C, 0xDF, 0xED, 0x03, 0x95, 0xB7, 0xD4, 0x15, 0x6E, 0x1C, 0xDE, 0xD3, 0x17, 0x02, 0x52, 0x15, 0xBF, 0xDA, 0xFB, 0x62, 0x2E, 0xB5, 0x47, 0x0A, 0x29, 0x08, 0xC5, 0x3C, 0x3C, 0x17, 0x00, 0xC3, 0x50, 0x13, 0x3C, 0xBF, 0xE6, 0xEF, 0x8A, 0x7C, 0x12, 0x22, 0x13, 0x19, 0x06, 0x56, 0xDE, 0xBC, 0xDC, 0xCC, 0xC5, 0xE7, 0xEE, 0xC4, 0x88, 0x48, 0x47, 0xE3, 0xA9, 0x76, 0xFA, 0x51, 0x7B, 0xB7, 0x1D, 0xAF, 0x48, 0x80, 0x25, 0xFD, 0x1C, 0xB8, 0x8C, 0x21, 0xA3, 0x37, 0x48, 0x77, 0xD6, 0x7E, 0x16, 0x8C, 0xC4, 0x0C, 0x05 -}; - -static const unsigned char app_fw20_3_prod_acexsig[0x100] = -{ - 0x5E, 0x3C, 0xF9, 0xB5, 0x50, 0x08, 0x3E, 0x49, 0x71, 0x21, 0x4F, 0x22, 0x5A, 0xDB, 0x32, 0x66, 0x8D, 0x64, 0x48, 0x96, 0x59, 0x47, 0xBF, 0xF0, 0x20, 0x4C, 0x6D, 0xE2, 0xCE, 0xE5, 0x44, 0x58, 0x78, 0x15, 0xCD, 0xA8, 0x58, 0xF7, 0x8A, 0xF2, 0x88, 0x04, 0xF1, 0x05, 0x7F, 0xF6, 0x1A, 0x61, 0x68, 0x22, 0xE9, 0x29, 0x01, 0xEF, 0x1C, 0x33, 0xB8, 0x30, 0x18, 0x96, 0xA2, 0x65, 0x15, 0xE6, 0x9C, 0x57, 0x95, 0x90, 0xF0, 0xB6, 0xBC, 0xB2, 0xCB, 0xDF, 0x43, 0xBD, 0x24, 0x6E, 0xD8, 0xC8, 0xAE, 0xA2, 0x32, 0xEA, 0x8A, 0x90, 0x50, 0x5D, 0xE1, 0x1B, 0x82, 0x72, 0x7C, 0x90, 0x24, 0x55, 0x2D, 0x3D, 0x8B, 0x95, 0xFC, 0xA4, 0xDA, 0xEF, 0x82, 0x2B, 0x25, 0xFA, 0x95, 0x28, 0xD0, 0x14, 0x5E, 0x67, 0xE6, 0x24, 0x0C, 0x5D, 0x56, 0x07, 0x79, 0x4F, 0x38, 0x4E, 0xF8, 0xDC, 0x74, 0x32, 0xFC, 0x92, 0xED, 0x99, 0x28, 0xC2, 0x1A, 0x52, 0xE5, 0x48, 0x21, 0xDB, 0x9C, 0x42, 0xF5, 0x71, 0xD3, 0x61, 0xF5, 0xCC, 0xF5, 0xC7, 0x7D, 0xC9, 0x60, 0x5C, 0x92, 0x68, 0x5A, 0xE6, 0x36, 0xF1, 0x7F, 0xCC, 0x85, 0x4C, 0x5C, 0x50, 0x6F, 0xA1, 0x0B, 0xA6, 0xC5, 0x57, 0x76, 0x6A, 0x81, 0x0B, 0x5B, 0xAB, 0x30, 0x85, 0xA8, 0x6B, 0x29, 0xC9, 0x9A, 0x96, 0xFF, 0xCD, 0x9B, 0x3A, 0x89, 0x94, 0x31, 0xC3, 0x4F, 0x0E, 0xC4, 0xA4, 0x19, 0x2F, 0xA4, 0x86, 0xCD, 0x07, 0x6D, 0xE4, 0xC7, 0x38, 0x32, 0xDF, 0x8E, 0xE7, 0xBE, 0x7B, 0xC1, 0x52, 0xBF, 0x4D, 0xB1, 0x66, 0x06, 0xBD, 0xBE, 0x98, 0xE9, 0x63, 0x9B, 0xFD, 0x43, 0x6B, 0x96, 0x9A, 0x8E, 0x78, 0x97, 0x1B, 0x7C, 0x8A, 0xE2, 0x8B, 0x28, 0xB8, 0xD0, 0xBD, 0x32, 0x05, 0x53, 0x92, 0xF7, 0x24, 0x04, 0xD0, 0x25, 0xB1, 0xF7, 0xDF -}; - -// EC APP -static const unsigned char ecapp_fw20_prod_hdrpub[0x100] = -{ - 0xE1, 0xF2, 0xF8, 0x92, 0xEC, 0x5D, 0xD9, 0xB2, 0x38, 0xCE, 0x52, 0x72, 0x10, 0x3A, 0x51, 0xB2, 0x47, 0xD9, 0xC7, 0x00, 0xF9, 0xEB, 0xE2, 0x1E, 0xE8, 0x53, 0x2F, 0xFF, 0xB0, 0x0D, 0x9E, 0x1B, 0x21, 0x48, 0x0B, 0xBF, 0x53, 0x48, 0x93, 0x9E, 0x20, 0x55, 0xF2, 0x8F, 0xFE, 0x5E, 0x0D, 0xE6, 0xBF, 0xDD, 0xF5, 0xE6, 0xDF, 0x95, 0xA6, 0x5B, 0x38, 0x81, 0x49, 0x1E, 0xE9, 0x77, 0xB1, 0x96, 0xEA, 0xAA, 0x83, 0x18, 0x09, 0x2F, 0x77, 0x59, 0x16, 0x0B, 0xA9, 0xF1, 0xE8, 0xE8, 0x3A, 0x05, 0xEA, 0x35, 0x4F, 0x1D, 0xD4, 0xF9, 0xC5, 0x1A, 0xA2, 0x9E, 0xF7, 0xBD, 0x3B, 0x90, 0xDA, 0x80, 0xA9, 0xA3, 0xED, 0xFC, 0xE3, 0xBF, 0x7A, 0xF1, 0x43, 0x67, 0x5F, 0x35, 0x24, 0xD1, 0x4B, 0xCF, 0x1F, 0x59, 0xF5, 0xF4, 0x5E, 0x88, 0xDE, 0x3D, 0x19, 0xDD, 0x5E, 0x04, 0xB2, 0x39, 0xB3, 0x29, 0x32, 0xBA, 0xA3, 0x1C, 0x18, 0x75, 0x01, 0xE0, 0xC9, 0x22, 0x23, 0xA1, 0x95, 0x04, 0xA6, 0x0F, 0xFB, 0x5D, 0x25, 0xC2, 0x88, 0x21, 0x5F, 0xFD, 0x13, 0xFE, 0x9F, 0x06, 0xE6, 0x5E, 0x53, 0x69, 0x1A, 0x15, 0xD0, 0xAB, 0xBE, 0x16, 0xC5, 0x01, 0x52, 0xEA, 0x87, 0x5F, 0xFE, 0xE6, 0xC3, 0xD1, 0x4F, 0xB9, 0xC3, 0x6B, 0xBD, 0x7D, 0x4E, 0x1D, 0xA9, 0xCD, 0xD8, 0x89, 0x1B, 0x0E, 0x64, 0x97, 0x57, 0x7E, 0x23, 0xF4, 0x85, 0xD0, 0xE9, 0x68, 0x00, 0xBF, 0x7E, 0x8B, 0xB2, 0x42, 0xED, 0x9B, 0xDD, 0xD8, 0x62, 0x13, 0x26, 0x93, 0xF8, 0x66, 0x0B, 0x32, 0x30, 0x8A, 0x9C, 0xF0, 0x7A, 0x7E, 0xD2, 0x04, 0x5D, 0xF1, 0xF2, 0xB2, 0x4A, 0xF2, 0x18, 0xF4, 0xAF, 0xA4, 0x34, 0x06, 0xE7, 0x09, 0x77, 0x01, 0x8F, 0xEE, 0x17, 0x27, 0x89, 0x9B, 0xF0, 0x76, 0x00, 0x25, 0x01, 0x09, 0xC1, 0x51 -}; - -static const unsigned char ecapp_fw20_prod_acexsig[0x100] = -{ - 0x6F, 0x5C, 0xA2, 0xD4, 0xDB, 0x21, 0x69, 0x54, 0xFE, 0x63, 0x55, 0x4C, 0x18, 0x86, 0xFF, 0x47, 0x73, 0x9B, 0x3A, 0x6B, 0xF3, 0x63, 0x47, 0x7E, 0x76, 0x86, 0x3B, 0xF1, 0xC6, 0x05, 0xE4, 0x4B, 0x8B, 0x61, 0xF3, 0x06, 0x02, 0x9B, 0x1B, 0xD1, 0x48, 0xCC, 0x51, 0xAF, 0x78, 0xE5, 0x58, 0xEE, 0xC4, 0x93, 0x83, 0xC2, 0xC9, 0x91, 0xD4, 0x4E, 0x00, 0xAD, 0xA8, 0x12, 0x40, 0x77, 0x20, 0xF8, 0xED, 0x11, 0xC1, 0x1D, 0x71, 0x14, 0x75, 0x1D, 0xB6, 0x89, 0xCC, 0xE1, 0x38, 0x3B, 0x8E, 0xAD, 0xA5, 0x17, 0x88, 0x90, 0xC1, 0xBE, 0x95, 0xBB, 0x0F, 0x0B, 0x12, 0xF9, 0x9E, 0xA9, 0x4F, 0x16, 0x34, 0x06, 0x2D, 0xFD, 0x1B, 0x51, 0x92, 0xE1, 0xD3, 0x7D, 0x6B, 0x31, 0xF0, 0xB4, 0xC0, 0x75, 0x91, 0x75, 0x2A, 0x0C, 0x99, 0x46, 0x2F, 0x4B, 0x34, 0xF3, 0x72, 0x10, 0x04, 0x23, 0x62, 0xC8, 0x94, 0xAD, 0x06, 0xC7, 0x69, 0x2A, 0x78, 0x3A, 0xCE, 0x8B, 0xE0, 0x16, 0x7D, 0x28, 0x35, 0xB2, 0xE1, 0x71, 0x49, 0x1C, 0x55, 0xD7, 0xA8, 0x6D, 0xCB, 0xA6, 0xF3, 0xB9, 0x35, 0xEA, 0x9D, 0xB0, 0x04, 0xE8, 0xB5, 0xEC, 0x15, 0xC6, 0xB1, 0x01, 0x69, 0xFB, 0x01, 0x87, 0x58, 0x83, 0xCC, 0xF5, 0x91, 0xC3, 0x3C, 0x2D, 0x7E, 0xAF, 0x43, 0xB9, 0xCD, 0x95, 0x20, 0xF9, 0x8F, 0x6F, 0x39, 0xDE, 0x95, 0x7B, 0x3F, 0xAD, 0x56, 0xF3, 0x07, 0xA3, 0x52, 0x12, 0x0F, 0x56, 0xA4, 0xF7, 0xAC, 0x55, 0xC2, 0x2F, 0x01, 0x0E, 0xFD, 0x26, 0xC9, 0x06, 0x8B, 0xCB, 0x6A, 0x4A, 0xE3, 0xF4, 0x58, 0x04, 0xAB, 0x64, 0x02, 0x99, 0xB9, 0xC5, 0xDA, 0x1F, 0x96, 0xEA, 0xEE, 0xB0, 0x33, 0xFD, 0xE9, 0x74, 0x90, 0x48, 0x7B, 0x88, 0xDE, 0x72, 0x57, 0x4C, 0x69, 0x47, 0x89, 0x03, 0xD6, 0x6B, 0x22, 0xFF -}; - -/* CTR_SDK 4 (4.2.8) */ -// APP -static const unsigned char app_fw21_prod_hdrpub[0x100] = -{ - 0xCF, 0x00, 0x9C, 0xB3, 0x20, 0xA0, 0x32, 0x47, 0x65, 0x89, 0xE0, 0xA0, 0x40, 0xC5, 0x57, 0x03, 0xA5, 0xF6, 0x8E, 0x66, 0xF5, 0x20, 0x28, 0xEE, 0xA7, 0x44, 0x3E, 0x8E, 0x4F, 0xC0, 0xC9, 0xD3, 0xE4, 0x3A, 0x0B, 0x31, 0xEC, 0x8F, 0xCC, 0x8D, 0x4C, 0xA6, 0x14, 0x49, 0x14, 0xBE, 0x05, 0xC1, 0xB4, 0x22, 0xAD, 0xCE, 0x1D, 0xCD, 0x29, 0x7B, 0x45, 0x16, 0xE5, 0xB9, 0xD6, 0xD2, 0x7A, 0xD0, 0x63, 0xCE, 0x69, 0xB2, 0xCA, 0xB8, 0x6D, 0x10, 0x9B, 0x99, 0x62, 0xF9, 0x45, 0xC0, 0x2E, 0xB5, 0xA1, 0xCD, 0x6B, 0xAF, 0x77, 0x9B, 0xEF, 0xD8, 0x56, 0x48, 0x75, 0x34, 0x02, 0x6E, 0xBA, 0xBF, 0x30, 0x54, 0xBF, 0x33, 0x4D, 0xB1, 0x36, 0xDC, 0x2A, 0xCD, 0x68, 0x81, 0x8C, 0x4F, 0xCB, 0xC2, 0xA2, 0x1D, 0x6E, 0xB0, 0xC0, 0x02, 0xF5, 0xF8, 0x1C, 0x6C, 0xB4, 0xC9, 0xD6, 0x90, 0x27, 0x50, 0xCA, 0x61, 0xE4, 0x1E, 0xA6, 0x1E, 0x43, 0x94, 0x01, 0x81, 0xC3, 0x69, 0xD0, 0xE2, 0x2C, 0xD7, 0x86, 0x1B, 0x8B, 0x69, 0x4D, 0xF0, 0xF1, 0x54, 0x24, 0x20, 0x91, 0xDD, 0x87, 0x12, 0x0A, 0x23, 0x98, 0x09, 0x63, 0xFD, 0xB4, 0x00, 0x69, 0x06, 0xD2, 0xF1, 0xC3, 0xE7, 0x9D, 0xCC, 0x87, 0x99, 0x01, 0x20, 0xDA, 0xD6, 0xA4, 0x24, 0x58, 0x9A, 0x62, 0x52, 0xF0, 0x30, 0x98, 0x6C, 0x08, 0x90, 0xB4, 0xA6, 0xE4, 0x79, 0x47, 0xF3, 0x1D, 0x58, 0x86, 0x4D, 0x9B, 0xA3, 0xE8, 0xFC, 0x1A, 0x63, 0xC3, 0x2F, 0xDF, 0xD3, 0xC6, 0x3B, 0x8E, 0xDA, 0xFB, 0xEA, 0x6D, 0xE3, 0x92, 0x14, 0xC8, 0x21, 0x2C, 0xE3, 0xF2, 0xE3, 0x31, 0x2E, 0xF6, 0x73, 0x27, 0xE8, 0xF2, 0xD5, 0x38, 0xB7, 0x04, 0x46, 0xA7, 0xBE, 0x92, 0x14, 0x66, 0x1E, 0x8D, 0x88, 0xF7, 0xC0, 0x60, 0xB5, 0x7F, 0xF3, 0xCE, 0x43 -}; - -static const unsigned char app_fw21_prod_acexsig[0x100] = -{ - 0x04, 0xE3, 0xD1, 0xD2, 0x32, 0xF4, 0x04, 0xE5, 0x79, 0x67, 0x1E, 0x74, 0xD8, 0x1B, 0x8F, 0x6C, 0xEB, 0xE2, 0xFE, 0x1B, 0xCE, 0xAB, 0x92, 0x03, 0xF0, 0x3C, 0xF5, 0x31, 0x07, 0xCE, 0x8E, 0x47, 0xB2, 0xE8, 0x52, 0x8F, 0xFF, 0xA7, 0x1B, 0xE1, 0xFF, 0x58, 0x5E, 0x99, 0xBA, 0x71, 0x90, 0x87, 0x01, 0x92, 0xE8, 0xD0, 0x67, 0x6E, 0x29, 0xD3, 0x1E, 0xA6, 0x22, 0x8F, 0x52, 0xAD, 0x6B, 0xB1, 0xB6, 0xC0, 0xC6, 0x44, 0x5D, 0x41, 0xD8, 0xCE, 0x3A, 0xEB, 0x04, 0x5A, 0x8C, 0x4D, 0x03, 0x9F, 0x31, 0xDC, 0x4E, 0x37, 0xEF, 0x7B, 0x42, 0x02, 0x19, 0x6A, 0x33, 0x60, 0xEF, 0xB9, 0x01, 0x11, 0xAC, 0x82, 0x10, 0xFD, 0x79, 0xD8, 0x2D, 0x5F, 0x42, 0xDE, 0xEC, 0x38, 0x79, 0xF6, 0x70, 0x84, 0x5A, 0xA8, 0x83, 0x58, 0xEF, 0x57, 0x2A, 0x04, 0x1E, 0x9B, 0xAF, 0xB3, 0xCC, 0x67, 0x62, 0xD1, 0x9B, 0xB0, 0xA4, 0x3C, 0x16, 0xB8, 0x0B, 0xC1, 0x7B, 0xE9, 0x39, 0x96, 0x32, 0xF5, 0xC4, 0xA0, 0xAB, 0xE8, 0xEA, 0x2F, 0x2D, 0x83, 0xF4, 0x8C, 0x62, 0xA3, 0x03, 0xDC, 0x6E, 0xAD, 0x9C, 0x46, 0x56, 0xD6, 0xF5, 0xDD, 0xB1, 0x95, 0x47, 0xE9, 0xAB, 0x40, 0x28, 0x87, 0x2B, 0x97, 0x42, 0x1A, 0x71, 0x9F, 0xB7, 0x7D, 0x45, 0x4A, 0x2B, 0xF6, 0xF7, 0xA8, 0x01, 0x14, 0x40, 0x77, 0x14, 0x49, 0x2B, 0x5D, 0x84, 0xA5, 0xC4, 0xA8, 0x10, 0xCD, 0x4B, 0x85, 0x34, 0x58, 0x74, 0x99, 0xD2, 0x44, 0x26, 0x37, 0xC7, 0x7A, 0x7A, 0x60, 0x85, 0x77, 0x73, 0xE9, 0x75, 0xB1, 0x6B, 0xDA, 0x43, 0x02, 0xAD, 0xD2, 0xC6, 0x7F, 0x53, 0xC4, 0xD3, 0x0F, 0x1D, 0xA0, 0x6F, 0xD8, 0x3B, 0xAF, 0xFF, 0x94, 0xF6, 0x27, 0x27, 0x7A, 0xEF, 0xF3, 0x96, 0xA5, 0x29, 0x4A, 0x47, 0xB1, 0x09, 0x2D, 0x34, 0xF5 -}; - -/* CTR_SDK 5 (5.2.3) */ -// APP -static const unsigned char app_fw23_prod_hdrpub[0x100] = -{ - 0xDD, 0x51, 0x85, 0xCB, 0x8C, 0x22, 0xBB, 0x76, 0x08, 0x31, 0xE1, 0xD3, 0x04, 0x30, 0xDB, 0x70, 0x78, 0x6F, 0xD5, 0xCB, 0x79, 0xD4, 0x87, 0x19, 0x3E, 0x39, 0x99, 0x07, 0x12, 0x1C, 0xC9, 0x7C, 0xC9, 0x34, 0x16, 0x37, 0x31, 0xFD, 0x7C, 0x56, 0xB4, 0x53, 0x5D, 0x9D, 0x1F, 0xFE, 0x87, 0x0A, 0x86, 0x2A, 0x9B, 0xF8, 0xE1, 0x2F, 0xE3, 0x98, 0xCD, 0xA0, 0xD2, 0xBF, 0xBF, 0x2D, 0x56, 0x73, 0xC2, 0x7B, 0x38, 0x89, 0x33, 0xA6, 0xC4, 0xC0, 0x7E, 0xF2, 0x54, 0xF6, 0x2F, 0x32, 0xD7, 0x88, 0x88, 0xCD, 0x4D, 0x2B, 0xB5, 0x6D, 0x97, 0xB7, 0x97, 0xF2, 0x5A, 0xCD, 0x93, 0xC8, 0x11, 0x3F, 0x44, 0xB3, 0xF8, 0x2B, 0x45, 0x3F, 0xE1, 0x44, 0x5D, 0x7D, 0xB8, 0x0C, 0x48, 0xD1, 0x53, 0xA8, 0xEF, 0x26, 0xCC, 0x5B, 0x25, 0x82, 0x92, 0x52, 0x2E, 0xAC, 0x32, 0x48, 0x27, 0x4F, 0x24, 0xD7, 0xF8, 0x1B, 0x64, 0x2F, 0x8C, 0x10, 0x84, 0xD3, 0xF7, 0x31, 0x6A, 0xD0, 0x79, 0xC3, 0x19, 0xF3, 0x4A, 0xB7, 0xC7, 0x4B, 0xD8, 0x32, 0x43, 0x29, 0x90, 0xED, 0x93, 0x70, 0x6D, 0xC0, 0x45, 0x1E, 0x6D, 0x3A, 0x8A, 0xCB, 0x43, 0xB1, 0xC3, 0xEB, 0x8D, 0xC0, 0xC3, 0x5C, 0x20, 0xA4, 0xFD, 0xF8, 0xFF, 0xED, 0x4E, 0x5A, 0x15, 0x22, 0x8E, 0x8E, 0xE8, 0x0D, 0x32, 0x0E, 0x15, 0xC4, 0xCF, 0x1F, 0xDF, 0x74, 0x42, 0x73, 0x3D, 0xF8, 0x3F, 0x32, 0x9B, 0xEE, 0xD6, 0xE4, 0x8F, 0x99, 0xAA, 0xF7, 0xFA, 0x72, 0xC0, 0xB6, 0xF6, 0x97, 0x22, 0xDB, 0xDB, 0x2E, 0xD0, 0xBF, 0x40, 0x58, 0x01, 0xE5, 0xBA, 0x07, 0x09, 0x70, 0x1E, 0x8F, 0x8B, 0xAF, 0x73, 0xF9, 0x0E, 0xB2, 0xC7, 0xF4, 0xB8, 0x88, 0xDD, 0xAF, 0xF5, 0x88, 0xA7, 0x1A, 0xE9, 0x39, 0xDF, 0x6C, 0x32, 0xE7, 0x7C, 0xAB, 0x33, 0xCD -}; - -static const unsigned char app_fw23_prod_acexsig[0x100] = -{ - 0xA6, 0x8B, 0x83, 0x98, 0x0A, 0x4C, 0x37, 0x75, 0x54, 0x25, 0xFE, 0xC9, 0x8A, 0x1E, 0x1A, 0x47, 0x0C, 0xD8, 0x12, 0x2E, 0xC0, 0xE4, 0x0D, 0x3D, 0xB9, 0x1E, 0x14, 0xEF, 0x05, 0xD4, 0x4F, 0xC1, 0x9E, 0xFD, 0xDF, 0xF1, 0x2C, 0xDE, 0x45, 0xC2, 0x1A, 0x83, 0xCD, 0x88, 0xFA, 0x51, 0x4F, 0x4A, 0x1F, 0xA3, 0x71, 0x1D, 0xC9, 0x81, 0xF0, 0xB4, 0x68, 0x86, 0x18, 0xCF, 0xF4, 0xE4, 0x58, 0x68, 0x54, 0xAE, 0x00, 0x9E, 0xC4, 0xA5, 0x0B, 0xFF, 0x52, 0x2D, 0x29, 0x83, 0xEF, 0x1F, 0x5A, 0x30, 0xD0, 0xA7, 0x1D, 0x1D, 0x0C, 0x17, 0xAA, 0x7F, 0x54, 0x50, 0xA9, 0x47, 0xA8, 0x2F, 0x31, 0xAE, 0x21, 0x0A, 0x2A, 0xF8, 0xDA, 0x59, 0x1F, 0x90, 0x92, 0x21, 0x56, 0x65, 0x0C, 0x20, 0xBE, 0x1E, 0x53, 0xBF, 0x56, 0x78, 0x38, 0xE0, 0x04, 0xBA, 0x43, 0x6C, 0xB3, 0xFC, 0xFB, 0x1F, 0x38, 0x41, 0x4D, 0xD5, 0xFE, 0xD8, 0x31, 0xC8, 0x53, 0xAD, 0xE1, 0x3F, 0x2C, 0x52, 0x84, 0x40, 0x90, 0x9B, 0xC4, 0xDB, 0xAB, 0x09, 0xAD, 0x41, 0x51, 0x77, 0x9D, 0x0D, 0x06, 0x56, 0x15, 0xEF, 0x45, 0x9D, 0x3A, 0xC3, 0x30, 0x1A, 0x7F, 0xBA, 0xB7, 0xB9, 0xA4, 0xAA, 0x3F, 0x0F, 0xB8, 0x9A, 0x5B, 0xE8, 0xE9, 0x59, 0x7B, 0x5E, 0x5D, 0x0A, 0x3C, 0x5D, 0x54, 0x3D, 0x48, 0x7B, 0x4C, 0x28, 0xD0, 0x91, 0xF6, 0xF1, 0x43, 0x52, 0xBC, 0xE6, 0x08, 0x52, 0x09, 0x0D, 0x64, 0xAE, 0x18, 0x67, 0x3E, 0x8C, 0xAD, 0xE7, 0x45, 0xD4, 0xE1, 0xA0, 0x65, 0xC2, 0x09, 0xE6, 0x44, 0xF4, 0xDE, 0xDD, 0xC5, 0x4C, 0x86, 0x9D, 0x60, 0x81, 0x28, 0x9F, 0x59, 0x78, 0xA5, 0x9F, 0x97, 0x09, 0x11, 0xD9, 0x1C, 0x0B, 0x98, 0x84, 0x83, 0x73, 0xEA, 0xF7, 0xFA, 0x27, 0x99, 0x3C, 0x12, 0xFB, 0x50, 0x43, 0x97, 0x17 -}; - -static const unsigned char app_fw23_2_prod_hdrpub[0x100] = -{ - 0xB0, 0x21, 0x4F, 0x59, 0x5E, 0xDA, 0xC8, 0x66, 0x28, 0xAC, 0x0F, 0xD6, 0xAA, 0xAA, 0xC3, 0xCD, 0x2E, 0x0C, 0xAF, 0x37, 0x44, 0xD2, 0x3A, 0x00, 0x5B, 0x8E, 0x06, 0x79, 0x7A, 0x26, 0x71, 0x0E, 0xF6, 0xC9, 0x41, 0x6D, 0x28, 0x9C, 0xC6, 0xF4, 0x74, 0x44, 0xFA, 0xAA, 0x06, 0xF9, 0xB7, 0x0B, 0x83, 0x04, 0xA2, 0x15, 0xA9, 0x2F, 0xD4, 0x66, 0x94, 0x3F, 0xCC, 0xE5, 0x09, 0xA6, 0xAC, 0xFF, 0x7A, 0x7D, 0x27, 0x44, 0x36, 0xE6, 0x83, 0x72, 0x37, 0x69, 0x64, 0x3E, 0x83, 0xFF, 0xED, 0x1A, 0x06, 0xBA, 0x7E, 0x7C, 0xED, 0x2F, 0xB0, 0x99, 0x06, 0x12, 0xC0, 0x74, 0xDF, 0xD3, 0xF9, 0xC9, 0x7D, 0xF1, 0xE1, 0xE3, 0x35, 0x3B, 0x8D, 0x35, 0xB9, 0x0D, 0xF0, 0x30, 0x0E, 0xA8, 0xCC, 0x3E, 0xDE, 0xA5, 0x53, 0x5A, 0xCD, 0x00, 0xCE, 0xBF, 0x6A, 0x62, 0x18, 0x44, 0xA1, 0xBB, 0xDF, 0xDE, 0x78, 0x54, 0xA4, 0x07, 0x3E, 0x63, 0xDB, 0x52, 0x8B, 0x12, 0x20, 0x87, 0x3B, 0x3F, 0xB0, 0xFA, 0xDD, 0x26, 0xB4, 0x32, 0x0B, 0x19, 0x97, 0x91, 0xD6, 0xA6, 0x98, 0xDB, 0xA7, 0xA7, 0xF7, 0xBD, 0x9A, 0xF8, 0x5F, 0x62, 0x9C, 0xFB, 0x70, 0x3D, 0xA2, 0x74, 0x4C, 0x00, 0xA9, 0xCD, 0xF3, 0x98, 0x69, 0x58, 0x40, 0xA1, 0xD8, 0x0F, 0x36, 0x15, 0xAC, 0xCA, 0x1C, 0xC0, 0xD4, 0x80, 0xC8, 0x66, 0x50, 0xBD, 0x78, 0x2C, 0x7C, 0xED, 0x3D, 0x87, 0x20, 0x85, 0x81, 0xAD, 0xBB, 0x8E, 0x6F, 0x79, 0x03, 0xBA, 0xBA, 0xFA, 0x26, 0x73, 0x21, 0xC1, 0xE4, 0xD8, 0x91, 0x1F, 0xE2, 0x86, 0xF1, 0x68, 0x6E, 0xC3, 0xC3, 0x91, 0x2F, 0x73, 0x92, 0xDA, 0x4B, 0x49, 0xE5, 0x4F, 0xD2, 0xA9, 0x82, 0xFE, 0x98, 0x83, 0xCB, 0x4C, 0xA2, 0xC3, 0x3F, 0x0E, 0xCC, 0x38, 0x9E, 0x82, 0xBA, 0xAD, 0x5C, 0xEB -}; - -static const unsigned char app_fw23_2_prod_acexsig[0x100] = -{ - 0x7B, 0xEB, 0x5A, 0xA0, 0x36, 0x1E, 0x1A, 0x75, 0x26, 0x22, 0x72, 0x46, 0xC0, 0x8D, 0x11, 0x8F, 0x9F, 0x7F, 0xDB, 0x8A, 0x06, 0xA9, 0xF9, 0x3E, 0xDC, 0x71, 0x49, 0xD3, 0x40, 0xBE, 0xC7, 0x3A, 0xFF, 0x67, 0xB4, 0x10, 0x23, 0x58, 0x0F, 0xBD, 0x24, 0x39, 0xBF, 0x3B, 0xEB, 0x73, 0x4D, 0x5B, 0xC8, 0xA7, 0x10, 0xDB, 0xFF, 0x54, 0x17, 0x1E, 0x65, 0x29, 0x3C, 0x63, 0x61, 0xB0, 0x9F, 0xC4, 0x80, 0x04, 0x53, 0xDD, 0xE9, 0x9F, 0xA8, 0xC6, 0x4A, 0x41, 0x16, 0x51, 0x4C, 0xF5, 0x7C, 0x38, 0x11, 0x43, 0xF2, 0xFC, 0x4B, 0xCA, 0x87, 0x99, 0x80, 0x20, 0x74, 0xE8, 0x31, 0x59, 0xAB, 0xA2, 0x6E, 0x07, 0xBA, 0xE0, 0x7B, 0xD1, 0x28, 0x1E, 0x37, 0x26, 0xA3, 0x70, 0xD2, 0x25, 0x82, 0xA6, 0x07, 0xE7, 0xBB, 0x18, 0xDE, 0xAA, 0x76, 0xB4, 0xCD, 0x83, 0xAF, 0xC7, 0xC5, 0x03, 0xE8, 0x96, 0x1A, 0xD5, 0x2C, 0x9F, 0xAC, 0x87, 0x70, 0x87, 0x90, 0x69, 0x65, 0x7C, 0xD4, 0x65, 0x0E, 0x64, 0xE1, 0x90, 0x7E, 0x09, 0x98, 0xC5, 0x30, 0x48, 0x5E, 0x7F, 0x4D, 0xB7, 0x08, 0x51, 0xF0, 0xD7, 0x00, 0xBE, 0x23, 0x17, 0xF6, 0x86, 0x09, 0x8B, 0xF2, 0x75, 0x67, 0x31, 0xE3, 0xB0, 0x0D, 0x7A, 0xF0, 0x56, 0x67, 0x21, 0x67, 0xC8, 0x9C, 0x4E, 0x16, 0x34, 0x6D, 0x1A, 0xB7, 0xB5, 0x7B, 0x02, 0x4D, 0x6B, 0xFA, 0xF8, 0x83, 0x09, 0x6C, 0x14, 0x06, 0x9B, 0x1F, 0x2E, 0x26, 0x61, 0xE9, 0xAD, 0xDE, 0xDD, 0x55, 0x92, 0xB3, 0x50, 0x41, 0xCB, 0x75, 0x47, 0xC1, 0xF8, 0x72, 0x44, 0xE5, 0xEA, 0xC9, 0x11, 0x91, 0xAF, 0x4E, 0xC5, 0xBA, 0xEA, 0x2F, 0x26, 0xE4, 0x73, 0xE3, 0x8A, 0xCD, 0x47, 0xB9, 0x53, 0x75, 0xFC, 0x74, 0xD5, 0x74, 0xCF, 0x1F, 0x75, 0x57, 0x9A, 0x1F, 0xA5, 0x56, 0xEF -}; - -static const unsigned char app_fw23_3_prod_hdrpub[0x100] = -{ - 0xB1, 0x75, 0x6B, 0x2D, 0x39, 0xB2, 0x1A, 0xFF, 0x5C, 0xE5, 0x49, 0xF9, 0x96, 0x5A, 0xAF, 0x3F, 0x5E, 0x64, 0xEB, 0xFC, 0x18, 0x50, 0x13, 0xCF, 0x52, 0xB8, 0x06, 0x8A, 0x5A, 0xEC, 0xB1, 0x2F, 0x61, 0x38, 0xFD, 0x9F, 0xC0, 0x2A, 0x51, 0x4F, 0x3B, 0x0E, 0xBA, 0xE7, 0xBA, 0xB8, 0x7C, 0xAA, 0x31, 0xC1, 0x66, 0x38, 0x10, 0x8B, 0xE4, 0x92, 0x42, 0xAB, 0xD7, 0x56, 0x1F, 0xEC, 0x23, 0xF4, 0x75, 0xFD, 0x7D, 0x87, 0xD3, 0xF4, 0x01, 0x3B, 0xA0, 0xF9, 0xD0, 0x17, 0xE4, 0x45, 0xAD, 0xB9, 0xF2, 0xB8, 0x74, 0x7A, 0x24, 0x98, 0x5F, 0x17, 0xD7, 0x21, 0x22, 0x4F, 0xDF, 0x4E, 0x1F, 0x6B, 0x60, 0x84, 0x88, 0x1D, 0xDB, 0xCF, 0x5C, 0xC6, 0xA7, 0xBF, 0x9D, 0x36, 0x88, 0xE2, 0xFD, 0x79, 0x43, 0x95, 0xE8, 0x1E, 0x9F, 0x89, 0x47, 0x40, 0x96, 0xFE, 0xE7, 0xD6, 0x67, 0xDA, 0x3A, 0x3C, 0x81, 0xBD, 0x65, 0x7C, 0xD1, 0xE4, 0xE1, 0x9E, 0x2C, 0xA9, 0xE4, 0xAE, 0x5B, 0xE6, 0x27, 0xAD, 0x25, 0xE6, 0xE4, 0x9F, 0x60, 0xB0, 0x95, 0xC3, 0xCC, 0x4C, 0xEC, 0x6D, 0xFB, 0x70, 0x17, 0xB5, 0x57, 0xC5, 0x47, 0x1C, 0xD2, 0x19, 0x4C, 0xF1, 0x33, 0xF3, 0x83, 0x62, 0x20, 0x8C, 0x23, 0x3D, 0xC3, 0x79, 0xD6, 0xD1, 0x7B, 0x12, 0x8C, 0xA2, 0x1A, 0x9F, 0xF2, 0xC4, 0x01, 0x5E, 0xA0, 0xA4, 0xC9, 0xCC, 0xDC, 0xC3, 0x67, 0xEC, 0x62, 0x81, 0x86, 0x6D, 0x14, 0x2E, 0xF9, 0xC9, 0xF9, 0xD3, 0x7B, 0x4B, 0xC6, 0x1A, 0xD6, 0x1E, 0xA8, 0x20, 0xA9, 0x19, 0xCF, 0xB1, 0xD8, 0xCB, 0xD3, 0xF4, 0x8E, 0x5C, 0xEC, 0x83, 0xD3, 0x54, 0xA2, 0x65, 0x78, 0xFF, 0x4B, 0x7E, 0xC2, 0x2C, 0xFC, 0xBE, 0x90, 0x53, 0x48, 0x06, 0x42, 0x45, 0xBA, 0xCC, 0xCE, 0xE0, 0x70, 0x5C, 0x47, 0xCE, 0xB5, 0xAD -}; - -static const unsigned char app_fw23_3_prod_acexsig[0x100] = -{ - 0x59, 0xB7, 0xA4, 0x4D, 0x58, 0x89, 0x33, 0xA0, 0x85, 0x77, 0xF7, 0x44, 0xE2, 0x14, 0x61, 0xFD, 0x42, 0xDF, 0x42, 0xEE, 0x08, 0x66, 0x2D, 0x28, 0x0C, 0xF9, 0x25, 0x12, 0x45, 0x50, 0x8C, 0x86, 0xF9, 0xCD, 0x86, 0xAB, 0x19, 0x4C, 0x1F, 0x1C, 0x0B, 0xDF, 0x21, 0xB5, 0x9C, 0x89, 0xFA, 0x90, 0xB3, 0x77, 0x3E, 0xA9, 0xCB, 0xDD, 0x6B, 0x15, 0x57, 0x12, 0xA1, 0xC1, 0x02, 0x4A, 0xED, 0xFC, 0x94, 0x02, 0xFF, 0x74, 0x87, 0x42, 0x5D, 0x6F, 0xBD, 0xD5, 0xAE, 0x25, 0xCA, 0x19, 0xF4, 0x89, 0x5A, 0x8E, 0x56, 0x22, 0xA2, 0xC9, 0xDE, 0x15, 0x32, 0xC9, 0x01, 0x43, 0x33, 0x3A, 0xBA, 0xCC, 0x52, 0x98, 0xCD, 0x6E, 0xD4, 0xC4, 0x51, 0x9F, 0xF4, 0xF1, 0xFC, 0xD3, 0x77, 0x22, 0x21, 0xAB, 0x79, 0xAA, 0x44, 0xDD, 0xA5, 0x88, 0xD5, 0x57, 0x26, 0x38, 0xFF, 0xBC, 0x31, 0xAF, 0xF2, 0xB2, 0xF1, 0xF2, 0x13, 0x22, 0xA8, 0xAA, 0x00, 0xA3, 0xFA, 0x82, 0x4A, 0xD6, 0xE6, 0xAF, 0xD7, 0x70, 0x0A, 0xEC, 0x1F, 0xC6, 0x7C, 0xA4, 0x5C, 0xEA, 0x1B, 0xBB, 0x4E, 0xF9, 0x8B, 0xEC, 0x06, 0x88, 0xC6, 0x16, 0x14, 0xB7, 0xCC, 0x77, 0xC1, 0x0B, 0xB4, 0x1D, 0x88, 0xE7, 0xDE, 0x0D, 0x64, 0xDF, 0xD0, 0x71, 0x76, 0xF5, 0x40, 0x38, 0xF5, 0x09, 0xAB, 0x57, 0x91, 0x02, 0x96, 0x39, 0x09, 0x6D, 0x30, 0xEB, 0x4C, 0x69, 0x34, 0x1E, 0xDD, 0xA3, 0x1B, 0xAD, 0xC9, 0xFC, 0xD3, 0xD0, 0x8A, 0x34, 0xD9, 0xDC, 0x48, 0x27, 0x7C, 0x3A, 0xB4, 0x5D, 0xB2, 0x34, 0x56, 0xAA, 0x51, 0x09, 0xD8, 0x74, 0xF0, 0xE3, 0xF2, 0xA0, 0x94, 0xC4, 0x35, 0x4C, 0xFD, 0xE7, 0xFB, 0x51, 0x6A, 0xCD, 0xAA, 0xDE, 0x32, 0x68, 0xE0, 0x5E, 0x3A, 0xC3, 0x62, 0x8E, 0xAB, 0xBA, 0x2B, 0x1D, 0xCE, 0x35, 0x24, 0x3F -}; - -// EC APP -static const unsigned char ecapp_fw23_prod_hdrpub[0x100] = -{ - 0xC1, 0xFB, 0x30, 0x9C, 0x96, 0xB3, 0xA9, 0x09, 0x62, 0x06, 0xFF, 0xA2, 0x8B, 0xAE, 0xD2, 0xB2, 0xC1, 0x8C, 0x47, 0xA2, 0xC4, 0x49, 0x15, 0x52, 0xE7, 0xD6, 0xA3, 0xEF, 0x7F, 0xD8, 0xB8, 0x28, 0xF1, 0x56, 0x31, 0xF4, 0x6C, 0x23, 0x69, 0xAA, 0x64, 0x8C, 0xC1, 0xD9, 0x37, 0xAA, 0xBB, 0x47, 0xB4, 0xAE, 0x5B, 0xFB, 0x19, 0xFA, 0xC4, 0xD5, 0x89, 0xB0, 0x36, 0x9F, 0xF9, 0x47, 0x56, 0xE8, 0x64, 0xC4, 0xB5, 0x46, 0x3A, 0x18, 0xD5, 0x12, 0x24, 0x61, 0x8D, 0xB2, 0xC8, 0xA6, 0xAC, 0x5C, 0x39, 0x38, 0x6B, 0x34, 0xC4, 0x68, 0x65, 0xF3, 0x72, 0xC7, 0x43, 0x9B, 0x91, 0xE9, 0xC5, 0x48, 0x2F, 0xC9, 0x84, 0xF7, 0xAC, 0xEA, 0xE1, 0x2C, 0xA8, 0xCB, 0x8F, 0xC4, 0x15, 0x4E, 0x85, 0x13, 0x83, 0x6E, 0x7F, 0x44, 0xD9, 0x6C, 0x69, 0x82, 0x38, 0x69, 0xE8, 0x90, 0xA4, 0x82, 0x52, 0xD7, 0xF2, 0x51, 0xAB, 0xEC, 0x26, 0xAD, 0x35, 0x21, 0x40, 0x7B, 0x6D, 0xA6, 0xC2, 0x78, 0xB3, 0xD1, 0xCC, 0xE8, 0x58, 0xC1, 0xF5, 0xC5, 0x47, 0xED, 0x7F, 0xBA, 0x64, 0xD4, 0x24, 0x14, 0x78, 0x8D, 0xD4, 0x81, 0xDE, 0x45, 0x17, 0x30, 0x30, 0x8F, 0xCA, 0x7E, 0x26, 0x74, 0x11, 0x04, 0x21, 0x9E, 0xE9, 0x07, 0x29, 0xEC, 0x20, 0x86, 0x13, 0x7B, 0x56, 0xA4, 0x2D, 0x8A, 0xCB, 0x7C, 0xED, 0x00, 0x84, 0xDE, 0x23, 0xDB, 0x3B, 0x6D, 0xBD, 0x1F, 0xAA, 0x6F, 0xB9, 0x37, 0x2B, 0xCF, 0x6E, 0x88, 0x3E, 0xDB, 0x1A, 0x79, 0xEC, 0x41, 0x8C, 0x63, 0xDA, 0x83, 0x2F, 0xFC, 0x8E, 0x65, 0xAB, 0x71, 0xAC, 0x94, 0xBC, 0x97, 0x8B, 0x5A, 0x04, 0x18, 0x72, 0xF4, 0x22, 0x37, 0x3B, 0xB4, 0x02, 0xF0, 0x25, 0x50, 0x78, 0xDC, 0xCF, 0x76, 0xBD, 0x59, 0xFC, 0x4C, 0xC6, 0x93, 0xCD, 0x64, 0x55, 0x59 -}; - -static const unsigned char ecapp_fw23_prod_acexsig[0x100] = -{ - 0x70, 0xF8, 0x6E, 0x2F, 0xC5, 0xE1, 0x2A, 0x05, 0x29, 0x3F, 0x04, 0xA1, 0xD8, 0x30, 0xEC, 0x09, 0x60, 0xC0, 0x90, 0x17, 0x53, 0x1E, 0x3C, 0x14, 0x56, 0xE2, 0xF9, 0x80, 0xD9, 0x22, 0x4A, 0xA7, 0xC7, 0xC6, 0x39, 0xC1, 0x77, 0x2F, 0xC5, 0x35, 0x2D, 0x7E, 0xC9, 0xED, 0x7E, 0xB6, 0x8F, 0x9A, 0x5F, 0xF1, 0x4B, 0xE2, 0xE7, 0x25, 0x37, 0xD1, 0xB6, 0xE5, 0x12, 0x00, 0xA2, 0x84, 0x8F, 0x43, 0xBB, 0xB7, 0x1E, 0x7B, 0xBA, 0x42, 0x45, 0x04, 0x6F, 0xB5, 0x39, 0x67, 0x2E, 0xD1, 0xA2, 0x01, 0xF9, 0x87, 0xF8, 0x6E, 0x88, 0x5B, 0x82, 0xB7, 0xA3, 0xCE, 0xA7, 0xE6, 0xD3, 0xB3, 0x73, 0x93, 0xCC, 0xD1, 0x69, 0x03, 0x53, 0xFD, 0xD3, 0xF1, 0x46, 0xF4, 0xAB, 0xC9, 0xB3, 0xEA, 0x09, 0xC4, 0x36, 0x27, 0x09, 0xFF, 0xBF, 0xA4, 0xF3, 0xC8, 0xB5, 0xD2, 0xC8, 0x0D, 0xC2, 0x6F, 0xA3, 0x7D, 0x63, 0xAD, 0xBE, 0x99, 0x2F, 0xAD, 0x0C, 0xC4, 0x22, 0xC5, 0x3C, 0x17, 0xF8, 0x06, 0x96, 0xBA, 0x1F, 0xEF, 0x77, 0xF5, 0xC6, 0xB4, 0x87, 0x8F, 0x27, 0xE3, 0x38, 0x27, 0x16, 0x13, 0x99, 0x70, 0x35, 0xF2, 0xB1, 0x79, 0x3C, 0x99, 0xB6, 0x2B, 0x32, 0x3D, 0x0F, 0x9A, 0x2D, 0xDB, 0x61, 0xAF, 0x76, 0x74, 0xBF, 0x9E, 0x04, 0x3B, 0xE7, 0xEA, 0xC6, 0x34, 0xC1, 0x53, 0xFF, 0x8E, 0x10, 0x1E, 0x3E, 0x6E, 0x72, 0xAB, 0x75, 0x13, 0x44, 0x94, 0x91, 0x94, 0x34, 0x45, 0xD0, 0x44, 0xD7, 0xC9, 0x17, 0x62, 0xC3, 0xB9, 0x72, 0xE2, 0x88, 0x6A, 0x8C, 0x1D, 0xEF, 0xF2, 0x93, 0x22, 0xF6, 0x88, 0xFC, 0x29, 0xB4, 0x8A, 0x9D, 0xF2, 0x4E, 0xF9, 0x3E, 0xEA, 0x4D, 0x41, 0x00, 0xB3, 0xC4, 0x8B, 0x80, 0xE0, 0xB2, 0xB4, 0xB4, 0x2B, 0xA5, 0xC0, 0x0F, 0x57, 0x90, 0xED, 0x64, 0x8D, 0xC0, 0xEE -}; - -/* SDK 7 (7.1.0) */ -// APP -static const unsigned char app_fw27_prod_hdrpub[0x100] = -{ - 0x9B, 0x3B, 0x52, 0x2D, 0x61, 0xF5, 0x0D, 0x14, 0x96, 0x09, 0x7A, 0x66, 0xC9, 0x86, 0x4C, 0x51, 0x61, 0x7D, 0xF8, 0x9B, 0xFD, 0xB0, 0x3E, 0x44, 0xC5, 0x0D, 0xC2, 0x6B, 0x6C, 0x66, 0x63, 0x3E, 0xE7, 0xC8, 0x04, 0xCB, 0x06, 0xC8, 0x35, 0xC5, 0x6D, 0xDB, 0x38, 0x36, 0xC5, 0x54, 0x32, 0xF0, 0x60, 0x11, 0x44, 0x9F, 0xD9, 0xFA, 0x6A, 0x43, 0xE1, 0x79, 0x56, 0xBF, 0xCF, 0xDD, 0xA8, 0x35, 0x15, 0x23, 0x51, 0xF9, 0x4B, 0xDA, 0x1A, 0xD9, 0x9D, 0x28, 0xF6, 0xCC, 0x1D, 0x53, 0x00, 0xC0, 0x3C, 0x13, 0x32, 0xBC, 0x7F, 0x75, 0x82, 0xCD, 0x79, 0xEF, 0x65, 0xBC, 0xCA, 0x98, 0x31, 0x0A, 0x7B, 0xEF, 0x18, 0xD8, 0xF3, 0x8D, 0x3A, 0x10, 0x22, 0xA0, 0xF2, 0x8D, 0xA5, 0xA8, 0xBE, 0xA0, 0x62, 0xEC, 0xE2, 0xC7, 0x6C, 0xCF, 0x06, 0x6B, 0xA7, 0xB5, 0xB8, 0x8C, 0xD5, 0x8E, 0xEF, 0xE3, 0x42, 0xC9, 0xAD, 0x44, 0x46, 0x3A, 0x4E, 0x77, 0x63, 0x02, 0xB4, 0x4E, 0xB4, 0x42, 0x65, 0x1D, 0x68, 0x98, 0x37, 0x7A, 0x27, 0x87, 0x31, 0xBE, 0x48, 0xFA, 0x4E, 0xD3, 0x85, 0xA6, 0xD6, 0xD2, 0x2D, 0xCD, 0x10, 0xC9, 0x13, 0x59, 0x12, 0x48, 0x14, 0x67, 0x3E, 0x40, 0xD3, 0xF8, 0x60, 0xA0, 0xBD, 0x77, 0x31, 0x76, 0x78, 0x85, 0x55, 0x53, 0x16, 0xF1, 0xB9, 0xFF, 0x7F, 0x3D, 0x9A, 0xF1, 0x33, 0x1E, 0x67, 0x8F, 0x6B, 0x4A, 0x7A, 0x79, 0x54, 0x8B, 0x43, 0xB5, 0xC2, 0xAF, 0xB8, 0x75, 0x11, 0xDE, 0x4D, 0x34, 0x6A, 0xD6, 0x5A, 0x3B, 0x48, 0x1F, 0x41, 0x9D, 0xF4, 0x58, 0x90, 0x67, 0xA8, 0x71, 0xD4, 0x09, 0x67, 0xF7, 0x55, 0xEF, 0xD3, 0x7C, 0x7D, 0x2F, 0x76, 0x84, 0x70, 0x6E, 0xAA, 0x75, 0x7D, 0xA9, 0x95, 0x42, 0xEF, 0x28, 0x29, 0x48, 0xCD, 0x79, 0xA8, 0x16, 0xB6, 0xB5 -}; - -static const unsigned char app_fw27_prod_acexsig[0x100] = -{ - 0x06, 0xDD, 0x68, 0x17, 0xAA, 0x40, 0x3A, 0x75, 0xD2, 0xCF, 0xA2, 0x5A, 0xC8, 0x1B, 0x74, 0x9D, 0x91, 0xCD, 0x38, 0x4E, 0xCA, 0x19, 0x60, 0x8E, 0x39, 0x71, 0x6C, 0xB9, 0xF9, 0x9F, 0x68, 0x44, 0xCF, 0x33, 0x94, 0x54, 0x72, 0xCC, 0xC6, 0x33, 0x96, 0x9F, 0x12, 0x07, 0xE9, 0x38, 0x87, 0x70, 0x11, 0x51, 0xFD, 0xBF, 0xD9, 0x2D, 0xFA, 0x3F, 0x70, 0x42, 0x75, 0x39, 0xE3, 0x97, 0x85, 0xAF, 0x7B, 0xC5, 0x87, 0x9B, 0x0B, 0xF9, 0xE4, 0x1C, 0xC5, 0x6B, 0x44, 0x2A, 0x10, 0x14, 0x86, 0xAA, 0xFE, 0x9E, 0x5B, 0x1D, 0x15, 0xBA, 0x8C, 0x34, 0xA2, 0xAF, 0x14, 0xD0, 0xD4, 0x0E, 0x7B, 0x3A, 0xD5, 0x3C, 0x53, 0xDB, 0x7C, 0xBE, 0x44, 0x58, 0x79, 0x42, 0x23, 0x3A, 0x77, 0xA3, 0x2C, 0xB9, 0xEB, 0x62, 0x19, 0x94, 0x2B, 0xA0, 0x67, 0x94, 0xC6, 0xB2, 0x90, 0xC6, 0x61, 0xFD, 0x43, 0xEC, 0xEA, 0x27, 0x7E, 0xA6, 0xB1, 0xED, 0xA9, 0x67, 0xED, 0x56, 0x91, 0x90, 0xF9, 0x32, 0x4E, 0xC3, 0x29, 0x5A, 0x84, 0x4C, 0xAB, 0x99, 0x75, 0x40, 0x8E, 0x19, 0xD9, 0x12, 0xD1, 0x06, 0x2D, 0xD0, 0x2C, 0xD9, 0x6C, 0x41, 0x35, 0x64, 0xDB, 0x80, 0x63, 0xB2, 0x01, 0xF8, 0x29, 0xAB, 0xF5, 0x70, 0x79, 0x4E, 0x0F, 0xFA, 0x23, 0x20, 0x2E, 0x04, 0x75, 0x48, 0x15, 0x9B, 0x71, 0xD5, 0x85, 0x09, 0x67, 0x7D, 0xAC, 0x6A, 0xFA, 0xC0, 0x16, 0xAF, 0x58, 0x26, 0xCD, 0x6F, 0x1F, 0xB8, 0xF5, 0x8D, 0xD1, 0x7D, 0x3D, 0x70, 0x2F, 0x08, 0xB8, 0x23, 0x61, 0x24, 0xAE, 0x94, 0x31, 0xA3, 0xBD, 0x1E, 0x18, 0xD7, 0x82, 0x92, 0xDD, 0x11, 0x79, 0x7D, 0x1F, 0xEC, 0x03, 0x08, 0x82, 0xCC, 0x52, 0x62, 0xC9, 0x27, 0x2D, 0x08, 0xD5, 0x6B, 0x4E, 0x86, 0x2E, 0x3F, 0x50, 0x5C, 0xA3, 0xC1, 0xDF, 0xF5 -}; diff --git a/makerom/exheader.c b/makerom/exheader.c index 0d8065b7..9765e1cb 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -108,7 +108,7 @@ int get_ExHeaderSettingsFromNcchset(exheader_settings *exhdrset, ncch_settings * /* Transfer settings */ exhdrset->keys = ncchset->keys; exhdrset->rsf = ncchset->rsfSet; - exhdrset->useAccessDescPreset = ncchset->keys->accessDescSign.presetType != desc_preset_NONE; + exhdrset->useAccessDescPreset = ncchset->keys->accessDescSign.presetType != desc_NotSpecified; /* Creating Output Buffer */ ncchset->sections.exhdr.size = sizeof(extended_hdr); diff --git a/makerom/keyset.h b/makerom/keyset.h index 54d38fcc..1e1db788 100644 --- a/makerom/keyset.h +++ b/makerom/keyset.h @@ -1,4 +1,5 @@ #pragma once +#include "desc/desc.h" typedef enum { @@ -28,16 +29,6 @@ typedef enum pki_CUSTOM, } pki_keyset; -typedef enum -{ - desc_preset_NONE, - desc_preset_APP, - desc_preset_EC_APP, - desc_preset_DLP, - desc_preset_DEMO, - desc_preset_FIRM, -} fixed_accessdesc_type; - // Structs typedef struct @@ -48,7 +39,7 @@ typedef struct struct { - fixed_accessdesc_type presetType; + u32 presetType; u32 targetFirmware; } accessDescSign; diff --git a/makerom/makerom.vcxproj b/makerom/makerom.vcxproj index fa3547f7..7d442011 100644 --- a/makerom/makerom.vcxproj +++ b/makerom/makerom.vcxproj @@ -101,9 +101,9 @@ + - diff --git a/makerom/makerom.vcxproj.filters b/makerom/makerom.vcxproj.filters index bcb59c1f..472d9e19 100644 --- a/makerom/makerom.vcxproj.filters +++ b/makerom/makerom.vcxproj.filters @@ -327,9 +327,6 @@ Resource Files\DESC - - Resource Files\DESC - Header Files @@ -339,6 +336,9 @@ Header Files + + Resource Files\DESC + diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 21a6eed4..8fa490e0 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -84,7 +84,7 @@ void SetDefaults(user_settings *set) { // Target Info set->common.keys.keyset = pki_TEST; - set->common.keys.accessDescSign.presetType = desc_preset_NONE; + set->common.keys.accessDescSign.presetType = desc_NotSpecified; // Build NCCH Info set->ncch.useSecCrypto = false; @@ -299,11 +299,13 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) char *app_type = calloc(app_type_len + 1, sizeof(char)); memcpy(app_type, tmp, app_type_len); - if (strcasecmp(app_type, "App") == 0 || strcasecmp(app_type, "SDApp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_APP; - else if (strcasecmp(app_type, "ECApp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_EC_APP; - else if (strcasecmp(app_type, "Demo") == 0) set->common.keys.accessDescSign.presetType = desc_preset_DEMO; - else if (strcasecmp(app_type, "DlpChild") == 0 || strcasecmp(app_type, "Dlp") == 0) set->common.keys.accessDescSign.presetType = desc_preset_DLP; - else if (strcasecmp(app_type, "FIRM") == 0) set->common.keys.accessDescSign.presetType = desc_preset_FIRM; + if (strcasecmp(app_type, "App") == 0 || strcasecmp(app_type, "SDApp") == 0) set->common.keys.accessDescSign.presetType = desc_Application; + else if (strcasecmp(app_type, "ECApp") == 0) set->common.keys.accessDescSign.presetType = desc_EcApplication; + else if (strcasecmp(app_type, "Demo") == 0) set->common.keys.accessDescSign.presetType = desc_Demo; + else if (strcasecmp(app_type, "DlpChild") == 0 || strcasecmp(app_type, "Dlp") == 0) set->common.keys.accessDescSign.presetType = desc_DlpChild; + else if (strcasecmp(app_type, "ExtApp") == 0) set->common.keys.accessDescSign.presetType = desc_ExtApplication; + else if (strcasecmp(app_type, "ExtDemo") == 0) set->common.keys.accessDescSign.presetType = desc_ExtDemo; + else if (strcasecmp(app_type, "ExtDlpChild") == 0 || strcasecmp(app_type, "ExtDlp") == 0) set->common.keys.accessDescSign.presetType = desc_ExtDlpChild; else { fprintf(stderr, "[SETTING ERROR] Accessdesc AppType preset '%s' not valid, please manually configure RSF\n", app_type); return USR_BAD_ARG; From a2f6616d21f08ca98c5c663c52d0951e3a87dad9 Mon Sep 17 00:00:00 2001 From: Myria Date: Sat, 26 Dec 2015 11:37:34 -0800 Subject: [PATCH 142/317] Fix Windows compilation by mapping fseeko64 to _fseeki64. --- ctrtool/utils.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ctrtool/utils.h b/ctrtool/utils.h index 4d766aac..f813b0c0 100644 --- a/ctrtool/utils.h +++ b/ctrtool/utils.h @@ -41,7 +41,12 @@ int makedir(const char* dir); u64 _fsize(const char *filename); -#ifndef _WIN32 +#ifdef _WIN32 +inline int fseeko64(FILE *__stream, long long __off, int __whence) +{ + return _fseeki64(__stream, __off, __whence); +} +#else extern int fseeko64 (FILE *__stream, __off64_t __off, int __whence); #endif From fc8eb8ce8cf65dc3a0651d555ae14f5339efffef Mon Sep 17 00:00:00 2001 From: Myria Date: Sat, 26 Dec 2015 11:38:37 -0800 Subject: [PATCH 143/317] Fix a bunch of Visual Studio compilation warnings, mostly related to truncating integers. --- ctrtool/cia.c | 12 +++++----- ctrtool/ctr.c | 2 +- ctrtool/cwav.c | 10 ++++---- ctrtool/filepath.c | 2 +- ctrtool/ivfc.c | 8 +++---- ctrtool/main.c | 6 ++--- ctrtool/ncch.c | 8 +++---- ctrtool/ncsd.c | 2 +- ctrtool/romfs.c | 6 ++--- ctrtool/utils.c | 60 +++++++++++++++++++++++----------------------- 10 files changed, 58 insertions(+), 58 deletions(-) diff --git a/ctrtool/cia.c b/ctrtool/cia.c index 91333bde..c2ba55bd 100644 --- a/ctrtool/cia.c +++ b/ctrtool/cia.c @@ -46,7 +46,7 @@ void cia_save(cia_context* ctx, u32 type, u32 flags) filepath* path = 0; ctr_tmd_body *body; ctr_tmd_contentchunk *chunk; - int i; + unsigned int i; char tmpname[255]; switch(type) @@ -151,7 +151,7 @@ void cia_save_blob(cia_context *ctx, char *out_path, u64 offset, u64 size, int d { u32 max = sizeof(buffer); if (max > size) - max = size; + max = (u32) size; if (max != fread(buffer, 1, max, ctx->file)) { @@ -195,9 +195,9 @@ void cia_process(cia_context* ctx, u32 actions) 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->offsettik = align((u32) (ctx->offsetcerts + ctx->sizecert), 64); + ctx->offsettmd = align((u32) (ctx->offsettik + ctx->sizetik), 64); + ctx->offsetcontent = align((u32) (ctx->offsettmd + ctx->sizetmd), 64); ctx->offsetmeta = align64(ctx->offsetcontent + ctx->sizecontent, 64); if (actions & InfoFlag) @@ -253,7 +253,7 @@ void cia_verify_contents(cia_context *ctx, u32 actions) ctr_tmd_contentchunk *chunk; u8 *verify_buf; u32 content_size=0; - int i; + unsigned i; // verify TMD content hashes, requires decryption .. body = tmd_get_body(&ctx->tmd); diff --git a/ctrtool/ctr.c b/ctrtool/ctr.c index 6948bf8f..e5198f70 100644 --- a/ctrtool/ctr.c +++ b/ctrtool/ctr.c @@ -244,7 +244,7 @@ int ctr_rsa_verify_hash(const u8 signature[0x100], const u8 hash[0x20], rsakey20 { ctr_rsa_context ctx; u32 result; - u8 output[0x100]; +// u8 output[0x100]; if (key->keytype == RSAKEY_INVALID) return Fail; diff --git a/ctrtool/cwav.c b/ctrtool/cwav.c index 4c1242d7..cf9f59b8 100644 --- a/ctrtool/cwav.c +++ b/ctrtool/cwav.c @@ -429,7 +429,7 @@ int cwav_dspadpcm_setup(cwav_dspadpcmstate* state, cwav_context* ctx, int isloop } 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; + state->channelstate[i].sampleoffset = (u32) (ctx->offset + getle32(adpcmchannel->info.sampleref.offset) + getle32(ctx->header.datablockref.offset) + 8 + startoffset); if (isloop) { state->channelstate[i].yn1 = getle16(adpcminfo->loopyn1); @@ -627,7 +627,7 @@ int cwav_imaadpcm_setup(cwav_imaadpcmstate* state, cwav_context* ctx, int isloop } 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; + state->channelstate[i].sampleoffset = (u32) (ctx->offset + getle32(adpcmchannel->info.sampleref.offset) + getle32(ctx->header.datablockref.offset) + 8 + startoffset); if (isloop) { state->channelstate[i].data = getle16(adpcminfo->loopdata); @@ -826,7 +826,7 @@ int cwav_pcm_setup(cwav_pcmstate* state, cwav_context* ctx, int isloop) cwav_channel* pcmchannel = &ctx->channel[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; + state->channelstate[i].sampleoffset = (u32) (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); } @@ -932,7 +932,7 @@ 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 infoheaderoffset = (u32) (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]); @@ -962,7 +962,7 @@ void cwav_print(cwav_context* ctx) { u32 channeloffset = infoheaderoffset + 0x1C + getle32(ctx->channel[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; + u32 sampleoffset = (u32) (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)); diff --git a/ctrtool/filepath.c b/ctrtool/filepath.c index 94321e22..366bb65b 100644 --- a/ctrtool/filepath.c +++ b/ctrtool/filepath.c @@ -47,7 +47,7 @@ void filepath_append_utf16(filepath* fpath, const u8* name) if (code > 0x7F) code = '#'; - fpath->pathname[size++] = code; + fpath->pathname[size++] = (char) code; } fpath->pathname[size] = 0; diff --git a/ctrtool/ivfc.c b/ctrtool/ivfc.c index fb7a5f05..041afda3 100644 --- a/ctrtool/ivfc.c +++ b/ctrtool/ivfc.c @@ -51,7 +51,7 @@ void ivfc_fseek(ivfc_context* ctx, u64 offset) u64 data_pos = offset - ctx->offset; fseeko64(ctx->file, offset, SEEK_SET); ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); - ctr_add_counter(&ctx->aes, data_pos / 0x10); + ctr_add_counter(&ctx->aes, (u32) (data_pos / 0x10)); } size_t ivfc_fread(ivfc_context* ctx, void* buffer, size_t size, size_t count) @@ -128,7 +128,7 @@ void ivfc_verify(ivfc_context* ctx, u32 flags) { ivfc_level* level = ctx->level + i; - blockcount = level->datasize / level->hashblocksize; + blockcount = (u32) (level->datasize / level->hashblocksize); if (level->datasize % level->hashblocksize != 0) { fprintf(stderr, "Error, IVFC block size mismatch\n"); @@ -160,7 +160,7 @@ void ivfc_read(ivfc_context* ctx, u64 offset, u64 size, u8* buffer) } ivfc_fseek(ctx, ctx->offset + offset); - if (size != ivfc_fread(ctx, buffer, 1, size)) + if (size != ivfc_fread(ctx, buffer, 1, (size_t) size)) { fprintf(stderr, "Error, IVFC could not read file\n"); return; @@ -177,7 +177,7 @@ void ivfc_hash(ivfc_context* ctx, u64 offset, u64 size, u8* hash) ivfc_read(ctx, offset, size, ctx->buffer); - ctr_sha_256(ctx->buffer, size, hash); + ctr_sha_256(ctx->buffer, (u32) size, hash); } void ivfc_print(ivfc_context* ctx) diff --git a/ctrtool/main.c b/ctrtool/main.c index 4a7199be..7997f172 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -344,7 +344,7 @@ int main(int argc, char* argv[]) firm_init(&firmctx); firm_set_file(&firmctx, ctx.infile); - firm_set_size(&firmctx, ctx.infilesize); + firm_set_size(&firmctx, (u32) ctx.infilesize); firm_set_usersettings(&firmctx, &ctx.usersettings); firm_process(&firmctx, ctx.actions); @@ -399,7 +399,7 @@ int main(int argc, char* argv[]) tmd_init(&tmdctx); tmd_set_file(&tmdctx, ctx.infile); - tmd_set_size(&tmdctx, ctx.infilesize); + tmd_set_size(&tmdctx, (u32) ctx.infilesize); tmd_set_usersettings(&tmdctx, &ctx.usersettings); tmd_process(&tmdctx, ctx.actions); @@ -412,7 +412,7 @@ int main(int argc, char* argv[]) lzss_init(&lzssctx); lzss_set_file(&lzssctx, ctx.infile); - lzss_set_size(&lzssctx, ctx.infilesize); + lzss_set_size(&lzssctx, (u32) ctx.infilesize); lzss_set_usersettings(&lzssctx, &ctx.usersettings); lzss_process(&lzssctx, ctx.actions); diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 7c06b231..8d2a9ff6 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -48,7 +48,7 @@ void ncch_set_file(ncch_context* 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); + u32 mediaunitsize = (u32) ncch_get_mediaunit_size(ctx); u8* partitionid = ctx->header.partitionid; u32 i; u64 x = 0; @@ -73,7 +73,7 @@ void ncch_get_counter(ncch_context* ctx, u8 counter[16], u8 type) for(i=0; i<8; i++) counter[i] = partitionid[i]; for(i=0; i<4; i++) - counter[12+i] = x>>((3-i)*8); + counter[12+i] = (u8) (x>>((3-i)*8)); } } @@ -148,7 +148,7 @@ int ncch_extract_buffer(ncch_context* ctx, u8* buffer, u32 buffersize, u32* outs u32 read_len = buffersize; if (read_len > ctx->extractsize) - read_len = ctx->extractsize; + read_len = (u32) ctx->extractsize; *outsize = read_len; @@ -234,7 +234,7 @@ void ncch_save(ncch_context* ctx, u32 type, u32 flags) void ncch_verify(ncch_context* ctx, u32 flags) { - u32 mediaunitsize = ncch_get_mediaunit_size(ctx); + u32 mediaunitsize = (u32) 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); diff --git a/ctrtool/ncsd.c b/ctrtool/ncsd.c index 384216a1..4a7d9284 100644 --- a/ctrtool/ncsd.c +++ b/ctrtool/ncsd.c @@ -120,7 +120,7 @@ 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); + unsigned int mediaunitsize = (unsigned int) ncsd_get_mediaunit_size(ctx); memcpy(magic, header->magic, 4); diff --git a/ctrtool/romfs.c b/ctrtool/romfs.c index 5889bdc4..f8c9ffb0 100644 --- a/ctrtool/romfs.c +++ b/ctrtool/romfs.c @@ -50,7 +50,7 @@ void romfs_fseek(romfs_context* ctx, u64 offset) u64 data_pos = offset - ctx->offset; fseeko64(ctx->file, offset, SEEK_SET); ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); - ctr_add_counter(&ctx->aes, data_pos / 0x10); + ctr_add_counter(&ctx->aes, (u32) (data_pos / 0x10)); } size_t romfs_fread(romfs_context* ctx, void* buffer, size_t size, size_t count) @@ -91,7 +91,7 @@ void romfs_process(romfs_context* ctx, u32 actions) return; } - ctx->infoblockoffset = ctx->offset + 0x1000; + ctx->infoblockoffset = (u32) (ctx->offset + 0x1000); romfs_fseek(ctx, ctx->infoblockoffset); romfs_fread(ctx, &ctx->infoheader, 1, sizeof(romfs_infoheader)); @@ -363,7 +363,7 @@ void romfs_extract_datafile(romfs_context* ctx, u64 offset, u64 size, const osch { max = sizeof(buffer); if (max > size) - max = size; + max = (u32) size; if (max != romfs_fread(ctx, buffer, 1, max)) { diff --git a/ctrtool/utils.c b/ctrtool/utils.c index 1f21f073..fe12a62e 100644 --- a/ctrtool/utils.c +++ b/ctrtool/utils.c @@ -73,59 +73,59 @@ u32 getbe16(const u8* p) void putle16(u8* p, u16 n) { - p[0] = n; - p[1] = n>>8; + p[0] = (u8) n; + p[1] = (u8) (n>>8); } void putle32(u8* p, u32 n) { - p[0] = n; - p[1] = n>>8; - p[2] = n>>16; - p[3] = n>>24; + p[0] = (u8) n; + p[1] = (u8) (n>>8); + p[2] = (u8) (n>>16); + p[3] = (u8) (n>>24); } void putle64(u8* p, u64 n) { - p[0] = n; - p[1] = n >> 8; - p[2] = n >> 16; - p[3] = n >> 24; - p[4] = n >> 32; - p[5] = n >> 40; - p[6] = n >> 48; - p[7] = n >> 56; + p[0] = (u8) n; + p[1] = (u8) (n >> 8); + p[2] = (u8) (n >> 16); + p[3] = (u8) (n >> 24); + p[4] = (u8) (n >> 32); + p[5] = (u8) (n >> 40); + p[6] = (u8) (n >> 48); + p[7] = (u8) (n >> 56); } void putbe16(u8* p, u16 n) { - p[1] = n; - p[0] = n >> 8; + p[1] = (u8) n; + p[0] = (u8) (n >> 8); } void putbe32(u8* p, u32 n) { - p[3] = n; - p[2] = n >> 8; - p[1] = n >> 16; - p[0] = n >> 24; + p[3] = (u8) n; + p[2] = (u8) (n >> 8); + p[1] = (u8) (n >> 16); + p[0] = (u8) (n >> 24); } void putbe64(u8* p, u64 n) { - p[7] = n; - p[6] = n >> 8; - p[5] = n >> 16; - p[4] = n >> 24; - p[3] = n >> 32; - p[2] = n >> 40; - p[1] = n >> 48; - p[0] = n >> 56; + p[7] = (u8) n; + p[6] = (u8) (n >> 8); + p[5] = (u8) (n >> 16); + p[4] = (u8) (n >> 24); + p[3] = (u8) (n >> 32); + p[2] = (u8) (n >> 40); + p[1] = (u8) (n >> 48); + p[0] = (u8) (n >> 56); } void readkeyfile(u8* key, const char* keyfname) { - u32 keysize = _fsize(keyfname); + u64 keysize = _fsize(keyfname); FILE* f = fopen(keyfname, "rb"); if (0 == f) @@ -136,7 +136,7 @@ void readkeyfile(u8* key, const char* keyfname) if (keysize != 16) { - fprintf(stdout, "Error key size mismatch, got %d, expected %d\n", keysize, 16); + fprintf(stdout, "Error key size mismatch, got %"PRId64", expected %d\n", keysize, 16); goto clean; } From a0ee2ae83ce8df792d6885b9ae7a3c3396d06183 Mon Sep 17 00:00:00 2001 From: Myria Date: Sat, 26 Dec 2015 11:39:33 -0800 Subject: [PATCH 144/317] Disable lame "security" warnings from Visual Studio. Changed C runtime library to static for release builds, so that no external .dll is required. --- ctrtool/ctrtool.vcxproj | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ctrtool/ctrtool.vcxproj b/ctrtool/ctrtool.vcxproj index d16bc575..e0d01b55 100644 --- a/ctrtool/ctrtool.vcxproj +++ b/ctrtool/ctrtool.vcxproj @@ -54,7 +54,7 @@ Disabled windows;.;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -75,15 +75,14 @@ MaxSpeed true windows;.;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDLL + WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded true Level3 ProgramDatabase - c:\dev\tools\bin\ctrtool.exe true Console true From 35ab1ad3a0c394ee03a60dbf21c53a4336f17e2f Mon Sep 17 00:00:00 2001 From: Myria Date: Sat, 26 Dec 2015 11:40:09 -0800 Subject: [PATCH 145/317] Formatting fix for help text. --- ctrtool/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/main.c b/ctrtool/main.c index 7997f172..76f9ad6f 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -66,7 +66,7 @@ static void usage(const char *argv0) " -n, --ncch=index Specify NCCH partition index.\n" " --exheader=file Specify Extended Header file path.\n" " --logo=file Specify Logo file path.\n" - " --plainrgn=file Specify Plain region file path" + " --plainrgn=file Specify Plain region file path\n" " --exefs=file Specify ExeFS file path.\n" " --exefsdir=dir Specify ExeFS directory path.\n" " --romfs=file Specify RomFS file path.\n" From 57818a56776cfa3171c314f39b39b082d1487190 Mon Sep 17 00:00:00 2001 From: Myria Date: Sat, 26 Dec 2015 11:45:54 -0800 Subject: [PATCH 146/317] keysize is unsigned. --- ctrtool/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/utils.c b/ctrtool/utils.c index fe12a62e..92125e5b 100644 --- a/ctrtool/utils.c +++ b/ctrtool/utils.c @@ -136,7 +136,7 @@ void readkeyfile(u8* key, const char* keyfname) if (keysize != 16) { - fprintf(stdout, "Error key size mismatch, got %"PRId64", expected %d\n", keysize, 16); + fprintf(stdout, "Error key size mismatch, got %"PRIu64", expected %d\n", keysize, 16); goto clean; } From 7e9893ec4d97cfad3c18715b91751e3f008d9dce Mon Sep 17 00:00:00 2001 From: Myria Date: Sat, 26 Dec 2015 12:45:45 -0800 Subject: [PATCH 147/317] Added support for --showsyscalls, which shows the names of the system calls, rather than just their IDs. --- ctrtool/ctrtool.vcxproj | 2 + ctrtool/ctrtool.vcxproj.filters | 6 ++ ctrtool/exheader.c | 84 ++++++++++------ ctrtool/exheader.h | 2 +- ctrtool/main.c | 3 + ctrtool/syscalls.c | 165 ++++++++++++++++++++++++++++++++ ctrtool/syscalls.h | 19 ++++ ctrtool/types.h | 3 +- 8 files changed, 253 insertions(+), 31 deletions(-) create mode 100644 ctrtool/syscalls.c create mode 100644 ctrtool/syscalls.h diff --git a/ctrtool/ctrtool.vcxproj b/ctrtool/ctrtool.vcxproj index e0d01b55..71596d61 100644 --- a/ctrtool/ctrtool.vcxproj +++ b/ctrtool/ctrtool.vcxproj @@ -99,6 +99,7 @@ + @@ -131,6 +132,7 @@ + diff --git a/ctrtool/ctrtool.vcxproj.filters b/ctrtool/ctrtool.vcxproj.filters index 075c29de..f7862f27 100644 --- a/ctrtool/ctrtool.vcxproj.filters +++ b/ctrtool/ctrtool.vcxproj.filters @@ -117,6 +117,9 @@ Source Files + + Source Files + @@ -209,5 +212,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index 9715616f..e9a6af1d 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -1,3 +1,4 @@ +#include #include #include @@ -5,6 +6,7 @@ #include "exheader.h" #include "utils.h" #include "ncch.h" +#include "syscalls.h" #include void exheader_init(exheader_context* ctx) @@ -193,7 +195,7 @@ int exheader_process(exheader_context* ctx, u32 actions) exheader_verify(ctx); if (actions & InfoFlag) - exheader_print(ctx); + exheader_print(ctx, actions); return 1; } @@ -231,7 +233,7 @@ void exheader_print_arm9accesscontrol(exheader_context* ctx) } } -void exheader_print_arm11kernelcapabilities(exheader_context* ctx) +void exheader_print_arm11kernelcapabilities(exheader_context* ctx, u32 actions) { unsigned int i, j; unsigned int systemcallmask[8]; @@ -294,42 +296,66 @@ void exheader_print_arm11kernelcapabilities(exheader_context* ctx) } fprintf(stdout, "Allowed systemcalls: "); - for(i=0; i<8; i++) + if(!(actions & ShowSyscallsFlag)) { - for(j=0; j<24; j++) + for(i=0; i<8; i++) { - svcmask = systemcallmask[i]; - - if (svcmask & (1< 0x%02X %s\n", svcid, svcname); + } + } + } + } fprintf(stdout, "Allowed interrupts: "); for(i=0; i<0x7F; i++) @@ -618,7 +644,7 @@ const char* exheader_getsystemmodeextstring(u8 systemmodeext, u8 systemmode) } -void exheader_print(exheader_context* ctx) +void exheader_print(exheader_context* ctx, u32 actions) { u32 i; u64 savedatasize = getle64(ctx->header.systeminfo.savedatasize); @@ -679,7 +705,7 @@ void exheader_print(exheader_context* ctx) fprintf(stdout, "Main thread priority: %d %s\n", ctx->system_local_caps.priority, exheader_getvalidstring(ctx->validpriority)); // print resource limit descriptor too? currently mostly zeroes... exheader_print_arm11storageinfo(ctx); - exheader_print_arm11kernelcapabilities(ctx); + exheader_print_arm11kernelcapabilities(ctx, actions); exheader_print_arm9accesscontrol(ctx); fprintf(stdout, "Service access: %s\n", exheader_getvalidstring(ctx->validservicecontrol)); diff --git a/ctrtool/exheader.h b/ctrtool/exheader.h index 9691e0f1..9ac656f0 100644 --- a/ctrtool/exheader.h +++ b/ctrtool/exheader.h @@ -194,7 +194,7 @@ int exheader_get_compressedflag(exheader_context* ctx); void exheader_read(exheader_context* ctx, u32 actions); int exheader_process(exheader_context* ctx, u32 actions); const char* exheader_getvalidstring(int valid); -void exheader_print(exheader_context* ctx); +void exheader_print(exheader_context* ctx, u32 actions); void exheader_verify(exheader_context* ctx); int exheader_hash_valid(exheader_context* ctx); int exheader_programid_valid(exheader_context* ctx); diff --git a/ctrtool/main.c b/ctrtool/main.c index 76f9ad6f..8282c9b3 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -58,6 +58,7 @@ static void usage(const char *argv0) " --ncchkey=key Set ncch key.\n" " --ncchsyskey=key Set ncch fixed system key.\n" " --showkeys Show the keys being used.\n" + " --showsyscalls Show system call names instead of numbers.\n" " -t, --intype=type Specify input file type [ncsd, ncch, exheader, cia, tmd, lzss,\n" " firm, cwav, exefs, romfs]\n" "LZSS options:\n" @@ -150,6 +151,7 @@ int main(int argc, char* argv[]) {"decompresscode", 0, NULL, 21}, {"titlekey", 1, NULL, 22}, {"plainrgn", 1, NULL, 23}, + {"showsyscalls", 0, NULL, 24}, {NULL}, }; @@ -239,6 +241,7 @@ int main(int argc, char* argv[]) case 21: ctx.actions |= DecompressCodeFlag; break; case 22: keyset_parse_titlekey(&tmpkeys, optarg, strlen(optarg)); break; case 23: settings_set_plainrgn_path(&ctx.usersettings, optarg); break; + case 24: ctx.actions |= ShowSyscallsFlag; break; default: usage(argv[0]); diff --git a/ctrtool/syscalls.c b/ctrtool/syscalls.c new file mode 100644 index 00000000..edf8e8c4 --- /dev/null +++ b/ctrtool/syscalls.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include "syscalls.h" + +// List of 3DS system calls. NULL indicates unknown. +static const char *const syscall_list[NUM_SYSCALLS] = +{ + NULL, // 00 + "ControlMemory", // 01 + "QueryMemory", // 02 + "ExitProcess", // 03 + "GetProcessAffinityMask", // 04 + "SetProcessAffinityMask", // 05 + "GetProcessIdealProcessor", // 06 + "SetProcessIdealProcessor", // 07 + "CreateThread", // 08 + "ExitThread", // 09 + "SleepThread", // 0A + "GetThreadPriority", // 0B + "SetThreadPriority", // 0C + "GetThreadAffinityMask", // 0D + "SetThreadAffinityMask", // 0E + "GetThreadIdealProcessor", // 0F + "SetThreadIdealProcessor", // 10 + "GetCurrentProcessorNumber", // 11 + "Run", // 12 + "CreateMutex", // 13 + "ReleaseMutex", // 14 + "CreateSemaphore", // 15 + "ReleaseSemaphore", // 16 + "CreateEvent", // 17 + "SignalEvent", // 18 + "ClearEvent", // 19 + "CreateTimer", // 1A + "SetTimer", // 1B + "CancelTimer", // 1C + "ClearTimer", // 1D + "CreateMemoryBlock", // 1E + "MapMemoryBlock", // 1F + "UnmapMemoryBlock", // 20 + "CreateAddressArbiter", // 21 + "ArbitrateAddress", // 22 + "CloseHandle", // 23 + "WaitSynchronization1", // 24 + "WaitSynchronizationN", // 25 + "SignalAndWait", // 26 + "DuplicateHandle", // 27 + "GetSystemTick", // 28 + "GetHandleInfo", // 29 + "GetSystemInfo", // 2A + "GetProcessInfo", // 2B + "GetThreadInfo", // 2C + "ConnectToPort", // 2D + "SendSyncRequest1", // 2E + "SendSyncRequest2", // 2F + "SendSyncRequest3", // 30 + "SendSyncRequest4", // 31 + "SendSyncRequest", // 32 + "OpenProcess", // 33 + "OpenThread", // 34 + "GetProcessId", // 35 + "GetProcessIdOfThread", // 36 + "GetThreadId", // 37 + "GetResourceLimit", // 38 + "GetResourceLimitLimitValues", // 39 + "GetResourceLimitCurrentValues", // 3A + "GetThreadContext", // 3B + "Break", // 3C + "OutputDebugString", // 3D + "ControlPerformanceCounter", // 3E + NULL, // 3F + NULL, // 40 + NULL, // 41 + NULL, // 42 + NULL, // 43 + NULL, // 44 + NULL, // 45 + NULL, // 46 + "CreatePort", // 47 + "CreateSessionToPort", // 48 + "CreateSession", // 49 + "AcceptSession", // 4A + "ReplyAndReceive1", // 4B + "ReplyAndReceive2", // 4C + "ReplyAndReceive3", // 4D + "ReplyAndReceive4", // 4E + "ReplyAndReceive", // 4F + "BindInterrupt", // 50 + "UnbindInterrupt", // 51 + "InvalidateProcessDataCache", // 52 + "StoreProcessDataCache", // 53 + "FlushProcessDataCache", // 54 + "StartInterProcessDma", // 55 + "StopDma", // 56 + "GetDmaState", // 57 + "RestartDma", // 58 + NULL, // 59 + NULL, // 5A + NULL, // 5B + NULL, // 5C + NULL, // 5D + NULL, // 5E + NULL, // 5F + "DebugActiveProcess", // 60 + "BreakDebugProcess", // 61 + "TerminateDebugProcess", // 62 + "GetProcessDebugEvent", // 63 + "ContinueDebugEvent", // 64 + "GetProcessList", // 65 + "GetThreadList", // 66 + "GetDebugThreadContext", // 67 + "SetDebugThreadContext", // 68 + "QueryDebugProcessMemory", // 69 + "ReadProcessMemory", // 6A + "WriteProcessMemory", // 6B + "SetHardwareBreakPoint", // 6C + "GetDebugThreadParam", // 6D + NULL, // 6E + NULL, // 6F + "ControlProcessMemory", // 70 + "MapProcessMemory", // 71 + "UnmapProcessMemory", // 72 + "CreateCodeSet", // 73 + NULL, // 74 + "CreateProcess", // 75 + "TerminateProcess", // 76 + "SetProcessResourceLimits", // 77 + "CreateResourceLimit", // 78 + "SetResourceLimitValues", // 79 + "AddCodeSegment", // 7A + "Backdoor", // 7B + "KernelSetState", // 7C + "QueryProcessMemory", // 7D + NULL, // 7E + NULL, // 7F +}; + + +void syscall_get_name(char *output, size_t size, unsigned int call_num) +{ + typedef char StaticAssert[sizeof(syscall_list) / sizeof(syscall_list[0]) == NUM_SYSCALLS ? 1 : -1]; + + if (size == 0) + { + return; + } + + const char *name = NULL; + if (call_num < (unsigned int) NUM_SYSCALLS) + { + name = syscall_list[call_num]; + } + + char name_buf[] = "UnknownXX"; + sprintf(&name_buf[sizeof(name_buf) - 3], "%02X", call_num & 0xFFu); + + name = name ? name : name_buf; + + size_t length = strlen(name); + length = (length > (size - 1)) ? (size - 1) : length; + + memcpy(output, name, length); + output[length] = '\0'; +} diff --git a/ctrtool/syscalls.h b/ctrtool/syscalls.h new file mode 100644 index 00000000..5afca898 --- /dev/null +++ b/ctrtool/syscalls.h @@ -0,0 +1,19 @@ +#ifndef _SYSCALLS_H_ +#define _SYSCALLS_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum { NUM_SYSCALLS = 0x80 }; + +void syscall_get_name(char *output, size_t size, unsigned int call_num); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/ctrtool/types.h b/ctrtool/types.h index 9234fe1f..5904a080 100644 --- a/ctrtool/types.h +++ b/ctrtool/types.h @@ -23,7 +23,8 @@ enum flags VerifyFlag = (1<<4), RawFlag = (1<<5), ShowKeysFlag = (1<<6), - DecompressCodeFlag = (1<<7) + DecompressCodeFlag = (1<<7), + ShowSyscallsFlag = (1<<8), }; enum validstate From 11d4d2a5edcbd968ae4a5edc6851e686253a6aa2 Mon Sep 17 00:00:00 2001 From: Myria Date: Sat, 26 Dec 2015 18:13:59 -0800 Subject: [PATCH 148/317] Supposedly, not having an fseeko64 is only a Visual Studio thing. Would fix mingw32, supposedly. --- ctrtool/utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/utils.h b/ctrtool/utils.h index f813b0c0..1d5eac83 100644 --- a/ctrtool/utils.h +++ b/ctrtool/utils.h @@ -41,7 +41,7 @@ int makedir(const char* dir); u64 _fsize(const char *filename); -#ifdef _WIN32 +#ifdef _MSC_VER inline int fseeko64(FILE *__stream, long long __off, int __whence) { return _fseeki64(__stream, __off, __whence); From 695fadcb3c74360349762466f7516cb44d7fd4bb Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 27 Dec 2015 14:55:39 +0800 Subject: [PATCH 149/317] [makerom] misc --- makerom/accessdesc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/accessdesc.c b/makerom/accessdesc.c index 1b8a3ef9..847118f4 100644 --- a/makerom/accessdesc.c +++ b/makerom/accessdesc.c @@ -132,7 +132,7 @@ int accessdesc_GetSignFromPreset(exheader_settings *exhdrset) return CANNOT_SIGN_ACCESSDESC; } - if(!pre_sign->modulus && exhdrset->keys->rsa.requiresPresignedDesc){ + if(!pre_sign && exhdrset->keys->rsa.requiresPresignedDesc){ fprintf(stderr,"[ACEXDESC ERROR] This AccessDesc template needs to be signed, the current keyset is incapable of doing so. Please configure RSF file with the appropriate signature data.\n"); return CANNOT_SIGN_ACCESSDESC; } From 26affd158c5636e13e1b77c001d45e62bd58eff7 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 5 Jan 2016 22:52:47 +0800 Subject: [PATCH 150/317] [makerom] misc --- makerom/aes_keygen.c | 124 ++++++++++++++++++++++++++++++++ makerom/aes_keygen.h | 12 ++++ makerom/keyset.c | 15 ++-- makerom/makerom.vcxproj | 2 + makerom/makerom.vcxproj.filters | 6 ++ 5 files changed, 149 insertions(+), 10 deletions(-) create mode 100644 makerom/aes_keygen.c create mode 100644 makerom/aes_keygen.h diff --git a/makerom/aes_keygen.c b/makerom/aes_keygen.c new file mode 100644 index 00000000..e5a35d6d --- /dev/null +++ b/makerom/aes_keygen.c @@ -0,0 +1,124 @@ +#include "aes_keygen.h" + +// 128bit wrap-around math +int32_t wrap_index(int32_t i) +{ + return i < 0 ? ((i % 16) + 16) % 16 : (i > 15 ? i % 16 : i); +} + +void n128_rrot(const uint8_t *in, uint32_t rot, uint8_t *out) +{ + uint32_t bit_shift, byte_shift; + + rot = rot % 128; + byte_shift = rot / 8; + bit_shift = rot % 8; + + for (int32_t i = 0; i < 16; i++) { + out[i] = (in[wrap_index(i - byte_shift)] >> bit_shift) | (in[wrap_index(i - byte_shift - 1)] << (8 - bit_shift)); + } + +} + +void n128_lrot(const uint8_t *in, uint32_t rot, uint8_t *out) +{ + uint32_t bit_shift, byte_shift; + + rot = rot % 128; + byte_shift = rot / 8; + bit_shift = rot % 8; + + for (int32_t i = 0; i < 16; i++) { + out[i] = (in[wrap_index(i + byte_shift)] << bit_shift) | (in[wrap_index(i + byte_shift + 1)] >> (8 - bit_shift)); + } +} + +/* out = a + b +*/ +void n128_add(const uint8_t *a, const uint8_t *b, uint8_t *out) +{ + uint8_t carry = 0; + uint32_t sum = 0; + + for (int i = 15; i >= 0; i--) { + sum = a[i] + b[i] + carry; + carry = sum >> 8; + out[i] = sum & 0xff; + } + + while (carry != 0) { + for (int i = 15; i >= 0; i--) { + sum = out[i] + carry; + carry = sum >> 8; + out[i] = sum & 0xff; + } + } +} + +/* out = a - b +*/ +void n128_sub(const uint8_t *a, const uint8_t *b, uint8_t *out) +{ + uint8_t carry = 0; + uint32_t sum = 0; + + for (int i = 15; i >= 0; i--) { + sum = a[i] - (b[i] + carry); + + // check to see if anything was borrowed from next byte + if (a[i] < (b[i] + carry)) { + sum += 0x100; + carry = 1; + } + else { + carry = 0; + } + + // set value + out[i] = sum & 0xff; + } + + + while (carry != 0) { + for (int i = 15; i >= 0; i--) { + sum = out[i] - carry; + + // check to see if anything was borrowed from next byte + if (out[i] < carry) { + sum += 0x100; + carry = 1; + } + else { + carry = 0; + } + + out[i] = sum & 0xff; + } + } + +} + +void n128_xor(const uint8_t *a, const uint8_t *b, uint8_t *out) +{ + for (int i = 0; i < 16; i++) { + out[i] = a[i] ^ b[i]; + } +} + +// keygen algorithm +void n_aes_keygen(const uint8_t *x, uint8_t x_shift, const uint8_t *y, uint8_t y_shift, const uint8_t *keygen_constant, uint8_t *key) +{ + // overall algo: + // key = ((x >>> x_shift) ^ (y >>> y_shift)) + keygen_constant + uint8_t x_rot[16], y_rot[16], key_xy[16]; + + // Rotate x and y + n128_rrot(x, x_shift, x_rot); + n128_rrot(y, y_shift, y_rot); + + // XOR rotated x and y + n128_xor(x_rot, y_rot, key_xy); + + // Add secret + n128_add(key_xy, keygen_constant, key); +} \ No newline at end of file diff --git a/makerom/aes_keygen.h b/makerom/aes_keygen.h new file mode 100644 index 00000000..0314744a --- /dev/null +++ b/makerom/aes_keygen.h @@ -0,0 +1,12 @@ +#pragma once +#include + +/* + AES Key generator for the Nintendo (Handheld) Consoles + + BYO keygen constants, and input >>> parameters + + key = ((x >>> x_shift) ^ (y >>> y_shift)) + keygen_constant +*/ + +void n_aes_keygen(const uint8_t *x, uint8_t x_shift, const uint8_t *y, uint8_t y_shift, const uint8_t *keygen_constant, uint8_t *key); diff --git a/makerom/keyset.c b/makerom/keyset.c index cdb32a6b..d4684717 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -1,4 +1,5 @@ #include "lib.h" +#include "aes_keygen.h" // KeyData #include "pki/test.h" // Test PKI @@ -49,16 +50,10 @@ void PrintBadKeySize(char *path, u32 size) u8* AesKeyScrambler(u8 *key, const u8 *keyX, const u8 *keyY) { - // Process keyX/keyY to get raw normal key - for(int i = 0; i < 16; i++) - key[i] = keyX[i] ^ ((keyY[i] >> 2) | ((keyY[i < 15 ? i+1 : 0] & 3) << 6)); // keyX[i] ^ - - const u8 SCRAMBLE_SECRET[16] = {0x51, 0xD7, 0x5D, 0xBE, 0xFD, 0x07, 0x57, 0x6A, 0x1C, 0xFC, 0x2A, 0xF0, 0x94, 0x4B, 0xD5, 0x6C}; - - // Apply Secret to get final normal key - for(int i = 0; i < 16; i++) - key[i] = key[i] ^ SCRAMBLE_SECRET[i]; - + static const uint8_t CTR_KEYGEN_CONST[16] = { 0xEE, 0x2E, 0xA9, 0x3B, 0x45, 0x0F, 0xFC, 0xF4, 0xD5, 0x62, 0xFF, 0x02, 0x04, 0x01, 0x22, 0xC8 }; + static const uint8_t CTR_KEYX_SHIFT = 39; + static const uint8_t CTR_KEYY_SHIFT = 41; + n_aes_keygen(keyX, CTR_KEYX_SHIFT, keyY, CTR_KEYY_SHIFT, CTR_KEYGEN_CONST, key); return key; } diff --git a/makerom/makerom.vcxproj b/makerom/makerom.vcxproj index 7d442011..dbdfd0a3 100644 --- a/makerom/makerom.vcxproj +++ b/makerom/makerom.vcxproj @@ -91,6 +91,7 @@ + @@ -194,6 +195,7 @@ + diff --git a/makerom/makerom.vcxproj.filters b/makerom/makerom.vcxproj.filters index 472d9e19..992fd34f 100644 --- a/makerom/makerom.vcxproj.filters +++ b/makerom/makerom.vcxproj.filters @@ -339,6 +339,9 @@ Resource Files\DESC + + Header Files + @@ -479,6 +482,9 @@ Source Files + + Source Files + From 9de65b09efb8c4171b992c6fc4755b7c1ac75ba0 Mon Sep 17 00:00:00 2001 From: profi200 Date: Thu, 7 Jan 2016 16:10:21 +0100 Subject: [PATCH 151/317] Fix gcc compatibility. --- ctrtool/utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/utils.h b/ctrtool/utils.h index 1d5eac83..93bf2193 100644 --- a/ctrtool/utils.h +++ b/ctrtool/utils.h @@ -47,7 +47,7 @@ inline int fseeko64(FILE *__stream, long long __off, int __whence) return _fseeki64(__stream, __off, __whence); } #else -extern int fseeko64 (FILE *__stream, __off64_t __off, int __whence); +extern int fseeko64 (FILE *__stream, off64_t __off, int __whence); #endif #ifdef __cplusplus From b1725fda79b01c449b913147fffc3a043abf23d3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 8 Jan 2016 19:58:31 +0800 Subject: [PATCH 152/317] [makerom] Changed makefile Warning-less compiling on OS X --- makerom/Makefile | 27 ++++++++++++++++++++------- makerom/utils.h | 6 ++---- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/makerom/Makefile b/makerom/Makefile index 19d05055..9af706b3 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -3,14 +3,27 @@ SRC_DIR = . polarssl libyaml OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) # Compiler Settings -LIBS = -static-libgcc -CXXFLAGS = -I. -CFLAGS = --std=c99 -O2 -flto -Wall -Wno-unused-but-set-variable -Wno-unused-value -Wno-unused-result -I. $(MAKEROM_BUILD_FLAGS) +CFLAGS = --std=c99 -O2 -flto -Wall -Wno-unused-value -Wno-unused-result -I. CC = gcc -CXX = g++ +ifeq ($(OS),Windows_NT) + #Windows Build CFG + CFLAGS += -Wno-unused-but-set-variable + LIBS += -static-libgcc +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Darwin) + # OS X + CFLAGS += + LIBS += -liconv + else + # Linux + CFLAGS += -Wno-unused-but-set-variable + LIBS += + endif +endif + # MAKEROM Build Settings -MAKEROM_BUILD_FLAGS = #-DDEBUG OUTPUT = makerom main: build @@ -18,7 +31,7 @@ main: build rebuild: clean build build: $(OBJS) - $(CXX) -o $(OUTPUT) $(LIBS) $(OBJS) + $(CC) -o $(OUTPUT) $(LIBS) $(OBJS) clean: - rm -rf $(OUTPUT) $(OBJS) *.cci *.cia *.cxi *.cfa + rm -rf $(OUTPUT) $(OBJS) \ No newline at end of file diff --git a/makerom/utils.h b/makerom/utils.h index 6370e415..dc8e27c1 100644 --- a/makerom/utils.h +++ b/makerom/utils.h @@ -18,7 +18,7 @@ u64 min64(u64 a, u64 b); u64 max64(u64 a, u64 b); // Strings -void memdump(FILE* fout, const char* prefix, const const u8* data, u32 size); +void memdump(FILE* fout, const char* prefix, const u8* data, u32 size); char* replace_filextention(const char *input, const char *extention); // Base64 @@ -52,6 +52,4 @@ u32 u8_to_u32(const u8 *value, u8 endianness); u64 u8_to_u64(const u8 *value, u8 endianness); int u16_to_u8(u8 *out_value, u16 in_value, u8 endianness); int u32_to_u8(u8 *out_value, u32 in_value, u8 endianness); -int u64_to_u8(u8 *out_value, u64 in_value, u8 endianness); - - +int u64_to_u8(u8 *out_value, u64 in_value, u8 endianness); \ No newline at end of file From 9b4415a7a0c38497f25d2791e5913a9b0dff14b3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 8 Jan 2016 20:45:14 +0800 Subject: [PATCH 153/317] [ctrtool] Fix compile errors And comparisons which would have always returned false. --- ctrtool/Makefile | 21 ++++++++++++++++++--- ctrtool/stream.c | 2 +- ctrtool/utils.h | 6 ++++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 73bd2051..3842b54e 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -3,12 +3,27 @@ SRC_DIR = . polarssl tinyxml OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) # Compiler Settings -LIBS = -static-libgcc -static-libstdc++ -CXXFLAGS = -I. -CFLAGS = -O2 -flto -Wall -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-result -I. OUTPUT = ctrtool +CXXFLAGS = -I. +CFLAGS = -O2 -flto -Wall -Wno-unused-variable -Wno-unused-result -I. CC = gcc CXX = g++ +ifeq ($(OS),Windows_NT) + #Windows Build CFG + CFLAGS += -Wno-unused-but-set-variable + LIBS += -static-libgcc -static-libstdc++ +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Darwin) + # OS X + CFLAGS += -Wno-unused-local-typedef + LIBS += -liconv + else + # Linux + CFLAGS += -Wno-unused-but-set-variable + LIBS += + endif +endif main: $(OBJS) $(CXX) -o $(OUTPUT) $(LIBS) $(OBJS) diff --git a/ctrtool/stream.c b/ctrtool/stream.c index e18dda31..dfb1ac6f 100644 --- a/ctrtool/stream.c +++ b/ctrtool/stream.c @@ -119,7 +119,7 @@ int stream_out_flush(stream_out_context* ctx) if (ctx->outbufferpos > 0) { size_t writtenbytes = fwrite(ctx->outbuffer, 1, ctx->outbufferpos, ctx->outfile); - if (writtenbytes < 0) + if (writtenbytes == 0) // will be zero if nothing is written return 0; diff --git a/ctrtool/utils.h b/ctrtool/utils.h index 93bf2193..990d900b 100644 --- a/ctrtool/utils.h +++ b/ctrtool/utils.h @@ -46,8 +46,10 @@ inline int fseeko64(FILE *__stream, long long __off, int __whence) { return _fseeki64(__stream, __off, __whence); } -#else -extern int fseeko64 (FILE *__stream, off64_t __off, int __whence); +#elif __APPLE__ + #define fseeko64 fseek // OS X file I/O is 64bit +#elif __linux__ + extern int fseeko64 (FILE *__stream, __off64_t __off, int __whence); #endif #ifdef __cplusplus From ed286878bd817eea5d112710d14fb3af99719638 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 8 Jan 2016 20:50:44 +0800 Subject: [PATCH 154/317] [ctrtool] removed silenced warning for hacky code. --- ctrtool/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 3842b54e..0f2361fa 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -16,7 +16,7 @@ else UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Darwin) # OS X - CFLAGS += -Wno-unused-local-typedef + CFLAGS += LIBS += -liconv else # Linux From 8420f9dadd91b753088bd47f3ebd0c2172942abe Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 15 Jan 2016 21:51:31 +0800 Subject: [PATCH 155/317] Update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 21388103..c990f923 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,8 @@ local.properties ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +*.VC.opendb + # User-specific files *.suo *.user From fefa25f5ad051791c25c950aae2133e2f16d3386 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 15 Jan 2016 21:51:59 +0800 Subject: [PATCH 156/317] [makerom] corrected aes keygen code, thanks plutoo. --- makerom/aes_keygen.c | 22 ++++++++++++++++++++++ makerom/aes_keygen.h | 8 ++------ makerom/keyset.c | 11 +---------- makerom/keyset.h | 4 +--- makerom/ncch.c | 5 +++-- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/makerom/aes_keygen.c b/makerom/aes_keygen.c index e5a35d6d..ffaa4d93 100644 --- a/makerom/aes_keygen.c +++ b/makerom/aes_keygen.c @@ -106,6 +106,7 @@ void n128_xor(const uint8_t *a, const uint8_t *b, uint8_t *out) } // keygen algorithm +/* void n_aes_keygen(const uint8_t *x, uint8_t x_shift, const uint8_t *y, uint8_t y_shift, const uint8_t *keygen_constant, uint8_t *key) { // overall algo: @@ -121,4 +122,25 @@ void n_aes_keygen(const uint8_t *x, uint8_t x_shift, const uint8_t *y, uint8_t y // Add secret n128_add(key_xy, keygen_constant, key); +} +*/ + +void ctr_aes_keygen(const uint8_t *x, const uint8_t *y, uint8_t *key) +{ + static const uint8_t KEYGEN_CONST[16] = { 0x1F, 0xF9, 0xE9, 0xAA, 0xC5, 0xFE, 0x04, 0x08, 0x02, 0x45, 0x91, 0xDC, 0x5D, 0x52, 0x76, 0x8A }; + + // overall algo: + // key = (((x <<< 2) ^ y) + KEYGEN_CONST) >>> 41 + uint8_t x_rot[16], key_xy[16], key_xyc[16]; + + // x_rot = x <<< 2 + n128_lrot(x, 2, x_rot); + + // key_xy = x_rot ^ y + n128_xor(x_rot, y, key_xy); + + // key_xyc = key_xy + KEYGEN_CONST + n128_add(key_xy, KEYGEN_CONST, key); + + n128_rrot(key_xyc, 41, key); } \ No newline at end of file diff --git a/makerom/aes_keygen.h b/makerom/aes_keygen.h index 0314744a..dae87a3c 100644 --- a/makerom/aes_keygen.h +++ b/makerom/aes_keygen.h @@ -2,11 +2,7 @@ #include /* - AES Key generator for the Nintendo (Handheld) Consoles - - BYO keygen constants, and input >>> parameters - - key = ((x >>> x_shift) ^ (y >>> y_shift)) + keygen_constant + AES Key generator for the Nintendo 3DS (CTR) Consoles */ -void n_aes_keygen(const uint8_t *x, uint8_t x_shift, const uint8_t *y, uint8_t y_shift, const uint8_t *keygen_constant, uint8_t *key); +void ctr_aes_keygen(const uint8_t *x, const uint8_t *y, uint8_t *key); diff --git a/makerom/keyset.c b/makerom/keyset.c index d4684717..3710d92f 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -48,15 +48,6 @@ void PrintBadKeySize(char *path, u32 size) fprintf(stderr,"[KEYSET ERROR] %s has invalid size (0x%x)\n",path,size); } -u8* AesKeyScrambler(u8 *key, const u8 *keyX, const u8 *keyY) -{ - static const uint8_t CTR_KEYGEN_CONST[16] = { 0xEE, 0x2E, 0xA9, 0x3B, 0x45, 0x0F, 0xFC, 0xF4, 0xD5, 0x62, 0xFF, 0x02, 0x04, 0x01, 0x22, 0xC8 }; - static const uint8_t CTR_KEYX_SHIFT = 39; - static const uint8_t CTR_KEYY_SHIFT = 41; - n_aes_keygen(keyX, CTR_KEYX_SHIFT, keyY, CTR_KEYY_SHIFT, CTR_KEYGEN_CONST, key); - return key; -} - int SetKeys(keys_struct *keys) { int result = 0; @@ -135,7 +126,7 @@ int LoadKeysFromResources(keys_struct *keys) // CIA //for(int i = 0; i < 6; i++){ // keys->aes.commonKey[i] = malloc(16); - // AesKeyScrambler(keys->aes.commonKey[i], ctr_common_etd_keyX_ppki, ctr_common_etd_keyY_ppki[i]); + // ctr_aes_keygen(ctr_common_etd_keyX_ppki, ctr_common_etd_keyY_ppki[i], keys->aes.commonKey[i]); //} if(keys->aes.currentCommonKey > 0xff) SetCurrentCommonKey(keys,0); diff --git a/makerom/keyset.h b/makerom/keyset.h index 1e1db788..4ab18ff9 100644 --- a/makerom/keyset.h +++ b/makerom/keyset.h @@ -96,6 +96,4 @@ void FreeKeys(keys_struct *keys); int SetCommonKey(keys_struct *keys, const u8 *key, u8 Index); int SetCurrentCommonKey(keys_struct *keys, u8 Index); int SetNormalKey(keys_struct *keys, const u8 *key); -int SetSystemFixedKey(keys_struct *keys, const u8 *key); - -u8* AesKeyScrambler(u8 *key, const u8 *keyX, const u8 *keyY); +int SetSystemFixedKey(keys_struct *keys, const u8 *key); \ No newline at end of file diff --git a/makerom/ncch.c b/makerom/ncch.c index 577843b2..e8d6412f 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -1,4 +1,5 @@ #include "lib.h" +#include "aes_keygen.h" #include "ncch_build.h" #include "exheader_build.h" #include "exheader_read.h" @@ -1043,12 +1044,12 @@ bool SetNcchKeys(keys_struct *keys, ncch_hdr *hdr) } if(keys->aes.ncchKeyX[0]) - AesKeyScrambler(keys->aes.ncchKey0,keys->aes.ncchKeyX[0],hdr->signature); + ctr_aes_keygen(keys->aes.ncchKeyX[0],hdr->signature,keys->aes.ncchKey0); else return false; if(keys->aes.ncchKeyX[hdr->flags[ncchflag_CONTENT_KEYX]]) - AesKeyScrambler(keys->aes.ncchKey1,keys->aes.ncchKeyX[hdr->flags[ncchflag_CONTENT_KEYX]],hdr->signature); + ctr_aes_keygen(keys->aes.ncchKeyX[ncchflag_CONTENT_KEYX], hdr->signature, keys->aes.ncchKey0); else return false; From 75a839efffe20ae6053290a5fc4f7de963d93203 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 15 Jan 2016 23:37:03 +0800 Subject: [PATCH 157/317] [makerom] cleaned up RSA keys --- makerom/keyset.c | 24 +- makerom/makerom.vcxproj | 1 + makerom/makerom.vcxproj.filters | 3 + makerom/pki/dev.h | 859 ++------------------------------ makerom/pki/dev_legacy.h | 145 +----- makerom/pki/prod.h | 715 +------------------------- makerom/pki/rsa_key.h | 14 + makerom/pki/test.h | 11 +- 8 files changed, 92 insertions(+), 1680 deletions(-) create mode 100644 makerom/pki/rsa_key.h diff --git a/makerom/keyset.c b/makerom/keyset.c index 3710d92f..c17bf0c6 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -108,12 +108,12 @@ int LoadKeysFromResources(keys_struct *keys) /* RSA Keys */ // CIA - SetTIK_RsaKey(keys, xs9_dpki_rsa_priv, xs9_dpki_rsa_pub); - SetTMD_RsaKey(keys, cpA_dpki_rsa_priv, cpA_dpki_rsa_pub); + SetTIK_RsaKey(keys, xs9_dpki_rsa.priv_exponent, xs9_dpki_rsa.modulus); + SetTMD_RsaKey(keys, cpA_dpki_rsa.priv_exponent, cpA_dpki_rsa.modulus); // CCI/CFA - Set_CCI_CFA_RsaKey(keys, dev_ncsd_cfa_priv, dev_ncsd_cfa_pub); + Set_CCI_CFA_RsaKey(keys, dev_ncsd_cfa_rsa.priv_exponent, dev_ncsd_cfa_rsa.modulus); // CXI - SetAccessDesc_RsaKey(keys, dev_acex_priv, dev_acex_pub); + SetAccessDesc_RsaKey(keys, dev_accessdesc_rsa.priv_exponent, dev_accessdesc_rsa.modulus); /* Certs */ SetCaCert(keys, ca4_dpki_cert); @@ -141,12 +141,12 @@ int LoadKeysFromResources(keys_struct *keys) /* RSA Keys */ // CIA - SetTIK_RsaKey(keys, xsC_ppki_rsa_priv, xsC_ppki_rsa_pub); - SetTMD_RsaKey(keys, cpB_ppki_rsa_priv, cpB_ppki_rsa_pub); + SetTIK_RsaKey(keys, xsC_ppki_rsa.priv_exponent, xsC_ppki_rsa.modulus); + SetTMD_RsaKey(keys, cpB_ppki_rsa.priv_exponent, cpB_ppki_rsa.modulus); // CCI/CFA - Set_CCI_CFA_RsaKey(keys, prod_ncsd_cfa_priv, prod_ncsd_cfa_pub); + Set_CCI_CFA_RsaKey(keys, prod_ncsd_cfa_rsa.priv_exponent, prod_ncsd_cfa_rsa.modulus); // CXI - SetAccessDesc_RsaKey(keys, prod_acex_priv, prod_acex_pub); + SetAccessDesc_RsaKey(keys, prod_accessdesc_rsa.priv_exponent, prod_accessdesc_rsa.modulus); /* Certs */ SetCaCert(keys, ca3_ppki_cert); @@ -159,15 +159,15 @@ int LoadKeysFromResources(keys_struct *keys) void SetDummyRsaData(keys_struct *keys) { if(!keys->rsa.xsPvt || !keys->rsa.xsPub) - SetTIK_RsaKey(keys, tpki_rsa_privExp, tpki_rsa_pubMod); + SetTIK_RsaKey(keys, tpki_rsa.priv_exponent, tpki_rsa.modulus); if(!keys->rsa.cpPvt || !keys->rsa.cpPub) - SetTMD_RsaKey(keys, tpki_rsa_privExp, tpki_rsa_pubMod); + SetTMD_RsaKey(keys, tpki_rsa.priv_exponent, tpki_rsa.modulus); if(!keys->rsa.cciCfaPvt || !keys->rsa.cciCfaPub) - Set_CCI_CFA_RsaKey(keys, tpki_rsa_privExp, tpki_rsa_pubMod); + Set_CCI_CFA_RsaKey(keys, tpki_rsa.priv_exponent, tpki_rsa.modulus); if(!keys->rsa.acexPvt || !keys->rsa.acexPub) - SetAccessDesc_RsaKey(keys, tpki_rsa_privExp, tpki_rsa_pubMod); + SetAccessDesc_RsaKey(keys, tpki_rsa.priv_exponent, tpki_rsa.modulus); /* Certs */ if(!keys->certs.caCert) diff --git a/makerom/makerom.vcxproj b/makerom/makerom.vcxproj index dbdfd0a3..97eedae3 100644 --- a/makerom/makerom.vcxproj +++ b/makerom/makerom.vcxproj @@ -106,6 +106,7 @@ + diff --git a/makerom/makerom.vcxproj.filters b/makerom/makerom.vcxproj.filters index 992fd34f..656358e8 100644 --- a/makerom/makerom.vcxproj.filters +++ b/makerom/makerom.vcxproj.filters @@ -342,6 +342,9 @@ Header Files + + Resource Files\PKI + diff --git a/makerom/pki/dev.h b/makerom/pki/dev.h index db7d9761..6e107355 100644 --- a/makerom/pki/dev.h +++ b/makerom/pki/dev.h @@ -1,4 +1,5 @@ #pragma once +#include "rsa_key.h" #ifdef PKI_LEGACY #include "pki/dev_legacy.h" @@ -24,871 +25,71 @@ static const unsigned char ctr_common_etd_key_dpki[2][16] = }; //RSA Keys -static const unsigned char root_dpki_rsa_pub[0x200] = +static const CtrRsa4096Key root_dpki_rsa = { - 0xD0, 0x1F, 0xE1, 0x00, 0xD4, 0x35, 0x56, 0xB2, - 0x4B, 0x56, 0xDA, 0xE9, 0x71, 0xB5, 0xA5, 0xD3, - 0x84, 0xB9, 0x30, 0x03, 0xBE, 0x1B, 0xBF, 0x28, - 0xA2, 0x30, 0x5B, 0x06, 0x06, 0x45, 0x46, 0x7D, - 0x5B, 0x02, 0x51, 0xD2, 0x56, 0x1A, 0x27, 0x4F, - 0x9E, 0x9F, 0x9C, 0xEC, 0x64, 0x61, 0x50, 0xAB, - 0x3D, 0x2A, 0xE3, 0x36, 0x68, 0x66, 0xAC, 0xA4, - 0xBA, 0xE8, 0x1A, 0xE3, 0xD7, 0x9A, 0xA6, 0xB0, - 0x4A, 0x8B, 0xCB, 0xA7, 0xE6, 0xFB, 0x64, 0x89, - 0x45, 0xEB, 0xDF, 0xDB, 0x85, 0xBA, 0x09, 0x1F, - 0xD7, 0xD1, 0x14, 0xB5, 0xA3, 0xA7, 0x80, 0xE3, - 0xA2, 0x2E, 0x6E, 0xCD, 0x87, 0xB5, 0xA4, 0xC6, - 0xF9, 0x10, 0xE4, 0x03, 0x22, 0x08, 0x81, 0x4B, - 0x0C, 0xEE, 0xA1, 0xA1, 0x7D, 0xF7, 0x39, 0x69, - 0x5F, 0x61, 0x7E, 0xF6, 0x35, 0x28, 0xDB, 0x94, - 0x96, 0x37, 0xA0, 0x56, 0x03, 0x7F, 0x7B, 0x32, - 0x41, 0x38, 0x95, 0xC0, 0xA8, 0xF1, 0x98, 0x2E, - 0x15, 0x65, 0xE3, 0x8E, 0xED, 0xC2, 0x2E, 0x59, - 0x0E, 0xE2, 0x67, 0x7B, 0x86, 0x09, 0xF4, 0x8C, - 0x2E, 0x30, 0x3F, 0xBC, 0x40, 0x5C, 0xAC, 0x18, - 0x04, 0x2F, 0x82, 0x20, 0x84, 0xE4, 0x93, 0x68, - 0x03, 0xDA, 0x7F, 0x41, 0x34, 0x92, 0x48, 0x56, - 0x2B, 0x8E, 0xE1, 0x2F, 0x78, 0xF8, 0x03, 0x24, - 0x63, 0x30, 0xBC, 0x7B, 0xE7, 0xEE, 0x72, 0x4A, - 0xF4, 0x58, 0xA4, 0x72, 0xE7, 0xAB, 0x46, 0xA1, - 0xA7, 0xC1, 0x0C, 0x2F, 0x18, 0xFA, 0x07, 0xC3, - 0xDD, 0xD8, 0x98, 0x06, 0xA1, 0x1C, 0x9C, 0xC1, - 0x30, 0xB2, 0x47, 0xA3, 0x3C, 0x8D, 0x47, 0xDE, - 0x67, 0xF2, 0x9E, 0x55, 0x77, 0xB1, 0x1C, 0x43, - 0x49, 0x3D, 0x5B, 0xBA, 0x76, 0x34, 0xA7, 0xE4, - 0xE7, 0x15, 0x31, 0xB7, 0xDF, 0x59, 0x81, 0xFE, - 0x24, 0xA1, 0x14, 0x55, 0x4C, 0xBD, 0x8F, 0x00, - 0x5C, 0xE1, 0xDB, 0x35, 0x08, 0x5C, 0xCF, 0xC7, - 0x78, 0x06, 0xB6, 0xDE, 0x25, 0x40, 0x68, 0xA2, - 0x6C, 0xB5, 0x49, 0x2D, 0x45, 0x80, 0x43, 0x8F, - 0xE1, 0xE5, 0xA9, 0xED, 0x75, 0xC5, 0xED, 0x45, - 0x1D, 0xCE, 0x78, 0x94, 0x39, 0xCC, 0xC3, 0xBA, - 0x28, 0xA2, 0x31, 0x2A, 0x1B, 0x87, 0x19, 0xEF, - 0x0F, 0x73, 0xB7, 0x13, 0x95, 0x0C, 0x02, 0x59, - 0x1A, 0x74, 0x62, 0xA6, 0x07, 0xF3, 0x7C, 0x0A, - 0xA7, 0xA1, 0x8F, 0xA9, 0x43, 0xA3, 0x6D, 0x75, - 0x2A, 0x5F, 0x41, 0x92, 0xF0, 0x13, 0x61, 0x00, - 0xAA, 0x9C, 0xB4, 0x1B, 0xBE, 0x14, 0xBE, 0xB1, - 0xF9, 0xFC, 0x69, 0x2F, 0xDF, 0xA0, 0x94, 0x46, - 0xDE, 0x5A, 0x9D, 0xDE, 0x2C, 0xA5, 0xF6, 0x8C, - 0x1C, 0x0C, 0x21, 0x42, 0x92, 0x87, 0xCB, 0x2D, - 0xAA, 0xA3, 0xD2, 0x63, 0x75, 0x2F, 0x73, 0xE0, - 0x9F, 0xAF, 0x44, 0x79, 0xD2, 0x81, 0x74, 0x29, - 0xF6, 0x98, 0x00, 0xAF, 0xDE, 0x6B, 0x59, 0x2D, - 0xC1, 0x98, 0x82, 0xBD, 0xF5, 0x81, 0xCC, 0xAB, - 0xF2, 0xCB, 0x91, 0x02, 0x9E, 0xF3, 0x5C, 0x4C, - 0xFD, 0xBB, 0xFF, 0x49, 0xC1, 0xFA, 0x1B, 0x2F, - 0xE3, 0x1D, 0xE7, 0xA5, 0x60, 0xEC, 0xB4, 0x7E, - 0xBC, 0xFE, 0x32, 0x42, 0x5B, 0x95, 0x6F, 0x81, - 0xB6, 0x99, 0x17, 0x48, 0x7E, 0x3B, 0x78, 0x91, - 0x51, 0xDB, 0x2E, 0x78, 0xB1, 0xFD, 0x2E, 0xBE, - 0x7E, 0x62, 0x6B, 0x3E, 0xA1, 0x65, 0xB4, 0xFB, - 0x00, 0xCC, 0xB7, 0x51, 0xAF, 0x50, 0x73, 0x29, - 0xC4, 0xA3, 0x93, 0x9E, 0xA6, 0xDD, 0x9C, 0x50, - 0xA0, 0xE7, 0x38, 0x6B, 0x01, 0x45, 0x79, 0x6B, - 0x41, 0xAF, 0x61, 0xF7, 0x85, 0x55, 0x94, 0x4F, - 0x3B, 0xC2, 0x2D, 0xC3, 0xBD, 0x0D, 0x00, 0xF8, - 0x79, 0x8A, 0x42, 0xB1, 0xAA, 0xA0, 0x83, 0x20, - 0x65, 0x9A, 0xC7, 0x39, 0x5A, 0xB4, 0xF3, 0x29 + .modulus = { 0xD0, 0x1F, 0xE1, 0x00, 0xD4, 0x35, 0x56, 0xB2, 0x4B, 0x56, 0xDA, 0xE9, 0x71, 0xB5, 0xA5, 0xD3, 0x84, 0xB9, 0x30, 0x03, 0xBE, 0x1B, 0xBF, 0x28, 0xA2, 0x30, 0x5B, 0x06, 0x06, 0x45, 0x46, 0x7D, 0x5B, 0x02, 0x51, 0xD2, 0x56, 0x1A, 0x27, 0x4F, 0x9E, 0x9F, 0x9C, 0xEC, 0x64, 0x61, 0x50, 0xAB, 0x3D, 0x2A, 0xE3, 0x36, 0x68, 0x66, 0xAC, 0xA4, 0xBA, 0xE8, 0x1A, 0xE3, 0xD7, 0x9A, 0xA6, 0xB0, 0x4A, 0x8B, 0xCB, 0xA7, 0xE6, 0xFB, 0x64, 0x89, 0x45, 0xEB, 0xDF, 0xDB, 0x85, 0xBA, 0x09, 0x1F, 0xD7, 0xD1, 0x14, 0xB5, 0xA3, 0xA7, 0x80, 0xE3, 0xA2, 0x2E, 0x6E, 0xCD, 0x87, 0xB5, 0xA4, 0xC6, 0xF9, 0x10, 0xE4, 0x03, 0x22, 0x08, 0x81, 0x4B, 0x0C, 0xEE, 0xA1, 0xA1, 0x7D, 0xF7, 0x39, 0x69, 0x5F, 0x61, 0x7E, 0xF6, 0x35, 0x28, 0xDB, 0x94, 0x96, 0x37, 0xA0, 0x56, 0x03, 0x7F, 0x7B, 0x32, 0x41, 0x38, 0x95, 0xC0, 0xA8, 0xF1, 0x98, 0x2E, 0x15, 0x65, 0xE3, 0x8E, 0xED, 0xC2, 0x2E, 0x59, 0x0E, 0xE2, 0x67, 0x7B, 0x86, 0x09, 0xF4, 0x8C, 0x2E, 0x30, 0x3F, 0xBC, 0x40, 0x5C, 0xAC, 0x18, 0x04, 0x2F, 0x82, 0x20, 0x84, 0xE4, 0x93, 0x68, 0x03, 0xDA, 0x7F, 0x41, 0x34, 0x92, 0x48, 0x56, 0x2B, 0x8E, 0xE1, 0x2F, 0x78, 0xF8, 0x03, 0x24, 0x63, 0x30, 0xBC, 0x7B, 0xE7, 0xEE, 0x72, 0x4A, 0xF4, 0x58, 0xA4, 0x72, 0xE7, 0xAB, 0x46, 0xA1, 0xA7, 0xC1, 0x0C, 0x2F, 0x18, 0xFA, 0x07, 0xC3, 0xDD, 0xD8, 0x98, 0x06, 0xA1, 0x1C, 0x9C, 0xC1, 0x30, 0xB2, 0x47, 0xA3, 0x3C, 0x8D, 0x47, 0xDE, 0x67, 0xF2, 0x9E, 0x55, 0x77, 0xB1, 0x1C, 0x43, 0x49, 0x3D, 0x5B, 0xBA, 0x76, 0x34, 0xA7, 0xE4, 0xE7, 0x15, 0x31, 0xB7, 0xDF, 0x59, 0x81, 0xFE, 0x24, 0xA1, 0x14, 0x55, 0x4C, 0xBD, 0x8F, 0x00, 0x5C, 0xE1, 0xDB, 0x35, 0x08, 0x5C, 0xCF, 0xC7, 0x78, 0x06, 0xB6, 0xDE, 0x25, 0x40, 0x68, 0xA2, 0x6C, 0xB5, 0x49, 0x2D, 0x45, 0x80, 0x43, 0x8F, 0xE1, 0xE5, 0xA9, 0xED, 0x75, 0xC5, 0xED, 0x45, 0x1D, 0xCE, 0x78, 0x94, 0x39, 0xCC, 0xC3, 0xBA, 0x28, 0xA2, 0x31, 0x2A, 0x1B, 0x87, 0x19, 0xEF, 0x0F, 0x73, 0xB7, 0x13, 0x95, 0x0C, 0x02, 0x59, 0x1A, 0x74, 0x62, 0xA6, 0x07, 0xF3, 0x7C, 0x0A, 0xA7, 0xA1, 0x8F, 0xA9, 0x43, 0xA3, 0x6D, 0x75, 0x2A, 0x5F, 0x41, 0x92, 0xF0, 0x13, 0x61, 0x00, 0xAA, 0x9C, 0xB4, 0x1B, 0xBE, 0x14, 0xBE, 0xB1, 0xF9, 0xFC, 0x69, 0x2F, 0xDF, 0xA0, 0x94, 0x46, 0xDE, 0x5A, 0x9D, 0xDE, 0x2C, 0xA5, 0xF6, 0x8C, 0x1C, 0x0C, 0x21, 0x42, 0x92, 0x87, 0xCB, 0x2D, 0xAA, 0xA3, 0xD2, 0x63, 0x75, 0x2F, 0x73, 0xE0, 0x9F, 0xAF, 0x44, 0x79, 0xD2, 0x81, 0x74, 0x29, 0xF6, 0x98, 0x00, 0xAF, 0xDE, 0x6B, 0x59, 0x2D, 0xC1, 0x98, 0x82, 0xBD, 0xF5, 0x81, 0xCC, 0xAB, 0xF2, 0xCB, 0x91, 0x02, 0x9E, 0xF3, 0x5C, 0x4C, 0xFD, 0xBB, 0xFF, 0x49, 0xC1, 0xFA, 0x1B, 0x2F, 0xE3, 0x1D, 0xE7, 0xA5, 0x60, 0xEC, 0xB4, 0x7E, 0xBC, 0xFE, 0x32, 0x42, 0x5B, 0x95, 0x6F, 0x81, 0xB6, 0x99, 0x17, 0x48, 0x7E, 0x3B, 0x78, 0x91, 0x51, 0xDB, 0x2E, 0x78, 0xB1, 0xFD, 0x2E, 0xBE, 0x7E, 0x62, 0x6B, 0x3E, 0xA1, 0x65, 0xB4, 0xFB, 0x00, 0xCC, 0xB7, 0x51, 0xAF, 0x50, 0x73, 0x29, 0xC4, 0xA3, 0x93, 0x9E, 0xA6, 0xDD, 0x9C, 0x50, 0xA0, 0xE7, 0x38, 0x6B, 0x01, 0x45, 0x79, 0x6B, 0x41, 0xAF, 0x61, 0xF7, 0x85, 0x55, 0x94, 0x4F, 0x3B, 0xC2, 0x2D, 0xC3, 0xBD, 0x0D, 0x00, 0xF8, 0x79, 0x8A, 0x42, 0xB1, 0xAA, 0xA0, 0x83, 0x20, 0x65, 0x9A, 0xC7, 0x39, 0x5A, 0xB4, 0xF3, 0x29 }, + .priv_exponent = {0} }; -static const unsigned char cpA_dpki_rsa_priv[0x100] = +static const CtrRsa2048Key cpA_dpki_rsa = { - 0x28, 0xCE, 0xDC, 0x39, 0x02, 0x7F, 0x3E, 0x8E, - 0xAA, 0xB7, 0x59, 0x11, 0xE0, 0x68, 0xBF, 0x80, - 0xA6, 0x44, 0x77, 0xDB, 0x1B, 0xA2, 0x50, 0xA3, - 0x69, 0xE5, 0x96, 0xB2, 0xC4, 0xCA, 0x7A, 0x35, - 0x0D, 0xFC, 0x4A, 0xB2, 0xFB, 0xC0, 0x18, 0xA5, - 0x30, 0xB4, 0x9D, 0x10, 0x44, 0xD1, 0xAD, 0x33, - 0xFD, 0x15, 0xA7, 0x8D, 0x0F, 0x17, 0xD5, 0xA4, - 0xF5, 0x5E, 0x7F, 0x33, 0xF6, 0x80, 0x04, 0x27, - 0x6A, 0xEA, 0x9C, 0xEE, 0x68, 0x04, 0x1A, 0xA5, - 0xD4, 0x35, 0xA2, 0x25, 0xA2, 0x31, 0xD9, 0xF2, - 0xF0, 0xAC, 0xDE, 0x69, 0xB6, 0x64, 0x56, 0x75, - 0x2E, 0x9B, 0xEA, 0xDE, 0x2A, 0xBB, 0xD6, 0x00, - 0xAA, 0xE6, 0x9B, 0xC2, 0xF6, 0x9F, 0x60, 0xCD, - 0x0E, 0xFA, 0xB1, 0x14, 0x4A, 0x47, 0xD6, 0x63, - 0x9A, 0xCD, 0x9C, 0x93, 0xB9, 0x09, 0x42, 0xDA, - 0x8F, 0xFB, 0xE5, 0x7B, 0xF1, 0x4F, 0x96, 0x33, - 0xF9, 0x45, 0x5B, 0xCC, 0x84, 0xAB, 0xC2, 0xD8, - 0xC4, 0x0C, 0x85, 0xFA, 0x51, 0x28, 0xB9, 0x97, - 0x95, 0x23, 0x8C, 0xB3, 0x1D, 0x4E, 0xB6, 0x1C, - 0xCC, 0x60, 0x41, 0xFB, 0x26, 0xC7, 0xD6, 0xCB, - 0x77, 0x18, 0xF7, 0xEA, 0xCD, 0x10, 0x3C, 0x5B, - 0xA3, 0xC0, 0x77, 0x4C, 0x11, 0xF3, 0x74, 0x50, - 0xEE, 0x23, 0x80, 0xC4, 0x5D, 0xDD, 0x57, 0xF5, - 0x7D, 0x49, 0x57, 0x4A, 0xBA, 0x62, 0xBF, 0x06, - 0xD9, 0xD1, 0x7F, 0x91, 0x10, 0x89, 0x6F, 0x49, - 0x09, 0xD7, 0xE9, 0xAF, 0x4C, 0x9F, 0x67, 0x9D, - 0x89, 0x82, 0xE4, 0xD5, 0xC1, 0x9A, 0xDC, 0x55, - 0x79, 0xE7, 0xE9, 0x2D, 0x81, 0x42, 0x14, 0x55, - 0x61, 0x47, 0x9B, 0xED, 0x76, 0x92, 0x1D, 0x2F, - 0xB5, 0x7C, 0x28, 0x4B, 0xFF, 0x7B, 0xC2, 0x3B, - 0x36, 0x73, 0x99, 0xA6, 0x21, 0x43, 0x0E, 0xA1, - 0x1F, 0x82, 0xB8, 0x91, 0x71, 0x11, 0xB2, 0xC1 + .modulus = { 0xAA, 0x7F, 0x93, 0x80, 0x28, 0x9B, 0xE8, 0x98, 0x63, 0x10, 0x7A, 0xE1, 0x0C, 0x59, 0x2C, 0x2F, 0x7C, 0xFF, 0xBD, 0xAA, 0xDD, 0x74, 0xF4, 0xA2, 0xFB, 0xAC, 0xD7, 0x6F, 0x00, 0x93, 0x42, 0x06, 0x34, 0x71, 0x56, 0xD8, 0x40, 0x49, 0x72, 0x9F, 0x3E, 0x24, 0xFA, 0x5E, 0x19, 0xD1, 0x5B, 0x63, 0x5C, 0xD2, 0xEF, 0x09, 0xDE, 0x32, 0xEE, 0x6B, 0x6F, 0xC8, 0xFA, 0x32, 0x8E, 0x2E, 0x96, 0xB9, 0x94, 0x41, 0x04, 0x7D, 0x07, 0x62, 0x95, 0xDA, 0x0D, 0x91, 0xD8, 0x09, 0x35, 0xD0, 0xDE, 0x8E, 0x6B, 0xC6, 0xAB, 0x14, 0x27, 0x01, 0x9C, 0xFE, 0x49, 0x96, 0xFC, 0x9B, 0x54, 0x79, 0x4D, 0xEB, 0xD7, 0xC6, 0x66, 0x73, 0xA6, 0xDD, 0x3A, 0x77, 0x65, 0x47, 0x94, 0xEC, 0x1C, 0x87, 0xAA, 0x46, 0xD9, 0x78, 0xA9, 0x7D, 0xDB, 0x11, 0x22, 0x6E, 0xD4, 0x12, 0xC2, 0x78, 0x4B, 0x21, 0x83, 0x92, 0xC7, 0x10, 0xC7, 0x74, 0x19, 0xFF, 0xAA, 0xF6, 0x0B, 0x75, 0xD8, 0x23, 0xDD, 0x33, 0xC3, 0xA1, 0x5B, 0xA7, 0x2D, 0x30, 0xA5, 0xA4, 0xD8, 0xF8, 0x0F, 0xD6, 0x73, 0xFD, 0x26, 0xCB, 0x29, 0xA6, 0xEF, 0x50, 0x39, 0xE2, 0x5F, 0x59, 0x61, 0x84, 0x6B, 0xDA, 0x2E, 0xC7, 0xCB, 0xE4, 0x38, 0x4B, 0x28, 0xFB, 0x0D, 0xD5, 0x8E, 0x7C, 0xAA, 0x7D, 0x4B, 0x37, 0x3A, 0xD7, 0x81, 0xDD, 0x73, 0xE3, 0x09, 0x93, 0xBD, 0xBD, 0x7E, 0x08, 0x55, 0x4A, 0x8C, 0xA5, 0xC9, 0x84, 0x2D, 0x71, 0x01, 0xA2, 0x2A, 0x01, 0xB0, 0x15, 0xFB, 0x30, 0x78, 0xB9, 0x13, 0xF4, 0xC7, 0x3F, 0xB5, 0xA6, 0xF1, 0xA2, 0x5E, 0x22, 0xB0, 0x02, 0xB6, 0xE0, 0x09, 0x54, 0x7F, 0x0F, 0xBD, 0xF0, 0xFE, 0xA5, 0x50, 0x1D, 0x93, 0x15, 0xF9, 0x3D, 0x83, 0x0F, 0x0F, 0x0E, 0x3D, 0xE2, 0x3D, 0x96, 0xE7, 0x09, 0xD9, 0x77 }, + .priv_exponent = { 0x28, 0xCE, 0xDC, 0x39, 0x02, 0x7F, 0x3E, 0x8E, 0xAA, 0xB7, 0x59, 0x11, 0xE0, 0x68, 0xBF, 0x80, 0xA6, 0x44, 0x77, 0xDB, 0x1B, 0xA2, 0x50, 0xA3, 0x69, 0xE5, 0x96, 0xB2, 0xC4, 0xCA, 0x7A, 0x35, 0x0D, 0xFC, 0x4A, 0xB2, 0xFB, 0xC0, 0x18, 0xA5, 0x30, 0xB4, 0x9D, 0x10, 0x44, 0xD1, 0xAD, 0x33, 0xFD, 0x15, 0xA7, 0x8D, 0x0F, 0x17, 0xD5, 0xA4, 0xF5, 0x5E, 0x7F, 0x33, 0xF6, 0x80, 0x04, 0x27, 0x6A, 0xEA, 0x9C, 0xEE, 0x68, 0x04, 0x1A, 0xA5, 0xD4, 0x35, 0xA2, 0x25, 0xA2, 0x31, 0xD9, 0xF2, 0xF0, 0xAC, 0xDE, 0x69, 0xB6, 0x64, 0x56, 0x75, 0x2E, 0x9B, 0xEA, 0xDE, 0x2A, 0xBB, 0xD6, 0x00, 0xAA, 0xE6, 0x9B, 0xC2, 0xF6, 0x9F, 0x60, 0xCD, 0x0E, 0xFA, 0xB1, 0x14, 0x4A, 0x47, 0xD6, 0x63, 0x9A, 0xCD, 0x9C, 0x93, 0xB9, 0x09, 0x42, 0xDA, 0x8F, 0xFB, 0xE5, 0x7B, 0xF1, 0x4F, 0x96, 0x33, 0xF9, 0x45, 0x5B, 0xCC, 0x84, 0xAB, 0xC2, 0xD8, 0xC4, 0x0C, 0x85, 0xFA, 0x51, 0x28, 0xB9, 0x97, 0x95, 0x23, 0x8C, 0xB3, 0x1D, 0x4E, 0xB6, 0x1C, 0xCC, 0x60, 0x41, 0xFB, 0x26, 0xC7, 0xD6, 0xCB, 0x77, 0x18, 0xF7, 0xEA, 0xCD, 0x10, 0x3C, 0x5B, 0xA3, 0xC0, 0x77, 0x4C, 0x11, 0xF3, 0x74, 0x50, 0xEE, 0x23, 0x80, 0xC4, 0x5D, 0xDD, 0x57, 0xF5, 0x7D, 0x49, 0x57, 0x4A, 0xBA, 0x62, 0xBF, 0x06, 0xD9, 0xD1, 0x7F, 0x91, 0x10, 0x89, 0x6F, 0x49, 0x09, 0xD7, 0xE9, 0xAF, 0x4C, 0x9F, 0x67, 0x9D, 0x89, 0x82, 0xE4, 0xD5, 0xC1, 0x9A, 0xDC, 0x55, 0x79, 0xE7, 0xE9, 0x2D, 0x81, 0x42, 0x14, 0x55, 0x61, 0x47, 0x9B, 0xED, 0x76, 0x92, 0x1D, 0x2F, 0xB5, 0x7C, 0x28, 0x4B, 0xFF, 0x7B, 0xC2, 0x3B, 0x36, 0x73, 0x99, 0xA6, 0x21, 0x43, 0x0E, 0xA1, 0x1F, 0x82, 0xB8, 0x91, 0x71, 0x11, 0xB2, 0xC1 } }; -static const unsigned char cpA_dpki_rsa_pub[0x100] = +static const CtrRsa2048Key xs9_dpki_rsa = { - 0xAA, 0x7F, 0x93, 0x80, 0x28, 0x9B, 0xE8, 0x98, - 0x63, 0x10, 0x7A, 0xE1, 0x0C, 0x59, 0x2C, 0x2F, - 0x7C, 0xFF, 0xBD, 0xAA, 0xDD, 0x74, 0xF4, 0xA2, - 0xFB, 0xAC, 0xD7, 0x6F, 0x00, 0x93, 0x42, 0x06, - 0x34, 0x71, 0x56, 0xD8, 0x40, 0x49, 0x72, 0x9F, - 0x3E, 0x24, 0xFA, 0x5E, 0x19, 0xD1, 0x5B, 0x63, - 0x5C, 0xD2, 0xEF, 0x09, 0xDE, 0x32, 0xEE, 0x6B, - 0x6F, 0xC8, 0xFA, 0x32, 0x8E, 0x2E, 0x96, 0xB9, - 0x94, 0x41, 0x04, 0x7D, 0x07, 0x62, 0x95, 0xDA, - 0x0D, 0x91, 0xD8, 0x09, 0x35, 0xD0, 0xDE, 0x8E, - 0x6B, 0xC6, 0xAB, 0x14, 0x27, 0x01, 0x9C, 0xFE, - 0x49, 0x96, 0xFC, 0x9B, 0x54, 0x79, 0x4D, 0xEB, - 0xD7, 0xC6, 0x66, 0x73, 0xA6, 0xDD, 0x3A, 0x77, - 0x65, 0x47, 0x94, 0xEC, 0x1C, 0x87, 0xAA, 0x46, - 0xD9, 0x78, 0xA9, 0x7D, 0xDB, 0x11, 0x22, 0x6E, - 0xD4, 0x12, 0xC2, 0x78, 0x4B, 0x21, 0x83, 0x92, - 0xC7, 0x10, 0xC7, 0x74, 0x19, 0xFF, 0xAA, 0xF6, - 0x0B, 0x75, 0xD8, 0x23, 0xDD, 0x33, 0xC3, 0xA1, - 0x5B, 0xA7, 0x2D, 0x30, 0xA5, 0xA4, 0xD8, 0xF8, - 0x0F, 0xD6, 0x73, 0xFD, 0x26, 0xCB, 0x29, 0xA6, - 0xEF, 0x50, 0x39, 0xE2, 0x5F, 0x59, 0x61, 0x84, - 0x6B, 0xDA, 0x2E, 0xC7, 0xCB, 0xE4, 0x38, 0x4B, - 0x28, 0xFB, 0x0D, 0xD5, 0x8E, 0x7C, 0xAA, 0x7D, - 0x4B, 0x37, 0x3A, 0xD7, 0x81, 0xDD, 0x73, 0xE3, - 0x09, 0x93, 0xBD, 0xBD, 0x7E, 0x08, 0x55, 0x4A, - 0x8C, 0xA5, 0xC9, 0x84, 0x2D, 0x71, 0x01, 0xA2, - 0x2A, 0x01, 0xB0, 0x15, 0xFB, 0x30, 0x78, 0xB9, - 0x13, 0xF4, 0xC7, 0x3F, 0xB5, 0xA6, 0xF1, 0xA2, - 0x5E, 0x22, 0xB0, 0x02, 0xB6, 0xE0, 0x09, 0x54, - 0x7F, 0x0F, 0xBD, 0xF0, 0xFE, 0xA5, 0x50, 0x1D, - 0x93, 0x15, 0xF9, 0x3D, 0x83, 0x0F, 0x0F, 0x0E, - 0x3D, 0xE2, 0x3D, 0x96, 0xE7, 0x09, 0xD9, 0x77 + .modulus = { 0xC0, 0x84, 0x4C, 0xEB, 0x7E, 0xB0, 0xCF, 0xF0, 0xAE, 0xB7, 0x77, 0x69, 0x85, 0x93, 0xE4, 0x99, 0x5A, 0x95, 0x4E, 0x58, 0x17, 0x38, 0xCE, 0xD6, 0x81, 0xB0, 0xBD, 0x77, 0x09, 0xE7, 0xF8, 0x9A, 0xDF, 0xAD, 0x05, 0x48, 0x83, 0xF6, 0xC3, 0xFD, 0xDF, 0x7B, 0x83, 0xE0, 0x0C, 0x26, 0x81, 0x54, 0x43, 0x29, 0xEA, 0x82, 0x6C, 0x89, 0xF0, 0xA6, 0x74, 0x42, 0x86, 0x4D, 0x32, 0x60, 0x32, 0x7D, 0xA7, 0x7A, 0x13, 0x40, 0x66, 0x59, 0xDA, 0x3E, 0x41, 0x6B, 0x27, 0x94, 0x03, 0x4F, 0xAA, 0x22, 0x9D, 0xD5, 0x54, 0x52, 0xDB, 0x27, 0x0A, 0x6A, 0xA2, 0x3D, 0x19, 0xB1, 0x66, 0x1B, 0x19, 0x7D, 0xAB, 0xC7, 0x0E, 0x88, 0x17, 0x91, 0xA1, 0x2A, 0xB4, 0x3C, 0x6C, 0xCB, 0xF5, 0xAA, 0x7C, 0x3A, 0xDD, 0x36, 0xFB, 0x35, 0x71, 0x7B, 0x20, 0x01, 0x59, 0x00, 0xD6, 0xF6, 0x90, 0x39, 0x35, 0x41, 0x31, 0xF8, 0xC1, 0xC0, 0x57, 0x3A, 0x35, 0x18, 0x58, 0x90, 0xB1, 0xAD, 0x9A, 0x0E, 0xEC, 0xE0, 0xF4, 0x7A, 0x7D, 0xA5, 0x27, 0x48, 0xC9, 0x72, 0xAB, 0x0D, 0x08, 0x7B, 0x62, 0x35, 0x40, 0x91, 0x14, 0x2B, 0xB1, 0x1D, 0x1A, 0xFA, 0xF9, 0xCD, 0x5C, 0x17, 0x13, 0x53, 0x52, 0x71, 0xCA, 0xE2, 0x2A, 0x78, 0xB1, 0x7F, 0x4A, 0xCD, 0x59, 0xD8, 0xBA, 0x1D, 0x7D, 0x70, 0x5F, 0x78, 0x1B, 0x9F, 0x9D, 0x37, 0x18, 0x8E, 0xD7, 0xCD, 0x0D, 0x49, 0x57, 0x74, 0x69, 0x88, 0x3A, 0x6B, 0x8E, 0x4E, 0x1B, 0x85, 0xDD, 0xBE, 0x39, 0x45, 0x05, 0x89, 0x56, 0x12, 0x97, 0x59, 0x9A, 0x09, 0xA4, 0xC8, 0x2D, 0x2F, 0xF5, 0xCF, 0xB4, 0x73, 0x70, 0xDB, 0x58, 0x1E, 0xB2, 0x4E, 0x77, 0x6F, 0xA4, 0x7E, 0x62, 0xDF, 0xB7, 0x05, 0xE8, 0x80, 0x42, 0x5C, 0xB8, 0x78, 0x87, 0x97, 0x7F, 0x66, 0x2C, 0x5F }, + .priv_exponent = { 0x74, 0xCB, 0xCF, 0x1E, 0xD0, 0x2D, 0xD4, 0xF9, 0xE0, 0x05, 0xCE, 0x9C, 0x66, 0x3D, 0xE3, 0x62, 0x66, 0x62, 0x4E, 0xB5, 0x82, 0xE1, 0x24, 0x1B, 0x5F, 0x73, 0x2A, 0x7F, 0x1D, 0xB3, 0x6E, 0x50, 0x07, 0x83, 0xA0, 0xC0, 0xED, 0xCE, 0xB7, 0xF9, 0x3D, 0xAC, 0x61, 0xC5, 0x7B, 0x99, 0xA0, 0xBC, 0xCE, 0x42, 0x8F, 0xD3, 0xB0, 0xA5, 0xBF, 0x2A, 0x3D, 0x3E, 0x5E, 0xDC, 0x56, 0xC3, 0xA5, 0xDE, 0x35, 0xCD, 0x0A, 0x00, 0xF8, 0x17, 0x6B, 0x20, 0x79, 0xEF, 0xD8, 0x83, 0x23, 0xBF, 0x21, 0x28, 0xFF, 0x38, 0x7D, 0x80, 0x07, 0x15, 0x18, 0x6C, 0xB9, 0x20, 0xF8, 0x85, 0x77, 0xBC, 0xD9, 0x2A, 0x35, 0x1C, 0xFE, 0xE3, 0xF1, 0xE8, 0x98, 0x2E, 0xA0, 0x4A, 0x48, 0x77, 0x35, 0x03, 0xC9, 0x7A, 0xAC, 0xDA, 0xBE, 0x6D, 0x1D, 0xFB, 0xE4, 0xDE, 0xEC, 0x70, 0x65, 0xFA, 0x10, 0x65, 0xA4, 0xB8, 0x6A, 0xDF, 0x32, 0x6B, 0x8E, 0x28, 0x79, 0x25, 0x87, 0x72, 0xC0, 0x7C, 0x5B, 0x81, 0xBC, 0x81, 0x92, 0x44, 0x7D, 0xEA, 0x61, 0xBD, 0x3C, 0x48, 0xF3, 0x0E, 0x18, 0xDC, 0x8D, 0x89, 0xA0, 0x34, 0xC3, 0xAE, 0x9C, 0x57, 0x72, 0xA6, 0xD7, 0x7C, 0x79, 0xF7, 0xE9, 0x14, 0x6E, 0x15, 0xAC, 0x01, 0xFA, 0xFF, 0xC8, 0xA2, 0x2A, 0x3A, 0xAB, 0x24, 0x3C, 0x7E, 0x2E, 0xC5, 0xDA, 0x83, 0xD5, 0x9D, 0x24, 0x10, 0x83, 0x7A, 0xF4, 0xBB, 0xA3, 0x6F, 0x88, 0xCE, 0xEC, 0x24, 0x1B, 0xF4, 0x36, 0x2E, 0x96, 0xC9, 0x6D, 0x19, 0x02, 0xFE, 0xAA, 0x21, 0x3E, 0x95, 0xA7, 0xFE, 0x83, 0xC8, 0x99, 0x7F, 0xD1, 0xCB, 0x7C, 0x1F, 0x91, 0x30, 0xDB, 0xA4, 0xD3, 0xDD, 0xDA, 0x9B, 0x12, 0x4E, 0x24, 0xD1, 0xA5, 0x6F, 0x15, 0xFC, 0x2C, 0x72, 0x98, 0x2C, 0x89, 0xC5, 0x7D, 0x89, 0xDE, 0x2B, 0x4E, 0x01 } }; -static const unsigned char xs9_dpki_rsa_priv[0x100] = +static const CtrRsa2048Key dev_ncsd_cfa_rsa = { - 0x74, 0xCB, 0xCF, 0x1E, 0xD0, 0x2D, 0xD4, 0xF9, - 0xE0, 0x05, 0xCE, 0x9C, 0x66, 0x3D, 0xE3, 0x62, - 0x66, 0x62, 0x4E, 0xB5, 0x82, 0xE1, 0x24, 0x1B, - 0x5F, 0x73, 0x2A, 0x7F, 0x1D, 0xB3, 0x6E, 0x50, - 0x07, 0x83, 0xA0, 0xC0, 0xED, 0xCE, 0xB7, 0xF9, - 0x3D, 0xAC, 0x61, 0xC5, 0x7B, 0x99, 0xA0, 0xBC, - 0xCE, 0x42, 0x8F, 0xD3, 0xB0, 0xA5, 0xBF, 0x2A, - 0x3D, 0x3E, 0x5E, 0xDC, 0x56, 0xC3, 0xA5, 0xDE, - 0x35, 0xCD, 0x0A, 0x00, 0xF8, 0x17, 0x6B, 0x20, - 0x79, 0xEF, 0xD8, 0x83, 0x23, 0xBF, 0x21, 0x28, - 0xFF, 0x38, 0x7D, 0x80, 0x07, 0x15, 0x18, 0x6C, - 0xB9, 0x20, 0xF8, 0x85, 0x77, 0xBC, 0xD9, 0x2A, - 0x35, 0x1C, 0xFE, 0xE3, 0xF1, 0xE8, 0x98, 0x2E, - 0xA0, 0x4A, 0x48, 0x77, 0x35, 0x03, 0xC9, 0x7A, - 0xAC, 0xDA, 0xBE, 0x6D, 0x1D, 0xFB, 0xE4, 0xDE, - 0xEC, 0x70, 0x65, 0xFA, 0x10, 0x65, 0xA4, 0xB8, - 0x6A, 0xDF, 0x32, 0x6B, 0x8E, 0x28, 0x79, 0x25, - 0x87, 0x72, 0xC0, 0x7C, 0x5B, 0x81, 0xBC, 0x81, - 0x92, 0x44, 0x7D, 0xEA, 0x61, 0xBD, 0x3C, 0x48, - 0xF3, 0x0E, 0x18, 0xDC, 0x8D, 0x89, 0xA0, 0x34, - 0xC3, 0xAE, 0x9C, 0x57, 0x72, 0xA6, 0xD7, 0x7C, - 0x79, 0xF7, 0xE9, 0x14, 0x6E, 0x15, 0xAC, 0x01, - 0xFA, 0xFF, 0xC8, 0xA2, 0x2A, 0x3A, 0xAB, 0x24, - 0x3C, 0x7E, 0x2E, 0xC5, 0xDA, 0x83, 0xD5, 0x9D, - 0x24, 0x10, 0x83, 0x7A, 0xF4, 0xBB, 0xA3, 0x6F, - 0x88, 0xCE, 0xEC, 0x24, 0x1B, 0xF4, 0x36, 0x2E, - 0x96, 0xC9, 0x6D, 0x19, 0x02, 0xFE, 0xAA, 0x21, - 0x3E, 0x95, 0xA7, 0xFE, 0x83, 0xC8, 0x99, 0x7F, - 0xD1, 0xCB, 0x7C, 0x1F, 0x91, 0x30, 0xDB, 0xA4, - 0xD3, 0xDD, 0xDA, 0x9B, 0x12, 0x4E, 0x24, 0xD1, - 0xA5, 0x6F, 0x15, 0xFC, 0x2C, 0x72, 0x98, 0x2C, - 0x89, 0xC5, 0x7D, 0x89, 0xDE, 0x2B, 0x4E, 0x01 + .modulus = { 0xB9, 0x0C, 0xC4, 0xC6, 0x78, 0xF8, 0x6E, 0x30, 0x05, 0x28, 0xC1, 0xCB, 0xD2, 0xCF, 0xA7, 0x80, 0x5C, 0x57, 0x4D, 0x16, 0x9C, 0xAF, 0xA6, 0xCD, 0x01, 0xBB, 0x83, 0x33, 0xAD, 0x03, 0xBB, 0x06, 0x63, 0xD8, 0x17, 0xF5, 0xE3, 0xDF, 0xDA, 0x0D, 0x3B, 0x86, 0x0E, 0xA2, 0x80, 0x47, 0x94, 0x44, 0x6F, 0xD9, 0x97, 0x7E, 0x78, 0x6A, 0xC3, 0x93, 0x93, 0xEF, 0x02, 0xFC, 0x22, 0x9F, 0x80, 0x77, 0x8C, 0x70, 0x92, 0x1C, 0x43, 0xB1, 0x37, 0x4C, 0x76, 0xE0, 0x57, 0x3B, 0xAB, 0x89, 0xFF, 0xEF, 0xE5, 0xBB, 0x3E, 0xAB, 0x91, 0x39, 0xB8, 0xD9, 0x66, 0x0B, 0x64, 0x28, 0x91, 0x92, 0xE9, 0xD0, 0xB3, 0xDF, 0xD1, 0x4B, 0xC1, 0x73, 0xB5, 0x3F, 0x56, 0xA0, 0x40, 0x10, 0xFE, 0x15, 0x2B, 0x1F, 0xA2, 0x7A, 0xDE, 0x31, 0xB0, 0x26, 0x40, 0xC3, 0x57, 0xFD, 0x35, 0xCB, 0xF0, 0xFA, 0xFF, 0xFB, 0x6F, 0xDB, 0xCD, 0x34, 0x1D, 0x51, 0x2D, 0x2D, 0x81, 0x18, 0xFF, 0x0C, 0x08, 0x51, 0xD5, 0xB4, 0x4B, 0x56, 0x16, 0x02, 0x9F, 0x4E, 0x6A, 0xDF, 0x06, 0x6E, 0xCB, 0x72, 0x85, 0xE9, 0x2E, 0x43, 0xA2, 0x08, 0x78, 0x0C, 0x38, 0x9C, 0x19, 0xBD, 0x7B, 0x74, 0x74, 0x68, 0xC4, 0x2D, 0xC1, 0x35, 0x9E, 0x65, 0x3B, 0xD8, 0x99, 0x04, 0x1C, 0x8B, 0x93, 0x8E, 0x7E, 0x92, 0x7C, 0xBB, 0xDD, 0x60, 0xEC, 0xE7, 0xFE, 0x0E, 0x9D, 0x4F, 0x36, 0x46, 0xE6, 0xF1, 0x5C, 0x94, 0x70, 0xEE, 0x67, 0x5F, 0x36, 0x2B, 0x70, 0x44, 0x8D, 0xCA, 0x09, 0xB9, 0x58, 0x67, 0xD2, 0x9F, 0xAD, 0x1F, 0x13, 0x54, 0x74, 0xAD, 0xA6, 0x84, 0x44, 0x28, 0xF3, 0xDE, 0x7E, 0x4C, 0x20, 0x2B, 0xC5, 0xE9, 0x12, 0xE9, 0x5E, 0xFB, 0x8D, 0x77, 0xA9, 0xA4, 0xD2, 0x0D, 0x3C, 0x38, 0x24, 0xBE, 0xF5, 0x8A, 0xB5, 0xF5 }, + .priv_exponent = { 0x32, 0x36, 0x43, 0xC2, 0xB3, 0x1A, 0x7E, 0x13, 0xAB, 0xA2, 0xB6, 0x8B, 0x4F, 0x05, 0xA7, 0xA6, 0xCD, 0xE7, 0xA6, 0x74, 0x47, 0x49, 0xE6, 0x51, 0xE4, 0x71, 0x74, 0x15, 0x76, 0x91, 0xF7, 0x92, 0xB1, 0x4E, 0xF6, 0x99, 0x73, 0x1E, 0xCF, 0xB5, 0x1D, 0x7C, 0xAF, 0xC5, 0xEA, 0x57, 0x01, 0xE5, 0x5C, 0x10, 0x47, 0xEA, 0x3A, 0x54, 0x86, 0x03, 0x2A, 0x76, 0x05, 0x72, 0x53, 0x16, 0xC2, 0xAE, 0x2D, 0xBE, 0x71, 0xF7, 0x17, 0x6B, 0x23, 0xDD, 0x2C, 0xB8, 0x8D, 0x13, 0x14, 0xE5, 0xDA, 0x3B, 0xC7, 0x33, 0x7A, 0xBA, 0xE5, 0x2A, 0x2B, 0x7D, 0x5A, 0x12, 0x27, 0x38, 0x56, 0xDF, 0xED, 0x70, 0x03, 0x0E, 0xED, 0x64, 0xC7, 0xF6, 0x54, 0xAC, 0xFE, 0x1D, 0x77, 0xA4, 0xE4, 0xBC, 0xEB, 0xB9, 0xA6, 0xC5, 0xFE, 0x3A, 0xAF, 0x58, 0x81, 0xE4, 0x3F, 0xA0, 0xE6, 0x93, 0x13, 0x2D, 0x98, 0x7D, 0xB3, 0xE2, 0xC9, 0xC8, 0xD6, 0x31, 0x91, 0x73, 0x9D, 0xCA, 0xC9, 0x44, 0xEF, 0xD0, 0x39, 0xBF, 0x38, 0xFD, 0x1C, 0x91, 0x72, 0x93, 0x40, 0xA9, 0x8A, 0x0D, 0x3E, 0x32, 0xC4, 0x59, 0x4B, 0x0C, 0xC7, 0xEA, 0x50, 0x41, 0x9F, 0xF5, 0xE2, 0xB7, 0x50, 0x7C, 0xE3, 0xC9, 0xEC, 0x46, 0x18, 0xAC, 0xB4, 0x91, 0x2A, 0x32, 0xE0, 0xD8, 0x10, 0x6F, 0xFC, 0x81, 0xB3, 0x95, 0xF3, 0xFC, 0x78, 0xC0, 0xEF, 0xE5, 0x7B, 0x8D, 0x14, 0xD4, 0x36, 0x26, 0x5F, 0xC6, 0x32, 0xC0, 0x19, 0x87, 0x5C, 0x77, 0x26, 0x37, 0xD8, 0xAE, 0x66, 0xD6, 0x0B, 0x28, 0x26, 0x43, 0x7C, 0x25, 0xDB, 0x6D, 0x5C, 0xE8, 0x94, 0x8F, 0xA9, 0x77, 0x07, 0xB2, 0xC0, 0x85, 0xCD, 0x41, 0xBA, 0x48, 0x88, 0x73, 0x34, 0xD5, 0x20, 0x8A, 0x0F, 0xE3, 0x9E, 0x99, 0xF0, 0xC8, 0xE8, 0xD9, 0x2C, 0x2A, 0x21, 0x69, 0xE4, 0xC1 } }; -static const unsigned char xs9_dpki_rsa_pub[0x100] = +static const CtrRsa2048Key dev_accessdesc_rsa = { - 0xC0, 0x84, 0x4C, 0xEB, 0x7E, 0xB0, 0xCF, 0xF0, - 0xAE, 0xB7, 0x77, 0x69, 0x85, 0x93, 0xE4, 0x99, - 0x5A, 0x95, 0x4E, 0x58, 0x17, 0x38, 0xCE, 0xD6, - 0x81, 0xB0, 0xBD, 0x77, 0x09, 0xE7, 0xF8, 0x9A, - 0xDF, 0xAD, 0x05, 0x48, 0x83, 0xF6, 0xC3, 0xFD, - 0xDF, 0x7B, 0x83, 0xE0, 0x0C, 0x26, 0x81, 0x54, - 0x43, 0x29, 0xEA, 0x82, 0x6C, 0x89, 0xF0, 0xA6, - 0x74, 0x42, 0x86, 0x4D, 0x32, 0x60, 0x32, 0x7D, - 0xA7, 0x7A, 0x13, 0x40, 0x66, 0x59, 0xDA, 0x3E, - 0x41, 0x6B, 0x27, 0x94, 0x03, 0x4F, 0xAA, 0x22, - 0x9D, 0xD5, 0x54, 0x52, 0xDB, 0x27, 0x0A, 0x6A, - 0xA2, 0x3D, 0x19, 0xB1, 0x66, 0x1B, 0x19, 0x7D, - 0xAB, 0xC7, 0x0E, 0x88, 0x17, 0x91, 0xA1, 0x2A, - 0xB4, 0x3C, 0x6C, 0xCB, 0xF5, 0xAA, 0x7C, 0x3A, - 0xDD, 0x36, 0xFB, 0x35, 0x71, 0x7B, 0x20, 0x01, - 0x59, 0x00, 0xD6, 0xF6, 0x90, 0x39, 0x35, 0x41, - 0x31, 0xF8, 0xC1, 0xC0, 0x57, 0x3A, 0x35, 0x18, - 0x58, 0x90, 0xB1, 0xAD, 0x9A, 0x0E, 0xEC, 0xE0, - 0xF4, 0x7A, 0x7D, 0xA5, 0x27, 0x48, 0xC9, 0x72, - 0xAB, 0x0D, 0x08, 0x7B, 0x62, 0x35, 0x40, 0x91, - 0x14, 0x2B, 0xB1, 0x1D, 0x1A, 0xFA, 0xF9, 0xCD, - 0x5C, 0x17, 0x13, 0x53, 0x52, 0x71, 0xCA, 0xE2, - 0x2A, 0x78, 0xB1, 0x7F, 0x4A, 0xCD, 0x59, 0xD8, - 0xBA, 0x1D, 0x7D, 0x70, 0x5F, 0x78, 0x1B, 0x9F, - 0x9D, 0x37, 0x18, 0x8E, 0xD7, 0xCD, 0x0D, 0x49, - 0x57, 0x74, 0x69, 0x88, 0x3A, 0x6B, 0x8E, 0x4E, - 0x1B, 0x85, 0xDD, 0xBE, 0x39, 0x45, 0x05, 0x89, - 0x56, 0x12, 0x97, 0x59, 0x9A, 0x09, 0xA4, 0xC8, - 0x2D, 0x2F, 0xF5, 0xCF, 0xB4, 0x73, 0x70, 0xDB, - 0x58, 0x1E, 0xB2, 0x4E, 0x77, 0x6F, 0xA4, 0x7E, - 0x62, 0xDF, 0xB7, 0x05, 0xE8, 0x80, 0x42, 0x5C, - 0xB8, 0x78, 0x87, 0x97, 0x7F, 0x66, 0x2C, 0x5F + .modulus = { 0xF4, 0x3C, 0x45, 0x82, 0xFB, 0xF8, 0x90, 0x5D, 0x07, 0x02, 0x9F, 0x2A, 0x98, 0x8B, 0x63, 0xB7, 0xD3, 0x8F, 0x3C, 0xE2, 0xE0, 0xE0, 0x93, 0xBF, 0xDF, 0x32, 0x43, 0x4D, 0xBE, 0xF4, 0xD1, 0x7A, 0x3A, 0x4E, 0x54, 0x31, 0xD7, 0x73, 0xAE, 0x99, 0x4C, 0xC4, 0x1F, 0x3C, 0x3E, 0xF0, 0x57, 0x05, 0xA3, 0x8A, 0x45, 0x54, 0x60, 0xD8, 0x8F, 0xD9, 0x1D, 0x68, 0x0D, 0x0E, 0x2E, 0xEF, 0xC8, 0xE8, 0x3D, 0xC9, 0x19, 0xF3, 0x73, 0x1E, 0x2D, 0xDA, 0x77, 0x88, 0x3E, 0xCA, 0x5E, 0x25, 0x70, 0x4B, 0xF7, 0x70, 0x95, 0x83, 0x54, 0x24, 0xE0, 0xC3, 0x1A, 0x75, 0xDF, 0x61, 0x3D, 0xD1, 0x42, 0xEC, 0x35, 0x1B, 0x38, 0xD6, 0xC1, 0xF6, 0x7E, 0x18, 0x2A, 0x84, 0x85, 0xDD, 0x57, 0x74, 0x1F, 0x0A, 0x2E, 0xF6, 0xB2, 0x94, 0xA2, 0x3E, 0xE9, 0xA1, 0xD0, 0x09, 0xF7, 0x3A, 0x99, 0x80, 0x05, 0xAF, 0x57, 0x55, 0xEF, 0x52, 0xFA, 0x24, 0x3E, 0x7F, 0xD4, 0x7C, 0x41, 0x44, 0x7B, 0x06, 0x7F, 0xB9, 0x5B, 0x2E, 0x8E, 0x96, 0xAE, 0x46, 0x12, 0x4D, 0x64, 0x21, 0xE5, 0x0F, 0x85, 0xCC, 0xEB, 0x92, 0xE5, 0xF0, 0xF5, 0xA7, 0x42, 0x27, 0x3B, 0xEC, 0xF8, 0xE7, 0x81, 0x75, 0x6F, 0x63, 0x0A, 0x8B, 0x0D, 0x77, 0x38, 0x51, 0xE6, 0x66, 0x33, 0xBA, 0x79, 0xDC, 0x2F, 0x2C, 0x8F, 0xC3, 0x28, 0x06, 0xBB, 0x03, 0x9C, 0xDB, 0xD1, 0x64, 0x0A, 0x66, 0xF0, 0xF8, 0xC1, 0x2A, 0x49, 0x1D, 0x0C, 0x6E, 0x35, 0xBB, 0xEA, 0xB3, 0x5C, 0x0D, 0xE9, 0x95, 0x7C, 0x67, 0xBE, 0x65, 0x77, 0xEC, 0x07, 0xC0, 0x23, 0x05, 0x0A, 0x72, 0x48, 0x86, 0xE9, 0x9E, 0xFC, 0x25, 0x15, 0xE7, 0xC8, 0x21, 0x65, 0xE0, 0x1B, 0xD5, 0xD5, 0x0E, 0xD3, 0x11, 0x54, 0xBB, 0x29, 0x78, 0xBF, 0x2A, 0x3C, 0x3B, 0xB6, 0xB1 }, + .priv_exponent = {0} }; -static const unsigned char dev_ncsd_cfa_priv[0x100] = +static const CtrRsa2048Key dev_crr_rsa = { - 0x32, 0x36, 0x43, 0xC2, 0xB3, 0x1A, 0x7E, 0x13, - 0xAB, 0xA2, 0xB6, 0x8B, 0x4F, 0x05, 0xA7, 0xA6, - 0xCD, 0xE7, 0xA6, 0x74, 0x47, 0x49, 0xE6, 0x51, - 0xE4, 0x71, 0x74, 0x15, 0x76, 0x91, 0xF7, 0x92, - 0xB1, 0x4E, 0xF6, 0x99, 0x73, 0x1E, 0xCF, 0xB5, - 0x1D, 0x7C, 0xAF, 0xC5, 0xEA, 0x57, 0x01, 0xE5, - 0x5C, 0x10, 0x47, 0xEA, 0x3A, 0x54, 0x86, 0x03, - 0x2A, 0x76, 0x05, 0x72, 0x53, 0x16, 0xC2, 0xAE, - 0x2D, 0xBE, 0x71, 0xF7, 0x17, 0x6B, 0x23, 0xDD, - 0x2C, 0xB8, 0x8D, 0x13, 0x14, 0xE5, 0xDA, 0x3B, - 0xC7, 0x33, 0x7A, 0xBA, 0xE5, 0x2A, 0x2B, 0x7D, - 0x5A, 0x12, 0x27, 0x38, 0x56, 0xDF, 0xED, 0x70, - 0x03, 0x0E, 0xED, 0x64, 0xC7, 0xF6, 0x54, 0xAC, - 0xFE, 0x1D, 0x77, 0xA4, 0xE4, 0xBC, 0xEB, 0xB9, - 0xA6, 0xC5, 0xFE, 0x3A, 0xAF, 0x58, 0x81, 0xE4, - 0x3F, 0xA0, 0xE6, 0x93, 0x13, 0x2D, 0x98, 0x7D, - 0xB3, 0xE2, 0xC9, 0xC8, 0xD6, 0x31, 0x91, 0x73, - 0x9D, 0xCA, 0xC9, 0x44, 0xEF, 0xD0, 0x39, 0xBF, - 0x38, 0xFD, 0x1C, 0x91, 0x72, 0x93, 0x40, 0xA9, - 0x8A, 0x0D, 0x3E, 0x32, 0xC4, 0x59, 0x4B, 0x0C, - 0xC7, 0xEA, 0x50, 0x41, 0x9F, 0xF5, 0xE2, 0xB7, - 0x50, 0x7C, 0xE3, 0xC9, 0xEC, 0x46, 0x18, 0xAC, - 0xB4, 0x91, 0x2A, 0x32, 0xE0, 0xD8, 0x10, 0x6F, - 0xFC, 0x81, 0xB3, 0x95, 0xF3, 0xFC, 0x78, 0xC0, - 0xEF, 0xE5, 0x7B, 0x8D, 0x14, 0xD4, 0x36, 0x26, - 0x5F, 0xC6, 0x32, 0xC0, 0x19, 0x87, 0x5C, 0x77, - 0x26, 0x37, 0xD8, 0xAE, 0x66, 0xD6, 0x0B, 0x28, - 0x26, 0x43, 0x7C, 0x25, 0xDB, 0x6D, 0x5C, 0xE8, - 0x94, 0x8F, 0xA9, 0x77, 0x07, 0xB2, 0xC0, 0x85, - 0xCD, 0x41, 0xBA, 0x48, 0x88, 0x73, 0x34, 0xD5, - 0x20, 0x8A, 0x0F, 0xE3, 0x9E, 0x99, 0xF0, 0xC8, - 0xE8, 0xD9, 0x2C, 0x2A, 0x21, 0x69, 0xE4, 0xC1 + .modulus = { 0xC5, 0xF7, 0x09, 0x80, 0x5F, 0xDA, 0xDC, 0xBD, 0x46, 0x07, 0x52, 0xAA, 0x6D, 0xCD, 0x72, 0xB2, 0x50, 0x09, 0x77, 0x47, 0x8A, 0x4F, 0x09, 0x2A, 0xE2, 0x91, 0xB4, 0x5F, 0x04, 0x97, 0x51, 0x75, 0xC9, 0x19, 0x6F, 0x95, 0xBB, 0x23, 0x14, 0x7D, 0x34, 0xDF, 0x77, 0x78, 0x07, 0xE8, 0xD1, 0x10, 0x11, 0xAF, 0xCE, 0x02, 0xB8, 0x73, 0xA9, 0xFB, 0x64, 0xB7, 0x65, 0x87, 0xE5, 0x67, 0xDD, 0x67, 0x37, 0x75, 0xFD, 0x0F, 0xE2, 0x97, 0xBC, 0x79, 0xA8, 0xCC, 0xF9, 0xFA, 0x18, 0xB2, 0x62, 0x4D, 0xF7, 0x53, 0x6B, 0x9C, 0x0E, 0x1A, 0xAB, 0x90, 0xE6, 0x52, 0x86, 0xC8, 0x1C, 0x92, 0x2C, 0x61, 0x53, 0xA9, 0x01, 0xDA, 0x43, 0x93, 0xD0, 0x42, 0x2E, 0xDD, 0xD5, 0x4C, 0x8C, 0x4C, 0xE8, 0x91, 0x56, 0xEC, 0xEE, 0x12, 0x70, 0x0B, 0x64, 0xF0, 0x0A, 0xD6, 0xAF, 0xF8, 0x60, 0xC2, 0xA8, 0x26, 0x7A, 0xC8, 0xBA, 0x55, 0x9A, 0xA1, 0x44, 0xFD, 0x09, 0x47, 0x26, 0xA0, 0xC1, 0x99, 0x9E, 0xF1, 0x24, 0xDF, 0xA3, 0xC2, 0xBB, 0xFF, 0x07, 0x45, 0xD9, 0xD6, 0xA4, 0xC0, 0xFF, 0x6C, 0x6C, 0x78, 0x7B, 0x6D, 0x70, 0x8C, 0x74, 0x44, 0xB0, 0x95, 0xE6, 0xC6, 0x66, 0x5E, 0x7E, 0xBE, 0x71, 0xC5, 0x91, 0x34, 0x73, 0xA7, 0xD4, 0x4C, 0x0D, 0xFC, 0xA9, 0x21, 0x49, 0x94, 0x92, 0xA1, 0x6A, 0x4D, 0x30, 0xA3, 0xD6, 0x9F, 0x6C, 0x60, 0x40, 0x0C, 0xEE, 0xEB, 0xF8, 0x99, 0x22, 0xE1, 0x6F, 0xFC, 0x3C, 0x96, 0x23, 0xAA, 0x11, 0x34, 0x00, 0x4E, 0xFC, 0x2D, 0x60, 0x41, 0x45, 0xE3, 0x5D, 0x78, 0x06, 0xB1, 0xF1, 0xC3, 0x07, 0xB9, 0xD3, 0x47, 0xD0, 0xF1, 0x8C, 0x26, 0x33, 0x1F, 0x6B, 0x46, 0xDD, 0xF3, 0xE3, 0x84, 0x64, 0xA7, 0xF1, 0x53, 0x11, 0xE1, 0x53, 0x4E, 0x99, 0xDB, 0xAC, 0x53 }, + .priv_exponent = {0} }; -static const unsigned char dev_ncsd_cfa_pub[0x100] = +static const unsigned char dev_crr_sign[0x100] = // signature over dev_crr_presigned_rsa.modulus + some other data in CRR header { - 0xB9, 0x0C, 0xC4, 0xC6, 0x78, 0xF8, 0x6E, 0x30, - 0x05, 0x28, 0xC1, 0xCB, 0xD2, 0xCF, 0xA7, 0x80, - 0x5C, 0x57, 0x4D, 0x16, 0x9C, 0xAF, 0xA6, 0xCD, - 0x01, 0xBB, 0x83, 0x33, 0xAD, 0x03, 0xBB, 0x06, - 0x63, 0xD8, 0x17, 0xF5, 0xE3, 0xDF, 0xDA, 0x0D, - 0x3B, 0x86, 0x0E, 0xA2, 0x80, 0x47, 0x94, 0x44, - 0x6F, 0xD9, 0x97, 0x7E, 0x78, 0x6A, 0xC3, 0x93, - 0x93, 0xEF, 0x02, 0xFC, 0x22, 0x9F, 0x80, 0x77, - 0x8C, 0x70, 0x92, 0x1C, 0x43, 0xB1, 0x37, 0x4C, - 0x76, 0xE0, 0x57, 0x3B, 0xAB, 0x89, 0xFF, 0xEF, - 0xE5, 0xBB, 0x3E, 0xAB, 0x91, 0x39, 0xB8, 0xD9, - 0x66, 0x0B, 0x64, 0x28, 0x91, 0x92, 0xE9, 0xD0, - 0xB3, 0xDF, 0xD1, 0x4B, 0xC1, 0x73, 0xB5, 0x3F, - 0x56, 0xA0, 0x40, 0x10, 0xFE, 0x15, 0x2B, 0x1F, - 0xA2, 0x7A, 0xDE, 0x31, 0xB0, 0x26, 0x40, 0xC3, - 0x57, 0xFD, 0x35, 0xCB, 0xF0, 0xFA, 0xFF, 0xFB, - 0x6F, 0xDB, 0xCD, 0x34, 0x1D, 0x51, 0x2D, 0x2D, - 0x81, 0x18, 0xFF, 0x0C, 0x08, 0x51, 0xD5, 0xB4, - 0x4B, 0x56, 0x16, 0x02, 0x9F, 0x4E, 0x6A, 0xDF, - 0x06, 0x6E, 0xCB, 0x72, 0x85, 0xE9, 0x2E, 0x43, - 0xA2, 0x08, 0x78, 0x0C, 0x38, 0x9C, 0x19, 0xBD, - 0x7B, 0x74, 0x74, 0x68, 0xC4, 0x2D, 0xC1, 0x35, - 0x9E, 0x65, 0x3B, 0xD8, 0x99, 0x04, 0x1C, 0x8B, - 0x93, 0x8E, 0x7E, 0x92, 0x7C, 0xBB, 0xDD, 0x60, - 0xEC, 0xE7, 0xFE, 0x0E, 0x9D, 0x4F, 0x36, 0x46, - 0xE6, 0xF1, 0x5C, 0x94, 0x70, 0xEE, 0x67, 0x5F, - 0x36, 0x2B, 0x70, 0x44, 0x8D, 0xCA, 0x09, 0xB9, - 0x58, 0x67, 0xD2, 0x9F, 0xAD, 0x1F, 0x13, 0x54, - 0x74, 0xAD, 0xA6, 0x84, 0x44, 0x28, 0xF3, 0xDE, - 0x7E, 0x4C, 0x20, 0x2B, 0xC5, 0xE9, 0x12, 0xE9, - 0x5E, 0xFB, 0x8D, 0x77, 0xA9, 0xA4, 0xD2, 0x0D, - 0x3C, 0x38, 0x24, 0xBE, 0xF5, 0x8A, 0xB5, 0xF5 + 0x96, 0x5C, 0xBE, 0x5E, 0xEF, 0x08, 0x0B, 0x29, 0xEF, 0x95, 0x12, 0xA4, 0x80, 0x36, 0x47, 0xD5, 0x8F, 0xEC, 0xD0, 0xCB, 0x00, 0x4C, 0x29, 0xDC, 0x5E, 0x87, 0xCF, 0x87, 0x66, 0x4B, 0x94, 0xDE, 0xF0, 0xF6, 0x95, 0x19, 0x13, 0xF4, 0x29, 0xC7, 0x89, 0x55, 0x91, 0x2E, 0x3B, 0x17, 0x1C, 0x86, 0x61, 0x03, 0x03, 0xDC, 0x6A, 0x3E, 0x33, 0xD4, 0x7A, 0xEA, 0xF9, 0x14, 0x82, 0x50, 0xF9, 0x3F, 0xAD, 0xCF, 0x9F, 0xB8, 0xE0, 0xAD, 0xF6, 0x40, 0x0F, 0xE6, 0x41, 0x7D, 0x18, 0xB4, 0xC2, 0x1A, 0x1F, 0xA6, 0x2F, 0x51, 0x40, 0x5F, 0x0A, 0x4B, 0x57, 0x4B, 0x73, 0xC2, 0x6A, 0xB9, 0x73, 0x18, 0x5A, 0x9E, 0xC3, 0x90, 0x7D, 0xCC, 0x5B, 0xEC, 0x46, 0x6D, 0x93, 0x09, 0xB6, 0xF0, 0x84, 0x64, 0x46, 0x12, 0x77, 0x7C, 0x87, 0x25, 0xEB, 0x4C, 0xAE, 0x83, 0xB8, 0x25, 0x6E, 0xF7, 0x7D, 0x45, 0xDF, 0xF6, 0x4A, 0x32, 0x9B, 0x8E, 0x32, 0xC6, 0xCF, 0x63, 0x37, 0x92, 0x7D, 0x01, 0xDD, 0xE9, 0x71, 0x37, 0x0C, 0x2E, 0xB7, 0x83, 0xB7, 0x7C, 0x43, 0x37, 0x20, 0x34, 0xF1, 0xAF, 0x78, 0x4A, 0xB0, 0x84, 0xE0, 0x4C, 0x67, 0x54, 0xA8, 0x75, 0x12, 0xE5, 0x27, 0x38, 0x45, 0xFF, 0x49, 0x9D, 0xF3, 0xFC, 0x3F, 0xCE, 0x73, 0x44, 0xE3, 0x28, 0xD0, 0xD6, 0x00, 0x1F, 0x46, 0xF9, 0x64, 0x3B, 0xDC, 0xEE, 0x30, 0xA8, 0x63, 0x05, 0xB0, 0xF1, 0xA6, 0x4B, 0x21, 0x34, 0x2A, 0xEE, 0xBA, 0x1C, 0x89, 0x0B, 0x80, 0xB8, 0x00, 0xC7, 0x40, 0xCB, 0x66, 0x69, 0x44, 0x27, 0x01, 0xAC, 0x08, 0xC3, 0x97, 0xC5, 0x37, 0xE4, 0x8B, 0xAA, 0x41, 0xB1, 0xEE, 0x48, 0xE0, 0xA7, 0xD6, 0x6F, 0x8A, 0xF5, 0x40, 0x90, 0xD4, 0x2D, 0x81, 0xD4, 0xED, 0x9D, 0xCA, 0x03, 0xFE, 0x35, 0xB3, 0x66, 0xAF, 0x82 }; -static const unsigned char dev_acex_priv[0x100] = +static const CtrRsa2048Key dev_crr_presigned_rsa = // pre signed rsakey { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .modulus = { 0xE2, 0xAD, 0xA6, 0xEA, 0xCA, 0xA3, 0xE8, 0xCC, 0xA9, 0x70, 0x1D, 0x2E, 0x23, 0x4B, 0xC6, 0x55, 0xCE, 0x13, 0xD9, 0xA7, 0x58, 0xB4, 0xC7, 0x73, 0x96, 0x1D, 0xE8, 0xC3, 0x09, 0x4D, 0x9B, 0xC3, 0xEB, 0x69, 0xA2, 0x37, 0x83, 0x5D, 0xD8, 0x37, 0x04, 0x72, 0x7A, 0x4F, 0xEA, 0x53, 0x98, 0x9D, 0x0E, 0x01, 0x34, 0x70, 0x9A, 0x82, 0x06, 0xE7, 0x6A, 0xC9, 0xF8, 0x0E, 0x49, 0x5A, 0xA4, 0xE7, 0x0E, 0xFA, 0xD4, 0xAB, 0x3B, 0xC5, 0xD7, 0xF1, 0xA4, 0xFC, 0x92, 0x7F, 0xD0, 0xF3, 0xCD, 0xD5, 0xB9, 0x2A, 0x1A, 0x41, 0x62, 0xB3, 0x7B, 0x3E, 0x1E, 0x35, 0x46, 0x41, 0x8E, 0xB2, 0x53, 0x1A, 0x60, 0xF8, 0xC2, 0xD1, 0x94, 0xB3, 0x9D, 0x76, 0x9F, 0x1D, 0x98, 0xAC, 0xF0, 0xCF, 0xE3, 0xA9, 0x05, 0x85, 0xF2, 0xBF, 0x55, 0x76, 0x1B, 0x89, 0x1C, 0xC3, 0x19, 0x2E, 0xEE, 0x94, 0xBE, 0x75, 0x0F, 0xA3, 0x76, 0x8B, 0x24, 0x24, 0x97, 0xFA, 0xC0, 0x53, 0x24, 0xF5, 0x85, 0x02, 0x19, 0x9D, 0xC5, 0x11, 0x2E, 0x6B, 0xA3, 0x26, 0xFE, 0xF7, 0x55, 0xD4, 0x23, 0x0A, 0x73, 0x3F, 0x37, 0x53, 0xEA, 0xC2, 0xB7, 0xC1, 0xC9, 0xD8, 0xF3, 0x2F, 0x78, 0x76, 0x4A, 0xA0, 0x69, 0x60, 0x91, 0xC2, 0x7D, 0x11, 0x74, 0xEF, 0x96, 0xD9, 0x74, 0x53, 0xB1, 0x1C, 0xB0, 0xC4, 0x32, 0x16, 0x82, 0x3B, 0xAF, 0x61, 0xB2, 0xDE, 0x38, 0x87, 0x3E, 0x37, 0x4B, 0xA3, 0x95, 0x88, 0x8E, 0xE0, 0x27, 0x9A, 0x1F, 0x7D, 0xB8, 0x23, 0xC3, 0x63, 0xE8, 0x68, 0x51, 0xD9, 0x8C, 0x4C, 0xC2, 0x59, 0x86, 0x49, 0xF7, 0x46, 0x9E, 0x3C, 0xD7, 0x9F, 0x89, 0x23, 0xB4, 0x73, 0x35, 0x2F, 0x18, 0x23, 0x76, 0xA3, 0x9F, 0x40, 0x0B, 0x76, 0x90, 0x85, 0xC8, 0x89, 0xDA, 0x65, 0xE7, 0x6E, 0xEF, 0x2E, 0xF5, 0x67 }, + .priv_exponent = { 0x8D, 0x27, 0x29, 0x6B, 0xC7, 0xA7, 0xED, 0xCD, 0x94, 0x2D, 0x36, 0x5E, 0x86, 0xA8, 0x26, 0xE7, 0x43, 0x9E, 0x64, 0xC8, 0xAA, 0x9A, 0x58, 0x21, 0x07, 0xF7, 0xB3, 0xFB, 0xCF, 0x8D, 0x3E, 0x53, 0xF0, 0x02, 0x25, 0x7B, 0x80, 0x18, 0x2E, 0x0D, 0x84, 0x7D, 0x6C, 0xE0, 0xDA, 0xC0, 0x17, 0xA6, 0xA5, 0x13, 0xE6, 0xFD, 0xBF, 0x98, 0xFC, 0x87, 0x9A, 0x9E, 0x0E, 0x13, 0x87, 0x66, 0x24, 0x8D, 0xA5, 0x6C, 0x58, 0x86, 0x10, 0x80, 0x90, 0x89, 0xEE, 0xFD, 0x40, 0x94, 0xCB, 0x1F, 0x26, 0xAB, 0xD1, 0xD3, 0xFF, 0xE9, 0x7B, 0x76, 0xDC, 0x65, 0xC0, 0x15, 0xD8, 0x9B, 0xF6, 0x29, 0xE1, 0x49, 0xE9, 0xDC, 0xBE, 0x24, 0x17, 0xFF, 0x09, 0x2C, 0xD6, 0xC4, 0x6D, 0x50, 0x33, 0xDC, 0xA0, 0x9D, 0x9D, 0xCC, 0xDD, 0x6E, 0x7B, 0xDF, 0x42, 0x22, 0x4D, 0x80, 0xC7, 0xEB, 0xCB, 0xB1, 0x60, 0x2F, 0x04, 0xEE, 0x04, 0x0E, 0x4C, 0x76, 0x49, 0x55, 0x92, 0xA5, 0xC1, 0x13, 0x60, 0x0A, 0x80, 0x15, 0x3D, 0x1C, 0xC6, 0x46, 0x57, 0x2E, 0x7C, 0xB0, 0x3D, 0x87, 0x06, 0x87, 0xFD, 0x31, 0xF8, 0xE7, 0x14, 0x97, 0x2A, 0x57, 0x40, 0xAC, 0x94, 0x61, 0xCD, 0xCF, 0xDE, 0x8C, 0x40, 0x46, 0x95, 0xA0, 0xD6, 0xF9, 0x2C, 0x08, 0x9B, 0x12, 0xBF, 0xF1, 0x88, 0x9C, 0x5D, 0x40, 0x32, 0x6D, 0x9D, 0x99, 0xA4, 0x80, 0xA6, 0xC2, 0x45, 0x5A, 0xD3, 0x22, 0xFE, 0xFA, 0x17, 0x54, 0x11, 0xEA, 0x41, 0xB4, 0xBD, 0x68, 0x1E, 0xDD, 0x3F, 0xE5, 0x92, 0xCB, 0x9E, 0xF8, 0xC0, 0x0A, 0x8B, 0xF5, 0x89, 0xA4, 0x03, 0x68, 0xF8, 0xF8, 0x99, 0x7C, 0xFE, 0xAD, 0x32, 0xDD, 0x5C, 0xB4, 0x06, 0x29, 0xDA, 0x96, 0x8B, 0xBA, 0xCB, 0x15, 0xDE, 0x38, 0x0D, 0xCA, 0xF7, 0x01, 0x65, 0xF6, 0x2D, 0x36, 0x6E, 0x71 } }; -static const unsigned char dev_acex_pub[0x100] = +static const CtrRsa2048Key dev_firm_rsa = { - 0xF4, 0x3C, 0x45, 0x82, 0xFB, 0xF8, 0x90, 0x5D, - 0x07, 0x02, 0x9F, 0x2A, 0x98, 0x8B, 0x63, 0xB7, - 0xD3, 0x8F, 0x3C, 0xE2, 0xE0, 0xE0, 0x93, 0xBF, - 0xDF, 0x32, 0x43, 0x4D, 0xBE, 0xF4, 0xD1, 0x7A, - 0x3A, 0x4E, 0x54, 0x31, 0xD7, 0x73, 0xAE, 0x99, - 0x4C, 0xC4, 0x1F, 0x3C, 0x3E, 0xF0, 0x57, 0x05, - 0xA3, 0x8A, 0x45, 0x54, 0x60, 0xD8, 0x8F, 0xD9, - 0x1D, 0x68, 0x0D, 0x0E, 0x2E, 0xEF, 0xC8, 0xE8, - 0x3D, 0xC9, 0x19, 0xF3, 0x73, 0x1E, 0x2D, 0xDA, - 0x77, 0x88, 0x3E, 0xCA, 0x5E, 0x25, 0x70, 0x4B, - 0xF7, 0x70, 0x95, 0x83, 0x54, 0x24, 0xE0, 0xC3, - 0x1A, 0x75, 0xDF, 0x61, 0x3D, 0xD1, 0x42, 0xEC, - 0x35, 0x1B, 0x38, 0xD6, 0xC1, 0xF6, 0x7E, 0x18, - 0x2A, 0x84, 0x85, 0xDD, 0x57, 0x74, 0x1F, 0x0A, - 0x2E, 0xF6, 0xB2, 0x94, 0xA2, 0x3E, 0xE9, 0xA1, - 0xD0, 0x09, 0xF7, 0x3A, 0x99, 0x80, 0x05, 0xAF, - 0x57, 0x55, 0xEF, 0x52, 0xFA, 0x24, 0x3E, 0x7F, - 0xD4, 0x7C, 0x41, 0x44, 0x7B, 0x06, 0x7F, 0xB9, - 0x5B, 0x2E, 0x8E, 0x96, 0xAE, 0x46, 0x12, 0x4D, - 0x64, 0x21, 0xE5, 0x0F, 0x85, 0xCC, 0xEB, 0x92, - 0xE5, 0xF0, 0xF5, 0xA7, 0x42, 0x27, 0x3B, 0xEC, - 0xF8, 0xE7, 0x81, 0x75, 0x6F, 0x63, 0x0A, 0x8B, - 0x0D, 0x77, 0x38, 0x51, 0xE6, 0x66, 0x33, 0xBA, - 0x79, 0xDC, 0x2F, 0x2C, 0x8F, 0xC3, 0x28, 0x06, - 0xBB, 0x03, 0x9C, 0xDB, 0xD1, 0x64, 0x0A, 0x66, - 0xF0, 0xF8, 0xC1, 0x2A, 0x49, 0x1D, 0x0C, 0x6E, - 0x35, 0xBB, 0xEA, 0xB3, 0x5C, 0x0D, 0xE9, 0x95, - 0x7C, 0x67, 0xBE, 0x65, 0x77, 0xEC, 0x07, 0xC0, - 0x23, 0x05, 0x0A, 0x72, 0x48, 0x86, 0xE9, 0x9E, - 0xFC, 0x25, 0x15, 0xE7, 0xC8, 0x21, 0x65, 0xE0, - 0x1B, 0xD5, 0xD5, 0x0E, 0xD3, 0x11, 0x54, 0xBB, - 0x29, 0x78, 0xBF, 0x2A, 0x3C, 0x3B, 0xB6, 0xB1 -}; - -static const unsigned char dev_crr_cert_pub[0x100] = // key to verify dev_crr_cert -{ - 0xC5, 0xF7, 0x09, 0x80, 0x5F, 0xDA, 0xDC, 0xBD, - 0x46, 0x07, 0x52, 0xAA, 0x6D, 0xCD, 0x72, 0xB2, - 0x50, 0x09, 0x77, 0x47, 0x8A, 0x4F, 0x09, 0x2A, - 0xE2, 0x91, 0xB4, 0x5F, 0x04, 0x97, 0x51, 0x75, - 0xC9, 0x19, 0x6F, 0x95, 0xBB, 0x23, 0x14, 0x7D, - 0x34, 0xDF, 0x77, 0x78, 0x07, 0xE8, 0xD1, 0x10, - 0x11, 0xAF, 0xCE, 0x02, 0xB8, 0x73, 0xA9, 0xFB, - 0x64, 0xB7, 0x65, 0x87, 0xE5, 0x67, 0xDD, 0x67, - 0x37, 0x75, 0xFD, 0x0F, 0xE2, 0x97, 0xBC, 0x79, - 0xA8, 0xCC, 0xF9, 0xFA, 0x18, 0xB2, 0x62, 0x4D, - 0xF7, 0x53, 0x6B, 0x9C, 0x0E, 0x1A, 0xAB, 0x90, - 0xE6, 0x52, 0x86, 0xC8, 0x1C, 0x92, 0x2C, 0x61, - 0x53, 0xA9, 0x01, 0xDA, 0x43, 0x93, 0xD0, 0x42, - 0x2E, 0xDD, 0xD5, 0x4C, 0x8C, 0x4C, 0xE8, 0x91, - 0x56, 0xEC, 0xEE, 0x12, 0x70, 0x0B, 0x64, 0xF0, - 0x0A, 0xD6, 0xAF, 0xF8, 0x60, 0xC2, 0xA8, 0x26, - 0x7A, 0xC8, 0xBA, 0x55, 0x9A, 0xA1, 0x44, 0xFD, - 0x09, 0x47, 0x26, 0xA0, 0xC1, 0x99, 0x9E, 0xF1, - 0x24, 0xDF, 0xA3, 0xC2, 0xBB, 0xFF, 0x07, 0x45, - 0xD9, 0xD6, 0xA4, 0xC0, 0xFF, 0x6C, 0x6C, 0x78, - 0x7B, 0x6D, 0x70, 0x8C, 0x74, 0x44, 0xB0, 0x95, - 0xE6, 0xC6, 0x66, 0x5E, 0x7E, 0xBE, 0x71, 0xC5, - 0x91, 0x34, 0x73, 0xA7, 0xD4, 0x4C, 0x0D, 0xFC, - 0xA9, 0x21, 0x49, 0x94, 0x92, 0xA1, 0x6A, 0x4D, - 0x30, 0xA3, 0xD6, 0x9F, 0x6C, 0x60, 0x40, 0x0C, - 0xEE, 0xEB, 0xF8, 0x99, 0x22, 0xE1, 0x6F, 0xFC, - 0x3C, 0x96, 0x23, 0xAA, 0x11, 0x34, 0x00, 0x4E, - 0xFC, 0x2D, 0x60, 0x41, 0x45, 0xE3, 0x5D, 0x78, - 0x06, 0xB1, 0xF1, 0xC3, 0x07, 0xB9, 0xD3, 0x47, - 0xD0, 0xF1, 0x8C, 0x26, 0x33, 0x1F, 0x6B, 0x46, - 0xDD, 0xF3, 0xE3, 0x84, 0x64, 0xA7, 0xF1, 0x53, - 0x11, 0xE1, 0x53, 0x4E, 0x99, 0xDB, 0xAC, 0x53 -}; - -static const unsigned char dev_crr_cert[0x100] = // signature over crr rsa key -{ - 0x96, 0x5C, 0xBE, 0x5E, 0xEF, 0x08, 0x0B, 0x29, - 0xEF, 0x95, 0x12, 0xA4, 0x80, 0x36, 0x47, 0xD5, - 0x8F, 0xEC, 0xD0, 0xCB, 0x00, 0x4C, 0x29, 0xDC, - 0x5E, 0x87, 0xCF, 0x87, 0x66, 0x4B, 0x94, 0xDE, - 0xF0, 0xF6, 0x95, 0x19, 0x13, 0xF4, 0x29, 0xC7, - 0x89, 0x55, 0x91, 0x2E, 0x3B, 0x17, 0x1C, 0x86, - 0x61, 0x03, 0x03, 0xDC, 0x6A, 0x3E, 0x33, 0xD4, - 0x7A, 0xEA, 0xF9, 0x14, 0x82, 0x50, 0xF9, 0x3F, - 0xAD, 0xCF, 0x9F, 0xB8, 0xE0, 0xAD, 0xF6, 0x40, - 0x0F, 0xE6, 0x41, 0x7D, 0x18, 0xB4, 0xC2, 0x1A, - 0x1F, 0xA6, 0x2F, 0x51, 0x40, 0x5F, 0x0A, 0x4B, - 0x57, 0x4B, 0x73, 0xC2, 0x6A, 0xB9, 0x73, 0x18, - 0x5A, 0x9E, 0xC3, 0x90, 0x7D, 0xCC, 0x5B, 0xEC, - 0x46, 0x6D, 0x93, 0x09, 0xB6, 0xF0, 0x84, 0x64, - 0x46, 0x12, 0x77, 0x7C, 0x87, 0x25, 0xEB, 0x4C, - 0xAE, 0x83, 0xB8, 0x25, 0x6E, 0xF7, 0x7D, 0x45, - 0xDF, 0xF6, 0x4A, 0x32, 0x9B, 0x8E, 0x32, 0xC6, - 0xCF, 0x63, 0x37, 0x92, 0x7D, 0x01, 0xDD, 0xE9, - 0x71, 0x37, 0x0C, 0x2E, 0xB7, 0x83, 0xB7, 0x7C, - 0x43, 0x37, 0x20, 0x34, 0xF1, 0xAF, 0x78, 0x4A, - 0xB0, 0x84, 0xE0, 0x4C, 0x67, 0x54, 0xA8, 0x75, - 0x12, 0xE5, 0x27, 0x38, 0x45, 0xFF, 0x49, 0x9D, - 0xF3, 0xFC, 0x3F, 0xCE, 0x73, 0x44, 0xE3, 0x28, - 0xD0, 0xD6, 0x00, 0x1F, 0x46, 0xF9, 0x64, 0x3B, - 0xDC, 0xEE, 0x30, 0xA8, 0x63, 0x05, 0xB0, 0xF1, - 0xA6, 0x4B, 0x21, 0x34, 0x2A, 0xEE, 0xBA, 0x1C, - 0x89, 0x0B, 0x80, 0xB8, 0x00, 0xC7, 0x40, 0xCB, - 0x66, 0x69, 0x44, 0x27, 0x01, 0xAC, 0x08, 0xC3, - 0x97, 0xC5, 0x37, 0xE4, 0x8B, 0xAA, 0x41, 0xB1, - 0xEE, 0x48, 0xE0, 0xA7, 0xD6, 0x6F, 0x8A, 0xF5, - 0x40, 0x90, 0xD4, 0x2D, 0x81, 0xD4, 0xED, 0x9D, - 0xCA, 0x03, 0xFE, 0x35, 0xB3, 0x66, 0xAF, 0x82 -}; - -static const unsigned char dev_crr_priv[0x100] = // pre signed rsakey -{ - 0x8D, 0x27, 0x29, 0x6B, 0xC7, 0xA7, 0xED, 0xCD, - 0x94, 0x2D, 0x36, 0x5E, 0x86, 0xA8, 0x26, 0xE7, - 0x43, 0x9E, 0x64, 0xC8, 0xAA, 0x9A, 0x58, 0x21, - 0x07, 0xF7, 0xB3, 0xFB, 0xCF, 0x8D, 0x3E, 0x53, - 0xF0, 0x02, 0x25, 0x7B, 0x80, 0x18, 0x2E, 0x0D, - 0x84, 0x7D, 0x6C, 0xE0, 0xDA, 0xC0, 0x17, 0xA6, - 0xA5, 0x13, 0xE6, 0xFD, 0xBF, 0x98, 0xFC, 0x87, - 0x9A, 0x9E, 0x0E, 0x13, 0x87, 0x66, 0x24, 0x8D, - 0xA5, 0x6C, 0x58, 0x86, 0x10, 0x80, 0x90, 0x89, - 0xEE, 0xFD, 0x40, 0x94, 0xCB, 0x1F, 0x26, 0xAB, - 0xD1, 0xD3, 0xFF, 0xE9, 0x7B, 0x76, 0xDC, 0x65, - 0xC0, 0x15, 0xD8, 0x9B, 0xF6, 0x29, 0xE1, 0x49, - 0xE9, 0xDC, 0xBE, 0x24, 0x17, 0xFF, 0x09, 0x2C, - 0xD6, 0xC4, 0x6D, 0x50, 0x33, 0xDC, 0xA0, 0x9D, - 0x9D, 0xCC, 0xDD, 0x6E, 0x7B, 0xDF, 0x42, 0x22, - 0x4D, 0x80, 0xC7, 0xEB, 0xCB, 0xB1, 0x60, 0x2F, - 0x04, 0xEE, 0x04, 0x0E, 0x4C, 0x76, 0x49, 0x55, - 0x92, 0xA5, 0xC1, 0x13, 0x60, 0x0A, 0x80, 0x15, - 0x3D, 0x1C, 0xC6, 0x46, 0x57, 0x2E, 0x7C, 0xB0, - 0x3D, 0x87, 0x06, 0x87, 0xFD, 0x31, 0xF8, 0xE7, - 0x14, 0x97, 0x2A, 0x57, 0x40, 0xAC, 0x94, 0x61, - 0xCD, 0xCF, 0xDE, 0x8C, 0x40, 0x46, 0x95, 0xA0, - 0xD6, 0xF9, 0x2C, 0x08, 0x9B, 0x12, 0xBF, 0xF1, - 0x88, 0x9C, 0x5D, 0x40, 0x32, 0x6D, 0x9D, 0x99, - 0xA4, 0x80, 0xA6, 0xC2, 0x45, 0x5A, 0xD3, 0x22, - 0xFE, 0xFA, 0x17, 0x54, 0x11, 0xEA, 0x41, 0xB4, - 0xBD, 0x68, 0x1E, 0xDD, 0x3F, 0xE5, 0x92, 0xCB, - 0x9E, 0xF8, 0xC0, 0x0A, 0x8B, 0xF5, 0x89, 0xA4, - 0x03, 0x68, 0xF8, 0xF8, 0x99, 0x7C, 0xFE, 0xAD, - 0x32, 0xDD, 0x5C, 0xB4, 0x06, 0x29, 0xDA, 0x96, - 0x8B, 0xBA, 0xCB, 0x15, 0xDE, 0x38, 0x0D, 0xCA, - 0xF7, 0x01, 0x65, 0xF6, 0x2D, 0x36, 0x6E, 0x71 -}; - -static const unsigned char dev_crr_pub[0x100] = // pre signed rsakey -{ - 0xE2, 0xAD, 0xA6, 0xEA, 0xCA, 0xA3, 0xE8, 0xCC, - 0xA9, 0x70, 0x1D, 0x2E, 0x23, 0x4B, 0xC6, 0x55, - 0xCE, 0x13, 0xD9, 0xA7, 0x58, 0xB4, 0xC7, 0x73, - 0x96, 0x1D, 0xE8, 0xC3, 0x09, 0x4D, 0x9B, 0xC3, - 0xEB, 0x69, 0xA2, 0x37, 0x83, 0x5D, 0xD8, 0x37, - 0x04, 0x72, 0x7A, 0x4F, 0xEA, 0x53, 0x98, 0x9D, - 0x0E, 0x01, 0x34, 0x70, 0x9A, 0x82, 0x06, 0xE7, - 0x6A, 0xC9, 0xF8, 0x0E, 0x49, 0x5A, 0xA4, 0xE7, - 0x0E, 0xFA, 0xD4, 0xAB, 0x3B, 0xC5, 0xD7, 0xF1, - 0xA4, 0xFC, 0x92, 0x7F, 0xD0, 0xF3, 0xCD, 0xD5, - 0xB9, 0x2A, 0x1A, 0x41, 0x62, 0xB3, 0x7B, 0x3E, - 0x1E, 0x35, 0x46, 0x41, 0x8E, 0xB2, 0x53, 0x1A, - 0x60, 0xF8, 0xC2, 0xD1, 0x94, 0xB3, 0x9D, 0x76, - 0x9F, 0x1D, 0x98, 0xAC, 0xF0, 0xCF, 0xE3, 0xA9, - 0x05, 0x85, 0xF2, 0xBF, 0x55, 0x76, 0x1B, 0x89, - 0x1C, 0xC3, 0x19, 0x2E, 0xEE, 0x94, 0xBE, 0x75, - 0x0F, 0xA3, 0x76, 0x8B, 0x24, 0x24, 0x97, 0xFA, - 0xC0, 0x53, 0x24, 0xF5, 0x85, 0x02, 0x19, 0x9D, - 0xC5, 0x11, 0x2E, 0x6B, 0xA3, 0x26, 0xFE, 0xF7, - 0x55, 0xD4, 0x23, 0x0A, 0x73, 0x3F, 0x37, 0x53, - 0xEA, 0xC2, 0xB7, 0xC1, 0xC9, 0xD8, 0xF3, 0x2F, - 0x78, 0x76, 0x4A, 0xA0, 0x69, 0x60, 0x91, 0xC2, - 0x7D, 0x11, 0x74, 0xEF, 0x96, 0xD9, 0x74, 0x53, - 0xB1, 0x1C, 0xB0, 0xC4, 0x32, 0x16, 0x82, 0x3B, - 0xAF, 0x61, 0xB2, 0xDE, 0x38, 0x87, 0x3E, 0x37, - 0x4B, 0xA3, 0x95, 0x88, 0x8E, 0xE0, 0x27, 0x9A, - 0x1F, 0x7D, 0xB8, 0x23, 0xC3, 0x63, 0xE8, 0x68, - 0x51, 0xD9, 0x8C, 0x4C, 0xC2, 0x59, 0x86, 0x49, - 0xF7, 0x46, 0x9E, 0x3C, 0xD7, 0x9F, 0x89, 0x23, - 0xB4, 0x73, 0x35, 0x2F, 0x18, 0x23, 0x76, 0xA3, - 0x9F, 0x40, 0x0B, 0x76, 0x90, 0x85, 0xC8, 0x89, - 0xDA, 0x65, 0xE7, 0x6E, 0xEF, 0x2E, 0xF5, 0x67 -}; - -static const unsigned char dev_firm_pub[0x100] = -{ - 0xC1, 0x1E, 0x84, 0xE4, 0xDA, 0xD7, 0xED, 0xC8, - 0xA5, 0xD9, 0xCB, 0x0B, 0xE9, 0x3B, 0x9D, 0xBC, - 0x15, 0x69, 0xF7, 0x95, 0xF6, 0x8F, 0xB7, 0x91, - 0x24, 0x19, 0xBE, 0x8F, 0xFE, 0xBB, 0xC1, 0x1D, - 0x09, 0xF0, 0xE3, 0x78, 0xA2, 0x0C, 0xC0, 0x0B, - 0x8E, 0xCD, 0xBA, 0x0E, 0x69, 0x4C, 0x61, 0x4A, - 0xBD, 0x13, 0xEA, 0xE2, 0x23, 0xE4, 0x0B, 0x62, - 0x1F, 0x9B, 0x63, 0x1B, 0x31, 0x34, 0xB7, 0x12, - 0x73, 0x64, 0x6B, 0x4E, 0x60, 0x41, 0x0A, 0x3D, - 0x54, 0x86, 0xFA, 0x49, 0xEC, 0x2D, 0x77, 0xAD, - 0x1D, 0xBC, 0x48, 0xE7, 0xFB, 0x07, 0xEF, 0x48, - 0xC3, 0xBE, 0xC3, 0xD6, 0xE1, 0x5B, 0x3D, 0xCD, - 0x5E, 0x6B, 0x46, 0x08, 0x6A, 0x3A, 0x0E, 0xAE, - 0x25, 0x49, 0x44, 0xA5, 0x40, 0x0D, 0x94, 0x53, - 0x18, 0xA4, 0x8F, 0x57, 0x8E, 0xF4, 0xCD, 0xB4, - 0xD3, 0x4E, 0xE7, 0x15, 0x9B, 0x11, 0x95, 0x85, - 0x00, 0xC4, 0x92, 0x94, 0xB8, 0xBE, 0xFE, 0xCB, - 0xB7, 0x50, 0xA0, 0xBE, 0x8A, 0xF5, 0xFD, 0xC8, - 0x0E, 0x4B, 0xA9, 0x41, 0x13, 0x62, 0x48, 0xCD, - 0x1A, 0xF3, 0xC8, 0x6B, 0xA5, 0xA0, 0xDF, 0xF1, - 0x9D, 0x3D, 0xFD, 0xE4, 0xD1, 0x7C, 0x1F, 0x00, - 0x0D, 0x99, 0x72, 0x6B, 0xA3, 0x05, 0x7F, 0x86, - 0x38, 0xBE, 0x4D, 0x5E, 0xAB, 0x93, 0xDF, 0xF3, - 0xEE, 0xA1, 0x9F, 0x22, 0x50, 0xA8, 0x7F, 0x31, - 0xAA, 0x2B, 0x03, 0x71, 0x9B, 0x14, 0xC4, 0x30, - 0x88, 0x42, 0x6F, 0xD5, 0x2C, 0x7B, 0x03, 0x64, - 0x3B, 0x14, 0xEC, 0x46, 0x33, 0xCC, 0x2C, 0x95, - 0x9F, 0x5C, 0x7B, 0x83, 0xC5, 0x67, 0xDD, 0x7C, - 0xA1, 0x2D, 0xD6, 0xEC, 0x17, 0x0B, 0x23, 0xC7, - 0xB1, 0x9A, 0x72, 0xBA, 0x78, 0xCB, 0x39, 0x68, - 0x5C, 0xB6, 0xB4, 0x61, 0xE1, 0x98, 0xCF, 0x1D, - 0x69, 0xF9, 0xD7, 0xB0, 0xA0, 0x5E, 0x9C, 0xEB + .modulus = {0xC1, 0x1E, 0x84, 0xE4, 0xDA, 0xD7, 0xED, 0xC8, 0xA5, 0xD9, 0xCB, 0x0B, 0xE9, 0x3B, 0x9D, 0xBC, 0x15, 0x69, 0xF7, 0x95, 0xF6, 0x8F, 0xB7, 0x91, 0x24, 0x19, 0xBE, 0x8F, 0xFE, 0xBB, 0xC1, 0x1D, 0x09, 0xF0, 0xE3, 0x78, 0xA2, 0x0C, 0xC0, 0x0B, 0x8E, 0xCD, 0xBA, 0x0E, 0x69, 0x4C, 0x61, 0x4A, 0xBD, 0x13, 0xEA, 0xE2, 0x23, 0xE4, 0x0B, 0x62, 0x1F, 0x9B, 0x63, 0x1B, 0x31, 0x34, 0xB7, 0x12, 0x73, 0x64, 0x6B, 0x4E, 0x60, 0x41, 0x0A, 0x3D, 0x54, 0x86, 0xFA, 0x49, 0xEC, 0x2D, 0x77, 0xAD, 0x1D, 0xBC, 0x48, 0xE7, 0xFB, 0x07, 0xEF, 0x48, 0xC3, 0xBE, 0xC3, 0xD6, 0xE1, 0x5B, 0x3D, 0xCD, 0x5E, 0x6B, 0x46, 0x08, 0x6A, 0x3A, 0x0E, 0xAE, 0x25, 0x49, 0x44, 0xA5, 0x40, 0x0D, 0x94, 0x53, 0x18, 0xA4, 0x8F, 0x57, 0x8E, 0xF4, 0xCD, 0xB4, 0xD3, 0x4E, 0xE7, 0x15, 0x9B, 0x11, 0x95, 0x85, 0x00, 0xC4, 0x92, 0x94, 0xB8, 0xBE, 0xFE, 0xCB, 0xB7, 0x50, 0xA0, 0xBE, 0x8A, 0xF5, 0xFD, 0xC8, 0x0E, 0x4B, 0xA9, 0x41, 0x13, 0x62, 0x48, 0xCD, 0x1A, 0xF3, 0xC8, 0x6B, 0xA5, 0xA0, 0xDF, 0xF1, 0x9D, 0x3D, 0xFD, 0xE4, 0xD1, 0x7C, 0x1F, 0x00, 0x0D, 0x99, 0x72, 0x6B, 0xA3, 0x05, 0x7F, 0x86, 0x38, 0xBE, 0x4D, 0x5E, 0xAB, 0x93, 0xDF, 0xF3, 0xEE, 0xA1, 0x9F, 0x22, 0x50, 0xA8, 0x7F, 0x31, 0xAA, 0x2B, 0x03, 0x71, 0x9B, 0x14, 0xC4, 0x30, 0x88, 0x42, 0x6F, 0xD5, 0x2C, 0x7B, 0x03, 0x64, 0x3B, 0x14, 0xEC, 0x46, 0x33, 0xCC, 0x2C, 0x95, 0x9F, 0x5C, 0x7B, 0x83, 0xC5, 0x67, 0xDD, 0x7C, 0xA1, 0x2D, 0xD6, 0xEC, 0x17, 0x0B, 0x23, 0xC7, 0xB1, 0x9A, 0x72, 0xBA, 0x78, 0xCB, 0x39, 0x68, 0x5C, 0xB6, 0xB4, 0x61, 0xE1, 0x98, 0xCF, 0x1D, 0x69, 0xF9, 0xD7, 0xB0, 0xA0, 0x5E, 0x9C, 0xEB }, + .priv_exponent = {0} }; //Certificates static const unsigned char ca4_dpki_cert[0x400] = { - 0x00, 0x01, 0x00, 0x03, 0x19, 0x49, 0x42, 0x9D, - 0x1E, 0x58, 0xA6, 0x2E, 0x7E, 0x8B, 0x56, 0xD1, - 0xB7, 0x6A, 0xE3, 0x02, 0xFD, 0x8B, 0x97, 0x49, - 0x1F, 0x77, 0x87, 0x45, 0xF7, 0x53, 0x88, 0xC4, - 0xDD, 0x0B, 0xEB, 0x1D, 0xF1, 0x22, 0xFB, 0x96, - 0x42, 0x15, 0x14, 0x97, 0x76, 0x4A, 0x53, 0xCF, - 0x78, 0x15, 0x18, 0x45, 0xE4, 0x2C, 0xA8, 0xFD, - 0xE4, 0x86, 0xFD, 0x2A, 0x4F, 0x53, 0xF8, 0xA1, - 0xBA, 0x00, 0x8A, 0x74, 0x85, 0xFF, 0x73, 0xB3, - 0xBF, 0x7E, 0x3C, 0x98, 0x07, 0x29, 0xD0, 0x65, - 0x6B, 0x69, 0x32, 0x19, 0xAD, 0xE8, 0x35, 0xEB, - 0x5F, 0xFF, 0xFC, 0xCB, 0x7C, 0xBB, 0x5E, 0x30, - 0x7F, 0xE0, 0x68, 0x8B, 0x88, 0x8E, 0xF2, 0xD2, - 0x05, 0x3F, 0xB7, 0xE7, 0x91, 0xE9, 0x85, 0xFD, - 0x15, 0xEF, 0x10, 0xD7, 0x9C, 0xCA, 0x88, 0xD6, - 0xBB, 0x15, 0xE8, 0xE4, 0x71, 0x4A, 0x98, 0xEE, - 0x09, 0xBF, 0x7B, 0x8A, 0xF0, 0x53, 0x23, 0x2B, - 0x64, 0x50, 0xE6, 0xD5, 0xFD, 0xFF, 0xC2, 0x0A, - 0x6D, 0x1E, 0xA6, 0xA2, 0x38, 0x12, 0xE1, 0x01, - 0x45, 0x25, 0xD5, 0x6D, 0x40, 0x82, 0x70, 0x3B, - 0x86, 0x98, 0x69, 0x59, 0xA7, 0x3C, 0xD1, 0xA1, - 0x43, 0x64, 0xD2, 0xC2, 0xDA, 0xEA, 0x96, 0xB0, - 0x95, 0xF7, 0x6C, 0x46, 0xE4, 0xFF, 0x41, 0x55, - 0x46, 0x5E, 0x70, 0xEF, 0x1E, 0xD3, 0x10, 0x53, - 0xD9, 0x70, 0x11, 0xE0, 0x10, 0xCC, 0x93, 0xE7, - 0x91, 0x40, 0x13, 0x68, 0x7F, 0xA3, 0xA8, 0x02, - 0x99, 0x6D, 0x1E, 0x55, 0x7B, 0x1C, 0xCC, 0x7A, - 0x7E, 0x8F, 0x58, 0x65, 0xC1, 0x74, 0x2E, 0x28, - 0xE2, 0x6D, 0xEF, 0x38, 0xA9, 0x3A, 0xB5, 0xD8, - 0x2D, 0x43, 0xEC, 0xCC, 0xBF, 0x0B, 0xEF, 0x22, - 0xE1, 0xFD, 0x57, 0xE2, 0x86, 0x43, 0x33, 0x58, - 0x2F, 0xED, 0xEA, 0xBC, 0x01, 0x2F, 0x98, 0x6D, - 0xDF, 0xC3, 0xE9, 0x44, 0x79, 0x73, 0x47, 0x03, - 0x08, 0x45, 0x5B, 0xDC, 0x57, 0xAA, 0x17, 0x0B, - 0x84, 0x42, 0x7F, 0x73, 0xA2, 0x9B, 0x48, 0xF6, - 0xDA, 0x13, 0x5F, 0x66, 0xC7, 0x45, 0xC1, 0x42, - 0xA8, 0x4A, 0xFB, 0x0E, 0x6A, 0x5E, 0xED, 0x85, - 0xD7, 0xB9, 0x71, 0x99, 0x36, 0xF8, 0xCE, 0x2B, - 0x62, 0x1F, 0x39, 0x5F, 0x40, 0xDC, 0x03, 0xBE, - 0xF8, 0x85, 0x4C, 0x11, 0x17, 0xFF, 0x0C, 0x12, - 0x86, 0x41, 0xCC, 0x78, 0x43, 0xB9, 0x7B, 0x43, - 0x46, 0xDB, 0x22, 0x6F, 0x60, 0x26, 0xAC, 0xB5, - 0x6C, 0x27, 0x8B, 0x8E, 0x0E, 0xA7, 0x9A, 0x2D, - 0x65, 0xEF, 0x79, 0x8E, 0x10, 0x78, 0xAD, 0x80, - 0xED, 0x4B, 0x96, 0x04, 0xD2, 0xF0, 0x8B, 0x2C, - 0xD6, 0x4A, 0x23, 0xA3, 0xDB, 0x27, 0x08, 0x33, - 0xB4, 0x02, 0xF8, 0x08, 0x51, 0xF3, 0x5B, 0xED, - 0x3E, 0xE4, 0x57, 0x7C, 0x66, 0x60, 0xFB, 0xF1, - 0x6D, 0x94, 0x13, 0xE0, 0x9C, 0x91, 0x7A, 0x49, - 0xD4, 0x2C, 0x6D, 0xA3, 0x75, 0xBC, 0x27, 0xF0, - 0x23, 0x0D, 0xB9, 0x8F, 0x89, 0x73, 0xAB, 0x02, - 0x7B, 0x52, 0x2C, 0xD5, 0x7E, 0xC0, 0x3D, 0x25, - 0xE8, 0xB3, 0xFC, 0x34, 0x94, 0xC9, 0x7F, 0xB1, - 0x08, 0xFE, 0x18, 0xC6, 0x8A, 0x43, 0x36, 0xE4, - 0x6C, 0x26, 0xB6, 0xF2, 0x80, 0xD2, 0x7E, 0x34, - 0xBE, 0x28, 0x7C, 0x3E, 0x46, 0x87, 0xBC, 0x9D, - 0x77, 0x6B, 0x76, 0xD9, 0x28, 0xD1, 0xB6, 0x35, - 0x2E, 0xC0, 0x34, 0x7D, 0x72, 0x94, 0xAA, 0x93, - 0x60, 0x26, 0x8D, 0x26, 0xF5, 0xF6, 0x52, 0x06, - 0x4A, 0xF2, 0x40, 0xD7, 0xD0, 0x0C, 0x7C, 0x5E, - 0xA3, 0xC3, 0x2D, 0xE6, 0x2D, 0x9B, 0x5C, 0x4B, - 0x4C, 0xAB, 0x6F, 0xD7, 0xBD, 0x37, 0x1D, 0x57, - 0xC2, 0x16, 0x60, 0x95, 0x91, 0x0E, 0x4A, 0xD8, - 0xE9, 0xED, 0x18, 0x1E, 0xF7, 0x61, 0x93, 0x61, - 0x53, 0x89, 0x2D, 0x77, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x52, 0x6F, 0x6F, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x43, 0x41, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x81, 0x12, 0x2A, 0x46, - 0xC9, 0xCC, 0x2D, 0xC4, 0xDF, 0x29, 0x30, 0xE4, - 0xDF, 0x3F, 0x8C, 0x70, 0xA0, 0x78, 0x94, 0x87, - 0x75, 0xAD, 0x5E, 0x9A, 0xA6, 0x04, 0xC5, 0xB4, - 0xD8, 0xEA, 0xFF, 0x2A, 0xA1, 0xD2, 0x14, 0x67, - 0x65, 0x64, 0xEF, 0xCA, 0x28, 0xCC, 0x00, 0x15, - 0x45, 0x54, 0xA1, 0xA3, 0xEA, 0x13, 0x79, 0xE9, - 0xE6, 0xCA, 0xAC, 0xED, 0x15, 0x93, 0xFE, 0x88, - 0xD8, 0x9A, 0xC6, 0xB8, 0xAC, 0xCC, 0xAB, 0x6E, - 0x20, 0x7C, 0xEB, 0x7C, 0xCA, 0x29, 0x80, 0x9E, - 0x29, 0x80, 0x44, 0x06, 0x62, 0xB7, 0xD4, 0x38, - 0x2A, 0x15, 0xDA, 0x43, 0x08, 0x57, 0x45, 0xA9, - 0xAA, 0xE5, 0x9A, 0xA0, 0x5B, 0xDB, 0x32, 0xF6, - 0x68, 0x69, 0xA2, 0xDD, 0x42, 0x95, 0x38, 0x6C, - 0x87, 0xEC, 0xDD, 0x35, 0x08, 0xA2, 0xCF, 0x60, - 0xD0, 0x1E, 0x23, 0xEC, 0x2F, 0xE6, 0x98, 0xF4, - 0x70, 0xD6, 0x00, 0x15, 0x49, 0xA2, 0xF0, 0x67, - 0x59, 0x13, 0x1E, 0x53, 0x4C, 0x70, 0x06, 0x05, - 0x7D, 0xEF, 0x1D, 0x18, 0xA8, 0x3F, 0x0A, 0xC7, - 0x9C, 0xFE, 0x80, 0xFF, 0x5A, 0x91, 0xF2, 0xBE, - 0xD4, 0xA0, 0x83, 0x70, 0x61, 0x19, 0x0A, 0x03, - 0x29, 0x90, 0x21, 0x65, 0x40, 0x3C, 0x9A, 0x90, - 0x8F, 0xB6, 0x15, 0x73, 0x9F, 0x3C, 0xE3, 0x3B, - 0xF1, 0xBA, 0xEA, 0x16, 0xC2, 0x5B, 0xCE, 0xD7, - 0x96, 0x3F, 0xAC, 0xC9, 0xD2, 0x4D, 0x9C, 0x0A, - 0xD7, 0x6F, 0xC0, 0x20, 0xB2, 0xC4, 0xB8, 0x4C, - 0x10, 0xA7, 0x41, 0xA2, 0xCC, 0x7D, 0x9B, 0xAC, - 0x3A, 0xAC, 0xCC, 0xA3, 0x52, 0x9B, 0xAC, 0x31, - 0x6A, 0x9A, 0xA7, 0x5D, 0x2A, 0x26, 0xC7, 0xD7, - 0xD2, 0x88, 0xCB, 0xA4, 0x66, 0xC5, 0xFE, 0x5F, - 0x45, 0x4A, 0xE6, 0x79, 0x74, 0x4A, 0x90, 0xA1, - 0x57, 0x72, 0xDB, 0x3B, 0x0E, 0x47, 0xA4, 0x9A, - 0xF0, 0x31, 0xD1, 0x6D, 0xBE, 0xAB, 0x33, 0x2B, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00, 0x01, 0x00, 0x03, 0x19, 0x49, 0x42, 0x9D, 0x1E, 0x58, 0xA6, 0x2E, 0x7E, 0x8B, 0x56, 0xD1, 0xB7, 0x6A, 0xE3, 0x02, 0xFD, 0x8B, 0x97, 0x49, 0x1F, 0x77, 0x87, 0x45, 0xF7, 0x53, 0x88, 0xC4, 0xDD, 0x0B, 0xEB, 0x1D, 0xF1, 0x22, 0xFB, 0x96, 0x42, 0x15, 0x14, 0x97, 0x76, 0x4A, 0x53, 0xCF, 0x78, 0x15, 0x18, 0x45, 0xE4, 0x2C, 0xA8, 0xFD, 0xE4, 0x86, 0xFD, 0x2A, 0x4F, 0x53, 0xF8, 0xA1, 0xBA, 0x00, 0x8A, 0x74, 0x85, 0xFF, 0x73, 0xB3, 0xBF, 0x7E, 0x3C, 0x98, 0x07, 0x29, 0xD0, 0x65, 0x6B, 0x69, 0x32, 0x19, 0xAD, 0xE8, 0x35, 0xEB, 0x5F, 0xFF, 0xFC, 0xCB, 0x7C, 0xBB, 0x5E, 0x30, 0x7F, 0xE0, 0x68, 0x8B, 0x88, 0x8E, 0xF2, 0xD2, 0x05, 0x3F, 0xB7, 0xE7, 0x91, 0xE9, 0x85, 0xFD, 0x15, 0xEF, 0x10, 0xD7, 0x9C, 0xCA, 0x88, 0xD6, 0xBB, 0x15, 0xE8, 0xE4, 0x71, 0x4A, 0x98, 0xEE, 0x09, 0xBF, 0x7B, 0x8A, 0xF0, 0x53, 0x23, 0x2B, 0x64, 0x50, 0xE6, 0xD5, 0xFD, 0xFF, 0xC2, 0x0A, 0x6D, 0x1E, 0xA6, 0xA2, 0x38, 0x12, 0xE1, 0x01, 0x45, 0x25, 0xD5, 0x6D, 0x40, 0x82, 0x70, 0x3B, 0x86, 0x98, 0x69, 0x59, 0xA7, 0x3C, 0xD1, 0xA1, 0x43, 0x64, 0xD2, 0xC2, 0xDA, 0xEA, 0x96, 0xB0, 0x95, 0xF7, 0x6C, 0x46, 0xE4, 0xFF, 0x41, 0x55, 0x46, 0x5E, 0x70, 0xEF, 0x1E, 0xD3, 0x10, 0x53, 0xD9, 0x70, 0x11, 0xE0, 0x10, 0xCC, 0x93, 0xE7, 0x91, 0x40, 0x13, 0x68, 0x7F, 0xA3, 0xA8, 0x02, 0x99, 0x6D, 0x1E, 0x55, 0x7B, 0x1C, 0xCC, 0x7A, 0x7E, 0x8F, 0x58, 0x65, 0xC1, 0x74, 0x2E, 0x28, 0xE2, 0x6D, 0xEF, 0x38, 0xA9, 0x3A, 0xB5, 0xD8, 0x2D, 0x43, 0xEC, 0xCC, 0xBF, 0x0B, 0xEF, 0x22, 0xE1, 0xFD, 0x57, 0xE2, 0x86, 0x43, 0x33, 0x58, 0x2F, 0xED, 0xEA, 0xBC, 0x01, 0x2F, 0x98, 0x6D, 0xDF, 0xC3, 0xE9, 0x44, 0x79, 0x73, 0x47, 0x03, 0x08, 0x45, 0x5B, 0xDC, 0x57, 0xAA, 0x17, 0x0B, 0x84, 0x42, 0x7F, 0x73, 0xA2, 0x9B, 0x48, 0xF6, 0xDA, 0x13, 0x5F, 0x66, 0xC7, 0x45, 0xC1, 0x42, 0xA8, 0x4A, 0xFB, 0x0E, 0x6A, 0x5E, 0xED, 0x85, 0xD7, 0xB9, 0x71, 0x99, 0x36, 0xF8, 0xCE, 0x2B, 0x62, 0x1F, 0x39, 0x5F, 0x40, 0xDC, 0x03, 0xBE, 0xF8, 0x85, 0x4C, 0x11, 0x17, 0xFF, 0x0C, 0x12, 0x86, 0x41, 0xCC, 0x78, 0x43, 0xB9, 0x7B, 0x43, 0x46, 0xDB, 0x22, 0x6F, 0x60, 0x26, 0xAC, 0xB5, 0x6C, 0x27, 0x8B, 0x8E, 0x0E, 0xA7, 0x9A, 0x2D, 0x65, 0xEF, 0x79, 0x8E, 0x10, 0x78, 0xAD, 0x80, 0xED, 0x4B, 0x96, 0x04, 0xD2, 0xF0, 0x8B, 0x2C, 0xD6, 0x4A, 0x23, 0xA3, 0xDB, 0x27, 0x08, 0x33, 0xB4, 0x02, 0xF8, 0x08, 0x51, 0xF3, 0x5B, 0xED, 0x3E, 0xE4, 0x57, 0x7C, 0x66, 0x60, 0xFB, 0xF1, 0x6D, 0x94, 0x13, 0xE0, 0x9C, 0x91, 0x7A, 0x49, 0xD4, 0x2C, 0x6D, 0xA3, 0x75, 0xBC, 0x27, 0xF0, 0x23, 0x0D, 0xB9, 0x8F, 0x89, 0x73, 0xAB, 0x02, 0x7B, 0x52, 0x2C, 0xD5, 0x7E, 0xC0, 0x3D, 0x25, 0xE8, 0xB3, 0xFC, 0x34, 0x94, 0xC9, 0x7F, 0xB1, 0x08, 0xFE, 0x18, 0xC6, 0x8A, 0x43, 0x36, 0xE4, 0x6C, 0x26, 0xB6, 0xF2, 0x80, 0xD2, 0x7E, 0x34, 0xBE, 0x28, 0x7C, 0x3E, 0x46, 0x87, 0xBC, 0x9D, 0x77, 0x6B, 0x76, 0xD9, 0x28, 0xD1, 0xB6, 0x35, 0x2E, 0xC0, 0x34, 0x7D, 0x72, 0x94, 0xAA, 0x93, 0x60, 0x26, 0x8D, 0x26, 0xF5, 0xF6, 0x52, 0x06, 0x4A, 0xF2, 0x40, 0xD7, 0xD0, 0x0C, 0x7C, 0x5E, 0xA3, 0xC3, 0x2D, 0xE6, 0x2D, 0x9B, 0x5C, 0x4B, 0x4C, 0xAB, 0x6F, 0xD7, 0xBD, 0x37, 0x1D, 0x57, 0xC2, 0x16, 0x60, 0x95, 0x91, 0x0E, 0x4A, 0xD8, 0xE9, 0xED, 0x18, 0x1E, 0xF7, 0x61, 0x93, 0x61, 0x53, 0x89, 0x2D, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x6F, 0x6F, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x12, 0x2A, 0x46, 0xC9, 0xCC, 0x2D, 0xC4, 0xDF, 0x29, 0x30, 0xE4, 0xDF, 0x3F, 0x8C, 0x70, 0xA0, 0x78, 0x94, 0x87, 0x75, 0xAD, 0x5E, 0x9A, 0xA6, 0x04, 0xC5, 0xB4, 0xD8, 0xEA, 0xFF, 0x2A, 0xA1, 0xD2, 0x14, 0x67, 0x65, 0x64, 0xEF, 0xCA, 0x28, 0xCC, 0x00, 0x15, 0x45, 0x54, 0xA1, 0xA3, 0xEA, 0x13, 0x79, 0xE9, 0xE6, 0xCA, 0xAC, 0xED, 0x15, 0x93, 0xFE, 0x88, 0xD8, 0x9A, 0xC6, 0xB8, 0xAC, 0xCC, 0xAB, 0x6E, 0x20, 0x7C, 0xEB, 0x7C, 0xCA, 0x29, 0x80, 0x9E, 0x29, 0x80, 0x44, 0x06, 0x62, 0xB7, 0xD4, 0x38, 0x2A, 0x15, 0xDA, 0x43, 0x08, 0x57, 0x45, 0xA9, 0xAA, 0xE5, 0x9A, 0xA0, 0x5B, 0xDB, 0x32, 0xF6, 0x68, 0x69, 0xA2, 0xDD, 0x42, 0x95, 0x38, 0x6C, 0x87, 0xEC, 0xDD, 0x35, 0x08, 0xA2, 0xCF, 0x60, 0xD0, 0x1E, 0x23, 0xEC, 0x2F, 0xE6, 0x98, 0xF4, 0x70, 0xD6, 0x00, 0x15, 0x49, 0xA2, 0xF0, 0x67, 0x59, 0x13, 0x1E, 0x53, 0x4C, 0x70, 0x06, 0x05, 0x7D, 0xEF, 0x1D, 0x18, 0xA8, 0x3F, 0x0A, 0xC7, 0x9C, 0xFE, 0x80, 0xFF, 0x5A, 0x91, 0xF2, 0xBE, 0xD4, 0xA0, 0x83, 0x70, 0x61, 0x19, 0x0A, 0x03, 0x29, 0x90, 0x21, 0x65, 0x40, 0x3C, 0x9A, 0x90, 0x8F, 0xB6, 0x15, 0x73, 0x9F, 0x3C, 0xE3, 0x3B, 0xF1, 0xBA, 0xEA, 0x16, 0xC2, 0x5B, 0xCE, 0xD7, 0x96, 0x3F, 0xAC, 0xC9, 0xD2, 0x4D, 0x9C, 0x0A, 0xD7, 0x6F, 0xC0, 0x20, 0xB2, 0xC4, 0xB8, 0x4C, 0x10, 0xA7, 0x41, 0xA2, 0xCC, 0x7D, 0x9B, 0xAC, 0x3A, 0xAC, 0xCC, 0xA3, 0x52, 0x9B, 0xAC, 0x31, 0x6A, 0x9A, 0xA7, 0x5D, 0x2A, 0x26, 0xC7, 0xD7, 0xD2, 0x88, 0xCB, 0xA4, 0x66, 0xC5, 0xFE, 0x5F, 0x45, 0x4A, 0xE6, 0x79, 0x74, 0x4A, 0x90, 0xA1, 0x57, 0x72, 0xDB, 0x3B, 0x0E, 0x47, 0xA4, 0x9A, 0xF0, 0x31, 0xD1, 0x6D, 0xBE, 0xAB, 0x33, 0x2B, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char xs9_dpki_cert[0x300] = { - 0x00, 0x01, 0x00, 0x04, 0x63, 0x80, 0x5A, 0x35, - 0x1A, 0x43, 0x7B, 0xA2, 0x43, 0x19, 0xBB, 0x3A, - 0x77, 0x7B, 0x7A, 0xF3, 0x5E, 0x72, 0x4B, 0x15, - 0x0A, 0x06, 0x39, 0x6C, 0x5F, 0xEC, 0x38, 0x45, - 0xB1, 0x88, 0x76, 0x26, 0x8D, 0x5E, 0xDA, 0xE6, - 0x2F, 0x14, 0xBA, 0x02, 0xFA, 0xD6, 0xFC, 0x3B, - 0x2B, 0xBE, 0x87, 0x07, 0x63, 0x8E, 0x55, 0xBF, - 0x05, 0x5A, 0xFC, 0xFC, 0xB3, 0x47, 0x69, 0x11, - 0x89, 0xDB, 0x1C, 0xAF, 0x4B, 0x43, 0x76, 0x62, - 0x3E, 0x30, 0x89, 0x0A, 0x9D, 0x3B, 0xBB, 0x3E, - 0x50, 0xBD, 0xF7, 0xA6, 0xC0, 0xF7, 0xF8, 0xBB, - 0x0D, 0xB5, 0x6A, 0xBB, 0xC6, 0xC3, 0x50, 0xC8, - 0x88, 0xBB, 0x9D, 0xF0, 0x9B, 0xD1, 0x30, 0x64, - 0x60, 0x69, 0xDD, 0x34, 0x67, 0xA7, 0x00, 0xEB, - 0xDC, 0xF9, 0x8C, 0xB0, 0xF7, 0x93, 0x0E, 0x81, - 0xFE, 0x98, 0xD9, 0x72, 0x45, 0x8B, 0x94, 0x7E, - 0x59, 0xE2, 0xBE, 0x4E, 0x91, 0x2D, 0x75, 0xCA, - 0x1B, 0x8E, 0x2E, 0xF4, 0x6D, 0x73, 0xB1, 0x6B, - 0x35, 0xB5, 0x67, 0x0D, 0x63, 0x2D, 0x51, 0x38, - 0x53, 0x28, 0x19, 0x1D, 0x9D, 0xAE, 0x8D, 0xC6, - 0x61, 0xCC, 0xEF, 0xA4, 0xAB, 0xE2, 0xF3, 0xB0, - 0x4C, 0x7B, 0xE2, 0x71, 0xB5, 0xF9, 0x2C, 0xFA, - 0x55, 0xCD, 0x88, 0x8B, 0x72, 0xCC, 0xBE, 0x67, - 0xFA, 0xDF, 0xEF, 0x6B, 0x53, 0x3C, 0x45, 0xD8, - 0xCB, 0xDF, 0xB2, 0x76, 0x41, 0x46, 0xD6, 0xC2, - 0x6F, 0x27, 0x16, 0xC5, 0x07, 0xF3, 0xF4, 0x44, - 0x66, 0xA3, 0x15, 0xD2, 0x77, 0xF2, 0x89, 0xDA, - 0xFD, 0xD5, 0x50, 0xCF, 0xA4, 0x9B, 0xEA, 0xCA, - 0xC9, 0x7B, 0xE5, 0x46, 0x0E, 0xED, 0x9B, 0xFB, - 0x04, 0xA9, 0xDA, 0x19, 0x58, 0xD9, 0x2A, 0x20, - 0x8A, 0xAC, 0xC1, 0xF4, 0x8E, 0xE9, 0x14, 0xD8, - 0x8A, 0xD7, 0x41, 0xD5, 0x5B, 0x9B, 0x64, 0x22, - 0xD8, 0xAF, 0xAE, 0xC7, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x52, 0x6F, 0x6F, 0x74, 0x2D, 0x43, 0x41, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x58, 0x53, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x39, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0xA3, 0x47, 0xA4, - 0xC0, 0x84, 0x4C, 0xEB, 0x7E, 0xB0, 0xCF, 0xF0, - 0xAE, 0xB7, 0x77, 0x69, 0x85, 0x93, 0xE4, 0x99, - 0x5A, 0x95, 0x4E, 0x58, 0x17, 0x38, 0xCE, 0xD6, - 0x81, 0xB0, 0xBD, 0x77, 0x09, 0xE7, 0xF8, 0x9A, - 0xDF, 0xAD, 0x05, 0x48, 0x83, 0xF6, 0xC3, 0xFD, - 0xDF, 0x7B, 0x83, 0xE0, 0x0C, 0x26, 0x81, 0x54, - 0x43, 0x29, 0xEA, 0x82, 0x6C, 0x89, 0xF0, 0xA6, - 0x74, 0x42, 0x86, 0x4D, 0x32, 0x60, 0x32, 0x7D, - 0xA7, 0x7A, 0x13, 0x40, 0x66, 0x59, 0xDA, 0x3E, - 0x41, 0x6B, 0x27, 0x94, 0x03, 0x4F, 0xAA, 0x22, - 0x9D, 0xD5, 0x54, 0x52, 0xDB, 0x27, 0x0A, 0x6A, - 0xA2, 0x3D, 0x19, 0xB1, 0x66, 0x1B, 0x19, 0x7D, - 0xAB, 0xC7, 0x0E, 0x88, 0x17, 0x91, 0xA1, 0x2A, - 0xB4, 0x3C, 0x6C, 0xCB, 0xF5, 0xAA, 0x7C, 0x3A, - 0xDD, 0x36, 0xFB, 0x35, 0x71, 0x7B, 0x20, 0x01, - 0x59, 0x00, 0xD6, 0xF6, 0x90, 0x39, 0x35, 0x41, - 0x31, 0xF8, 0xC1, 0xC0, 0x57, 0x3A, 0x35, 0x18, - 0x58, 0x90, 0xB1, 0xAD, 0x9A, 0x0E, 0xEC, 0xE0, - 0xF4, 0x7A, 0x7D, 0xA5, 0x27, 0x48, 0xC9, 0x72, - 0xAB, 0x0D, 0x08, 0x7B, 0x62, 0x35, 0x40, 0x91, - 0x14, 0x2B, 0xB1, 0x1D, 0x1A, 0xFA, 0xF9, 0xCD, - 0x5C, 0x17, 0x13, 0x53, 0x52, 0x71, 0xCA, 0xE2, - 0x2A, 0x78, 0xB1, 0x7F, 0x4A, 0xCD, 0x59, 0xD8, - 0xBA, 0x1D, 0x7D, 0x70, 0x5F, 0x78, 0x1B, 0x9F, - 0x9D, 0x37, 0x18, 0x8E, 0xD7, 0xCD, 0x0D, 0x49, - 0x57, 0x74, 0x69, 0x88, 0x3A, 0x6B, 0x8E, 0x4E, - 0x1B, 0x85, 0xDD, 0xBE, 0x39, 0x45, 0x05, 0x89, - 0x56, 0x12, 0x97, 0x59, 0x9A, 0x09, 0xA4, 0xC8, - 0x2D, 0x2F, 0xF5, 0xCF, 0xB4, 0x73, 0x70, 0xDB, - 0x58, 0x1E, 0xB2, 0x4E, 0x77, 0x6F, 0xA4, 0x7E, - 0x62, 0xDF, 0xB7, 0x05, 0xE8, 0x80, 0x42, 0x5C, - 0xB8, 0x78, 0x87, 0x97, 0x7F, 0x66, 0x2C, 0x5F, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00, 0x01, 0x00, 0x04, 0x63, 0x80, 0x5A, 0x35, 0x1A, 0x43, 0x7B, 0xA2, 0x43, 0x19, 0xBB, 0x3A, 0x77, 0x7B, 0x7A, 0xF3, 0x5E, 0x72, 0x4B, 0x15, 0x0A, 0x06, 0x39, 0x6C, 0x5F, 0xEC, 0x38, 0x45, 0xB1, 0x88, 0x76, 0x26, 0x8D, 0x5E, 0xDA, 0xE6, 0x2F, 0x14, 0xBA, 0x02, 0xFA, 0xD6, 0xFC, 0x3B, 0x2B, 0xBE, 0x87, 0x07, 0x63, 0x8E, 0x55, 0xBF, 0x05, 0x5A, 0xFC, 0xFC, 0xB3, 0x47, 0x69, 0x11, 0x89, 0xDB, 0x1C, 0xAF, 0x4B, 0x43, 0x76, 0x62, 0x3E, 0x30, 0x89, 0x0A, 0x9D, 0x3B, 0xBB, 0x3E, 0x50, 0xBD, 0xF7, 0xA6, 0xC0, 0xF7, 0xF8, 0xBB, 0x0D, 0xB5, 0x6A, 0xBB, 0xC6, 0xC3, 0x50, 0xC8, 0x88, 0xBB, 0x9D, 0xF0, 0x9B, 0xD1, 0x30, 0x64, 0x60, 0x69, 0xDD, 0x34, 0x67, 0xA7, 0x00, 0xEB, 0xDC, 0xF9, 0x8C, 0xB0, 0xF7, 0x93, 0x0E, 0x81, 0xFE, 0x98, 0xD9, 0x72, 0x45, 0x8B, 0x94, 0x7E, 0x59, 0xE2, 0xBE, 0x4E, 0x91, 0x2D, 0x75, 0xCA, 0x1B, 0x8E, 0x2E, 0xF4, 0x6D, 0x73, 0xB1, 0x6B, 0x35, 0xB5, 0x67, 0x0D, 0x63, 0x2D, 0x51, 0x38, 0x53, 0x28, 0x19, 0x1D, 0x9D, 0xAE, 0x8D, 0xC6, 0x61, 0xCC, 0xEF, 0xA4, 0xAB, 0xE2, 0xF3, 0xB0, 0x4C, 0x7B, 0xE2, 0x71, 0xB5, 0xF9, 0x2C, 0xFA, 0x55, 0xCD, 0x88, 0x8B, 0x72, 0xCC, 0xBE, 0x67, 0xFA, 0xDF, 0xEF, 0x6B, 0x53, 0x3C, 0x45, 0xD8, 0xCB, 0xDF, 0xB2, 0x76, 0x41, 0x46, 0xD6, 0xC2, 0x6F, 0x27, 0x16, 0xC5, 0x07, 0xF3, 0xF4, 0x44, 0x66, 0xA3, 0x15, 0xD2, 0x77, 0xF2, 0x89, 0xDA, 0xFD, 0xD5, 0x50, 0xCF, 0xA4, 0x9B, 0xEA, 0xCA, 0xC9, 0x7B, 0xE5, 0x46, 0x0E, 0xED, 0x9B, 0xFB, 0x04, 0xA9, 0xDA, 0x19, 0x58, 0xD9, 0x2A, 0x20, 0x8A, 0xAC, 0xC1, 0xF4, 0x8E, 0xE9, 0x14, 0xD8, 0x8A, 0xD7, 0x41, 0xD5, 0x5B, 0x9B, 0x64, 0x22, 0xD8, 0xAF, 0xAE, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x6F, 0x6F, 0x74, 0x2D, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x58, 0x53, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xA3, 0x47, 0xA4, 0xC0, 0x84, 0x4C, 0xEB, 0x7E, 0xB0, 0xCF, 0xF0, 0xAE, 0xB7, 0x77, 0x69, 0x85, 0x93, 0xE4, 0x99, 0x5A, 0x95, 0x4E, 0x58, 0x17, 0x38, 0xCE, 0xD6, 0x81, 0xB0, 0xBD, 0x77, 0x09, 0xE7, 0xF8, 0x9A, 0xDF, 0xAD, 0x05, 0x48, 0x83, 0xF6, 0xC3, 0xFD, 0xDF, 0x7B, 0x83, 0xE0, 0x0C, 0x26, 0x81, 0x54, 0x43, 0x29, 0xEA, 0x82, 0x6C, 0x89, 0xF0, 0xA6, 0x74, 0x42, 0x86, 0x4D, 0x32, 0x60, 0x32, 0x7D, 0xA7, 0x7A, 0x13, 0x40, 0x66, 0x59, 0xDA, 0x3E, 0x41, 0x6B, 0x27, 0x94, 0x03, 0x4F, 0xAA, 0x22, 0x9D, 0xD5, 0x54, 0x52, 0xDB, 0x27, 0x0A, 0x6A, 0xA2, 0x3D, 0x19, 0xB1, 0x66, 0x1B, 0x19, 0x7D, 0xAB, 0xC7, 0x0E, 0x88, 0x17, 0x91, 0xA1, 0x2A, 0xB4, 0x3C, 0x6C, 0xCB, 0xF5, 0xAA, 0x7C, 0x3A, 0xDD, 0x36, 0xFB, 0x35, 0x71, 0x7B, 0x20, 0x01, 0x59, 0x00, 0xD6, 0xF6, 0x90, 0x39, 0x35, 0x41, 0x31, 0xF8, 0xC1, 0xC0, 0x57, 0x3A, 0x35, 0x18, 0x58, 0x90, 0xB1, 0xAD, 0x9A, 0x0E, 0xEC, 0xE0, 0xF4, 0x7A, 0x7D, 0xA5, 0x27, 0x48, 0xC9, 0x72, 0xAB, 0x0D, 0x08, 0x7B, 0x62, 0x35, 0x40, 0x91, 0x14, 0x2B, 0xB1, 0x1D, 0x1A, 0xFA, 0xF9, 0xCD, 0x5C, 0x17, 0x13, 0x53, 0x52, 0x71, 0xCA, 0xE2, 0x2A, 0x78, 0xB1, 0x7F, 0x4A, 0xCD, 0x59, 0xD8, 0xBA, 0x1D, 0x7D, 0x70, 0x5F, 0x78, 0x1B, 0x9F, 0x9D, 0x37, 0x18, 0x8E, 0xD7, 0xCD, 0x0D, 0x49, 0x57, 0x74, 0x69, 0x88, 0x3A, 0x6B, 0x8E, 0x4E, 0x1B, 0x85, 0xDD, 0xBE, 0x39, 0x45, 0x05, 0x89, 0x56, 0x12, 0x97, 0x59, 0x9A, 0x09, 0xA4, 0xC8, 0x2D, 0x2F, 0xF5, 0xCF, 0xB4, 0x73, 0x70, 0xDB, 0x58, 0x1E, 0xB2, 0x4E, 0x77, 0x6F, 0xA4, 0x7E, 0x62, 0xDF, 0xB7, 0x05, 0xE8, 0x80, 0x42, 0x5C, 0xB8, 0x78, 0x87, 0x97, 0x7F, 0x66, 0x2C, 0x5F, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char cpA_dpki_cert[0x300] = { - 0x00, 0x01, 0x00, 0x04, 0x50, 0x05, 0xD7, 0x5E, - 0x6D, 0xDE, 0xB8, 0x78, 0x3C, 0x81, 0xE9, 0xEF, - 0x0D, 0x17, 0xCD, 0x58, 0xF5, 0x94, 0x26, 0xA3, - 0xFD, 0x6F, 0x69, 0x90, 0xE8, 0xF8, 0x32, 0x87, - 0x12, 0x2E, 0xC2, 0x5C, 0xA1, 0x4B, 0x99, 0x24, - 0x23, 0x37, 0xBA, 0x91, 0xA7, 0x5B, 0x0F, 0x7C, - 0x59, 0xFB, 0xF7, 0xD1, 0x89, 0x27, 0x22, 0xC4, - 0xE6, 0xAF, 0xC7, 0xDE, 0xC7, 0x4A, 0x6E, 0x00, - 0x7F, 0x43, 0x4A, 0x88, 0x8A, 0x82, 0x15, 0xE8, - 0xDF, 0x2B, 0x52, 0xED, 0x42, 0x00, 0xBC, 0x69, - 0xB4, 0xDA, 0x7F, 0xEB, 0x74, 0x6C, 0x7A, 0x2D, - 0x96, 0x56, 0x5B, 0x45, 0x59, 0x7B, 0x8F, 0xAE, - 0xB1, 0x6B, 0xDC, 0x76, 0xC1, 0xC8, 0x0C, 0x47, - 0xF5, 0x0D, 0xA9, 0xC3, 0xE1, 0xFE, 0x28, 0x50, - 0x1C, 0x26, 0xA2, 0xD1, 0x54, 0x4B, 0xD1, 0x60, - 0x4A, 0x9E, 0x8F, 0x32, 0x2A, 0xEF, 0x31, 0x5F, - 0xEA, 0x48, 0x22, 0x67, 0x22, 0xB7, 0xCB, 0x37, - 0x2F, 0xF3, 0x5F, 0x5E, 0x61, 0x6A, 0x53, 0x44, - 0xA5, 0x85, 0xE5, 0xA0, 0x8A, 0x2E, 0x17, 0x77, - 0x57, 0x2B, 0x7A, 0x9A, 0xF7, 0xD2, 0xD8, 0xC4, - 0x9C, 0xD0, 0xA0, 0x54, 0xBF, 0x8A, 0x9D, 0xB4, - 0x9F, 0xC6, 0x60, 0x61, 0x7C, 0xB8, 0x35, 0x4E, - 0x25, 0x7F, 0x68, 0x68, 0x2F, 0x94, 0xB3, 0xCC, - 0x53, 0x8C, 0x42, 0x6F, 0x88, 0xC5, 0x48, 0x5C, - 0xBE, 0xC1, 0xD0, 0x48, 0x04, 0x74, 0x96, 0x5A, - 0x7E, 0x82, 0x59, 0xAA, 0x9F, 0xB6, 0x61, 0x46, - 0xCE, 0x59, 0x21, 0xC6, 0xF0, 0xC1, 0x75, 0x1F, - 0x21, 0x91, 0x7F, 0x24, 0x96, 0xCB, 0x0C, 0x70, - 0x15, 0x7A, 0xB7, 0xBB, 0x3A, 0x9F, 0x57, 0x56, - 0x56, 0x5C, 0x38, 0x92, 0x2E, 0xFD, 0xC8, 0xF1, - 0x70, 0xB9, 0xAE, 0xA1, 0xAE, 0x36, 0xF5, 0x5E, - 0x35, 0x26, 0x63, 0x0A, 0xBA, 0xB2, 0x05, 0x0F, - 0xF0, 0x0C, 0xDC, 0xBB, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x52, 0x6F, 0x6F, 0x74, 0x2D, 0x43, 0x41, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x61, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0xA3, 0x4D, 0x5B, - 0xAA, 0x7F, 0x93, 0x80, 0x28, 0x9B, 0xE8, 0x98, - 0x63, 0x10, 0x7A, 0xE1, 0x0C, 0x59, 0x2C, 0x2F, - 0x7C, 0xFF, 0xBD, 0xAA, 0xDD, 0x74, 0xF4, 0xA2, - 0xFB, 0xAC, 0xD7, 0x6F, 0x00, 0x93, 0x42, 0x06, - 0x34, 0x71, 0x56, 0xD8, 0x40, 0x49, 0x72, 0x9F, - 0x3E, 0x24, 0xFA, 0x5E, 0x19, 0xD1, 0x5B, 0x63, - 0x5C, 0xD2, 0xEF, 0x09, 0xDE, 0x32, 0xEE, 0x6B, - 0x6F, 0xC8, 0xFA, 0x32, 0x8E, 0x2E, 0x96, 0xB9, - 0x94, 0x41, 0x04, 0x7D, 0x07, 0x62, 0x95, 0xDA, - 0x0D, 0x91, 0xD8, 0x09, 0x35, 0xD0, 0xDE, 0x8E, - 0x6B, 0xC6, 0xAB, 0x14, 0x27, 0x01, 0x9C, 0xFE, - 0x49, 0x96, 0xFC, 0x9B, 0x54, 0x79, 0x4D, 0xEB, - 0xD7, 0xC6, 0x66, 0x73, 0xA6, 0xDD, 0x3A, 0x77, - 0x65, 0x47, 0x94, 0xEC, 0x1C, 0x87, 0xAA, 0x46, - 0xD9, 0x78, 0xA9, 0x7D, 0xDB, 0x11, 0x22, 0x6E, - 0xD4, 0x12, 0xC2, 0x78, 0x4B, 0x21, 0x83, 0x92, - 0xC7, 0x10, 0xC7, 0x74, 0x19, 0xFF, 0xAA, 0xF6, - 0x0B, 0x75, 0xD8, 0x23, 0xDD, 0x33, 0xC3, 0xA1, - 0x5B, 0xA7, 0x2D, 0x30, 0xA5, 0xA4, 0xD8, 0xF8, - 0x0F, 0xD6, 0x73, 0xFD, 0x26, 0xCB, 0x29, 0xA6, - 0xEF, 0x50, 0x39, 0xE2, 0x5F, 0x59, 0x61, 0x84, - 0x6B, 0xDA, 0x2E, 0xC7, 0xCB, 0xE4, 0x38, 0x4B, - 0x28, 0xFB, 0x0D, 0xD5, 0x8E, 0x7C, 0xAA, 0x7D, - 0x4B, 0x37, 0x3A, 0xD7, 0x81, 0xDD, 0x73, 0xE3, - 0x09, 0x93, 0xBD, 0xBD, 0x7E, 0x08, 0x55, 0x4A, - 0x8C, 0xA5, 0xC9, 0x84, 0x2D, 0x71, 0x01, 0xA2, - 0x2A, 0x01, 0xB0, 0x15, 0xFB, 0x30, 0x78, 0xB9, - 0x13, 0xF4, 0xC7, 0x3F, 0xB5, 0xA6, 0xF1, 0xA2, - 0x5E, 0x22, 0xB0, 0x02, 0xB6, 0xE0, 0x09, 0x54, - 0x7F, 0x0F, 0xBD, 0xF0, 0xFE, 0xA5, 0x50, 0x1D, - 0x93, 0x15, 0xF9, 0x3D, 0x83, 0x0F, 0x0F, 0x0E, - 0x3D, 0xE2, 0x3D, 0x96, 0xE7, 0x09, 0xD9, 0x77, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00, 0x01, 0x00, 0x04, 0x50, 0x05, 0xD7, 0x5E, 0x6D, 0xDE, 0xB8, 0x78, 0x3C, 0x81, 0xE9, 0xEF, 0x0D, 0x17, 0xCD, 0x58, 0xF5, 0x94, 0x26, 0xA3, 0xFD, 0x6F, 0x69, 0x90, 0xE8, 0xF8, 0x32, 0x87, 0x12, 0x2E, 0xC2, 0x5C, 0xA1, 0x4B, 0x99, 0x24, 0x23, 0x37, 0xBA, 0x91, 0xA7, 0x5B, 0x0F, 0x7C, 0x59, 0xFB, 0xF7, 0xD1, 0x89, 0x27, 0x22, 0xC4, 0xE6, 0xAF, 0xC7, 0xDE, 0xC7, 0x4A, 0x6E, 0x00, 0x7F, 0x43, 0x4A, 0x88, 0x8A, 0x82, 0x15, 0xE8, 0xDF, 0x2B, 0x52, 0xED, 0x42, 0x00, 0xBC, 0x69, 0xB4, 0xDA, 0x7F, 0xEB, 0x74, 0x6C, 0x7A, 0x2D, 0x96, 0x56, 0x5B, 0x45, 0x59, 0x7B, 0x8F, 0xAE, 0xB1, 0x6B, 0xDC, 0x76, 0xC1, 0xC8, 0x0C, 0x47, 0xF5, 0x0D, 0xA9, 0xC3, 0xE1, 0xFE, 0x28, 0x50, 0x1C, 0x26, 0xA2, 0xD1, 0x54, 0x4B, 0xD1, 0x60, 0x4A, 0x9E, 0x8F, 0x32, 0x2A, 0xEF, 0x31, 0x5F, 0xEA, 0x48, 0x22, 0x67, 0x22, 0xB7, 0xCB, 0x37, 0x2F, 0xF3, 0x5F, 0x5E, 0x61, 0x6A, 0x53, 0x44, 0xA5, 0x85, 0xE5, 0xA0, 0x8A, 0x2E, 0x17, 0x77, 0x57, 0x2B, 0x7A, 0x9A, 0xF7, 0xD2, 0xD8, 0xC4, 0x9C, 0xD0, 0xA0, 0x54, 0xBF, 0x8A, 0x9D, 0xB4, 0x9F, 0xC6, 0x60, 0x61, 0x7C, 0xB8, 0x35, 0x4E, 0x25, 0x7F, 0x68, 0x68, 0x2F, 0x94, 0xB3, 0xCC, 0x53, 0x8C, 0x42, 0x6F, 0x88, 0xC5, 0x48, 0x5C, 0xBE, 0xC1, 0xD0, 0x48, 0x04, 0x74, 0x96, 0x5A, 0x7E, 0x82, 0x59, 0xAA, 0x9F, 0xB6, 0x61, 0x46, 0xCE, 0x59, 0x21, 0xC6, 0xF0, 0xC1, 0x75, 0x1F, 0x21, 0x91, 0x7F, 0x24, 0x96, 0xCB, 0x0C, 0x70, 0x15, 0x7A, 0xB7, 0xBB, 0x3A, 0x9F, 0x57, 0x56, 0x56, 0x5C, 0x38, 0x92, 0x2E, 0xFD, 0xC8, 0xF1, 0x70, 0xB9, 0xAE, 0xA1, 0xAE, 0x36, 0xF5, 0x5E, 0x35, 0x26, 0x63, 0x0A, 0xBA, 0xB2, 0x05, 0x0F, 0xF0, 0x0C, 0xDC, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x6F, 0x6F, 0x74, 0x2D, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xA3, 0x4D, 0x5B, 0xAA, 0x7F, 0x93, 0x80, 0x28, 0x9B, 0xE8, 0x98, 0x63, 0x10, 0x7A, 0xE1, 0x0C, 0x59, 0x2C, 0x2F, 0x7C, 0xFF, 0xBD, 0xAA, 0xDD, 0x74, 0xF4, 0xA2, 0xFB, 0xAC, 0xD7, 0x6F, 0x00, 0x93, 0x42, 0x06, 0x34, 0x71, 0x56, 0xD8, 0x40, 0x49, 0x72, 0x9F, 0x3E, 0x24, 0xFA, 0x5E, 0x19, 0xD1, 0x5B, 0x63, 0x5C, 0xD2, 0xEF, 0x09, 0xDE, 0x32, 0xEE, 0x6B, 0x6F, 0xC8, 0xFA, 0x32, 0x8E, 0x2E, 0x96, 0xB9, 0x94, 0x41, 0x04, 0x7D, 0x07, 0x62, 0x95, 0xDA, 0x0D, 0x91, 0xD8, 0x09, 0x35, 0xD0, 0xDE, 0x8E, 0x6B, 0xC6, 0xAB, 0x14, 0x27, 0x01, 0x9C, 0xFE, 0x49, 0x96, 0xFC, 0x9B, 0x54, 0x79, 0x4D, 0xEB, 0xD7, 0xC6, 0x66, 0x73, 0xA6, 0xDD, 0x3A, 0x77, 0x65, 0x47, 0x94, 0xEC, 0x1C, 0x87, 0xAA, 0x46, 0xD9, 0x78, 0xA9, 0x7D, 0xDB, 0x11, 0x22, 0x6E, 0xD4, 0x12, 0xC2, 0x78, 0x4B, 0x21, 0x83, 0x92, 0xC7, 0x10, 0xC7, 0x74, 0x19, 0xFF, 0xAA, 0xF6, 0x0B, 0x75, 0xD8, 0x23, 0xDD, 0x33, 0xC3, 0xA1, 0x5B, 0xA7, 0x2D, 0x30, 0xA5, 0xA4, 0xD8, 0xF8, 0x0F, 0xD6, 0x73, 0xFD, 0x26, 0xCB, 0x29, 0xA6, 0xEF, 0x50, 0x39, 0xE2, 0x5F, 0x59, 0x61, 0x84, 0x6B, 0xDA, 0x2E, 0xC7, 0xCB, 0xE4, 0x38, 0x4B, 0x28, 0xFB, 0x0D, 0xD5, 0x8E, 0x7C, 0xAA, 0x7D, 0x4B, 0x37, 0x3A, 0xD7, 0x81, 0xDD, 0x73, 0xE3, 0x09, 0x93, 0xBD, 0xBD, 0x7E, 0x08, 0x55, 0x4A, 0x8C, 0xA5, 0xC9, 0x84, 0x2D, 0x71, 0x01, 0xA2, 0x2A, 0x01, 0xB0, 0x15, 0xFB, 0x30, 0x78, 0xB9, 0x13, 0xF4, 0xC7, 0x3F, 0xB5, 0xA6, 0xF1, 0xA2, 0x5E, 0x22, 0xB0, 0x02, 0xB6, 0xE0, 0x09, 0x54, 0x7F, 0x0F, 0xBD, 0xF0, 0xFE, 0xA5, 0x50, 0x1D, 0x93, 0x15, 0xF9, 0x3D, 0x83, 0x0F, 0x0F, 0x0E, 0x3D, 0xE2, 0x3D, 0x96, 0xE7, 0x09, 0xD9, 0x77, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/makerom/pki/dev_legacy.h b/makerom/pki/dev_legacy.h index 5d7c638d..83596274 100644 --- a/makerom/pki/dev_legacy.h +++ b/makerom/pki/dev_legacy.h @@ -1,4 +1,5 @@ #pragma once +#include "rsa_key.h" // AES Keys static const unsigned char rvl_twl_common_etd_key_dpki[1][16] = @@ -21,148 +22,16 @@ static const unsigned char xs_dpki_eccPubKey[60] = // RSA Keys // CA2 PKI -static const unsigned char cp7_dpki_rsa_priv[256] = +static const CtrRsa2048Key cp7_dpki_rsa = { - 0x32, 0xB5, 0xC3, 0x23, 0x67, 0x7B, 0x04, 0xF2, - 0xBB, 0x00, 0x9F, 0xBC, 0x37, 0x4D, 0x9D, 0x2F, - 0x71, 0xDB, 0x8C, 0xC7, 0x06, 0xB4, 0xEF, 0x0E, - 0x43, 0x4E, 0xDC, 0xDD, 0x77, 0xC0, 0x16, 0xD8, - 0x16, 0x10, 0xE3, 0x5F, 0x0E, 0x68, 0x0D, 0x08, - 0xAF, 0x0E, 0x8E, 0xFB, 0x53, 0xDD, 0xAB, 0xE0, - 0x9C, 0x75, 0x9E, 0xFC, 0xB8, 0x6C, 0x8E, 0x62, - 0xBB, 0x6E, 0x6B, 0x99, 0x63, 0xD8, 0x76, 0xB1, - 0x98, 0x05, 0xCA, 0xC4, 0x92, 0xBE, 0x2F, 0x5A, - 0x76, 0xE8, 0xCA, 0xF4, 0xAC, 0xA2, 0xB0, 0x32, - 0x8A, 0xB7, 0xBB, 0xEB, 0xB3, 0xF1, 0xC2, 0xB1, - 0x80, 0x9D, 0x17, 0xBA, 0x4F, 0xF6, 0x5B, 0x23, - 0xF6, 0x5D, 0xAD, 0x43, 0xA6, 0xF1, 0xE4, 0x47, - 0x7C, 0x4F, 0x93, 0x2E, 0x04, 0x75, 0xEE, 0x2E, - 0x09, 0x6E, 0xCA, 0x17, 0x0E, 0x58, 0x4C, 0x99, - 0x7C, 0x45, 0x07, 0xD8, 0x70, 0x91, 0xDE, 0x89, - 0x13, 0xB9, 0xBA, 0x47, 0x33, 0x4F, 0x4F, 0x79, - 0x58, 0x16, 0x3F, 0x86, 0x11, 0x35, 0x91, 0x48, - 0x9B, 0xA7, 0xF7, 0x77, 0xB0, 0xF0, 0x48, 0xE4, - 0x11, 0x41, 0xF4, 0x25, 0x7D, 0xE5, 0xAC, 0x71, - 0x6F, 0xCD, 0x9B, 0x6E, 0xD2, 0x30, 0xFF, 0x1D, - 0x38, 0xE9, 0x2F, 0xE0, 0xD9, 0xA9, 0x89, 0x68, - 0xB6, 0xAC, 0x45, 0xC8, 0x3B, 0x9F, 0x49, 0x30, - 0x93, 0x3B, 0x69, 0x6B, 0xBE, 0x20, 0x68, 0x4B, - 0x87, 0x1F, 0x7C, 0xEA, 0xBC, 0xC2, 0x77, 0xED, - 0xCC, 0x36, 0xCD, 0xA0, 0xC6, 0xAD, 0x46, 0xF2, - 0xFD, 0x2D, 0xB9, 0x55, 0x2F, 0x81, 0xD5, 0xA7, - 0xDF, 0x4E, 0x5B, 0xE3, 0xEA, 0x92, 0xB4, 0xBF, - 0x22, 0xAA, 0x05, 0xA2, 0x86, 0x7B, 0x1A, 0x06, - 0xFB, 0x58, 0x00, 0x64, 0xC0, 0xE0, 0xF8, 0x9E, - 0x1B, 0x1C, 0x3C, 0x23, 0x20, 0xA4, 0x03, 0xEC, - 0x27, 0x7A, 0x9D, 0x8B, 0xD2, 0x71, 0xAB, 0xFD + .modulus = { 0xC3, 0xD9, 0xA5, 0xF3, 0xC2, 0x5D, 0x16, 0xD2, 0x64, 0xAD, 0x2C, 0x0C, 0xB7, 0xE6, 0x5C, 0x50, 0x6D, 0x07, 0x63, 0x05, 0x32, 0x6E, 0xD9, 0xCD, 0xEB, 0x3A, 0x5F, 0x23, 0xB0, 0x3F, 0x38, 0x80, 0x3F, 0x60, 0x96, 0xDC, 0xCC, 0xDB, 0xE3, 0xD4, 0x6E, 0x9A, 0x00, 0x7B, 0x25, 0xBE, 0xF9, 0x59, 0xE7, 0x90, 0x4A, 0xDD, 0x10, 0xF6, 0x12, 0x00, 0x99, 0xC6, 0xFD, 0x3C, 0x80, 0xBE, 0x9B, 0xDF, 0x74, 0x5A, 0x04, 0x59, 0xB2, 0x2A, 0x7C, 0x0C, 0xB4, 0xE7, 0x73, 0xD7, 0x04, 0xF4, 0x2B, 0x77, 0x9A, 0x69, 0xAC, 0x9F, 0xDA, 0x4E, 0x65, 0xF1, 0x3C, 0x30, 0x38, 0x98, 0x4E, 0x94, 0x67, 0xE3, 0xA9, 0x11, 0x2D, 0x81, 0x5E, 0x53, 0xF5, 0x75, 0xD9, 0x27, 0x52, 0x87, 0xFB, 0x98, 0x10, 0x98, 0x2F, 0x62, 0x30, 0x93, 0x5E, 0xAF, 0xEB, 0xC0, 0x3A, 0x64, 0x53, 0x6B, 0x60, 0x2E, 0x17, 0x22, 0x25, 0x97, 0x5F, 0x64, 0x8B, 0x10, 0x67, 0xC0, 0x14, 0x9C, 0xBE, 0x8F, 0x1E, 0x15, 0xF5, 0x73, 0xA7, 0x50, 0x04, 0x7F, 0x34, 0xEB, 0x02, 0x03, 0x32, 0x41, 0x75, 0x20, 0x4A, 0x40, 0x7C, 0x79, 0xDD, 0xE4, 0x29, 0xD6, 0x60, 0x73, 0x2E, 0x02, 0xD4, 0x79, 0x87, 0xF1, 0x33, 0x5A, 0xAA, 0x95, 0xFB, 0x55, 0x0D, 0x29, 0x0D, 0xA2, 0xCF, 0xC6, 0x04, 0xB7, 0x8A, 0x63, 0xCA, 0x64, 0x52, 0x87, 0xAA, 0xEB, 0xE0, 0x7D, 0x6F, 0x6C, 0xDF, 0x0E, 0x6F, 0xF1, 0xC4, 0xC2, 0x60, 0x6C, 0xC4, 0x77, 0x29, 0x03, 0x1E, 0x6B, 0x9C, 0x3C, 0xA3, 0xED, 0x2D, 0xB4, 0xF0, 0x58, 0x81, 0x50, 0xB2, 0x6E, 0x66, 0xDB, 0x99, 0xAC, 0x44, 0x77, 0xA6, 0xFE, 0x80, 0x6E, 0x08, 0xFA, 0xEA, 0xC5, 0x27, 0x92, 0x71, 0xFF, 0xCF, 0x29, 0xB1, 0x13, 0xEB, 0x14, 0x50, 0xD1, 0x81, 0xD8, 0xBF, 0x48, 0x05, 0xBD }, + .priv_exponent = { 0x32, 0xB5, 0xC3, 0x23, 0x67, 0x7B, 0x04, 0xF2, 0xBB, 0x00, 0x9F, 0xBC, 0x37, 0x4D, 0x9D, 0x2F, 0x71, 0xDB, 0x8C, 0xC7, 0x06, 0xB4, 0xEF, 0x0E, 0x43, 0x4E, 0xDC, 0xDD, 0x77, 0xC0, 0x16, 0xD8, 0x16, 0x10, 0xE3, 0x5F, 0x0E, 0x68, 0x0D, 0x08, 0xAF, 0x0E, 0x8E, 0xFB, 0x53, 0xDD, 0xAB, 0xE0, 0x9C, 0x75, 0x9E, 0xFC, 0xB8, 0x6C, 0x8E, 0x62, 0xBB, 0x6E, 0x6B, 0x99, 0x63, 0xD8, 0x76, 0xB1, 0x98, 0x05, 0xCA, 0xC4, 0x92, 0xBE, 0x2F, 0x5A, 0x76, 0xE8, 0xCA, 0xF4, 0xAC, 0xA2, 0xB0, 0x32, 0x8A, 0xB7, 0xBB, 0xEB, 0xB3, 0xF1, 0xC2, 0xB1, 0x80, 0x9D, 0x17, 0xBA, 0x4F, 0xF6, 0x5B, 0x23, 0xF6, 0x5D, 0xAD, 0x43, 0xA6, 0xF1, 0xE4, 0x47, 0x7C, 0x4F, 0x93, 0x2E, 0x04, 0x75, 0xEE, 0x2E, 0x09, 0x6E, 0xCA, 0x17, 0x0E, 0x58, 0x4C, 0x99, 0x7C, 0x45, 0x07, 0xD8, 0x70, 0x91, 0xDE, 0x89, 0x13, 0xB9, 0xBA, 0x47, 0x33, 0x4F, 0x4F, 0x79, 0x58, 0x16, 0x3F, 0x86, 0x11, 0x35, 0x91, 0x48, 0x9B, 0xA7, 0xF7, 0x77, 0xB0, 0xF0, 0x48, 0xE4, 0x11, 0x41, 0xF4, 0x25, 0x7D, 0xE5, 0xAC, 0x71, 0x6F, 0xCD, 0x9B, 0x6E, 0xD2, 0x30, 0xFF, 0x1D, 0x38, 0xE9, 0x2F, 0xE0, 0xD9, 0xA9, 0x89, 0x68, 0xB6, 0xAC, 0x45, 0xC8, 0x3B, 0x9F, 0x49, 0x30, 0x93, 0x3B, 0x69, 0x6B, 0xBE, 0x20, 0x68, 0x4B, 0x87, 0x1F, 0x7C, 0xEA, 0xBC, 0xC2, 0x77, 0xED, 0xCC, 0x36, 0xCD, 0xA0, 0xC6, 0xAD, 0x46, 0xF2, 0xFD, 0x2D, 0xB9, 0x55, 0x2F, 0x81, 0xD5, 0xA7, 0xDF, 0x4E, 0x5B, 0xE3, 0xEA, 0x92, 0xB4, 0xBF, 0x22, 0xAA, 0x05, 0xA2, 0x86, 0x7B, 0x1A, 0x06, 0xFB, 0x58, 0x00, 0x64, 0xC0, 0xE0, 0xF8, 0x9E, 0x1B, 0x1C, 0x3C, 0x23, 0x20, 0xA4, 0x03, 0xEC, 0x27, 0x7A, 0x9D, 0x8B, 0xD2, 0x71, 0xAB, 0xFD } }; -static const unsigned char cp7_dpki_rsa_pub[256] = +static const CtrRsa2048Key xs6_dpki_rsa = { - 0xC3, 0xD9, 0xA5, 0xF3, 0xC2, 0x5D, 0x16, 0xD2, - 0x64, 0xAD, 0x2C, 0x0C, 0xB7, 0xE6, 0x5C, 0x50, - 0x6D, 0x07, 0x63, 0x05, 0x32, 0x6E, 0xD9, 0xCD, - 0xEB, 0x3A, 0x5F, 0x23, 0xB0, 0x3F, 0x38, 0x80, - 0x3F, 0x60, 0x96, 0xDC, 0xCC, 0xDB, 0xE3, 0xD4, - 0x6E, 0x9A, 0x00, 0x7B, 0x25, 0xBE, 0xF9, 0x59, - 0xE7, 0x90, 0x4A, 0xDD, 0x10, 0xF6, 0x12, 0x00, - 0x99, 0xC6, 0xFD, 0x3C, 0x80, 0xBE, 0x9B, 0xDF, - 0x74, 0x5A, 0x04, 0x59, 0xB2, 0x2A, 0x7C, 0x0C, - 0xB4, 0xE7, 0x73, 0xD7, 0x04, 0xF4, 0x2B, 0x77, - 0x9A, 0x69, 0xAC, 0x9F, 0xDA, 0x4E, 0x65, 0xF1, - 0x3C, 0x30, 0x38, 0x98, 0x4E, 0x94, 0x67, 0xE3, - 0xA9, 0x11, 0x2D, 0x81, 0x5E, 0x53, 0xF5, 0x75, - 0xD9, 0x27, 0x52, 0x87, 0xFB, 0x98, 0x10, 0x98, - 0x2F, 0x62, 0x30, 0x93, 0x5E, 0xAF, 0xEB, 0xC0, - 0x3A, 0x64, 0x53, 0x6B, 0x60, 0x2E, 0x17, 0x22, - 0x25, 0x97, 0x5F, 0x64, 0x8B, 0x10, 0x67, 0xC0, - 0x14, 0x9C, 0xBE, 0x8F, 0x1E, 0x15, 0xF5, 0x73, - 0xA7, 0x50, 0x04, 0x7F, 0x34, 0xEB, 0x02, 0x03, - 0x32, 0x41, 0x75, 0x20, 0x4A, 0x40, 0x7C, 0x79, - 0xDD, 0xE4, 0x29, 0xD6, 0x60, 0x73, 0x2E, 0x02, - 0xD4, 0x79, 0x87, 0xF1, 0x33, 0x5A, 0xAA, 0x95, - 0xFB, 0x55, 0x0D, 0x29, 0x0D, 0xA2, 0xCF, 0xC6, - 0x04, 0xB7, 0x8A, 0x63, 0xCA, 0x64, 0x52, 0x87, - 0xAA, 0xEB, 0xE0, 0x7D, 0x6F, 0x6C, 0xDF, 0x0E, - 0x6F, 0xF1, 0xC4, 0xC2, 0x60, 0x6C, 0xC4, 0x77, - 0x29, 0x03, 0x1E, 0x6B, 0x9C, 0x3C, 0xA3, 0xED, - 0x2D, 0xB4, 0xF0, 0x58, 0x81, 0x50, 0xB2, 0x6E, - 0x66, 0xDB, 0x99, 0xAC, 0x44, 0x77, 0xA6, 0xFE, - 0x80, 0x6E, 0x08, 0xFA, 0xEA, 0xC5, 0x27, 0x92, - 0x71, 0xFF, 0xCF, 0x29, 0xB1, 0x13, 0xEB, 0x14, - 0x50, 0xD1, 0x81, 0xD8, 0xBF, 0x48, 0x05, 0xBD -}; - -static const unsigned char xs6_dpki_rsa_priv[256] = -{ - 0x53, 0x00, 0x39, 0x3F, 0x1F, 0xA6, 0x21, 0xFA, - 0x9E, 0xB9, 0x0C, 0x94, 0xF8, 0xF3, 0x48, 0x5E, - 0x73, 0xAC, 0xEC, 0xFD, 0x34, 0x74, 0x46, 0x30, - 0x01, 0xD5, 0xEF, 0x1D, 0x39, 0x88, 0xB2, 0x26, - 0x51, 0xC7, 0xD1, 0x12, 0xC2, 0xAA, 0xAE, 0x3B, - 0x10, 0x12, 0x48, 0x18, 0x54, 0x60, 0xEA, 0xC6, - 0x63, 0x8F, 0xAE, 0x8E, 0x5E, 0x62, 0xAE, 0x7F, - 0x6C, 0x8A, 0x61, 0xB2, 0x7F, 0x25, 0x0D, 0xB5, - 0x62, 0x15, 0xAF, 0x56, 0x71, 0xBF, 0xC6, 0x8C, - 0x53, 0x1D, 0x70, 0xD3, 0x36, 0xE6, 0x37, 0x12, - 0x26, 0x0C, 0x61, 0xB6, 0x11, 0x25, 0x2C, 0x7C, - 0x49, 0x2A, 0x2E, 0xAE, 0xBF, 0x01, 0xBA, 0x7F, - 0x82, 0x43, 0xDE, 0xD6, 0x62, 0x7C, 0x61, 0x47, - 0xB8, 0xAD, 0xA7, 0x64, 0xD5, 0xE5, 0xAF, 0x1D, - 0xE6, 0xA9, 0x0D, 0x83, 0x2D, 0x74, 0x00, 0xD5, - 0x86, 0x45, 0xEA, 0xCA, 0x31, 0xF1, 0x0B, 0xFB, - 0xCF, 0x92, 0xFF, 0xB9, 0x95, 0x18, 0xB4, 0xBA, - 0xAC, 0x49, 0x5B, 0x04, 0x45, 0x39, 0x6A, 0x3E, - 0xB9, 0x52, 0xA9, 0x47, 0x18, 0xA4, 0x60, 0x18, - 0x3D, 0xFD, 0xE2, 0x43, 0x5E, 0x64, 0x2C, 0x48, - 0xC4, 0x65, 0xC5, 0x75, 0x81, 0xB4, 0xC9, 0x12, - 0x64, 0x7E, 0x7E, 0x9E, 0x6E, 0x3D, 0xA2, 0x82, - 0x47, 0xBD, 0xAB, 0x40, 0x76, 0x5F, 0x39, 0x50, - 0x3C, 0x93, 0x3A, 0x0D, 0xD7, 0x00, 0x9B, 0xCA, - 0x50, 0x63, 0xDD, 0x1C, 0x86, 0x69, 0xE1, 0x9D, - 0xD1, 0xFF, 0x99, 0x03, 0x70, 0x82, 0xCF, 0xE2, - 0xBB, 0x70, 0x9E, 0x51, 0xFC, 0x9C, 0x53, 0x3F, - 0x3F, 0x45, 0xA7, 0x2F, 0x9D, 0xEC, 0x05, 0x0B, - 0xE7, 0x5D, 0x89, 0xD4, 0xC4, 0x2F, 0x50, 0xB6, - 0x91, 0xF5, 0x81, 0xA5, 0x83, 0xC7, 0x07, 0x9C, - 0x82, 0x64, 0x9C, 0xD7, 0x66, 0x4A, 0x1B, 0x4F, - 0x34, 0x7E, 0x3C, 0xFC, 0x6B, 0x73, 0x01, 0x51 -}; - -static const unsigned char xs6_dpki_rsa_pub[256] = -{ - 0xCB, 0xDA, 0x82, 0x8B, 0xB1, 0x9C, 0x9F, 0xCD, - 0x0B, 0x6E, 0xBE, 0x65, 0xE6, 0x85, 0x7B, 0x29, - 0x85, 0xB1, 0x56, 0x67, 0x16, 0xD8, 0xEB, 0xD1, - 0x42, 0x68, 0x50, 0x01, 0x01, 0x04, 0x24, 0x37, - 0x73, 0x87, 0x87, 0x9A, 0x92, 0x7D, 0xCD, 0x52, - 0xC2, 0x3A, 0xA3, 0x80, 0xB6, 0xE8, 0x50, 0x81, - 0x13, 0xE5, 0xEC, 0xDE, 0xDA, 0x5D, 0x38, 0xB3, - 0x3D, 0x9B, 0xFA, 0x4A, 0x61, 0x02, 0x00, 0xF1, - 0xC2, 0xC8, 0x68, 0x7F, 0x1B, 0xCE, 0x09, 0xDD, - 0x04, 0x95, 0x64, 0xBD, 0xA5, 0x9A, 0x22, 0x14, - 0x9E, 0x3E, 0xA0, 0x33, 0x73, 0x31, 0xD3, 0x93, - 0x60, 0x44, 0xB0, 0x9C, 0x65, 0x01, 0xF8, 0xB8, - 0x79, 0xE9, 0x5E, 0x8A, 0x16, 0x0D, 0xC6, 0x3F, - 0x1B, 0x7D, 0x5F, 0x3B, 0x7E, 0x64, 0xAA, 0xE1, - 0x51, 0x27, 0x38, 0x91, 0x29, 0xB4, 0xB1, 0x13, - 0xC1, 0x2C, 0xB7, 0x96, 0x5C, 0x48, 0xE4, 0x89, - 0xE8, 0x58, 0xA5, 0x34, 0x86, 0xFE, 0x21, 0x11, - 0x76, 0x6E, 0x01, 0x24, 0xCB, 0x5F, 0x83, 0x36, - 0x99, 0x87, 0x32, 0xCB, 0xAC, 0xF5, 0x6E, 0x46, - 0x4A, 0x6C, 0xD2, 0xCD, 0x65, 0x27, 0x2C, 0xD1, - 0x46, 0x30, 0x3F, 0x31, 0xAA, 0x39, 0x9A, 0x44, - 0xBE, 0x5E, 0x91, 0x93, 0x51, 0x82, 0xF8, 0x65, - 0xF3, 0x66, 0x5D, 0x06, 0x10, 0x7C, 0x9E, 0xE3, - 0x8D, 0x91, 0x03, 0xEE, 0x58, 0x54, 0x8B, 0x30, - 0x1A, 0x9E, 0x01, 0xDB, 0x37, 0x6B, 0x89, 0x3D, - 0xC4, 0xE6, 0xC7, 0x4E, 0xBE, 0xA2, 0x5A, 0xCF, - 0x0D, 0x28, 0xE7, 0xC7, 0xB3, 0x12, 0x99, 0x8D, - 0xE7, 0x71, 0x93, 0x9D, 0xD1, 0xE3, 0xD6, 0x6B, - 0xD4, 0x37, 0xC0, 0xD5, 0x7D, 0x8B, 0xEF, 0x33, - 0xE5, 0x27, 0xBA, 0x04, 0xCF, 0xD0, 0x62, 0x61, - 0x9C, 0xD0, 0x7E, 0x38, 0xC1, 0xC7, 0xE6, 0x70, - 0x49, 0x20, 0x71, 0x5C, 0xC6, 0x35, 0x76, 0x15 + .modulus = { 0xCB, 0xDA, 0x82, 0x8B, 0xB1, 0x9C, 0x9F, 0xCD, 0x0B, 0x6E, 0xBE, 0x65, 0xE6, 0x85, 0x7B, 0x29, 0x85, 0xB1, 0x56, 0x67, 0x16, 0xD8, 0xEB, 0xD1, 0x42, 0x68, 0x50, 0x01, 0x01, 0x04, 0x24, 0x37, 0x73, 0x87, 0x87, 0x9A, 0x92, 0x7D, 0xCD, 0x52, 0xC2, 0x3A, 0xA3, 0x80, 0xB6, 0xE8, 0x50, 0x81, 0x13, 0xE5, 0xEC, 0xDE, 0xDA, 0x5D, 0x38, 0xB3, 0x3D, 0x9B, 0xFA, 0x4A, 0x61, 0x02, 0x00, 0xF1, 0xC2, 0xC8, 0x68, 0x7F, 0x1B, 0xCE, 0x09, 0xDD, 0x04, 0x95, 0x64, 0xBD, 0xA5, 0x9A, 0x22, 0x14, 0x9E, 0x3E, 0xA0, 0x33, 0x73, 0x31, 0xD3, 0x93, 0x60, 0x44, 0xB0, 0x9C, 0x65, 0x01, 0xF8, 0xB8, 0x79, 0xE9, 0x5E, 0x8A, 0x16, 0x0D, 0xC6, 0x3F, 0x1B, 0x7D, 0x5F, 0x3B, 0x7E, 0x64, 0xAA, 0xE1, 0x51, 0x27, 0x38, 0x91, 0x29, 0xB4, 0xB1, 0x13, 0xC1, 0x2C, 0xB7, 0x96, 0x5C, 0x48, 0xE4, 0x89, 0xE8, 0x58, 0xA5, 0x34, 0x86, 0xFE, 0x21, 0x11, 0x76, 0x6E, 0x01, 0x24, 0xCB, 0x5F, 0x83, 0x36, 0x99, 0x87, 0x32, 0xCB, 0xAC, 0xF5, 0x6E, 0x46, 0x4A, 0x6C, 0xD2, 0xCD, 0x65, 0x27, 0x2C, 0xD1, 0x46, 0x30, 0x3F, 0x31, 0xAA, 0x39, 0x9A, 0x44, 0xBE, 0x5E, 0x91, 0x93, 0x51, 0x82, 0xF8, 0x65, 0xF3, 0x66, 0x5D, 0x06, 0x10, 0x7C, 0x9E, 0xE3, 0x8D, 0x91, 0x03, 0xEE, 0x58, 0x54, 0x8B, 0x30, 0x1A, 0x9E, 0x01, 0xDB, 0x37, 0x6B, 0x89, 0x3D, 0xC4, 0xE6, 0xC7, 0x4E, 0xBE, 0xA2, 0x5A, 0xCF, 0x0D, 0x28, 0xE7, 0xC7, 0xB3, 0x12, 0x99, 0x8D, 0xE7, 0x71, 0x93, 0x9D, 0xD1, 0xE3, 0xD6, 0x6B, 0xD4, 0x37, 0xC0, 0xD5, 0x7D, 0x8B, 0xEF, 0x33, 0xE5, 0x27, 0xBA, 0x04, 0xCF, 0xD0, 0x62, 0x61, 0x9C, 0xD0, 0x7E, 0x38, 0xC1, 0xC7, 0xE6, 0x70, 0x49, 0x20, 0x71, 0x5C, 0xC6, 0x35, 0x76, 0x15 }, + .priv_exponent = {0x53, 0x00, 0x39, 0x3F, 0x1F, 0xA6, 0x21, 0xFA, 0x9E, 0xB9, 0x0C, 0x94, 0xF8, 0xF3, 0x48, 0x5E, 0x73, 0xAC, 0xEC, 0xFD, 0x34, 0x74, 0x46, 0x30, 0x01, 0xD5, 0xEF, 0x1D, 0x39, 0x88, 0xB2, 0x26, 0x51, 0xC7, 0xD1, 0x12, 0xC2, 0xAA, 0xAE, 0x3B, 0x10, 0x12, 0x48, 0x18, 0x54, 0x60, 0xEA, 0xC6, 0x63, 0x8F, 0xAE, 0x8E, 0x5E, 0x62, 0xAE, 0x7F, 0x6C, 0x8A, 0x61, 0xB2, 0x7F, 0x25, 0x0D, 0xB5, 0x62, 0x15, 0xAF, 0x56, 0x71, 0xBF, 0xC6, 0x8C, 0x53, 0x1D, 0x70, 0xD3, 0x36, 0xE6, 0x37, 0x12, 0x26, 0x0C, 0x61, 0xB6, 0x11, 0x25, 0x2C, 0x7C, 0x49, 0x2A, 0x2E, 0xAE, 0xBF, 0x01, 0xBA, 0x7F, 0x82, 0x43, 0xDE, 0xD6, 0x62, 0x7C, 0x61, 0x47, 0xB8, 0xAD, 0xA7, 0x64, 0xD5, 0xE5, 0xAF, 0x1D, 0xE6, 0xA9, 0x0D, 0x83, 0x2D, 0x74, 0x00, 0xD5, 0x86, 0x45, 0xEA, 0xCA, 0x31, 0xF1, 0x0B, 0xFB, 0xCF, 0x92, 0xFF, 0xB9, 0x95, 0x18, 0xB4, 0xBA, 0xAC, 0x49, 0x5B, 0x04, 0x45, 0x39, 0x6A, 0x3E, 0xB9, 0x52, 0xA9, 0x47, 0x18, 0xA4, 0x60, 0x18, 0x3D, 0xFD, 0xE2, 0x43, 0x5E, 0x64, 0x2C, 0x48, 0xC4, 0x65, 0xC5, 0x75, 0x81, 0xB4, 0xC9, 0x12, 0x64, 0x7E, 0x7E, 0x9E, 0x6E, 0x3D, 0xA2, 0x82, 0x47, 0xBD, 0xAB, 0x40, 0x76, 0x5F, 0x39, 0x50, 0x3C, 0x93, 0x3A, 0x0D, 0xD7, 0x00, 0x9B, 0xCA, 0x50, 0x63, 0xDD, 0x1C, 0x86, 0x69, 0xE1, 0x9D, 0xD1, 0xFF, 0x99, 0x03, 0x70, 0x82, 0xCF, 0xE2, 0xBB, 0x70, 0x9E, 0x51, 0xFC, 0x9C, 0x53, 0x3F, 0x3F, 0x45, 0xA7, 0x2F, 0x9D, 0xEC, 0x05, 0x0B, 0xE7, 0x5D, 0x89, 0xD4, 0xC4, 0x2F, 0x50, 0xB6, 0x91, 0xF5, 0x81, 0xA5, 0x83, 0xC7, 0x07, 0x9C, 0x82, 0x64, 0x9C, 0xD7, 0x66, 0x4A, 0x1B, 0x4F, 0x34, 0x7E, 0x3C, 0xFC, 0x6B, 0x73, 0x01, 0x51} }; // Certificates diff --git a/makerom/pki/prod.h b/makerom/pki/prod.h index 6935a951..bec496be 100644 --- a/makerom/pki/prod.h +++ b/makerom/pki/prod.h @@ -27,727 +27,54 @@ static const unsigned char ctr_common_etd_keyY_ppki[6][16] = }; // RSA KEYS -static const unsigned char root_ppki_rsa_pub[0x200] = +static const CtrRsa4096Key root_dpki_rsa = { - 0xF8, 0x24, 0x6C, 0x58, 0xBA, 0xE7, 0x50, 0x03, - 0x01, 0xFB, 0xB7, 0xC2, 0xEB, 0xE0, 0x01, 0x05, - 0x71, 0xDA, 0x92, 0x23, 0x78, 0xF0, 0x51, 0x4E, - 0xC0, 0x03, 0x1D, 0xD0, 0xD2, 0x1E, 0xD3, 0xD0, - 0x7E, 0xFC, 0x85, 0x20, 0x69, 0xB5, 0xDE, 0x9B, - 0xB9, 0x51, 0xA8, 0xBC, 0x90, 0xA2, 0x44, 0x92, - 0x6D, 0x37, 0x92, 0x95, 0xAE, 0x94, 0x36, 0xAA, - 0xA6, 0xA3, 0x02, 0x51, 0x0C, 0x7B, 0x1D, 0xED, - 0xD5, 0xFB, 0x20, 0x86, 0x9D, 0x7F, 0x30, 0x16, - 0xF6, 0xBE, 0x65, 0xD3, 0x83, 0xA1, 0x6D, 0xB3, - 0x32, 0x1B, 0x95, 0x35, 0x18, 0x90, 0xB1, 0x70, - 0x02, 0x93, 0x7E, 0xE1, 0x93, 0xF5, 0x7E, 0x99, - 0xA2, 0x47, 0x4E, 0x9D, 0x38, 0x24, 0xC7, 0xAE, - 0xE3, 0x85, 0x41, 0xF5, 0x67, 0xE7, 0x51, 0x8C, - 0x7A, 0x0E, 0x38, 0xE7, 0xEB, 0xAF, 0x41, 0x19, - 0x1B, 0xCF, 0xF1, 0x7B, 0x42, 0xA6, 0xB4, 0xED, - 0xE6, 0xCE, 0x8D, 0xE7, 0x31, 0x8F, 0x7F, 0x52, - 0x04, 0xB3, 0x99, 0x0E, 0x22, 0x67, 0x45, 0xAF, - 0xD4, 0x85, 0xB2, 0x44, 0x93, 0x00, 0x8B, 0x08, - 0xC7, 0xF6, 0xB7, 0xE5, 0x6B, 0x02, 0xB3, 0xE8, - 0xFE, 0x0C, 0x9D, 0x85, 0x9C, 0xB8, 0xB6, 0x82, - 0x23, 0xB8, 0xAB, 0x27, 0xEE, 0x5F, 0x65, 0x38, - 0x07, 0x8B, 0x2D, 0xB9, 0x1E, 0x2A, 0x15, 0x3E, - 0x85, 0x81, 0x80, 0x72, 0xA2, 0x3B, 0x6D, 0xD9, - 0x32, 0x81, 0x05, 0x4F, 0x6F, 0xB0, 0xF6, 0xF5, - 0xAD, 0x28, 0x3E, 0xCA, 0x0B, 0x7A, 0xF3, 0x54, - 0x55, 0xE0, 0x3D, 0xA7, 0xB6, 0x83, 0x26, 0xF3, - 0xEC, 0x83, 0x4A, 0xF3, 0x14, 0x04, 0x8A, 0xC6, - 0xDF, 0x20, 0xD2, 0x85, 0x08, 0x67, 0x3C, 0xAB, - 0x62, 0xA2, 0xC7, 0xBC, 0x13, 0x1A, 0x53, 0x3E, - 0x0B, 0x66, 0x80, 0x6B, 0x1C, 0x30, 0x66, 0x4B, - 0x37, 0x23, 0x31, 0xBD, 0xC4, 0xB0, 0xCA, 0xD8, - 0xD1, 0x1E, 0xE7, 0xBB, 0xD9, 0x28, 0x55, 0x48, - 0xAA, 0xEC, 0x1F, 0x66, 0xE8, 0x21, 0xB3, 0xC8, - 0xA0, 0x47, 0x69, 0x00, 0xC5, 0xE6, 0x88, 0xE8, - 0x0C, 0xCE, 0x3C, 0x61, 0xD6, 0x9C, 0xBB, 0xA1, - 0x37, 0xC6, 0x60, 0x4F, 0x7A, 0x72, 0xDD, 0x8C, - 0x7B, 0x3E, 0x3D, 0x51, 0x29, 0x0D, 0xAA, 0x6A, - 0x59, 0x7B, 0x08, 0x1F, 0x9D, 0x36, 0x33, 0xA3, - 0x46, 0x7A, 0x35, 0x61, 0x09, 0xAC, 0xA7, 0xDD, - 0x7D, 0x2E, 0x2F, 0xB2, 0xC1, 0xAE, 0xB8, 0xE2, - 0x0F, 0x48, 0x92, 0xD8, 0xB9, 0xF8, 0xB4, 0x6F, - 0x4E, 0x3C, 0x11, 0xF4, 0xF4, 0x7D, 0x8B, 0x75, - 0x7D, 0xFE, 0xFE, 0xA3, 0x89, 0x9C, 0x33, 0x59, - 0x5C, 0x5E, 0xFD, 0xEB, 0xCB, 0xAB, 0xE8, 0x41, - 0x3E, 0x3A, 0x9A, 0x80, 0x3C, 0x69, 0x35, 0x6E, - 0xB2, 0xB2, 0xAD, 0x5C, 0xC4, 0xC8, 0x58, 0x45, - 0x5E, 0xF5, 0xF7, 0xB3, 0x06, 0x44, 0xB4, 0x7C, - 0x64, 0x06, 0x8C, 0xDF, 0x80, 0x9F, 0x76, 0x02, - 0x5A, 0x2D, 0xB4, 0x46, 0xE0, 0x3D, 0x7C, 0xF6, - 0x2F, 0x34, 0xE7, 0x02, 0x45, 0x7B, 0x02, 0xA4, - 0xCF, 0x5D, 0x9D, 0xD5, 0x3C, 0xA5, 0x3A, 0x7C, - 0xA6, 0x29, 0x78, 0x8C, 0x67, 0xCA, 0x08, 0xBF, - 0xEC, 0xCA, 0x43, 0xA9, 0x57, 0xAD, 0x16, 0xC9, - 0x4E, 0x1C, 0xD8, 0x75, 0xCA, 0x10, 0x7D, 0xCE, - 0x7E, 0x01, 0x18, 0xF0, 0xDF, 0x6B, 0xFE, 0xE5, - 0x1D, 0xDB, 0xD9, 0x91, 0xC2, 0x6E, 0x60, 0xCD, - 0x48, 0x58, 0xAA, 0x59, 0x2C, 0x82, 0x00, 0x75, - 0xF2, 0x9F, 0x52, 0x6C, 0x91, 0x7C, 0x6F, 0xE5, - 0x40, 0x3E, 0xA7, 0xD4, 0xA5, 0x0C, 0xEC, 0x3B, - 0x73, 0x84, 0xDE, 0x88, 0x6E, 0x82, 0xD2, 0xEB, - 0x4D, 0x4E, 0x42, 0xB5, 0xF2, 0xB1, 0x49, 0xA8, - 0x1E, 0xA7, 0xCE, 0x71, 0x44, 0xDC, 0x29, 0x94, - 0xCF, 0xC4, 0x4E, 0x1F, 0x91, 0xCB, 0xD4, 0x95 + .modulus = { 0xF8, 0x24, 0x6C, 0x58, 0xBA, 0xE7, 0x50, 0x03, 0x01, 0xFB, 0xB7, 0xC2, 0xEB, 0xE0, 0x01, 0x05, 0x71, 0xDA, 0x92, 0x23, 0x78, 0xF0, 0x51, 0x4E, 0xC0, 0x03, 0x1D, 0xD0, 0xD2, 0x1E, 0xD3, 0xD0, 0x7E, 0xFC, 0x85, 0x20, 0x69, 0xB5, 0xDE, 0x9B, 0xB9, 0x51, 0xA8, 0xBC, 0x90, 0xA2, 0x44, 0x92, 0x6D, 0x37, 0x92, 0x95, 0xAE, 0x94, 0x36, 0xAA, 0xA6, 0xA3, 0x02, 0x51, 0x0C, 0x7B, 0x1D, 0xED, 0xD5, 0xFB, 0x20, 0x86, 0x9D, 0x7F, 0x30, 0x16, 0xF6, 0xBE, 0x65, 0xD3, 0x83, 0xA1, 0x6D, 0xB3, 0x32, 0x1B, 0x95, 0x35, 0x18, 0x90, 0xB1, 0x70, 0x02, 0x93, 0x7E, 0xE1, 0x93, 0xF5, 0x7E, 0x99, 0xA2, 0x47, 0x4E, 0x9D, 0x38, 0x24, 0xC7, 0xAE, 0xE3, 0x85, 0x41, 0xF5, 0x67, 0xE7, 0x51, 0x8C, 0x7A, 0x0E, 0x38, 0xE7, 0xEB, 0xAF, 0x41, 0x19, 0x1B, 0xCF, 0xF1, 0x7B, 0x42, 0xA6, 0xB4, 0xED, 0xE6, 0xCE, 0x8D, 0xE7, 0x31, 0x8F, 0x7F, 0x52, 0x04, 0xB3, 0x99, 0x0E, 0x22, 0x67, 0x45, 0xAF, 0xD4, 0x85, 0xB2, 0x44, 0x93, 0x00, 0x8B, 0x08, 0xC7, 0xF6, 0xB7, 0xE5, 0x6B, 0x02, 0xB3, 0xE8, 0xFE, 0x0C, 0x9D, 0x85, 0x9C, 0xB8, 0xB6, 0x82, 0x23, 0xB8, 0xAB, 0x27, 0xEE, 0x5F, 0x65, 0x38, 0x07, 0x8B, 0x2D, 0xB9, 0x1E, 0x2A, 0x15, 0x3E, 0x85, 0x81, 0x80, 0x72, 0xA2, 0x3B, 0x6D, 0xD9, 0x32, 0x81, 0x05, 0x4F, 0x6F, 0xB0, 0xF6, 0xF5, 0xAD, 0x28, 0x3E, 0xCA, 0x0B, 0x7A, 0xF3, 0x54, 0x55, 0xE0, 0x3D, 0xA7, 0xB6, 0x83, 0x26, 0xF3, 0xEC, 0x83, 0x4A, 0xF3, 0x14, 0x04, 0x8A, 0xC6, 0xDF, 0x20, 0xD2, 0x85, 0x08, 0x67, 0x3C, 0xAB, 0x62, 0xA2, 0xC7, 0xBC, 0x13, 0x1A, 0x53, 0x3E, 0x0B, 0x66, 0x80, 0x6B, 0x1C, 0x30, 0x66, 0x4B, 0x37, 0x23, 0x31, 0xBD, 0xC4, 0xB0, 0xCA, 0xD8, 0xD1, 0x1E, 0xE7, 0xBB, 0xD9, 0x28, 0x55, 0x48, 0xAA, 0xEC, 0x1F, 0x66, 0xE8, 0x21, 0xB3, 0xC8, 0xA0, 0x47, 0x69, 0x00, 0xC5, 0xE6, 0x88, 0xE8, 0x0C, 0xCE, 0x3C, 0x61, 0xD6, 0x9C, 0xBB, 0xA1, 0x37, 0xC6, 0x60, 0x4F, 0x7A, 0x72, 0xDD, 0x8C, 0x7B, 0x3E, 0x3D, 0x51, 0x29, 0x0D, 0xAA, 0x6A, 0x59, 0x7B, 0x08, 0x1F, 0x9D, 0x36, 0x33, 0xA3, 0x46, 0x7A, 0x35, 0x61, 0x09, 0xAC, 0xA7, 0xDD, 0x7D, 0x2E, 0x2F, 0xB2, 0xC1, 0xAE, 0xB8, 0xE2, 0x0F, 0x48, 0x92, 0xD8, 0xB9, 0xF8, 0xB4, 0x6F, 0x4E, 0x3C, 0x11, 0xF4, 0xF4, 0x7D, 0x8B, 0x75, 0x7D, 0xFE, 0xFE, 0xA3, 0x89, 0x9C, 0x33, 0x59, 0x5C, 0x5E, 0xFD, 0xEB, 0xCB, 0xAB, 0xE8, 0x41, 0x3E, 0x3A, 0x9A, 0x80, 0x3C, 0x69, 0x35, 0x6E, 0xB2, 0xB2, 0xAD, 0x5C, 0xC4, 0xC8, 0x58, 0x45, 0x5E, 0xF5, 0xF7, 0xB3, 0x06, 0x44, 0xB4, 0x7C, 0x64, 0x06, 0x8C, 0xDF, 0x80, 0x9F, 0x76, 0x02, 0x5A, 0x2D, 0xB4, 0x46, 0xE0, 0x3D, 0x7C, 0xF6, 0x2F, 0x34, 0xE7, 0x02, 0x45, 0x7B, 0x02, 0xA4, 0xCF, 0x5D, 0x9D, 0xD5, 0x3C, 0xA5, 0x3A, 0x7C, 0xA6, 0x29, 0x78, 0x8C, 0x67, 0xCA, 0x08, 0xBF, 0xEC, 0xCA, 0x43, 0xA9, 0x57, 0xAD, 0x16, 0xC9, 0x4E, 0x1C, 0xD8, 0x75, 0xCA, 0x10, 0x7D, 0xCE, 0x7E, 0x01, 0x18, 0xF0, 0xDF, 0x6B, 0xFE, 0xE5, 0x1D, 0xDB, 0xD9, 0x91, 0xC2, 0x6E, 0x60, 0xCD, 0x48, 0x58, 0xAA, 0x59, 0x2C, 0x82, 0x00, 0x75, 0xF2, 0x9F, 0x52, 0x6C, 0x91, 0x7C, 0x6F, 0xE5, 0x40, 0x3E, 0xA7, 0xD4, 0xA5, 0x0C, 0xEC, 0x3B, 0x73, 0x84, 0xDE, 0x88, 0x6E, 0x82, 0xD2, 0xEB, 0x4D, 0x4E, 0x42, 0xB5, 0xF2, 0xB1, 0x49, 0xA8, 0x1E, 0xA7, 0xCE, 0x71, 0x44, 0xDC, 0x29, 0x94, 0xCF, 0xC4, 0x4E, 0x1F, 0x91, 0xCB, 0xD4, 0x95 }, + .priv_exponent = { 0 } }; -static const unsigned char cpB_ppki_rsa_priv[256] = +static const CtrRsa2048Key cpB_ppki_rsa = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .modulus = { 0xA6, 0x89, 0xC5, 0x90, 0xFD, 0x0B, 0x2F, 0x0D, 0x4F, 0x56, 0xB6, 0x32, 0xFB, 0x93, 0x4E, 0xD0, 0x73, 0x95, 0x17, 0xB3, 0x3A, 0x79, 0xDE, 0x04, 0x0E, 0xE9, 0x2D, 0xC3, 0x1D, 0x37, 0xC7, 0xF7, 0x3B, 0xF0, 0x4B, 0xD3, 0xE4, 0x4E, 0x20, 0xAB, 0x5A, 0x6F, 0xEA, 0xF5, 0x98, 0x4C, 0xC1, 0xF6, 0x06, 0x2E, 0x9A, 0x9F, 0xE5, 0x6C, 0x32, 0x85, 0xDC, 0x6F, 0x25, 0xDD, 0xD5, 0xD0, 0xBF, 0x9F, 0xE2, 0xEF, 0xE8, 0x35, 0xDF, 0x26, 0x34, 0xED, 0x93, 0x7F, 0xAB, 0x02, 0x14, 0xD1, 0x04, 0x80, 0x9C, 0xF7, 0x4B, 0x86, 0x0E, 0x6B, 0x04, 0x83, 0xF4, 0xCD, 0x2D, 0xAB, 0x2A, 0x96, 0x02, 0xBC, 0x56, 0xF0, 0xD6, 0xBD, 0x94, 0x6A, 0xED, 0x6E, 0x0B, 0xE4, 0xF0, 0x8F, 0x26, 0x68, 0x6B, 0xD0, 0x9E, 0xF7, 0xDB, 0x32, 0x5F, 0x82, 0xB1, 0x8F, 0x6A, 0xF2, 0xED, 0x52, 0x5B, 0xFD, 0x82, 0x8B, 0x65, 0x3F, 0xEE, 0x6E, 0xCE, 0x40, 0x0D, 0x5A, 0x48, 0xFF, 0xE2, 0x2D, 0x53, 0x8B, 0xB5, 0x33, 0x5B, 0x41, 0x53, 0x34, 0x2D, 0x43, 0x35, 0xAC, 0xF5, 0x90, 0xD0, 0xD3, 0x0A, 0xE2, 0x04, 0x3C, 0x7F, 0x5A, 0xD2, 0x14, 0xFC, 0x9C, 0x0F, 0xE6, 0xFA, 0x40, 0xA5, 0xC8, 0x65, 0x06, 0xCA, 0x63, 0x69, 0xBC, 0xEE, 0x44, 0xA3, 0x2D, 0x9E, 0x69, 0x5C, 0xF0, 0x0B, 0x4F, 0xD7, 0x9A, 0xDB, 0x56, 0x8D, 0x14, 0x9C, 0x20, 0x28, 0xA1, 0x4C, 0x9D, 0x71, 0xB8, 0x50, 0xCA, 0x36, 0x5B, 0x37, 0xF7, 0x0B, 0x65, 0x77, 0x91, 0xFC, 0x5D, 0x72, 0x8C, 0x4E, 0x18, 0xFD, 0x22, 0x55, 0x7C, 0x40, 0x62, 0xD7, 0x47, 0x71, 0x53, 0x3C, 0x70, 0x17, 0x9D, 0x3D, 0xAE, 0x8F, 0x92, 0xB1, 0x17, 0xE4, 0x5C, 0xB3, 0x32, 0xF3, 0xB3, 0xC2, 0xA2, 0x2E, 0x70, 0x5C, 0xFE, 0xC6, 0x6F, 0x6D, 0xA3, 0x77, 0x2B }, + .priv_exponent = { 0 } }; -static const unsigned char cpB_ppki_rsa_pub[256] = +static const CtrRsa2048Key xsC_ppki_rsa = { - 0xA6, 0x89, 0xC5, 0x90, 0xFD, 0x0B, 0x2F, 0x0D, - 0x4F, 0x56, 0xB6, 0x32, 0xFB, 0x93, 0x4E, 0xD0, - 0x73, 0x95, 0x17, 0xB3, 0x3A, 0x79, 0xDE, 0x04, - 0x0E, 0xE9, 0x2D, 0xC3, 0x1D, 0x37, 0xC7, 0xF7, - 0x3B, 0xF0, 0x4B, 0xD3, 0xE4, 0x4E, 0x20, 0xAB, - 0x5A, 0x6F, 0xEA, 0xF5, 0x98, 0x4C, 0xC1, 0xF6, - 0x06, 0x2E, 0x9A, 0x9F, 0xE5, 0x6C, 0x32, 0x85, - 0xDC, 0x6F, 0x25, 0xDD, 0xD5, 0xD0, 0xBF, 0x9F, - 0xE2, 0xEF, 0xE8, 0x35, 0xDF, 0x26, 0x34, 0xED, - 0x93, 0x7F, 0xAB, 0x02, 0x14, 0xD1, 0x04, 0x80, - 0x9C, 0xF7, 0x4B, 0x86, 0x0E, 0x6B, 0x04, 0x83, - 0xF4, 0xCD, 0x2D, 0xAB, 0x2A, 0x96, 0x02, 0xBC, - 0x56, 0xF0, 0xD6, 0xBD, 0x94, 0x6A, 0xED, 0x6E, - 0x0B, 0xE4, 0xF0, 0x8F, 0x26, 0x68, 0x6B, 0xD0, - 0x9E, 0xF7, 0xDB, 0x32, 0x5F, 0x82, 0xB1, 0x8F, - 0x6A, 0xF2, 0xED, 0x52, 0x5B, 0xFD, 0x82, 0x8B, - 0x65, 0x3F, 0xEE, 0x6E, 0xCE, 0x40, 0x0D, 0x5A, - 0x48, 0xFF, 0xE2, 0x2D, 0x53, 0x8B, 0xB5, 0x33, - 0x5B, 0x41, 0x53, 0x34, 0x2D, 0x43, 0x35, 0xAC, - 0xF5, 0x90, 0xD0, 0xD3, 0x0A, 0xE2, 0x04, 0x3C, - 0x7F, 0x5A, 0xD2, 0x14, 0xFC, 0x9C, 0x0F, 0xE6, - 0xFA, 0x40, 0xA5, 0xC8, 0x65, 0x06, 0xCA, 0x63, - 0x69, 0xBC, 0xEE, 0x44, 0xA3, 0x2D, 0x9E, 0x69, - 0x5C, 0xF0, 0x0B, 0x4F, 0xD7, 0x9A, 0xDB, 0x56, - 0x8D, 0x14, 0x9C, 0x20, 0x28, 0xA1, 0x4C, 0x9D, - 0x71, 0xB8, 0x50, 0xCA, 0x36, 0x5B, 0x37, 0xF7, - 0x0B, 0x65, 0x77, 0x91, 0xFC, 0x5D, 0x72, 0x8C, - 0x4E, 0x18, 0xFD, 0x22, 0x55, 0x7C, 0x40, 0x62, - 0xD7, 0x47, 0x71, 0x53, 0x3C, 0x70, 0x17, 0x9D, - 0x3D, 0xAE, 0x8F, 0x92, 0xB1, 0x17, 0xE4, 0x5C, - 0xB3, 0x32, 0xF3, 0xB3, 0xC2, 0xA2, 0x2E, 0x70, - 0x5C, 0xFE, 0xC6, 0x6F, 0x6D, 0xA3, 0x77, 0x2B + .modulus = { 0xAD, 0x50, 0x5B, 0xB6, 0xC6, 0x7E, 0x2E, 0x5B, 0xDD, 0x6A, 0x3B, 0xEC, 0x43, 0xD9, 0x10, 0xC7, 0x72, 0xE9, 0xCC, 0x29, 0x0D, 0xA5, 0x85, 0x88, 0xB7, 0x7D, 0xCC, 0x11, 0x68, 0x0B, 0xB3, 0xE2, 0x9F, 0x4E, 0xAB, 0xBB, 0x26, 0xE9, 0x8C, 0x26, 0x01, 0x98, 0x5C, 0x04, 0x1B, 0xB1, 0x43, 0x78, 0xE6, 0x89, 0x18, 0x1A, 0xAD, 0x77, 0x05, 0x68, 0xE9, 0x28, 0xA2, 0xB9, 0x81, 0x67, 0xEE, 0x3E, 0x10, 0xD0, 0x72, 0xBE, 0xEF, 0x1F, 0xA2, 0x2F, 0xA2, 0xAA, 0x3E, 0x13, 0xF1, 0x1E, 0x18, 0x36, 0xA9, 0x2A, 0x42, 0x81, 0xEF, 0x70, 0xAA, 0xF4, 0xE4, 0x62, 0x99, 0x82, 0x21, 0xC6, 0xFB, 0xB9, 0xBD, 0xD0, 0x17, 0xE6, 0xAC, 0x59, 0x04, 0x94, 0xE9, 0xCE, 0xA9, 0x85, 0x9C, 0xEB, 0x2D, 0x2A, 0x4C, 0x17, 0x66, 0xF2, 0xC3, 0x39, 0x12, 0xC5, 0x8F, 0x14, 0xA8, 0x03, 0xE3, 0x6F, 0xCC, 0xDC, 0xCC, 0xDC, 0x13, 0xFD, 0x7A, 0xE7, 0x7C, 0x7A, 0x78, 0xD9, 0x97, 0xE6, 0xAC, 0xC3, 0x55, 0x57, 0xE0, 0xD3, 0xE9, 0xEB, 0x64, 0xB4, 0x3C, 0x92, 0xF4, 0xC5, 0x0D, 0x67, 0xA6, 0x02, 0xDE, 0xB3, 0x91, 0xB0, 0x66, 0x61, 0xCD, 0x32, 0x88, 0x0B, 0xD6, 0x49, 0x12, 0xAF, 0x1C, 0xBC, 0xB7, 0x16, 0x2A, 0x06, 0xF0, 0x25, 0x65, 0xD3, 0xB0, 0xEC, 0xE4, 0xFC, 0xEC, 0xDD, 0xAE, 0x8A, 0x49, 0x34, 0xDB, 0x8E, 0xE6, 0x7F, 0x30, 0x17, 0x98, 0x62, 0x21, 0x15, 0x5D, 0x13, 0x1C, 0x6C, 0x3F, 0x09, 0xAB, 0x19, 0x45, 0xC2, 0x06, 0xAC, 0x70, 0xC9, 0x42, 0xB3, 0x6F, 0x49, 0xA1, 0x18, 0x3B, 0xCD, 0x78, 0xB6, 0xE4, 0xB4, 0x7C, 0x6C, 0x5C, 0xAC, 0x0F, 0x8D, 0x62, 0xF8, 0x97, 0xC6, 0x95, 0x3D, 0xD1, 0x2F, 0x28, 0xB7, 0x0C, 0x5B, 0x7D, 0xF7, 0x51, 0x81, 0x9A, 0x98, 0x34, 0x65, 0x26, 0x25 }, + .priv_exponent = { 0 } }; -static const unsigned char xsC_ppki_rsa_priv[256] = +static const CtrRsa2048Key prod_ncsd_cfa_rsa = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + .modulus = { 0xFB, 0xDE, 0xB8, 0x2B, 0x40, 0x93, 0x0F, 0xF6, 0xB1, 0x9A, 0x08, 0x06, 0x1B, 0x86, 0xFE, 0xD0, 0xDF, 0x10, 0x79, 0x17, 0x3D, 0x8C, 0xE2, 0x7A, 0xCE, 0x8F, 0x23, 0x45, 0xB9, 0x0A, 0x6D, 0xED, 0x30, 0x0E, 0xC1, 0xA8, 0x92, 0xC4, 0xBD, 0x1A, 0xCE, 0xA7, 0xAC, 0x77, 0xAA, 0x47, 0xE5, 0x20, 0x4A, 0x44, 0x91, 0xDF, 0x1C, 0xFE, 0x86, 0x28, 0x12, 0x2D, 0x66, 0xDF, 0xBE, 0xAD, 0x96, 0x61, 0xED, 0xF2, 0xF7, 0x41, 0x7B, 0x57, 0x88, 0x6B, 0x24, 0x1E, 0x7D, 0xEC, 0xBE, 0x98, 0x65, 0x65, 0x36, 0x65, 0x99, 0xA9, 0xFE, 0x24, 0x67, 0x85, 0x99, 0xEE, 0x2A, 0xAE, 0xEE, 0xB1, 0x81, 0x1A, 0x22, 0xE3, 0x6D, 0x75, 0x6E, 0x21, 0xBC, 0xEF, 0x11, 0x5C, 0x61, 0xAF, 0x0C, 0x30, 0x00, 0xB6, 0xA2, 0x23, 0xED, 0xFE, 0x70, 0x15, 0xDA, 0x52, 0xE1, 0xE6, 0x2D, 0xCE, 0x34, 0xE8, 0xAA, 0x4C, 0xF1, 0xD6, 0x67, 0x56, 0x57, 0xD3, 0xDB, 0xC0, 0x90, 0x49, 0x6F, 0x45, 0x73, 0x93, 0x4E, 0x30, 0x30, 0x70, 0xF5, 0xC9, 0x8F, 0x31, 0x25, 0xF2, 0xC2, 0xE7, 0x33, 0x7F, 0x4E, 0xB6, 0xF5, 0x2A, 0xDF, 0x20, 0x00, 0xE5, 0x79, 0xB2, 0xD0, 0xF9, 0x17, 0xF7, 0x7E, 0x16, 0x90, 0x40, 0x00, 0x57, 0x91, 0x44, 0x78, 0xEF, 0x1C, 0xE0, 0x85, 0x09, 0xDA, 0xF4, 0x14, 0x7E, 0x4B, 0xD7, 0x35, 0xD6, 0x87, 0x54, 0x8F, 0x2A, 0xB5, 0xA7, 0x6F, 0x50, 0xD0, 0xF7, 0xD1, 0xF1, 0x19, 0xC9, 0xAC, 0x22, 0x7E, 0x05, 0x11, 0xF5, 0xF2, 0x6D, 0xEE, 0x92, 0x27, 0x57, 0x5F, 0xE5, 0x15, 0x0D, 0x27, 0x68, 0xBF, 0x52, 0x65, 0x74, 0x73, 0xA6, 0x58, 0x6D, 0x79, 0x18, 0xAC, 0x31, 0xDD, 0xDD, 0x80, 0x8B, 0x75, 0x24, 0xE1, 0x17, 0xE1, 0x95, 0x25, 0x16, 0x29, 0xAB, 0x69, 0x69, 0xC8, 0x28, 0xEE, 0x5D }, + .priv_exponent = { 0 } }; -static const unsigned char xsC_ppki_rsa_pub[256] = +static const CtrRsa2048Key prod_accessdesc_rsa = { - 0xAD, 0x50, 0x5B, 0xB6, 0xC6, 0x7E, 0x2E, 0x5B, - 0xDD, 0x6A, 0x3B, 0xEC, 0x43, 0xD9, 0x10, 0xC7, - 0x72, 0xE9, 0xCC, 0x29, 0x0D, 0xA5, 0x85, 0x88, - 0xB7, 0x7D, 0xCC, 0x11, 0x68, 0x0B, 0xB3, 0xE2, - 0x9F, 0x4E, 0xAB, 0xBB, 0x26, 0xE9, 0x8C, 0x26, - 0x01, 0x98, 0x5C, 0x04, 0x1B, 0xB1, 0x43, 0x78, - 0xE6, 0x89, 0x18, 0x1A, 0xAD, 0x77, 0x05, 0x68, - 0xE9, 0x28, 0xA2, 0xB9, 0x81, 0x67, 0xEE, 0x3E, - 0x10, 0xD0, 0x72, 0xBE, 0xEF, 0x1F, 0xA2, 0x2F, - 0xA2, 0xAA, 0x3E, 0x13, 0xF1, 0x1E, 0x18, 0x36, - 0xA9, 0x2A, 0x42, 0x81, 0xEF, 0x70, 0xAA, 0xF4, - 0xE4, 0x62, 0x99, 0x82, 0x21, 0xC6, 0xFB, 0xB9, - 0xBD, 0xD0, 0x17, 0xE6, 0xAC, 0x59, 0x04, 0x94, - 0xE9, 0xCE, 0xA9, 0x85, 0x9C, 0xEB, 0x2D, 0x2A, - 0x4C, 0x17, 0x66, 0xF2, 0xC3, 0x39, 0x12, 0xC5, - 0x8F, 0x14, 0xA8, 0x03, 0xE3, 0x6F, 0xCC, 0xDC, - 0xCC, 0xDC, 0x13, 0xFD, 0x7A, 0xE7, 0x7C, 0x7A, - 0x78, 0xD9, 0x97, 0xE6, 0xAC, 0xC3, 0x55, 0x57, - 0xE0, 0xD3, 0xE9, 0xEB, 0x64, 0xB4, 0x3C, 0x92, - 0xF4, 0xC5, 0x0D, 0x67, 0xA6, 0x02, 0xDE, 0xB3, - 0x91, 0xB0, 0x66, 0x61, 0xCD, 0x32, 0x88, 0x0B, - 0xD6, 0x49, 0x12, 0xAF, 0x1C, 0xBC, 0xB7, 0x16, - 0x2A, 0x06, 0xF0, 0x25, 0x65, 0xD3, 0xB0, 0xEC, - 0xE4, 0xFC, 0xEC, 0xDD, 0xAE, 0x8A, 0x49, 0x34, - 0xDB, 0x8E, 0xE6, 0x7F, 0x30, 0x17, 0x98, 0x62, - 0x21, 0x15, 0x5D, 0x13, 0x1C, 0x6C, 0x3F, 0x09, - 0xAB, 0x19, 0x45, 0xC2, 0x06, 0xAC, 0x70, 0xC9, - 0x42, 0xB3, 0x6F, 0x49, 0xA1, 0x18, 0x3B, 0xCD, - 0x78, 0xB6, 0xE4, 0xB4, 0x7C, 0x6C, 0x5C, 0xAC, - 0x0F, 0x8D, 0x62, 0xF8, 0x97, 0xC6, 0x95, 0x3D, - 0xD1, 0x2F, 0x28, 0xB7, 0x0C, 0x5B, 0x7D, 0xF7, - 0x51, 0x81, 0x9A, 0x98, 0x34, 0x65, 0x26, 0x25 + .modulus = { 0xB1, 0xE3, 0xE3, 0x5F, 0x01, 0x39, 0x80, 0xD1, 0x56, 0x78, 0x9D, 0xB7, 0x06, 0xF7, 0x1D, 0xBF, 0x3E, 0x22, 0x76, 0xED, 0xF9, 0x5D, 0xA2, 0x36, 0xB6, 0x30, 0x61, 0x05, 0x96, 0xD3, 0x00, 0xB9, 0xED, 0xF1, 0xD7, 0xE0, 0x1D, 0xA0, 0x4F, 0xB7, 0xCF, 0x5A, 0x19, 0x87, 0x75, 0x49, 0x88, 0x40, 0xED, 0xE3, 0x6F, 0x7C, 0x90, 0x4A, 0x64, 0x45, 0x98, 0xD7, 0x04, 0xB9, 0x5A, 0x6B, 0x45, 0xAA, 0x7E, 0x94, 0xC0, 0xB3, 0xB7, 0xDB, 0x7B, 0x66, 0x59, 0x20, 0xB7, 0x08, 0xE2, 0xF3, 0x83, 0xA3, 0x7F, 0xE3, 0x20, 0x21, 0xA0, 0xEB, 0xB7, 0x28, 0x0F, 0xF3, 0x2B, 0x15, 0xA4, 0xC9, 0xD0, 0xAB, 0x89, 0x39, 0x99, 0x7E, 0x76, 0x5F, 0x9E, 0x4D, 0x1E, 0x01, 0x22, 0x8D, 0x74, 0xA6, 0xEB, 0x9A, 0xA3, 0x9D, 0x45, 0xE5, 0x10, 0x61, 0x6E, 0x20, 0xFD, 0x23, 0x75, 0xC0, 0xC5, 0x05, 0x03, 0xC5, 0x4C, 0x02, 0x4F, 0x54, 0x4B, 0x57, 0x08, 0xB4, 0x46, 0xC3, 0x2C, 0xF1, 0xF9, 0x52, 0x6C, 0xCD, 0x14, 0x55, 0xA8, 0x55, 0x92, 0x6D, 0xE2, 0x4A, 0x41, 0x46, 0xEB, 0x08, 0xC5, 0xF3, 0xB4, 0x8D, 0x0D, 0x5E, 0x21, 0xEA, 0xAF, 0x4D, 0x27, 0x4D, 0xDE, 0x77, 0x93, 0x97, 0xE2, 0xC7, 0x6B, 0x66, 0x1F, 0xDB, 0x2D, 0x6E, 0xA9, 0x5F, 0x61, 0x14, 0x17, 0x7B, 0x2B, 0x66, 0x5A, 0xB5, 0x01, 0x89, 0xF2, 0x23, 0x75, 0x25, 0x25, 0x9C, 0x86, 0x9A, 0x89, 0xFF, 0x64, 0x1D, 0x5B, 0xCE, 0xD7, 0x7E, 0x3F, 0x2D, 0xA8, 0xDA, 0xB5, 0x5A, 0xC5, 0x5F, 0x59, 0x20, 0xB0, 0xED, 0x1C, 0x91, 0xFF, 0xA3, 0x27, 0xB8, 0x8E, 0xCF, 0x82, 0x15, 0xE5, 0x49, 0xEF, 0xE4, 0x58, 0xE1, 0x5F, 0x8F, 0x53, 0xB9, 0x33, 0x2A, 0x56, 0x24, 0xAA, 0xA1, 0xD3, 0x6E, 0x47, 0x1A, 0x63, 0x44, 0x19, 0xB3, 0x8E, 0xA5 }, + .priv_exponent = { 0 } }; -static const unsigned char prod_acex_pub[0x100] = +static const CtrRsa2048Key prod_firm_rsa = { - 0xB1, 0xE3, 0xE3, 0x5F, 0x01, 0x39, 0x80, 0xD1, - 0x56, 0x78, 0x9D, 0xB7, 0x06, 0xF7, 0x1D, 0xBF, - 0x3E, 0x22, 0x76, 0xED, 0xF9, 0x5D, 0xA2, 0x36, - 0xB6, 0x30, 0x61, 0x05, 0x96, 0xD3, 0x00, 0xB9, - 0xED, 0xF1, 0xD7, 0xE0, 0x1D, 0xA0, 0x4F, 0xB7, - 0xCF, 0x5A, 0x19, 0x87, 0x75, 0x49, 0x88, 0x40, - 0xED, 0xE3, 0x6F, 0x7C, 0x90, 0x4A, 0x64, 0x45, - 0x98, 0xD7, 0x04, 0xB9, 0x5A, 0x6B, 0x45, 0xAA, - 0x7E, 0x94, 0xC0, 0xB3, 0xB7, 0xDB, 0x7B, 0x66, - 0x59, 0x20, 0xB7, 0x08, 0xE2, 0xF3, 0x83, 0xA3, - 0x7F, 0xE3, 0x20, 0x21, 0xA0, 0xEB, 0xB7, 0x28, - 0x0F, 0xF3, 0x2B, 0x15, 0xA4, 0xC9, 0xD0, 0xAB, - 0x89, 0x39, 0x99, 0x7E, 0x76, 0x5F, 0x9E, 0x4D, - 0x1E, 0x01, 0x22, 0x8D, 0x74, 0xA6, 0xEB, 0x9A, - 0xA3, 0x9D, 0x45, 0xE5, 0x10, 0x61, 0x6E, 0x20, - 0xFD, 0x23, 0x75, 0xC0, 0xC5, 0x05, 0x03, 0xC5, - 0x4C, 0x02, 0x4F, 0x54, 0x4B, 0x57, 0x08, 0xB4, - 0x46, 0xC3, 0x2C, 0xF1, 0xF9, 0x52, 0x6C, 0xCD, - 0x14, 0x55, 0xA8, 0x55, 0x92, 0x6D, 0xE2, 0x4A, - 0x41, 0x46, 0xEB, 0x08, 0xC5, 0xF3, 0xB4, 0x8D, - 0x0D, 0x5E, 0x21, 0xEA, 0xAF, 0x4D, 0x27, 0x4D, - 0xDE, 0x77, 0x93, 0x97, 0xE2, 0xC7, 0x6B, 0x66, - 0x1F, 0xDB, 0x2D, 0x6E, 0xA9, 0x5F, 0x61, 0x14, - 0x17, 0x7B, 0x2B, 0x66, 0x5A, 0xB5, 0x01, 0x89, - 0xF2, 0x23, 0x75, 0x25, 0x25, 0x9C, 0x86, 0x9A, - 0x89, 0xFF, 0x64, 0x1D, 0x5B, 0xCE, 0xD7, 0x7E, - 0x3F, 0x2D, 0xA8, 0xDA, 0xB5, 0x5A, 0xC5, 0x5F, - 0x59, 0x20, 0xB0, 0xED, 0x1C, 0x91, 0xFF, 0xA3, - 0x27, 0xB8, 0x8E, 0xCF, 0x82, 0x15, 0xE5, 0x49, - 0xEF, 0xE4, 0x58, 0xE1, 0x5F, 0x8F, 0x53, 0xB9, - 0x33, 0x2A, 0x56, 0x24, 0xAA, 0xA1, 0xD3, 0x6E, - 0x47, 0x1A, 0x63, 0x44, 0x19, 0xB3, 0x8E, 0xA5 -}; - -static const unsigned char prod_acex_priv[256] = -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static const unsigned char prod_ncsd_cfa_pub[0x100] = -{ - 0xFB, 0xDE, 0xB8, 0x2B, 0x40, 0x93, 0x0F, 0xF6, - 0xB1, 0x9A, 0x08, 0x06, 0x1B, 0x86, 0xFE, 0xD0, - 0xDF, 0x10, 0x79, 0x17, 0x3D, 0x8C, 0xE2, 0x7A, - 0xCE, 0x8F, 0x23, 0x45, 0xB9, 0x0A, 0x6D, 0xED, - 0x30, 0x0E, 0xC1, 0xA8, 0x92, 0xC4, 0xBD, 0x1A, - 0xCE, 0xA7, 0xAC, 0x77, 0xAA, 0x47, 0xE5, 0x20, - 0x4A, 0x44, 0x91, 0xDF, 0x1C, 0xFE, 0x86, 0x28, - 0x12, 0x2D, 0x66, 0xDF, 0xBE, 0xAD, 0x96, 0x61, - 0xED, 0xF2, 0xF7, 0x41, 0x7B, 0x57, 0x88, 0x6B, - 0x24, 0x1E, 0x7D, 0xEC, 0xBE, 0x98, 0x65, 0x65, - 0x36, 0x65, 0x99, 0xA9, 0xFE, 0x24, 0x67, 0x85, - 0x99, 0xEE, 0x2A, 0xAE, 0xEE, 0xB1, 0x81, 0x1A, - 0x22, 0xE3, 0x6D, 0x75, 0x6E, 0x21, 0xBC, 0xEF, - 0x11, 0x5C, 0x61, 0xAF, 0x0C, 0x30, 0x00, 0xB6, - 0xA2, 0x23, 0xED, 0xFE, 0x70, 0x15, 0xDA, 0x52, - 0xE1, 0xE6, 0x2D, 0xCE, 0x34, 0xE8, 0xAA, 0x4C, - 0xF1, 0xD6, 0x67, 0x56, 0x57, 0xD3, 0xDB, 0xC0, - 0x90, 0x49, 0x6F, 0x45, 0x73, 0x93, 0x4E, 0x30, - 0x30, 0x70, 0xF5, 0xC9, 0x8F, 0x31, 0x25, 0xF2, - 0xC2, 0xE7, 0x33, 0x7F, 0x4E, 0xB6, 0xF5, 0x2A, - 0xDF, 0x20, 0x00, 0xE5, 0x79, 0xB2, 0xD0, 0xF9, - 0x17, 0xF7, 0x7E, 0x16, 0x90, 0x40, 0x00, 0x57, - 0x91, 0x44, 0x78, 0xEF, 0x1C, 0xE0, 0x85, 0x09, - 0xDA, 0xF4, 0x14, 0x7E, 0x4B, 0xD7, 0x35, 0xD6, - 0x87, 0x54, 0x8F, 0x2A, 0xB5, 0xA7, 0x6F, 0x50, - 0xD0, 0xF7, 0xD1, 0xF1, 0x19, 0xC9, 0xAC, 0x22, - 0x7E, 0x05, 0x11, 0xF5, 0xF2, 0x6D, 0xEE, 0x92, - 0x27, 0x57, 0x5F, 0xE5, 0x15, 0x0D, 0x27, 0x68, - 0xBF, 0x52, 0x65, 0x74, 0x73, 0xA6, 0x58, 0x6D, - 0x79, 0x18, 0xAC, 0x31, 0xDD, 0xDD, 0x80, 0x8B, - 0x75, 0x24, 0xE1, 0x17, 0xE1, 0x95, 0x25, 0x16, - 0x29, 0xAB, 0x69, 0x69, 0xC8, 0x28, 0xEE, 0x5D -}; - -static const unsigned char prod_ncsd_cfa_priv[256] = -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static const unsigned char prod_firm_pub[0x100] = -{ - 0xDE, 0xCF, 0xB6, 0xFC, 0x3D, 0x33, 0xE9, 0x55, - 0xFD, 0xAC, 0x90, 0xE8, 0x88, 0x17, 0xB0, 0x03, - 0xA1, 0x6B, 0x9A, 0xAB, 0x72, 0x70, 0x79, 0x32, - 0xA2, 0xA0, 0x8C, 0xBB, 0x33, 0x6F, 0xB0, 0x76, - 0x96, 0x2E, 0xC4, 0xE9, 0x2E, 0xD8, 0x8F, 0x92, - 0xC0, 0x2D, 0x4D, 0x41, 0x0F, 0xDE, 0x45, 0x1B, - 0x25, 0x3C, 0xBE, 0x37, 0x6B, 0x45, 0x82, 0x21, - 0xE6, 0x4D, 0xB1, 0x23, 0x81, 0x82, 0xB6, 0x81, - 0x62, 0xB7, 0x30, 0xF4, 0x60, 0x4B, 0xC7, 0xF7, - 0xF0, 0x17, 0x0C, 0xB5, 0x75, 0x88, 0x77, 0x93, - 0x52, 0x63, 0x70, 0xF0, 0x0B, 0xC6, 0x73, 0x43, - 0x41, 0xEE, 0xE4, 0xF0, 0x71, 0xEC, 0xC8, 0xC1, - 0x32, 0xC4, 0xDC, 0xA9, 0x99, 0x1D, 0x31, 0xB8, - 0xA4, 0x7E, 0xDD, 0x19, 0x04, 0x0F, 0x02, 0xA8, - 0x1A, 0xAF, 0xB3, 0x48, 0x9A, 0x29, 0x29, 0x5E, - 0x49, 0x84, 0xE0, 0x94, 0x11, 0xD1, 0x7E, 0xAB, - 0xB2, 0xC0, 0x44, 0x7E, 0xA1, 0x1B, 0x5E, 0x9D, - 0x0D, 0x1A, 0xF9, 0x02, 0x9A, 0x2E, 0x53, 0x03, - 0x2D, 0x48, 0x96, 0x7C, 0x2C, 0xA6, 0xD7, 0xAC, - 0xF1, 0xED, 0x2B, 0x18, 0xBB, 0x01, 0xCB, 0x13, - 0xB9, 0xAC, 0xA6, 0xEE, 0x55, 0x00, 0x37, 0x7C, - 0x69, 0x61, 0x62, 0x89, 0x01, 0x54, 0x77, 0x9F, - 0x07, 0x5D, 0x26, 0x34, 0x3A, 0xA9, 0x49, 0xA5, - 0xAF, 0xF2, 0x5E, 0x06, 0x51, 0xB7, 0x1C, 0xE0, - 0xDE, 0xDA, 0x5C, 0x0B, 0x9F, 0x98, 0xC2, 0x15, - 0xFD, 0xBA, 0xD8, 0xA9, 0x99, 0x00, 0xAB, 0xA4, - 0x8E, 0x4A, 0x16, 0x9D, 0x66, 0x2A, 0xE8, 0x56, - 0x64, 0xB2, 0xB6, 0xC0, 0x93, 0xAF, 0x4D, 0x38, - 0xA0, 0x16, 0x5C, 0xE4, 0xBD, 0x62, 0xC2, 0x46, - 0x6B, 0xC9, 0x5A, 0x59, 0x4A, 0x72, 0x58, 0xFD, - 0xB2, 0xCC, 0x36, 0x87, 0x30, 0x85, 0xE8, 0xA1, - 0x04, 0x5B, 0xE0, 0x17, 0x9B, 0xD0, 0xEC, 0x9B + .modulus = { 0xDE, 0xCF, 0xB6, 0xFC, 0x3D, 0x33, 0xE9, 0x55, 0xFD, 0xAC, 0x90, 0xE8, 0x88, 0x17, 0xB0, 0x03, 0xA1, 0x6B, 0x9A, 0xAB, 0x72, 0x70, 0x79, 0x32, 0xA2, 0xA0, 0x8C, 0xBB, 0x33, 0x6F, 0xB0, 0x76, 0x96, 0x2E, 0xC4, 0xE9, 0x2E, 0xD8, 0x8F, 0x92, 0xC0, 0x2D, 0x4D, 0x41, 0x0F, 0xDE, 0x45, 0x1B, 0x25, 0x3C, 0xBE, 0x37, 0x6B, 0x45, 0x82, 0x21, 0xE6, 0x4D, 0xB1, 0x23, 0x81, 0x82, 0xB6, 0x81, 0x62, 0xB7, 0x30, 0xF4, 0x60, 0x4B, 0xC7, 0xF7, 0xF0, 0x17, 0x0C, 0xB5, 0x75, 0x88, 0x77, 0x93, 0x52, 0x63, 0x70, 0xF0, 0x0B, 0xC6, 0x73, 0x43, 0x41, 0xEE, 0xE4, 0xF0, 0x71, 0xEC, 0xC8, 0xC1, 0x32, 0xC4, 0xDC, 0xA9, 0x99, 0x1D, 0x31, 0xB8, 0xA4, 0x7E, 0xDD, 0x19, 0x04, 0x0F, 0x02, 0xA8, 0x1A, 0xAF, 0xB3, 0x48, 0x9A, 0x29, 0x29, 0x5E, 0x49, 0x84, 0xE0, 0x94, 0x11, 0xD1, 0x7E, 0xAB, 0xB2, 0xC0, 0x44, 0x7E, 0xA1, 0x1B, 0x5E, 0x9D, 0x0D, 0x1A, 0xF9, 0x02, 0x9A, 0x2E, 0x53, 0x03, 0x2D, 0x48, 0x96, 0x7C, 0x2C, 0xA6, 0xD7, 0xAC, 0xF1, 0xED, 0x2B, 0x18, 0xBB, 0x01, 0xCB, 0x13, 0xB9, 0xAC, 0xA6, 0xEE, 0x55, 0x00, 0x37, 0x7C, 0x69, 0x61, 0x62, 0x89, 0x01, 0x54, 0x77, 0x9F, 0x07, 0x5D, 0x26, 0x34, 0x3A, 0xA9, 0x49, 0xA5, 0xAF, 0xF2, 0x5E, 0x06, 0x51, 0xB7, 0x1C, 0xE0, 0xDE, 0xDA, 0x5C, 0x0B, 0x9F, 0x98, 0xC2, 0x15, 0xFD, 0xBA, 0xD8, 0xA9, 0x99, 0x00, 0xAB, 0xA4, 0x8E, 0x4A, 0x16, 0x9D, 0x66, 0x2A, 0xE8, 0x56, 0x64, 0xB2, 0xB6, 0xC0, 0x93, 0xAF, 0x4D, 0x38, 0xA0, 0x16, 0x5C, 0xE4, 0xBD, 0x62, 0xC2, 0x46, 0x6B, 0xC9, 0x5A, 0x59, 0x4A, 0x72, 0x58, 0xFD, 0xB2, 0xCC, 0x36, 0x87, 0x30, 0x85, 0xE8, 0xA1, 0x04, 0x5B, 0xE0, 0x17, 0x9B, 0xD0, 0xEC, 0x9B }, + .priv_exponent = { 0 } }; //Certificates static const unsigned char ca3_ppki_cert[0x400] = { - 0x00, 0x01, 0x00, 0x03, 0x70, 0x41, 0x38, 0xEF, - 0xBB, 0xBD, 0xA1, 0x6A, 0x98, 0x7D, 0xD9, 0x01, - 0x32, 0x6D, 0x1C, 0x94, 0x59, 0x48, 0x4C, 0x88, - 0xA2, 0x86, 0x1B, 0x91, 0xA3, 0x12, 0x58, 0x7A, - 0xE7, 0x0E, 0xF6, 0x23, 0x7E, 0xC5, 0x0E, 0x10, - 0x32, 0xDC, 0x39, 0xDD, 0xE8, 0x9A, 0x96, 0xA8, - 0xE8, 0x59, 0xD7, 0x6A, 0x98, 0xA6, 0xE7, 0xE3, - 0x6A, 0x0C, 0xFE, 0x35, 0x2C, 0xA8, 0x93, 0x05, - 0x82, 0x34, 0xFF, 0x83, 0x3F, 0xCB, 0x3B, 0x03, - 0x81, 0x1E, 0x9F, 0x0D, 0xC0, 0xD9, 0xA5, 0x2F, - 0x80, 0x45, 0xB4, 0xB2, 0xF9, 0x41, 0x1B, 0x67, - 0xA5, 0x1C, 0x44, 0xB5, 0xEF, 0x8C, 0xE7, 0x7B, - 0xD6, 0xD5, 0x6B, 0xA7, 0x57, 0x34, 0xA1, 0x85, - 0x6D, 0xE6, 0xD4, 0xBE, 0xD6, 0xD3, 0xA2, 0x42, - 0xC7, 0xC8, 0x79, 0x1B, 0x34, 0x22, 0x37, 0x5E, - 0x5C, 0x77, 0x9A, 0xBF, 0x07, 0x2F, 0x76, 0x95, - 0xEF, 0xA0, 0xF7, 0x5B, 0xCB, 0x83, 0x78, 0x9F, - 0xC3, 0x0E, 0x3F, 0xE4, 0xCC, 0x83, 0x92, 0x20, - 0x78, 0x40, 0x63, 0x89, 0x49, 0xC7, 0xF6, 0x88, - 0x56, 0x5F, 0x64, 0x9B, 0x74, 0xD6, 0x3D, 0x8D, - 0x58, 0xFF, 0xAD, 0xDA, 0x57, 0x1E, 0x95, 0x54, - 0x42, 0x6B, 0x13, 0x18, 0xFC, 0x46, 0x89, 0x83, - 0xD4, 0xC8, 0xA5, 0x62, 0x8B, 0x06, 0xB6, 0xFC, - 0x5D, 0x50, 0x7C, 0x13, 0xE7, 0xA1, 0x8A, 0xC1, - 0x51, 0x1E, 0xB6, 0xD6, 0x2E, 0xA5, 0x44, 0x8F, - 0x83, 0x50, 0x14, 0x47, 0xA9, 0xAF, 0xB3, 0xEC, - 0xC2, 0x90, 0x3C, 0x9D, 0xD5, 0x2F, 0x92, 0x2A, - 0xC9, 0xAC, 0xDB, 0xEF, 0x58, 0xC6, 0x02, 0x18, - 0x48, 0xD9, 0x6E, 0x20, 0x87, 0x32, 0xD3, 0xD1, - 0xD9, 0xD9, 0xEA, 0x44, 0x0D, 0x91, 0x62, 0x1C, - 0x7A, 0x99, 0xDB, 0x88, 0x43, 0xC5, 0x9C, 0x1F, - 0x2E, 0x2C, 0x7D, 0x9B, 0x57, 0x7D, 0x51, 0x2C, - 0x16, 0x6D, 0x6F, 0x7E, 0x1A, 0xAD, 0x4A, 0x77, - 0x4A, 0x37, 0x44, 0x7E, 0x78, 0xFE, 0x20, 0x21, - 0xE1, 0x4A, 0x95, 0xD1, 0x12, 0xA0, 0x68, 0xAD, - 0xA0, 0x19, 0xF4, 0x63, 0xC7, 0xA5, 0x56, 0x85, - 0xAA, 0xBB, 0x68, 0x88, 0xB9, 0x24, 0x64, 0x83, - 0xD1, 0x8B, 0x9C, 0x80, 0x6F, 0x47, 0x49, 0x18, - 0x33, 0x17, 0x82, 0x34, 0x4A, 0x4B, 0x85, 0x31, - 0x33, 0x4B, 0x26, 0x30, 0x32, 0x63, 0xD9, 0xD2, - 0xEB, 0x4F, 0x4B, 0xB9, 0x96, 0x02, 0xB3, 0x52, - 0xF6, 0xAE, 0x40, 0x46, 0xC6, 0x9A, 0x5E, 0x7E, - 0x8E, 0x4A, 0x18, 0xEF, 0x9B, 0xC0, 0xA2, 0xDE, - 0xD6, 0x13, 0x10, 0x41, 0x70, 0x12, 0xFD, 0x82, - 0x4C, 0xC1, 0x16, 0xCF, 0xB7, 0xC4, 0xC1, 0xF7, - 0xEC, 0x71, 0x77, 0xA1, 0x74, 0x46, 0xCB, 0xDE, - 0x96, 0xF3, 0xED, 0xD8, 0x8F, 0xCD, 0x05, 0x2F, - 0x0B, 0x88, 0x8A, 0x45, 0xFD, 0xAF, 0x2B, 0x63, - 0x13, 0x54, 0xF4, 0x0D, 0x16, 0xE5, 0xFA, 0x9C, - 0x2C, 0x4E, 0xDA, 0x98, 0xE7, 0x98, 0xD1, 0x5E, - 0x60, 0x46, 0xDC, 0x53, 0x63, 0xF3, 0x09, 0x6B, - 0x2C, 0x60, 0x7A, 0x9D, 0x8D, 0xD5, 0x5B, 0x15, - 0x02, 0xA6, 0xAC, 0x7D, 0x3C, 0xC8, 0xD8, 0xC5, - 0x75, 0x99, 0x8E, 0x7D, 0x79, 0x69, 0x10, 0xC8, - 0x04, 0xC4, 0x95, 0x23, 0x50, 0x57, 0xE9, 0x1E, - 0xCD, 0x26, 0x37, 0xC9, 0xC1, 0x84, 0x51, 0x51, - 0xAC, 0x6B, 0x9A, 0x04, 0x90, 0xAE, 0x3E, 0xC6, - 0xF4, 0x77, 0x40, 0xA0, 0xDB, 0x0B, 0xA3, 0x6D, - 0x07, 0x59, 0x56, 0xCE, 0xE7, 0x35, 0x4E, 0xA3, - 0xE9, 0xA4, 0xF2, 0x72, 0x0B, 0x26, 0x55, 0x0C, - 0x7D, 0x39, 0x43, 0x24, 0xBC, 0x0C, 0xB7, 0xE9, - 0x31, 0x7D, 0x8A, 0x86, 0x61, 0xF4, 0x21, 0x91, - 0xFF, 0x10, 0xB0, 0x82, 0x56, 0xCE, 0x3F, 0xD2, - 0x5B, 0x74, 0x5E, 0x51, 0x94, 0x90, 0x6B, 0x4D, - 0x61, 0xCB, 0x4C, 0x2E, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x52, 0x6F, 0x6F, 0x74, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x43, 0x41, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7B, 0xE8, 0xEF, 0x6C, - 0xB2, 0x79, 0xC9, 0xE2, 0xEE, 0xE1, 0x21, 0xC6, - 0xEA, 0xF4, 0x4F, 0xF6, 0x39, 0xF8, 0x8F, 0x07, - 0x8B, 0x4B, 0x77, 0xED, 0x9F, 0x95, 0x60, 0xB0, - 0x35, 0x82, 0x81, 0xB5, 0x0E, 0x55, 0xAB, 0x72, - 0x11, 0x15, 0xA1, 0x77, 0x70, 0x3C, 0x7A, 0x30, - 0xFE, 0x3A, 0xE9, 0xEF, 0x1C, 0x60, 0xBC, 0x1D, - 0x97, 0x46, 0x76, 0xB2, 0x3A, 0x68, 0xCC, 0x04, - 0xB1, 0x98, 0x52, 0x5B, 0xC9, 0x68, 0xF1, 0x1D, - 0xE2, 0xDB, 0x50, 0xE4, 0xD9, 0xE7, 0xF0, 0x71, - 0xE5, 0x62, 0xDA, 0xE2, 0x09, 0x22, 0x33, 0xE9, - 0xD3, 0x63, 0xF6, 0x1D, 0xD7, 0xC1, 0x9F, 0xF3, - 0xA4, 0xA9, 0x1E, 0x8F, 0x65, 0x53, 0xD4, 0x71, - 0xDD, 0x7B, 0x84, 0xB9, 0xF1, 0xB8, 0xCE, 0x73, - 0x35, 0xF0, 0xF5, 0x54, 0x05, 0x63, 0xA1, 0xEA, - 0xB8, 0x39, 0x63, 0xE0, 0x9B, 0xE9, 0x01, 0x01, - 0x1F, 0x99, 0x54, 0x63, 0x61, 0x28, 0x70, 0x20, - 0xE9, 0xCC, 0x0D, 0xAB, 0x48, 0x7F, 0x14, 0x0D, - 0x66, 0x26, 0xA1, 0x83, 0x6D, 0x27, 0x11, 0x1F, - 0x20, 0x68, 0xDE, 0x47, 0x72, 0x14, 0x91, 0x51, - 0xCF, 0x69, 0xC6, 0x1B, 0xA6, 0x0E, 0xF9, 0xD9, - 0x49, 0xA0, 0xF7, 0x1F, 0x54, 0x99, 0xF2, 0xD3, - 0x9A, 0xD2, 0x8C, 0x70, 0x05, 0x34, 0x82, 0x93, - 0xC4, 0x31, 0xFF, 0xBD, 0x33, 0xF6, 0xBC, 0xA6, - 0x0D, 0xC7, 0x19, 0x5E, 0xA2, 0xBC, 0xC5, 0x6D, - 0x20, 0x0B, 0xAF, 0x6D, 0x06, 0xD0, 0x9C, 0x41, - 0xDB, 0x8D, 0xE9, 0xC7, 0x20, 0x15, 0x4C, 0xA4, - 0x83, 0x2B, 0x69, 0xC0, 0x8C, 0x69, 0xCD, 0x3B, - 0x07, 0x3A, 0x00, 0x63, 0x60, 0x2F, 0x46, 0x2D, - 0x33, 0x80, 0x61, 0xA5, 0xEA, 0x6C, 0x91, 0x5C, - 0xD5, 0x62, 0x35, 0x79, 0xC3, 0xEB, 0x64, 0xCE, - 0x44, 0xEF, 0x58, 0x6D, 0x14, 0xBA, 0xAA, 0x88, - 0x34, 0x01, 0x9B, 0x3E, 0xEB, 0xEE, 0xD3, 0x79, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00, 0x01, 0x00, 0x03, 0x70, 0x41, 0x38, 0xEF, 0xBB, 0xBD, 0xA1, 0x6A, 0x98, 0x7D, 0xD9, 0x01, 0x32, 0x6D, 0x1C, 0x94, 0x59, 0x48, 0x4C, 0x88, 0xA2, 0x86, 0x1B, 0x91, 0xA3, 0x12, 0x58, 0x7A, 0xE7, 0x0E, 0xF6, 0x23, 0x7E, 0xC5, 0x0E, 0x10, 0x32, 0xDC, 0x39, 0xDD, 0xE8, 0x9A, 0x96, 0xA8, 0xE8, 0x59, 0xD7, 0x6A, 0x98, 0xA6, 0xE7, 0xE3, 0x6A, 0x0C, 0xFE, 0x35, 0x2C, 0xA8, 0x93, 0x05, 0x82, 0x34, 0xFF, 0x83, 0x3F, 0xCB, 0x3B, 0x03, 0x81, 0x1E, 0x9F, 0x0D, 0xC0, 0xD9, 0xA5, 0x2F, 0x80, 0x45, 0xB4, 0xB2, 0xF9, 0x41, 0x1B, 0x67, 0xA5, 0x1C, 0x44, 0xB5, 0xEF, 0x8C, 0xE7, 0x7B, 0xD6, 0xD5, 0x6B, 0xA7, 0x57, 0x34, 0xA1, 0x85, 0x6D, 0xE6, 0xD4, 0xBE, 0xD6, 0xD3, 0xA2, 0x42, 0xC7, 0xC8, 0x79, 0x1B, 0x34, 0x22, 0x37, 0x5E, 0x5C, 0x77, 0x9A, 0xBF, 0x07, 0x2F, 0x76, 0x95, 0xEF, 0xA0, 0xF7, 0x5B, 0xCB, 0x83, 0x78, 0x9F, 0xC3, 0x0E, 0x3F, 0xE4, 0xCC, 0x83, 0x92, 0x20, 0x78, 0x40, 0x63, 0x89, 0x49, 0xC7, 0xF6, 0x88, 0x56, 0x5F, 0x64, 0x9B, 0x74, 0xD6, 0x3D, 0x8D, 0x58, 0xFF, 0xAD, 0xDA, 0x57, 0x1E, 0x95, 0x54, 0x42, 0x6B, 0x13, 0x18, 0xFC, 0x46, 0x89, 0x83, 0xD4, 0xC8, 0xA5, 0x62, 0x8B, 0x06, 0xB6, 0xFC, 0x5D, 0x50, 0x7C, 0x13, 0xE7, 0xA1, 0x8A, 0xC1, 0x51, 0x1E, 0xB6, 0xD6, 0x2E, 0xA5, 0x44, 0x8F, 0x83, 0x50, 0x14, 0x47, 0xA9, 0xAF, 0xB3, 0xEC, 0xC2, 0x90, 0x3C, 0x9D, 0xD5, 0x2F, 0x92, 0x2A, 0xC9, 0xAC, 0xDB, 0xEF, 0x58, 0xC6, 0x02, 0x18, 0x48, 0xD9, 0x6E, 0x20, 0x87, 0x32, 0xD3, 0xD1, 0xD9, 0xD9, 0xEA, 0x44, 0x0D, 0x91, 0x62, 0x1C, 0x7A, 0x99, 0xDB, 0x88, 0x43, 0xC5, 0x9C, 0x1F, 0x2E, 0x2C, 0x7D, 0x9B, 0x57, 0x7D, 0x51, 0x2C, 0x16, 0x6D, 0x6F, 0x7E, 0x1A, 0xAD, 0x4A, 0x77, 0x4A, 0x37, 0x44, 0x7E, 0x78, 0xFE, 0x20, 0x21, 0xE1, 0x4A, 0x95, 0xD1, 0x12, 0xA0, 0x68, 0xAD, 0xA0, 0x19, 0xF4, 0x63, 0xC7, 0xA5, 0x56, 0x85, 0xAA, 0xBB, 0x68, 0x88, 0xB9, 0x24, 0x64, 0x83, 0xD1, 0x8B, 0x9C, 0x80, 0x6F, 0x47, 0x49, 0x18, 0x33, 0x17, 0x82, 0x34, 0x4A, 0x4B, 0x85, 0x31, 0x33, 0x4B, 0x26, 0x30, 0x32, 0x63, 0xD9, 0xD2, 0xEB, 0x4F, 0x4B, 0xB9, 0x96, 0x02, 0xB3, 0x52, 0xF6, 0xAE, 0x40, 0x46, 0xC6, 0x9A, 0x5E, 0x7E, 0x8E, 0x4A, 0x18, 0xEF, 0x9B, 0xC0, 0xA2, 0xDE, 0xD6, 0x13, 0x10, 0x41, 0x70, 0x12, 0xFD, 0x82, 0x4C, 0xC1, 0x16, 0xCF, 0xB7, 0xC4, 0xC1, 0xF7, 0xEC, 0x71, 0x77, 0xA1, 0x74, 0x46, 0xCB, 0xDE, 0x96, 0xF3, 0xED, 0xD8, 0x8F, 0xCD, 0x05, 0x2F, 0x0B, 0x88, 0x8A, 0x45, 0xFD, 0xAF, 0x2B, 0x63, 0x13, 0x54, 0xF4, 0x0D, 0x16, 0xE5, 0xFA, 0x9C, 0x2C, 0x4E, 0xDA, 0x98, 0xE7, 0x98, 0xD1, 0x5E, 0x60, 0x46, 0xDC, 0x53, 0x63, 0xF3, 0x09, 0x6B, 0x2C, 0x60, 0x7A, 0x9D, 0x8D, 0xD5, 0x5B, 0x15, 0x02, 0xA6, 0xAC, 0x7D, 0x3C, 0xC8, 0xD8, 0xC5, 0x75, 0x99, 0x8E, 0x7D, 0x79, 0x69, 0x10, 0xC8, 0x04, 0xC4, 0x95, 0x23, 0x50, 0x57, 0xE9, 0x1E, 0xCD, 0x26, 0x37, 0xC9, 0xC1, 0x84, 0x51, 0x51, 0xAC, 0x6B, 0x9A, 0x04, 0x90, 0xAE, 0x3E, 0xC6, 0xF4, 0x77, 0x40, 0xA0, 0xDB, 0x0B, 0xA3, 0x6D, 0x07, 0x59, 0x56, 0xCE, 0xE7, 0x35, 0x4E, 0xA3, 0xE9, 0xA4, 0xF2, 0x72, 0x0B, 0x26, 0x55, 0x0C, 0x7D, 0x39, 0x43, 0x24, 0xBC, 0x0C, 0xB7, 0xE9, 0x31, 0x7D, 0x8A, 0x86, 0x61, 0xF4, 0x21, 0x91, 0xFF, 0x10, 0xB0, 0x82, 0x56, 0xCE, 0x3F, 0xD2, 0x5B, 0x74, 0x5E, 0x51, 0x94, 0x90, 0x6B, 0x4D, 0x61, 0xCB, 0x4C, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x6F, 0x6F, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xE8, 0xEF, 0x6C, 0xB2, 0x79, 0xC9, 0xE2, 0xEE, 0xE1, 0x21, 0xC6, 0xEA, 0xF4, 0x4F, 0xF6, 0x39, 0xF8, 0x8F, 0x07, 0x8B, 0x4B, 0x77, 0xED, 0x9F, 0x95, 0x60, 0xB0, 0x35, 0x82, 0x81, 0xB5, 0x0E, 0x55, 0xAB, 0x72, 0x11, 0x15, 0xA1, 0x77, 0x70, 0x3C, 0x7A, 0x30, 0xFE, 0x3A, 0xE9, 0xEF, 0x1C, 0x60, 0xBC, 0x1D, 0x97, 0x46, 0x76, 0xB2, 0x3A, 0x68, 0xCC, 0x04, 0xB1, 0x98, 0x52, 0x5B, 0xC9, 0x68, 0xF1, 0x1D, 0xE2, 0xDB, 0x50, 0xE4, 0xD9, 0xE7, 0xF0, 0x71, 0xE5, 0x62, 0xDA, 0xE2, 0x09, 0x22, 0x33, 0xE9, 0xD3, 0x63, 0xF6, 0x1D, 0xD7, 0xC1, 0x9F, 0xF3, 0xA4, 0xA9, 0x1E, 0x8F, 0x65, 0x53, 0xD4, 0x71, 0xDD, 0x7B, 0x84, 0xB9, 0xF1, 0xB8, 0xCE, 0x73, 0x35, 0xF0, 0xF5, 0x54, 0x05, 0x63, 0xA1, 0xEA, 0xB8, 0x39, 0x63, 0xE0, 0x9B, 0xE9, 0x01, 0x01, 0x1F, 0x99, 0x54, 0x63, 0x61, 0x28, 0x70, 0x20, 0xE9, 0xCC, 0x0D, 0xAB, 0x48, 0x7F, 0x14, 0x0D, 0x66, 0x26, 0xA1, 0x83, 0x6D, 0x27, 0x11, 0x1F, 0x20, 0x68, 0xDE, 0x47, 0x72, 0x14, 0x91, 0x51, 0xCF, 0x69, 0xC6, 0x1B, 0xA6, 0x0E, 0xF9, 0xD9, 0x49, 0xA0, 0xF7, 0x1F, 0x54, 0x99, 0xF2, 0xD3, 0x9A, 0xD2, 0x8C, 0x70, 0x05, 0x34, 0x82, 0x93, 0xC4, 0x31, 0xFF, 0xBD, 0x33, 0xF6, 0xBC, 0xA6, 0x0D, 0xC7, 0x19, 0x5E, 0xA2, 0xBC, 0xC5, 0x6D, 0x20, 0x0B, 0xAF, 0x6D, 0x06, 0xD0, 0x9C, 0x41, 0xDB, 0x8D, 0xE9, 0xC7, 0x20, 0x15, 0x4C, 0xA4, 0x83, 0x2B, 0x69, 0xC0, 0x8C, 0x69, 0xCD, 0x3B, 0x07, 0x3A, 0x00, 0x63, 0x60, 0x2F, 0x46, 0x2D, 0x33, 0x80, 0x61, 0xA5, 0xEA, 0x6C, 0x91, 0x5C, 0xD5, 0x62, 0x35, 0x79, 0xC3, 0xEB, 0x64, 0xCE, 0x44, 0xEF, 0x58, 0x6D, 0x14, 0xBA, 0xAA, 0x88, 0x34, 0x01, 0x9B, 0x3E, 0xEB, 0xEE, 0xD3, 0x79, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char xsC_ppki_cert[0x300] = { - 0x00, 0x01, 0x00, 0x04, 0x91, 0x9E, 0xBE, 0x46, - 0x4A, 0xD0, 0xF5, 0x52, 0xCD, 0x1B, 0x72, 0xE7, - 0x88, 0x49, 0x10, 0xCF, 0x55, 0xA9, 0xF0, 0x2E, - 0x50, 0x78, 0x96, 0x41, 0xD8, 0x96, 0x68, 0x3D, - 0xC0, 0x05, 0xBD, 0x0A, 0xEA, 0x87, 0x07, 0x9D, - 0x8A, 0xC2, 0x84, 0xC6, 0x75, 0x06, 0x5F, 0x74, - 0xC8, 0xBF, 0x37, 0xC8, 0x80, 0x44, 0x40, 0x95, - 0x02, 0xA0, 0x22, 0x98, 0x0B, 0xB8, 0xAD, 0x48, - 0x38, 0x3F, 0x6D, 0x28, 0xA7, 0x9D, 0xE3, 0x96, - 0x26, 0xCC, 0xB2, 0xB2, 0x2A, 0x0F, 0x19, 0xE4, - 0x10, 0x32, 0xF0, 0x94, 0xB3, 0x9F, 0xF0, 0x13, - 0x31, 0x46, 0xDE, 0xC8, 0xF6, 0xC1, 0xA9, 0xD5, - 0x5C, 0xD2, 0x8D, 0x9E, 0x1C, 0x47, 0xB3, 0xD1, - 0x1F, 0x4F, 0x54, 0x26, 0xC2, 0xC7, 0x80, 0x13, - 0x5A, 0x27, 0x75, 0xD3, 0xCA, 0x67, 0x9B, 0xC7, - 0xE8, 0x34, 0xF0, 0xE0, 0xFB, 0x58, 0xE6, 0x88, - 0x60, 0xA7, 0x13, 0x30, 0xFC, 0x95, 0x79, 0x17, - 0x93, 0xC8, 0xFB, 0xA9, 0x35, 0xA7, 0xA6, 0x90, - 0x8F, 0x22, 0x9D, 0xEE, 0x2A, 0x0C, 0xA6, 0xB9, - 0xB2, 0x3B, 0x12, 0xD4, 0x95, 0xA6, 0xFE, 0x19, - 0xD0, 0xD7, 0x26, 0x48, 0x21, 0x68, 0x78, 0x60, - 0x5A, 0x66, 0x53, 0x8D, 0xBF, 0x37, 0x68, 0x99, - 0x90, 0x5D, 0x34, 0x45, 0xFC, 0x5C, 0x72, 0x7A, - 0x0E, 0x13, 0xE0, 0xE2, 0xC8, 0x97, 0x1C, 0x9C, - 0xFA, 0x6C, 0x60, 0x67, 0x88, 0x75, 0x73, 0x2A, - 0x4E, 0x75, 0x52, 0x3D, 0x2F, 0x56, 0x2F, 0x12, - 0xAA, 0xBD, 0x15, 0x73, 0xBF, 0x06, 0xC9, 0x40, - 0x54, 0xAE, 0xFA, 0x81, 0xA7, 0x14, 0x17, 0xAF, - 0x9A, 0x4A, 0x06, 0x6D, 0x0F, 0xFC, 0x5A, 0xD6, - 0x4B, 0xAB, 0x28, 0xB1, 0xFF, 0x60, 0x66, 0x1F, - 0x44, 0x37, 0xD4, 0x9E, 0x1E, 0x0D, 0x94, 0x12, - 0xEB, 0x4B, 0xCA, 0xCF, 0x4C, 0xFD, 0x6A, 0x34, - 0x08, 0x84, 0x79, 0x82, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x52, 0x6F, 0x6F, 0x74, 0x2D, 0x43, 0x41, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x58, 0x53, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x63, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x13, 0x7A, 0x08, 0x94, - 0xAD, 0x50, 0x5B, 0xB6, 0xC6, 0x7E, 0x2E, 0x5B, - 0xDD, 0x6A, 0x3B, 0xEC, 0x43, 0xD9, 0x10, 0xC7, - 0x72, 0xE9, 0xCC, 0x29, 0x0D, 0xA5, 0x85, 0x88, - 0xB7, 0x7D, 0xCC, 0x11, 0x68, 0x0B, 0xB3, 0xE2, - 0x9F, 0x4E, 0xAB, 0xBB, 0x26, 0xE9, 0x8C, 0x26, - 0x01, 0x98, 0x5C, 0x04, 0x1B, 0xB1, 0x43, 0x78, - 0xE6, 0x89, 0x18, 0x1A, 0xAD, 0x77, 0x05, 0x68, - 0xE9, 0x28, 0xA2, 0xB9, 0x81, 0x67, 0xEE, 0x3E, - 0x10, 0xD0, 0x72, 0xBE, 0xEF, 0x1F, 0xA2, 0x2F, - 0xA2, 0xAA, 0x3E, 0x13, 0xF1, 0x1E, 0x18, 0x36, - 0xA9, 0x2A, 0x42, 0x81, 0xEF, 0x70, 0xAA, 0xF4, - 0xE4, 0x62, 0x99, 0x82, 0x21, 0xC6, 0xFB, 0xB9, - 0xBD, 0xD0, 0x17, 0xE6, 0xAC, 0x59, 0x04, 0x94, - 0xE9, 0xCE, 0xA9, 0x85, 0x9C, 0xEB, 0x2D, 0x2A, - 0x4C, 0x17, 0x66, 0xF2, 0xC3, 0x39, 0x12, 0xC5, - 0x8F, 0x14, 0xA8, 0x03, 0xE3, 0x6F, 0xCC, 0xDC, - 0xCC, 0xDC, 0x13, 0xFD, 0x7A, 0xE7, 0x7C, 0x7A, - 0x78, 0xD9, 0x97, 0xE6, 0xAC, 0xC3, 0x55, 0x57, - 0xE0, 0xD3, 0xE9, 0xEB, 0x64, 0xB4, 0x3C, 0x92, - 0xF4, 0xC5, 0x0D, 0x67, 0xA6, 0x02, 0xDE, 0xB3, - 0x91, 0xB0, 0x66, 0x61, 0xCD, 0x32, 0x88, 0x0B, - 0xD6, 0x49, 0x12, 0xAF, 0x1C, 0xBC, 0xB7, 0x16, - 0x2A, 0x06, 0xF0, 0x25, 0x65, 0xD3, 0xB0, 0xEC, - 0xE4, 0xFC, 0xEC, 0xDD, 0xAE, 0x8A, 0x49, 0x34, - 0xDB, 0x8E, 0xE6, 0x7F, 0x30, 0x17, 0x98, 0x62, - 0x21, 0x15, 0x5D, 0x13, 0x1C, 0x6C, 0x3F, 0x09, - 0xAB, 0x19, 0x45, 0xC2, 0x06, 0xAC, 0x70, 0xC9, - 0x42, 0xB3, 0x6F, 0x49, 0xA1, 0x18, 0x3B, 0xCD, - 0x78, 0xB6, 0xE4, 0xB4, 0x7C, 0x6C, 0x5C, 0xAC, - 0x0F, 0x8D, 0x62, 0xF8, 0x97, 0xC6, 0x95, 0x3D, - 0xD1, 0x2F, 0x28, 0xB7, 0x0C, 0x5B, 0x7D, 0xF7, - 0x51, 0x81, 0x9A, 0x98, 0x34, 0x65, 0x26, 0x25, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00, 0x01, 0x00, 0x04, 0x91, 0x9E, 0xBE, 0x46, 0x4A, 0xD0, 0xF5, 0x52, 0xCD, 0x1B, 0x72, 0xE7, 0x88, 0x49, 0x10, 0xCF, 0x55, 0xA9, 0xF0, 0x2E, 0x50, 0x78, 0x96, 0x41, 0xD8, 0x96, 0x68, 0x3D, 0xC0, 0x05, 0xBD, 0x0A, 0xEA, 0x87, 0x07, 0x9D, 0x8A, 0xC2, 0x84, 0xC6, 0x75, 0x06, 0x5F, 0x74, 0xC8, 0xBF, 0x37, 0xC8, 0x80, 0x44, 0x40, 0x95, 0x02, 0xA0, 0x22, 0x98, 0x0B, 0xB8, 0xAD, 0x48, 0x38, 0x3F, 0x6D, 0x28, 0xA7, 0x9D, 0xE3, 0x96, 0x26, 0xCC, 0xB2, 0xB2, 0x2A, 0x0F, 0x19, 0xE4, 0x10, 0x32, 0xF0, 0x94, 0xB3, 0x9F, 0xF0, 0x13, 0x31, 0x46, 0xDE, 0xC8, 0xF6, 0xC1, 0xA9, 0xD5, 0x5C, 0xD2, 0x8D, 0x9E, 0x1C, 0x47, 0xB3, 0xD1, 0x1F, 0x4F, 0x54, 0x26, 0xC2, 0xC7, 0x80, 0x13, 0x5A, 0x27, 0x75, 0xD3, 0xCA, 0x67, 0x9B, 0xC7, 0xE8, 0x34, 0xF0, 0xE0, 0xFB, 0x58, 0xE6, 0x88, 0x60, 0xA7, 0x13, 0x30, 0xFC, 0x95, 0x79, 0x17, 0x93, 0xC8, 0xFB, 0xA9, 0x35, 0xA7, 0xA6, 0x90, 0x8F, 0x22, 0x9D, 0xEE, 0x2A, 0x0C, 0xA6, 0xB9, 0xB2, 0x3B, 0x12, 0xD4, 0x95, 0xA6, 0xFE, 0x19, 0xD0, 0xD7, 0x26, 0x48, 0x21, 0x68, 0x78, 0x60, 0x5A, 0x66, 0x53, 0x8D, 0xBF, 0x37, 0x68, 0x99, 0x90, 0x5D, 0x34, 0x45, 0xFC, 0x5C, 0x72, 0x7A, 0x0E, 0x13, 0xE0, 0xE2, 0xC8, 0x97, 0x1C, 0x9C, 0xFA, 0x6C, 0x60, 0x67, 0x88, 0x75, 0x73, 0x2A, 0x4E, 0x75, 0x52, 0x3D, 0x2F, 0x56, 0x2F, 0x12, 0xAA, 0xBD, 0x15, 0x73, 0xBF, 0x06, 0xC9, 0x40, 0x54, 0xAE, 0xFA, 0x81, 0xA7, 0x14, 0x17, 0xAF, 0x9A, 0x4A, 0x06, 0x6D, 0x0F, 0xFC, 0x5A, 0xD6, 0x4B, 0xAB, 0x28, 0xB1, 0xFF, 0x60, 0x66, 0x1F, 0x44, 0x37, 0xD4, 0x9E, 0x1E, 0x0D, 0x94, 0x12, 0xEB, 0x4B, 0xCA, 0xCF, 0x4C, 0xFD, 0x6A, 0x34, 0x08, 0x84, 0x79, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x6F, 0x6F, 0x74, 0x2D, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x58, 0x53, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x7A, 0x08, 0x94, 0xAD, 0x50, 0x5B, 0xB6, 0xC6, 0x7E, 0x2E, 0x5B, 0xDD, 0x6A, 0x3B, 0xEC, 0x43, 0xD9, 0x10, 0xC7, 0x72, 0xE9, 0xCC, 0x29, 0x0D, 0xA5, 0x85, 0x88, 0xB7, 0x7D, 0xCC, 0x11, 0x68, 0x0B, 0xB3, 0xE2, 0x9F, 0x4E, 0xAB, 0xBB, 0x26, 0xE9, 0x8C, 0x26, 0x01, 0x98, 0x5C, 0x04, 0x1B, 0xB1, 0x43, 0x78, 0xE6, 0x89, 0x18, 0x1A, 0xAD, 0x77, 0x05, 0x68, 0xE9, 0x28, 0xA2, 0xB9, 0x81, 0x67, 0xEE, 0x3E, 0x10, 0xD0, 0x72, 0xBE, 0xEF, 0x1F, 0xA2, 0x2F, 0xA2, 0xAA, 0x3E, 0x13, 0xF1, 0x1E, 0x18, 0x36, 0xA9, 0x2A, 0x42, 0x81, 0xEF, 0x70, 0xAA, 0xF4, 0xE4, 0x62, 0x99, 0x82, 0x21, 0xC6, 0xFB, 0xB9, 0xBD, 0xD0, 0x17, 0xE6, 0xAC, 0x59, 0x04, 0x94, 0xE9, 0xCE, 0xA9, 0x85, 0x9C, 0xEB, 0x2D, 0x2A, 0x4C, 0x17, 0x66, 0xF2, 0xC3, 0x39, 0x12, 0xC5, 0x8F, 0x14, 0xA8, 0x03, 0xE3, 0x6F, 0xCC, 0xDC, 0xCC, 0xDC, 0x13, 0xFD, 0x7A, 0xE7, 0x7C, 0x7A, 0x78, 0xD9, 0x97, 0xE6, 0xAC, 0xC3, 0x55, 0x57, 0xE0, 0xD3, 0xE9, 0xEB, 0x64, 0xB4, 0x3C, 0x92, 0xF4, 0xC5, 0x0D, 0x67, 0xA6, 0x02, 0xDE, 0xB3, 0x91, 0xB0, 0x66, 0x61, 0xCD, 0x32, 0x88, 0x0B, 0xD6, 0x49, 0x12, 0xAF, 0x1C, 0xBC, 0xB7, 0x16, 0x2A, 0x06, 0xF0, 0x25, 0x65, 0xD3, 0xB0, 0xEC, 0xE4, 0xFC, 0xEC, 0xDD, 0xAE, 0x8A, 0x49, 0x34, 0xDB, 0x8E, 0xE6, 0x7F, 0x30, 0x17, 0x98, 0x62, 0x21, 0x15, 0x5D, 0x13, 0x1C, 0x6C, 0x3F, 0x09, 0xAB, 0x19, 0x45, 0xC2, 0x06, 0xAC, 0x70, 0xC9, 0x42, 0xB3, 0x6F, 0x49, 0xA1, 0x18, 0x3B, 0xCD, 0x78, 0xB6, 0xE4, 0xB4, 0x7C, 0x6C, 0x5C, 0xAC, 0x0F, 0x8D, 0x62, 0xF8, 0x97, 0xC6, 0x95, 0x3D, 0xD1, 0x2F, 0x28, 0xB7, 0x0C, 0x5B, 0x7D, 0xF7, 0x51, 0x81, 0x9A, 0x98, 0x34, 0x65, 0x26, 0x25, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const unsigned char cpB_ppki_cert[0x300] = { - 0x00, 0x01, 0x00, 0x04, 0x2E, 0xA6, 0x6C, 0x66, - 0xCF, 0xF3, 0x35, 0x79, 0x7D, 0x04, 0x97, 0xB7, - 0x7A, 0x19, 0x7F, 0x9F, 0xE5, 0x1A, 0xB5, 0xA4, - 0x13, 0x75, 0xDC, 0x73, 0xFD, 0x9E, 0x0B, 0x10, - 0x66, 0x9B, 0x1B, 0x9A, 0x5B, 0x7E, 0x8A, 0xB2, - 0x8F, 0x01, 0xB6, 0x7B, 0x62, 0x54, 0xC1, 0x4A, - 0xA1, 0x33, 0x14, 0x18, 0xF2, 0x5B, 0xA5, 0x49, - 0x00, 0x4C, 0x37, 0x8D, 0xD7, 0x2F, 0x0C, 0xE6, - 0x3B, 0x1F, 0x70, 0x91, 0xAA, 0xFE, 0x38, 0x09, - 0xB7, 0xAC, 0x6C, 0x28, 0x76, 0xA6, 0x1D, 0x60, - 0x51, 0x6C, 0x43, 0xA6, 0x37, 0x29, 0x16, 0x2D, - 0x28, 0x0B, 0xE2, 0x1B, 0xE8, 0xE2, 0xFE, 0x05, - 0x7D, 0x8E, 0xB6, 0xE2, 0x04, 0x24, 0x22, 0x45, - 0x73, 0x1A, 0xB6, 0xFE, 0xE3, 0x0E, 0x53, 0x35, - 0x37, 0x3E, 0xEB, 0xA9, 0x70, 0xD5, 0x31, 0xBB, - 0xA2, 0xCB, 0x22, 0x2D, 0x96, 0x84, 0x38, 0x7D, - 0x5F, 0x2A, 0x1B, 0xF7, 0x52, 0x00, 0xCE, 0x06, - 0x56, 0xE3, 0x90, 0xCE, 0x19, 0x13, 0x5B, 0x59, - 0xE1, 0x4F, 0x0F, 0xA5, 0xC1, 0x28, 0x1A, 0x73, - 0x86, 0xCC, 0xD1, 0xC8, 0xEC, 0x3F, 0xAD, 0x70, - 0xFB, 0xCE, 0x74, 0xDE, 0xEE, 0x1F, 0xD0, 0x5F, - 0x46, 0x33, 0x0B, 0x51, 0xF9, 0xB7, 0x9E, 0x1D, - 0xDB, 0xF4, 0xE3, 0x3F, 0x14, 0x88, 0x9D, 0x05, - 0x28, 0x29, 0x24, 0xC5, 0xF5, 0xDC, 0x27, 0x66, - 0xEF, 0x06, 0x27, 0xD7, 0xEE, 0xDC, 0x73, 0x6E, - 0x67, 0xC2, 0xE5, 0xB9, 0x38, 0x34, 0x66, 0x80, - 0x72, 0x21, 0x6D, 0x1C, 0x78, 0xB8, 0x23, 0xA0, - 0x72, 0xD3, 0x4F, 0xF3, 0xEC, 0xF9, 0xBD, 0x11, - 0xA2, 0x9A, 0xF1, 0x6C, 0x33, 0xBD, 0x09, 0xAF, - 0xB2, 0xD7, 0x4D, 0x53, 0x4E, 0x02, 0x7C, 0x19, - 0x24, 0x0D, 0x59, 0x5A, 0x68, 0xEB, 0xB3, 0x05, - 0xAC, 0xC4, 0x4A, 0xB3, 0x8A, 0xB8, 0x20, 0xC6, - 0xD4, 0x26, 0x56, 0x0C, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x52, 0x6F, 0x6F, 0x74, 0x2D, 0x43, 0x41, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x62, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x13, 0x7A, 0x08, 0x0B, - 0xA6, 0x89, 0xC5, 0x90, 0xFD, 0x0B, 0x2F, 0x0D, - 0x4F, 0x56, 0xB6, 0x32, 0xFB, 0x93, 0x4E, 0xD0, - 0x73, 0x95, 0x17, 0xB3, 0x3A, 0x79, 0xDE, 0x04, - 0x0E, 0xE9, 0x2D, 0xC3, 0x1D, 0x37, 0xC7, 0xF7, - 0x3B, 0xF0, 0x4B, 0xD3, 0xE4, 0x4E, 0x20, 0xAB, - 0x5A, 0x6F, 0xEA, 0xF5, 0x98, 0x4C, 0xC1, 0xF6, - 0x06, 0x2E, 0x9A, 0x9F, 0xE5, 0x6C, 0x32, 0x85, - 0xDC, 0x6F, 0x25, 0xDD, 0xD5, 0xD0, 0xBF, 0x9F, - 0xE2, 0xEF, 0xE8, 0x35, 0xDF, 0x26, 0x34, 0xED, - 0x93, 0x7F, 0xAB, 0x02, 0x14, 0xD1, 0x04, 0x80, - 0x9C, 0xF7, 0x4B, 0x86, 0x0E, 0x6B, 0x04, 0x83, - 0xF4, 0xCD, 0x2D, 0xAB, 0x2A, 0x96, 0x02, 0xBC, - 0x56, 0xF0, 0xD6, 0xBD, 0x94, 0x6A, 0xED, 0x6E, - 0x0B, 0xE4, 0xF0, 0x8F, 0x26, 0x68, 0x6B, 0xD0, - 0x9E, 0xF7, 0xDB, 0x32, 0x5F, 0x82, 0xB1, 0x8F, - 0x6A, 0xF2, 0xED, 0x52, 0x5B, 0xFD, 0x82, 0x8B, - 0x65, 0x3F, 0xEE, 0x6E, 0xCE, 0x40, 0x0D, 0x5A, - 0x48, 0xFF, 0xE2, 0x2D, 0x53, 0x8B, 0xB5, 0x33, - 0x5B, 0x41, 0x53, 0x34, 0x2D, 0x43, 0x35, 0xAC, - 0xF5, 0x90, 0xD0, 0xD3, 0x0A, 0xE2, 0x04, 0x3C, - 0x7F, 0x5A, 0xD2, 0x14, 0xFC, 0x9C, 0x0F, 0xE6, - 0xFA, 0x40, 0xA5, 0xC8, 0x65, 0x06, 0xCA, 0x63, - 0x69, 0xBC, 0xEE, 0x44, 0xA3, 0x2D, 0x9E, 0x69, - 0x5C, 0xF0, 0x0B, 0x4F, 0xD7, 0x9A, 0xDB, 0x56, - 0x8D, 0x14, 0x9C, 0x20, 0x28, 0xA1, 0x4C, 0x9D, - 0x71, 0xB8, 0x50, 0xCA, 0x36, 0x5B, 0x37, 0xF7, - 0x0B, 0x65, 0x77, 0x91, 0xFC, 0x5D, 0x72, 0x8C, - 0x4E, 0x18, 0xFD, 0x22, 0x55, 0x7C, 0x40, 0x62, - 0xD7, 0x47, 0x71, 0x53, 0x3C, 0x70, 0x17, 0x9D, - 0x3D, 0xAE, 0x8F, 0x92, 0xB1, 0x17, 0xE4, 0x5C, - 0xB3, 0x32, 0xF3, 0xB3, 0xC2, 0xA2, 0x2E, 0x70, - 0x5C, 0xFE, 0xC6, 0x6F, 0x6D, 0xA3, 0x77, 0x2B, - 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00, 0x01, 0x00, 0x04, 0x2E, 0xA6, 0x6C, 0x66, 0xCF, 0xF3, 0x35, 0x79, 0x7D, 0x04, 0x97, 0xB7, 0x7A, 0x19, 0x7F, 0x9F, 0xE5, 0x1A, 0xB5, 0xA4, 0x13, 0x75, 0xDC, 0x73, 0xFD, 0x9E, 0x0B, 0x10, 0x66, 0x9B, 0x1B, 0x9A, 0x5B, 0x7E, 0x8A, 0xB2, 0x8F, 0x01, 0xB6, 0x7B, 0x62, 0x54, 0xC1, 0x4A, 0xA1, 0x33, 0x14, 0x18, 0xF2, 0x5B, 0xA5, 0x49, 0x00, 0x4C, 0x37, 0x8D, 0xD7, 0x2F, 0x0C, 0xE6, 0x3B, 0x1F, 0x70, 0x91, 0xAA, 0xFE, 0x38, 0x09, 0xB7, 0xAC, 0x6C, 0x28, 0x76, 0xA6, 0x1D, 0x60, 0x51, 0x6C, 0x43, 0xA6, 0x37, 0x29, 0x16, 0x2D, 0x28, 0x0B, 0xE2, 0x1B, 0xE8, 0xE2, 0xFE, 0x05, 0x7D, 0x8E, 0xB6, 0xE2, 0x04, 0x24, 0x22, 0x45, 0x73, 0x1A, 0xB6, 0xFE, 0xE3, 0x0E, 0x53, 0x35, 0x37, 0x3E, 0xEB, 0xA9, 0x70, 0xD5, 0x31, 0xBB, 0xA2, 0xCB, 0x22, 0x2D, 0x96, 0x84, 0x38, 0x7D, 0x5F, 0x2A, 0x1B, 0xF7, 0x52, 0x00, 0xCE, 0x06, 0x56, 0xE3, 0x90, 0xCE, 0x19, 0x13, 0x5B, 0x59, 0xE1, 0x4F, 0x0F, 0xA5, 0xC1, 0x28, 0x1A, 0x73, 0x86, 0xCC, 0xD1, 0xC8, 0xEC, 0x3F, 0xAD, 0x70, 0xFB, 0xCE, 0x74, 0xDE, 0xEE, 0x1F, 0xD0, 0x5F, 0x46, 0x33, 0x0B, 0x51, 0xF9, 0xB7, 0x9E, 0x1D, 0xDB, 0xF4, 0xE3, 0x3F, 0x14, 0x88, 0x9D, 0x05, 0x28, 0x29, 0x24, 0xC5, 0xF5, 0xDC, 0x27, 0x66, 0xEF, 0x06, 0x27, 0xD7, 0xEE, 0xDC, 0x73, 0x6E, 0x67, 0xC2, 0xE5, 0xB9, 0x38, 0x34, 0x66, 0x80, 0x72, 0x21, 0x6D, 0x1C, 0x78, 0xB8, 0x23, 0xA0, 0x72, 0xD3, 0x4F, 0xF3, 0xEC, 0xF9, 0xBD, 0x11, 0xA2, 0x9A, 0xF1, 0x6C, 0x33, 0xBD, 0x09, 0xAF, 0xB2, 0xD7, 0x4D, 0x53, 0x4E, 0x02, 0x7C, 0x19, 0x24, 0x0D, 0x59, 0x5A, 0x68, 0xEB, 0xB3, 0x05, 0xAC, 0xC4, 0x4A, 0xB3, 0x8A, 0xB8, 0x20, 0xC6, 0xD4, 0x26, 0x56, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x6F, 0x6F, 0x74, 0x2D, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x43, 0x50, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x7A, 0x08, 0x0B, 0xA6, 0x89, 0xC5, 0x90, 0xFD, 0x0B, 0x2F, 0x0D, 0x4F, 0x56, 0xB6, 0x32, 0xFB, 0x93, 0x4E, 0xD0, 0x73, 0x95, 0x17, 0xB3, 0x3A, 0x79, 0xDE, 0x04, 0x0E, 0xE9, 0x2D, 0xC3, 0x1D, 0x37, 0xC7, 0xF7, 0x3B, 0xF0, 0x4B, 0xD3, 0xE4, 0x4E, 0x20, 0xAB, 0x5A, 0x6F, 0xEA, 0xF5, 0x98, 0x4C, 0xC1, 0xF6, 0x06, 0x2E, 0x9A, 0x9F, 0xE5, 0x6C, 0x32, 0x85, 0xDC, 0x6F, 0x25, 0xDD, 0xD5, 0xD0, 0xBF, 0x9F, 0xE2, 0xEF, 0xE8, 0x35, 0xDF, 0x26, 0x34, 0xED, 0x93, 0x7F, 0xAB, 0x02, 0x14, 0xD1, 0x04, 0x80, 0x9C, 0xF7, 0x4B, 0x86, 0x0E, 0x6B, 0x04, 0x83, 0xF4, 0xCD, 0x2D, 0xAB, 0x2A, 0x96, 0x02, 0xBC, 0x56, 0xF0, 0xD6, 0xBD, 0x94, 0x6A, 0xED, 0x6E, 0x0B, 0xE4, 0xF0, 0x8F, 0x26, 0x68, 0x6B, 0xD0, 0x9E, 0xF7, 0xDB, 0x32, 0x5F, 0x82, 0xB1, 0x8F, 0x6A, 0xF2, 0xED, 0x52, 0x5B, 0xFD, 0x82, 0x8B, 0x65, 0x3F, 0xEE, 0x6E, 0xCE, 0x40, 0x0D, 0x5A, 0x48, 0xFF, 0xE2, 0x2D, 0x53, 0x8B, 0xB5, 0x33, 0x5B, 0x41, 0x53, 0x34, 0x2D, 0x43, 0x35, 0xAC, 0xF5, 0x90, 0xD0, 0xD3, 0x0A, 0xE2, 0x04, 0x3C, 0x7F, 0x5A, 0xD2, 0x14, 0xFC, 0x9C, 0x0F, 0xE6, 0xFA, 0x40, 0xA5, 0xC8, 0x65, 0x06, 0xCA, 0x63, 0x69, 0xBC, 0xEE, 0x44, 0xA3, 0x2D, 0x9E, 0x69, 0x5C, 0xF0, 0x0B, 0x4F, 0xD7, 0x9A, 0xDB, 0x56, 0x8D, 0x14, 0x9C, 0x20, 0x28, 0xA1, 0x4C, 0x9D, 0x71, 0xB8, 0x50, 0xCA, 0x36, 0x5B, 0x37, 0xF7, 0x0B, 0x65, 0x77, 0x91, 0xFC, 0x5D, 0x72, 0x8C, 0x4E, 0x18, 0xFD, 0x22, 0x55, 0x7C, 0x40, 0x62, 0xD7, 0x47, 0x71, 0x53, 0x3C, 0x70, 0x17, 0x9D, 0x3D, 0xAE, 0x8F, 0x92, 0xB1, 0x17, 0xE4, 0x5C, 0xB3, 0x32, 0xF3, 0xB3, 0xC2, 0xA2, 0x2E, 0x70, 0x5C, 0xFE, 0xC6, 0x6F, 0x6D, 0xA3, 0x77, 0x2B, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; \ No newline at end of file diff --git a/makerom/pki/rsa_key.h b/makerom/pki/rsa_key.h new file mode 100644 index 00000000..73f68bb5 --- /dev/null +++ b/makerom/pki/rsa_key.h @@ -0,0 +1,14 @@ +#pragma once +#include + +typedef struct CtrRsa2048Key { + uint8_t modulus[0x100]; + uint8_t priv_exponent[0x100]; + //uint8_t pub_exponent[0x3]; +} CtrRsa2048Key; + +typedef struct CtrRsa4096Key { + uint8_t modulus[0x200]; + uint8_t priv_exponent[0x200]; + //uint8_t pub_exponent[0x3]; +} CtrRsa4096Key; \ No newline at end of file diff --git a/makerom/pki/test.h b/makerom/pki/test.h index f04cad47..0d659d06 100644 --- a/makerom/pki/test.h +++ b/makerom/pki/test.h @@ -1,4 +1,5 @@ #pragma once +#include "rsa_key.h" // AES KEYS static const unsigned char zeros_aesKey[16] = @@ -8,14 +9,10 @@ static const unsigned char zeros_aesKey[16] = }; // RSA KEYS -static const unsigned char tpki_rsa_privExp[256] = +static const CtrRsa2048Key tpki_rsa = { - 0x3E, 0x2B, 0xBE, 0xBA, 0x7F, 0x29, 0x02, 0x52, 0xBF, 0x1B, 0xF1, 0xE4, 0x21, 0x2F, 0xD9, 0x76, 0x1E, 0x39, 0x23, 0x4A, 0x6D, 0xFF, 0x99, 0xF6, 0x33, 0xAA, 0x2B, 0x62, 0x03, 0x0A, 0x0E, 0x15, 0xAC, 0x16, 0xB9, 0x85, 0x63, 0x77, 0xF5, 0x74, 0x24, 0x61, 0xB1, 0x01, 0x6E, 0xEB, 0x72, 0x24, 0x1E, 0x5D, 0xFA, 0x8F, 0xA8, 0x5A, 0x10, 0x14, 0x47, 0xBD, 0x05, 0xA0, 0x7E, 0xE5, 0xFF, 0x60, 0x87, 0x2A, 0x18, 0x31, 0xC1, 0x39, 0x6C, 0xD5, 0x45, 0xBB, 0x29, 0x05, 0x04, 0xFB, 0x7A, 0xA2, 0x68, 0x21, 0x5F, 0xED, 0x4E, 0xFE, 0x64, 0x60, 0x69, 0xBD, 0x96, 0xD0, 0xA7, 0x06, 0x3D, 0x53, 0x7B, 0x68, 0x92, 0x88, 0x50, 0x86, 0xEE, 0x06, 0x5D, 0x72, 0x73, 0x9A, 0x39, 0xB6, 0x72, 0x3B, 0x20, 0x01, 0x39, 0xDF, 0x37, 0x28, 0x1E, 0xF5, 0x39, 0x63, 0xBC, 0x2A, 0xF2, 0x5E, 0xAB, 0x1A, 0x99, 0xE4, 0x5B, 0xEB, 0xE6, 0x36, 0x30, 0x6C, 0x40, 0x01, 0x61, 0x60, 0xCC, 0x55, 0x89, 0x6D, 0xCA, 0x7E, 0xE0, 0x64, 0x78, 0x7F, 0x7B, 0x26, 0xAE, 0x3E, 0xA3, 0x12, 0x45, 0x16, 0xF6, 0xC8, 0xD0, 0xB9, 0x4F, 0x91, 0x11, 0x12, 0x11, 0xBB, 0xBB, 0x7F, 0xAB, 0xC7, 0x82, 0xDC, 0x4A, 0x61, 0x9C, 0x14, 0xAE, 0x29, 0xFD, 0x3A, 0x60, 0x13, 0x93, 0x19, 0x2F, 0x54, 0x49, 0xB2, 0x44, 0x34, 0x58, 0x14, 0xD7, 0x2F, 0x70, 0x25, 0xA0, 0x48, 0x66, 0x76, 0x55, 0x87, 0x9B, 0x25, 0x77, 0x6D, 0x0B, 0x75, 0x98, 0x8B, 0xA6, 0x39, 0x40, 0x3C, 0x21, 0x7F, 0x2A, 0x24, 0xC1, 0xA5, 0xC1, 0xDC, 0x5A, 0x57, 0x54, 0xF6, 0x03, 0xF6, 0xAD, 0x51, 0x33, 0x40, 0x6D, 0x5C, 0x26, 0x5E, 0x29, 0x92, 0x82, 0xE5, 0x29, 0x13, 0x7D, 0x7D, 0xFE, 0x08, 0x73, 0xBC, 0x5D, 0xC4, 0xE9, 0x2B, 0xD6, 0x71 -}; - -static const unsigned char tpki_rsa_pubMod[256] = -{ - 0xCA, 0xC5, 0x88, 0xC7, 0xF1, 0x2A, 0x09, 0x2B, 0x76, 0x49, 0xC0, 0xA8, 0x35, 0x75, 0x10, 0x82, 0xC2, 0xB5, 0xE5, 0xB2, 0xE9, 0xC8, 0x18, 0x88, 0xF3, 0x98, 0x89, 0xBF, 0x9D, 0xE6, 0xE4, 0x0B, 0x71, 0x5D, 0xDD, 0x3F, 0x13, 0x82, 0x71, 0xF2, 0xED, 0x31, 0x86, 0x99, 0xD9, 0x47, 0xFE, 0xC5, 0x7A, 0x75, 0x93, 0xE1, 0xF8, 0x6D, 0xC6, 0x3D, 0x9B, 0xE1, 0x15, 0x99, 0xE1, 0xC2, 0xE0, 0x5C, 0x38, 0x4B, 0x35, 0xA2, 0x4D, 0x3E, 0xE2, 0xCE, 0xFB, 0xB3, 0x08, 0xA3, 0xDD, 0x0C, 0x26, 0x31, 0x84, 0x92, 0x27, 0xC8, 0x8A, 0x8E, 0xC8, 0x83, 0xA8, 0x6C, 0xA7, 0xA3, 0x39, 0x71, 0x9E, 0xF1, 0x34, 0x91, 0x01, 0xDF, 0x11, 0x4A, 0x9C, 0xF9, 0x8B, 0xF9, 0x2F, 0x46, 0x44, 0x0A, 0x72, 0x38, 0xF3, 0x8B, 0x6D, 0x23, 0x33, 0x89, 0xBF, 0x66, 0x34, 0xA7, 0x86, 0xE6, 0xAD, 0xF2, 0xDE, 0xF9, 0xAB, 0x16, 0xA1, 0x40, 0xEE, 0xD8, 0xF7, 0x6C, 0xDC, 0x00, 0x92, 0xCB, 0x31, 0x49, 0xFC, 0x26, 0x64, 0x24, 0x08, 0x8F, 0xC6, 0x60, 0xFF, 0x1E, 0xE3, 0xF0, 0xDD, 0xFB, 0x6D, 0x0D, 0x0F, 0x49, 0x7C, 0xAD, 0x03, 0xEC, 0x9F, 0x63, 0x58, 0xFA, 0x46, 0xDF, 0xA2, 0x64, 0x0E, 0xCC, 0x85, 0x57, 0xE7, 0x2C, 0x61, 0x7F, 0x59, 0xB8, 0x62, 0x7D, 0x59, 0x0E, 0xF6, 0x84, 0x96, 0x99, 0x42, 0xB0, 0x39, 0x83, 0x80, 0xB5, 0x52, 0x2E, 0x07, 0x3F, 0x92, 0xE3, 0x9E, 0xF5, 0x47, 0xEB, 0xA7, 0xD7, 0xD4, 0x15, 0xF1, 0x22, 0x82, 0x32, 0xBE, 0x2A, 0xD0, 0x8C, 0x01, 0xCC, 0x30, 0xA9, 0x11, 0x96, 0xF6, 0xE9, 0x2B, 0xEA, 0x0E, 0xF8, 0x2D, 0x0D, 0xB1, 0x91, 0xD5, 0x1A, 0x94, 0x51, 0xB9, 0x85, 0x39, 0xB0, 0xAF, 0x9F, 0x54, 0x9E, 0x99, 0xE1, 0x46, 0xE5, 0x6F, 0xE2, 0x5F, 0x4B, 0x4E, 0x23 + .modulus = { 0xCA, 0xC5, 0x88, 0xC7, 0xF1, 0x2A, 0x09, 0x2B, 0x76, 0x49, 0xC0, 0xA8, 0x35, 0x75, 0x10, 0x82, 0xC2, 0xB5, 0xE5, 0xB2, 0xE9, 0xC8, 0x18, 0x88, 0xF3, 0x98, 0x89, 0xBF, 0x9D, 0xE6, 0xE4, 0x0B, 0x71, 0x5D, 0xDD, 0x3F, 0x13, 0x82, 0x71, 0xF2, 0xED, 0x31, 0x86, 0x99, 0xD9, 0x47, 0xFE, 0xC5, 0x7A, 0x75, 0x93, 0xE1, 0xF8, 0x6D, 0xC6, 0x3D, 0x9B, 0xE1, 0x15, 0x99, 0xE1, 0xC2, 0xE0, 0x5C, 0x38, 0x4B, 0x35, 0xA2, 0x4D, 0x3E, 0xE2, 0xCE, 0xFB, 0xB3, 0x08, 0xA3, 0xDD, 0x0C, 0x26, 0x31, 0x84, 0x92, 0x27, 0xC8, 0x8A, 0x8E, 0xC8, 0x83, 0xA8, 0x6C, 0xA7, 0xA3, 0x39, 0x71, 0x9E, 0xF1, 0x34, 0x91, 0x01, 0xDF, 0x11, 0x4A, 0x9C, 0xF9, 0x8B, 0xF9, 0x2F, 0x46, 0x44, 0x0A, 0x72, 0x38, 0xF3, 0x8B, 0x6D, 0x23, 0x33, 0x89, 0xBF, 0x66, 0x34, 0xA7, 0x86, 0xE6, 0xAD, 0xF2, 0xDE, 0xF9, 0xAB, 0x16, 0xA1, 0x40, 0xEE, 0xD8, 0xF7, 0x6C, 0xDC, 0x00, 0x92, 0xCB, 0x31, 0x49, 0xFC, 0x26, 0x64, 0x24, 0x08, 0x8F, 0xC6, 0x60, 0xFF, 0x1E, 0xE3, 0xF0, 0xDD, 0xFB, 0x6D, 0x0D, 0x0F, 0x49, 0x7C, 0xAD, 0x03, 0xEC, 0x9F, 0x63, 0x58, 0xFA, 0x46, 0xDF, 0xA2, 0x64, 0x0E, 0xCC, 0x85, 0x57, 0xE7, 0x2C, 0x61, 0x7F, 0x59, 0xB8, 0x62, 0x7D, 0x59, 0x0E, 0xF6, 0x84, 0x96, 0x99, 0x42, 0xB0, 0x39, 0x83, 0x80, 0xB5, 0x52, 0x2E, 0x07, 0x3F, 0x92, 0xE3, 0x9E, 0xF5, 0x47, 0xEB, 0xA7, 0xD7, 0xD4, 0x15, 0xF1, 0x22, 0x82, 0x32, 0xBE, 0x2A, 0xD0, 0x8C, 0x01, 0xCC, 0x30, 0xA9, 0x11, 0x96, 0xF6, 0xE9, 0x2B, 0xEA, 0x0E, 0xF8, 0x2D, 0x0D, 0xB1, 0x91, 0xD5, 0x1A, 0x94, 0x51, 0xB9, 0x85, 0x39, 0xB0, 0xAF, 0x9F, 0x54, 0x9E, 0x99, 0xE1, 0x46, 0xE5, 0x6F, 0xE2, 0x5F, 0x4B, 0x4E, 0x23 }, + .priv_exponent = { 0x3E, 0x2B, 0xBE, 0xBA, 0x7F, 0x29, 0x02, 0x52, 0xBF, 0x1B, 0xF1, 0xE4, 0x21, 0x2F, 0xD9, 0x76, 0x1E, 0x39, 0x23, 0x4A, 0x6D, 0xFF, 0x99, 0xF6, 0x33, 0xAA, 0x2B, 0x62, 0x03, 0x0A, 0x0E, 0x15, 0xAC, 0x16, 0xB9, 0x85, 0x63, 0x77, 0xF5, 0x74, 0x24, 0x61, 0xB1, 0x01, 0x6E, 0xEB, 0x72, 0x24, 0x1E, 0x5D, 0xFA, 0x8F, 0xA8, 0x5A, 0x10, 0x14, 0x47, 0xBD, 0x05, 0xA0, 0x7E, 0xE5, 0xFF, 0x60, 0x87, 0x2A, 0x18, 0x31, 0xC1, 0x39, 0x6C, 0xD5, 0x45, 0xBB, 0x29, 0x05, 0x04, 0xFB, 0x7A, 0xA2, 0x68, 0x21, 0x5F, 0xED, 0x4E, 0xFE, 0x64, 0x60, 0x69, 0xBD, 0x96, 0xD0, 0xA7, 0x06, 0x3D, 0x53, 0x7B, 0x68, 0x92, 0x88, 0x50, 0x86, 0xEE, 0x06, 0x5D, 0x72, 0x73, 0x9A, 0x39, 0xB6, 0x72, 0x3B, 0x20, 0x01, 0x39, 0xDF, 0x37, 0x28, 0x1E, 0xF5, 0x39, 0x63, 0xBC, 0x2A, 0xF2, 0x5E, 0xAB, 0x1A, 0x99, 0xE4, 0x5B, 0xEB, 0xE6, 0x36, 0x30, 0x6C, 0x40, 0x01, 0x61, 0x60, 0xCC, 0x55, 0x89, 0x6D, 0xCA, 0x7E, 0xE0, 0x64, 0x78, 0x7F, 0x7B, 0x26, 0xAE, 0x3E, 0xA3, 0x12, 0x45, 0x16, 0xF6, 0xC8, 0xD0, 0xB9, 0x4F, 0x91, 0x11, 0x12, 0x11, 0xBB, 0xBB, 0x7F, 0xAB, 0xC7, 0x82, 0xDC, 0x4A, 0x61, 0x9C, 0x14, 0xAE, 0x29, 0xFD, 0x3A, 0x60, 0x13, 0x93, 0x19, 0x2F, 0x54, 0x49, 0xB2, 0x44, 0x34, 0x58, 0x14, 0xD7, 0x2F, 0x70, 0x25, 0xA0, 0x48, 0x66, 0x76, 0x55, 0x87, 0x9B, 0x25, 0x77, 0x6D, 0x0B, 0x75, 0x98, 0x8B, 0xA6, 0x39, 0x40, 0x3C, 0x21, 0x7F, 0x2A, 0x24, 0xC1, 0xA5, 0xC1, 0xDC, 0x5A, 0x57, 0x54, 0xF6, 0x03, 0xF6, 0xAD, 0x51, 0x33, 0x40, 0x6D, 0x5C, 0x26, 0x5E, 0x29, 0x92, 0x82, 0xE5, 0x29, 0x13, 0x7D, 0x7D, 0xFE, 0x08, 0x73, 0xBC, 0x5D, 0xC4, 0xE9, 0x2B, 0xD6, 0x71 } }; //Certificates From 22a112763427ec2c2a2ccdbf10c8673868f5513b Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Jan 2016 10:10:18 +0800 Subject: [PATCH 158/317] [makerom] Fix typo in ctr_aes_keygen() --- makerom/aes_keygen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/aes_keygen.c b/makerom/aes_keygen.c index ffaa4d93..c9934101 100644 --- a/makerom/aes_keygen.c +++ b/makerom/aes_keygen.c @@ -140,7 +140,7 @@ void ctr_aes_keygen(const uint8_t *x, const uint8_t *y, uint8_t *key) n128_xor(x_rot, y, key_xy); // key_xyc = key_xy + KEYGEN_CONST - n128_add(key_xy, KEYGEN_CONST, key); + n128_add(key_xy, KEYGEN_CONST, key_xyc); n128_rrot(key_xyc, 41, key); } \ No newline at end of file From 2ffdca34c5bd062108cabad85070e4b5b72a5044 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Jan 2016 10:37:12 +0800 Subject: [PATCH 159/317] [makerom] Fix typo in prod keyset --- makerom/pki/prod.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/pki/prod.h b/makerom/pki/prod.h index bec496be..31250811 100644 --- a/makerom/pki/prod.h +++ b/makerom/pki/prod.h @@ -27,7 +27,7 @@ static const unsigned char ctr_common_etd_keyY_ppki[6][16] = }; // RSA KEYS -static const CtrRsa4096Key root_dpki_rsa = +static const CtrRsa4096Key root_ppki_rsa = { .modulus = { 0xF8, 0x24, 0x6C, 0x58, 0xBA, 0xE7, 0x50, 0x03, 0x01, 0xFB, 0xB7, 0xC2, 0xEB, 0xE0, 0x01, 0x05, 0x71, 0xDA, 0x92, 0x23, 0x78, 0xF0, 0x51, 0x4E, 0xC0, 0x03, 0x1D, 0xD0, 0xD2, 0x1E, 0xD3, 0xD0, 0x7E, 0xFC, 0x85, 0x20, 0x69, 0xB5, 0xDE, 0x9B, 0xB9, 0x51, 0xA8, 0xBC, 0x90, 0xA2, 0x44, 0x92, 0x6D, 0x37, 0x92, 0x95, 0xAE, 0x94, 0x36, 0xAA, 0xA6, 0xA3, 0x02, 0x51, 0x0C, 0x7B, 0x1D, 0xED, 0xD5, 0xFB, 0x20, 0x86, 0x9D, 0x7F, 0x30, 0x16, 0xF6, 0xBE, 0x65, 0xD3, 0x83, 0xA1, 0x6D, 0xB3, 0x32, 0x1B, 0x95, 0x35, 0x18, 0x90, 0xB1, 0x70, 0x02, 0x93, 0x7E, 0xE1, 0x93, 0xF5, 0x7E, 0x99, 0xA2, 0x47, 0x4E, 0x9D, 0x38, 0x24, 0xC7, 0xAE, 0xE3, 0x85, 0x41, 0xF5, 0x67, 0xE7, 0x51, 0x8C, 0x7A, 0x0E, 0x38, 0xE7, 0xEB, 0xAF, 0x41, 0x19, 0x1B, 0xCF, 0xF1, 0x7B, 0x42, 0xA6, 0xB4, 0xED, 0xE6, 0xCE, 0x8D, 0xE7, 0x31, 0x8F, 0x7F, 0x52, 0x04, 0xB3, 0x99, 0x0E, 0x22, 0x67, 0x45, 0xAF, 0xD4, 0x85, 0xB2, 0x44, 0x93, 0x00, 0x8B, 0x08, 0xC7, 0xF6, 0xB7, 0xE5, 0x6B, 0x02, 0xB3, 0xE8, 0xFE, 0x0C, 0x9D, 0x85, 0x9C, 0xB8, 0xB6, 0x82, 0x23, 0xB8, 0xAB, 0x27, 0xEE, 0x5F, 0x65, 0x38, 0x07, 0x8B, 0x2D, 0xB9, 0x1E, 0x2A, 0x15, 0x3E, 0x85, 0x81, 0x80, 0x72, 0xA2, 0x3B, 0x6D, 0xD9, 0x32, 0x81, 0x05, 0x4F, 0x6F, 0xB0, 0xF6, 0xF5, 0xAD, 0x28, 0x3E, 0xCA, 0x0B, 0x7A, 0xF3, 0x54, 0x55, 0xE0, 0x3D, 0xA7, 0xB6, 0x83, 0x26, 0xF3, 0xEC, 0x83, 0x4A, 0xF3, 0x14, 0x04, 0x8A, 0xC6, 0xDF, 0x20, 0xD2, 0x85, 0x08, 0x67, 0x3C, 0xAB, 0x62, 0xA2, 0xC7, 0xBC, 0x13, 0x1A, 0x53, 0x3E, 0x0B, 0x66, 0x80, 0x6B, 0x1C, 0x30, 0x66, 0x4B, 0x37, 0x23, 0x31, 0xBD, 0xC4, 0xB0, 0xCA, 0xD8, 0xD1, 0x1E, 0xE7, 0xBB, 0xD9, 0x28, 0x55, 0x48, 0xAA, 0xEC, 0x1F, 0x66, 0xE8, 0x21, 0xB3, 0xC8, 0xA0, 0x47, 0x69, 0x00, 0xC5, 0xE6, 0x88, 0xE8, 0x0C, 0xCE, 0x3C, 0x61, 0xD6, 0x9C, 0xBB, 0xA1, 0x37, 0xC6, 0x60, 0x4F, 0x7A, 0x72, 0xDD, 0x8C, 0x7B, 0x3E, 0x3D, 0x51, 0x29, 0x0D, 0xAA, 0x6A, 0x59, 0x7B, 0x08, 0x1F, 0x9D, 0x36, 0x33, 0xA3, 0x46, 0x7A, 0x35, 0x61, 0x09, 0xAC, 0xA7, 0xDD, 0x7D, 0x2E, 0x2F, 0xB2, 0xC1, 0xAE, 0xB8, 0xE2, 0x0F, 0x48, 0x92, 0xD8, 0xB9, 0xF8, 0xB4, 0x6F, 0x4E, 0x3C, 0x11, 0xF4, 0xF4, 0x7D, 0x8B, 0x75, 0x7D, 0xFE, 0xFE, 0xA3, 0x89, 0x9C, 0x33, 0x59, 0x5C, 0x5E, 0xFD, 0xEB, 0xCB, 0xAB, 0xE8, 0x41, 0x3E, 0x3A, 0x9A, 0x80, 0x3C, 0x69, 0x35, 0x6E, 0xB2, 0xB2, 0xAD, 0x5C, 0xC4, 0xC8, 0x58, 0x45, 0x5E, 0xF5, 0xF7, 0xB3, 0x06, 0x44, 0xB4, 0x7C, 0x64, 0x06, 0x8C, 0xDF, 0x80, 0x9F, 0x76, 0x02, 0x5A, 0x2D, 0xB4, 0x46, 0xE0, 0x3D, 0x7C, 0xF6, 0x2F, 0x34, 0xE7, 0x02, 0x45, 0x7B, 0x02, 0xA4, 0xCF, 0x5D, 0x9D, 0xD5, 0x3C, 0xA5, 0x3A, 0x7C, 0xA6, 0x29, 0x78, 0x8C, 0x67, 0xCA, 0x08, 0xBF, 0xEC, 0xCA, 0x43, 0xA9, 0x57, 0xAD, 0x16, 0xC9, 0x4E, 0x1C, 0xD8, 0x75, 0xCA, 0x10, 0x7D, 0xCE, 0x7E, 0x01, 0x18, 0xF0, 0xDF, 0x6B, 0xFE, 0xE5, 0x1D, 0xDB, 0xD9, 0x91, 0xC2, 0x6E, 0x60, 0xCD, 0x48, 0x58, 0xAA, 0x59, 0x2C, 0x82, 0x00, 0x75, 0xF2, 0x9F, 0x52, 0x6C, 0x91, 0x7C, 0x6F, 0xE5, 0x40, 0x3E, 0xA7, 0xD4, 0xA5, 0x0C, 0xEC, 0x3B, 0x73, 0x84, 0xDE, 0x88, 0x6E, 0x82, 0xD2, 0xEB, 0x4D, 0x4E, 0x42, 0xB5, 0xF2, 0xB1, 0x49, 0xA8, 0x1E, 0xA7, 0xCE, 0x71, 0x44, 0xDC, 0x29, 0x94, 0xCF, 0xC4, 0x4E, 0x1F, 0x91, 0xCB, 0xD4, 0x95 }, .priv_exponent = { 0 } From 8ac78cac33d4d59c30b2420c9e13bf314b901631 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Jan 2016 10:39:40 +0800 Subject: [PATCH 160/317] [makerom] Fix BSS duplication in ExeFS code, issue #20 --- makerom/code.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/makerom/code.c b/makerom/code.c index 483dfa41..88b23e6a 100644 --- a/makerom/code.c +++ b/makerom/code.c @@ -12,9 +12,8 @@ typedef struct code_segment { u32 address; u32 memSize; + u32 fileSize; u32 pageNum; - - u32 size; const u8 *data; } code_segment; @@ -132,7 +131,7 @@ void CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u64 segment_f out->address = 0; out->memSize = 0; out->pageNum = 0; - out->size = 0; + out->fileSize = 0; out->data = NULL; /* Find segment */ @@ -147,8 +146,8 @@ void CreateCodeSegmentFromElf(code_segment *out, elf_context *elf, u64 segment_f if ((segments[i].flags & ~PF_CTRSDK) == segment_flags && segments[i].type == PT_LOAD) { out->address = segments[i].vAddr; out->memSize = segments[i].memSize; - out->pageNum = SizeToPage(out->memSize); - out->size = segments[i].fileSize; + out->fileSize = segments[i].fileSize; + out->pageNum = SizeToPage(out->fileSize); out->data = segments[i].ptr; break; } @@ -167,23 +166,23 @@ int CreateExeFsCode(elf_context *elf, ncch_settings *set) CreateCodeSegmentFromElf(&rwdata, elf, PF_DATA); /* Checking the existence of essential ELF Segments */ - if (!text.size) return NOT_FIND_TEXT_SEGMENT; - if (!rwdata.size) return NOT_FIND_DATA_SEGMENT; + if (!text.fileSize) return NOT_FIND_TEXT_SEGMENT; + if (!rwdata.fileSize) return NOT_FIND_DATA_SEGMENT; - /* Calculating BSS size */ - set->codeDetails.bssSize = rwdata.memSize - rwdata.size; + /* Calculating BSS fileSize */ + set->codeDetails.bssSize = rwdata.memSize - rwdata.fileSize; /* Allocating Buffer for ExeFs Code */ u32 size = PageToSize(text.pageNum + rodata.pageNum + rwdata.pageNum); u8 *code = calloc(1, size); /* Writing Code into Buffer */ - u8 *textPos = (code + text.address - text.address); - u8 *rodataPos = (code + rodata.address - text.address); - u8 *rwdataPos = (code + rwdata.address - text.address); - if (text.size) memcpy(textPos, text.data, text.size); - if (rodata.size) memcpy(rodataPos, rodata.data, rodata.size); - if (rwdata.size) memcpy(rwdataPos, rwdata.data, rwdata.size); + u8 *textPos = (code + PageToSize(0)); + u8 *rodataPos = (code + PageToSize(text.pageNum)); + u8 *rwdataPos = (code + PageToSize(text.pageNum + rodata.pageNum)); + if (text.fileSize) memcpy(textPos, text.data, text.fileSize); + if (rodata.fileSize) memcpy(rodataPos, rodata.data, rodata.fileSize); + if (rwdata.fileSize) memcpy(rwdataPos, rwdata.data, rwdata.fileSize); /* Compressing if needed */ From 7b8d16fac61913f08b78a7af9183dc8e5e56f8b9 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 16 Jan 2016 18:26:12 +0800 Subject: [PATCH 161/317] [makerom] misc --- makerom/code.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/code.c b/makerom/code.c index 88b23e6a..11124471 100644 --- a/makerom/code.c +++ b/makerom/code.c @@ -169,7 +169,7 @@ int CreateExeFsCode(elf_context *elf, ncch_settings *set) if (!text.fileSize) return NOT_FIND_TEXT_SEGMENT; if (!rwdata.fileSize) return NOT_FIND_DATA_SEGMENT; - /* Calculating BSS fileSize */ + /* Calculating BSS size */ set->codeDetails.bssSize = rwdata.memSize - rwdata.fileSize; /* Allocating Buffer for ExeFs Code */ From 3548ebb1cf8038c0712279e9a7051ee61ec6807b Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 18 Jan 2016 19:28:52 +0800 Subject: [PATCH 162/317] [makerom] Added dev common keys: 2,3,4,5 --- makerom/keyset.c | 2 +- makerom/pki/dev.h | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/makerom/keyset.c b/makerom/keyset.c index c17bf0c6..85cc3f7a 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -91,7 +91,7 @@ int LoadKeysFromResources(keys_struct *keys) keys->keysetLoaded = true; /* AES Keys */ // CIA - for(int i = 0; i < 2; i++) + for(int i = 0; i < 6; i++) SetCommonKey(keys, ctr_common_etd_key_dpki[i],i); if(keys->aes.currentCommonKey > 0xff) diff --git a/makerom/pki/dev.h b/makerom/pki/dev.h index 6e107355..1099a23b 100644 --- a/makerom/pki/dev.h +++ b/makerom/pki/dev.h @@ -18,10 +18,14 @@ static const unsigned char dev_fixed_ncch_key[2][16] = {0x52, 0x7C, 0xE6, 0x30, 0xA9, 0xCA, 0x30, 0x5F, 0x36, 0x96, 0xF3, 0xCD, 0xE9, 0x54, 0x19, 0x4B} , // System FixedKey }; -static const unsigned char ctr_common_etd_key_dpki[2][16] = +static const unsigned char ctr_common_etd_key_dpki[6][16] = { {0x55, 0xA3, 0xF8, 0x72, 0xBD, 0xC8, 0x0C, 0x55, 0x5A, 0x65, 0x43, 0x81, 0x13, 0x9E, 0x15, 0x3B} , // 0 - eShop Titles {0x44, 0x34, 0xED, 0x14, 0x82, 0x0C, 0xA1, 0xEB, 0xAB, 0x82, 0xC1, 0x6E, 0x7B, 0xEF, 0x0C, 0x25} , // 1 - System Titles + {0xF6, 0x2E, 0x3F, 0x95, 0x8E, 0x28, 0xA2, 0x1F, 0x28, 0x9E, 0xEC, 0x71, 0xA8, 0x66, 0x29, 0xDC} , // 2 + {0x2B, 0x49, 0xCB, 0x6F, 0x99, 0x98, 0xD9, 0xAD, 0x94, 0xF2, 0xED, 0xE7, 0xB5, 0xDA, 0x3E, 0x27} , // 3 + {0x75, 0x05, 0x52, 0xBF, 0xAA, 0x1C, 0x04, 0x07, 0x55, 0xC8, 0xD5, 0x9A, 0x55, 0xF9, 0xAD, 0x1F} , // 4 + {0xAA, 0xDA, 0x4C, 0xA8, 0xF6, 0xE5, 0xA9, 0x77, 0xE0, 0xA0, 0xF9, 0xE4, 0x76, 0xCF, 0x0D, 0x63} , // 5 }; //RSA Keys From 58ca552e3ef704624e45c4843eab717a13e04b09 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 18 Jan 2016 23:44:58 +0800 Subject: [PATCH 163/317] [makerom] Encryption is now disabled by default. --- makerom/user_settings.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 8fa490e0..e35e89ee 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -96,7 +96,7 @@ void SetDefaults(user_settings *set) // RSF Settings clrmem(&set->common.rsfSet, sizeof(rsf_settings)); set->common.rsfSet.Option.EnableCompress = true; - set->common.rsfSet.Option.EnableCrypt = true; + set->common.rsfSet.Option.EnableCrypt = false; set->common.rsfSet.Option.UseOnSD = false; set->common.rsfSet.Option.FreeProductCode = false; @@ -175,18 +175,15 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } if (strcasecmp(argv[i + 1], "test") == 0 || strcasecmp(argv[i + 1], "t") == 0) { set->common.keys.keyset = pki_TEST; - set->common.rsfSet.Option.EnableCrypt = false; // prefer unencrypted output } //else if(strcasecmp(argv[i+1],"beta") == 0 || strcasecmp(argv[i+1],"b") == 0) { // set->common.keys.keyset = pki_BETA; //} else if (strcasecmp(argv[i + 1], "debug") == 0 || strcasecmp(argv[i + 1], "development") == 0 || strcasecmp(argv[i + 1], "d") == 0) { set->common.keys.keyset = pki_DEVELOPMENT; - set->common.rsfSet.Option.EnableCrypt = true; // prefer encrypted output } else if (strcasecmp(argv[i + 1], "retail") == 0 || strcasecmp(argv[i + 1], "production") == 0 || strcasecmp(argv[i + 1], "p") == 0) { set->common.keys.keyset = pki_PRODUCTION; - set->common.rsfSet.Option.EnableCrypt = true; // prefer encrypted output } //else if(strcasecmp(argv[i+1],"custom") == 0 || strcasecmp(argv[i+1],"c") == 0) { // set->common.keys.keyset = pki_CUSTOM; From 167e3f550930c2a0a52b239d8a53f96369b71855 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 18 Jan 2016 23:46:25 +0800 Subject: [PATCH 164/317] [makerom/ctrtool] Release 0.15 --- makerom/user_settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index e35e89ee..68cc1bb6 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -880,7 +880,7 @@ void PrintNoNeedParam(char *arg) void DisplayBanner(void) { - printf("CTR MAKEROM v0.14 (C) 3DSGuy 2014\n"); + printf("CTR MAKEROM v0.15 (C) 3DSGuy 2014\n"); printf("Built: %s %s\n\n", __TIME__, __DATE__); } From 8dd652737a0f617cb25dfcd7c1dad92db1c0c21d Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 24 Jan 2016 04:20:44 +0800 Subject: [PATCH 165/317] [makerom] Increased maximum affinity mask to 3 (from 1). --- makerom/exheader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makerom/exheader.c b/makerom/exheader.c index 9765e1cb..3338aa09 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -346,8 +346,8 @@ int SetARM11SystemLocalInfoFlags(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_ /* flag[2] */ if(rsf->AccessControlInfo.AffinityMask){ arm11->affinityMask = strtol(rsf->AccessControlInfo.AffinityMask,NULL,0); - if(arm11->affinityMask > 1){ - fprintf(stderr,"[EXHEADER ERROR] Unexpected AffinityMask: %d. Expected range: 0x0 - 0x1\n", arm11->affinityMask); + if(arm11->affinityMask > 3){ + fprintf(stderr,"[EXHEADER ERROR] Unexpected AffinityMask: %d. Expected range: 0x0 - 0x3\n", arm11->affinityMask); return EXHDR_BAD_RSF_OPT; } } From a8c0850b02cb9a80a6e2c9dc360f0c8642d21398 Mon Sep 17 00:00:00 2001 From: Alex Eckhart Date: Mon, 25 Jan 2016 22:32:05 -0700 Subject: [PATCH 166/317] Fix agb_firm cia generation. --- makerom/exheader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makerom/exheader.c b/makerom/exheader.c index 9765e1cb..579e4d15 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -1242,7 +1242,7 @@ int GetSaveDataSizeFromString(u64 *out, char *string, char *moduleName) fprintf(stderr,"[ERROR] Invalid save data size format.\n"); return EXHDR_BAD_RSF_OPT; } - if((SaveDataSize & 65536) != 0){ + if((SaveDataSize % 65536) != 0){ if(moduleName) fprintf(stderr,"[%s ERROR] Save data size must be aligned to 64K.\n",moduleName); else @@ -1280,7 +1280,7 @@ int GetSaveDataSize_rsf(u64 *SaveDataSize, user_settings *usrset) fprintf(stderr,"[EXHEADER ERROR] Invalid save data size format.\n"); return EXHDR_BAD_RSF_OPT; } - if((*SaveDataSize & 65536) != 0){ + if((*SaveDataSize % 65536) != 0){ fprintf(stderr,"[EXHEADER ERROR] Save data size must be aligned to 64K.\n"); return EXHDR_BAD_RSF_OPT; } From d9556da765285306bcd8da3c0969f3e60e6b731a Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 15 Feb 2016 13:42:56 +0800 Subject: [PATCH 167/317] Cleaned up polarssl. --- makerom/crypto.c | 139 +--- makerom/polarssl/arc4.h | 98 --- makerom/polarssl/asn1.h | 246 ------- makerom/polarssl/asn1write.h | 46 -- makerom/polarssl/blowfish.h | 171 ----- makerom/polarssl/camellia.h | 200 ------ makerom/polarssl/certs.h | 47 -- makerom/polarssl/cipher.h | 463 ------------- makerom/polarssl/cipher_wrap.h | 107 --- makerom/polarssl/config.h | 83 +-- makerom/polarssl/ctr_drbg.h | 231 ------- makerom/polarssl/debug.h | 89 --- makerom/polarssl/des.h | 252 ------- makerom/polarssl/dhm.h | 244 ------- makerom/polarssl/entropy.h | 153 ----- makerom/polarssl/entropy_poll.h | 75 -- makerom/polarssl/error.h | 108 --- makerom/polarssl/gcm.h | 147 ---- makerom/polarssl/havege.h | 71 -- makerom/polarssl/md.c | 298 -------- makerom/polarssl/md.h | 363 ---------- makerom/polarssl/md2.h | 171 ----- makerom/polarssl/md4.h | 177 ----- makerom/polarssl/md5.c | 585 ---------------- makerom/polarssl/md5.h | 182 ----- makerom/polarssl/md_wrap.c | 733 -------------------- makerom/polarssl/md_wrap.h | 64 -- makerom/polarssl/net.h | 159 ----- makerom/polarssl/openssl.h | 136 ---- makerom/polarssl/padlock.c | 162 ----- makerom/polarssl/padlock.h | 108 --- makerom/polarssl/pbkdf2.h | 82 --- makerom/polarssl/pem.h | 105 --- makerom/polarssl/pkcs11.h | 161 ----- makerom/polarssl/pkcs12.h | 131 ---- makerom/polarssl/pkcs5.h | 122 ---- makerom/polarssl/rsa.c | 6 +- makerom/polarssl/rsa.h | 6 +- makerom/polarssl/sha2.h | 5 - makerom/polarssl/sha4.c | 760 --------------------- makerom/polarssl/sha4.h | 186 ----- makerom/polarssl/ssl.h | 1140 ------------------------------- makerom/polarssl/ssl_cache.h | 119 ---- makerom/polarssl/timing.h | 75 -- makerom/polarssl/version.h | 81 --- makerom/polarssl/x509.h | 778 --------------------- makerom/polarssl/x509write.h | 46 -- makerom/polarssl/xtea.h | 129 ---- 48 files changed, 49 insertions(+), 9991 deletions(-) delete mode 100644 makerom/polarssl/arc4.h delete mode 100644 makerom/polarssl/asn1.h delete mode 100644 makerom/polarssl/asn1write.h delete mode 100644 makerom/polarssl/blowfish.h delete mode 100644 makerom/polarssl/camellia.h delete mode 100644 makerom/polarssl/certs.h delete mode 100644 makerom/polarssl/cipher.h delete mode 100644 makerom/polarssl/cipher_wrap.h delete mode 100644 makerom/polarssl/ctr_drbg.h delete mode 100644 makerom/polarssl/debug.h delete mode 100644 makerom/polarssl/des.h delete mode 100644 makerom/polarssl/dhm.h delete mode 100644 makerom/polarssl/entropy.h delete mode 100644 makerom/polarssl/entropy_poll.h delete mode 100644 makerom/polarssl/error.h delete mode 100644 makerom/polarssl/gcm.h delete mode 100644 makerom/polarssl/havege.h delete mode 100644 makerom/polarssl/md.c delete mode 100644 makerom/polarssl/md.h delete mode 100644 makerom/polarssl/md2.h delete mode 100644 makerom/polarssl/md4.h delete mode 100644 makerom/polarssl/md5.c delete mode 100644 makerom/polarssl/md5.h delete mode 100644 makerom/polarssl/md_wrap.c delete mode 100644 makerom/polarssl/md_wrap.h delete mode 100644 makerom/polarssl/net.h delete mode 100644 makerom/polarssl/openssl.h delete mode 100644 makerom/polarssl/padlock.c delete mode 100644 makerom/polarssl/padlock.h delete mode 100644 makerom/polarssl/pbkdf2.h delete mode 100644 makerom/polarssl/pem.h delete mode 100644 makerom/polarssl/pkcs11.h delete mode 100644 makerom/polarssl/pkcs12.h delete mode 100644 makerom/polarssl/pkcs5.h delete mode 100644 makerom/polarssl/sha4.c delete mode 100644 makerom/polarssl/sha4.h delete mode 100644 makerom/polarssl/ssl.h delete mode 100644 makerom/polarssl/ssl_cache.h delete mode 100644 makerom/polarssl/timing.h delete mode 100644 makerom/polarssl/version.h delete mode 100644 makerom/polarssl/x509.h delete mode 100644 makerom/polarssl/x509write.h delete mode 100644 makerom/polarssl/xtea.h diff --git a/makerom/crypto.c b/makerom/crypto.c index f654efe2..7231edc1 100644 --- a/makerom/crypto.c +++ b/makerom/crypto.c @@ -189,145 +189,8 @@ int RsaSignVerify(void *data, u64 len, u8 *sign, u8 *mod, u8 *priv_exp, u32 sig_ if(rsa_mode == CTR_RSA_VERIFY) rsa_result = rsa_pkcs1_verify(&ctx, RSA_PUBLIC, GetRsaHashType(sig_type), 0, hash, sign); else // CTR_RSA_SIGN - rsa_result = ctr_rsa_rsassa_pkcs1_v15_sign(&ctx, RSA_PRIVATE, GetRsaHashType(sig_type), 0, hash, sign); + rsa_result = rsa_rsassa_pkcs1_v15_sign(&ctx, RSA_PRIVATE, GetRsaHashType(sig_type), 0, hash, sign); rsa_free(&ctx); return rsa_result; -} - -/** -* Hacked from rsa.c, polarssl doesn't like generating signatures when only D and N are present -**/ -int ctr_rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, - int mode, - int hash_id, - unsigned int hashlen, - const unsigned char *hash, - unsigned char *sig ) -{ - size_t nb_pad, olen, ret; - unsigned char *p = sig; - - if( ctx->padding != RSA_PKCS_V15 ) - return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); - - olen = ctx->len; - - 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 ) || ( nb_pad > olen ) ) - return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); - - *p++ = 0; - *p++ = RSA_SIGN; - memset( p, 0xFF, nb_pad ); - p += nb_pad; - *p++ = 0; - - 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 ); - } - - mpi T, T1, T2; - - mpi_init( &T ); mpi_init( &T1 ); mpi_init( &T2 ); - - MPI_CHK( mpi_read_binary( &T, sig, ctx->len ) ); - - if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) - { - mpi_free( &T ); - return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); - } - - MPI_CHK( mpi_exp_mod( &T, &T, &ctx->D, &ctx->N, &ctx->RN ) ); - - MPI_CHK( mpi_write_binary( &T, sig, olen ) ); - -cleanup: - - mpi_free( &T ); mpi_free( &T1 ); mpi_free( &T2 ); - - return( 0 ); } \ No newline at end of file diff --git a/makerom/polarssl/arc4.h b/makerom/polarssl/arc4.h deleted file mode 100644 index d60be3f2..00000000 --- a/makerom/polarssl/arc4.h +++ /dev/null @@ -1,98 +0,0 @@ -/** - * \file arc4.h - * - * \brief The ARCFOUR stream cipher - * - * Copyright (C) 2006-2013, 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_ARC4_H -#define POLARSSL_ARC4_H - -#include "polarssl/config.h" - -#include - -#if !defined(POLARSSL_ARC4_ALT) -// Regular implementation -// - -/** - * \brief ARC4 context structure - */ -typedef struct -{ - int x; /*!< permutation index */ - int y; /*!< permutation index */ - unsigned char m[256]; /*!< permutation table */ -} -arc4_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief ARC4 key schedule - * - * \param ctx ARC4 context to be initialized - * \param key the secret key - * \param keylen length of the key - */ -void arc4_setup( arc4_context *ctx, const unsigned char *key, unsigned int keylen ); - -/** - * \brief ARC4 cipher function - * - * \param ctx ARC4 context - * \param length length of the input data - * \param input buffer holding the input data - * \param output buffer for the output data - * - * \return 0 if successful - */ -int arc4_crypt( arc4_context *ctx, size_t length, const unsigned char *input, - unsigned char *output ); - -#ifdef __cplusplus -} -#endif - -#else /* POLARSSL_ARC4_ALT */ -#include "polarssl/arc4_alt.h" -#endif /* POLARSSL_ARC4_ALT */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int arc4_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* arc4.h */ diff --git a/makerom/polarssl/asn1.h b/makerom/polarssl/asn1.h deleted file mode 100644 index e21ae098..00000000 --- a/makerom/polarssl/asn1.h +++ /dev/null @@ -1,246 +0,0 @@ -/** - * \file asn1.h - * - * \brief Generic ASN.1 parsing - * - * Copyright (C) 2006-2011, 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_ASN1_H -#define POLARSSL_ASN1_H - -#include "polarssl/config.h" - -#if defined(POLARSSL_BIGNUM_C) -#include "polarssl/bignum.h" -#endif - -#include - -/** - * \addtogroup asn1_module - * \{ - */ - -/** - * \name ASN1 Error codes - * These error codes are OR'ed to X509 error codes for - * higher error granularity. - * ASN1 is a standard to specify data structures. - * \{ - */ -#define POLARSSL_ERR_ASN1_OUT_OF_DATA -0x0060 /**< Out of data when parsing an ASN1 data structure. */ -#define POLARSSL_ERR_ASN1_UNEXPECTED_TAG -0x0062 /**< ASN1 tag was of an unexpected value. */ -#define POLARSSL_ERR_ASN1_INVALID_LENGTH -0x0064 /**< Error when trying to determine the length or invalid length. */ -#define POLARSSL_ERR_ASN1_LENGTH_MISMATCH -0x0066 /**< Actual length differs from expected length. */ -#define POLARSSL_ERR_ASN1_INVALID_DATA -0x0068 /**< Data is invalid. (not used) */ -#define POLARSSL_ERR_ASN1_MALLOC_FAILED -0x006A /**< Memory allocation failed */ -#define POLARSSL_ERR_ASN1_BUF_TOO_SMALL -0x006C /**< Buffer too small when writing ASN.1 data structure. */ - -/* \} name */ - -/** - * \name DER constants - * These constants comply with DER encoded the ANS1 type tags. - * DER encoding uses hexadecimal representation. - * An example DER sequence is:\n - * - 0x02 -- tag indicating INTEGER - * - 0x01 -- length in octets - * - 0x05 -- value - * Such sequences are typically read into \c ::x509_buf. - * \{ - */ -#define ASN1_BOOLEAN 0x01 -#define ASN1_INTEGER 0x02 -#define ASN1_BIT_STRING 0x03 -#define ASN1_OCTET_STRING 0x04 -#define ASN1_NULL 0x05 -#define ASN1_OID 0x06 -#define ASN1_UTF8_STRING 0x0C -#define ASN1_SEQUENCE 0x10 -#define ASN1_SET 0x11 -#define ASN1_PRINTABLE_STRING 0x13 -#define ASN1_T61_STRING 0x14 -#define ASN1_IA5_STRING 0x16 -#define ASN1_UTC_TIME 0x17 -#define ASN1_GENERALIZED_TIME 0x18 -#define ASN1_UNIVERSAL_STRING 0x1C -#define ASN1_BMP_STRING 0x1E -#define ASN1_PRIMITIVE 0x00 -#define ASN1_CONSTRUCTED 0x20 -#define ASN1_CONTEXT_SPECIFIC 0x80 -/* \} name */ -/* \} addtogroup asn1_module */ - -/** Returns the size of the binary string, without the trailing \\0 */ -#define OID_SIZE(x) (sizeof(x) - 1) - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \name Functions to parse ASN.1 data structures - * \{ - */ - -/** - * Type-length-value structure that allows for ASN1 using DER. - */ -typedef struct _asn1_buf -{ - int tag; /**< ASN1 type, e.g. ASN1_UTF8_STRING. */ - size_t len; /**< ASN1 length, e.g. in octets. */ - unsigned char *p; /**< ASN1 data, e.g. in ASCII. */ -} -asn1_buf; - -/** - * Container for ASN1 bit strings. - */ -typedef struct _asn1_bitstring -{ - size_t len; /**< ASN1 length, e.g. in octets. */ - unsigned char unused_bits; /**< Number of unused bits at the end of the string */ - unsigned char *p; /**< Raw ASN1 data for the bit string */ -} -asn1_bitstring; - -/** - * Container for a sequence of ASN.1 items - */ -typedef struct _asn1_sequence -{ - asn1_buf buf; /**< Buffer containing the given ASN.1 item. */ - struct _asn1_sequence *next; /**< The next entry in the sequence. */ -} -asn1_sequence; - -/** - * Get the length of an ASN.1 element. - * Updates the pointer to immediately behind the length. - * - * \param p The position in the ASN.1 data - * \param end End of data - * \param len The variable that will receive the value - * - * \return 0 if successful, POLARSSL_ERR_ASN1_OUT_OF_DATA on reaching - * end of data, POLARSSL_ERR_ASN1_INVALID_LENGTH if length is - * unparseable. - */ -int asn1_get_len( unsigned char **p, - const unsigned char *end, - size_t *len ); - -/** - * Get the tag and length of the tag. Check for the requested tag. - * Updates the pointer to immediately behind the tag and length. - * - * \param p The position in the ASN.1 data - * \param end End of data - * \param len The variable that will receive the length - * \param tag The expected tag - * - * \return 0 if successful, POLARSSL_ERR_ASN1_UNEXPECTED_TAG if tag did - * not match requested tag, or another specific ASN.1 error code. - */ -int asn1_get_tag( unsigned char **p, - const unsigned char *end, - size_t *len, int tag ); - -/** - * Retrieve a boolean ASN.1 tag and its value. - * Updates the pointer to immediately behind the full tag. - * - * \param p The position in the ASN.1 data - * \param end End of data - * \param val The variable that will receive the value - * - * \return 0 if successful or a specific ASN.1 error code. - */ -int asn1_get_bool( unsigned char **p, - const unsigned char *end, - int *val ); - -/** - * Retrieve an integer ASN.1 tag and its value. - * Updates the pointer to immediately behind the full tag. - * - * \param p The position in the ASN.1 data - * \param end End of data - * \param val The variable that will receive the value - * - * \return 0 if successful or a specific ASN.1 error code. - */ -int asn1_get_int( unsigned char **p, - const unsigned char *end, - int *val ); - -/** - * Retrieve a bitstring ASN.1 tag and its value. - * Updates the pointer to immediately behind the full tag. - * - * \param p The position in the ASN.1 data - * \param end End of data - * \param bs The variable that will receive the value - * - * \return 0 if successful or a specific ASN.1 error code. - */ -int asn1_get_bitstring( unsigned char **p, const unsigned char *end, - asn1_bitstring *bs); - -/** - * Parses and splits an ASN.1 "SEQUENCE OF " - * Updated the pointer to immediately behind the full sequence tag. - * - * \param p The position in the ASN.1 data - * \param end End of data - * \param cur First variable in the chain to fill - * \param tag Type of sequence - * - * \return 0 if successful or a specific ASN.1 error code. - */ -int asn1_get_sequence_of( unsigned char **p, - const unsigned char *end, - asn1_sequence *cur, - int tag); - -#if defined(POLARSSL_BIGNUM_C) -/** - * Retrieve a MPI value from an integer ASN.1 tag. - * Updates the pointer to immediately behind the full tag. - * - * \param p The position in the ASN.1 data - * \param end End of data - * \param X The MPI that will receive the value - * - * \return 0 if successful or a specific ASN.1 or MPI error code. - */ -int asn1_get_mpi( unsigned char **p, - const unsigned char *end, - mpi *X ); -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* asn1.h */ diff --git a/makerom/polarssl/asn1write.h b/makerom/polarssl/asn1write.h deleted file mode 100644 index 7ea26800..00000000 --- a/makerom/polarssl/asn1write.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * \file asn1write.h - * - * \brief ASN.1 buffer writing functionality - * - * Copyright (C) 2006-2012, 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_ASN1_WRITE_H -#define POLARSSL_ASN1_WRITE_H - -#include "polarssl/asn1.h" - -#define ASN1_CHK_ADD(g, f) if( ( ret = f ) < 0 ) return( ret ); else g += ret - -int asn1_write_len( unsigned char **p, unsigned char *start, size_t len ); -int asn1_write_tag( unsigned char **p, unsigned char *start, unsigned char tag ); -int asn1_write_mpi( unsigned char **p, unsigned char *start, mpi *X ); -int asn1_write_null( unsigned char **p, unsigned char *start ); -int asn1_write_oid( unsigned char **p, unsigned char *start, char *oid ); -int asn1_write_algorithm_identifier( unsigned char **p, unsigned char *start, char *algorithm_oid ); -int asn1_write_int( unsigned char **p, unsigned char *start, int val ); -int asn1_write_printable_string( unsigned char **p, unsigned char *start, - char *text ); -int asn1_write_ia5_string( unsigned char **p, unsigned char *start, - char *text ); - -#endif /* POLARSSL_ASN1_WRITE_H */ diff --git a/makerom/polarssl/blowfish.h b/makerom/polarssl/blowfish.h deleted file mode 100644 index 0157f8ef..00000000 --- a/makerom/polarssl/blowfish.h +++ /dev/null @@ -1,171 +0,0 @@ -/** - * \file blowfish.h - * - * \brief Blowfish block cipher - * - * Copyright (C) 2012-2013, 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_BLOWFISH_H -#define POLARSSL_BLOWFISH_H - -#include "polarssl/config.h" - -#include - -#ifdef _MSC_VER -#include -typedef UINT32 uint32_t; -#else -#include -#endif - -#define BLOWFISH_ENCRYPT 1 -#define BLOWFISH_DECRYPT 0 -#define BLOWFISH_MAX_KEY 448 -#define BLOWFISH_MIN_KEY 32 -#define BLOWFISH_ROUNDS 16 /* when increasing this value, make sure to extend the initialisation vectors */ -#define BLOWFISH_BLOCKSIZE 8 /* Blowfish uses 64 bit blocks */ - -#define POLARSSL_ERR_BLOWFISH_INVALID_KEY_LENGTH -0x0016 /**< Invalid key length. */ -#define POLARSSL_ERR_BLOWFISH_INVALID_INPUT_LENGTH -0x0018 /**< Invalid data input length. */ - -#if !defined(POLARSSL_BLOWFISH_ALT) -// Regular implementation -// - -/** - * \brief Blowfish context structure - */ -typedef struct -{ - uint32_t P[BLOWFISH_ROUNDS + 2]; /*!< Blowfish round keys */ - uint32_t S[4][256]; /*!< key dependent S-boxes */ -} -blowfish_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Blowfish key schedule - * - * \param ctx Blowfish context to be initialized - * \param key encryption key - * \param keysize must be between 32 and 448 bits - * - * \return 0 if successful, or POLARSSL_ERR_BLOWFISH_INVALID_KEY_LENGTH - */ -int blowfish_setkey( blowfish_context *ctx, const unsigned char *key, unsigned int keysize ); - -/** - * \brief Blowfish-ECB block encryption/decryption - * - * \param ctx Blowfish context - * \param mode BLOWFISH_ENCRYPT or BLOWFISH_DECRYPT - * \param input 8-byte input block - * \param output 8-byte output block - * - * \return 0 if successful - */ -int blowfish_crypt_ecb( blowfish_context *ctx, - int mode, - const unsigned char input[BLOWFISH_BLOCKSIZE], - unsigned char output[BLOWFISH_BLOCKSIZE] ); - -/** - * \brief Blowfish-CBC buffer encryption/decryption - * Length should be a multiple of the block - * size (8 bytes) - * - * \param ctx Blowfish context - * \param mode BLOWFISH_ENCRYPT or BLOWFISH_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_BLOWFISH_INVALID_INPUT_LENGTH - */ -int blowfish_crypt_cbc( blowfish_context *ctx, - int mode, - size_t length, - unsigned char iv[BLOWFISH_BLOCKSIZE], - const unsigned char *input, - unsigned char *output ); - -/** - * \brief Blowfish CFB buffer encryption/decryption. - * - * both - * \param ctx Blowfish context - * \param mode BLOWFISH_ENCRYPT or BLOWFISH_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 blowfish_crypt_cfb64( blowfish_context *ctx, - int mode, - size_t length, - size_t *iv_off, - unsigned char iv[BLOWFISH_BLOCKSIZE], - const unsigned char *input, - unsigned char *output ); - -/** - * \brief Blowfish-CTR buffer encryption/decryption - * - * Warning: You have to keep the maximum use of your counter in mind! - * - * \param length The length of the data - * \param nc_off The offset in the current stream_block (for resuming - * within current cipher stream). The offset pointer to - * should be 0 at the start of a stream. - * \param nonce_counter The 64-bit nonce and counter. - * \param stream_block The saved stream-block for resuming. Is overwritten - * by the function. - * \param input The input data stream - * \param output The output data stream - * - * \return 0 if successful - */ -int blowfish_crypt_ctr( blowfish_context *ctx, - size_t length, - size_t *nc_off, - unsigned char nonce_counter[BLOWFISH_BLOCKSIZE], - unsigned char stream_block[BLOWFISH_BLOCKSIZE], - const unsigned char *input, - unsigned char *output ); - -#ifdef __cplusplus -} -#endif - -#else /* POLARSSL_BLOWFISH_ALT */ -#include "polarssl/blowfish_alt.h" -#endif /* POLARSSL_BLOWFISH_ALT */ - -#endif /* blowfish.h */ diff --git a/makerom/polarssl/camellia.h b/makerom/polarssl/camellia.h deleted file mode 100644 index 3bd3bc30..00000000 --- a/makerom/polarssl/camellia.h +++ /dev/null @@ -1,200 +0,0 @@ -/** - * \file camellia.h - * - * \brief Camellia block cipher - * - * Copyright (C) 2006-2013, 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_CAMELLIA_H -#define POLARSSL_CAMELLIA_H - -#include "polarssl/config.h" - -#include - -#ifdef _MSC_VER -#include -typedef UINT32 uint32_t; -#else -#include -#endif - -#define CAMELLIA_ENCRYPT 1 -#define CAMELLIA_DECRYPT 0 - -#define POLARSSL_ERR_CAMELLIA_INVALID_KEY_LENGTH -0x0024 /**< Invalid key length. */ -#define POLARSSL_ERR_CAMELLIA_INVALID_INPUT_LENGTH -0x0026 /**< Invalid data input length. */ - -#if !defined(POLARSSL_CAMELLIA_ALT) -// Regular implementation -// - -/** - * \brief CAMELLIA context structure - */ -typedef struct -{ - int nr; /*!< number of rounds */ - uint32_t rk[68]; /*!< CAMELLIA round keys */ -} -camellia_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief CAMELLIA key schedule (encryption) - * - * \param ctx CAMELLIA context to be initialized - * \param key encryption key - * \param keysize must be 128, 192 or 256 - * - * \return 0 if successful, or POLARSSL_ERR_CAMELLIA_INVALID_KEY_LENGTH - */ -int camellia_setkey_enc( camellia_context *ctx, const unsigned char *key, unsigned int keysize ); - -/** - * \brief CAMELLIA key schedule (decryption) - * - * \param ctx CAMELLIA context to be initialized - * \param key decryption key - * \param keysize must be 128, 192 or 256 - * - * \return 0 if successful, or POLARSSL_ERR_CAMELLIA_INVALID_KEY_LENGTH - */ -int camellia_setkey_dec( camellia_context *ctx, const unsigned char *key, unsigned int keysize ); - -/** - * \brief CAMELLIA-ECB block encryption/decryption - * - * \param ctx CAMELLIA context - * \param mode CAMELLIA_ENCRYPT or CAMELLIA_DECRYPT - * \param input 16-byte input block - * \param output 16-byte output block - * - * \return 0 if successful - */ -int camellia_crypt_ecb( camellia_context *ctx, - int mode, - const unsigned char input[16], - unsigned char output[16] ); - -/** - * \brief CAMELLIA-CBC buffer encryption/decryption - * Length should be a multiple of the block - * size (16 bytes) - * - * \param ctx CAMELLIA context - * \param mode CAMELLIA_ENCRYPT or CAMELLIA_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_CAMELLIA_INVALID_INPUT_LENGTH - */ -int camellia_crypt_cbc( camellia_context *ctx, - int mode, - size_t length, - unsigned char iv[16], - const unsigned char *input, - unsigned char *output ); - -/** - * \brief CAMELLIA-CFB128 buffer encryption/decryption - * - * Note: Due to the nature of CFB you should use the same key schedule for - * both encryption and decryption. So a context initialized with - * camellia_setkey_enc() for both CAMELLIA_ENCRYPT and CAMELLIE_DECRYPT. - * - * \param ctx CAMELLIA context - * \param mode CAMELLIA_ENCRYPT or CAMELLIA_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, or POLARSSL_ERR_CAMELLIA_INVALID_INPUT_LENGTH - */ -int camellia_crypt_cfb128( camellia_context *ctx, - int mode, - size_t length, - size_t *iv_off, - unsigned char iv[16], - const unsigned char *input, - unsigned char *output ); - -/** - * \brief CAMELLIA-CTR buffer encryption/decryption - * - * Warning: You have to keep the maximum use of your counter in mind! - * - * Note: Due to the nature of CTR you should use the same key schedule for - * both encryption and decryption. So a context initialized with - * camellia_setkey_enc() for both CAMELLIA_ENCRYPT and CAMELLIA_DECRYPT. - * - * \param length The length of the data - * \param nc_off The offset in the current stream_block (for resuming - * within current cipher stream). The offset pointer to - * should be 0 at the start of a stream. - * \param nonce_counter The 128-bit nonce and counter. - * \param stream_block The saved stream-block for resuming. Is overwritten - * by the function. - * \param input The input data stream - * \param output The output data stream - * - * \return 0 if successful - */ -int camellia_crypt_ctr( camellia_context *ctx, - size_t length, - size_t *nc_off, - unsigned char nonce_counter[16], - unsigned char stream_block[16], - const unsigned char *input, - unsigned char *output ); - -#ifdef __cplusplus -} -#endif - -#else /* POLARSSL_CAMELLIA_ALT */ -#include "polarssl/camellia_alt.h" -#endif /* POLARSSL_CAMELLIA_ALT */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int camellia_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* camellia.h */ diff --git a/makerom/polarssl/certs.h b/makerom/polarssl/certs.h deleted file mode 100644 index 5399e326..00000000 --- a/makerom/polarssl/certs.h +++ /dev/null @@ -1,47 +0,0 @@ -/** - * \file certs.h - * - * \brief Sample certificates and DHM parameters for testing - * - * 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_CERTS_H -#define POLARSSL_CERTS_H - -#ifdef __cplusplus -extern "C" { -#endif - -extern const char test_ca_crt[]; -extern const char test_ca_key[]; -extern const char test_ca_pwd[]; -extern const char test_srv_crt[]; -extern const char test_srv_key[]; -extern const char test_cli_crt[]; -extern const char test_cli_key[]; -extern const char test_dhm_params[]; - -#ifdef __cplusplus -} -#endif - -#endif /* certs.h */ diff --git a/makerom/polarssl/cipher.h b/makerom/polarssl/cipher.h deleted file mode 100644 index 8224128e..00000000 --- a/makerom/polarssl/cipher.h +++ /dev/null @@ -1,463 +0,0 @@ -/** - * \file cipher.h - * - * \brief Generic cipher wrapper. - * - * \author Adriaan de Jong - * - * Copyright (C) 2006-2012, 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_CIPHER_H -#define POLARSSL_CIPHER_H - -#include - -#if defined(_MSC_VER) && !defined(inline) -#define inline _inline -#else -#if defined(__ARMCC_VERSION) && !defined(inline) -#define inline __inline -#endif /* __ARMCC_VERSION */ -#endif /*_MSC_VER */ - -#define POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE -0x6080 /**< The selected feature is not available. */ -#define POLARSSL_ERR_CIPHER_BAD_INPUT_DATA -0x6100 /**< Bad input parameters to function. */ -#define POLARSSL_ERR_CIPHER_ALLOC_FAILED -0x6180 /**< Failed to allocate memory. */ -#define POLARSSL_ERR_CIPHER_INVALID_PADDING -0x6200 /**< Input data contains invalid padding and is rejected. */ -#define POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED -0x6280 /**< Decryption of block requires a full block. */ - -typedef enum { - POLARSSL_CIPHER_ID_NONE = 0, - POLARSSL_CIPHER_ID_NULL, - POLARSSL_CIPHER_ID_AES, - POLARSSL_CIPHER_ID_DES, - POLARSSL_CIPHER_ID_3DES, - POLARSSL_CIPHER_ID_CAMELLIA, - POLARSSL_CIPHER_ID_BLOWFISH, -} cipher_id_t; - -typedef enum { - POLARSSL_CIPHER_NONE = 0, - POLARSSL_CIPHER_NULL, - POLARSSL_CIPHER_AES_128_CBC, - POLARSSL_CIPHER_AES_192_CBC, - POLARSSL_CIPHER_AES_256_CBC, - POLARSSL_CIPHER_AES_128_CFB128, - POLARSSL_CIPHER_AES_192_CFB128, - POLARSSL_CIPHER_AES_256_CFB128, - POLARSSL_CIPHER_AES_128_CTR, - POLARSSL_CIPHER_AES_192_CTR, - POLARSSL_CIPHER_AES_256_CTR, - POLARSSL_CIPHER_CAMELLIA_128_CBC, - POLARSSL_CIPHER_CAMELLIA_192_CBC, - POLARSSL_CIPHER_CAMELLIA_256_CBC, - POLARSSL_CIPHER_CAMELLIA_128_CFB128, - POLARSSL_CIPHER_CAMELLIA_192_CFB128, - POLARSSL_CIPHER_CAMELLIA_256_CFB128, - POLARSSL_CIPHER_CAMELLIA_128_CTR, - POLARSSL_CIPHER_CAMELLIA_192_CTR, - POLARSSL_CIPHER_CAMELLIA_256_CTR, - POLARSSL_CIPHER_DES_CBC, - POLARSSL_CIPHER_DES_EDE_CBC, - POLARSSL_CIPHER_DES_EDE3_CBC, - POLARSSL_CIPHER_BLOWFISH_CBC, - POLARSSL_CIPHER_BLOWFISH_CFB64, - POLARSSL_CIPHER_BLOWFISH_CTR, -} cipher_type_t; - -typedef enum { - POLARSSL_MODE_NONE = 0, - POLARSSL_MODE_NULL, - POLARSSL_MODE_CBC, - POLARSSL_MODE_CFB, - POLARSSL_MODE_OFB, - POLARSSL_MODE_CTR, -} cipher_mode_t; - -typedef enum { - POLARSSL_OPERATION_NONE = -1, - POLARSSL_DECRYPT = 0, - POLARSSL_ENCRYPT, -} operation_t; - -enum { - /** Undefined key length */ - POLARSSL_KEY_LENGTH_NONE = 0, - /** Key length, in bits (including parity), for DES keys */ - POLARSSL_KEY_LENGTH_DES = 64, - /** Key length, in bits (including parity), for DES in two key EDE */ - POLARSSL_KEY_LENGTH_DES_EDE = 128, - /** Key length, in bits (including parity), for DES in three-key EDE */ - POLARSSL_KEY_LENGTH_DES_EDE3 = 192, - /** Maximum length of any IV, in bytes */ - POLARSSL_MAX_IV_LENGTH = 16, -}; - -/** - * Base cipher information. The non-mode specific functions and values. - */ -typedef struct { - - /** Base Cipher type (e.g. POLARSSL_CIPHER_ID_AES) */ - cipher_id_t cipher; - - /** Encrypt using CBC */ - int (*cbc_func)( void *ctx, operation_t mode, size_t length, unsigned char *iv, - const unsigned char *input, unsigned char *output ); - - /** Encrypt using CFB (Full length) */ - int (*cfb_func)( void *ctx, operation_t mode, size_t length, size_t *iv_off, - unsigned char *iv, const unsigned char *input, unsigned char *output ); - - /** Encrypt using CTR */ - int (*ctr_func)( void *ctx, size_t length, size_t *nc_off, unsigned char *nonce_counter, - unsigned char *stream_block, const unsigned char *input, unsigned char *output ); - - /** Set key for encryption purposes */ - int (*setkey_enc_func)( void *ctx, const unsigned char *key, unsigned int key_length); - - /** Set key for decryption purposes */ - int (*setkey_dec_func)( void *ctx, const unsigned char *key, unsigned int key_length); - - /** Allocate a new context */ - void * (*ctx_alloc_func)( void ); - - /** Free the given context */ - void (*ctx_free_func)( void *ctx ); - -} cipher_base_t; - -/** - * Cipher information. Allows cipher functions to be called in a generic way. - */ -typedef struct { - /** Full cipher identifier (e.g. POLARSSL_CIPHER_AES_256_CBC) */ - cipher_type_t type; - - /** Cipher mode (e.g. POLARSSL_MODE_CBC) */ - cipher_mode_t mode; - - /** Cipher key length, in bits (default length for variable sized ciphers) - * (Includes parity bits for ciphers like DES) */ - unsigned int key_length; - - /** Name of the cipher */ - const char * name; - - /** IV size, in bytes */ - unsigned int iv_size; - - /** block size, in bytes */ - unsigned int block_size; - - /** Base cipher information and functions */ - const cipher_base_t *base; - -} cipher_info_t; - -/** - * Generic cipher context. - */ -typedef struct { - /** Information about the associated cipher */ - const cipher_info_t *cipher_info; - - /** Key length to use */ - int key_length; - - /** Operation that the context's key has been initialised for */ - operation_t operation; - - /** Buffer for data that hasn't been encrypted yet */ - unsigned char unprocessed_data[POLARSSL_MAX_IV_LENGTH]; - - /** Number of bytes that still need processing */ - size_t unprocessed_len; - - /** Current IV or NONCE_COUNTER for CTR-mode */ - unsigned char iv[POLARSSL_MAX_IV_LENGTH]; - - /** Cipher-specific context */ - void *cipher_ctx; -} cipher_context_t; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Returns the list of ciphers supported by the generic cipher module. - * - * \return a statically allocated array of ciphers, the last entry - * is 0. - */ -const int *cipher_list( void ); - -/** - * \brief Returns the cipher information structure associated - * with the given cipher name. - * - * \param cipher_name Name of the cipher to search for. - * - * \return the cipher information structure associated with the - * given cipher_name, or NULL if not found. - */ -const cipher_info_t *cipher_info_from_string( const char *cipher_name ); - -/** - * \brief Returns the cipher information structure associated - * with the given cipher type. - * - * \param cipher_type Type of the cipher to search for. - * - * \return the cipher information structure associated with the - * given cipher_type, or NULL if not found. - */ -const cipher_info_t *cipher_info_from_type( const cipher_type_t cipher_type ); - -/** - * \brief Initialises and fills the cipher context structure with - * the appropriate values. - * - * \param ctx context to initialise. May not be NULL. - * \param cipher_info cipher to use. - * - * \return \c 0 on success, - * \c POLARSSL_ERR_CIPHER_BAD_INPUT_DATA on parameter failure, - * \c POLARSSL_ERR_CIPHER_ALLOC_FAILED if allocation of the - * cipher-specific context failed. - */ -int cipher_init_ctx( cipher_context_t *ctx, const cipher_info_t *cipher_info ); - -/** - * \brief Free the cipher-specific context of ctx. Freeing ctx - * itself remains the responsibility of the caller. - * - * \param ctx Free the cipher-specific context - * - * \returns 0 on success, POLARSSL_ERR_CIPHER_BAD_INPUT_DATA if - * parameter verification fails. - */ -int cipher_free_ctx( cipher_context_t *ctx ); - -/** - * \brief Returns the block size of the given cipher. - * - * \param ctx cipher's context. Must have been initialised. - * - * \return size of the cipher's blocks, or 0 if ctx has not been - * initialised. - */ -static inline unsigned int cipher_get_block_size( const cipher_context_t *ctx ) -{ - if( NULL == ctx || NULL == ctx->cipher_info ) - return 0; - - return ctx->cipher_info->block_size; -} - -/** - * \brief Returns the mode of operation for the cipher. - * (e.g. POLARSSL_MODE_CBC) - * - * \param ctx cipher's context. Must have been initialised. - * - * \return mode of operation, or POLARSSL_MODE_NONE if ctx - * has not been initialised. - */ -static inline cipher_mode_t cipher_get_cipher_mode( const cipher_context_t *ctx ) -{ - if( NULL == ctx || NULL == ctx->cipher_info ) - return POLARSSL_MODE_NONE; - - return ctx->cipher_info->mode; -} - -/** - * \brief Returns the size of the cipher's IV. - * - * \param ctx cipher's context. Must have been initialised. - * - * \return size of the cipher's IV, or 0 if ctx has not been - * initialised. - */ -static inline int cipher_get_iv_size( const cipher_context_t *ctx ) -{ - if( NULL == ctx || NULL == ctx->cipher_info ) - return 0; - - return ctx->cipher_info->iv_size; -} - -/** - * \brief Returns the type of the given cipher. - * - * \param ctx cipher's context. Must have been initialised. - * - * \return type of the cipher, or POLARSSL_CIPHER_NONE if ctx has - * not been initialised. - */ -static inline cipher_type_t cipher_get_type( const cipher_context_t *ctx ) -{ - if( NULL == ctx || NULL == ctx->cipher_info ) - return POLARSSL_CIPHER_NONE; - - return ctx->cipher_info->type; -} - -/** - * \brief Returns the name of the given cipher, as a string. - * - * \param ctx cipher's context. Must have been initialised. - * - * \return name of the cipher, or NULL if ctx was not initialised. - */ -static inline const char *cipher_get_name( const cipher_context_t *ctx ) -{ - if( NULL == ctx || NULL == ctx->cipher_info ) - return 0; - - return ctx->cipher_info->name; -} - -/** - * \brief Returns the key length of the cipher. - * - * \param ctx cipher's context. Must have been initialised. - * - * \return cipher's key length, in bits, or - * POLARSSL_KEY_LENGTH_NONE if ctx has not been - * initialised. - */ -static inline int cipher_get_key_size ( const cipher_context_t *ctx ) -{ - if( NULL == ctx ) - return POLARSSL_KEY_LENGTH_NONE; - - return ctx->key_length; -} - -/** - * \brief Returns the operation of the given cipher. - * - * \param ctx cipher's context. Must have been initialised. - * - * \return operation (POLARSSL_ENCRYPT or POLARSSL_DECRYPT), - * or POLARSSL_OPERATION_NONE if ctx has not been - * initialised. - */ -static inline operation_t cipher_get_operation( const cipher_context_t *ctx ) -{ - if( NULL == ctx || NULL == ctx->cipher_info ) - return POLARSSL_OPERATION_NONE; - - return ctx->operation; -} - -/** - * \brief Set the key to use with the given context. - * - * \param ctx generic cipher context. May not be NULL. Must have been - * initialised using cipher_context_from_type or - * cipher_context_from_string. - * \param key The key to use. - * \param key_length key length to use, in bits. - * \param operation Operation that the key will be used for, either - * POLARSSL_ENCRYPT or POLARSSL_DECRYPT. - * - * \returns 0 on success, POLARSSL_ERR_CIPHER_BAD_INPUT_DATA if - * parameter verification fails or a cipher specific - * error code. - */ -int cipher_setkey( cipher_context_t *ctx, const unsigned char *key, int key_length, - const operation_t operation ); - -/** - * \brief Reset the given context, setting the IV to iv - * - * \param ctx generic cipher context - * \param iv IV to use or NONCE_COUNTER in the case of a CTR-mode cipher - * - * \returns 0 on success, POLARSSL_ERR_CIPHER_BAD_INPUT_DATA - * if parameter verification fails. - */ -int cipher_reset( cipher_context_t *ctx, const unsigned char *iv ); - -/** - * \brief Generic cipher update function. Encrypts/decrypts - * using the given cipher context. Writes as many block - * size'd blocks of data as possible to output. Any data - * that cannot be written immediately will either be added - * to the next block, or flushed when cipher_final is - * called. - * - * \param ctx generic cipher context - * \param input buffer holding the input data - * \param ilen length of the input data - * \param output buffer for the output data. Should be able to hold at - * least ilen + block_size. Cannot be the same buffer as - * input! - * \param olen length of the output data, will be filled with the - * actual number of bytes written. - * - * \returns 0 on success, POLARSSL_ERR_CIPHER_BAD_INPUT_DATA if - * parameter verification fails, - * POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE on an - * unsupported mode for a cipher or a cipher specific - * error code. - */ -int cipher_update( cipher_context_t *ctx, const unsigned char *input, size_t ilen, - unsigned char *output, size_t *olen ); - -/** - * \brief Generic cipher finalisation function. If data still - * needs to be flushed from an incomplete block, data - * contained within it will be padded with the size of - * the last block, and written to the output buffer. - * - * \param ctx Generic cipher context - * \param output buffer to write data to. Needs block_size data available. - * \param olen length of the data written to the output buffer. - * - * \returns 0 on success, POLARSSL_ERR_CIPHER_BAD_INPUT_DATA if - * parameter verification fails, - * POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED if decryption - * expected a full block but was not provided one, - * POLARSSL_ERR_CIPHER_INVALID_PADDING on invalid padding - * while decrypting or a cipher specific error code. - */ -int cipher_finish( cipher_context_t *ctx, unsigned char *output, size_t *olen); - - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int cipher_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* POLARSSL_MD_H */ diff --git a/makerom/polarssl/cipher_wrap.h b/makerom/polarssl/cipher_wrap.h deleted file mode 100644 index 2f18effa..00000000 --- a/makerom/polarssl/cipher_wrap.h +++ /dev/null @@ -1,107 +0,0 @@ -/** - * \file cipher_wrap.h - * - * \brief Cipher wrappers. - * - * \author Adriaan de Jong - * - * Copyright (C) 2006-2012, 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_CIPHER_WRAP_H -#define POLARSSL_CIPHER_WRAP_H - -#include "polarssl/config.h" -#include "polarssl/cipher.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(POLARSSL_AES_C) - -extern const cipher_info_t aes_128_cbc_info; -extern const cipher_info_t aes_192_cbc_info; -extern const cipher_info_t aes_256_cbc_info; - -#if defined(POLARSSL_CIPHER_MODE_CFB) -extern const cipher_info_t aes_128_cfb128_info; -extern const cipher_info_t aes_192_cfb128_info; -extern const cipher_info_t aes_256_cfb128_info; -#endif /* POLARSSL_CIPHER_MODE_CFB */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) -extern const cipher_info_t aes_128_ctr_info; -extern const cipher_info_t aes_192_ctr_info; -extern const cipher_info_t aes_256_ctr_info; -#endif /* POLARSSL_CIPHER_MODE_CTR */ - -#endif /* defined(POLARSSL_AES_C) */ - -#if defined(POLARSSL_CAMELLIA_C) - -extern const cipher_info_t camellia_128_cbc_info; -extern const cipher_info_t camellia_192_cbc_info; -extern const cipher_info_t camellia_256_cbc_info; - -#if defined(POLARSSL_CIPHER_MODE_CFB) -extern const cipher_info_t camellia_128_cfb128_info; -extern const cipher_info_t camellia_192_cfb128_info; -extern const cipher_info_t camellia_256_cfb128_info; -#endif /* POLARSSL_CIPHER_MODE_CFB */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) -extern const cipher_info_t camellia_128_ctr_info; -extern const cipher_info_t camellia_192_ctr_info; -extern const cipher_info_t camellia_256_ctr_info; -#endif /* POLARSSL_CIPHER_MODE_CTR */ - -#endif /* defined(POLARSSL_CAMELLIA_C) */ - -#if defined(POLARSSL_DES_C) - -extern const cipher_info_t des_cbc_info; -extern const cipher_info_t des_ede_cbc_info; -extern const cipher_info_t des_ede3_cbc_info; - -#endif /* defined(POLARSSL_DES_C) */ - -#if defined(POLARSSL_BLOWFISH_C) -extern const cipher_info_t blowfish_cbc_info; - -#if defined(POLARSSL_CIPHER_MODE_CFB) -extern const cipher_info_t blowfish_cfb64_info; -#endif /* POLARSSL_CIPHER_MODE_CFB */ - -#if defined(POLARSSL_CIPHER_MODE_CTR) -extern const cipher_info_t blowfish_ctr_info; -#endif /* POLARSSL_CIPHER_MODE_CTR */ -#endif /* defined(POLARSSL_BLOWFISH_C) */ - -#if defined(POLARSSL_CIPHER_NULL_CIPHER) -extern const cipher_info_t null_cipher_info; -#endif /* defined(POLARSSL_CIPHER_NULL_CIPHER) */ - -#ifdef __cplusplus -} -#endif - -#endif /* POLARSSL_CIPHER_WRAP_H */ diff --git a/makerom/polarssl/config.h b/makerom/polarssl/config.h index bd1a4663..2c43ff4d 100644 --- a/makerom/polarssl/config.h +++ b/makerom/polarssl/config.h @@ -83,7 +83,7 @@ * include/polarssl/bn_mul.h * */ -#define POLARSSL_HAVE_ASM +//#define POLARSSL_HAVE_ASM /** * \def POLARSSL_HAVE_SSE2 @@ -148,7 +148,7 @@ * * Enable Cipher Feedback mode (CFB) for symmetric ciphers. */ -#define POLARSSL_CIPHER_MODE_CFB +//#define POLARSSL_CIPHER_MODE_CFB /** * \def POLARSSL_CIPHER_MODE_CTR @@ -198,7 +198,7 @@ * Disable if you run into name conflicts and want to really remove the * error_strerror() */ -#define POLARSSL_ERROR_STRERROR_DUMMY +//#define POLARSSL_ERROR_STRERROR_DUMMY /** * \def POLARSSL_GENPRIME @@ -207,14 +207,14 @@ * * Enable the RSA prime-number generation code. */ -#define POLARSSL_GENPRIME +//#define POLARSSL_GENPRIME /** * \def POLARSSL_FS_IO * * Enable functions that use the filesystem. */ -#define POLARSSL_FS_IO +//#define POLARSSL_FS_IO /** * \def POLARSSL_NO_DEFAULT_ENTROPY_SOURCES @@ -248,7 +248,7 @@ * Enable support for PKCS#1 v2.1 encoding. * This enables support for RSAES-OAEP and RSASSA-PSS operations. */ -#define POLARSSL_PKCS1_V21 +//#define POLARSSL_PKCS1_V21 /** * \def POLARSSL_RSA_NO_CRT @@ -257,15 +257,16 @@ * * Uncomment this macro to disable the use of CRT in RSA. * -#define POLARSSL_RSA_NO_CRT */ +#define POLARSSL_RSA_NO_CRT + /** * \def POLARSSL_SELF_TEST * * Enable the checkup functions (*_self_test). */ -#define POLARSSL_SELF_TEST +//#define POLARSSL_SELF_TEST /** * \def POLARSSL_SSL_ALL_ALERT_MESSAGES @@ -279,7 +280,7 @@ * * Enable sending of all alert messages */ -#define POLARSSL_SSL_ALERT_MESSAGES +//#define POLARSSL_SSL_ALERT_MESSAGES /** * \def POLARSSL_SSL_DEBUG_ALL @@ -315,7 +316,7 @@ * * Comment this macro to disable support for SSLv2 Client Hello messages. */ -#define POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO +//#define POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO /** * \def POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION @@ -391,7 +392,7 @@ * TLS_RSA_WITH_RC4_128_MD5 * TLS_RSA_WITH_RC4_128_SHA */ -#define POLARSSL_ARC4_C +//#define POLARSSL_ARC4_C /** * \def POLARSSL_ASN1_PARSE_C @@ -401,7 +402,7 @@ * Module: library/asn1.c * Caller: library/x509parse.c */ -#define POLARSSL_ASN1_PARSE_C +//#define POLARSSL_ASN1_PARSE_C /** * \def POLARSSL_ASN1_WRITE_C @@ -410,7 +411,7 @@ * * Module: library/asn1write.c */ -#define POLARSSL_ASN1_WRITE_C +//#define POLARSSL_ASN1_WRITE_C /** * \def POLARSSL_BASE64_C @@ -446,7 +447,7 @@ * * Module: library/blowfish.c */ -#define POLARSSL_BLOWFISH_C +//#define POLARSSL_BLOWFISH_C /** * \def POLARSSL_CAMELLIA_C @@ -467,7 +468,7 @@ * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 */ -#define POLARSSL_CAMELLIA_C +//#define POLARSSL_CAMELLIA_C /** * \def POLARSSL_CERTS_C @@ -479,7 +480,7 @@ * * This module is used for testing (ssl_client/server). */ -#define POLARSSL_CERTS_C +//#define POLARSSL_CERTS_C /** * \def POLARSSL_CIPHER_C @@ -491,7 +492,7 @@ * * Uncomment to enable generic cipher wrappers. */ -#define POLARSSL_CIPHER_C +//#define POLARSSL_CIPHER_C /** * \def POLARSSL_CTR_DRBG_C @@ -505,7 +506,7 @@ * * This module provides the CTR_DRBG AES-256 random number generator. */ -#define POLARSSL_CTR_DRBG_C +//#define POLARSSL_CTR_DRBG_C /** * \def POLARSSL_DEBUG_C @@ -519,7 +520,7 @@ * * This module provides debugging functions. */ -#define POLARSSL_DEBUG_C +//#define POLARSSL_DEBUG_C /** * \def POLARSSL_DES_C @@ -537,7 +538,7 @@ * * PEM uses DES/3DES for decrypting encrypted keys. */ -#define POLARSSL_DES_C +//#define POLARSSL_DES_C /** * \def POLARSSL_DHM_C @@ -563,7 +564,7 @@ * TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 * TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 */ -#define POLARSSL_DHM_C +//#define POLARSSL_DHM_C /** * \def POLARSSL_ENTROPY_C @@ -577,7 +578,7 @@ * * This module provides a generic entropy pool */ -#define POLARSSL_ENTROPY_C +//#define POLARSSL_ENTROPY_C /** * \def POLARSSL_ERROR_C @@ -589,7 +590,7 @@ * * This module enables err_strerror(). */ -#define POLARSSL_ERROR_C +//#define POLARSSL_ERROR_C /** * \def POLARSSL_GCM_C @@ -605,7 +606,7 @@ * TLS_RSA_WITH_AES_128_GCM_SHA256 * TLS_RSA_WITH_AES_256_GCM_SHA384 */ -#define POLARSSL_GCM_C +//#define POLARSSL_GCM_C /** * \def POLARSSL_HAVEGE_C @@ -640,7 +641,7 @@ * * Uncomment to enable generic message digest wrappers. */ -#define POLARSSL_MD_C +//#define POLARSSL_MD_C /** * \def POLARSSL_MD2_C @@ -681,7 +682,7 @@ * This module is required for SSL/TLS and X.509. * PEM uses MD5 for decrypting encrypted keys. */ -#define POLARSSL_MD5_C +//#define POLARSSL_MD5_C /** * \def POLARSSL_NET_C @@ -693,7 +694,7 @@ * * This module provides TCP/IP networking routines. */ -#define POLARSSL_NET_C +//#define POLARSSL_NET_C /** * \def POLARSSL_PADLOCK_C @@ -705,7 +706,7 @@ * * This modules adds support for the VIA PadLock on x86. */ -#define POLARSSL_PADLOCK_C +//#define POLARSSL_PADLOCK_C /** * \def POLARSSL_PBKDF2_C @@ -733,7 +734,7 @@ * * This modules adds support for decoding PEM files. */ -#define POLARSSL_PEM_C +//#define POLARSSL_PEM_C /** * \def POLARSSL_PKCS5_C @@ -746,7 +747,7 @@ * * This module adds support for the PKCS#5 functions. */ -#define POLARSSL_PKCS5_C +//#define POLARSSL_PKCS5_C /** * \def POLARSSL_PKCS11_C @@ -778,7 +779,7 @@ * * This module enables PKCS#12 functions. */ -#define POLARSSL_PKCS12_C +//#define POLARSSL_PKCS12_C /** * \def POLARSSL_RSA_C @@ -837,7 +838,7 @@ * * This module adds support for SHA-384 and SHA-512. */ -#define POLARSSL_SHA4_C +//#define POLARSSL_SHA4_C /** * \def POLARSSL_SSL_CACHE_C @@ -849,7 +850,7 @@ * * Requires: POLARSSL_SSL_CACHE_C */ -#define POLARSSL_SSL_CACHE_C +//#define POLARSSL_SSL_CACHE_C /** * \def POLARSSL_SSL_CLI_C @@ -863,7 +864,7 @@ * * This module is required for SSL/TLS client support. */ -#define POLARSSL_SSL_CLI_C +//#define POLARSSL_SSL_CLI_C /** * \def POLARSSL_SSL_SRV_C @@ -877,7 +878,7 @@ * * This module is required for SSL/TLS server support. */ -#define POLARSSL_SSL_SRV_C +//#define POLARSSL_SSL_SRV_C /** * \def POLARSSL_SSL_TLS_C @@ -892,7 +893,7 @@ * * This module is required for SSL/TLS. */ -#define POLARSSL_SSL_TLS_C +//#define POLARSSL_SSL_TLS_C /** * \def POLARSSL_TIMING_C @@ -904,7 +905,7 @@ * * This module is used by the HAVEGE random number generator. */ -#define POLARSSL_TIMING_C +//#define POLARSSL_TIMING_C /** * \def POLARSSL_VERSION_C @@ -915,7 +916,7 @@ * * This module provides run-time version information. */ -#define POLARSSL_VERSION_C +//#define POLARSSL_VERSION_C /** * \def POLARSSL_X509_PARSE_C @@ -931,7 +932,7 @@ * * This module is required for X.509 certificate parsing. */ -#define POLARSSL_X509_PARSE_C +//#define POLARSSL_X509_PARSE_C /** * \def POLARSSL_X509_WRITE_C @@ -944,7 +945,7 @@ * * This module is required for X.509 certificate request writing. */ -#define POLARSSL_X509_WRITE_C +//#define POLARSSL_X509_WRITE_C /** * \def POLARSSL_XTEA_C @@ -954,7 +955,7 @@ * Module: library/xtea.c * Caller: */ -#define POLARSSL_XTEA_C +//#define POLARSSL_XTEA_C /* \} name */ /** diff --git a/makerom/polarssl/ctr_drbg.h b/makerom/polarssl/ctr_drbg.h deleted file mode 100644 index c75feda8..00000000 --- a/makerom/polarssl/ctr_drbg.h +++ /dev/null @@ -1,231 +0,0 @@ -/** - * \file ctr_drbg.h - * - * \brief CTR_DRBG based on AES-256 (NIST SP 800-90) - * - * Copyright (C) 2006-2013, 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_CTR_DRBG_H -#define POLARSSL_CTR_DRBG_H - -#include - -#include "polarssl/aes.h" - -#define POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED -0x0034 /**< The entropy source failed. */ -#define POLARSSL_ERR_CTR_DRBG_REQUEST_TOO_BIG -0x0036 /**< Too many random requested in single call. */ -#define POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG -0x0038 /**< Input too large (Entropy + additional). */ -#define POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR -0x003A /**< Read/write error in file. */ - -#define CTR_DRBG_BLOCKSIZE 16 /**< Block size used by the cipher */ -#define CTR_DRBG_KEYSIZE 32 /**< Key size used by the cipher */ -#define CTR_DRBG_KEYBITS ( CTR_DRBG_KEYSIZE * 8 ) -#define CTR_DRBG_SEEDLEN ( CTR_DRBG_KEYSIZE + CTR_DRBG_BLOCKSIZE ) - /**< The seed length (counter + AES key) */ - -#if !defined(POLARSSL_CONFIG_OPTIONS) -#define CTR_DRBG_ENTROPY_LEN 48 /**< Amount of entropy used per seed by default */ -#define CTR_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ -#define CTR_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ -#define CTR_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ -#define CTR_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ -#endif /* !POLARSSL_CONFIG_OPTIONS */ - -#define CTR_DRBG_PR_OFF 0 /**< No prediction resistance */ -#define CTR_DRBG_PR_ON 1 /**< Prediction resistance enabled */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief CTR_DRBG context structure - */ -typedef struct -{ - unsigned char counter[16]; /*!< counter (V) */ - int reseed_counter; /*!< reseed counter */ - int prediction_resistance; /*!< enable prediction resistance (Automatic - reseed before every random generation) */ - size_t entropy_len; /*!< amount of entropy grabbed on each (re)seed */ - int reseed_interval; /*!< reseed interval */ - - aes_context aes_ctx; /*!< AES context */ - - /* - * Callbacks (Entropy) - */ - int (*f_entropy)(void *, unsigned char *, size_t); - - void *p_entropy; /*!< context for the entropy function */ -} -ctr_drbg_context; - -/** - * \brief CTR_DRBG initialization - * - * Note: Personalization data can be provided in addition to the more generic - * entropy source to make this instantiation as unique as possible. - * - * \param ctx CTR_DRBG context to be initialized - * \param f_entropy Entropy callback (p_entropy, buffer to fill, buffer - * length) - * \param p_entropy Entropy context - * \param custom Personalization data (Device specific identifiers) - * (Can be NULL) - * \param len Length of personalization data - * - * \return 0 if successful, or - * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED - */ -int ctr_drbg_init( ctr_drbg_context *ctx, - int (*f_entropy)(void *, unsigned char *, size_t), - void *p_entropy, - const unsigned char *custom, - size_t len ); - -/** - * \brief Enable / disable prediction resistance (Default: Off) - * - * Note: If enabled, entropy is used for ctx->entropy_len before each call! - * Only use this if you have ample supply of good entropy! - * - * \param ctx CTR_DRBG context - * \param resistance CTR_DRBG_PR_ON or CTR_DRBG_PR_OFF - */ -void ctr_drbg_set_prediction_resistance( ctr_drbg_context *ctx, - int resistance ); - -/** - * \brief Set the amount of entropy grabbed on each (re)seed - * (Default: CTR_DRBG_ENTROPY_LEN) - * - * \param ctx CTR_DRBG context - * \param len Amount of entropy to grab - */ -void ctr_drbg_set_entropy_len( ctr_drbg_context *ctx, - size_t len ); - -/** - * \brief Set the reseed interval - * (Default: CTR_DRBG_RESEED_INTERVAL) - * - * \param ctx CTR_DRBG context - * \param interval Reseed interval - */ -void ctr_drbg_set_reseed_interval( ctr_drbg_context *ctx, - int interval ); - -/** - * \brief CTR_DRBG reseeding (extracts data from entropy source) - * - * \param ctx CTR_DRBG context - * \param additional Additional data to add to state (Can be NULL) - * \param len Length of additional data - * - * \return 0 if successful, or - * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED - */ -int ctr_drbg_reseed( ctr_drbg_context *ctx, - const unsigned char *additional, size_t len ); - -/** - * \brief CTR_DRBG update state - * - * \param ctx CTR_DRBG context - * \param additional Additional data to update state with - * \param add_len Length of additional data - */ -void ctr_drbg_update( ctr_drbg_context *ctx, - const unsigned char *additional, size_t add_len ); - -/** - * \brief CTR_DRBG generate random with additional update input - * - * Note: Automatically reseeds if reseed_counter is reached. - * - * \param p_rng CTR_DRBG context - * \param output Buffer to fill - * \param output_len Length of the buffer - * \param additional Additional data to update with (Can be NULL) - * \param add_len Length of additional data - * - * \return 0 if successful, or - * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED, or - * POLARSSL_ERR_CTR_DRBG_REQUEST_TOO_BIG - */ -int ctr_drbg_random_with_add( void *p_rng, - unsigned char *output, size_t output_len, - const unsigned char *additional, size_t add_len ); - -/** - * \brief CTR_DRBG generate random - * - * Note: Automatically reseeds if reseed_counter is reached. - * - * \param p_rng CTR_DRBG context - * \param output Buffer to fill - * \param output_len Length of the buffer - * - * \return 0 if successful, or - * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED, or - * POLARSSL_ERR_CTR_DRBG_REQUEST_TOO_BIG - */ -int ctr_drbg_random( void *p_rng, - unsigned char *output, size_t output_len ); - -#if defined(POLARSSL_FS_IO) -/** - * \brief Write a seed file - * - * \param path Name of the file - * - * \return 0 if successful, 1 on file error, or - * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED - */ -int ctr_drbg_write_seed_file( ctr_drbg_context *ctx, const char *path ); - -/** - * \brief Read and update a seed file. Seed is added to this - * instance - * - * \param path Name of the file - * - * \return 0 if successful, 1 on file error, - * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or - * POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG - */ -int ctr_drbg_update_seed_file( ctr_drbg_context *ctx, const char *path ); -#endif - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int ctr_drbg_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* ctr_drbg.h */ diff --git a/makerom/polarssl/debug.h b/makerom/polarssl/debug.h deleted file mode 100644 index d85b6d3b..00000000 --- a/makerom/polarssl/debug.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * \file debug.h - * - * \brief Debug functions - * - * Copyright (C) 2006-2011, 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_DEBUG_H -#define POLARSSL_DEBUG_H - -#include "polarssl/config.h" -#include "polarssl/ssl.h" - -#if defined(POLARSSL_DEBUG_C) - -#define SSL_DEBUG_MSG( level, args ) \ - debug_print_msg( ssl, level, __FILE__, __LINE__, debug_fmt args ); - -#define SSL_DEBUG_RET( level, text, ret ) \ - debug_print_ret( ssl, level, __FILE__, __LINE__, text, ret ); - -#define SSL_DEBUG_BUF( level, text, buf, len ) \ - debug_print_buf( ssl, level, __FILE__, __LINE__, text, buf, len ); - -#define SSL_DEBUG_MPI( level, text, X ) \ - debug_print_mpi( ssl, level, __FILE__, __LINE__, text, X ); - -#define SSL_DEBUG_CRT( level, text, crt ) \ - debug_print_crt( ssl, level, __FILE__, __LINE__, text, crt ); - -#else - -#define SSL_DEBUG_MSG( level, args ) do { } while( 0 ) -#define SSL_DEBUG_RET( level, text, ret ) do { } while( 0 ) -#define SSL_DEBUG_BUF( level, text, buf, len ) do { } while( 0 ) -#define SSL_DEBUG_MPI( level, text, X ) do { } while( 0 ) -#define SSL_DEBUG_CRT( level, text, crt ) do { } while( 0 ) - -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -char *debug_fmt( const char *format, ... ); - -void debug_print_msg( const ssl_context *ssl, int level, - const char *file, int line, const char *text ); - -void debug_print_ret( const ssl_context *ssl, int level, - const char *file, int line, - const char *text, int ret ); - -void debug_print_buf( const ssl_context *ssl, int level, - const char *file, int line, const char *text, - unsigned char *buf, size_t len ); - -void debug_print_mpi( const ssl_context *ssl, int level, - const char *file, int line, - const char *text, const mpi *X ); - -void debug_print_crt( const ssl_context *ssl, int level, - const char *file, int line, - const char *text, const x509_cert *crt ); - -#ifdef __cplusplus -} -#endif - -#endif /* debug.h */ diff --git a/makerom/polarssl/des.h b/makerom/polarssl/des.h deleted file mode 100644 index 443cd3fe..00000000 --- a/makerom/polarssl/des.h +++ /dev/null @@ -1,252 +0,0 @@ -/** - * \file des.h - * - * \brief DES block cipher - * - * Copyright (C) 2006-2013, 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_DES_H -#define POLARSSL_DES_H - -#include "polarssl/config.h" - -#include - -#ifdef _MSC_VER -#include -typedef UINT32 uint32_t; -#else -#include -#endif - -#define DES_ENCRYPT 1 -#define DES_DECRYPT 0 - -#define POLARSSL_ERR_DES_INVALID_INPUT_LENGTH -0x0032 /**< The data input has an invalid length. */ - -#define DES_KEY_SIZE 8 - -#if !defined(POLARSSL_DES_ALT) -// Regular implementation -// - -/** - * \brief DES context structure - */ -typedef struct -{ - int mode; /*!< encrypt/decrypt */ - uint32_t sk[32]; /*!< DES subkeys */ -} -des_context; - -/** - * \brief Triple-DES context structure - */ -typedef struct -{ - int mode; /*!< encrypt/decrypt */ - uint32_t sk[96]; /*!< 3DES subkeys */ -} -des3_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Set key parity on the given key to odd. - * - * DES keys are 56 bits long, but each byte is padded with - * a parity bit to allow verification. - * - * \param key 8-byte secret key - */ -void des_key_set_parity( unsigned char key[DES_KEY_SIZE] ); - -/** - * \brief Check that key parity on the given key is odd. - * - * DES keys are 56 bits long, but each byte is padded with - * a parity bit to allow verification. - * - * \param key 8-byte secret key - * - * \return 0 is parity was ok, 1 if parity was not correct. - */ -int des_key_check_key_parity( const unsigned char key[DES_KEY_SIZE] ); - -/** - * \brief Check that key is not a weak or semi-weak DES key - * - * \param key 8-byte secret key - * - * \return 0 if no weak key was found, 1 if a weak key was identified. - */ -int des_key_check_weak( const unsigned char key[DES_KEY_SIZE] ); - -/** - * \brief DES key schedule (56-bit, encryption) - * - * \param ctx DES context to be initialized - * \param key 8-byte secret key - * - * \return 0 - */ -int des_setkey_enc( des_context *ctx, const unsigned char key[DES_KEY_SIZE] ); - -/** - * \brief DES key schedule (56-bit, decryption) - * - * \param ctx DES context to be initialized - * \param key 8-byte secret key - * - * \return 0 - */ -int des_setkey_dec( des_context *ctx, const unsigned char key[DES_KEY_SIZE] ); - -/** - * \brief Triple-DES key schedule (112-bit, encryption) - * - * \param ctx 3DES context to be initialized - * \param key 16-byte secret key - * - * \return 0 - */ -int des3_set2key_enc( des3_context *ctx, const unsigned char key[DES_KEY_SIZE * 2] ); - -/** - * \brief Triple-DES key schedule (112-bit, decryption) - * - * \param ctx 3DES context to be initialized - * \param key 16-byte secret key - * - * \return 0 - */ -int des3_set2key_dec( des3_context *ctx, const unsigned char key[DES_KEY_SIZE * 2] ); - -/** - * \brief Triple-DES key schedule (168-bit, encryption) - * - * \param ctx 3DES context to be initialized - * \param key 24-byte secret key - * - * \return 0 - */ -int des3_set3key_enc( des3_context *ctx, const unsigned char key[DES_KEY_SIZE * 3] ); - -/** - * \brief Triple-DES key schedule (168-bit, decryption) - * - * \param ctx 3DES context to be initialized - * \param key 24-byte secret key - * - * \return 0 - */ -int des3_set3key_dec( des3_context *ctx, const unsigned char key[DES_KEY_SIZE * 3] ); - -/** - * \brief DES-ECB block encryption/decryption - * - * \param ctx DES context - * \param input 64-bit input block - * \param output 64-bit output block - * - * \return 0 if successful - */ -int des_crypt_ecb( des_context *ctx, - const unsigned char input[8], - unsigned char output[8] ); - -/** - * \brief DES-CBC buffer encryption/decryption - * - * \param ctx DES context - * \param mode DES_ENCRYPT or DES_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 - */ -int des_crypt_cbc( des_context *ctx, - int mode, - size_t length, - unsigned char iv[8], - const unsigned char *input, - unsigned char *output ); - -/** - * \brief 3DES-ECB block encryption/decryption - * - * \param ctx 3DES context - * \param input 64-bit input block - * \param output 64-bit output block - * - * \return 0 if successful - */ -int des3_crypt_ecb( des3_context *ctx, - const unsigned char input[8], - unsigned char output[8] ); - -/** - * \brief 3DES-CBC buffer encryption/decryption - * - * \param ctx 3DES context - * \param mode DES_ENCRYPT or DES_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_DES_INVALID_INPUT_LENGTH - */ -int des3_crypt_cbc( des3_context *ctx, - int mode, - size_t length, - unsigned char iv[8], - const unsigned char *input, - unsigned char *output ); - -#ifdef __cplusplus -} -#endif - -#else /* POLARSSL_DES_ALT */ -#include "polarssl/des_alt.h" -#endif /* POLARSSL_DES_ALT */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int des_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* des.h */ diff --git a/makerom/polarssl/dhm.h b/makerom/polarssl/dhm.h deleted file mode 100644 index a1643ca7..00000000 --- a/makerom/polarssl/dhm.h +++ /dev/null @@ -1,244 +0,0 @@ -/** - * \file dhm.h - * - * \brief Diffie-Hellman-Merkle key exchange - * - * 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_DHM_H -#define POLARSSL_DHM_H - -#include "polarssl/bignum.h" - -/* - * DHM Error codes - */ -#define POLARSSL_ERR_DHM_BAD_INPUT_DATA -0x3080 /**< Bad input parameters to function. */ -#define POLARSSL_ERR_DHM_READ_PARAMS_FAILED -0x3100 /**< Reading of the DHM parameters failed. */ -#define POLARSSL_ERR_DHM_MAKE_PARAMS_FAILED -0x3180 /**< Making of the DHM parameters failed. */ -#define POLARSSL_ERR_DHM_READ_PUBLIC_FAILED -0x3200 /**< Reading of the public values failed. */ -#define POLARSSL_ERR_DHM_MAKE_PUBLIC_FAILED -0x3280 /**< Making of the public value failed. */ -#define POLARSSL_ERR_DHM_CALC_SECRET_FAILED -0x3300 /**< Calculation of the DHM secret failed. */ - -/** - * RFC 3526 defines a number of standardized Diffie-Hellman groups - * for IKE. - * RFC 5114 defines a number of standardized Diffie-Hellman groups - * that can be used. - * - * Some are included here for convenience. - * - * Included are: - * RFC 3526 3. 2048-bit MODP Group - * RFC 3526 4. 3072-bit MODP Group - * RFC 5114 2.1. 1024-bit MODP Group with 160-bit Prime Order Subgroup - * RFC 5114 2.2. 2048-bit MODP Group with 224-bit Prime Order Subgroup - */ -#define POLARSSL_DHM_RFC3526_MODP_2048_P \ - "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ - "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ - "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ - "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ - "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ - "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ - "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ - "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ - "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ - "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ - "15728E5A8AACAA68FFFFFFFFFFFFFFFF" - -#define POLARSSL_DHM_RFC3526_MODP_2048_G "02" - -#define POLARSSL_DHM_RFC3526_MODP_3072_P \ - "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ - "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ - "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ - "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ - "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ - "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ - "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ - "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ - "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ - "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ - "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ - "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ - "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ - "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ - "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ - "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF" - -#define POLARSSL_DHM_RFC3526_MODP_3072_G "02" - -#define POLARSSL_DHM_RFC5114_MODP_1024_P \ - "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6" \ - "9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0" \ - "13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70" \ - "98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0" \ - "A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708" \ - "DF1FB2BC2E4A4371" - -#define POLARSSL_DHM_RFC5114_MODP_1024_G \ - "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F" \ - "D6406CFF14266D31266FEA1E5C41564B777E690F5504F213" \ - "160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1" \ - "909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A" \ - "D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24" \ - "855E6EEB22B3B2E5" - -#define POLARSSL_DHM_RFC5114_MODP_2048_P \ - "AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1" \ - "B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15" \ - "EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC212" \ - "9037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207" \ - "C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708" \ - "B3BF8A317091883681286130BC8985DB1602E714415D9330" \ - "278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486D" \ - "CDF93ACC44328387315D75E198C641A480CD86A1B9E587E8" \ - "BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763" \ - "C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71" \ - "CF9DE5384E71B81C0AC4DFFE0C10E64F" - -#define POLARSSL_DHM_RFC5114_MODP_2048_G \ - "AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF"\ - "74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFA"\ - "AB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7"\ - "C17669101999024AF4D027275AC1348BB8A762D0521BC98A"\ - "E247150422EA1ED409939D54DA7460CDB5F6C6B250717CBE"\ - "F180EB34118E98D119529A45D6F834566E3025E316A330EF"\ - "BB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB"\ - "10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381"\ - "B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269"\ - "EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC0179"\ - "81BC087F2A7065B384B890D3191F2BFA" - -/** - * \brief DHM context structure - */ -typedef struct -{ - size_t len; /*!< size(P) in chars */ - mpi P; /*!< prime modulus */ - mpi G; /*!< generator */ - mpi X; /*!< secret value */ - mpi GX; /*!< self = G^X mod P */ - mpi GY; /*!< peer = G^Y mod P */ - mpi K; /*!< key = GY^X mod P */ - mpi RP; /*!< cached R^2 mod P */ -} -dhm_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Parse the ServerKeyExchange parameters - * - * \param ctx DHM context - * \param p &(start of input buffer) - * \param end end of buffer - * - * \return 0 if successful, or an POLARSSL_ERR_DHM_XXX error code - */ -int dhm_read_params( dhm_context *ctx, - unsigned char **p, - const unsigned char *end ); - -/** - * \brief Setup and write the ServerKeyExchange parameters - * - * \param ctx DHM context - * \param x_size private value size in bytes - * \param output destination buffer - * \param olen number of chars written - * \param f_rng RNG function - * \param p_rng RNG parameter - * - * \note This function assumes that ctx->P and ctx->G - * have already been properly set (for example - * using mpi_read_string or mpi_read_binary). - * - * \return 0 if successful, or an POLARSSL_ERR_DHM_XXX error code - */ -int dhm_make_params( dhm_context *ctx, int x_size, - unsigned char *output, size_t *olen, - int (*f_rng)(void *, unsigned char *, size_t), - void *p_rng ); - -/** - * \brief Import the peer's public value G^Y - * - * \param ctx DHM context - * \param input input buffer - * \param ilen size of buffer - * - * \return 0 if successful, or an POLARSSL_ERR_DHM_XXX error code - */ -int dhm_read_public( dhm_context *ctx, - const unsigned char *input, size_t ilen ); - -/** - * \brief Create own private value X and export G^X - * - * \param ctx DHM context - * \param x_size private value size in bytes - * \param output destination buffer - * \param olen must be equal to ctx->P.len - * \param f_rng RNG function - * \param p_rng RNG parameter - * - * \return 0 if successful, or an POLARSSL_ERR_DHM_XXX error code - */ -int dhm_make_public( dhm_context *ctx, int x_size, - unsigned char *output, size_t olen, - int (*f_rng)(void *, unsigned char *, size_t), - void *p_rng ); - -/** - * \brief Derive and export the shared secret (G^Y)^X mod P - * - * \param ctx DHM context - * \param output destination buffer - * \param olen number of chars written - * - * \return 0 if successful, or an POLARSSL_ERR_DHM_XXX error code - */ -int dhm_calc_secret( dhm_context *ctx, - unsigned char *output, size_t *olen ); - -/** - * \brief Free the components of a DHM key - */ -void dhm_free( dhm_context *ctx ); - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int dhm_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/makerom/polarssl/entropy.h b/makerom/polarssl/entropy.h deleted file mode 100644 index 5b6b3806..00000000 --- a/makerom/polarssl/entropy.h +++ /dev/null @@ -1,153 +0,0 @@ -/** - * \file entropy.h - * - * \brief Entropy accumulator implementation - * - * Copyright (C) 2006-2013, 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_ENTROPY_H -#define POLARSSL_ENTROPY_H - -#include - -#include "polarssl/config.h" - -#include "polarssl/sha4.h" -#if defined(POLARSSL_HAVEGE_C) -#include "polarssl/havege.h" -#endif - -#define POLARSSL_ERR_ENTROPY_SOURCE_FAILED -0x003C /**< Critical entropy source failure. */ -#define POLARSSL_ERR_ENTROPY_MAX_SOURCES -0x003E /**< No more sources can be added. */ -#define POLARSSL_ERR_ENTROPY_NO_SOURCES_DEFINED -0x0040 /**< No sources have been added to poll. */ - -#if !defined(POLARSSL_CONFIG_OPTIONS) -#define ENTROPY_MAX_SOURCES 20 /**< Maximum number of sources supported */ -#define ENTROPY_MAX_GATHER 128 /**< Maximum amount requested from entropy sources */ -#endif /* !POLARSSL_CONFIG_OPTIONS */ - -#define ENTROPY_BLOCK_SIZE 64 /**< Block size of entropy accumulator (SHA-512) */ - -#define ENTROPY_SOURCE_MANUAL ENTROPY_MAX_SOURCES - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Entropy poll callback pointer - * - * \param data Callback-specific data pointer - * \param output Data to fill - * \param len Maximum size to provide - * \param olen The actual amount of bytes put into the buffer (Can be 0) - * - * \return 0 if no critical failures occurred, - * POLARSSL_ERR_ENTROPY_SOURCE_FAILED otherwise - */ -typedef int (*f_source_ptr)(void *, unsigned char *, size_t, size_t *); - -/** - * \brief Entropy source state - */ -typedef struct -{ - f_source_ptr f_source; /**< The entropy source callback */ - void * p_source; /**< The callback data pointer */ - size_t size; /**< Amount received */ - size_t threshold; /**< Minimum level required before release */ -} -source_state; - -/** - * \brief Entropy context structure - */ -typedef struct -{ - sha4_context accumulator; - int source_count; - source_state source[ENTROPY_MAX_SOURCES]; -#if defined(POLARSSL_HAVEGE_C) - havege_state havege_data; -#endif -} -entropy_context; - -/** - * \brief Initialize the context - * - * \param ctx Entropy context to initialize - */ -void entropy_init( entropy_context *ctx ); - -/** - * \brief Adds an entropy source to poll - * - * \param ctx Entropy context - * \param f_source Entropy function - * \param p_source Function data - * \param threshold Minimum required from source before entropy is released - * ( with entropy_func() ) - * - * \return 0 if successful or POLARSSL_ERR_ENTROPY_MAX_SOURCES - */ -int entropy_add_source( entropy_context *ctx, - f_source_ptr f_source, void *p_source, - size_t threshold ); - -/** - * \brief Trigger an extra gather poll for the accumulator - * - * \param ctx Entropy context - * - * \return 0 if successful, or POLARSSL_ERR_ENTROPY_SOURCE_FAILED - */ -int entropy_gather( entropy_context *ctx ); - -/** - * \brief Retrieve entropy from the accumulator (Max ENTROPY_BLOCK_SIZE) - * - * \param data Entropy context - * \param output Buffer to fill - * \param len Length of buffer - * - * \return 0 if successful, or POLARSSL_ERR_ENTROPY_SOURCE_FAILED - */ -int entropy_func( void *data, unsigned char *output, size_t len ); - -/** - * \brief Add data to the accumulator manually - * - * \param ctx Entropy context - * \param data Data to add - * \param len Length of data - * - * \return 0 if successful - */ -int entropy_update_manual( entropy_context *ctx, - const unsigned char *data, size_t len ); - -#ifdef __cplusplus -} -#endif - -#endif /* entropy.h */ diff --git a/makerom/polarssl/entropy_poll.h b/makerom/polarssl/entropy_poll.h deleted file mode 100644 index 4d09e74c..00000000 --- a/makerom/polarssl/entropy_poll.h +++ /dev/null @@ -1,75 +0,0 @@ -/** - * \file entropy_poll.h - * - * \brief Platform-specific and custom entropy polling functions - * - * Copyright (C) 2006-2011, 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_ENTROPY_POLL_H -#define POLARSSL_ENTROPY_POLL_H - -#include - -#include "polarssl/config.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Default thresholds for built-in sources - */ -#define ENTROPY_MIN_PLATFORM 128 /**< Minimum for platform source */ -#define ENTROPY_MIN_HAVEGE 128 /**< Minimum for HAVEGE */ -#define ENTROPY_MIN_HARDCLOCK 32 /**< Minimum for hardclock() */ - -#if !defined(POLARSSL_NO_PLATFORM_ENTROPY) -/** - * \brief Platform-specific entropy poll callback - */ -int platform_entropy_poll( void *data, - unsigned char *output, size_t len, size_t *olen ); -#endif - -#if defined(POLARSSL_HAVEGE_C) -/** - * \brief HAVEGE based entropy poll callback - * - * Requires an HAVEGE state as its data pointer. - */ -int havege_poll( void *data, - unsigned char *output, size_t len, size_t *olen ); -#endif - -#if defined(POLARSSL_TIMING_C) -/** - * \brief hardclock-based entropy poll callback - */ -int hardclock_poll( void *data, - unsigned char *output, size_t len, size_t *olen ); -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* entropy_poll.h */ diff --git a/makerom/polarssl/error.h b/makerom/polarssl/error.h deleted file mode 100644 index 94c73a8f..00000000 --- a/makerom/polarssl/error.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * \file error.h - * - * \brief Error to string translation - * - * 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_ERROR_H -#define POLARSSL_ERROR_H - -#include - -/** - * Error code layout. - * - * Currently we try to keep all error codes within the negative space of 16 - * bytes signed integers to support all platforms (-0x0000 - -0x8000). In - * addition we'd like to give two layers of information on the error if - * possible. - * - * For that purpose the error codes are segmented in the following manner: - * - * 16 bit error code bit-segmentation - * - * 1 bit - Intentionally not used - * 3 bits - High level module ID - * 5 bits - Module-dependent error code - * 6 bits - Low level module errors - * 1 bit - Intentionally not used - * - * Low-level module errors (0x007E-0x0002) - * - * Module Nr Codes assigned - * MPI 7 0x0002-0x0010 - * GCM 2 0x0012-0x0014 - * BLOWFISH 2 0x0016-0x0018 - * AES 2 0x0020-0x0022 - * CAMELLIA 2 0x0024-0x0026 - * XTEA 1 0x0028-0x0028 - * BASE64 2 0x002A-0x002C - * PADLOCK 1 0x0030-0x0030 - * DES 1 0x0032-0x0032 - * CTR_DBRG 3 0x0034-0x003A - * ENTROPY 3 0x003C-0x0040 - * NET 11 0x0042-0x0056 - * ASN1 7 0x0060-0x006C - * MD2 1 0x0070-0x0070 - * MD4 1 0x0072-0x0072 - * MD5 1 0x0074-0x0074 - * SHA1 1 0x0076-0x0076 - * SHA2 1 0x0078-0x0078 - * SHA4 1 0x007A-0x007A - * - * High-level module nr (3 bits - 0x1...-0x8...) - * Name ID Nr of Errors - * PEM 1 9 - * PKCS#12 1 4 (Started from top) - * X509 2 23 - * DHM 3 6 - * PKCS5 3 4 (Started from top) - * RSA 4 9 - * MD 5 4 - * CIPHER 6 5 - * SSL 6 2 (Started from top) - * SSL 7 31 - * - * Module dependent error code (5 bits 0x.08.-0x.F8.) - */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Translate a PolarSSL error code into a string representation, - * Result is truncated if necessary and always includes a terminating - * null byte. - * - * \param errnum error code - * \param buffer buffer to place representation in - * \param buflen length of the buffer - */ -void error_strerror( int errnum, char *buffer, size_t buflen ); - -#ifdef __cplusplus -} -#endif - -#endif /* error.h */ diff --git a/makerom/polarssl/gcm.h b/makerom/polarssl/gcm.h deleted file mode 100644 index b3df62c4..00000000 --- a/makerom/polarssl/gcm.h +++ /dev/null @@ -1,147 +0,0 @@ -/** - * \file gcm.h - * - * \brief Galois/Counter mode for AES - * - * Copyright (C) 2006-2012, 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_GCM_H -#define POLARSSL_GCM_H - -#include "polarssl/aes.h" - -#ifdef _MSC_VER -#include -typedef UINT64 uint64_t; -#else -#include -#endif - -#define GCM_ENCRYPT 1 -#define GCM_DECRYPT 0 - -#define POLARSSL_ERR_GCM_AUTH_FAILED -0x0012 /**< Authenticated decryption failed. */ -#define POLARSSL_ERR_GCM_BAD_INPUT -0x0014 /**< Bad input parameters to function. */ - -/** - * \brief GCM context structure - */ -typedef struct { - aes_context aes_ctx; /*!< AES context used */ - uint64_t HL[16]; /*!< Precalculated HTable */ - uint64_t HH[16]; /*!< Precalculated HTable */ -} -gcm_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief GCM initialization (encryption) - * - * \param ctx GCM 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 gcm_init( gcm_context *ctx, const unsigned char *key, unsigned int keysize ); - -/** - * \brief GCM buffer encryption/decryption using AES - * - * \note On encryption, the output buffer can be the same as the input buffer. - * On decryption, the output buffer cannot be the same as input buffer. - * If buffers overlap, the output buffer must trail at least 8 bytes - * behind the input buffer. - * - * \param ctx GCM context - * \param mode GCM_ENCRYPT or GCM_DECRYPT - * \param length length of the input data - * \param iv initialization vector - * \param iv_len length of IV - * \param add additional data - * \param add_len length of additional data - * \param input buffer holding the input data - * \param output buffer for holding the output data - * \param tag_len length of the tag to generate - * \param tag buffer for holding the tag - * - * \return 0 if successful - */ -int gcm_crypt_and_tag( gcm_context *ctx, - int mode, - size_t length, - const unsigned char *iv, - size_t iv_len, - const unsigned char *add, - size_t add_len, - const unsigned char *input, - unsigned char *output, - size_t tag_len, - unsigned char *tag ); - -/** - * \brief GCM buffer authenticated decryption using AES - * - * \note On decryption, the output buffer cannot be the same as input buffer. - * If buffers overlap, the output buffer must trail at least 8 bytes - * behind the input buffer. - * - * \param ctx GCM context - * \param length length of the input data - * \param iv initialization vector - * \param iv_len length of IV - * \param add additional data - * \param add_len length of additional data - * \param tag buffer holding the tag - * \param tag_len length of the tag - * \param input buffer holding the input data - * \param output buffer for holding the output data - * - * \return 0 if successful and authenticated, - * POLARSSL_ERR_GCM_AUTH_FAILED if tag does not match - */ -int gcm_auth_decrypt( gcm_context *ctx, - size_t length, - const unsigned char *iv, - size_t iv_len, - const unsigned char *add, - size_t add_len, - const unsigned char *tag, - size_t tag_len, - const unsigned char *input, - unsigned char *output ); - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int gcm_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* gcm.h */ diff --git a/makerom/polarssl/havege.h b/makerom/polarssl/havege.h deleted file mode 100644 index 53c4f38c..00000000 --- a/makerom/polarssl/havege.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * \file havege.h - * - * \brief HAVEGE: HArdware Volatile Entropy Gathering and Expansion - * - * 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_HAVEGE_H -#define POLARSSL_HAVEGE_H - -#include - -#define COLLECT_SIZE 1024 - -/** - * \brief HAVEGE state structure - */ -typedef struct -{ - int PT1, PT2, offset[2]; - int pool[COLLECT_SIZE]; - int WALK[8192]; -} -havege_state; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief HAVEGE initialization - * - * \param hs HAVEGE state to be initialized - */ -void havege_init( havege_state *hs ); - -/** - * \brief HAVEGE rand function - * - * \param p_rng A HAVEGE state - * \param output Buffer to fill - * \param len Length of buffer - * - * \return 0 - */ -int havege_random( void *p_rng, unsigned char *output, size_t len ); - -#ifdef __cplusplus -} -#endif - -#endif /* havege.h */ diff --git a/makerom/polarssl/md.c b/makerom/polarssl/md.c deleted file mode 100644 index ab0f468c..00000000 --- a/makerom/polarssl/md.c +++ /dev/null @@ -1,298 +0,0 @@ -/** - * \file md.c - * - * \brief Generic message digest wrapper for PolarSSL - * - * \author Adriaan de Jong - * - * 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_MD_C) - -#include "polarssl/md.h" -#include "polarssl/md_wrap.h" - -#include -#include - -//#if defined _MSC_VER && !defined strcasecmp -//#define strcasecmp _stricmp -//#endif - -static const int supported_digests[] = { - -#if defined(POLARSSL_MD2_C) - POLARSSL_MD_MD2, -#endif - -#if defined(POLARSSL_MD4_C) - POLARSSL_MD_MD4, -#endif - -#if defined(POLARSSL_MD5_C) - POLARSSL_MD_MD5, -#endif - -#if defined(POLARSSL_SHA1_C) - POLARSSL_MD_SHA1, -#endif - -#if defined(POLARSSL_SHA2_C) - POLARSSL_MD_SHA224, - POLARSSL_MD_SHA256, -#endif - -#if defined(POLARSSL_SHA4_C) - POLARSSL_MD_SHA384, - POLARSSL_MD_SHA512, -#endif - - 0 -}; - -const int *md_list( void ) -{ - return supported_digests; -} - -const md_info_t *md_info_from_string( const char *md_name ) -{ - if( NULL == md_name ) - return NULL; - - /* Get the appropriate digest information */ -#if defined(POLARSSL_MD2_C) - if( !strcasecmp( "MD2", md_name ) ) - return md_info_from_type( POLARSSL_MD_MD2 ); -#endif -#if defined(POLARSSL_MD4_C) - if( !strcasecmp( "MD4", md_name ) ) - return md_info_from_type( POLARSSL_MD_MD4 ); -#endif -#if defined(POLARSSL_MD5_C) - if( !strcasecmp( "MD5", md_name ) ) - return md_info_from_type( POLARSSL_MD_MD5 ); -#endif -#if defined(POLARSSL_SHA1_C) - if( !strcasecmp( "SHA1", md_name ) || !strcasecmp( "SHA", md_name ) ) - return md_info_from_type( POLARSSL_MD_SHA1 ); -#endif -#if defined(POLARSSL_SHA2_C) - if( !strcasecmp( "SHA224", md_name ) ) - return md_info_from_type( POLARSSL_MD_SHA224 ); - if( !strcasecmp( "SHA256", md_name ) ) - return md_info_from_type( POLARSSL_MD_SHA256 ); -#endif -#if defined(POLARSSL_SHA4_C) - if( !strcasecmp( "SHA384", md_name ) ) - return md_info_from_type( POLARSSL_MD_SHA384 ); - if( !strcasecmp( "SHA512", md_name ) ) - return md_info_from_type( POLARSSL_MD_SHA512 ); -#endif - return NULL; -} - -const md_info_t *md_info_from_type( md_type_t md_type ) -{ - switch( md_type ) - { -#if defined(POLARSSL_MD2_C) - case POLARSSL_MD_MD2: - return &md2_info; -#endif -#if defined(POLARSSL_MD4_C) - case POLARSSL_MD_MD4: - return &md4_info; -#endif -#if defined(POLARSSL_MD5_C) - case POLARSSL_MD_MD5: - return &md5_info; -#endif -#if defined(POLARSSL_SHA1_C) - case POLARSSL_MD_SHA1: - return &sha1_info; -#endif -#if defined(POLARSSL_SHA2_C) - case POLARSSL_MD_SHA224: - return &sha224_info; - case POLARSSL_MD_SHA256: - return &sha256_info; -#endif -#if defined(POLARSSL_SHA4_C) - case POLARSSL_MD_SHA384: - return &sha384_info; - case POLARSSL_MD_SHA512: - return &sha512_info; -#endif - default: - return NULL; - } -} - -int md_init_ctx( md_context_t *ctx, const md_info_t *md_info ) -{ - if( md_info == NULL || ctx == NULL ) - return POLARSSL_ERR_MD_BAD_INPUT_DATA; - - memset( ctx, 0, sizeof( md_context_t ) ); - - if( ( ctx->md_ctx = md_info->ctx_alloc_func() ) == NULL ) - return POLARSSL_ERR_MD_ALLOC_FAILED; - - ctx->md_info = md_info; - - md_info->starts_func( ctx->md_ctx ); - - return 0; -} - -int md_free_ctx( md_context_t *ctx ) -{ - if( ctx == NULL || ctx->md_info == NULL ) - return POLARSSL_ERR_MD_BAD_INPUT_DATA; - - ctx->md_info->ctx_free_func( ctx->md_ctx ); - ctx->md_ctx = NULL; - - return 0; -} - -int md_starts( md_context_t *ctx ) -{ - if( ctx == NULL || ctx->md_info == NULL ) - return POLARSSL_ERR_MD_BAD_INPUT_DATA; - - ctx->md_info->starts_func( ctx->md_ctx ); - - return 0; -} - -int md_update( md_context_t *ctx, const unsigned char *input, size_t ilen ) -{ - if( ctx == NULL || ctx->md_info == NULL ) - return POLARSSL_ERR_MD_BAD_INPUT_DATA; - - ctx->md_info->update_func( ctx->md_ctx, input, ilen ); - - return 0; -} - -int md_finish( md_context_t *ctx, unsigned char *output ) -{ - if( ctx == NULL || ctx->md_info == NULL ) - return POLARSSL_ERR_MD_BAD_INPUT_DATA; - - ctx->md_info->finish_func( ctx->md_ctx, output ); - - return 0; -} - -int md( const md_info_t *md_info, const unsigned char *input, size_t ilen, - unsigned char *output ) -{ - if ( md_info == NULL ) - return POLARSSL_ERR_MD_BAD_INPUT_DATA; - - md_info->digest_func( input, ilen, output ); - - return 0; -} - -int md_file( const md_info_t *md_info, const char *path, unsigned char *output ) -{ -#if defined(POLARSSL_FS_IO) - int ret; -#endif - - if( md_info == NULL ) - return POLARSSL_ERR_MD_BAD_INPUT_DATA; - -#if defined(POLARSSL_FS_IO) - ret = md_info->file_func( path, output ); - if( ret != 0 ) - return( POLARSSL_ERR_MD_FILE_IO_ERROR + ret ); - - return( ret ); -#else - ((void) path); - ((void) output); - - return POLARSSL_ERR_MD_FEATURE_UNAVAILABLE; -#endif -} - -int md_hmac_starts( md_context_t *ctx, const unsigned char *key, size_t keylen ) -{ - if( ctx == NULL || ctx->md_info == NULL ) - return POLARSSL_ERR_MD_BAD_INPUT_DATA; - - ctx->md_info->hmac_starts_func( ctx->md_ctx, key, keylen); - - return 0; -} - -int md_hmac_update( md_context_t *ctx, const unsigned char *input, size_t ilen ) -{ - if( ctx == NULL || ctx->md_info == NULL ) - return POLARSSL_ERR_MD_BAD_INPUT_DATA; - - ctx->md_info->hmac_update_func( ctx->md_ctx, input, ilen ); - - return 0; -} - -int md_hmac_finish( md_context_t *ctx, unsigned char *output) -{ - if( ctx == NULL || ctx->md_info == NULL ) - return POLARSSL_ERR_MD_BAD_INPUT_DATA; - - ctx->md_info->hmac_finish_func( ctx->md_ctx, output); - - return 0; -} - -int md_hmac_reset( md_context_t *ctx ) -{ - if( ctx == NULL || ctx->md_info == NULL ) - return POLARSSL_ERR_MD_BAD_INPUT_DATA; - - ctx->md_info->hmac_reset_func( ctx->md_ctx); - - return 0; -} - -int md_hmac( const md_info_t *md_info, const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char *output ) -{ - if( md_info == NULL ) - return POLARSSL_ERR_MD_BAD_INPUT_DATA; - - md_info->hmac_func( key, keylen, input, ilen, output ); - - return 0; -} - -#endif diff --git a/makerom/polarssl/md.h b/makerom/polarssl/md.h deleted file mode 100644 index 6a1bdd41..00000000 --- a/makerom/polarssl/md.h +++ /dev/null @@ -1,363 +0,0 @@ -/** - * \file md.h - * - * \brief Generic message digest wrapper - * - * \author Adriaan de Jong - * - * Copyright (C) 2006-2011, 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_MD_H -#define POLARSSL_MD_H - -#include - -#if defined(_MSC_VER) && !defined(inline) -#define inline _inline -#else -#if defined(__ARMCC_VERSION) && !defined(inline) -#define inline __inline -#endif /* __ARMCC_VERSION */ -#endif /*_MSC_VER */ - -#define POLARSSL_ERR_MD_FEATURE_UNAVAILABLE -0x5080 /**< The selected feature is not available. */ -#define POLARSSL_ERR_MD_BAD_INPUT_DATA -0x5100 /**< Bad input parameters to function. */ -#define POLARSSL_ERR_MD_ALLOC_FAILED -0x5180 /**< Failed to allocate memory. */ -#define POLARSSL_ERR_MD_FILE_IO_ERROR -0x5200 /**< Opening or reading of file failed. */ - -typedef enum { - POLARSSL_MD_NONE=0, - POLARSSL_MD_MD2, - POLARSSL_MD_MD4, - POLARSSL_MD_MD5, - POLARSSL_MD_SHA1, - POLARSSL_MD_SHA224, - POLARSSL_MD_SHA256, - POLARSSL_MD_SHA384, - POLARSSL_MD_SHA512, -} md_type_t; - -#define POLARSSL_MD_MAX_SIZE 64 /* longest known is SHA512 */ - -/** - * Message digest information. Allows message digest functions to be called - * in a generic way. - */ -typedef struct { - /** Digest identifier */ - md_type_t type; - - /** Name of the message digest */ - const char * name; - - /** Output length of the digest function */ - int size; - - /** Digest initialisation function */ - void (*starts_func)( void *ctx ); - - /** Digest update function */ - void (*update_func)( void *ctx, const unsigned char *input, size_t ilen ); - - /** Digest finalisation function */ - void (*finish_func)( void *ctx, unsigned char *output ); - - /** Generic digest function */ - void (*digest_func)( const unsigned char *input, size_t ilen, - unsigned char *output ); - - /** Generic file digest function */ - int (*file_func)( const char *path, unsigned char *output ); - - /** HMAC Initialisation function */ - void (*hmac_starts_func)( void *ctx, const unsigned char *key, size_t keylen ); - - /** HMAC update function */ - void (*hmac_update_func)( void *ctx, const unsigned char *input, size_t ilen ); - - /** HMAC finalisation function */ - void (*hmac_finish_func)( void *ctx, unsigned char *output); - - /** HMAC context reset function */ - void (*hmac_reset_func)( void *ctx ); - - /** Generic HMAC function */ - void (*hmac_func)( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char *output ); - - /** Allocate a new context */ - void * (*ctx_alloc_func)( void ); - - /** Free the given context */ - void (*ctx_free_func)( void *ctx ); - -} md_info_t; - -/** - * Generic message digest context. - */ -typedef struct { - /** Information about the associated message digest */ - const md_info_t *md_info; - - /** Digest-specific context */ - void *md_ctx; -} md_context_t; - -#define MD_CONTEXT_T_INIT { \ - NULL, /* md_info */ \ - NULL, /* md_ctx */ \ -} - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Returns the list of digests supported by the generic digest module. - * - * \return a statically allocated array of digests, the last entry - * is 0. - */ -const int *md_list( void ); - -/** - * \brief Returns the message digest information associated with the - * given digest name. - * - * \param md_name Name of the digest to search for. - * - * \return The message digest information associated with md_name or - * NULL if not found. - */ -const md_info_t *md_info_from_string( const char *md_name ); - -/** - * \brief Returns the message digest information associated with the - * given digest type. - * - * \param md_type type of digest to search for. - * - * \return The message digest information associated with md_type or - * NULL if not found. - */ -const md_info_t *md_info_from_type( md_type_t md_type ); - -/** - * \brief Initialises and fills the message digest context structure with - * the appropriate values. - * - * \param ctx context to initialise. May not be NULL. The - * digest-specific context (ctx->md_ctx) must be NULL. It will - * be allocated, and must be freed using md_free_ctx() later. - * \param md_info message digest to use. - * - * \returns \c 0 on success, \c POLARSSL_ERR_MD_BAD_INPUT_DATA on - * parameter failure, \c POLARSSL_ERR_MD_ALLOC_FAILED if - * allocation of the digest-specific context failed. - */ -int md_init_ctx( md_context_t *ctx, const md_info_t *md_info ); - -/** - * \brief Free the message-specific context of ctx. Freeing ctx itself - * remains the responsibility of the caller. - * - * \param ctx Free the message-specific context - * - * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter - * verification fails. - */ -int md_free_ctx( md_context_t *ctx ); - -/** - * \brief Returns the size of the message digest output. - * - * \param md_info message digest info - * - * \return size of the message digest output. - */ -static inline unsigned char md_get_size( const md_info_t *md_info ) -{ - if( md_info == NULL ) - return( 0 ); - - return md_info->size; -} - -/** - * \brief Returns the type of the message digest output. - * - * \param md_info message digest info - * - * \return type of the message digest output. - */ -static inline md_type_t md_get_type( const md_info_t *md_info ) -{ - if( md_info == NULL ) - return( POLARSSL_MD_NONE ); - - return md_info->type; -} - -/** - * \brief Returns the name of the message digest output. - * - * \param md_info message digest info - * - * \return name of the message digest output. - */ -static inline const char *md_get_name( const md_info_t *md_info ) -{ - if( md_info == NULL ) - return( NULL ); - - return md_info->name; -} - -/** - * \brief Set-up the given context for a new message digest - * - * \param ctx generic message digest context. - * - * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter - * verification fails. - */ -int md_starts( md_context_t *ctx ); - -/** - * \brief Generic message digest process buffer - * - * \param ctx Generic message digest context - * \param input buffer holding the datal - * \param ilen length of the input data - * - * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter - * verification fails. - */ -int md_update( md_context_t *ctx, const unsigned char *input, size_t ilen ); - -/** - * \brief Generic message digest final digest - * - * \param ctx Generic message digest context - * \param output Generic message digest checksum result - * - * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter - * verification fails. - */ -int md_finish( md_context_t *ctx, unsigned char *output ); - -/** - * \brief Output = message_digest( input buffer ) - * - * \param md_info message digest info - * \param input buffer holding the data - * \param ilen length of the input data - * \param output Generic message digest checksum result - * - * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter - * verification fails. - */ -int md( const md_info_t *md_info, const unsigned char *input, size_t ilen, - unsigned char *output ); - -/** - * \brief Output = message_digest( file contents ) - * - * \param md_info message digest info - * \param path input file name - * \param output generic message digest checksum result - * - * \return 0 if successful, POLARSSL_ERR_MD_FILE_OPEN_FAILED if fopen - * failed, POLARSSL_ERR_MD_FILE_READ_FAILED if fread failed, - * POLARSSL_ERR_MD_BAD_INPUT_DATA if md_info was NULL. - */ -int md_file( const md_info_t *md_info, const char *path, unsigned char *output ); - -/** - * \brief Generic HMAC context setup - * - * \param ctx HMAC context to be initialized - * \param key HMAC secret key - * \param keylen length of the HMAC key - * - * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter - * verification fails. - */ -int md_hmac_starts( md_context_t *ctx, const unsigned char *key, size_t keylen ); - -/** - * \brief Generic HMAC process buffer - * - * \param ctx HMAC context - * \param input buffer holding the data - * \param ilen length of the input data - * - * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter - * verification fails. - */ -int md_hmac_update( md_context_t *ctx, const unsigned char *input, size_t ilen ); - -/** - * \brief Generic HMAC final digest - * - * \param ctx HMAC context - * \param output Generic HMAC checksum result - * - * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter - * verification fails. - */ -int md_hmac_finish( md_context_t *ctx, unsigned char *output); - -/** - * \brief Generic HMAC context reset - * - * \param ctx HMAC context to be reset - * - * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter - * verification fails. - */ -int md_hmac_reset( md_context_t *ctx ); - -/** - * \brief Output = Generic_HMAC( hmac key, input buffer ) - * - * \param md_info message digest info - * \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 Generic HMAC-result - * - * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter - * verification fails. - */ -int md_hmac( const md_info_t *md_info, const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char *output ); - -#ifdef __cplusplus -} -#endif - -#endif /* POLARSSL_MD_H */ diff --git a/makerom/polarssl/md2.h b/makerom/polarssl/md2.h deleted file mode 100644 index f4372423..00000000 --- a/makerom/polarssl/md2.h +++ /dev/null @@ -1,171 +0,0 @@ -/** - * \file md2.h - * - * \brief MD2 message digest algorithm (hash function) - * - * Copyright (C) 2006-2013, 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_MD2_H -#define POLARSSL_MD2_H - -#include "polarssl/config.h" - -#include - -#define POLARSSL_ERR_MD2_FILE_IO_ERROR -0x0070 /**< Read/write error in file. */ - -#if !defined(POLARSSL_MD2_ALT) -// Regular implementation -// - -/** - * \brief MD2 context structure - */ -typedef struct -{ - unsigned char cksum[16]; /*!< checksum of the data block */ - unsigned char state[48]; /*!< intermediate digest state */ - unsigned char buffer[16]; /*!< data block being processed */ - - unsigned char ipad[16]; /*!< HMAC: inner padding */ - unsigned char opad[16]; /*!< HMAC: outer padding */ - size_t left; /*!< amount of data in buffer */ -} -md2_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief MD2 context setup - * - * \param ctx context to be initialized - */ -void md2_starts( md2_context *ctx ); - -/** - * \brief MD2 process buffer - * - * \param ctx MD2 context - * \param input buffer holding the data - * \param ilen length of the input data - */ -void md2_update( md2_context *ctx, const unsigned char *input, size_t ilen ); - -/** - * \brief MD2 final digest - * - * \param ctx MD2 context - * \param output MD2 checksum result - */ -void md2_finish( md2_context *ctx, unsigned char output[16] ); - -#ifdef __cplusplus -} -#endif - -#else /* POLARSSL_MD2_ALT */ -#include "polarssl/md2_alt.h" -#endif /* POLARSSL_MD2_ALT */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Output = MD2( input buffer ) - * - * \param input buffer holding the data - * \param ilen length of the input data - * \param output MD2 checksum result - */ -void md2( const unsigned char *input, size_t ilen, unsigned char output[16] ); - -/** - * \brief Output = MD2( file contents ) - * - * \param path input file name - * \param output MD2 checksum result - * - * \return 0 if successful, or POLARSSL_ERR_MD2_FILE_IO_ERROR - */ -int md2_file( const char *path, unsigned char output[16] ); - -/** - * \brief MD2 HMAC context setup - * - * \param ctx HMAC context to be initialized - * \param key HMAC secret key - * \param keylen length of the HMAC key - */ -void md2_hmac_starts( md2_context *ctx, const unsigned char *key, size_t keylen ); - -/** - * \brief MD2 HMAC process buffer - * - * \param ctx HMAC context - * \param input buffer holding the data - * \param ilen length of the input data - */ -void md2_hmac_update( md2_context *ctx, const unsigned char *input, size_t ilen ); - -/** - * \brief MD2 HMAC final digest - * - * \param ctx HMAC context - * \param output MD2 HMAC checksum result - */ -void md2_hmac_finish( md2_context *ctx, unsigned char output[16] ); - -/** - * \brief MD2 HMAC context reset - * - * \param ctx HMAC context to be reset - */ -void md2_hmac_reset( md2_context *ctx ); - -/** - * \brief Output = HMAC-MD2( 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-MD2 result - */ -void md2_hmac( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char output[16] ); - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int md2_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* md2.h */ diff --git a/makerom/polarssl/md4.h b/makerom/polarssl/md4.h deleted file mode 100644 index 9600361f..00000000 --- a/makerom/polarssl/md4.h +++ /dev/null @@ -1,177 +0,0 @@ -/** - * \file md4.h - * - * \brief MD4 message digest algorithm (hash function) - * - * Copyright (C) 2006-2013, 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_MD4_H -#define POLARSSL_MD4_H - -#include "polarssl/config.h" - -#include - -#ifdef _MSC_VER -#include -typedef UINT32 uint32_t; -#else -#include -#endif - -#define POLARSSL_ERR_MD4_FILE_IO_ERROR -0x0072 /**< Read/write error in file. */ - -#if !defined(POLARSSL_MD4_ALT) -// Regular implementation -// - -/** - * \brief MD4 context structure - */ -typedef struct -{ - uint32_t total[2]; /*!< number of bytes processed */ - uint32_t state[4]; /*!< 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 */ -} -md4_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief MD4 context setup - * - * \param ctx context to be initialized - */ -void md4_starts( md4_context *ctx ); - -/** - * \brief MD4 process buffer - * - * \param ctx MD4 context - * \param input buffer holding the data - * \param ilen length of the input data - */ -void md4_update( md4_context *ctx, const unsigned char *input, size_t ilen ); - -/** - * \brief MD4 final digest - * - * \param ctx MD4 context - * \param output MD4 checksum result - */ -void md4_finish( md4_context *ctx, unsigned char output[16] ); - -#ifdef __cplusplus -} -#endif - -#else /* POLARSSL_MD4_ALT */ -#include "polarssl/md4_alt.h" -#endif /* POLARSSL_MD4_ALT */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Output = MD4( input buffer ) - * - * \param input buffer holding the data - * \param ilen length of the input data - * \param output MD4 checksum result - */ -void md4( const unsigned char *input, size_t ilen, unsigned char output[16] ); - -/** - * \brief Output = MD4( file contents ) - * - * \param path input file name - * \param output MD4 checksum result - * - * \return 0 if successful, or POLARSSL_ERR_MD4_FILE_IO_ERROR - */ -int md4_file( const char *path, unsigned char output[16] ); - -/** - * \brief MD4 HMAC context setup - * - * \param ctx HMAC context to be initialized - * \param key HMAC secret key - * \param keylen length of the HMAC key - */ -void md4_hmac_starts( md4_context *ctx, const unsigned char *key, size_t keylen ); - -/** - * \brief MD4 HMAC process buffer - * - * \param ctx HMAC context - * \param input buffer holding the data - * \param ilen length of the input data - */ -void md4_hmac_update( md4_context *ctx, const unsigned char *input, size_t ilen ); - -/** - * \brief MD4 HMAC final digest - * - * \param ctx HMAC context - * \param output MD4 HMAC checksum result - */ -void md4_hmac_finish( md4_context *ctx, unsigned char output[16] ); - -/** - * \brief MD4 HMAC context reset - * - * \param ctx HMAC context to be reset - */ -void md4_hmac_reset( md4_context *ctx ); - -/** - * \brief Output = HMAC-MD4( 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-MD4 result - */ -void md4_hmac( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char output[16] ); - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int md4_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* md4.h */ diff --git a/makerom/polarssl/md5.c b/makerom/polarssl/md5.c deleted file mode 100644 index b28461e9..00000000 --- a/makerom/polarssl/md5.c +++ /dev/null @@ -1,585 +0,0 @@ -/* - * RFC 1321 compliant MD5 implementation - * - * Copyright (C) 2006-2013, 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 MD5 algorithm was designed by Ron Rivest in 1991. - * - * http://www.ietf.org/rfc/rfc1321.txt - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_MD5_C) - -#include "polarssl/md5.h" - -#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) -#include -#endif - -#if !defined(POLARSSL_MD5_ALT) - -/* - * 32-bit integer manipulation macros (little endian) - */ -#ifndef GET_UINT32_LE -#define GET_UINT32_LE(n,b,i) \ -{ \ - (n) = ( (uint32_t) (b)[(i) ] ) \ - | ( (uint32_t) (b)[(i) + 1] << 8 ) \ - | ( (uint32_t) (b)[(i) + 2] << 16 ) \ - | ( (uint32_t) (b)[(i) + 3] << 24 ); \ -} -#endif - -#ifndef PUT_UINT32_LE -#define PUT_UINT32_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 - -/* - * MD5 context setup - */ -void md5_starts( md5_context *ctx ) -{ - ctx->total[0] = 0; - ctx->total[1] = 0; - - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xEFCDAB89; - ctx->state[2] = 0x98BADCFE; - ctx->state[3] = 0x10325476; -} - -void md5_process( md5_context *ctx, const unsigned char data[64] ) -{ - uint32_t X[16], A, B, C, D; - - GET_UINT32_LE( X[ 0], data, 0 ); - GET_UINT32_LE( X[ 1], data, 4 ); - GET_UINT32_LE( X[ 2], data, 8 ); - GET_UINT32_LE( X[ 3], data, 12 ); - GET_UINT32_LE( X[ 4], data, 16 ); - GET_UINT32_LE( X[ 5], data, 20 ); - GET_UINT32_LE( X[ 6], data, 24 ); - GET_UINT32_LE( X[ 7], data, 28 ); - GET_UINT32_LE( X[ 8], data, 32 ); - GET_UINT32_LE( X[ 9], data, 36 ); - GET_UINT32_LE( X[10], data, 40 ); - GET_UINT32_LE( X[11], data, 44 ); - GET_UINT32_LE( X[12], data, 48 ); - GET_UINT32_LE( X[13], data, 52 ); - GET_UINT32_LE( X[14], data, 56 ); - GET_UINT32_LE( X[15], data, 60 ); - -#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) - -#define P(a,b,c,d,k,s,t) \ -{ \ - a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ -} - - A = ctx->state[0]; - B = ctx->state[1]; - C = ctx->state[2]; - D = ctx->state[3]; - -#define F(x,y,z) (z ^ (x & (y ^ z))) - - P( A, B, C, D, 0, 7, 0xD76AA478 ); - P( D, A, B, C, 1, 12, 0xE8C7B756 ); - P( C, D, A, B, 2, 17, 0x242070DB ); - P( B, C, D, A, 3, 22, 0xC1BDCEEE ); - P( A, B, C, D, 4, 7, 0xF57C0FAF ); - P( D, A, B, C, 5, 12, 0x4787C62A ); - P( C, D, A, B, 6, 17, 0xA8304613 ); - P( B, C, D, A, 7, 22, 0xFD469501 ); - P( A, B, C, D, 8, 7, 0x698098D8 ); - P( D, A, B, C, 9, 12, 0x8B44F7AF ); - P( C, D, A, B, 10, 17, 0xFFFF5BB1 ); - P( B, C, D, A, 11, 22, 0x895CD7BE ); - P( A, B, C, D, 12, 7, 0x6B901122 ); - P( D, A, B, C, 13, 12, 0xFD987193 ); - P( C, D, A, B, 14, 17, 0xA679438E ); - P( B, C, D, A, 15, 22, 0x49B40821 ); - -#undef F - -#define F(x,y,z) (y ^ (z & (x ^ y))) - - P( A, B, C, D, 1, 5, 0xF61E2562 ); - P( D, A, B, C, 6, 9, 0xC040B340 ); - P( C, D, A, B, 11, 14, 0x265E5A51 ); - P( B, C, D, A, 0, 20, 0xE9B6C7AA ); - P( A, B, C, D, 5, 5, 0xD62F105D ); - P( D, A, B, C, 10, 9, 0x02441453 ); - P( C, D, A, B, 15, 14, 0xD8A1E681 ); - P( B, C, D, A, 4, 20, 0xE7D3FBC8 ); - P( A, B, C, D, 9, 5, 0x21E1CDE6 ); - P( D, A, B, C, 14, 9, 0xC33707D6 ); - P( C, D, A, B, 3, 14, 0xF4D50D87 ); - P( B, C, D, A, 8, 20, 0x455A14ED ); - P( A, B, C, D, 13, 5, 0xA9E3E905 ); - P( D, A, B, C, 2, 9, 0xFCEFA3F8 ); - P( C, D, A, B, 7, 14, 0x676F02D9 ); - P( B, C, D, A, 12, 20, 0x8D2A4C8A ); - -#undef F - -#define F(x,y,z) (x ^ y ^ z) - - P( A, B, C, D, 5, 4, 0xFFFA3942 ); - P( D, A, B, C, 8, 11, 0x8771F681 ); - P( C, D, A, B, 11, 16, 0x6D9D6122 ); - P( B, C, D, A, 14, 23, 0xFDE5380C ); - P( A, B, C, D, 1, 4, 0xA4BEEA44 ); - P( D, A, B, C, 4, 11, 0x4BDECFA9 ); - P( C, D, A, B, 7, 16, 0xF6BB4B60 ); - P( B, C, D, A, 10, 23, 0xBEBFBC70 ); - P( A, B, C, D, 13, 4, 0x289B7EC6 ); - P( D, A, B, C, 0, 11, 0xEAA127FA ); - P( C, D, A, B, 3, 16, 0xD4EF3085 ); - P( B, C, D, A, 6, 23, 0x04881D05 ); - P( A, B, C, D, 9, 4, 0xD9D4D039 ); - P( D, A, B, C, 12, 11, 0xE6DB99E5 ); - P( C, D, A, B, 15, 16, 0x1FA27CF8 ); - P( B, C, D, A, 2, 23, 0xC4AC5665 ); - -#undef F - -#define F(x,y,z) (y ^ (x | ~z)) - - P( A, B, C, D, 0, 6, 0xF4292244 ); - P( D, A, B, C, 7, 10, 0x432AFF97 ); - P( C, D, A, B, 14, 15, 0xAB9423A7 ); - P( B, C, D, A, 5, 21, 0xFC93A039 ); - P( A, B, C, D, 12, 6, 0x655B59C3 ); - P( D, A, B, C, 3, 10, 0x8F0CCC92 ); - P( C, D, A, B, 10, 15, 0xFFEFF47D ); - P( B, C, D, A, 1, 21, 0x85845DD1 ); - P( A, B, C, D, 8, 6, 0x6FA87E4F ); - P( D, A, B, C, 15, 10, 0xFE2CE6E0 ); - P( C, D, A, B, 6, 15, 0xA3014314 ); - P( B, C, D, A, 13, 21, 0x4E0811A1 ); - P( A, B, C, D, 4, 6, 0xF7537E82 ); - P( D, A, B, C, 11, 10, 0xBD3AF235 ); - P( C, D, A, B, 2, 15, 0x2AD7D2BB ); - P( B, C, D, A, 9, 21, 0xEB86D391 ); - -#undef F - - ctx->state[0] += A; - ctx->state[1] += B; - ctx->state[2] += C; - ctx->state[3] += D; -} - -/* - * MD5 process buffer - */ -void md5_update( md5_context *ctx, const unsigned char *input, size_t ilen ) -{ - size_t fill; - uint32_t left; - - if( ilen <= 0 ) - return; - - left = ctx->total[0] & 0x3F; - fill = 64 - left; - - ctx->total[0] += (uint32_t) ilen; - ctx->total[0] &= 0xFFFFFFFF; - - if( ctx->total[0] < (uint32_t) ilen ) - ctx->total[1]++; - - if( left && ilen >= fill ) - { - memcpy( (void *) (ctx->buffer + left), input, fill ); - md5_process( ctx, ctx->buffer ); - input += fill; - ilen -= fill; - left = 0; - } - - while( ilen >= 64 ) - { - md5_process( ctx, input ); - input += 64; - ilen -= 64; - } - - if( ilen > 0 ) - { - memcpy( (void *) (ctx->buffer + left), input, ilen ); - } -} - -static const unsigned char md5_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 -}; - -/* - * MD5 final digest - */ -void md5_finish( md5_context *ctx, unsigned char output[16] ) -{ - uint32_t last, padn; - uint32_t high, low; - unsigned char msglen[8]; - - high = ( ctx->total[0] >> 29 ) - | ( ctx->total[1] << 3 ); - low = ( ctx->total[0] << 3 ); - - PUT_UINT32_LE( low, msglen, 0 ); - PUT_UINT32_LE( high, msglen, 4 ); - - last = ctx->total[0] & 0x3F; - padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); - - md5_update( ctx, md5_padding, padn ); - md5_update( ctx, msglen, 8 ); - - PUT_UINT32_LE( ctx->state[0], output, 0 ); - PUT_UINT32_LE( ctx->state[1], output, 4 ); - PUT_UINT32_LE( ctx->state[2], output, 8 ); - PUT_UINT32_LE( ctx->state[3], output, 12 ); -} - -#endif /* !POLARSSL_MD5_ALT */ - -/* - * output = MD5( input buffer ) - */ -void md5( const unsigned char *input, size_t ilen, unsigned char output[16] ) -{ - md5_context ctx; - - md5_starts( &ctx ); - md5_update( &ctx, input, ilen ); - md5_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( md5_context ) ); -} - -#if defined(POLARSSL_FS_IO) -/* - * output = MD5( file contents ) - */ -int md5_file( const char *path, unsigned char output[16] ) -{ - FILE *f; - size_t n; - md5_context ctx; - unsigned char buf[1024]; - - if( ( f = fopen( path, "rb" ) ) == NULL ) - return( POLARSSL_ERR_MD5_FILE_IO_ERROR ); - - md5_starts( &ctx ); - - while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) - md5_update( &ctx, buf, n ); - - md5_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( md5_context ) ); - - if( ferror( f ) != 0 ) - { - fclose( f ); - return( POLARSSL_ERR_MD5_FILE_IO_ERROR ); - } - - fclose( f ); - return( 0 ); -} -#endif /* POLARSSL_FS_IO */ - -/* - * MD5 HMAC context setup - */ -void md5_hmac_starts( md5_context *ctx, const unsigned char *key, size_t keylen ) -{ - size_t i; - unsigned char sum[16]; - - if( keylen > 64 ) - { - md5( key, keylen, sum ); - keylen = 16; - 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] ); - } - - md5_starts( ctx ); - md5_update( ctx, ctx->ipad, 64 ); - - memset( sum, 0, sizeof( sum ) ); -} - -/* - * MD5 HMAC process buffer - */ -void md5_hmac_update( md5_context *ctx, const unsigned char *input, size_t ilen ) -{ - md5_update( ctx, input, ilen ); -} - -/* - * MD5 HMAC final digest - */ -void md5_hmac_finish( md5_context *ctx, unsigned char output[16] ) -{ - unsigned char tmpbuf[16]; - - md5_finish( ctx, tmpbuf ); - md5_starts( ctx ); - md5_update( ctx, ctx->opad, 64 ); - md5_update( ctx, tmpbuf, 16 ); - md5_finish( ctx, output ); - - memset( tmpbuf, 0, sizeof( tmpbuf ) ); -} - -/* - * MD5 HMAC context reset - */ -void md5_hmac_reset( md5_context *ctx ) -{ - md5_starts( ctx ); - md5_update( ctx, ctx->ipad, 64 ); -} - -/* - * output = HMAC-MD5( hmac key, input buffer ) - */ -void md5_hmac( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char output[16] ) -{ - md5_context ctx; - - md5_hmac_starts( &ctx, key, keylen ); - md5_hmac_update( &ctx, input, ilen ); - md5_hmac_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( md5_context ) ); -} - -#if defined(POLARSSL_SELF_TEST) -/* - * RFC 1321 test vectors - */ -static unsigned char md5_test_buf[7][81] = -{ - { "" }, - { "a" }, - { "abc" }, - { "message digest" }, - { "abcdefghijklmnopqrstuvwxyz" }, - { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, - { "12345678901234567890123456789012345678901234567890123456789012" \ - "345678901234567890" } -}; - -static const int md5_test_buflen[7] = -{ - 0, 1, 3, 14, 26, 62, 80 -}; - -static const unsigned char md5_test_sum[7][16] = -{ - { 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04, - 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E }, - { 0x0C, 0xC1, 0x75, 0xB9, 0xC0, 0xF1, 0xB6, 0xA8, - 0x31, 0xC3, 0x99, 0xE2, 0x69, 0x77, 0x26, 0x61 }, - { 0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0, - 0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72 }, - { 0xF9, 0x6B, 0x69, 0x7D, 0x7C, 0xB7, 0x93, 0x8D, - 0x52, 0x5A, 0x2F, 0x31, 0xAA, 0xF1, 0x61, 0xD0 }, - { 0xC3, 0xFC, 0xD3, 0xD7, 0x61, 0x92, 0xE4, 0x00, - 0x7D, 0xFB, 0x49, 0x6C, 0xCA, 0x67, 0xE1, 0x3B }, - { 0xD1, 0x74, 0xAB, 0x98, 0xD2, 0x77, 0xD9, 0xF5, - 0xA5, 0x61, 0x1C, 0x2C, 0x9F, 0x41, 0x9D, 0x9F }, - { 0x57, 0xED, 0xF4, 0xA2, 0x2B, 0xE3, 0xC9, 0x55, - 0xAC, 0x49, 0xDA, 0x2E, 0x21, 0x07, 0xB6, 0x7A } -}; - -/* - * RFC 2202 test vectors - */ -static unsigned char md5_hmac_test_key[7][26] = -{ - { "\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" }, - { "\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" }, - { "" }, /* 0xAA 80 times */ - { "" } -}; - -static const int md5_hmac_test_keylen[7] = -{ - 16, 4, 16, 25, 16, 80, 80 -}; - -static unsigned char md5_hmac_test_buf[7][74] = -{ - { "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" }, - { "Test Using Larger Than Block-Size Key and Larger" - " Than One Block-Size Data" } -}; - -static const int md5_hmac_test_buflen[7] = -{ - 8, 28, 50, 50, 20, 54, 73 -}; - -static const unsigned char md5_hmac_test_sum[7][16] = -{ - { 0x92, 0x94, 0x72, 0x7A, 0x36, 0x38, 0xBB, 0x1C, - 0x13, 0xF4, 0x8E, 0xF8, 0x15, 0x8B, 0xFC, 0x9D }, - { 0x75, 0x0C, 0x78, 0x3E, 0x6A, 0xB0, 0xB5, 0x03, - 0xEA, 0xA8, 0x6E, 0x31, 0x0A, 0x5D, 0xB7, 0x38 }, - { 0x56, 0xBE, 0x34, 0x52, 0x1D, 0x14, 0x4C, 0x88, - 0xDB, 0xB8, 0xC7, 0x33, 0xF0, 0xE8, 0xB3, 0xF6 }, - { 0x69, 0x7E, 0xAF, 0x0A, 0xCA, 0x3A, 0x3A, 0xEA, - 0x3A, 0x75, 0x16, 0x47, 0x46, 0xFF, 0xAA, 0x79 }, - { 0x56, 0x46, 0x1E, 0xF2, 0x34, 0x2E, 0xDC, 0x00, - 0xF9, 0xBA, 0xB9, 0x95 }, - { 0x6B, 0x1A, 0xB7, 0xFE, 0x4B, 0xD7, 0xBF, 0x8F, - 0x0B, 0x62, 0xE6, 0xCE, 0x61, 0xB9, 0xD0, 0xCD }, - { 0x6F, 0x63, 0x0F, 0xAD, 0x67, 0xCD, 0xA0, 0xEE, - 0x1F, 0xB1, 0xF5, 0x62, 0xDB, 0x3A, 0xA5, 0x3E } -}; - -/* - * Checkup routine - */ -int md5_self_test( int verbose ) -{ - int i, buflen; - unsigned char buf[1024]; - unsigned char md5sum[16]; - md5_context ctx; - - for( i = 0; i < 7; i++ ) - { - if( verbose != 0 ) - printf( " MD5 test #%d: ", i + 1 ); - - md5( md5_test_buf[i], md5_test_buflen[i], md5sum ); - - if( memcmp( md5sum, md5_test_sum[i], 16 ) != 0 ) - { - if( verbose != 0 ) - printf( "failed\n" ); - - return( 1 ); - } - - if( verbose != 0 ) - printf( "passed\n" ); - } - - if( verbose != 0 ) - printf( "\n" ); - - for( i = 0; i < 7; i++ ) - { - if( verbose != 0 ) - printf( " HMAC-MD5 test #%d: ", i + 1 ); - - if( i == 5 || i == 6 ) - { - memset( buf, '\xAA', buflen = 80 ); - md5_hmac_starts( &ctx, buf, buflen ); - } - else - md5_hmac_starts( &ctx, md5_hmac_test_key[i], - md5_hmac_test_keylen[i] ); - - md5_hmac_update( &ctx, md5_hmac_test_buf[i], - md5_hmac_test_buflen[i] ); - - md5_hmac_finish( &ctx, md5sum ); - - buflen = ( i == 4 ) ? 12 : 16; - - if( memcmp( md5sum, md5_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/makerom/polarssl/md5.h b/makerom/polarssl/md5.h deleted file mode 100644 index ecb64282..00000000 --- a/makerom/polarssl/md5.h +++ /dev/null @@ -1,182 +0,0 @@ -/** - * \file md5.h - * - * \brief MD5 message digest algorithm (hash function) - * - * Copyright (C) 2006-2013, 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_MD5_H -#define POLARSSL_MD5_H - -#include "polarssl/config.h" - -#include - -#ifdef _MSC_VER -#include -typedef UINT32 uint32_t; -#else -#include -#endif - -#define POLARSSL_ERR_MD5_FILE_IO_ERROR -0x0074 /**< Read/write error in file. */ - -#if !defined(POLARSSL_MD5_ALT) -// Regular implementation -// - -/** - * \brief MD5 context structure - */ -typedef struct -{ - uint32_t total[2]; /*!< number of bytes processed */ - uint32_t state[4]; /*!< 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 */ -} -md5_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief MD5 context setup - * - * \param ctx context to be initialized - */ -void md5_starts( md5_context *ctx ); - -/** - * \brief MD5 process buffer - * - * \param ctx MD5 context - * \param input buffer holding the data - * \param ilen length of the input data - */ -void md5_update( md5_context *ctx, const unsigned char *input, size_t ilen ); - -/** - * \brief MD5 final digest - * - * \param ctx MD5 context - * \param output MD5 checksum result - */ -void md5_finish( md5_context *ctx, unsigned char output[16] ); - -/* Internal use */ -void md5_process( md5_context *ctx, const unsigned char data[64] ); - -#ifdef __cplusplus -} -#endif - -#else /* POLARSSL_MD5_ALT */ -#include "polarssl/md5_alt.h" -#endif /* POLARSSL_MD5_ALT */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Output = MD5( input buffer ) - * - * \param input buffer holding the data - * \param ilen length of the input data - * \param output MD5 checksum result - */ -void md5( const unsigned char *input, size_t ilen, unsigned char output[16] ); - -/** - * \brief Output = MD5( file contents ) - * - * \param path input file name - * \param output MD5 checksum result - * - * \return 0 if successful, or POLARSSL_ERR_MD5_FILE_IO_ERROR - */ -int md5_file( const char *path, unsigned char output[16] ); - -/** - * \brief MD5 HMAC context setup - * - * \param ctx HMAC context to be initialized - * \param key HMAC secret key - * \param keylen length of the HMAC key - */ -void md5_hmac_starts( md5_context *ctx, - const unsigned char *key, size_t keylen ); - -/** - * \brief MD5 HMAC process buffer - * - * \param ctx HMAC context - * \param input buffer holding the data - * \param ilen length of the input data - */ -void md5_hmac_update( md5_context *ctx, - const unsigned char *input, size_t ilen ); - -/** - * \brief MD5 HMAC final digest - * - * \param ctx HMAC context - * \param output MD5 HMAC checksum result - */ -void md5_hmac_finish( md5_context *ctx, unsigned char output[16] ); - -/** - * \brief MD5 HMAC context reset - * - * \param ctx HMAC context to be reset - */ -void md5_hmac_reset( md5_context *ctx ); - -/** - * \brief Output = HMAC-MD5( 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-MD5 result - */ -void md5_hmac( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char output[16] ); - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int md5_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* md5.h */ diff --git a/makerom/polarssl/md_wrap.c b/makerom/polarssl/md_wrap.c deleted file mode 100644 index f276db59..00000000 --- a/makerom/polarssl/md_wrap.c +++ /dev/null @@ -1,733 +0,0 @@ -/** - * \file md_wrap.c - - * \brief Generic message digest wrapper for PolarSSL - * - * \author Adriaan de Jong - * - * 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. - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_MD_C) - -#include "polarssl/md_wrap.h" - -#if defined(POLARSSL_MD2_C) -#include "polarssl/md2.h" -#endif - -#if defined(POLARSSL_MD4_C) -#include "polarssl/md4.h" -#endif - -#if defined(POLARSSL_MD5_C) -#include "polarssl/md5.h" -#endif - -#if defined(POLARSSL_SHA1_C) -#include "polarssl/sha1.h" -#endif - -#if defined(POLARSSL_SHA2_C) -#include "polarssl/sha2.h" -#endif - -#if defined(POLARSSL_SHA4_C) -#include "polarssl/sha4.h" -#endif - -#include - -#if defined(POLARSSL_MD2_C) - -static void md2_starts_wrap( void *ctx ) -{ - md2_starts( (md2_context *) ctx ); -} - -static void md2_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - md2_update( (md2_context *) ctx, input, ilen ); -} - -static void md2_finish_wrap( void *ctx, unsigned char *output ) -{ - md2_finish( (md2_context *) ctx, output ); -} - -int md2_file_wrap( const char *path, unsigned char *output ) -{ -#if defined(POLARSSL_FS_IO) - return md2_file( path, output ); -#else - ((void) path); - ((void) output); - return POLARSSL_ERR_MD_FEATURE_UNAVAILABLE; -#endif -} - -static void md2_hmac_starts_wrap( void *ctx, const unsigned char *key, size_t keylen ) -{ - md2_hmac_starts( (md2_context *) ctx, key, keylen ); -} - -static void md2_hmac_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - md2_hmac_update( (md2_context *) ctx, input, ilen ); -} - -static void md2_hmac_finish_wrap( void *ctx, unsigned char *output ) -{ - md2_hmac_finish( (md2_context *) ctx, output ); -} - -static void md2_hmac_reset_wrap( void *ctx ) -{ - md2_hmac_reset( (md2_context *) ctx ); -} - -static void * md2_ctx_alloc( void ) -{ - return malloc( sizeof( md2_context ) ); -} - -static void md2_ctx_free( void *ctx ) -{ - free( ctx ); -} - -const md_info_t md2_info = { - POLARSSL_MD_MD2, - "MD2", - 16, - md2_starts_wrap, - md2_update_wrap, - md2_finish_wrap, - md2, - md2_file_wrap, - md2_hmac_starts_wrap, - md2_hmac_update_wrap, - md2_hmac_finish_wrap, - md2_hmac_reset_wrap, - md2_hmac, - md2_ctx_alloc, - md2_ctx_free, -}; - -#endif - -#if defined(POLARSSL_MD4_C) - -void md4_starts_wrap( void *ctx ) -{ - md4_starts( (md4_context *) ctx ); -} - -void md4_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - md4_update( (md4_context *) ctx, input, ilen ); -} - -void md4_finish_wrap( void *ctx, unsigned char *output ) -{ - md4_finish( (md4_context *) ctx, output ); -} - -int md4_file_wrap( const char *path, unsigned char *output ) -{ -#if defined(POLARSSL_FS_IO) - return md4_file( path, output ); -#else - ((void) path); - ((void) output); - return POLARSSL_ERR_MD_FEATURE_UNAVAILABLE; -#endif -} - -void md4_hmac_starts_wrap( void *ctx, const unsigned char *key, size_t keylen ) -{ - md4_hmac_starts( (md4_context *) ctx, key, keylen ); -} - -void md4_hmac_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - md4_hmac_update( (md4_context *) ctx, input, ilen ); -} - -void md4_hmac_finish_wrap( void *ctx, unsigned char *output ) -{ - md4_hmac_finish( (md4_context *) ctx, output ); -} - -void md4_hmac_reset_wrap( void *ctx ) -{ - md4_hmac_reset( (md4_context *) ctx ); -} - -void *md4_ctx_alloc( void ) -{ - return malloc( sizeof( md4_context ) ); -} - -void md4_ctx_free( void *ctx ) -{ - free( ctx ); -} - -const md_info_t md4_info = { - POLARSSL_MD_MD4, - "MD4", - 16, - md4_starts_wrap, - md4_update_wrap, - md4_finish_wrap, - md4, - md4_file_wrap, - md4_hmac_starts_wrap, - md4_hmac_update_wrap, - md4_hmac_finish_wrap, - md4_hmac_reset_wrap, - md4_hmac, - md4_ctx_alloc, - md4_ctx_free, -}; - -#endif - -#if defined(POLARSSL_MD5_C) - -static void md5_starts_wrap( void *ctx ) -{ - md5_starts( (md5_context *) ctx ); -} - -static void md5_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - md5_update( (md5_context *) ctx, input, ilen ); -} - -static void md5_finish_wrap( void *ctx, unsigned char *output ) -{ - md5_finish( (md5_context *) ctx, output ); -} - -int md5_file_wrap( const char *path, unsigned char *output ) -{ -#if defined(POLARSSL_FS_IO) - return md5_file( path, output ); -#else - ((void) path); - ((void) output); - return POLARSSL_ERR_MD_FEATURE_UNAVAILABLE; -#endif -} - -static void md5_hmac_starts_wrap( void *ctx, const unsigned char *key, size_t keylen ) -{ - md5_hmac_starts( (md5_context *) ctx, key, keylen ); -} - -static void md5_hmac_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - md5_hmac_update( (md5_context *) ctx, input, ilen ); -} - -static void md5_hmac_finish_wrap( void *ctx, unsigned char *output ) -{ - md5_hmac_finish( (md5_context *) ctx, output ); -} - -static void md5_hmac_reset_wrap( void *ctx ) -{ - md5_hmac_reset( (md5_context *) ctx ); -} - -static void * md5_ctx_alloc( void ) -{ - return malloc( sizeof( md5_context ) ); -} - -static void md5_ctx_free( void *ctx ) -{ - free( ctx ); -} - -const md_info_t md5_info = { - POLARSSL_MD_MD5, - "MD5", - 16, - md5_starts_wrap, - md5_update_wrap, - md5_finish_wrap, - md5, - md5_file_wrap, - md5_hmac_starts_wrap, - md5_hmac_update_wrap, - md5_hmac_finish_wrap, - md5_hmac_reset_wrap, - md5_hmac, - md5_ctx_alloc, - md5_ctx_free, -}; - -#endif - -#if defined(POLARSSL_SHA1_C) - -void sha1_starts_wrap( void *ctx ) -{ - sha1_starts( (sha1_context *) ctx ); -} - -void sha1_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - sha1_update( (sha1_context *) ctx, input, ilen ); -} - -void sha1_finish_wrap( void *ctx, unsigned char *output ) -{ - sha1_finish( (sha1_context *) ctx, output ); -} - -int sha1_file_wrap( const char *path, unsigned char *output ) -{ -#if defined(POLARSSL_FS_IO) - return sha1_file( path, output ); -#else - ((void) path); - ((void) output); - return POLARSSL_ERR_MD_FEATURE_UNAVAILABLE; -#endif -} - -void sha1_hmac_starts_wrap( void *ctx, const unsigned char *key, size_t keylen ) -{ - sha1_hmac_starts( (sha1_context *) ctx, key, keylen ); -} - -void sha1_hmac_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - sha1_hmac_update( (sha1_context *) ctx, input, ilen ); -} - -void sha1_hmac_finish_wrap( void *ctx, unsigned char *output ) -{ - sha1_hmac_finish( (sha1_context *) ctx, output ); -} - -void sha1_hmac_reset_wrap( void *ctx ) -{ - sha1_hmac_reset( (sha1_context *) ctx ); -} - -void * sha1_ctx_alloc( void ) -{ - return malloc( sizeof( sha1_context ) ); -} - -void sha1_ctx_free( void *ctx ) -{ - free( ctx ); -} - -const md_info_t sha1_info = { - POLARSSL_MD_SHA1, - "SHA1", - 20, - sha1_starts_wrap, - sha1_update_wrap, - sha1_finish_wrap, - sha1, - sha1_file_wrap, - sha1_hmac_starts_wrap, - sha1_hmac_update_wrap, - sha1_hmac_finish_wrap, - sha1_hmac_reset_wrap, - sha1_hmac, - sha1_ctx_alloc, - sha1_ctx_free, -}; - -#endif - -/* - * Wrappers for generic message digests - */ -#if defined(POLARSSL_SHA2_C) - -void sha224_starts_wrap( void *ctx ) -{ - sha2_starts( (sha2_context *) ctx, 1 ); -} - -void sha224_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - sha2_update( (sha2_context *) ctx, input, ilen ); -} - -void sha224_finish_wrap( void *ctx, unsigned char *output ) -{ - sha2_finish( (sha2_context *) ctx, output ); -} - -void sha224_wrap( const unsigned char *input, size_t ilen, - unsigned char *output ) -{ - sha2( input, ilen, output, 1 ); -} - -int sha224_file_wrap( const char *path, unsigned char *output ) -{ -#if defined(POLARSSL_FS_IO) - return sha2_file( path, output, 1 ); -#else - ((void) path); - ((void) output); - return POLARSSL_ERR_MD_FEATURE_UNAVAILABLE; -#endif -} - -void sha224_hmac_starts_wrap( void *ctx, const unsigned char *key, size_t keylen ) -{ - sha2_hmac_starts( (sha2_context *) ctx, key, keylen, 1 ); -} - -void sha224_hmac_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - sha2_hmac_update( (sha2_context *) ctx, input, ilen ); -} - -void sha224_hmac_finish_wrap( void *ctx, unsigned char *output ) -{ - sha2_hmac_finish( (sha2_context *) ctx, output ); -} - -void sha224_hmac_reset_wrap( void *ctx ) -{ - sha2_hmac_reset( (sha2_context *) ctx ); -} - -void sha224_hmac_wrap( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char *output ) -{ - sha2_hmac( key, keylen, input, ilen, output, 1 ); -} - -void * sha224_ctx_alloc( void ) -{ - return malloc( sizeof( sha2_context ) ); -} - -void sha224_ctx_free( void *ctx ) -{ - free( ctx ); -} - -const md_info_t sha224_info = { - POLARSSL_MD_SHA224, - "SHA224", - 28, - sha224_starts_wrap, - sha224_update_wrap, - sha224_finish_wrap, - sha224_wrap, - sha224_file_wrap, - sha224_hmac_starts_wrap, - sha224_hmac_update_wrap, - sha224_hmac_finish_wrap, - sha224_hmac_reset_wrap, - sha224_hmac_wrap, - sha224_ctx_alloc, - sha224_ctx_free, -}; - -void sha256_starts_wrap( void *ctx ) -{ - sha2_starts( (sha2_context *) ctx, 0 ); -} - -void sha256_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - sha2_update( (sha2_context *) ctx, input, ilen ); -} - -void sha256_finish_wrap( void *ctx, unsigned char *output ) -{ - sha2_finish( (sha2_context *) ctx, output ); -} - -void sha256_wrap( const unsigned char *input, size_t ilen, - unsigned char *output ) -{ - sha2( input, ilen, output, 0 ); -} - -int sha256_file_wrap( const char *path, unsigned char *output ) -{ -#if defined(POLARSSL_FS_IO) - return sha2_file( path, output, 0 ); -#else - ((void) path); - ((void) output); - return POLARSSL_ERR_MD_FEATURE_UNAVAILABLE; -#endif -} - -void sha256_hmac_starts_wrap( void *ctx, const unsigned char *key, size_t keylen ) -{ - sha2_hmac_starts( (sha2_context *) ctx, key, keylen, 0 ); -} - -void sha256_hmac_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - sha2_hmac_update( (sha2_context *) ctx, input, ilen ); -} - -void sha256_hmac_finish_wrap( void *ctx, unsigned char *output ) -{ - sha2_hmac_finish( (sha2_context *) ctx, output ); -} - -void sha256_hmac_reset_wrap( void *ctx ) -{ - sha2_hmac_reset( (sha2_context *) ctx ); -} - -void sha256_hmac_wrap( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char *output ) -{ - sha2_hmac( key, keylen, input, ilen, output, 0 ); -} - -void * sha256_ctx_alloc( void ) -{ - return malloc( sizeof( sha2_context ) ); -} - -void sha256_ctx_free( void *ctx ) -{ - free( ctx ); -} - -const md_info_t sha256_info = { - POLARSSL_MD_SHA256, - "SHA256", - 32, - sha256_starts_wrap, - sha256_update_wrap, - sha256_finish_wrap, - sha256_wrap, - sha256_file_wrap, - sha256_hmac_starts_wrap, - sha256_hmac_update_wrap, - sha256_hmac_finish_wrap, - sha256_hmac_reset_wrap, - sha256_hmac_wrap, - sha256_ctx_alloc, - sha256_ctx_free, -}; - -#endif - -#if defined(POLARSSL_SHA4_C) - -void sha384_starts_wrap( void *ctx ) -{ - sha4_starts( (sha4_context *) ctx, 1 ); -} - -void sha384_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - sha4_update( (sha4_context *) ctx, input, ilen ); -} - -void sha384_finish_wrap( void *ctx, unsigned char *output ) -{ - sha4_finish( (sha4_context *) ctx, output ); -} - -void sha384_wrap( const unsigned char *input, size_t ilen, - unsigned char *output ) -{ - sha4( input, ilen, output, 1 ); -} - -int sha384_file_wrap( const char *path, unsigned char *output ) -{ -#if defined(POLARSSL_FS_IO) - return sha4_file( path, output, 1 ); -#else - ((void) path); - ((void) output); - return POLARSSL_ERR_MD_FEATURE_UNAVAILABLE; -#endif -} - -void sha384_hmac_starts_wrap( void *ctx, const unsigned char *key, size_t keylen ) -{ - sha4_hmac_starts( (sha4_context *) ctx, key, keylen, 1 ); -} - -void sha384_hmac_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - sha4_hmac_update( (sha4_context *) ctx, input, ilen ); -} - -void sha384_hmac_finish_wrap( void *ctx, unsigned char *output ) -{ - sha4_hmac_finish( (sha4_context *) ctx, output ); -} - -void sha384_hmac_reset_wrap( void *ctx ) -{ - sha4_hmac_reset( (sha4_context *) ctx ); -} - -void sha384_hmac_wrap( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char *output ) -{ - sha4_hmac( key, keylen, input, ilen, output, 1 ); -} - -void * sha384_ctx_alloc( void ) -{ - return malloc( sizeof( sha4_context ) ); -} - -void sha384_ctx_free( void *ctx ) -{ - free( ctx ); -} - -const md_info_t sha384_info = { - POLARSSL_MD_SHA384, - "SHA384", - 48, - sha384_starts_wrap, - sha384_update_wrap, - sha384_finish_wrap, - sha384_wrap, - sha384_file_wrap, - sha384_hmac_starts_wrap, - sha384_hmac_update_wrap, - sha384_hmac_finish_wrap, - sha384_hmac_reset_wrap, - sha384_hmac_wrap, - sha384_ctx_alloc, - sha384_ctx_free, -}; - -void sha512_starts_wrap( void *ctx ) -{ - sha4_starts( (sha4_context *) ctx, 0 ); -} - -void sha512_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - sha4_update( (sha4_context *) ctx, input, ilen ); -} - -void sha512_finish_wrap( void *ctx, unsigned char *output ) -{ - sha4_finish( (sha4_context *) ctx, output ); -} - -void sha512_wrap( const unsigned char *input, size_t ilen, - unsigned char *output ) -{ - sha4( input, ilen, output, 0 ); -} - -int sha512_file_wrap( const char *path, unsigned char *output ) -{ -#if defined(POLARSSL_FS_IO) - return sha4_file( path, output, 0 ); -#else - ((void) path); - ((void) output); - return POLARSSL_ERR_MD_FEATURE_UNAVAILABLE; -#endif -} - -void sha512_hmac_starts_wrap( void *ctx, const unsigned char *key, size_t keylen ) -{ - sha4_hmac_starts( (sha4_context *) ctx, key, keylen, 0 ); -} - -void sha512_hmac_update_wrap( void *ctx, const unsigned char *input, size_t ilen ) -{ - sha4_hmac_update( (sha4_context *) ctx, input, ilen ); -} - -void sha512_hmac_finish_wrap( void *ctx, unsigned char *output ) -{ - sha4_hmac_finish( (sha4_context *) ctx, output ); -} - -void sha512_hmac_reset_wrap( void *ctx ) -{ - sha4_hmac_reset( (sha4_context *) ctx ); -} - -void sha512_hmac_wrap( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char *output ) -{ - sha4_hmac( key, keylen, input, ilen, output, 0 ); -} - -void * sha512_ctx_alloc( void ) -{ - return malloc( sizeof( sha4_context ) ); -} - -void sha512_ctx_free( void *ctx ) -{ - free( ctx ); -} - -const md_info_t sha512_info = { - POLARSSL_MD_SHA512, - "SHA512", - 64, - sha512_starts_wrap, - sha512_update_wrap, - sha512_finish_wrap, - sha512_wrap, - sha512_file_wrap, - sha512_hmac_starts_wrap, - sha512_hmac_update_wrap, - sha512_hmac_finish_wrap, - sha512_hmac_reset_wrap, - sha512_hmac_wrap, - sha512_ctx_alloc, - sha512_ctx_free, -}; - -#endif - -#endif diff --git a/makerom/polarssl/md_wrap.h b/makerom/polarssl/md_wrap.h deleted file mode 100644 index 43fc76fc..00000000 --- a/makerom/polarssl/md_wrap.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * \file md_wrap.h - * - * \brief Message digest wrappers. - * - * \author Adriaan de Jong - * - * Copyright (C) 2006-2011, 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_MD_WRAP_H -#define POLARSSL_MD_WRAP_H - -#include "polarssl/config.h" -#include "polarssl/md.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(POLARSSL_MD2_C) -extern const md_info_t md2_info; -#endif -#if defined(POLARSSL_MD4_C) -extern const md_info_t md4_info; -#endif -#if defined(POLARSSL_MD5_C) -extern const md_info_t md5_info; -#endif -#if defined(POLARSSL_SHA1_C) -extern const md_info_t sha1_info; -#endif -#if defined(POLARSSL_SHA2_C) -extern const md_info_t sha224_info; -extern const md_info_t sha256_info; -#endif -#if defined(POLARSSL_SHA4_C) -extern const md_info_t sha384_info; -extern const md_info_t sha512_info; -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* POLARSSL_MD_WRAP_H */ diff --git a/makerom/polarssl/net.h b/makerom/polarssl/net.h deleted file mode 100644 index 88302ac0..00000000 --- a/makerom/polarssl/net.h +++ /dev/null @@ -1,159 +0,0 @@ -/** - * \file net.h - * - * \brief Network communication functions - * - * Copyright (C) 2006-2011, 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_NET_H -#define POLARSSL_NET_H - -#include - -#define POLARSSL_ERR_NET_UNKNOWN_HOST -0x0056 /**< Failed to get an IP address for the given hostname. */ -#define POLARSSL_ERR_NET_SOCKET_FAILED -0x0042 /**< Failed to open a socket. */ -#define POLARSSL_ERR_NET_CONNECT_FAILED -0x0044 /**< The connection to the given server / port failed. */ -#define POLARSSL_ERR_NET_BIND_FAILED -0x0046 /**< Binding of the socket failed. */ -#define POLARSSL_ERR_NET_LISTEN_FAILED -0x0048 /**< Could not listen on the socket. */ -#define POLARSSL_ERR_NET_ACCEPT_FAILED -0x004A /**< Could not accept the incoming connection. */ -#define POLARSSL_ERR_NET_RECV_FAILED -0x004C /**< Reading information from the socket failed. */ -#define POLARSSL_ERR_NET_SEND_FAILED -0x004E /**< Sending information through the socket failed. */ -#define POLARSSL_ERR_NET_CONN_RESET -0x0050 /**< Connection was reset by peer. */ -#define POLARSSL_ERR_NET_WANT_READ -0x0052 /**< Connection requires a read call. */ -#define POLARSSL_ERR_NET_WANT_WRITE -0x0054 /**< Connection requires a write call. */ - -#define POLARSSL_NET_LISTEN_BACKLOG 10 /**< The backlog that listen() should use. */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Initiate a TCP connection with host:port - * - * \param fd Socket to use - * \param host Host to connect to - * \param port Port to connect to - * - * \return 0 if successful, or one of: - * POLARSSL_ERR_NET_SOCKET_FAILED, - * POLARSSL_ERR_NET_UNKNOWN_HOST, - * POLARSSL_ERR_NET_CONNECT_FAILED - */ -int net_connect( int *fd, const char *host, int port ); - -/** - * \brief Create a listening socket on bind_ip:port. - * If bind_ip == NULL, all interfaces are binded. - * - * \param fd Socket to use - * \param bind_ip IP to bind to, can be NULL - * \param port Port number to use - * - * \return 0 if successful, or one of: - * POLARSSL_ERR_NET_SOCKET_FAILED, - * POLARSSL_ERR_NET_BIND_FAILED, - * POLARSSL_ERR_NET_LISTEN_FAILED - */ -int net_bind( int *fd, const char *bind_ip, int port ); - -/** - * \brief Accept a connection from a remote client - * - * \param bind_fd Relevant socket - * \param client_fd Will contain the connected client socket - * \param client_ip Will contain the client IP address - * - * \return 0 if successful, POLARSSL_ERR_NET_ACCEPT_FAILED, or - * POLARSSL_ERR_NET_WOULD_BLOCK is bind_fd was set to - * non-blocking and accept() is blocking. - */ -int net_accept( int bind_fd, int *client_fd, void *client_ip ); - -/** - * \brief Set the socket blocking - * - * \param fd Socket to set - * - * \return 0 if successful, or a non-zero error code - */ -int net_set_block( int fd ); - -/** - * \brief Set the socket non-blocking - * - * \param fd Socket to set - * - * \return 0 if successful, or a non-zero error code - */ -int net_set_nonblock( int fd ); - -/** - * \brief Portable usleep helper - * - * \param usec Amount of microseconds to sleep - * - * \note Real amount of time slept will not be less than - * select()'s timeout granularity (typically, 10ms). - */ -void net_usleep( unsigned long usec ); - -/** - * \brief Read at most 'len' characters. If no error occurs, - * the actual amount read is returned. - * - * \param ctx Socket - * \param buf The buffer to write to - * \param len Maximum length of the buffer - * - * \return This function returns the number of bytes received, - * or a non-zero error code; POLARSSL_ERR_NET_WANT_READ - * indicates read() is blocking. - */ -int net_recv( void *ctx, unsigned char *buf, size_t len ); - -/** - * \brief Write at most 'len' characters. If no error occurs, - * the actual amount read is returned. - * - * \param ctx Socket - * \param buf The buffer to read from - * \param len The length of the buffer - * - * \return This function returns the number of bytes sent, - * or a non-zero error code; POLARSSL_ERR_NET_WANT_WRITE - * indicates write() is blocking. - */ -int net_send( void *ctx, const unsigned char *buf, size_t len ); - -/** - * \brief Gracefully shutdown the connection - * - * \param fd The socket to close - */ -void net_close( int fd ); - -#ifdef __cplusplus -} -#endif - -#endif /* net.h */ diff --git a/makerom/polarssl/openssl.h b/makerom/polarssl/openssl.h deleted file mode 100644 index 4423857d..00000000 --- a/makerom/polarssl/openssl.h +++ /dev/null @@ -1,136 +0,0 @@ -/** - * \file openssl.h - * - * \brief OpenSSL wrapper (definitions, inline functions). - * - * 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. - */ -/* - * OpenSSL wrapper contributed by David Barett - */ -#ifndef POLARSSL_OPENSSL_H -#define POLARSSL_OPENSSL_H - -#include "polarssl/aes.h" -#include "polarssl/md5.h" -#include "polarssl/rsa.h" -#include "polarssl/sha1.h" - -#define AES_SIZE 16 -#define AES_BLOCK_SIZE 16 -#define AES_KEY aes_context -#define MD5_CTX md5_context -#define SHA_CTX sha1_context - -#define SHA1_Init( CTX ) \ - sha1_starts( (CTX) ) -#define SHA1_Update( CTX, BUF, LEN ) \ - sha1_update( (CTX), (unsigned char *)(BUF), (LEN) ) -#define SHA1_Final( OUT, CTX ) \ - sha1_finish( (CTX), (OUT) ) - -#define MD5_Init( CTX ) \ - md5_starts( (CTX) ) -#define MD5_Update( CTX, BUF, LEN ) \ - md5_update( (CTX), (unsigned char *)(BUF), (LEN) ) -#define MD5_Final( OUT, CTX ) \ - md5_finish( (CTX), (OUT) ) - -#define AES_set_encrypt_key( KEY, KEYSIZE, CTX ) \ - aes_setkey_enc( (CTX), (KEY), (KEYSIZE) ) -#define AES_set_decrypt_key( KEY, KEYSIZE, CTX ) \ - aes_setkey_dec( (CTX), (KEY), (KEYSIZE) ) -#define AES_cbc_encrypt( INPUT, OUTPUT, LEN, CTX, IV, MODE ) \ - aes_crypt_cbc( (CTX), (MODE), (LEN), (IV), (INPUT), (OUTPUT) ) - -/* - * RSA stuff follows. TODO: needs cleanup - */ -inline int __RSA_Passthrough( void *output, void *input, int size ) -{ - memcpy( output, input, size ); - return size; -} - -inline rsa_context* d2i_RSA_PUBKEY( void *ignore, unsigned char **bufptr, - int len ) -{ - unsigned char *buffer = *(unsigned char **) bufptr; - rsa_context *rsa; - - /* - * Not a general-purpose parser: only parses public key from *exactly* - * openssl genrsa -out privkey.pem 512 (or 1024) - * openssl rsa -in privkey.pem -out privatekey.der -outform der - * openssl rsa -in privkey.pem -out pubkey.der -outform der -pubout - * - * TODO: make a general-purpose parse - */ - if( ignore != 0 || ( len != 94 && len != 162 ) ) - return( 0 ); - - rsa = (rsa_context *) malloc( sizeof( rsa_rsa ) ); - if( rsa == NULL ) - return( 0 ); - - memset( rsa, 0, sizeof( rsa_context ) ); - - if( ( len == 94 && - mpi_read_binary( &rsa->N, &buffer[ 25], 64 ) == 0 && - mpi_read_binary( &rsa->E, &buffer[ 91], 3 ) == 0 ) || - ( len == 162 && - mpi_read_binary( &rsa->N, &buffer[ 29], 128 ) == 0 ) && - mpi_read_binary( &rsa->E, &buffer[159], 3 ) == 0 ) - { - /* - * key read successfully - */ - rsa->len = ( mpi_msb( &rsa->N ) + 7 ) >> 3; - return( rsa ); - } - else - { - memset( rsa, 0, sizeof( rsa_context ) ); - free( rsa ); - return( 0 ); - } -} - -#define RSA rsa_context -#define RSA_PKCS1_PADDING 1 /* ignored; always encrypt with this */ -#define RSA_size( CTX ) (CTX)->len -#define RSA_free( CTX ) rsa_free( CTX ) -#define ERR_get_error( ) "ERR_get_error() not supported" -#define RSA_blinding_off( IGNORE ) - -#define d2i_RSAPrivateKey( a, b, c ) new rsa_context /* TODO: C++ bleh */ - -inline int RSA_public_decrypt ( int size, unsigned char* input, unsigned char* output, RSA* key, int ignore ) { int outsize=size; if( !rsa_pkcs1_decrypt( key, RSA_PUBLIC, &outsize, input, output ) ) return outsize; else return -1; } -inline int RSA_private_decrypt( int size, unsigned char* input, unsigned char* output, RSA* key, int ignore ) { int outsize=size; if( !rsa_pkcs1_decrypt( key, RSA_PRIVATE, &outsize, input, output ) ) return outsize; else return -1; } -inline int RSA_public_encrypt ( int size, unsigned char* input, unsigned char* output, RSA* key, int ignore ) { if( !rsa_pkcs1_encrypt( key, RSA_PUBLIC, size, input, output ) ) return RSA_size(key); else return -1; } -inline int RSA_private_encrypt( int size, unsigned char* input, unsigned char* output, RSA* key, int ignore ) { if( !rsa_pkcs1_encrypt( key, RSA_PRIVATE, size, input, output ) ) return RSA_size(key); else return -1; } - -#ifdef __cplusplus -} -#endif - -#endif /* openssl.h */ diff --git a/makerom/polarssl/padlock.c b/makerom/polarssl/padlock.c deleted file mode 100644 index 9ddac15e..00000000 --- a/makerom/polarssl/padlock.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * VIA PadLock support functions - * - * 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 implementation is based on the VIA PadLock Programming Guide: - * - * http://www.via.com.tw/en/downloads/whitepapers/initiatives/padlock/ - * programming_guide.pdf - */ - -#include "polarssl/config.h" - -#if defined(POLARSSL_PADLOCK_C) - -#include "polarssl/padlock.h" - -#if defined(POLARSSL_HAVE_X86) - -/* - * PadLock detection routine - */ -int padlock_supports( int feature ) -{ - static int flags = -1; - int ebx, edx; - - if( flags == -1 ) - { - __asm__( "movl %%ebx, %0 \n" \ - "movl $0xC0000000, %%eax \n" \ - "cpuid \n" \ - "cmpl $0xC0000001, %%eax \n" \ - "movl $0, %%edx \n" \ - "jb unsupported \n" \ - "movl $0xC0000001, %%eax \n" \ - "cpuid \n" \ - "unsupported: \n" \ - "movl %%edx, %1 \n" \ - "movl %2, %%ebx \n" - : "=m" (ebx), "=m" (edx) - : "m" (ebx) - : "eax", "ecx", "edx" ); - - flags = edx; - } - - return( flags & feature ); -} - -/* - * PadLock AES-ECB block en(de)cryption - */ -int padlock_xcryptecb( aes_context *ctx, - int mode, - const unsigned char input[16], - unsigned char output[16] ) -{ - int ebx; - uint32_t *rk; - uint32_t *blk; - uint32_t *ctrl; - unsigned char buf[256]; - - rk = ctx->rk; - blk = PADLOCK_ALIGN16( buf ); - memcpy( blk, input, 16 ); - - ctrl = blk + 4; - *ctrl = 0x80 | ctx->nr | ( ( ctx->nr + ( mode^1 ) - 10 ) << 9 ); - - __asm__( "pushfl; popfl \n" \ - "movl %%ebx, %0 \n" \ - "movl $1, %%ecx \n" \ - "movl %2, %%edx \n" \ - "movl %3, %%ebx \n" \ - "movl %4, %%esi \n" \ - "movl %4, %%edi \n" \ - ".byte 0xf3,0x0f,0xa7,0xc8\n" \ - "movl %1, %%ebx \n" - : "=m" (ebx) - : "m" (ebx), "m" (ctrl), "m" (rk), "m" (blk) - : "ecx", "edx", "esi", "edi" ); - - memcpy( output, blk, 16 ); - - return( 0 ); -} - -/* - * PadLock AES-CBC buffer en(de)cryption - */ -int padlock_xcryptcbc( aes_context *ctx, - int mode, - size_t length, - unsigned char iv[16], - const unsigned char *input, - unsigned char *output ) -{ - int ebx; - size_t count; - uint32_t *rk; - uint32_t *iw; - uint32_t *ctrl; - unsigned char buf[256]; - - if( ( (long) input & 15 ) != 0 || - ( (long) output & 15 ) != 0 ) - return( POLARSSL_ERR_PADLOCK_DATA_MISALIGNED ); - - rk = ctx->rk; - iw = PADLOCK_ALIGN16( buf ); - memcpy( iw, iv, 16 ); - - ctrl = iw + 4; - *ctrl = 0x80 | ctx->nr | ( ( ctx->nr + (mode^1) - 10 ) << 9 ); - - count = (length + 15) >> 4; - - __asm__( "pushfl; popfl \n" \ - "movl %%ebx, %0 \n" \ - "movl %2, %%ecx \n" \ - "movl %3, %%edx \n" \ - "movl %4, %%ebx \n" \ - "movl %5, %%esi \n" \ - "movl %6, %%edi \n" \ - "movl %7, %%eax \n" \ - ".byte 0xf3,0x0f,0xa7,0xd0\n" \ - "movl %1, %%ebx \n" - : "=m" (ebx) - : "m" (ebx), "m" (count), "m" (ctrl), - "m" (rk), "m" (input), "m" (output), "m" (iw) - : "eax", "ecx", "edx", "esi", "edi" ); - - memcpy( iv, iw, 16 ); - - return( 0 ); -} - -#endif - -#endif diff --git a/makerom/polarssl/padlock.h b/makerom/polarssl/padlock.h deleted file mode 100644 index 48f5f624..00000000 --- a/makerom/polarssl/padlock.h +++ /dev/null @@ -1,108 +0,0 @@ -/** - * \file padlock.h - * - * \brief VIA PadLock ACE for HW encryption/decryption supported by some processors - * - * 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" - -#define POLARSSL_ERR_PADLOCK_DATA_MISALIGNED -0x0030 /**< Input data should be aligned. */ - -#if defined(POLARSSL_HAVE_ASM) && defined(__GNUC__) && defined(__i386__) - -#ifndef POLARSSL_HAVE_X86 -#define POLARSSL_HAVE_X86 -#endif - -#ifdef _MSC_VER -#include -typedef INT32 int32_t; -#else -#include -#endif - - -#define PADLOCK_RNG 0x000C -#define PADLOCK_ACE 0x00C0 -#define PADLOCK_PHE 0x0C00 -#define PADLOCK_PMM 0x3000 - -#define PADLOCK_ALIGN16(x) (uint32_t *) (16 + ((int32_t) x & ~15)) - -#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, - size_t length, - unsigned char iv[16], - const unsigned char *input, - unsigned char *output ); - -#ifdef __cplusplus -} -#endif - -#endif /* HAVE_X86 */ - -#endif /* padlock.h */ diff --git a/makerom/polarssl/pbkdf2.h b/makerom/polarssl/pbkdf2.h deleted file mode 100644 index 2a1f4ba2..00000000 --- a/makerom/polarssl/pbkdf2.h +++ /dev/null @@ -1,82 +0,0 @@ -/** - * \file pbkdf2.h - * - * \brief Password-Based Key Derivation Function 2 (from PKCS#5) - * DEPRECATED: use pkcs5.h instead. - * - * \author Mathias Olsson - * - * Copyright (C) 2006-2012, 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_PBKDF2_H -#define POLARSSL_PBKDF2_H - -#include - -#include "polarssl/md.h" - -#ifdef _MSC_VER -#include -typedef UINT32 uint32_t; -#else -#include -#endif - -#define POLARSSL_ERR_PBKDF2_BAD_INPUT_DATA -0x007C /**< Bad input parameters to function. */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief PKCS#5 PBKDF2 using HMAC - * DEPRECATED: Use pkcs5_pbkdf2_hmac() instead! - * - * \param ctx Generic HMAC context - * \param password Password to use when generating key - * \param plen Length of password - * \param salt Salt to use when generating key - * \param slen Length of salt - * \param iteration_count Iteration count - * \param key_length Length of generated key - * \param output Generated key. Must be at least as big as key_length - * - * \returns 0 on success, or a PolarSSL error code if verification fails. - */ -int pbkdf2_hmac( md_context_t *ctx, const unsigned char *password, - size_t plen, const unsigned char *salt, size_t slen, - unsigned int iteration_count, - uint32_t key_length, unsigned char *output ); - -/** - * \brief Checkup routine - * DEPRECATED: Use pkcs5_self_test() instead! - * - * \return 0 if successful, or 1 if the test failed - */ -int pbkdf2_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* pbkdf2.h */ diff --git a/makerom/polarssl/pem.h b/makerom/polarssl/pem.h deleted file mode 100644 index e95dc10a..00000000 --- a/makerom/polarssl/pem.h +++ /dev/null @@ -1,105 +0,0 @@ -/** - * \file pem.h - * - * \brief Privacy Enhanced Mail (PEM) decoding - * - * Copyright (C) 2006-2013, 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_PEM_H -#define POLARSSL_PEM_H - -#include - -/** - * \name PEM Error codes - * These error codes are returned in case of errors reading the - * PEM data. - * \{ - */ -#define POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT -0x1080 /**< No PEM header or footer found. */ -#define POLARSSL_ERR_PEM_INVALID_DATA -0x1100 /**< PEM string is not as expected. */ -#define POLARSSL_ERR_PEM_MALLOC_FAILED -0x1180 /**< Failed to allocate memory. */ -#define POLARSSL_ERR_PEM_INVALID_ENC_IV -0x1200 /**< RSA IV is not in hex-format. */ -#define POLARSSL_ERR_PEM_UNKNOWN_ENC_ALG -0x1280 /**< Unsupported key encryption algorithm. */ -#define POLARSSL_ERR_PEM_PASSWORD_REQUIRED -0x1300 /**< Private key password can't be empty. */ -#define POLARSSL_ERR_PEM_PASSWORD_MISMATCH -0x1380 /**< Given private key password does not allow for correct decryption. */ -#define POLARSSL_ERR_PEM_FEATURE_UNAVAILABLE -0x1400 /**< Unavailable feature, e.g. hashing/encryption combination. */ -#define POLARSSL_ERR_PEM_BAD_INPUT_DATA -0x1480 /**< Bad input parameters to function. */ -/* \} name */ - -/** - * \brief PEM context structure - */ -typedef struct -{ - unsigned char *buf; /*!< buffer for decoded data */ - size_t buflen; /*!< length of the buffer */ - unsigned char *info; /*!< buffer for extra header information */ -} -pem_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief PEM context setup - * - * \param ctx context to be initialized - */ -void pem_init( pem_context *ctx ); - -/** - * \brief Read a buffer for PEM information and store the resulting - * data into the specified context buffers. - * - * \param ctx context to use - * \param header header string to seek and expect - * \param footer footer string to seek and expect - * \param data source data to look in - * \param pwd password for decryption (can be NULL) - * \param pwdlen length of password - * \param use_len destination for total length used (set after header is - * correctly read, so unless you get - * POLARSSL_ERR_PEM_BAD_INPUT_DATA or - * POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT, use_len is - * the length to skip) - * - * \return 0 on success, ior a specific PEM error code - */ -int pem_read_buffer( pem_context *ctx, char *header, char *footer, - const unsigned char *data, - const unsigned char *pwd, - size_t pwdlen, size_t *use_len ); - -/** - * \brief PEM context memory freeing - * - * \param ctx context to be freed - */ -void pem_free( pem_context *ctx ); - -#ifdef __cplusplus -} -#endif - -#endif /* pem.h */ diff --git a/makerom/polarssl/pkcs11.h b/makerom/polarssl/pkcs11.h deleted file mode 100644 index 3e95392d..00000000 --- a/makerom/polarssl/pkcs11.h +++ /dev/null @@ -1,161 +0,0 @@ -/** - * \file pkcs11.h - * - * \brief Wrapper for PKCS#11 library libpkcs11-helper - * - * \author Adriaan de Jong - * - * Copyright (C) 2006-2011, 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_PKCS11_H -#define POLARSSL_PKCS11_H - -#include "polarssl/config.h" - -#if defined(POLARSSL_PKCS11_C) - -#include "polarssl/x509.h" - -#include - -#if defined(_MSC_VER) && !defined(inline) -#define inline _inline -#else -#if defined(__ARMCC_VERSION) && !defined(inline) -#define inline __inline -#endif /* __ARMCC_VERSION */ -#endif /*_MSC_VER */ - -/** - * Context for PKCS #11 private keys. - */ -typedef struct { - pkcs11h_certificate_t pkcs11h_cert; - int len; -} pkcs11_context; - -/** - * Fill in a PolarSSL certificate, based on the given PKCS11 helper certificate. - * - * \param cert X.509 certificate to fill - * \param pkcs11h_cert PKCS #11 helper certificate - * - * \return 0 on success. - */ -int pkcs11_x509_cert_init( x509_cert *cert, pkcs11h_certificate_t pkcs11h_cert ); - -/** - * Initialise a pkcs11_context, storing the given certificate. Note that the - * pkcs11_context will take over control of the certificate, freeing it when - * done. - * - * \param priv_key Private key structure to fill. - * \param pkcs11_cert PKCS #11 helper certificate - * - * \return 0 on success - */ -int pkcs11_priv_key_init( pkcs11_context *priv_key, - pkcs11h_certificate_t pkcs11_cert ); - -/** - * Free the contents of the given private key context. Note that the structure - * itself is not freed. - * - * \param priv_key Private key structure to cleanup - */ -void pkcs11_priv_key_free( pkcs11_context *priv_key ); - -/** - * \brief Do an RSA private key decrypt, then remove the message padding - * - * \param ctx PKCS #11 context - * \param mode must be RSA_PRIVATE, for compatibility with rsa.c's signature - * \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 pkcs11_decrypt( pkcs11_context *ctx, - int mode, size_t *olen, - const unsigned char *input, - unsigned char *output, - size_t output_max_len ); - -/** - * \brief Do a private RSA to sign a message digest - * - * \param ctx PKCS #11 context - * \param mode must be RSA_PRIVATE, for compatibility with rsa.c's signature - * \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 pkcs11_sign( pkcs11_context *ctx, - int mode, - int hash_id, - unsigned int hashlen, - const unsigned char *hash, - unsigned char *sig ); - -/** - * SSL/TLS wrappers for PKCS#11 functions - */ -static inline int ssl_pkcs11_decrypt( void *ctx, int mode, size_t *olen, - const unsigned char *input, unsigned char *output, - size_t output_max_len ) -{ - return pkcs11_decrypt( (pkcs11_context *) ctx, mode, olen, input, output, - output_max_len ); -} - -static inline int ssl_pkcs11_sign( void *ctx, - int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, - int mode, int hash_id, unsigned int hashlen, - const unsigned char *hash, unsigned char *sig ) -{ - ((void) f_rng); - ((void) p_rng); - return pkcs11_sign( (pkcs11_context *) ctx, mode, hash_id, - hashlen, hash, sig ); -} - -static inline size_t ssl_pkcs11_key_len( void *ctx ) -{ - return ( (pkcs11_context *) ctx )->len; -} - -#endif /* POLARSSL_PKCS11_C */ - -#endif /* POLARSSL_PKCS11_H */ diff --git a/makerom/polarssl/pkcs12.h b/makerom/polarssl/pkcs12.h deleted file mode 100644 index ef559bde..00000000 --- a/makerom/polarssl/pkcs12.h +++ /dev/null @@ -1,131 +0,0 @@ -/** - * \file pkcs12.h - * - * \brief PKCS#12 Personal Information Exchange Syntax - * - * Copyright (C) 2006-2013, 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_PKCS12_H -#define POLARSSL_PKCS12_H - -#include - -#include "polarssl/md.h" -#include "polarssl/cipher.h" -#include "polarssl/asn1.h" - -#define POLARSSL_ERR_PKCS12_BAD_INPUT_DATA -0x1F80 /**< Bad input parameters to function. */ -#define POLARSSL_ERR_PKCS12_FEATURE_UNAVAILABLE -0x1F00 /**< Feature not available, e.g. unsupported encryption scheme. */ -#define POLARSSL_ERR_PKCS12_PBE_INVALID_FORMAT -0x1E80 /**< PBE ASN.1 data not as expected. */ -#define POLARSSL_ERR_PKCS12_PASSWORD_MISMATCH -0x1E00 /**< Given private key password does not allow for correct decryption. */ - -#define PKCS12_DERIVE_KEY 1 /*< encryption/decryption key */ -#define PKCS12_DERIVE_IV 2 /*< initialization vector */ -#define PKCS12_DERIVE_MAC_KEY 3 /*< integrity / MAC key */ - -#define PKCS12_PBE_DECRYPT 0 -#define PKCS12_PBE_ENCRYPT 1 - -/* - * PKCS#12 PBE types - */ -#define OID_PKCS12 "\x2a\x86\x48\x86\xf7\x0d\x01\x0c" -#define OID_PKCS12_PBE_SHA1_RC4_128 OID_PKCS12 "\x01\x01" -#define OID_PKCS12_PBE_SHA1_DES3_EDE_CBC OID_PKCS12 "\x01\x03" -#define OID_PKCS12_PBE_SHA1_DES2_EDE_CBC OID_PKCS12 "\x01\x04" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief PKCS12 Password Based function (encryption / decryption) - * for pbeWithSHAAnd128BitRC4 - * - * \param pbe_params an ASN1 buffer containing the pkcs-12PbeParams structure - * \param mode either PKCS12_PBE_ENCRYPT or PKCS12_PBE_DECRYPT - * \param pwd the password used (may be NULL if no password is used) - * \param pwdlen length of the password (may be 0) - * \param input the input data - * \param len data length - * \param output the output buffer - * - * \return 0 if successful, or a PolarSSL error code - */ -int pkcs12_pbe_sha1_rc4_128( asn1_buf *pbe_params, int mode, - const unsigned char *pwd, size_t pwdlen, - const unsigned char *input, size_t len, - unsigned char *output ); - -/** - * \brief PKCS12 Password Based function (encryption / decryption) - * for cipher-based and md-based PBE's - * - * \param pbe_params an ASN1 buffer containing the pkcs-12PbeParams structure - * \param mode either PKCS12_PBE_ENCRYPT or PKCS12_PBE_DECRYPT - * \param cipher_type the cipher used - * \param md_type the md used - * \param pwd the password used (may be NULL if no password is used) - * \param pwdlen length of the password (may be 0) - * \param input the input data - * \param len data length - * \param output the output buffer - * - * \return 0 if successful, or a PolarSSL error code - */ -int pkcs12_pbe( asn1_buf *pbe_params, int mode, - cipher_type_t cipher_type, md_type_t md_type, - const unsigned char *pwd, size_t pwdlen, - const unsigned char *input, size_t len, - unsigned char *output ); - -/** - * \brief The PKCS#12 derivation function uses a password and a salt - * to produce pseudo-random bits for a particular "purpose". - * - * Depending on the given id, this function can produce an - * encryption/decryption key, an nitialization vector or an - * integrity key. - * - * \param data buffer to store the derived data in - * \param datalen length to fill - * \param pwd password to use (may be NULL if no password is used) - * \param pwdlen length of the password (may be 0) - * \param salt salt buffer to use - * \param saltlen length of the salt - * \param md md type to use during the derivation - * \param id id that describes the purpose (can be PKCS12_DERIVE_KEY, - * PKCS12_DERIVE_IV or PKCS12_DERIVE_MAC_KEY) - * \param iterations number of iterations - * - * \return 0 if successful, or a MD, BIGNUM type error. - */ -int pkcs12_derivation( unsigned char *data, size_t datalen, - const unsigned char *pwd, size_t pwdlen, - const unsigned char *salt, size_t saltlen, - md_type_t md, int id, int iterations ); - -#ifdef __cplusplus -} -#endif - -#endif /* pkcs12.h */ diff --git a/makerom/polarssl/pkcs5.h b/makerom/polarssl/pkcs5.h deleted file mode 100644 index 18f1a4be..00000000 --- a/makerom/polarssl/pkcs5.h +++ /dev/null @@ -1,122 +0,0 @@ -/** - * \file pkcs#5.h - * - * \brief PKCS#5 functions - * - * \author Mathias Olsson - * - * Copyright (C) 2006-2013, 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_PKCS5_H -#define POLARSSL_PKCS5_H - -#include - -#include "polarssl/asn1.h" -#include "polarssl/md.h" - -#ifdef _MSC_VER -#include -typedef UINT32 uint32_t; -#else -#include -#endif - -#define POLARSSL_ERR_PKCS5_BAD_INPUT_DATA -0x3f80 /**< Bad input parameters to function. */ -#define POLARSSL_ERR_PKCS5_INVALID_FORMAT -0x3f00 /**< Unexpected ASN.1 data. */ -#define POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE -0x3e80 /**< Requested encryption or digest alg not available. */ -#define POLARSSL_ERR_PKCS5_PASSWORD_MISMATCH -0x3e00 /**< Given private key password does not allow for correct decryption. */ - -#define PKCS5_DECRYPT 0 -#define PKCS5_ENCRYPT 1 - -/* - * PKCS#5 OIDs - */ -#define OID_PKCS5 "\x2a\x86\x48\x86\xf7\x0d\x01\x05" -#define OID_PKCS5_PBES2 OID_PKCS5 "\x0d" -#define OID_PKCS5_PBKDF2 OID_PKCS5 "\x0c" - -/* - * Encryption Algorithm OIDs - */ -#define OID_DES_CBC "\x2b\x0e\x03\x02\x07" -#define OID_DES_EDE3_CBC "\x2a\x86\x48\x86\xf7\x0d\x03\x07" - -/* - * Digest Algorithm OIDs - */ -#define OID_HMAC_SHA1 "\x2a\x86\x48\x86\xf7\x0d\x02\x07" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief PKCS#5 PBES2 function - * - * \param pbe_params the ASN.1 algorithm parameters - * \param mode either PKCS5_DECRYPT or PKCS5_ENCRYPT - * \param pwd password to use when generating key - * \param plen length of password - * \param data data to process - * \param datalen length of data - * \param output output buffer - * - * \returns 0 on success, or a PolarSSL error code if verification fails. - */ -int pkcs5_pbes2( asn1_buf *pbe_params, int mode, - const unsigned char *pwd, size_t pwdlen, - const unsigned char *data, size_t datalen, - unsigned char *output ); - -/** - * \brief PKCS#5 PBKDF2 using HMAC - * - * \param ctx Generic HMAC context - * \param password Password to use when generating key - * \param plen Length of password - * \param salt Salt to use when generating key - * \param slen Length of salt - * \param iteration_count Iteration count - * \param key_length Length of generated key - * \param output Generated key. Must be at least as big as key_length - * - * \returns 0 on success, or a PolarSSL error code if verification fails. - */ -int pkcs5_pbkdf2_hmac( md_context_t *ctx, const unsigned char *password, - size_t plen, const unsigned char *salt, size_t slen, - unsigned int iteration_count, - uint32_t key_length, unsigned char *output ); - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int pkcs5_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* pkcs5.h */ diff --git a/makerom/polarssl/rsa.c b/makerom/polarssl/rsa.c index e53d9a2e..5b2d6e02 100644 --- a/makerom/polarssl/rsa.c +++ b/makerom/polarssl/rsa.c @@ -1012,7 +1012,7 @@ int rsa_rsassa_pss_verify( rsa_context *ctx, int hash_id, unsigned int hashlen, const unsigned char *hash, - unsigned char *sig ) + const unsigned char *sig ) { int ret; size_t siglen; @@ -1143,7 +1143,7 @@ int rsa_rsassa_pkcs1_v15_verify( rsa_context *ctx, int hash_id, unsigned int hashlen, const unsigned char *hash, - unsigned char *sig ) + const unsigned char *sig ) { int ret; size_t len, siglen; @@ -1251,7 +1251,7 @@ int rsa_pkcs1_verify( rsa_context *ctx, int hash_id, unsigned int hashlen, const unsigned char *hash, - unsigned char *sig ) + const unsigned char *sig ) { switch( ctx->padding ) { diff --git a/makerom/polarssl/rsa.h b/makerom/polarssl/rsa.h index 2424cc1f..9b9060fa 100644 --- a/makerom/polarssl/rsa.h +++ b/makerom/polarssl/rsa.h @@ -521,7 +521,7 @@ int rsa_pkcs1_verify( rsa_context *ctx, int hash_id, unsigned int hashlen, const unsigned char *hash, - unsigned char *sig ); + const unsigned char *sig ); /** * \brief Perform a PKCS#1 v1.5 verification (RSASSA-PKCS1-v1_5-VERIFY) @@ -544,7 +544,7 @@ int rsa_rsassa_pkcs1_v15_verify( rsa_context *ctx, int hash_id, unsigned int hashlen, const unsigned char *hash, - unsigned char *sig ); + const unsigned char *sig ); /** * \brief Perform a PKCS#1 v2.1 PSS verification (RSASSA-PSS-VERIFY) @@ -574,7 +574,7 @@ int rsa_rsassa_pss_verify( rsa_context *ctx, int hash_id, unsigned int hashlen, const unsigned char *hash, - unsigned char *sig ); + const unsigned char *sig ); /** * \brief Free the components of an RSA key diff --git a/makerom/polarssl/sha2.h b/makerom/polarssl/sha2.h index 4f124721..cbc0724c 100644 --- a/makerom/polarssl/sha2.h +++ b/makerom/polarssl/sha2.h @@ -40,7 +40,6 @@ typedef UINT32 uint32_t; #define POLARSSL_ERR_SHA2_FILE_IO_ERROR -0x0078 /**< Read/write error in file. */ -#if !defined(POLARSSL_SHA2_ALT) // Regular implementation // @@ -95,10 +94,6 @@ void sha2_process( sha2_context *ctx, const unsigned char data[64] ); } #endif -#else /* POLARSSL_SHA2_ALT */ -#include "polarssl/sha2_alt.h" -#endif /* POLARSSL_SHA2_ALT */ - #ifdef __cplusplus extern "C" { #endif diff --git a/makerom/polarssl/sha4.c b/makerom/polarssl/sha4.c deleted file mode 100644 index 466420ab..00000000 --- a/makerom/polarssl/sha4.c +++ /dev/null @@ -1,760 +0,0 @@ -/* - * FIPS-180-2 compliant SHA-384/512 implementation - * - * Copyright (C) 2006-2013, 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-512 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_SHA4_C) - -#include "polarssl/sha4.h" - -#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) -#include -#endif - -#if !defined(POLARSSL_SHA4_ALT) - -/* - * 64-bit integer manipulation macros (big endian) - */ -#ifndef GET_UINT64_BE -#define GET_UINT64_BE(n,b,i) \ -{ \ - (n) = ( (uint64_t) (b)[(i) ] << 56 ) \ - | ( (uint64_t) (b)[(i) + 1] << 48 ) \ - | ( (uint64_t) (b)[(i) + 2] << 40 ) \ - | ( (uint64_t) (b)[(i) + 3] << 32 ) \ - | ( (uint64_t) (b)[(i) + 4] << 24 ) \ - | ( (uint64_t) (b)[(i) + 5] << 16 ) \ - | ( (uint64_t) (b)[(i) + 6] << 8 ) \ - | ( (uint64_t) (b)[(i) + 7] ); \ -} -#endif - -#ifndef PUT_UINT64_BE -#define PUT_UINT64_BE(n,b,i) \ -{ \ - (b)[(i) ] = (unsigned char) ( (n) >> 56 ); \ - (b)[(i) + 1] = (unsigned char) ( (n) >> 48 ); \ - (b)[(i) + 2] = (unsigned char) ( (n) >> 40 ); \ - (b)[(i) + 3] = (unsigned char) ( (n) >> 32 ); \ - (b)[(i) + 4] = (unsigned char) ( (n) >> 24 ); \ - (b)[(i) + 5] = (unsigned char) ( (n) >> 16 ); \ - (b)[(i) + 6] = (unsigned char) ( (n) >> 8 ); \ - (b)[(i) + 7] = (unsigned char) ( (n) ); \ -} -#endif - -/* - * Round constants - */ -static const uint64_t K[80] = -{ - UL64(0x428A2F98D728AE22), UL64(0x7137449123EF65CD), - UL64(0xB5C0FBCFEC4D3B2F), UL64(0xE9B5DBA58189DBBC), - UL64(0x3956C25BF348B538), UL64(0x59F111F1B605D019), - UL64(0x923F82A4AF194F9B), UL64(0xAB1C5ED5DA6D8118), - UL64(0xD807AA98A3030242), UL64(0x12835B0145706FBE), - UL64(0x243185BE4EE4B28C), UL64(0x550C7DC3D5FFB4E2), - UL64(0x72BE5D74F27B896F), UL64(0x80DEB1FE3B1696B1), - UL64(0x9BDC06A725C71235), UL64(0xC19BF174CF692694), - UL64(0xE49B69C19EF14AD2), UL64(0xEFBE4786384F25E3), - UL64(0x0FC19DC68B8CD5B5), UL64(0x240CA1CC77AC9C65), - UL64(0x2DE92C6F592B0275), UL64(0x4A7484AA6EA6E483), - UL64(0x5CB0A9DCBD41FBD4), UL64(0x76F988DA831153B5), - UL64(0x983E5152EE66DFAB), UL64(0xA831C66D2DB43210), - UL64(0xB00327C898FB213F), UL64(0xBF597FC7BEEF0EE4), - UL64(0xC6E00BF33DA88FC2), UL64(0xD5A79147930AA725), - UL64(0x06CA6351E003826F), UL64(0x142929670A0E6E70), - UL64(0x27B70A8546D22FFC), UL64(0x2E1B21385C26C926), - UL64(0x4D2C6DFC5AC42AED), UL64(0x53380D139D95B3DF), - UL64(0x650A73548BAF63DE), UL64(0x766A0ABB3C77B2A8), - UL64(0x81C2C92E47EDAEE6), UL64(0x92722C851482353B), - UL64(0xA2BFE8A14CF10364), UL64(0xA81A664BBC423001), - UL64(0xC24B8B70D0F89791), UL64(0xC76C51A30654BE30), - UL64(0xD192E819D6EF5218), UL64(0xD69906245565A910), - UL64(0xF40E35855771202A), UL64(0x106AA07032BBD1B8), - UL64(0x19A4C116B8D2D0C8), UL64(0x1E376C085141AB53), - UL64(0x2748774CDF8EEB99), UL64(0x34B0BCB5E19B48A8), - UL64(0x391C0CB3C5C95A63), UL64(0x4ED8AA4AE3418ACB), - UL64(0x5B9CCA4F7763E373), UL64(0x682E6FF3D6B2B8A3), - UL64(0x748F82EE5DEFB2FC), UL64(0x78A5636F43172F60), - UL64(0x84C87814A1F0AB72), UL64(0x8CC702081A6439EC), - UL64(0x90BEFFFA23631E28), UL64(0xA4506CEBDE82BDE9), - UL64(0xBEF9A3F7B2C67915), UL64(0xC67178F2E372532B), - UL64(0xCA273ECEEA26619C), UL64(0xD186B8C721C0C207), - UL64(0xEADA7DD6CDE0EB1E), UL64(0xF57D4F7FEE6ED178), - UL64(0x06F067AA72176FBA), UL64(0x0A637DC5A2C898A6), - UL64(0x113F9804BEF90DAE), UL64(0x1B710B35131C471B), - UL64(0x28DB77F523047D84), UL64(0x32CAAB7B40C72493), - UL64(0x3C9EBE0A15C9BEBC), UL64(0x431D67C49C100D4C), - UL64(0x4CC5D4BECB3E42B6), UL64(0x597F299CFC657E2A), - UL64(0x5FCB6FAB3AD6FAEC), UL64(0x6C44198C4A475817) -}; - -/* - * SHA-512 context setup - */ -void sha4_starts( sha4_context *ctx, int is384 ) -{ - ctx->total[0] = 0; - ctx->total[1] = 0; - - if( is384 == 0 ) - { - /* SHA-512 */ - ctx->state[0] = UL64(0x6A09E667F3BCC908); - ctx->state[1] = UL64(0xBB67AE8584CAA73B); - ctx->state[2] = UL64(0x3C6EF372FE94F82B); - ctx->state[3] = UL64(0xA54FF53A5F1D36F1); - ctx->state[4] = UL64(0x510E527FADE682D1); - ctx->state[5] = UL64(0x9B05688C2B3E6C1F); - ctx->state[6] = UL64(0x1F83D9ABFB41BD6B); - ctx->state[7] = UL64(0x5BE0CD19137E2179); - } - else - { - /* SHA-384 */ - ctx->state[0] = UL64(0xCBBB9D5DC1059ED8); - ctx->state[1] = UL64(0x629A292A367CD507); - ctx->state[2] = UL64(0x9159015A3070DD17); - ctx->state[3] = UL64(0x152FECD8F70E5939); - ctx->state[4] = UL64(0x67332667FFC00B31); - ctx->state[5] = UL64(0x8EB44A8768581511); - ctx->state[6] = UL64(0xDB0C2E0D64F98FA7); - ctx->state[7] = UL64(0x47B5481DBEFA4FA4); - } - - ctx->is384 = is384; -} - -static void sha4_process( sha4_context *ctx, const unsigned char data[128] ) -{ - int i; - uint64_t temp1, temp2, W[80]; - uint64_t A, B, C, D, E, F, G, H; - -#define SHR(x,n) (x >> n) -#define ROTR(x,n) (SHR(x,n) | (x << (64 - n))) - -#define S0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHR(x, 7)) -#define S1(x) (ROTR(x,19) ^ ROTR(x,61) ^ SHR(x, 6)) - -#define S2(x) (ROTR(x,28) ^ ROTR(x,34) ^ ROTR(x,39)) -#define S3(x) (ROTR(x,14) ^ ROTR(x,18) ^ ROTR(x,41)) - -#define F0(x,y,z) ((x & y) | (z & (x | y))) -#define F1(x,y,z) (z ^ (x & (y ^ z))) - -#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; \ -} - - for( i = 0; i < 16; i++ ) - { - GET_UINT64_BE( W[i], data, i << 3 ); - } - - for( ; i < 80; i++ ) - { - W[i] = S1(W[i - 2]) + W[i - 7] + - S0(W[i - 15]) + W[i - 16]; - } - - 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]; - i = 0; - - do - { - P( A, B, C, D, E, F, G, H, W[i], K[i] ); i++; - P( H, A, B, C, D, E, F, G, W[i], K[i] ); i++; - P( G, H, A, B, C, D, E, F, W[i], K[i] ); i++; - P( F, G, H, A, B, C, D, E, W[i], K[i] ); i++; - P( E, F, G, H, A, B, C, D, W[i], K[i] ); i++; - P( D, E, F, G, H, A, B, C, W[i], K[i] ); i++; - P( C, D, E, F, G, H, A, B, W[i], K[i] ); i++; - P( B, C, D, E, F, G, H, A, W[i], K[i] ); i++; - } - while( i < 80 ); - - 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-512 process buffer - */ -void sha4_update( sha4_context *ctx, const unsigned char *input, size_t ilen ) -{ - size_t fill; - unsigned int left; - - if( ilen <= 0 ) - return; - - left = (unsigned int) (ctx->total[0] & 0x7F); - fill = 128 - left; - - ctx->total[0] += (uint64_t) ilen; - - if( ctx->total[0] < (uint64_t) ilen ) - ctx->total[1]++; - - if( left && ilen >= fill ) - { - memcpy( (void *) (ctx->buffer + left), input, fill ); - sha4_process( ctx, ctx->buffer ); - input += fill; - ilen -= fill; - left = 0; - } - - while( ilen >= 128 ) - { - sha4_process( ctx, input ); - input += 128; - ilen -= 128; - } - - if( ilen > 0 ) - memcpy( (void *) (ctx->buffer + left), input, ilen ); -} - -static const unsigned char sha4_padding[128] = -{ - 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, - 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, 0 -}; - -/* - * SHA-512 final digest - */ -void sha4_finish( sha4_context *ctx, unsigned char output[64] ) -{ - size_t last, padn; - uint64_t high, low; - unsigned char msglen[16]; - - high = ( ctx->total[0] >> 61 ) - | ( ctx->total[1] << 3 ); - low = ( ctx->total[0] << 3 ); - - PUT_UINT64_BE( high, msglen, 0 ); - PUT_UINT64_BE( low, msglen, 8 ); - - last = (size_t)( ctx->total[0] & 0x7F ); - padn = ( last < 112 ) ? ( 112 - last ) : ( 240 - last ); - - sha4_update( ctx, sha4_padding, padn ); - sha4_update( ctx, msglen, 16 ); - - PUT_UINT64_BE( ctx->state[0], output, 0 ); - PUT_UINT64_BE( ctx->state[1], output, 8 ); - PUT_UINT64_BE( ctx->state[2], output, 16 ); - PUT_UINT64_BE( ctx->state[3], output, 24 ); - PUT_UINT64_BE( ctx->state[4], output, 32 ); - PUT_UINT64_BE( ctx->state[5], output, 40 ); - - if( ctx->is384 == 0 ) - { - PUT_UINT64_BE( ctx->state[6], output, 48 ); - PUT_UINT64_BE( ctx->state[7], output, 56 ); - } -} - -#endif /* !POLARSSL_SHA4_ALT */ - -/* - * output = SHA-512( input buffer ) - */ -void sha4( const unsigned char *input, size_t ilen, - unsigned char output[64], int is384 ) -{ - sha4_context ctx; - - sha4_starts( &ctx, is384 ); - sha4_update( &ctx, input, ilen ); - sha4_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( sha4_context ) ); -} - -#if defined(POLARSSL_FS_IO) -/* - * output = SHA-512( file contents ) - */ -int sha4_file( const char *path, unsigned char output[64], int is384 ) -{ - FILE *f; - size_t n; - sha4_context ctx; - unsigned char buf[1024]; - - if( ( f = fopen( path, "rb" ) ) == NULL ) - return( POLARSSL_ERR_SHA4_FILE_IO_ERROR ); - - sha4_starts( &ctx, is384 ); - - while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) - sha4_update( &ctx, buf, n ); - - sha4_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( sha4_context ) ); - - if( ferror( f ) != 0 ) - { - fclose( f ); - return( POLARSSL_ERR_SHA4_FILE_IO_ERROR ); - } - - fclose( f ); - return( 0 ); -} -#endif /* POLARSSL_FS_IO */ - -/* - * SHA-512 HMAC context setup - */ -void sha4_hmac_starts( sha4_context *ctx, const unsigned char *key, size_t keylen, - int is384 ) -{ - size_t i; - unsigned char sum[64]; - - if( keylen > 128 ) - { - sha4( key, keylen, sum, is384 ); - keylen = ( is384 ) ? 48 : 64; - key = sum; - } - - memset( ctx->ipad, 0x36, 128 ); - memset( ctx->opad, 0x5C, 128 ); - - 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] ); - } - - sha4_starts( ctx, is384 ); - sha4_update( ctx, ctx->ipad, 128 ); - - memset( sum, 0, sizeof( sum ) ); -} - -/* - * SHA-512 HMAC process buffer - */ -void sha4_hmac_update( sha4_context *ctx, - const unsigned char *input, size_t ilen ) -{ - sha4_update( ctx, input, ilen ); -} - -/* - * SHA-512 HMAC final digest - */ -void sha4_hmac_finish( sha4_context *ctx, unsigned char output[64] ) -{ - int is384, hlen; - unsigned char tmpbuf[64]; - - is384 = ctx->is384; - hlen = ( is384 == 0 ) ? 64 : 48; - - sha4_finish( ctx, tmpbuf ); - sha4_starts( ctx, is384 ); - sha4_update( ctx, ctx->opad, 128 ); - sha4_update( ctx, tmpbuf, hlen ); - sha4_finish( ctx, output ); - - memset( tmpbuf, 0, sizeof( tmpbuf ) ); -} - -/* - * SHA-512 HMAC context reset - */ -void sha4_hmac_reset( sha4_context *ctx ) -{ - sha4_starts( ctx, ctx->is384 ); - sha4_update( ctx, ctx->ipad, 128 ); -} - -/* - * output = HMAC-SHA-512( hmac key, input buffer ) - */ -void sha4_hmac( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char output[64], int is384 ) -{ - sha4_context ctx; - - sha4_hmac_starts( &ctx, key, keylen, is384 ); - sha4_hmac_update( &ctx, input, ilen ); - sha4_hmac_finish( &ctx, output ); - - memset( &ctx, 0, sizeof( sha4_context ) ); -} - -#if defined(POLARSSL_SELF_TEST) - -/* - * FIPS-180-2 test vectors - */ -static unsigned char sha4_test_buf[3][113] = -{ - { "abc" }, - { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" - "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" }, - { "" } -}; - -static const int sha4_test_buflen[3] = -{ - 3, 112, 1000 -}; - -static const unsigned char sha4_test_sum[6][64] = -{ - /* - * SHA-384 test vectors - */ - { 0xCB, 0x00, 0x75, 0x3F, 0x45, 0xA3, 0x5E, 0x8B, - 0xB5, 0xA0, 0x3D, 0x69, 0x9A, 0xC6, 0x50, 0x07, - 0x27, 0x2C, 0x32, 0xAB, 0x0E, 0xDE, 0xD1, 0x63, - 0x1A, 0x8B, 0x60, 0x5A, 0x43, 0xFF, 0x5B, 0xED, - 0x80, 0x86, 0x07, 0x2B, 0xA1, 0xE7, 0xCC, 0x23, - 0x58, 0xBA, 0xEC, 0xA1, 0x34, 0xC8, 0x25, 0xA7 }, - { 0x09, 0x33, 0x0C, 0x33, 0xF7, 0x11, 0x47, 0xE8, - 0x3D, 0x19, 0x2F, 0xC7, 0x82, 0xCD, 0x1B, 0x47, - 0x53, 0x11, 0x1B, 0x17, 0x3B, 0x3B, 0x05, 0xD2, - 0x2F, 0xA0, 0x80, 0x86, 0xE3, 0xB0, 0xF7, 0x12, - 0xFC, 0xC7, 0xC7, 0x1A, 0x55, 0x7E, 0x2D, 0xB9, - 0x66, 0xC3, 0xE9, 0xFA, 0x91, 0x74, 0x60, 0x39 }, - { 0x9D, 0x0E, 0x18, 0x09, 0x71, 0x64, 0x74, 0xCB, - 0x08, 0x6E, 0x83, 0x4E, 0x31, 0x0A, 0x4A, 0x1C, - 0xED, 0x14, 0x9E, 0x9C, 0x00, 0xF2, 0x48, 0x52, - 0x79, 0x72, 0xCE, 0xC5, 0x70, 0x4C, 0x2A, 0x5B, - 0x07, 0xB8, 0xB3, 0xDC, 0x38, 0xEC, 0xC4, 0xEB, - 0xAE, 0x97, 0xDD, 0xD8, 0x7F, 0x3D, 0x89, 0x85 }, - - /* - * SHA-512 test vectors - */ - { 0xDD, 0xAF, 0x35, 0xA1, 0x93, 0x61, 0x7A, 0xBA, - 0xCC, 0x41, 0x73, 0x49, 0xAE, 0x20, 0x41, 0x31, - 0x12, 0xE6, 0xFA, 0x4E, 0x89, 0xA9, 0x7E, 0xA2, - 0x0A, 0x9E, 0xEE, 0xE6, 0x4B, 0x55, 0xD3, 0x9A, - 0x21, 0x92, 0x99, 0x2A, 0x27, 0x4F, 0xC1, 0xA8, - 0x36, 0xBA, 0x3C, 0x23, 0xA3, 0xFE, 0xEB, 0xBD, - 0x45, 0x4D, 0x44, 0x23, 0x64, 0x3C, 0xE8, 0x0E, - 0x2A, 0x9A, 0xC9, 0x4F, 0xA5, 0x4C, 0xA4, 0x9F }, - { 0x8E, 0x95, 0x9B, 0x75, 0xDA, 0xE3, 0x13, 0xDA, - 0x8C, 0xF4, 0xF7, 0x28, 0x14, 0xFC, 0x14, 0x3F, - 0x8F, 0x77, 0x79, 0xC6, 0xEB, 0x9F, 0x7F, 0xA1, - 0x72, 0x99, 0xAE, 0xAD, 0xB6, 0x88, 0x90, 0x18, - 0x50, 0x1D, 0x28, 0x9E, 0x49, 0x00, 0xF7, 0xE4, - 0x33, 0x1B, 0x99, 0xDE, 0xC4, 0xB5, 0x43, 0x3A, - 0xC7, 0xD3, 0x29, 0xEE, 0xB6, 0xDD, 0x26, 0x54, - 0x5E, 0x96, 0xE5, 0x5B, 0x87, 0x4B, 0xE9, 0x09 }, - { 0xE7, 0x18, 0x48, 0x3D, 0x0C, 0xE7, 0x69, 0x64, - 0x4E, 0x2E, 0x42, 0xC7, 0xBC, 0x15, 0xB4, 0x63, - 0x8E, 0x1F, 0x98, 0xB1, 0x3B, 0x20, 0x44, 0x28, - 0x56, 0x32, 0xA8, 0x03, 0xAF, 0xA9, 0x73, 0xEB, - 0xDE, 0x0F, 0xF2, 0x44, 0x87, 0x7E, 0xA6, 0x0A, - 0x4C, 0xB0, 0x43, 0x2C, 0xE5, 0x77, 0xC3, 0x1B, - 0xEB, 0x00, 0x9C, 0x5C, 0x2C, 0x49, 0xAA, 0x2E, - 0x4E, 0xAD, 0xB2, 0x17, 0xAD, 0x8C, 0xC0, 0x9B } -}; - -/* - * RFC 4231 test vectors - */ -static unsigned char sha4_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 sha4_hmac_test_keylen[7] = -{ - 20, 4, 20, 25, 20, 131, 131 -}; - -static unsigned char sha4_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 sha4_hmac_test_buflen[7] = -{ - 8, 28, 50, 50, 20, 54, 152 -}; - -static const unsigned char sha4_hmac_test_sum[14][64] = -{ - /* - * HMAC-SHA-384 test vectors - */ - { 0xAF, 0xD0, 0x39, 0x44, 0xD8, 0x48, 0x95, 0x62, - 0x6B, 0x08, 0x25, 0xF4, 0xAB, 0x46, 0x90, 0x7F, - 0x15, 0xF9, 0xDA, 0xDB, 0xE4, 0x10, 0x1E, 0xC6, - 0x82, 0xAA, 0x03, 0x4C, 0x7C, 0xEB, 0xC5, 0x9C, - 0xFA, 0xEA, 0x9E, 0xA9, 0x07, 0x6E, 0xDE, 0x7F, - 0x4A, 0xF1, 0x52, 0xE8, 0xB2, 0xFA, 0x9C, 0xB6 }, - { 0xAF, 0x45, 0xD2, 0xE3, 0x76, 0x48, 0x40, 0x31, - 0x61, 0x7F, 0x78, 0xD2, 0xB5, 0x8A, 0x6B, 0x1B, - 0x9C, 0x7E, 0xF4, 0x64, 0xF5, 0xA0, 0x1B, 0x47, - 0xE4, 0x2E, 0xC3, 0x73, 0x63, 0x22, 0x44, 0x5E, - 0x8E, 0x22, 0x40, 0xCA, 0x5E, 0x69, 0xE2, 0xC7, - 0x8B, 0x32, 0x39, 0xEC, 0xFA, 0xB2, 0x16, 0x49 }, - { 0x88, 0x06, 0x26, 0x08, 0xD3, 0xE6, 0xAD, 0x8A, - 0x0A, 0xA2, 0xAC, 0xE0, 0x14, 0xC8, 0xA8, 0x6F, - 0x0A, 0xA6, 0x35, 0xD9, 0x47, 0xAC, 0x9F, 0xEB, - 0xE8, 0x3E, 0xF4, 0xE5, 0x59, 0x66, 0x14, 0x4B, - 0x2A, 0x5A, 0xB3, 0x9D, 0xC1, 0x38, 0x14, 0xB9, - 0x4E, 0x3A, 0xB6, 0xE1, 0x01, 0xA3, 0x4F, 0x27 }, - { 0x3E, 0x8A, 0x69, 0xB7, 0x78, 0x3C, 0x25, 0x85, - 0x19, 0x33, 0xAB, 0x62, 0x90, 0xAF, 0x6C, 0xA7, - 0x7A, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9C, - 0xC5, 0x57, 0x7C, 0x6E, 0x1F, 0x57, 0x3B, 0x4E, - 0x68, 0x01, 0xDD, 0x23, 0xC4, 0xA7, 0xD6, 0x79, - 0xCC, 0xF8, 0xA3, 0x86, 0xC6, 0x74, 0xCF, 0xFB }, - { 0x3A, 0xBF, 0x34, 0xC3, 0x50, 0x3B, 0x2A, 0x23, - 0xA4, 0x6E, 0xFC, 0x61, 0x9B, 0xAE, 0xF8, 0x97 }, - { 0x4E, 0xCE, 0x08, 0x44, 0x85, 0x81, 0x3E, 0x90, - 0x88, 0xD2, 0xC6, 0x3A, 0x04, 0x1B, 0xC5, 0xB4, - 0x4F, 0x9E, 0xF1, 0x01, 0x2A, 0x2B, 0x58, 0x8F, - 0x3C, 0xD1, 0x1F, 0x05, 0x03, 0x3A, 0xC4, 0xC6, - 0x0C, 0x2E, 0xF6, 0xAB, 0x40, 0x30, 0xFE, 0x82, - 0x96, 0x24, 0x8D, 0xF1, 0x63, 0xF4, 0x49, 0x52 }, - { 0x66, 0x17, 0x17, 0x8E, 0x94, 0x1F, 0x02, 0x0D, - 0x35, 0x1E, 0x2F, 0x25, 0x4E, 0x8F, 0xD3, 0x2C, - 0x60, 0x24, 0x20, 0xFE, 0xB0, 0xB8, 0xFB, 0x9A, - 0xDC, 0xCE, 0xBB, 0x82, 0x46, 0x1E, 0x99, 0xC5, - 0xA6, 0x78, 0xCC, 0x31, 0xE7, 0x99, 0x17, 0x6D, - 0x38, 0x60, 0xE6, 0x11, 0x0C, 0x46, 0x52, 0x3E }, - - /* - * HMAC-SHA-512 test vectors - */ - { 0x87, 0xAA, 0x7C, 0xDE, 0xA5, 0xEF, 0x61, 0x9D, - 0x4F, 0xF0, 0xB4, 0x24, 0x1A, 0x1D, 0x6C, 0xB0, - 0x23, 0x79, 0xF4, 0xE2, 0xCE, 0x4E, 0xC2, 0x78, - 0x7A, 0xD0, 0xB3, 0x05, 0x45, 0xE1, 0x7C, 0xDE, - 0xDA, 0xA8, 0x33, 0xB7, 0xD6, 0xB8, 0xA7, 0x02, - 0x03, 0x8B, 0x27, 0x4E, 0xAE, 0xA3, 0xF4, 0xE4, - 0xBE, 0x9D, 0x91, 0x4E, 0xEB, 0x61, 0xF1, 0x70, - 0x2E, 0x69, 0x6C, 0x20, 0x3A, 0x12, 0x68, 0x54 }, - { 0x16, 0x4B, 0x7A, 0x7B, 0xFC, 0xF8, 0x19, 0xE2, - 0xE3, 0x95, 0xFB, 0xE7, 0x3B, 0x56, 0xE0, 0xA3, - 0x87, 0xBD, 0x64, 0x22, 0x2E, 0x83, 0x1F, 0xD6, - 0x10, 0x27, 0x0C, 0xD7, 0xEA, 0x25, 0x05, 0x54, - 0x97, 0x58, 0xBF, 0x75, 0xC0, 0x5A, 0x99, 0x4A, - 0x6D, 0x03, 0x4F, 0x65, 0xF8, 0xF0, 0xE6, 0xFD, - 0xCA, 0xEA, 0xB1, 0xA3, 0x4D, 0x4A, 0x6B, 0x4B, - 0x63, 0x6E, 0x07, 0x0A, 0x38, 0xBC, 0xE7, 0x37 }, - { 0xFA, 0x73, 0xB0, 0x08, 0x9D, 0x56, 0xA2, 0x84, - 0xEF, 0xB0, 0xF0, 0x75, 0x6C, 0x89, 0x0B, 0xE9, - 0xB1, 0xB5, 0xDB, 0xDD, 0x8E, 0xE8, 0x1A, 0x36, - 0x55, 0xF8, 0x3E, 0x33, 0xB2, 0x27, 0x9D, 0x39, - 0xBF, 0x3E, 0x84, 0x82, 0x79, 0xA7, 0x22, 0xC8, - 0x06, 0xB4, 0x85, 0xA4, 0x7E, 0x67, 0xC8, 0x07, - 0xB9, 0x46, 0xA3, 0x37, 0xBE, 0xE8, 0x94, 0x26, - 0x74, 0x27, 0x88, 0x59, 0xE1, 0x32, 0x92, 0xFB }, - { 0xB0, 0xBA, 0x46, 0x56, 0x37, 0x45, 0x8C, 0x69, - 0x90, 0xE5, 0xA8, 0xC5, 0xF6, 0x1D, 0x4A, 0xF7, - 0xE5, 0x76, 0xD9, 0x7F, 0xF9, 0x4B, 0x87, 0x2D, - 0xE7, 0x6F, 0x80, 0x50, 0x36, 0x1E, 0xE3, 0xDB, - 0xA9, 0x1C, 0xA5, 0xC1, 0x1A, 0xA2, 0x5E, 0xB4, - 0xD6, 0x79, 0x27, 0x5C, 0xC5, 0x78, 0x80, 0x63, - 0xA5, 0xF1, 0x97, 0x41, 0x12, 0x0C, 0x4F, 0x2D, - 0xE2, 0xAD, 0xEB, 0xEB, 0x10, 0xA2, 0x98, 0xDD }, - { 0x41, 0x5F, 0xAD, 0x62, 0x71, 0x58, 0x0A, 0x53, - 0x1D, 0x41, 0x79, 0xBC, 0x89, 0x1D, 0x87, 0xA6 }, - { 0x80, 0xB2, 0x42, 0x63, 0xC7, 0xC1, 0xA3, 0xEB, - 0xB7, 0x14, 0x93, 0xC1, 0xDD, 0x7B, 0xE8, 0xB4, - 0x9B, 0x46, 0xD1, 0xF4, 0x1B, 0x4A, 0xEE, 0xC1, - 0x12, 0x1B, 0x01, 0x37, 0x83, 0xF8, 0xF3, 0x52, - 0x6B, 0x56, 0xD0, 0x37, 0xE0, 0x5F, 0x25, 0x98, - 0xBD, 0x0F, 0xD2, 0x21, 0x5D, 0x6A, 0x1E, 0x52, - 0x95, 0xE6, 0x4F, 0x73, 0xF6, 0x3F, 0x0A, 0xEC, - 0x8B, 0x91, 0x5A, 0x98, 0x5D, 0x78, 0x65, 0x98 }, - { 0xE3, 0x7B, 0x6A, 0x77, 0x5D, 0xC8, 0x7D, 0xBA, - 0xA4, 0xDF, 0xA9, 0xF9, 0x6E, 0x5E, 0x3F, 0xFD, - 0xDE, 0xBD, 0x71, 0xF8, 0x86, 0x72, 0x89, 0x86, - 0x5D, 0xF5, 0xA3, 0x2D, 0x20, 0xCD, 0xC9, 0x44, - 0xB6, 0x02, 0x2C, 0xAC, 0x3C, 0x49, 0x82, 0xB1, - 0x0D, 0x5E, 0xEB, 0x55, 0xC3, 0xE4, 0xDE, 0x15, - 0x13, 0x46, 0x76, 0xFB, 0x6D, 0xE0, 0x44, 0x60, - 0x65, 0xC9, 0x74, 0x40, 0xFA, 0x8C, 0x6A, 0x58 } -}; - -/* - * Checkup routine - */ -int sha4_self_test( int verbose ) -{ - int i, j, k, buflen; - unsigned char buf[1024]; - unsigned char sha4sum[64]; - sha4_context ctx; - - for( i = 0; i < 6; i++ ) - { - j = i % 3; - k = i < 3; - - if( verbose != 0 ) - printf( " SHA-%d test #%d: ", 512 - k * 128, j + 1 ); - - sha4_starts( &ctx, k ); - - if( j == 2 ) - { - memset( buf, 'a', buflen = 1000 ); - - for( j = 0; j < 1000; j++ ) - sha4_update( &ctx, buf, buflen ); - } - else - sha4_update( &ctx, sha4_test_buf[j], - sha4_test_buflen[j] ); - - sha4_finish( &ctx, sha4sum ); - - if( memcmp( sha4sum, sha4_test_sum[i], 64 - k * 16 ) != 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: ", 512 - k * 128, j + 1 ); - - if( j == 5 || j == 6 ) - { - memset( buf, '\xAA', buflen = 131 ); - sha4_hmac_starts( &ctx, buf, buflen, k ); - } - else - sha4_hmac_starts( &ctx, sha4_hmac_test_key[j], - sha4_hmac_test_keylen[j], k ); - - sha4_hmac_update( &ctx, sha4_hmac_test_buf[j], - sha4_hmac_test_buflen[j] ); - - sha4_hmac_finish( &ctx, sha4sum ); - - buflen = ( j == 4 ) ? 16 : 64 - k * 16; - - if( memcmp( sha4sum, sha4_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/makerom/polarssl/sha4.h b/makerom/polarssl/sha4.h deleted file mode 100644 index e2fd55a8..00000000 --- a/makerom/polarssl/sha4.h +++ /dev/null @@ -1,186 +0,0 @@ -/** - * \file sha4.h - * - * \brief SHA-384 and SHA-512 cryptographic hash function - * - * Copyright (C) 2006-2013, 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_SHA4_H -#define POLARSSL_SHA4_H - -#include "polarssl/config.h" - -#include - -#if defined(_MSC_VER) || defined(__WATCOMC__) - #define UL64(x) x##ui64 - typedef unsigned __int64 uint64_t; -#else - #include - #define UL64(x) x##ULL -#endif - -#define POLARSSL_ERR_SHA4_FILE_IO_ERROR -0x007A /**< Read/write error in file. */ - -#if !defined(POLARSSL_SHA1_ALT) -// Regular implementation -// - -/** - * \brief SHA-512 context structure - */ -typedef struct -{ - uint64_t total[2]; /*!< number of bytes processed */ - uint64_t state[8]; /*!< intermediate digest state */ - unsigned char buffer[128]; /*!< data block being processed */ - - unsigned char ipad[128]; /*!< HMAC: inner padding */ - unsigned char opad[128]; /*!< HMAC: outer padding */ - int is384; /*!< 0 => SHA-512, else SHA-384 */ -} -sha4_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief SHA-512 context setup - * - * \param ctx context to be initialized - * \param is384 0 = use SHA512, 1 = use SHA384 - */ -void sha4_starts( sha4_context *ctx, int is384 ); - -/** - * \brief SHA-512 process buffer - * - * \param ctx SHA-512 context - * \param input buffer holding the data - * \param ilen length of the input data - */ -void sha4_update( sha4_context *ctx, const unsigned char *input, size_t ilen ); - -/** - * \brief SHA-512 final digest - * - * \param ctx SHA-512 context - * \param output SHA-384/512 checksum result - */ -void sha4_finish( sha4_context *ctx, unsigned char output[64] ); - -#ifdef __cplusplus -} -#endif - -#else /* POLARSSL_SHA4_ALT */ -#include "polarssl/sha4_alt.h" -#endif /* POLARSSL_SHA4_ALT */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Output = SHA-512( input buffer ) - * - * \param input buffer holding the data - * \param ilen length of the input data - * \param output SHA-384/512 checksum result - * \param is384 0 = use SHA512, 1 = use SHA384 - */ -void sha4( const unsigned char *input, size_t ilen, - unsigned char output[64], int is384 ); - -/** - * \brief Output = SHA-512( file contents ) - * - * \param path input file name - * \param output SHA-384/512 checksum result - * \param is384 0 = use SHA512, 1 = use SHA384 - * - * \return 0 if successful, or POLARSSL_ERR_SHA4_FILE_IO_ERROR - */ -int sha4_file( const char *path, unsigned char output[64], int is384 ); - -/** - * \brief SHA-512 HMAC context setup - * - * \param ctx HMAC context to be initialized - * \param is384 0 = use SHA512, 1 = use SHA384 - * \param key HMAC secret key - * \param keylen length of the HMAC key - */ -void sha4_hmac_starts( sha4_context *ctx, const unsigned char *key, size_t keylen, - int is384 ); - -/** - * \brief SHA-512 HMAC process buffer - * - * \param ctx HMAC context - * \param input buffer holding the data - * \param ilen length of the input data - */ -void sha4_hmac_update( sha4_context *ctx, const unsigned char *input, size_t ilen ); - -/** - * \brief SHA-512 HMAC final digest - * - * \param ctx HMAC context - * \param output SHA-384/512 HMAC checksum result - */ -void sha4_hmac_finish( sha4_context *ctx, unsigned char output[64] ); - -/** - * \brief SHA-512 HMAC context reset - * - * \param ctx HMAC context to be reset - */ -void sha4_hmac_reset( sha4_context *ctx ); - -/** - * \brief Output = HMAC-SHA-512( 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-384/512 result - * \param is384 0 = use SHA512, 1 = use SHA384 - */ -void sha4_hmac( const unsigned char *key, size_t keylen, - const unsigned char *input, size_t ilen, - unsigned char output[64], int is384 ); - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int sha4_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* sha4.h */ diff --git a/makerom/polarssl/ssl.h b/makerom/polarssl/ssl.h deleted file mode 100644 index 5b2d9787..00000000 --- a/makerom/polarssl/ssl.h +++ /dev/null @@ -1,1140 +0,0 @@ -/** - * \file ssl.h - * - * \brief SSL/TLS functions. - * - * Copyright (C) 2006-2013, 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_SSL_H -#define POLARSSL_SSL_H - -#include - -#include "polarssl/net.h" -#include "polarssl/rsa.h" -#include "polarssl/md5.h" -#include "polarssl/sha1.h" -#include "polarssl/sha2.h" -#include "polarssl/sha4.h" -#include "polarssl/x509.h" -#include "polarssl/config.h" - -#if defined(POLARSSL_DHM_C) -#include "polarssl/dhm.h" -#endif - -#if defined(POLARSSL_ZLIB_SUPPORT) -#include "polarssl/zlib.h" -#endif - -#if defined(_MSC_VER) && !defined(inline) -#define inline _inline -#else -#if defined(__ARMCC_VERSION) && !defined(inline) -#define inline __inline -#endif /* __ARMCC_VERSION */ -#endif /*_MSC_VER */ - -/* - * SSL Error codes - */ -#define POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE -0x7080 /**< The requested feature is not available. */ -#define POLARSSL_ERR_SSL_BAD_INPUT_DATA -0x7100 /**< Bad input parameters to function. */ -#define POLARSSL_ERR_SSL_INVALID_MAC -0x7180 /**< Verification of the message MAC failed. */ -#define POLARSSL_ERR_SSL_INVALID_RECORD -0x7200 /**< An invalid SSL record was received. */ -#define POLARSSL_ERR_SSL_CONN_EOF -0x7280 /**< The connection indicated an EOF. */ -#define POLARSSL_ERR_SSL_UNKNOWN_CIPHER -0x7300 /**< An unknown cipher was received. */ -#define POLARSSL_ERR_SSL_NO_CIPHER_CHOSEN -0x7380 /**< The server has no ciphersuites in common with the client. */ -#define POLARSSL_ERR_SSL_NO_SESSION_FOUND -0x7400 /**< No session to recover was found. */ -#define POLARSSL_ERR_SSL_NO_CLIENT_CERTIFICATE -0x7480 /**< No client certification received from the client, but required by the authentication mode. */ -#define POLARSSL_ERR_SSL_CERTIFICATE_TOO_LARGE -0x7500 /**< Our own certificate(s) is/are too large to send in an SSL message.*/ -#define POLARSSL_ERR_SSL_CERTIFICATE_REQUIRED -0x7580 /**< The own certificate is not set, but needed by the server. */ -#define POLARSSL_ERR_SSL_PRIVATE_KEY_REQUIRED -0x7600 /**< The own private key is not set, but needed. */ -#define POLARSSL_ERR_SSL_CA_CHAIN_REQUIRED -0x7680 /**< No CA Chain is set, but required to operate. */ -#define POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE -0x7700 /**< An unexpected message was received from our peer. */ -#define POLARSSL_ERR_SSL_FATAL_ALERT_MESSAGE -0x7780 /**< A fatal alert message was received from our peer. */ -#define POLARSSL_ERR_SSL_PEER_VERIFY_FAILED -0x7800 /**< Verification of our peer failed. */ -#define POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY -0x7880 /**< The peer notified us that the connection is going to be closed. */ -#define POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO -0x7900 /**< Processing of the ClientHello handshake message failed. */ -#define POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO -0x7980 /**< Processing of the ServerHello handshake message failed. */ -#define POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE -0x7A00 /**< Processing of the Certificate handshake message failed. */ -#define POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST -0x7A80 /**< Processing of the CertificateRequest handshake message failed. */ -#define POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE -0x7B00 /**< Processing of the ServerKeyExchange handshake message failed. */ -#define POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO_DONE -0x7B80 /**< Processing of the ServerHelloDone handshake message failed. */ -#define POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE -0x7C00 /**< Processing of the ClientKeyExchange handshake message failed. */ -#define POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_DHM_RP -0x7C80 /**< Processing of the ClientKeyExchange handshake message failed in DHM Read Public. */ -#define POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_DHM_CS -0x7D00 /**< Processing of the ClientKeyExchange handshake message failed in DHM Calculate Secret. */ -#define POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY -0x7D80 /**< Processing of the CertificateVerify handshake message failed. */ -#define POLARSSL_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC -0x7E00 /**< Processing of the ChangeCipherSpec handshake message failed. */ -#define POLARSSL_ERR_SSL_BAD_HS_FINISHED -0x7E80 /**< Processing of the Finished handshake message failed. */ -#define POLARSSL_ERR_SSL_MALLOC_FAILED -0x7F00 /**< Memory allocation failed */ -#define POLARSSL_ERR_SSL_HW_ACCEL_FAILED -0x7F80 /**< Hardware acceleration function returned with error */ -#define POLARSSL_ERR_SSL_HW_ACCEL_FALLTHROUGH -0x6F80 /**< Hardware acceleration function skipped / left alone data */ -#define POLARSSL_ERR_SSL_COMPRESSION_FAILED -0x6F00 /**< Processing of the compression / decompression failed */ -#define POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION -0x6E80 /**< Handshake protocol not within min/max boundaries */ - -/* - * Various constants - */ -#define SSL_MAJOR_VERSION_3 3 -#define SSL_MINOR_VERSION_0 0 /*!< SSL v3.0 */ -#define SSL_MINOR_VERSION_1 1 /*!< TLS v1.0 */ -#define SSL_MINOR_VERSION_2 2 /*!< TLS v1.1 */ -#define SSL_MINOR_VERSION_3 3 /*!< TLS v1.2 */ - -#define SSL_IS_CLIENT 0 -#define SSL_IS_SERVER 1 -#define SSL_COMPRESS_NULL 0 -#define SSL_COMPRESS_DEFLATE 1 - -#define SSL_VERIFY_NONE 0 -#define SSL_VERIFY_OPTIONAL 1 -#define SSL_VERIFY_REQUIRED 2 - -#define SSL_INITIAL_HANDSHAKE 0 -#define SSL_RENEGOTIATION 1 - -#define SSL_LEGACY_RENEGOTIATION 0 -#define SSL_SECURE_RENEGOTIATION 1 - -#define SSL_RENEGOTIATION_DISABLED 0 -#define SSL_RENEGOTIATION_ENABLED 1 - -#define SSL_LEGACY_NO_RENEGOTIATION 0 -#define SSL_LEGACY_ALLOW_RENEGOTIATION 1 -#define SSL_LEGACY_BREAK_HANDSHAKE 2 - -/* - * Size of the input / output buffer. - * Note: the RFC defines the default size of SSL / TLS messages. If you - * change the value here, other clients / servers may not be able to - * communicate with you anymore. Only change this value if you control - * both sides of the connection and have it reduced at both sides! - */ -#if !defined(POLARSSL_CONFIG_OPTIONS) -#define SSL_MAX_CONTENT_LEN 16384 /**< Size of the input / output buffer */ -#endif /* !POLARSSL_CONFIG_OPTIONS */ - -/* - * Allow an extra 512 bytes for the record header - * and encryption overhead (counter + MAC + padding) - * and allow for a maximum of 1024 of compression expansion if - * enabled. - */ -#if defined(POLARSSL_ZLIB_SUPPORT) -#define SSL_COMPRESSION_ADD 1024 -#else -#define SSL_COMPRESSION_ADD 0 -#endif - -#define SSL_BUFFER_LEN (SSL_MAX_CONTENT_LEN + SSL_COMPRESSION_ADD + 512) - -/* - * Supported ciphersuites (Official IANA names) - */ -#define TLS_RSA_WITH_NULL_MD5 0x01 /**< Weak! */ -#define TLS_RSA_WITH_NULL_SHA 0x02 /**< Weak! */ -#define TLS_RSA_WITH_NULL_SHA256 0x3B /**< Weak! */ -#define TLS_RSA_WITH_DES_CBC_SHA 0x09 /**< Weak! Not in TLS 1.2 */ -#define TLS_DHE_RSA_WITH_DES_CBC_SHA 0x15 /**< Weak! Not in TLS 1.2 */ - -#define TLS_RSA_WITH_RC4_128_MD5 0x04 -#define TLS_RSA_WITH_RC4_128_SHA 0x05 - -#define TLS_RSA_WITH_3DES_EDE_CBC_SHA 0x0A -#define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0x16 - -#define TLS_RSA_WITH_AES_128_CBC_SHA 0x2F -#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x33 -#define TLS_RSA_WITH_AES_256_CBC_SHA 0x35 -#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x39 -#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x3C /**< TLS 1.2 */ -#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x3D /**< TLS 1.2 */ -#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x67 /**< TLS 1.2 */ -#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x6B /**< TLS 1.2 */ - -#define TLS_RSA_WITH_CAMELLIA_128_CBC_SHA 0x41 -#define TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA 0x45 -#define TLS_RSA_WITH_CAMELLIA_256_CBC_SHA 0x84 -#define TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA 0x88 -#define TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xBA /**< TLS 1.2 */ -#define TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xBE /**< TLS 1.2 */ -#define TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 0xC0 /**< TLS 1.2 */ -#define TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 0xC4 /**< TLS 1.2 */ - -#define TLS_RSA_WITH_AES_128_GCM_SHA256 0x9C -#define TLS_RSA_WITH_AES_256_GCM_SHA384 0x9D -#define TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x9E -#define TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x9F - -#define SSL_EMPTY_RENEGOTIATION_INFO 0xFF /**< renegotiation info ext */ - -/* - * Supported Signature and Hash algorithms (For TLS 1.2) - */ -#define SSL_HASH_NONE 0 -#define SSL_HASH_MD5 1 -#define SSL_HASH_SHA1 2 -#define SSL_HASH_SHA224 3 -#define SSL_HASH_SHA256 4 -#define SSL_HASH_SHA384 5 -#define SSL_HASH_SHA512 6 - -#define SSL_SIG_RSA 1 - -/* - * Client Certificate Types - */ -#define SSL_CERT_TYPE_RSA_SIGN 1 - -/* - * Message, alert and handshake types - */ -#define SSL_MSG_CHANGE_CIPHER_SPEC 20 -#define SSL_MSG_ALERT 21 -#define SSL_MSG_HANDSHAKE 22 -#define SSL_MSG_APPLICATION_DATA 23 - -#define SSL_ALERT_LEVEL_WARNING 1 -#define SSL_ALERT_LEVEL_FATAL 2 - -#define SSL_ALERT_MSG_CLOSE_NOTIFY 0 /* 0x00 */ -#define SSL_ALERT_MSG_UNEXPECTED_MESSAGE 10 /* 0x0A */ -#define SSL_ALERT_MSG_BAD_RECORD_MAC 20 /* 0x14 */ -#define SSL_ALERT_MSG_DECRYPTION_FAILED 21 /* 0x15 */ -#define SSL_ALERT_MSG_RECORD_OVERFLOW 22 /* 0x16 */ -#define SSL_ALERT_MSG_DECOMPRESSION_FAILURE 30 /* 0x1E */ -#define SSL_ALERT_MSG_HANDSHAKE_FAILURE 40 /* 0x28 */ -#define SSL_ALERT_MSG_NO_CERT 41 /* 0x29 */ -#define SSL_ALERT_MSG_BAD_CERT 42 /* 0x2A */ -#define SSL_ALERT_MSG_UNSUPPORTED_CERT 43 /* 0x2B */ -#define SSL_ALERT_MSG_CERT_REVOKED 44 /* 0x2C */ -#define SSL_ALERT_MSG_CERT_EXPIRED 45 /* 0x2D */ -#define SSL_ALERT_MSG_CERT_UNKNOWN 46 /* 0x2E */ -#define SSL_ALERT_MSG_ILLEGAL_PARAMETER 47 /* 0x2F */ -#define SSL_ALERT_MSG_UNKNOWN_CA 48 /* 0x30 */ -#define SSL_ALERT_MSG_ACCESS_DENIED 49 /* 0x31 */ -#define SSL_ALERT_MSG_DECODE_ERROR 50 /* 0x32 */ -#define SSL_ALERT_MSG_DECRYPT_ERROR 51 /* 0x33 */ -#define SSL_ALERT_MSG_EXPORT_RESTRICTION 60 /* 0x3C */ -#define SSL_ALERT_MSG_PROTOCOL_VERSION 70 /* 0x46 */ -#define SSL_ALERT_MSG_INSUFFICIENT_SECURITY 71 /* 0x47 */ -#define SSL_ALERT_MSG_INTERNAL_ERROR 80 /* 0x50 */ -#define SSL_ALERT_MSG_USER_CANCELED 90 /* 0x5A */ -#define SSL_ALERT_MSG_NO_RENEGOTIATION 100 /* 0x64 */ -#define SSL_ALERT_MSG_UNSUPPORTED_EXT 110 /* 0x6E */ -#define SSL_ALERT_MSG_UNRECOGNIZED_NAME 112 /* 0x70 */ - -#define SSL_HS_HELLO_REQUEST 0 -#define SSL_HS_CLIENT_HELLO 1 -#define SSL_HS_SERVER_HELLO 2 -#define SSL_HS_CERTIFICATE 11 -#define SSL_HS_SERVER_KEY_EXCHANGE 12 -#define SSL_HS_CERTIFICATE_REQUEST 13 -#define SSL_HS_SERVER_HELLO_DONE 14 -#define SSL_HS_CERTIFICATE_VERIFY 15 -#define SSL_HS_CLIENT_KEY_EXCHANGE 16 -#define SSL_HS_FINISHED 20 - -/* - * TLS extensions - */ -#define TLS_EXT_SERVERNAME 0 -#define TLS_EXT_SERVERNAME_HOSTNAME 0 - -#define TLS_EXT_SIG_ALG 13 - -#define TLS_EXT_RENEGOTIATION_INFO 0xFF01 - - -/* - * Generic function pointers for allowing external RSA private key - * implementations. - */ -typedef int (*rsa_decrypt_func)( void *ctx, int mode, size_t *olen, - const unsigned char *input, unsigned char *output, - size_t output_max_len ); -typedef int (*rsa_sign_func)( void *ctx, - int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, - int mode, int hash_id, unsigned int hashlen, - const unsigned char *hash, unsigned char *sig ); -typedef size_t (*rsa_key_len_func)( void *ctx ); - -/* - * SSL state machine - */ -typedef enum -{ - SSL_HELLO_REQUEST, - SSL_CLIENT_HELLO, - SSL_SERVER_HELLO, - SSL_SERVER_CERTIFICATE, - SSL_SERVER_KEY_EXCHANGE, - SSL_CERTIFICATE_REQUEST, - SSL_SERVER_HELLO_DONE, - SSL_CLIENT_CERTIFICATE, - SSL_CLIENT_KEY_EXCHANGE, - SSL_CERTIFICATE_VERIFY, - SSL_CLIENT_CHANGE_CIPHER_SPEC, - SSL_CLIENT_FINISHED, - SSL_SERVER_CHANGE_CIPHER_SPEC, - SSL_SERVER_FINISHED, - SSL_FLUSH_BUFFERS, - SSL_HANDSHAKE_WRAPUP, - SSL_HANDSHAKE_OVER -} -ssl_states; - -typedef struct _ssl_session ssl_session; -typedef struct _ssl_context ssl_context; -typedef struct _ssl_transform ssl_transform; -typedef struct _ssl_handshake_params ssl_handshake_params; - -/* - * This structure is used for storing current session data. - */ -struct _ssl_session -{ - time_t start; /*!< starting time */ - int ciphersuite; /*!< chosen ciphersuite */ - int compression; /*!< chosen compression */ - size_t length; /*!< session id length */ - unsigned char id[32]; /*!< session identifier */ - unsigned char master[48]; /*!< the master secret */ - x509_cert *peer_cert; /*!< peer X.509 cert chain */ -}; - -/* - * This structure contains a full set of runtime transform parameters - * either in negotiation or active. - */ -struct _ssl_transform -{ - /* - * Session specific crypto layer - */ - unsigned int keylen; /*!< symmetric key length */ - size_t minlen; /*!< min. ciphertext length */ - size_t ivlen; /*!< IV length */ - size_t fixed_ivlen; /*!< Fixed part of IV (AEAD) */ - size_t maclen; /*!< MAC length */ - - unsigned char iv_enc[16]; /*!< IV (encryption) */ - unsigned char iv_dec[16]; /*!< IV (decryption) */ - - unsigned char mac_enc[32]; /*!< MAC (encryption) */ - unsigned char mac_dec[32]; /*!< MAC (decryption) */ - - uint32_t ctx_enc[136]; /*!< encryption context */ - uint32_t ctx_dec[136]; /*!< decryption context */ - - /* - * Session specific compression layer - */ -#if defined(POLARSSL_ZLIB_SUPPORT) - z_stream ctx_deflate; /*!< compression context */ - z_stream ctx_inflate; /*!< decompression context */ -#endif -}; - -/* - * This structure contains the parameters only needed during handshake. - */ -struct _ssl_handshake_params -{ - /* - * Handshake specific crypto variables - */ - int sig_alg; /*!< Signature algorithm */ - int cert_type; /*!< Requested cert type */ - int verify_sig_alg; /*!< Signature algorithm for verify */ -#if defined(POLARSSL_DHM_C) - dhm_context dhm_ctx; /*!< DHM key exchange */ -#endif - - /* - * Checksum contexts - */ - md5_context fin_md5; - sha1_context fin_sha1; - sha2_context fin_sha2; - sha4_context fin_sha4; - - void (*update_checksum)(ssl_context *, unsigned char *, size_t); - void (*calc_verify)(ssl_context *, unsigned char *); - void (*calc_finished)(ssl_context *, unsigned char *, int); - int (*tls_prf)(unsigned char *, size_t, char *, - unsigned char *, size_t, - unsigned char *, size_t); - - size_t pmslen; /*!< premaster length */ - - unsigned char randbytes[64]; /*!< random bytes */ - unsigned char premaster[POLARSSL_MPI_MAX_SIZE]; - /*!< premaster secret */ - - int resume; /*!< session resume indicator*/ -}; - -struct _ssl_context -{ - /* - * Miscellaneous - */ - int state; /*!< SSL handshake: current state */ - int renegotiation; /*!< Initial or renegotiation */ - - int major_ver; /*!< equal to SSL_MAJOR_VERSION_3 */ - int minor_ver; /*!< either 0 (SSL3) or 1 (TLS1.0) */ - - int max_major_ver; /*!< max. major version from client */ - int max_minor_ver; /*!< max. minor version from client */ - int min_major_ver; /*!< min. major version accepted */ - int min_minor_ver; /*!< min. minor version accepted */ - - /* - * Callbacks (RNG, debug, I/O, verification) - */ - int (*f_rng)(void *, unsigned char *, size_t); - void (*f_dbg)(void *, int, const char *); - int (*f_recv)(void *, unsigned char *, size_t); - int (*f_send)(void *, const unsigned char *, size_t); - int (*f_vrfy)(void *, x509_cert *, int, int *); - int (*f_get_cache)(void *, ssl_session *); - int (*f_set_cache)(void *, const ssl_session *); - int (*f_sni)(void *, ssl_context *, const unsigned char *, size_t); - - void *p_rng; /*!< context for the RNG function */ - void *p_dbg; /*!< context for the debug function */ - void *p_recv; /*!< context for reading operations */ - void *p_send; /*!< context for writing operations */ - void *p_vrfy; /*!< context for verification */ - void *p_get_cache; /*!< context for cache retrieval */ - void *p_set_cache; /*!< context for cache store */ - void *p_sni; /*!< context for SNI extension */ - void *p_hw_data; /*!< context for HW acceleration */ - - /* - * Session layer - */ - ssl_session *session_in; /*!< current session data (in) */ - ssl_session *session_out; /*!< current session data (out) */ - ssl_session *session; /*!< negotiated session data */ - ssl_session *session_negotiate; /*!< session data in negotiation */ - - ssl_handshake_params *handshake; /*!< params required only during - the handshake process */ - - /* - * Record layer transformations - */ - ssl_transform *transform_in; /*!< current transform params (in) */ - ssl_transform *transform_out; /*!< current transform params (in) */ - ssl_transform *transform; /*!< negotiated transform params */ - ssl_transform *transform_negotiate; /*!< transform params in negotiation */ - - /* - * Record layer (incoming data) - */ - unsigned char *in_ctr; /*!< 64-bit incoming message counter */ - unsigned char *in_hdr; /*!< 5-byte record header (in_ctr+8) */ - unsigned char *in_msg; /*!< the message contents (in_hdr+5) */ - unsigned char *in_offt; /*!< read offset in application data */ - - int in_msgtype; /*!< record header: message type */ - size_t in_msglen; /*!< record header: message length */ - size_t in_left; /*!< amount of data read so far */ - - size_t in_hslen; /*!< current handshake message length */ - int nb_zero; /*!< # of 0-length encrypted messages */ - - /* - * Record layer (outgoing data) - */ - unsigned char *out_ctr; /*!< 64-bit outgoing message counter */ - unsigned char *out_hdr; /*!< 5-byte record header (out_ctr+8) */ - unsigned char *out_msg; /*!< the message contents (out_hdr+32)*/ - - int out_msgtype; /*!< record header: message type */ - size_t out_msglen; /*!< record header: message length */ - size_t out_left; /*!< amount of data not yet written */ - - /* - * PKI layer - */ - void *rsa_key; /*!< own RSA private key */ - rsa_decrypt_func rsa_decrypt; /*!< function for RSA decrypt*/ - rsa_sign_func rsa_sign; /*!< function for RSA sign */ - rsa_key_len_func rsa_key_len; /*!< function for RSA key len*/ - - x509_cert *own_cert; /*!< own X.509 certificate */ - x509_cert *ca_chain; /*!< own trusted CA chain */ - x509_crl *ca_crl; /*!< trusted CA CRLs */ - const char *peer_cn; /*!< expected peer CN */ - - /* - * User settings - */ - int endpoint; /*!< 0: client, 1: server */ - int authmode; /*!< verification mode */ - int client_auth; /*!< flag for client auth. */ - int verify_result; /*!< verification result */ - int disable_renegotiation; /*!< enable/disable renegotiation */ - int allow_legacy_renegotiation; /*!< allow legacy renegotiation */ - const int **ciphersuites; /*!< allowed ciphersuites / version */ - -#if defined(POLARSSL_DHM_C) - mpi dhm_P; /*!< prime modulus for DHM */ - mpi dhm_G; /*!< generator for DHM */ -#endif - - /* - * TLS extensions - */ - unsigned char *hostname; - size_t hostname_len; - - /* - * Secure renegotiation - */ - int secure_renegotiation; /*!< does peer support legacy or - secure renegotiation */ - size_t verify_data_len; /*!< length of verify data stored */ - char own_verify_data[36]; /*!< previous handshake verify data */ - char peer_verify_data[36]; /*!< previous handshake verify data */ -}; - -#ifdef __cplusplus -extern "C" { -#endif - -extern const int ssl_default_ciphersuites[]; - -#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) -extern int (*ssl_hw_record_init)(ssl_context *ssl, - const unsigned char *key_enc, const unsigned char *key_dec, - const unsigned char *iv_enc, const unsigned char *iv_dec, - const unsigned char *mac_enc, const unsigned char *mac_dec); -extern int (*ssl_hw_record_reset)(ssl_context *ssl); -extern int (*ssl_hw_record_write)(ssl_context *ssl); -extern int (*ssl_hw_record_read)(ssl_context *ssl); -extern int (*ssl_hw_record_finish)(ssl_context *ssl); -#endif - -/** - * \brief Returns the list of ciphersuites supported by the SSL/TLS module. - * - * \return a statically allocated array of ciphersuites, the last - * entry is 0. - */ -static inline const int *ssl_list_ciphersuites( void ) -{ - return ssl_default_ciphersuites; -} - -/** - * \brief Return the name of the ciphersuite associated with the given - * ID - * - * \param ciphersuite_id SSL ciphersuite ID - * - * \return a string containing the ciphersuite name - */ -const char *ssl_get_ciphersuite_name( const int ciphersuite_id ); - -/** - * \brief Return the ID of the ciphersuite associated with the given - * name - * - * \param ciphersuite_name SSL ciphersuite name - * - * \return the ID with the ciphersuite or 0 if not found - */ -int ssl_get_ciphersuite_id( const char *ciphersuite_name ); - -/** - * \brief Initialize an SSL context - * - * \param ssl SSL context - * - * \return 0 if successful, or POLARSSL_ERR_SSL_MALLOC_FAILED if - * memory allocation failed - */ -int ssl_init( ssl_context *ssl ); - -/** - * \brief Reset an already initialized SSL context for re-use - * while retaining application-set variables, function - * pointers and data. - * - * \param ssl SSL context - * \return 0 if successful, or POLASSL_ERR_SSL_MALLOC_FAILED, - POLARSSL_ERR_SSL_HW_ACCEL_FAILED or - * POLARSSL_ERR_SSL_COMPRESSION_FAILED - */ -int ssl_session_reset( ssl_context *ssl ); - -/** - * \brief Set the current endpoint type - * - * \param ssl SSL context - * \param endpoint must be SSL_IS_CLIENT or SSL_IS_SERVER - */ -void ssl_set_endpoint( ssl_context *ssl, int endpoint ); - -/** - * \brief Set the certificate verification mode - * - * \param ssl SSL context - * \param authmode can be: - * - * SSL_VERIFY_NONE: peer certificate is not checked (default), - * this is insecure and SHOULD be avoided. - * - * SSL_VERIFY_OPTIONAL: peer certificate is checked, however the - * handshake continues even if verification failed; - * ssl_get_verify_result() can be called after the - * handshake is complete. - * - * SSL_VERIFY_REQUIRED: peer *must* present a valid certificate, - * handshake is aborted if verification failed. - */ -void ssl_set_authmode( ssl_context *ssl, int authmode ); - -/** - * \brief Set the verification callback (Optional). - * - * If set, the verify callback is called for each - * certificate in the chain. For implementation - * information, please see \c x509parse_verify() - * - * \param ssl SSL context - * \param f_vrfy verification function - * \param p_vrfy verification parameter - */ -void ssl_set_verify( ssl_context *ssl, - int (*f_vrfy)(void *, x509_cert *, int, int *), - void *p_vrfy ); - -/** - * \brief Set the random number generator callback - * - * \param ssl SSL context - * \param f_rng RNG function - * \param p_rng RNG parameter - */ -void ssl_set_rng( ssl_context *ssl, - int (*f_rng)(void *, unsigned char *, size_t), - void *p_rng ); - -/** - * \brief Set the debug callback - * - * \param ssl SSL context - * \param f_dbg debug function - * \param p_dbg debug parameter - */ -void ssl_set_dbg( ssl_context *ssl, - void (*f_dbg)(void *, int, const char *), - void *p_dbg ); - -/** - * \brief Set the underlying BIO read and write callbacks - * - * \param ssl SSL context - * \param f_recv read callback - * \param p_recv read parameter - * \param f_send write callback - * \param p_send write parameter - */ -void ssl_set_bio( ssl_context *ssl, - int (*f_recv)(void *, unsigned char *, size_t), void *p_recv, - int (*f_send)(void *, const unsigned char *, size_t), void *p_send ); - -/** - * \brief Set the session cache callbacks (server-side only) - * If not set, no session resuming is done. - * - * The session cache has the responsibility to check for stale - * entries based on timeout. See RFC 5246 for recommendations. - * - * Warning: session.peer_cert is cleared by the SSL/TLS layer on - * connection shutdown, so do not cache the pointer! Either set - * it to NULL or make a full copy of the certificate. - * - * The get callback is called once during the initial handshake - * to enable session resuming. The get function has the - * following parameters: (void *parameter, ssl_session *session) - * If a valid entry is found, it should fill the master of - * the session object with the cached values and return 0, - * return 1 otherwise. Optionally peer_cert can be set as well - * if it is properly present in cache entry. - * - * The set callback is called once during the initial handshake - * to enable session resuming after the entire handshake has - * been finished. The set function has the following parameters: - * (void *parameter, const ssl_session *session). The function - * should create a cache entry for future retrieval based on - * the data in the session structure and should keep in mind - * that the ssl_session object presented (and all its referenced - * data) is cleared by the SSL/TLS layer when the connection is - * terminated. It is recommended to add metadata to determine if - * an entry is still valid in the future. Return 0 if - * successfully cached, return 1 otherwise. - * - * \param ssl SSL context - * \param f_get_cache session get callback - * \param p_get_cache session get parameter - * \param f_set_cache session set callback - * \param p_set_cache session set parameter - */ -void ssl_set_session_cache( ssl_context *ssl, - int (*f_get_cache)(void *, ssl_session *), void *p_get_cache, - int (*f_set_cache)(void *, const ssl_session *), void *p_set_cache ); - -/** - * \brief Request resumption of session (client-side only) - * Session data is copied from presented session structure. - * - * Warning: session.peer_cert is cleared by the SSL/TLS layer on - * connection shutdown, so do not cache the pointer! Either set - * it to NULL or make a full copy of the certificate when - * storing the session for use in this function. - * - * \param ssl SSL context - * \param session session context - */ -void ssl_set_session( ssl_context *ssl, const ssl_session *session ); - -/** - * \brief Set the list of allowed ciphersuites - * (Default: ssl_default_ciphersuites) - * (Overrides all version specific lists) - * - * \param ssl SSL context - * \param ciphersuites 0-terminated list of allowed ciphersuites - */ -void ssl_set_ciphersuites( ssl_context *ssl, const int *ciphersuites ); - -/** - * \brief Set the list of allowed ciphersuites for a specific - * version of the protocol. - * (Default: ssl_default_ciphersuites) - * (Only useful on the server side) - * - * \param ssl SSL context - * \param ciphersuites 0-terminated list of allowed ciphersuites - * \param major Major version number (only SSL_MAJOR_VERSION_3 - * supported) - * \param minor Minor version number (SSL_MINOR_VERSION_0, - * SSL_MINOR_VERSION_1 and SSL_MINOR_VERSION_2, - * SSL_MINOR_VERSION_3 supported) - */ -void ssl_set_ciphersuites_for_version( ssl_context *ssl, - const int *ciphersuites, - int major, int minor ); - -/** - * \brief Set the data required to verify peer certificate - * - * \param ssl SSL context - * \param ca_chain trusted CA chain (meaning all fully trusted top-level CAs) - * \param ca_crl trusted CA CRLs - * \param peer_cn expected peer CommonName (or NULL) - */ -void ssl_set_ca_chain( ssl_context *ssl, x509_cert *ca_chain, - x509_crl *ca_crl, const char *peer_cn ); - -/** - * \brief Set own certificate chain and private key - * - * Note: own_cert should contain IN order from the bottom - * up your certificate chain. The top certificate (self-signed) - * can be omitted. - * - * \param ssl SSL context - * \param own_cert own public certificate chain - * \param rsa_key own private RSA key - */ -void ssl_set_own_cert( ssl_context *ssl, x509_cert *own_cert, - rsa_context *rsa_key ); - -/** - * \brief Set own certificate and alternate non-PolarSSL private - * key and handling callbacks, such as the PKCS#11 wrappers - * or any other external private key handler. - * (see the respective RSA functions in rsa.h for documentation - * of the callback parameters, with the only change being - * that the rsa_context * is a void * in the callbacks) - * - * Note: own_cert should contain IN order from the bottom - * up your certificate chain. The top certificate (self-signed) - * can be omitted. - * - * \param ssl SSL context - * \param own_cert own public certificate chain - * \param rsa_key alternate implementation private RSA key - * \param rsa_decrypt_func alternate implementation of \c rsa_pkcs1_decrypt() - * \param rsa_sign_func alternate implementation of \c rsa_pkcs1_sign() - * \param rsa_key_len_func function returning length of RSA key in bytes - */ -void ssl_set_own_cert_alt( ssl_context *ssl, x509_cert *own_cert, - void *rsa_key, - rsa_decrypt_func rsa_decrypt, - rsa_sign_func rsa_sign, - rsa_key_len_func rsa_key_len ); - -#if defined(POLARSSL_DHM_C) -/** - * \brief Set the Diffie-Hellman public P and G values, - * read as hexadecimal strings (server-side only) - * (Default: POLARSSL_DHM_RFC5114_MODP_1024_[PG]) - * - * \param ssl SSL context - * \param dhm_P Diffie-Hellman-Merkle modulus - * \param dhm_G Diffie-Hellman-Merkle generator - * - * \return 0 if successful - */ -int ssl_set_dh_param( ssl_context *ssl, const char *dhm_P, const char *dhm_G ); - -/** - * \brief Set the Diffie-Hellman public P and G values, - * read from existing context (server-side only) - * - * \param ssl SSL context - * \param dhm_ctx Diffie-Hellman-Merkle context - * - * \return 0 if successful - */ -int ssl_set_dh_param_ctx( ssl_context *ssl, dhm_context *dhm_ctx ); -#endif - -/** - * \brief Set hostname for ServerName TLS extension - * (client-side only) - * - * - * \param ssl SSL context - * \param hostname the server hostname - * - * \return 0 if successful or POLARSSL_ERR_SSL_MALLOC_FAILED - */ -int ssl_set_hostname( ssl_context *ssl, const char *hostname ); - -/** - * \brief Set server side ServerName TLS extension callback - * (optional, server-side only). - * - * If set, the ServerName callback is called whenever the - * server receives a ServerName TLS extension from the client - * during a handshake. The ServerName callback has the - * following parameters: (void *parameter, ssl_context *ssl, - * const unsigned char *hostname, size_t len). If a suitable - * certificate is found, the callback should set the - * certificate and key to use with ssl_set_own_cert() (and - * possibly adjust the CA chain as well) and return 0. The - * callback should return -1 to abort the handshake at this - * point. - * - * \param ssl SSL context - * \param f_sni verification function - * \param p_sni verification parameter - */ -void ssl_set_sni( ssl_context *ssl, - int (*f_sni)(void *, ssl_context *, const unsigned char *, - size_t), - void *p_sni ); - -/** - * \brief Set the maximum supported version sent from the client side - * - * \param ssl SSL context - * \param major Major version number (only SSL_MAJOR_VERSION_3 supported) - * \param minor Minor version number (SSL_MINOR_VERSION_0, - * SSL_MINOR_VERSION_1 and SSL_MINOR_VERSION_2, - * SSL_MINOR_VERSION_3 supported) - */ -void ssl_set_max_version( ssl_context *ssl, int major, int minor ); - - -/** - * \brief Set the minimum accepted SSL/TLS protocol version - * (Default: SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0) - * - * \param ssl SSL context - * \param major Major version number (only SSL_MAJOR_VERSION_3 supported) - * \param minor Minor version number (SSL_MINOR_VERSION_0, - * SSL_MINOR_VERSION_1 and SSL_MINOR_VERSION_2, - * SSL_MINOR_VERSION_3 supported) - */ -void ssl_set_min_version( ssl_context *ssl, int major, int minor ); - -/** - * \brief Enable / Disable renegotiation support for connection when - * initiated by peer - * (Default: SSL_RENEGOTIATION_DISABLED) - * - * Note: A server with support enabled is more vulnerable for a - * resource DoS by a malicious client. You should enable this on - * a client to enable server-initiated renegotiation. - * - * \param ssl SSL context - * \param renegotiation Enable or disable (SSL_RENEGOTIATION_ENABLED or - * SSL_RENEGOTIATION_DISABLED) - */ -void ssl_set_renegotiation( ssl_context *ssl, int renegotiation ); - -/** - * \brief Prevent or allow legacy renegotiation. - * (Default: SSL_LEGACY_NO_RENEGOTIATION) - * - * SSL_LEGACY_NO_RENEGOTIATION allows connections to - * be established even if the peer does not support - * secure renegotiation, but does not allow renegotiation - * to take place if not secure. - * (Interoperable and secure option) - * - * SSL_LEGACY_ALLOW_RENEGOTIATION allows renegotiations - * with non-upgraded peers. Allowing legacy renegotiation - * makes the connection vulnerable to specific man in the - * middle attacks. (See RFC 5746) - * (Most interoperable and least secure option) - * - * SSL_LEGACY_BREAK_HANDSHAKE breaks off connections - * if peer does not support secure renegotiation. Results - * in interoperability issues with non-upgraded peers - * that do not support renegotiation altogether. - * (Most secure option, interoperability issues) - * - * \param ssl SSL context - * \param allow_legacy Prevent or allow (SSL_NO_LEGACY_RENEGOTIATION, - * SSL_ALLOW_LEGACY_RENEGOTIATION or - * SSL_LEGACY_BREAK_HANDSHAKE) - */ -void ssl_legacy_renegotiation( ssl_context *ssl, int allow_legacy ); - -/** - * \brief Return the number of data bytes available to read - * - * \param ssl SSL context - * - * \return how many bytes are available in the read buffer - */ -size_t ssl_get_bytes_avail( const ssl_context *ssl ); - -/** - * \brief Return the result of the certificate verification - * - * \param ssl SSL context - * - * \return 0 if successful, or a combination of: - * BADCERT_EXPIRED - * BADCERT_REVOKED - * BADCERT_CN_MISMATCH - * BADCERT_NOT_TRUSTED - */ -int ssl_get_verify_result( const ssl_context *ssl ); - -/** - * \brief Return the name of the current ciphersuite - * - * \param ssl SSL context - * - * \return a string containing the ciphersuite name - */ -const char *ssl_get_ciphersuite( const ssl_context *ssl ); - -/** - * \brief Return the current SSL version (SSLv3/TLSv1/etc) - * - * \param ssl SSL context - * - * \return a string containing the SSL version - */ -const char *ssl_get_version( const ssl_context *ssl ); - -/** - * \brief Return the peer certificate from the current connection - * - * Note: Can be NULL in case no certificate was sent during - * the handshake. Different calls for the same connection can - * return the same or different pointers for the same - * certificate and even a different certificate altogether. - * The peer cert CAN change in a single connection if - * renegotiation is performed. - * - * \param ssl SSL context - * - * \return the current peer certificate - */ -const x509_cert *ssl_get_peer_cert( const ssl_context *ssl ); - -/** - * \brief Perform the SSL handshake - * - * \param ssl SSL context - * - * \return 0 if successful, POLARSSL_ERR_NET_WANT_READ, - * POLARSSL_ERR_NET_WANT_WRITE, or a specific SSL error code. - */ -int ssl_handshake( ssl_context *ssl ); - -/** - * \brief Perform a single step of the SSL handshake - * - * Note: the state of the context (ssl->state) will be at - * the following state after execution of this function. - * Do not call this function if state is SSL_HANDSHAKE_OVER. - * - * \param ssl SSL context - * - * \return 0 if successful, POLARSSL_ERR_NET_WANT_READ, - * POLARSSL_ERR_NET_WANT_WRITE, or a specific SSL error code. - */ -int ssl_handshake_step( ssl_context *ssl ); - -/** - * \brief Perform an SSL renegotiation on the running connection - * - * \param ssl SSL context - * - * \return 0 if succesful, or any ssl_handshake() return value. - */ -int ssl_renegotiate( ssl_context *ssl ); - -/** - * \brief Read at most 'len' application data bytes - * - * \param ssl SSL context - * \param buf buffer that will hold the data - * \param len how many bytes must be read - * - * \return This function returns the number of bytes read, 0 for EOF, - * or a negative error code. - */ -int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len ); - -/** - * \brief Write exactly 'len' application data bytes - * - * \param ssl SSL context - * \param buf buffer holding the data - * \param len how many bytes must be written - * - * \return This function returns the number of bytes written, - * or a negative error code. - * - * \note When this function returns POLARSSL_ERR_NET_WANT_WRITE, - * it must be called later with the *same* arguments, - * until it returns a positive value. - */ -int ssl_write( ssl_context *ssl, const unsigned char *buf, size_t len ); - -/** - * \brief Send an alert message - * - * \param ssl SSL context - * \param level The alert level of the message - * (SSL_ALERT_LEVEL_WARNING or SSL_ALERT_LEVEL_FATAL) - * \param message The alert message (SSL_ALERT_MSG_*) - * - * \return 0 if successful, or a specific SSL error code. - */ -int ssl_send_alert_message( ssl_context *ssl, - unsigned char level, - unsigned char message ); -/** - * \brief Notify the peer that the connection is being closed - * - * \param ssl SSL context - */ -int ssl_close_notify( ssl_context *ssl ); - -/** - * \brief Free referenced items in an SSL context and clear memory - * - * \param ssl SSL context - */ -void ssl_free( ssl_context *ssl ); - -/** - * \brief Free referenced items in an SSL session including the - * peer certificate and clear memory - * - * \param session SSL session - */ -void ssl_session_free( ssl_session *session ); - -/** - * \brief Free referenced items in an SSL transform context and clear - * memory - * - * \param transform SSL transform context - */ -void ssl_transform_free( ssl_transform *transform ); - -/** - * \brief Free referenced items in an SSL handshake context and clear - * memory - * - * \param handshake SSL handshake context - */ -void ssl_handshake_free( ssl_handshake_params *handshake ); - -/* - * Internal functions (do not call directly) - */ -int ssl_handshake_client_step( ssl_context *ssl ); -int ssl_handshake_server_step( ssl_context *ssl ); -void ssl_handshake_wrapup( ssl_context *ssl ); - -int ssl_send_fatal_handshake_failure( ssl_context *ssl ); - -int ssl_derive_keys( ssl_context *ssl ); - -int ssl_read_record( ssl_context *ssl ); -/** - * \return 0 if successful, POLARSSL_ERR_SSL_CONN_EOF on EOF or - * another negative error code. - */ -int ssl_fetch_input( ssl_context *ssl, size_t nb_want ); - -int ssl_write_record( ssl_context *ssl ); -int ssl_flush_output( ssl_context *ssl ); - -int ssl_parse_certificate( ssl_context *ssl ); -int ssl_write_certificate( ssl_context *ssl ); - -int ssl_parse_change_cipher_spec( ssl_context *ssl ); -int ssl_write_change_cipher_spec( ssl_context *ssl ); - -int ssl_parse_finished( ssl_context *ssl ); -int ssl_write_finished( ssl_context *ssl ); - -void ssl_optimize_checksum( ssl_context *ssl, int ciphersuite ); - -#ifdef __cplusplus -} -#endif - -#endif /* ssl.h */ diff --git a/makerom/polarssl/ssl_cache.h b/makerom/polarssl/ssl_cache.h deleted file mode 100644 index 521129a8..00000000 --- a/makerom/polarssl/ssl_cache.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - * \file ssl_cache.h - * - * \brief SSL session cache implementation - * - * Copyright (C) 2006-2013, 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_SSL_CACHE_H -#define POLARSSL_SSL_CACHE_H - -#include "polarssl/ssl.h" - -#if !defined(POLARSSL_CONFIG_OPTIONS) -#define SSL_CACHE_DEFAULT_TIMEOUT 86400 /*!< 1 day */ -#define SSL_CACHE_DEFAULT_MAX_ENTRIES 50 /*!< Maximum entries in cache */ -#endif /* !POLARSSL_CONFIG_OPTIONS */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct _ssl_cache_context ssl_cache_context; -typedef struct _ssl_cache_entry ssl_cache_entry; - -/** - * \brief This structure is used for storing cache entries - */ -struct _ssl_cache_entry -{ - time_t timestamp; /*!< entry timestamp */ - ssl_session session; /*!< entry session */ - x509_buf peer_cert; /*!< entry peer_cert */ - ssl_cache_entry *next; /*!< chain pointer */ -}; - -/** - * \brief Cache context - */ -struct _ssl_cache_context -{ - ssl_cache_entry *chain; /*!< start of the chain */ - int timeout; /*!< cache entry timeout */ - int max_entries; /*!< maximum entries */ -}; - -/** - * \brief Initialize an SSL cache context - * - * \param cache SSL cache context - */ -void ssl_cache_init( ssl_cache_context *cache ); - -/** - * \brief Cache get callback implementation - * - * \param data SSL cache context - * \param session session to retrieve entry for - */ -int ssl_cache_get( void *data, ssl_session *session ); - -/** - * \brief Cache set callback implementation - * - * \param data SSL cache context - * \param session session to store entry for - */ -int ssl_cache_set( void *data, const ssl_session *session ); - -/** - * \brief Set the cache timeout - * (Default: SSL_CACHE_DEFAULT_TIMEOUT (1 day)) - * - * A timeout of 0 indicates no timeout. - * - * \param cache SSL cache context - * \param timeout cache entry timeout - */ -void ssl_cache_set_timeout( ssl_cache_context *cache, int timeout ); - -/** - * \brief Set the cache timeout - * (Default: SSL_CACHE_DEFAULT_MAX_ENTRIES (50)) - * - * \param cache SSL cache context - * \param max cache entry maximum - */ -void ssl_cache_set_max_entries( ssl_cache_context *cache, int max ); - -/** - * \brief Free referenced items in a cache context and clear memory - * - * \param cache SSL cache context - */ -void ssl_cache_free( ssl_cache_context *cache ); - -#ifdef __cplusplus -} -#endif - -#endif /* ssl_cache.h */ diff --git a/makerom/polarssl/timing.h b/makerom/polarssl/timing.h deleted file mode 100644 index 355c63c7..00000000 --- a/makerom/polarssl/timing.h +++ /dev/null @@ -1,75 +0,0 @@ -/** - * \file timing.h - * - * \brief Portable interface to the CPU cycle counter - * - * 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_TIMING_H -#define POLARSSL_TIMING_H - -/** - * \brief timer structure - */ -struct hr_time -{ - unsigned char opaque[32]; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -extern volatile int alarmed; - -/** - * \brief Return the CPU cycle counter value - */ -unsigned long hardclock( void ); - -/** - * \brief Return the elapsed time in milliseconds - * - * \param val points to a timer structure - * \param reset if set to 1, the timer is restarted - */ -unsigned long get_timer( struct hr_time *val, int reset ); - -/** - * \brief Setup an alarm clock - * - * \param seconds delay before the "alarmed" flag is set - */ -void set_alarm( int seconds ); - -/** - * \brief Sleep for a certain amount of time - * - * \param milliseconds delay in milliseconds - */ -void m_sleep( int milliseconds ); - -#ifdef __cplusplus -} -#endif - -#endif /* timing.h */ diff --git a/makerom/polarssl/version.h b/makerom/polarssl/version.h deleted file mode 100644 index ebaa9c0f..00000000 --- a/makerom/polarssl/version.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * \file version.h - * - * \brief Run-time version information - * - * Copyright (C) 2006-2012, 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 defines and run-time variables can be used to - * determine the version number of the PolarSSL library used. - */ -#ifndef POLARSSL_VERSION_H -#define POLARSSL_VERSION_H - -#include "polarssl/config.h" - -/** - * The version number x.y.z is split into three parts. - * Major, Minor, Patchlevel - */ -#define POLARSSL_VERSION_MAJOR 1 -#define POLARSSL_VERSION_MINOR 2 -#define POLARSSL_VERSION_PATCH 8 - -/** - * The single version number has the following structure: - * MMNNPP00 - * Major version | Minor version | Patch version - */ -#define POLARSSL_VERSION_NUMBER 0x01020800 -#define POLARSSL_VERSION_STRING "1.2.8" -#define POLARSSL_VERSION_STRING_FULL "PolarSSL 1.2.8" - -#if defined(POLARSSL_VERSION_C) - -/** - * Get the version number. - * - * \return The constructed version number in the format - * MMNNPP00 (Major, Minor, Patch). - */ -unsigned int version_get_number( void ); - -/** - * Get the version string ("x.y.z"). - * - * \param string The string that will receive the value. - * (Should be at least 9 bytes in size) - */ -void version_get_string( char *string ); - -/** - * Get the full version string ("PolarSSL x.y.z"). - * - * \param string The string that will receive the value. - * (Should be at least 18 bytes in size) - */ -void version_get_string_full( char *string ); - -#endif /* POLARSSL_VERSION_C */ - -#endif /* version.h */ diff --git a/makerom/polarssl/x509.h b/makerom/polarssl/x509.h deleted file mode 100644 index 71a51a94..00000000 --- a/makerom/polarssl/x509.h +++ /dev/null @@ -1,778 +0,0 @@ -/** - * \file x509.h - * - * \brief X.509 certificate and private key decoding - * - * Copyright (C) 2006-2011, 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_X509_H -#define POLARSSL_X509_H - -#include "polarssl/asn1.h" -#include "polarssl/rsa.h" -#include "polarssl/dhm.h" - -/** - * \addtogroup x509_module - * \{ - */ - -/** - * \name X509 Error codes - * \{ - */ -#define POLARSSL_ERR_X509_FEATURE_UNAVAILABLE -0x2080 /**< Unavailable feature, e.g. RSA hashing/encryption combination. */ -#define POLARSSL_ERR_X509_CERT_INVALID_PEM -0x2100 /**< The PEM-encoded certificate contains invalid elements, e.g. invalid character. */ -#define POLARSSL_ERR_X509_CERT_INVALID_FORMAT -0x2180 /**< The certificate format is invalid, e.g. different type expected. */ -#define POLARSSL_ERR_X509_CERT_INVALID_VERSION -0x2200 /**< The certificate version element is invalid. */ -#define POLARSSL_ERR_X509_CERT_INVALID_SERIAL -0x2280 /**< The serial tag or value is invalid. */ -#define POLARSSL_ERR_X509_CERT_INVALID_ALG -0x2300 /**< The algorithm tag or value is invalid. */ -#define POLARSSL_ERR_X509_CERT_INVALID_NAME -0x2380 /**< The name tag or value is invalid. */ -#define POLARSSL_ERR_X509_CERT_INVALID_DATE -0x2400 /**< The date tag or value is invalid. */ -#define POLARSSL_ERR_X509_CERT_INVALID_PUBKEY -0x2480 /**< The pubkey tag or value is invalid (only RSA is supported). */ -#define POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE -0x2500 /**< The signature tag or value invalid. */ -#define POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS -0x2580 /**< The extension tag or value is invalid. */ -#define POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION -0x2600 /**< Certificate or CRL has an unsupported version number. */ -#define POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG -0x2680 /**< Signature algorithm (oid) is unsupported. */ -#define POLARSSL_ERR_X509_UNKNOWN_PK_ALG -0x2700 /**< Key algorithm is unsupported (only RSA is supported). */ -#define POLARSSL_ERR_X509_CERT_SIG_MISMATCH -0x2780 /**< Certificate signature algorithms do not match. (see \c ::x509_cert sig_oid) */ -#define POLARSSL_ERR_X509_CERT_VERIFY_FAILED -0x2800 /**< Certificate verification failed, e.g. CRL, CA or signature check failed. */ -#define POLARSSL_ERR_X509_KEY_INVALID_VERSION -0x2880 /**< Unsupported RSA key version */ -#define POLARSSL_ERR_X509_KEY_INVALID_FORMAT -0x2900 /**< Invalid RSA key tag or value. */ -#define POLARSSL_ERR_X509_CERT_UNKNOWN_FORMAT -0x2980 /**< Format not recognized as DER or PEM. */ -#define POLARSSL_ERR_X509_INVALID_INPUT -0x2A00 /**< Input invalid. */ -#define POLARSSL_ERR_X509_MALLOC_FAILED -0x2A80 /**< Allocation of memory failed. */ -#define POLARSSL_ERR_X509_FILE_IO_ERROR -0x2B00 /**< Read/write of file failed. */ -#define POLARSSL_ERR_X509_PASSWORD_REQUIRED -0x2B80 /**< Private key password can't be empty. */ -#define POLARSSL_ERR_X509_PASSWORD_MISMATCH -0x2C00 /**< Given private key password does not allow for correct decryption. */ -/* \} name */ - - -/** - * \name X509 Verify codes - * \{ - */ -#define BADCERT_EXPIRED 0x01 /**< The certificate validity has expired. */ -#define BADCERT_REVOKED 0x02 /**< The certificate has been revoked (is on a CRL). */ -#define BADCERT_CN_MISMATCH 0x04 /**< The certificate Common Name (CN) does not match with the expected CN. */ -#define BADCERT_NOT_TRUSTED 0x08 /**< The certificate is not correctly signed by the trusted CA. */ -#define BADCRL_NOT_TRUSTED 0x10 /**< CRL is not correctly signed by the trusted CA. */ -#define BADCRL_EXPIRED 0x20 /**< CRL is expired. */ -#define BADCERT_MISSING 0x40 /**< Certificate was missing. */ -#define BADCERT_SKIP_VERIFY 0x80 /**< Certificate verification was skipped. */ -#define BADCERT_OTHER 0x0100 /**< Other reason (can be used by verify callback) */ -/* \} name */ -/* \} addtogroup x509_module */ - -/* - * various object identifiers - */ -#define X520_COMMON_NAME 3 -#define X520_COUNTRY 6 -#define X520_LOCALITY 7 -#define X520_STATE 8 -#define X520_ORGANIZATION 10 -#define X520_ORG_UNIT 11 -#define PKCS9_EMAIL 1 - -#define X509_OUTPUT_DER 0x01 -#define X509_OUTPUT_PEM 0x02 -#define PEM_LINE_LENGTH 72 -#define X509_ISSUER 0x01 -#define X509_SUBJECT 0x02 - -#define OID_X520 "\x55\x04" -#define OID_CN OID_X520 "\x03" -#define OID_COUNTRY OID_X520 "\x06" -#define OID_LOCALITY OID_X520 "\x07" -#define OID_STATE OID_X520 "\x08" -#define OID_ORGANIZATION OID_X520 "\x0A" -#define OID_ORG_UNIT OID_X520 "\x0B" - -#define OID_PKCS1 "\x2A\x86\x48\x86\xF7\x0D\x01\x01" -#define OID_PKCS1_RSA OID_PKCS1 "\x01" -#define OID_PKCS1_SHA1 OID_PKCS1 "\x05" - -#define OID_RSA_SHA_OBS "\x2B\x0E\x03\x02\x1D" - -#define OID_PKCS9 "\x2A\x86\x48\x86\xF7\x0D\x01\x09" -#define OID_PKCS9_EMAIL OID_PKCS9 "\x01" - -/** ISO arc for standard certificate and CRL extensions */ -#define OID_ID_CE "\x55\x1D" /**< id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} */ - -/** - * Private Internet Extensions - * { iso(1) identified-organization(3) dod(6) internet(1) - * security(5) mechanisms(5) pkix(7) } - */ -#define OID_PKIX "\x2B\x06\x01\x05\x05\x07" - -/* - * OIDs for standard certificate extensions - */ -#define OID_AUTHORITY_KEY_IDENTIFIER OID_ID_CE "\x23" /**< id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } */ -#define OID_SUBJECT_KEY_IDENTIFIER OID_ID_CE "\x0E" /**< id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 } */ -#define OID_KEY_USAGE OID_ID_CE "\x0F" /**< id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } */ -#define OID_CERTIFICATE_POLICIES OID_ID_CE "\x20" /**< id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } */ -#define OID_POLICY_MAPPINGS OID_ID_CE "\x21" /**< id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } */ -#define OID_SUBJECT_ALT_NAME OID_ID_CE "\x11" /**< id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } */ -#define OID_ISSUER_ALT_NAME OID_ID_CE "\x12" /**< id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } */ -#define OID_SUBJECT_DIRECTORY_ATTRS OID_ID_CE "\x09" /**< id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } */ -#define OID_BASIC_CONSTRAINTS OID_ID_CE "\x13" /**< id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } */ -#define OID_NAME_CONSTRAINTS OID_ID_CE "\x1E" /**< id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } */ -#define OID_POLICY_CONSTRAINTS OID_ID_CE "\x24" /**< id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } */ -#define OID_EXTENDED_KEY_USAGE OID_ID_CE "\x25" /**< id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 } */ -#define OID_CRL_DISTRIBUTION_POINTS OID_ID_CE "\x1F" /**< id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 } */ -#define OID_INIHIBIT_ANYPOLICY OID_ID_CE "\x36" /**< id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-ce 54 } */ -#define OID_FRESHEST_CRL OID_ID_CE "\x2E" /**< id-ce-freshestCRL OBJECT IDENTIFIER ::= { id-ce 46 } */ - -/* - * X.509 v3 Key Usage Extension flags - */ -#define KU_DIGITAL_SIGNATURE (0x80) /* bit 0 */ -#define KU_NON_REPUDIATION (0x40) /* bit 1 */ -#define KU_KEY_ENCIPHERMENT (0x20) /* bit 2 */ -#define KU_DATA_ENCIPHERMENT (0x10) /* bit 3 */ -#define KU_KEY_AGREEMENT (0x08) /* bit 4 */ -#define KU_KEY_CERT_SIGN (0x04) /* bit 5 */ -#define KU_CRL_SIGN (0x02) /* bit 6 */ - -/* - * X.509 v3 Extended key usage OIDs - */ -#define OID_ANY_EXTENDED_KEY_USAGE OID_EXTENDED_KEY_USAGE "\x00" /**< anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } */ - -#define OID_KP OID_PKIX "\x03" /**< id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } */ -#define OID_SERVER_AUTH OID_KP "\x01" /**< id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } */ -#define OID_CLIENT_AUTH OID_KP "\x02" /**< id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } */ -#define OID_CODE_SIGNING OID_KP "\x03" /**< id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } */ -#define OID_EMAIL_PROTECTION OID_KP "\x04" /**< id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } */ -#define OID_TIME_STAMPING OID_KP "\x08" /**< id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } */ -#define OID_OCSP_SIGNING OID_KP "\x09" /**< id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } */ - -#define STRING_SERVER_AUTH "TLS Web Server Authentication" -#define STRING_CLIENT_AUTH "TLS Web Client Authentication" -#define STRING_CODE_SIGNING "Code Signing" -#define STRING_EMAIL_PROTECTION "E-mail Protection" -#define STRING_TIME_STAMPING "Time Stamping" -#define STRING_OCSP_SIGNING "OCSP Signing" - -/* - * OIDs for CRL extensions - */ -#define OID_PRIVATE_KEY_USAGE_PERIOD OID_ID_CE "\x10" -#define OID_CRL_NUMBER OID_ID_CE "\x14" /**< id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 } */ - -/* - * Netscape certificate extensions - */ -#define OID_NETSCAPE "\x60\x86\x48\x01\x86\xF8\x42" /**< Netscape OID */ -#define OID_NS_CERT OID_NETSCAPE "\x01" -#define OID_NS_CERT_TYPE OID_NS_CERT "\x01" -#define OID_NS_BASE_URL OID_NS_CERT "\x02" -#define OID_NS_REVOCATION_URL OID_NS_CERT "\x03" -#define OID_NS_CA_REVOCATION_URL OID_NS_CERT "\x04" -#define OID_NS_RENEWAL_URL OID_NS_CERT "\x07" -#define OID_NS_CA_POLICY_URL OID_NS_CERT "\x08" -#define OID_NS_SSL_SERVER_NAME OID_NS_CERT "\x0C" -#define OID_NS_COMMENT OID_NS_CERT "\x0D" -#define OID_NS_DATA_TYPE OID_NETSCAPE "\x02" -#define OID_NS_CERT_SEQUENCE OID_NS_DATA_TYPE "\x05" - -/* - * Netscape certificate types - * (http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html) - */ - -#define NS_CERT_TYPE_SSL_CLIENT (0x80) /* bit 0 */ -#define NS_CERT_TYPE_SSL_SERVER (0x40) /* bit 1 */ -#define NS_CERT_TYPE_EMAIL (0x20) /* bit 2 */ -#define NS_CERT_TYPE_OBJECT_SIGNING (0x10) /* bit 3 */ -#define NS_CERT_TYPE_RESERVED (0x08) /* bit 4 */ -#define NS_CERT_TYPE_SSL_CA (0x04) /* bit 5 */ -#define NS_CERT_TYPE_EMAIL_CA (0x02) /* bit 6 */ -#define NS_CERT_TYPE_OBJECT_SIGNING_CA (0x01) /* bit 7 */ - -#define EXT_AUTHORITY_KEY_IDENTIFIER (1 << 0) -#define EXT_SUBJECT_KEY_IDENTIFIER (1 << 1) -#define EXT_KEY_USAGE (1 << 2) -#define EXT_CERTIFICATE_POLICIES (1 << 3) -#define EXT_POLICY_MAPPINGS (1 << 4) -#define EXT_SUBJECT_ALT_NAME (1 << 5) -#define EXT_ISSUER_ALT_NAME (1 << 6) -#define EXT_SUBJECT_DIRECTORY_ATTRS (1 << 7) -#define EXT_BASIC_CONSTRAINTS (1 << 8) -#define EXT_NAME_CONSTRAINTS (1 << 9) -#define EXT_POLICY_CONSTRAINTS (1 << 10) -#define EXT_EXTENDED_KEY_USAGE (1 << 11) -#define EXT_CRL_DISTRIBUTION_POINTS (1 << 12) -#define EXT_INIHIBIT_ANYPOLICY (1 << 13) -#define EXT_FRESHEST_CRL (1 << 14) - -#define EXT_NS_CERT_TYPE (1 << 16) - -/* - * Storage format identifiers - * Recognized formats: PEM and DER - */ -#define X509_FORMAT_DER 1 -#define X509_FORMAT_PEM 2 - -/** - * \addtogroup x509_module - * \{ */ - -/** - * \name Structures for parsing X.509 certificates and CRLs - * \{ - */ - -/** - * Type-length-value structure that allows for ASN1 using DER. - */ -typedef asn1_buf x509_buf; - -/** - * Container for ASN1 bit strings. - */ -typedef asn1_bitstring x509_bitstring; - -/** - * Container for ASN1 named information objects. - * It allows for Relative Distinguished Names (e.g. cn=polarssl,ou=code,etc.). - */ -typedef struct _x509_name -{ - x509_buf oid; /**< The object identifier. */ - x509_buf val; /**< The named value. */ - struct _x509_name *next; /**< The next named information object. */ -} -x509_name; - -/** - * Container for a sequence of ASN.1 items - */ -typedef asn1_sequence x509_sequence; - -/** Container for date and time (precision in seconds). */ -typedef struct _x509_time -{ - int year, mon, day; /**< Date. */ - int hour, min, sec; /**< Time. */ -} -x509_time; - -/** - * Container for an X.509 certificate. The certificate may be chained. - */ -typedef struct _x509_cert -{ - x509_buf raw; /**< The raw certificate data (DER). */ - x509_buf tbs; /**< The raw certificate body (DER). The part that is To Be Signed. */ - - int version; /**< The X.509 version. (0=v1, 1=v2, 2=v3) */ - x509_buf serial; /**< Unique id for certificate issued by a specific CA. */ - x509_buf sig_oid1; /**< Signature algorithm, e.g. sha1RSA */ - - x509_buf issuer_raw; /**< The raw issuer data (DER). Used for quick comparison. */ - x509_buf subject_raw; /**< The raw subject data (DER). Used for quick comparison. */ - - x509_name issuer; /**< The parsed issuer data (named information object). */ - x509_name subject; /**< The parsed subject data (named information object). */ - - x509_time valid_from; /**< Start time of certificate validity. */ - x509_time valid_to; /**< End time of certificate validity. */ - - x509_buf pk_oid; /**< Subject public key info. Includes the public key algorithm and the key itself. */ - rsa_context rsa; /**< Container for the RSA context. Only RSA is supported for public keys at this time. */ - - x509_buf issuer_id; /**< Optional X.509 v2/v3 issuer unique identifier. */ - x509_buf subject_id; /**< Optional X.509 v2/v3 subject unique identifier. */ - x509_buf v3_ext; /**< Optional X.509 v3 extensions. Only Basic Contraints are supported at this time. */ - x509_sequence subject_alt_names; /**< Optional list of Subject Alternative Names (Only dNSName supported). */ - - int ext_types; /**< Bit string containing detected and parsed extensions */ - int ca_istrue; /**< Optional Basic Constraint extension value: 1 if this certificate belongs to a CA, 0 otherwise. */ - int max_pathlen; /**< Optional Basic Constraint extension value: The maximum path length to the root certificate. Path length is 1 higher than RFC 5280 'meaning', so 1+ */ - - unsigned char key_usage; /**< Optional key usage extension value: See the values below */ - - x509_sequence ext_key_usage; /**< Optional list of extended key usage OIDs. */ - - unsigned char ns_cert_type; /**< Optional Netscape certificate type extension value: See the values below */ - - x509_buf sig_oid2; /**< Signature algorithm. Must match sig_oid1. */ - x509_buf sig; /**< Signature: hash of the tbs part signed with the private key. */ - int sig_alg; /**< Internal representation of the signature algorithm, e.g. SIG_RSA_MD2 */ - - struct _x509_cert *next; /**< Next certificate in the CA-chain. */ -} -x509_cert; - -/** - * Certificate revocation list entry. - * Contains the CA-specific serial numbers and revocation dates. - */ -typedef struct _x509_crl_entry -{ - x509_buf raw; - - x509_buf serial; - - x509_time revocation_date; - - x509_buf entry_ext; - - struct _x509_crl_entry *next; -} -x509_crl_entry; - -/** - * Certificate revocation list structure. - * Every CRL may have multiple entries. - */ -typedef struct _x509_crl -{ - x509_buf raw; /**< The raw certificate data (DER). */ - x509_buf tbs; /**< The raw certificate body (DER). The part that is To Be Signed. */ - - int version; - x509_buf sig_oid1; - - x509_buf issuer_raw; /**< The raw issuer data (DER). */ - - x509_name issuer; /**< The parsed issuer data (named information object). */ - - x509_time this_update; - x509_time next_update; - - x509_crl_entry entry; /**< The CRL entries containing the certificate revocation times for this CA. */ - - x509_buf crl_ext; - - x509_buf sig_oid2; - x509_buf sig; - int sig_alg; - - struct _x509_crl *next; -} -x509_crl; -/** \} name Structures for parsing X.509 certificates and CRLs */ -/** \} addtogroup x509_module */ - -/** - * \name Structures for writing X.509 certificates. - * XvP: commented out as they are not used. - * - typedef struct _x509_node x509_node; - * - typedef struct _x509_raw x509_raw; - */ -/* -typedef struct _x509_node -{ - unsigned char *data; - unsigned char *p; - unsigned char *end; - - size_t len; -} -x509_node; - -typedef struct _x509_raw -{ - x509_node raw; - x509_node tbs; - - x509_node version; - x509_node serial; - x509_node tbs_signalg; - x509_node issuer; - x509_node validity; - x509_node subject; - x509_node subpubkey; - - x509_node signalg; - x509_node sign; -} -x509_raw; -*/ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \name Functions to read in DHM parameters, a certificate, CRL or private RSA key - * \{ - */ - -/** \ingroup x509_module */ -/** - * \brief Parse a single DER formatted certificate and add it - * to the chained list. - * - * \param chain points to the start of the chain - * \param buf buffer holding the certificate DER data - * \param buflen size of the buffer - * - * \return 0 if successful, or a specific X509 or PEM error code - */ -int x509parse_crt_der( x509_cert *chain, const unsigned char *buf, size_t buflen ); - -/** - * \brief Parse one or more certificates and add them - * to the chained list. Parses permissively. If some - * certificates can be parsed, the result is the number - * of failed certificates it encountered. If none complete - * correctly, the first error is returned. - * - * \param chain points to the start of the chain - * \param buf buffer holding the certificate data - * \param buflen size of the buffer - * - * \return 0 if all certificates parsed successfully, a positive number - * if partly successful or a specific X509 or PEM error code - */ -int x509parse_crt( x509_cert *chain, const unsigned char *buf, size_t buflen ); - -/** \ingroup x509_module */ -/** - * \brief Load one or more certificates and add them - * to the chained list. Parses permissively. If some - * certificates can be parsed, the result is the number - * of failed certificates it encountered. If none complete - * correctly, the first error is returned. - * - * \param chain points to the start of the chain - * \param path filename to read the certificates from - * - * \return 0 if all certificates parsed successfully, a positive number - * if partly successful or a specific X509 or PEM error code - */ -int x509parse_crtfile( x509_cert *chain, const char *path ); - -/** \ingroup x509_module */ -/** - * \brief Load one or more certificate files from a path and add them - * to the chained list. Parses permissively. If some - * certificates can be parsed, the result is the number - * of failed certificates it encountered. If none complete - * correctly, the first error is returned. - * - * \param chain points to the start of the chain - * \param path directory / folder to read the certificate files from - * - * \return 0 if all certificates parsed successfully, a positive number - * if partly successful or a specific X509 or PEM error code - */ -int x509parse_crtpath( x509_cert *chain, const char *path ); - -/** \ingroup x509_module */ -/** - * \brief Parse one or more CRLs and add them - * to the chained list - * - * \param chain points to the start of the chain - * \param buf buffer holding the CRL data - * \param buflen size of the buffer - * - * \return 0 if successful, or a specific X509 or PEM error code - */ -int x509parse_crl( x509_crl *chain, const unsigned char *buf, size_t buflen ); - -/** \ingroup x509_module */ -/** - * \brief Load one or more CRLs and add them - * to the chained list - * - * \param chain points to the start of the chain - * \param path filename to read the CRLs from - * - * \return 0 if successful, or a specific X509 or PEM error code - */ -int x509parse_crlfile( x509_crl *chain, const char *path ); - -/** \ingroup x509_module */ -/** - * \brief Parse a private RSA key - * - * \param rsa RSA context to be initialized - * \param key input buffer - * \param keylen size of the buffer - * \param pwd password for decryption (optional) - * \param pwdlen size of the password - * - * \return 0 if successful, or a specific X509 or PEM error code - */ -int x509parse_key( rsa_context *rsa, - const unsigned char *key, size_t keylen, - const unsigned char *pwd, size_t pwdlen ); - -/** \ingroup x509_module */ -/** - * \brief Load and parse a private RSA key - * - * \param rsa RSA context to be initialized - * \param path filename to read the private key from - * \param password password to decrypt the file (can be NULL) - * - * \return 0 if successful, or a specific X509 or PEM error code - */ -int x509parse_keyfile( rsa_context *rsa, const char *path, - const char *password ); - -/** \ingroup x509_module */ -/** - * \brief Parse a public RSA key - * - * \param rsa RSA context to be initialized - * \param key input buffer - * \param keylen size of the buffer - * - * \return 0 if successful, or a specific X509 or PEM error code - */ -int x509parse_public_key( rsa_context *rsa, - const unsigned char *key, size_t keylen ); - -/** \ingroup x509_module */ -/** - * \brief Load and parse a public RSA key - * - * \param rsa RSA context to be initialized - * \param path filename to read the private key from - * - * \return 0 if successful, or a specific X509 or PEM error code - */ -int x509parse_public_keyfile( rsa_context *rsa, const char *path ); - -/** \ingroup x509_module */ -/** - * \brief Parse DHM parameters - * - * \param dhm DHM context to be initialized - * \param dhmin input buffer - * \param dhminlen size of the buffer - * - * \return 0 if successful, or a specific X509 or PEM error code - */ -int x509parse_dhm( dhm_context *dhm, const unsigned char *dhmin, size_t dhminlen ); - -/** \ingroup x509_module */ -/** - * \brief Load and parse DHM parameters - * - * \param dhm DHM context to be initialized - * \param path filename to read the DHM Parameters from - * - * \return 0 if successful, or a specific X509 or PEM error code - */ -int x509parse_dhmfile( dhm_context *dhm, const char *path ); - -/** \} name Functions to read in DHM parameters, a certificate, CRL or private RSA key */ - -/** - * \brief Store the certificate DN in printable form into buf; - * no more than size characters will be written. - * - * \param buf Buffer to write to - * \param size Maximum size of buffer - * \param dn The X509 name to represent - * - * \return The amount of data written to the buffer, or -1 in - * case of an error. - */ -int x509parse_dn_gets( char *buf, size_t size, const x509_name *dn ); - -/** - * \brief Store the certificate serial in printable form into buf; - * no more than size characters will be written. - * - * \param buf Buffer to write to - * \param size Maximum size of buffer - * \param serial The X509 serial to represent - * - * \return The amount of data written to the buffer, or -1 in - * case of an error. - */ -int x509parse_serial_gets( char *buf, size_t size, const x509_buf *serial ); - -/** - * \brief Returns an informational string about the - * certificate. - * - * \param buf Buffer to write to - * \param size Maximum size of buffer - * \param prefix A line prefix - * \param crt The X509 certificate to represent - * - * \return The amount of data written to the buffer, or -1 in - * case of an error. - */ -int x509parse_cert_info( char *buf, size_t size, const char *prefix, - const x509_cert *crt ); - -/** - * \brief Returns an informational string about the - * CRL. - * - * \param buf Buffer to write to - * \param size Maximum size of buffer - * \param prefix A line prefix - * \param crl The X509 CRL to represent - * - * \return The amount of data written to the buffer, or -1 in - * case of an error. - */ -int x509parse_crl_info( char *buf, size_t size, const char *prefix, - const x509_crl *crl ); - -/** - * \brief Give an known OID, return its descriptive string. - * - * \param oid buffer containing the oid - * - * \return Return a string if the OID is known, - * or NULL otherwise. - */ -const char *x509_oid_get_description( x509_buf *oid ); - -/** - * \brief Give an OID, return a string version of its OID number. - * - * \param buf Buffer to write to - * \param size Maximum size of buffer - * \param oid Buffer containing the OID - * - * \return The amount of data written to the buffer, or -1 in - * case of an error. - */ -int x509_oid_get_numeric_string( char *buf, size_t size, x509_buf *oid ); - -/** - * \brief Check a given x509_time against the system time and check - * if it is valid. - * - * \param time x509_time to check - * - * \return Return 0 if the x509_time is still valid, - * or 1 otherwise. - */ -int x509parse_time_expired( const x509_time *time ); - -/** - * \name Functions to verify a certificate - * \{ - */ -/** \ingroup x509_module */ -/** - * \brief Verify the certificate signature - * - * The verify callback is a user-supplied callback that - * can clear / modify / add flags for a certificate. If set, - * the verification callback is called for each - * certificate in the chain (from the trust-ca down to the - * presented crt). The parameters for the callback are: - * (void *parameter, x509_cert *crt, int certificate_depth, - * int *flags). With the flags representing current flags for - * that specific certificate and the certificate depth from - * the bottom (Peer cert depth = 0). - * - * All flags left after returning from the callback - * are also returned to the application. The function should - * return 0 for anything but a fatal error. - * - * \param crt a certificate to be verified - * \param trust_ca the trusted CA chain - * \param ca_crl the CRL chain for trusted CA's - * \param cn expected Common Name (can be set to - * NULL if the CN must not be verified) - * \param flags result of the verification - * \param f_vrfy verification function - * \param p_vrfy verification parameter - * - * \return 0 if successful or POLARSSL_ERR_X509_SIG_VERIFY_FAILED, - * in which case *flags will have one or more of - * the following values set: - * BADCERT_EXPIRED -- - * BADCERT_REVOKED -- - * BADCERT_CN_MISMATCH -- - * BADCERT_NOT_TRUSTED - * or another error in case of a fatal error encountered - * during the verification process. - */ -int x509parse_verify( x509_cert *crt, - x509_cert *trust_ca, - x509_crl *ca_crl, - const char *cn, int *flags, - int (*f_vrfy)(void *, x509_cert *, int, int *), - void *p_vrfy ); - -/** - * \brief Verify the certificate signature - * - * \param crt a certificate to be verified - * \param crl the CRL to verify against - * - * \return 1 if the certificate is revoked, 0 otherwise - * - */ -int x509parse_revoked( const x509_cert *crt, const x509_crl *crl ); - -/** \} name Functions to verify a certificate */ - - - -/** - * \name Functions to clear a certificate, CRL or private RSA key - * \{ - */ -/** \ingroup x509_module */ -/** - * \brief Unallocate all certificate data - * - * \param crt Certificate chain to free - */ -void x509_free( x509_cert *crt ); - -/** \ingroup x509_module */ -/** - * \brief Unallocate all CRL data - * - * \param crl CRL chain to free - */ -void x509_crl_free( x509_crl *crl ); - -/** \} name Functions to clear a certificate, CRL or private RSA key */ - - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int x509_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* x509.h */ diff --git a/makerom/polarssl/x509write.h b/makerom/polarssl/x509write.h deleted file mode 100644 index 16bfab3b..00000000 --- a/makerom/polarssl/x509write.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * \file x509write.h - * - * \brief X509 buffer writing functionality - * - * Copyright (C) 2006-2012, 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_X509_WRITE_H -#define POLARSSL_X509_WRITE_H - -#include "polarssl/rsa.h" - -typedef struct _x509_req_name -{ - char oid[128]; - char name[128]; - - struct _x509_req_name *next; -} -x509_req_name; - -int x509_write_pubkey_der( unsigned char *buf, size_t size, rsa_context *rsa ); -int x509_write_key_der( unsigned char *buf, size_t size, rsa_context *rsa ); -int x509_write_cert_req( unsigned char *buf, size_t size, rsa_context *rsa, - x509_req_name *req_name, int hash_id ); - -#endif /* POLARSSL_X509_WRITE_H */ diff --git a/makerom/polarssl/xtea.h b/makerom/polarssl/xtea.h deleted file mode 100644 index eda7e44e..00000000 --- a/makerom/polarssl/xtea.h +++ /dev/null @@ -1,129 +0,0 @@ -/** - * \file xtea.h - * - * \brief XTEA block cipher (32-bit) - * - * Copyright (C) 2006-2013, 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_XTEA_H -#define POLARSSL_XTEA_H - -#include "polarssl/config.h" - -#include - -#ifdef _MSC_VER -#include -typedef UINT32 uint32_t; -#else -#include -#endif - -#define XTEA_ENCRYPT 1 -#define XTEA_DECRYPT 0 - -#define POLARSSL_ERR_XTEA_INVALID_INPUT_LENGTH -0x0028 /**< The data input has an invalid length. */ - -#if !defined(POLARSSL_XTEA_ALT) -// Regular implementation -// - -/** - * \brief XTEA context structure - */ -typedef struct -{ - uint32_t k[4]; /*!< key */ -} -xtea_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief XTEA key schedule - * - * \param ctx XTEA context to be initialized - * \param key the secret key - */ -void xtea_setup( xtea_context *ctx, unsigned char key[16] ); - -/** - * \brief XTEA cipher function - * - * \param ctx XTEA context - * \param mode XTEA_ENCRYPT or XTEA_DECRYPT - * \param input 8-byte input block - * \param output 8-byte output block - * - * \return 0 if successful - */ -int xtea_crypt_ecb( xtea_context *ctx, - int mode, - unsigned char input[8], - unsigned char output[8] ); - -/** - * \brief XTEA CBC cipher function - * - * \param ctx XTEA context - * \param mode XTEA_ENCRYPT or XTEA_DECRYPT - * \param length the length of input, multiple of 8 - * \param iv initialization vector for CBC mode - * \param input input block - * \param output output block - * - * \return 0 if successful, - * POLARSSL_ERR_XTEA_INVALID_INPUT_LENGTH if the length % 8 != 0 - */ -int xtea_crypt_cbc( xtea_context *ctx, - int mode, - size_t length, - unsigned char iv[8], - unsigned char *input, - unsigned char *output); - -#ifdef __cplusplus -} -#endif - -#else /* POLARSSL_XTEA_ALT */ -#include "polarssl/xtea_alt.h" -#endif /* POLARSSL_XTEA_ALT */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief Checkup routine - * - * \return 0 if successful, or 1 if the test failed - */ -int xtea_self_test( int verbose ); - -#ifdef __cplusplus -} -#endif - -#endif /* xtea.h */ From 32f075b03d503fd22b0309223bd2fd3d69bd357f Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 15 Feb 2016 14:17:53 +0800 Subject: [PATCH 168/317] Fixed issue #25 --- makerom/user_settings.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 68cc1bb6..3702316b 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -702,8 +702,8 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) int CheckArgumentCombination(user_settings *set) { - // If content 0 was not specified, we must build it (a NCCH file) - if (set->common.contentPath[0] == NULL) { + // If content 0 was not specified (and a special file aka SRL,CIA,CCI was not specified), we must build it (a NCCH file) + if (set->common.contentPath[0] == NULL && set->common.workingFilePath == NULL) { set->ncch.buildNcch0 = true; // A CXI can contain elements of a CFA, but not the other way round. if (set->ncch.ncchType & CXI) @@ -717,6 +717,7 @@ int CheckArgumentCombination(user_settings *set) } else { set->ncch.buildNcch0 = false; + set->ncch.ncchType = 0; } for (int i = 0; i < CIA_MAX_CONTENT; i++) { From 521ecb71dc3583db0d4022f371721622e74cbde8 Mon Sep 17 00:00:00 2001 From: piratesephiroth Date: Mon, 15 Feb 2016 05:55:59 -0300 Subject: [PATCH 169/317] Fix CategoryFlags always being considered invalid --- makerom/titleid.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/makerom/titleid.c b/makerom/titleid.c index 33961a4e..251282be 100644 --- a/makerom/titleid.c +++ b/makerom/titleid.c @@ -143,8 +143,7 @@ int SetPIDCategoryFromFlags(u16 *cat, char **CategoryFlags, u32 FlagNum) else if(strcmp(CategoryFlags[i],"CanSkipConvertJumpId") == 0) ret = SetPIDCategoryFromFlag(cat,PROGRAM_ID_CATEGORY_FLAG_CAN_SKIP_CONVERT_JUMP_ID,"CanSkipConvertJumpId"); - if(ret == PID_INVALID_CATEGORY) break; - + else { fprintf(stderr,"[ID ERROR] Invalid CategoryFlag: \"%s\"\n",CategoryFlags[i]); return PID_INVALID_CATEGORY; @@ -242,4 +241,4 @@ bool IsContents(u16 Category) bool IsAddOnContent(u16 Category) { return Category == PROGRAM_ID_CATEGORY_ADD_ON_CONTENTS; -} \ No newline at end of file +} From b2206ea17b9fc6af407fb59dbf46c6fa1a630ea1 Mon Sep 17 00:00:00 2001 From: luigoalma Date: Wed, 17 Feb 2016 19:15:08 +0000 Subject: [PATCH 170/317] Fix newline on cia_print() --- ctrtool/cia.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/cia.c b/ctrtool/cia.c index c2ba55bd..bf757557 100644 --- a/ctrtool/cia.c +++ b/ctrtool/cia.c @@ -299,7 +299,7 @@ void cia_print(cia_context* ctx) fprintf(stdout, "Version %04x\n", getle16(header->version)); fprintf(stdout, "Certificates offset: 0x%"PRIx64"\n", ctx->offsetcerts); fprintf(stdout, "Certificates size: 0x%x\n", ctx->sizecert); - fprintf(stdout, "Ticket offset: 0x%"PRIx64"n", ctx->offsettik); + fprintf(stdout, "Ticket offset: 0x%"PRIx64"\n", ctx->offsettik); fprintf(stdout, "Ticket size 0x%x\n", ctx->sizetik); fprintf(stdout, "TMD offset: 0x%"PRIx64"\n", ctx->offsettmd); fprintf(stdout, "TMD size: 0x%x\n", ctx->sizetmd); From cedbe6d149d726200f85c1cde49d282757dc4826 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 18 Feb 2016 19:50:07 +0800 Subject: [PATCH 171/317] Fixed array transfer bug. --- makerom/cia.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makerom/cia.c b/makerom/cia.c index 36b54d92..492181ac 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -155,7 +155,7 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) ciaset->ciaSections.content.buffer = usrset->common.workingFile.buffer; ciaset->ciaSections.content.size = usrset->common.workingFile.size; usrset->common.workingFile.buffer = NULL; - ciaset->ciaSections.content.size = 0; + usrset->common.workingFile.size = 0; ciaset->content.includeUpdateNcch = usrset->cia.includeUpdateNcch; ciaset->verbose = usrset->common.verbose; @@ -473,7 +473,7 @@ int GetSettingsFromSrl(cia_settings *ciaset) fprintf(stderr,"[CIA ERROR] Invalid TWL SRL File\n"); return FAILED_TO_IMPORT_FILE; } - + // Check if TWL SRL File if(u8_to_u16(&hdr->title_id[6],LE) != 0x0003){ fprintf(stderr,"[CIA ERROR] Invalid TWL SRL File\n"); From 9dc611bbbf05ff700654f636f7150b91146d6c9f Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 20 Mar 2016 11:34:50 +0800 Subject: [PATCH 172/317] Fix CIA content hash validation fail. --- ctrtool/tmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/tmd.c b/ctrtool/tmd.c index bcb687f1..95d23276 100644 --- a/ctrtool/tmd.c +++ b/ctrtool/tmd.c @@ -169,7 +169,7 @@ void tmd_print(tmd_context* ctx) fprintf(stdout, "\n"); fprintf(stdout, "Content size: %016"PRIx64"\n", getbe64(chunk->size)); - switch(ctx->content_hash_stat[getbe16(chunk->index)]) { + switch(ctx->content_hash_stat[i]) { 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; From cf2ba24d69f3cae1f06e91ea62fc7c2798e69c0f Mon Sep 17 00:00:00 2001 From: Yifan Lu Date: Thu, 24 Mar 2016 17:48:14 -0500 Subject: [PATCH 173/317] Added support for disabling padding between segments for code Fixed potential bug where code size is calculated by taking the page size of combined segments rather than the combined padded segment size. This could be a problem, for example, if two segments add up to require two pages of padding. Fixed potential bug where memory-size is used for codeDetails.rwSize; this counts .bss which is not in the file --- makerom/code.c | 21 ++++++++++++++------- makerom/ncch.c | 1 + makerom/ncch_build.h | 1 + makerom/user_settings.c | 10 ++++++++++ makerom/user_settings.h | 1 + 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/makerom/code.c b/makerom/code.c index 11124471..e7d60c4d 100644 --- a/makerom/code.c +++ b/makerom/code.c @@ -173,13 +173,20 @@ int CreateExeFsCode(elf_context *elf, ncch_settings *set) set->codeDetails.bssSize = rwdata.memSize - rwdata.fileSize; /* Allocating Buffer for ExeFs Code */ - u32 size = PageToSize(text.pageNum + rodata.pageNum + rwdata.pageNum); + bool noCodePadding = set->options.noCodePadding; + u32 size; + if (noCodePadding) { + size = text.fileSize + rodata.fileSize + rwdata.fileSize; + } + else { + size = PageToSize(text.pageNum + rodata.pageNum + rwdata.pageNum); + } u8 *code = calloc(1, size); /* Writing Code into Buffer */ - u8 *textPos = (code + PageToSize(0)); - u8 *rodataPos = (code + PageToSize(text.pageNum)); - u8 *rwdataPos = (code + PageToSize(text.pageNum + rodata.pageNum)); + u8 *textPos = code; + u8 *rodataPos = (textPos + (noCodePadding ? text.fileSize : PageToSize(text.pageNum))); + u8 *rwdataPos = (rodataPos + (noCodePadding ? rodata.fileSize : PageToSize(rodata.pageNum))); if (text.fileSize) memcpy(textPos, text.data, text.fileSize); if (rodata.fileSize) memcpy(rodataPos, rodata.data, rodata.fileSize); if (rwdata.fileSize) memcpy(rwdataPos, rwdata.data, rwdata.fileSize); @@ -204,15 +211,15 @@ int CreateExeFsCode(elf_context *elf, ncch_settings *set) /* Setting code_segment data and freeing original buffers */ set->codeDetails.textAddress = text.address; set->codeDetails.textMaxPages = text.pageNum; - set->codeDetails.textSize = text.memSize; + set->codeDetails.textSize = text.fileSize; set->codeDetails.roAddress = rodata.address; set->codeDetails.roMaxPages = rodata.pageNum; - set->codeDetails.roSize = rodata.memSize; + set->codeDetails.roSize = rodata.fileSize; set->codeDetails.rwAddress = rwdata.address; set->codeDetails.rwMaxPages = rwdata.pageNum; - set->codeDetails.rwSize = rwdata.memSize; + set->codeDetails.rwSize = rwdata.fileSize; if (set->rsfSet->SystemControlInfo.StackSize) set->codeDetails.stackSize = strtoul(set->rsfSet->SystemControlInfo.StackSize, NULL, 0); diff --git a/makerom/ncch.c b/makerom/ncch.c index e8d6412f..ccc2e3c5 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -177,6 +177,7 @@ int GetBasicOptions(ncch_settings *ncchset, user_settings *usrset) ncchset->options.IsCfa = (usrset->ncch.ncchType == CFA); ncchset->options.IsBuildingCodeSection = (usrset->ncch.elfPath != NULL); ncchset->options.UseRomFS = ((ncchset->rsfSet->RomFs.RootPath && strlen(ncchset->rsfSet->RomFs.RootPath) > 0) || usrset->ncch.romfsPath); + ncchset->options.noCodePadding = usrset->ncch.noCodePadding; ncchset->options.useSecCrypto = usrset->ncch.useSecCrypto; ncchset->options.keyXID = usrset->ncch.keyXID; diff --git a/makerom/ncch_build.h b/makerom/ncch_build.h index d97ab7b7..7aa90957 100644 --- a/makerom/ncch_build.h +++ b/makerom/ncch_build.h @@ -19,6 +19,7 @@ typedef struct bool IsCfa; bool IsBuildingCodeSection; bool UseRomFS; + bool noCodePadding; bool useSecCrypto; u8 keyXID; diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 3702316b..f1a864e8 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -92,6 +92,7 @@ void SetDefaults(user_settings *set) set->ncch.includeExefsLogo = false; set->common.outFormat = NCCH; set->ncch.ncchType = format_not_set; + set->ncch.noCodePadding = false; // RSF Settings clrmem(&set->common.rsfSet, sizeof(rsf_settings)); @@ -352,6 +353,14 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) set->ncch.ncchType |= CFA; return 1; } + else if (strcmp(argv[i], "-nocodepadding") == 0) { + if (ParamNum) { + PrintNoNeedParam(argv[i]); + return USR_BAD_ARG; + } + set->ncch.noCodePadding = true; + return 1; + } // Ncch Rebuild Options else if (strcmp(argv[i], "-code") == 0) { @@ -941,6 +950,7 @@ void DisplayExtendedHelp(char *app_name) printf(" -logo Logo file (Overrides \"BasicInfo/Logo\" in RSF)\n"); printf(" -desc : Specify Access Descriptor template\n"); printf(" -exefslogo Include Logo in ExeFS (Required for usage on <5.0 systems)\n"); + printf(" -nocodepadding For building sysmodules, do not pad .code segments\n"); printf("NCCH REBUILD OPTIONS:\n"); printf(" -code Decompressed ExeFS \".code\"\n"); printf(" -exheader Exheader template\n"); diff --git a/makerom/user_settings.h b/makerom/user_settings.h index ea0f008d..ba6af79e 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -267,6 +267,7 @@ typedef struct char *exheaderPath; // for .code details char *plainRegionPath; // prebuilt Plain Region char *romfsPath; // Prebuild _cleartext_ romfs binary + bool noCodePadding; // do not pad code.bin for sysmodule bool useSecCrypto; u8 keyXID; From 65e6974494804ae3137b4f02641f17f5652542ab Mon Sep 17 00:00:00 2001 From: Timothy Redaelli Date: Wed, 13 Apr 2016 18:57:45 +0200 Subject: [PATCH 174/317] [makerom] Fix parsing of -content option --- makerom/user_settings.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index f1a864e8..6d19e059 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -614,7 +614,15 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - char *pos = strstr(argv[i + 1], ":"); + int count = 0; + char *pos = argv[i + 1]; + while ((pos = strstr(pos + 1, ":"))) + count++; + + pos = argv[i + 1]; + while (count-- > 1) + pos = strstr(pos + 1, ":"); + if (!pos || strlen(pos) < 2) { fprintf(stderr, "[SETTING ERROR] Bad argument '%s %s', correct format:\n", argv[i], argv[i + 1]); fprintf(stderr, " %s :\n", argv[i]); From d3be7adce15c878a9f5fdb6f6113cff327307448 Mon Sep 17 00:00:00 2001 From: Steven Smith Date: Wed, 25 May 2016 18:54:47 -0700 Subject: [PATCH 175/317] Add seed DB access permission. --- ctrtool/exheader.c | 3 +++ makerom/exheader.c | 2 ++ makerom/exheader.h | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index e9a6af1d..57844790 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -464,6 +464,9 @@ char* exheader_print_accessinfobit(u32 bit, char *str) case 20 : sprintf(str,"Category HomeMenu"); break; + case 21 : + sprintf(str,"Seed DB"); + break; default : sprintf(str,"Bit %d (unknown)",bit); break; diff --git a/makerom/exheader.c b/makerom/exheader.c index c132a88b..fc0777cd 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -490,6 +490,8 @@ int SetARM11StorageInfoFsAccessInfo(exhdr_ARM11SystemLocalCapabilities *arm11, r accessInfo |= fsaccess_SHELL; else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"CategoryHomeMenu") == 0) accessInfo |= fsaccess_CATEGORY_HOME_MENU; + else if(strcmp(rsf->AccessControlInfo.FileSystemAccess[i],"SeedDB") == 0) + accessInfo |= fsaccess_SEEDDB; else{ fprintf(stderr,"[EXHEADER ERROR] Invalid FileSystemAccess Name: \"%s\"\n",rsf->AccessControlInfo.FileSystemAccess[i]); return EXHDR_BAD_RSF_OPT; diff --git a/makerom/exheader.h b/makerom/exheader.h index d3ef983b..8c72ff84 100644 --- a/makerom/exheader.h +++ b/makerom/exheader.h @@ -88,6 +88,7 @@ typedef enum fsaccess_SHOP = (1 << 18), // 0x00040000 probably used by eshop fsaccess_SHELL = (1 << 19), // 0x00080000 reference to "Nintendo [User Interface] Shell" (NS)? fsaccess_CATEGORY_HOME_MENU = (1 << 20), // 0x00100000 used by homemenu + fsaccess_SEEDDB = (1 << 21), // 0x00200000 seeddb access } file_system_access; typedef enum @@ -243,4 +244,4 @@ int CheckAccessDescSignature(access_descriptor *acexDesc, keys_struct *keys); int GetSaveDataSizeFromString(u64 *out, char *string, char *moduleName); int GetRemasterVersion_rsf(u16 *RemasterVersion, user_settings *usrset); -void ErrorParamNotFound(char *string); \ No newline at end of file +void ErrorParamNotFound(char *string); From de5c4e1b2aaa7e2e9c020c258c4918ad64d88824 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 14 Jun 2016 18:17:59 +0800 Subject: [PATCH 176/317] License both ctrtool and makerom under MIT License. --- ctrtool/LICENSE | 22 ++++++++++++++++++++++ makerom/LICENSE | 23 +++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 ctrtool/LICENSE create mode 100644 makerom/LICENSE diff --git a/ctrtool/LICENSE b/ctrtool/LICENSE new file mode 100644 index 00000000..37685952 --- /dev/null +++ b/ctrtool/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2012 neimod +Copyright (c) 2014 3DSGuy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/makerom/LICENSE b/makerom/LICENSE new file mode 100644 index 00000000..61db3e0a --- /dev/null +++ b/makerom/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2014 3DSGuy +Copyright (c) 2014 applestash +Copyright (c) 2015-2016 Jakcron + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file From 01cd4cba7bf3c83001f393d357cfde57dd390514 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 14 Jun 2016 20:35:05 +0800 Subject: [PATCH 177/317] [ctrtool] Improved efficiency of aes-ctr operations. --- ctrtool/ctr.c | 46 +++++++++++++++++++++++------------- ctrtool/ctr.h | 6 +++-- ctrtool/exefs.c | 14 +++++++---- ctrtool/exheader.c | 3 ++- ctrtool/ivfc.c | 59 ++++++++++++++++++++++++++++++++++------------ ctrtool/ivfc.h | 1 - ctrtool/ncch.c | 3 ++- ctrtool/romfs.c | 8 +++++-- 8 files changed, 96 insertions(+), 44 deletions(-) diff --git a/ctrtool/ctr.c b/ctrtool/ctr.c index e5198f70..54cfc0fa 100644 --- a/ctrtool/ctr.c +++ b/ctrtool/ctr.c @@ -4,6 +4,7 @@ #include #include "ctr.h" +#include "utils.h" void ctr_set_iv( ctr_aes_context* ctx, @@ -15,23 +16,30 @@ void ctr_set_iv( ctr_aes_context* ctx, void ctr_add_counter( ctr_aes_context* ctx, u32 block_num ) { - u32 i, j; - for (i = 0; i < block_num; i++) { - for (j = 0x10; j > 0; j--) { - // increment u8 by 1 - ctx->ctr[j - 1]++; - - // if it didn't overflow to 0, then we can exit now - if (ctx->ctr[j - 1]) - break; - - // if we reach here, the next u8 needs to be incremented - - // Loop to beginning back if needed - if (j == 1) - j = 0x10; + u32 ctr[4]; + ctr[3] = getbe32(&ctx->ctr[0]); + ctr[2] = getbe32(&ctx->ctr[4]); + ctr[1] = getbe32(&ctx->ctr[8]); + ctr[0] = getbe32(&ctx->ctr[12]); + + for (u32 i = 0; i < 4; i++) { + u64 total = ctr[i] + block_num; + // if there wasn't a wrap around, add the two together and exit + if (total <= 0xffffffff) { + ctr[i] += block_num; + break; } + + // add the difference + ctr[i] = (u32)(total - 0x100000000); + // carry to next word + block_num = (u32)(total >> 32); } + + putbe32(ctx->ctr + 0x00, ctr[3]); + putbe32(ctx->ctr + 0x04, ctr[2]); + putbe32(ctx->ctr + 0x08, ctr[1]); + putbe32(ctx->ctr + 0x0C, ctr[0]); } void ctr_set_counter( ctr_aes_context* ctx, @@ -41,11 +49,15 @@ void ctr_set_counter( ctr_aes_context* ctx, } +void ctr_init_key(ctr_aes_context* ctx, + u8 key[16]) +{ + aes_setkey_enc(&ctx->aes, key, 128); +} + 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); } diff --git a/ctrtool/ctr.h b/ctrtool/ctr.h index 66caff7b..48b61e55 100644 --- a/ctrtool/ctr.h +++ b/ctrtool/ctr.h @@ -61,10 +61,12 @@ void ctr_add_counter( ctr_aes_context* ctx, void ctr_set_counter( ctr_aes_context* ctx, u8 ctr[16] ); +void ctr_init_key(ctr_aes_context* ctx, + u8 key[16]); + void ctr_init_counter( ctr_aes_context* ctx, - u8 key[16], - u8 ctr[16] ); + u8 ctr[16] ); void ctr_crypt_counter_block( ctr_aes_context* ctx, diff --git a/ctrtool/exefs.c b/ctrtool/exefs.c index 36180438..e732e4cb 100644 --- a/ctrtool/exefs.c +++ b/ctrtool/exefs.c @@ -124,7 +124,8 @@ void exefs_save(exefs_context* ctx, u32 index, u32 flags) } fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); - ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); + ctr_init_key(&ctx->aes, ctx->key); + ctr_init_counter(&ctx->aes, ctx->counter); ctr_add_counter(&ctx->aes, offset / 0x10); if (index == 0 && (ctx->compressedflag || (flags & DecompressCodeFlag)) && ((flags & RawFlag) == 0)) @@ -210,10 +211,12 @@ void exefs_read_header(exefs_context* ctx, u32 flags) fseeko64(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) + if (ctx->encrypted) { + ctr_init_key(&ctx->aes, ctx->key); + ctr_init_counter(&ctx->aes, ctx->counter); ctr_crypt_counter(&ctx->aes, (u8*)&ctx->header, (u8*)&ctx->header, sizeof(exefs_header)); + } + } void exefs_calculate_hash(exefs_context* ctx, u8 hash[32]) @@ -269,7 +272,8 @@ int exefs_verify(exefs_context* ctx, u32 index, u32 flags) return 0; fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); - ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); + ctr_init_key(&ctx->aes, ctx->key); + ctr_init_counter(&ctx->aes, ctx->counter); ctr_add_counter(&ctx->aes, offset / 0x10); ctr_sha_256_init(&ctx->sha); diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index 57844790..4a96f0f1 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -93,7 +93,8 @@ void exheader_read(exheader_context* ctx, u32 actions) fseeko64(ctx->file, ctx->offset, SEEK_SET); fread(&ctx->header, 1, sizeof(exheader_header), ctx->file); - ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); + ctr_init_key(&ctx->aes, ctx->key); + ctr_init_counter(&ctx->aes, ctx->counter); if (ctx->encrypted) ctr_crypt_counter(&ctx->aes, (u8*)&ctx->header, (u8*)&ctx->header, sizeof(exheader_header)); diff --git a/ctrtool/ivfc.c b/ctrtool/ivfc.c index 041afda3..4a77a676 100644 --- a/ctrtool/ivfc.c +++ b/ctrtool/ivfc.c @@ -38,7 +38,7 @@ void ivfc_set_encrypted(ivfc_context* ctx, u32 encrypted) void ivfc_set_key(ivfc_context* ctx, u8 key[16]) { - memcpy(ctx->key, key, 16); + ctr_init_key(&ctx->aes, key); } void ivfc_set_counter(ivfc_context* ctx, u8 counter[16]) @@ -50,8 +50,14 @@ void ivfc_fseek(ivfc_context* ctx, u64 offset) { u64 data_pos = offset - ctx->offset; fseeko64(ctx->file, offset, SEEK_SET); - ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); - ctr_add_counter(&ctx->aes, (u32) (data_pos / 0x10)); + + if (ctx->encrypted) { + //printf("start fseek encrypted prep\n"); + ctr_init_counter(&ctx->aes, ctx->counter); + //printf("middle fseek encrypted prep\n"); + ctr_add_counter(&ctx->aes, (u32)(data_pos / 0x10)); + //printf("finish fseek encrypted prep\n"); + } } size_t ivfc_fread(ivfc_context* ctx, void* buffer, size_t size, size_t count) @@ -124,31 +130,54 @@ void ivfc_verify(ivfc_context* ctx, u32 flags) level->hashcheck = Fail; } - for(i=0; ilevelcount; i++) + // Import IVFC level hashes + uint8_t *levelhash[IVFC_MAX_LEVEL] = { NULL }; + + for (i=0; ilevelcount; i++) { - ivfc_level* level = ctx->level + i; + blockcount = (u32)(ctx->level[i].datasize / ctx->level[i].hashblocksize); + u32 read_size = align(blockcount * 0x20, ctx->level[i].hashblocksize); + levelhash[i] = malloc(read_size); + ivfc_read(ctx, ctx->level[i].hashoffset, read_size, levelhash[i]); + } - blockcount = (u32) (level->datasize / level->hashblocksize); - if (level->datasize % level->hashblocksize != 0) + // Verify blocks + for (i=0; ilevelcount; i++) + { + blockcount = (u32) (ctx->level[i].datasize / ctx->level[i].hashblocksize); + if (ctx->level[i].datasize % ctx->level[i].hashblocksize != 0) { fprintf(stderr, "Error, IVFC block size mismatch\n"); return; } - level->hashcheck = Good; + ctx->level[i].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; + // a hash level + if (i < 2) { + ctr_sha_256(levelhash[i+1] + ctx->level[i].hashblocksize * j, ctx->level[i].hashblocksize, calchash); + } + // a data level + else { + ivfc_read(ctx, ctx->level[i].dataoffset + j * ctx->level[i].hashblocksize, ctx->level[i].hashblocksize, ctx->buffer); + ctr_sha_256(ctx->buffer, (u32)ctx->level[i].hashblocksize, calchash); + } + + if (memcmp(calchash, levelhash[i] + 0x20 * j, 0x20) != 0) { + ctx->level[i].hashcheck = Fail; + } + } } + + // Free level hashes + for (int i = 0; i < 3; i++) { + free(levelhash[i]); + } } void ivfc_read(ivfc_context* ctx, u64 offset, u64 size, u8* buffer) diff --git a/ctrtool/ivfc.h b/ctrtool/ivfc.h index 9662f45e..67b5c7e6 100644 --- a/ctrtool/ivfc.h +++ b/ctrtool/ivfc.h @@ -45,7 +45,6 @@ typedef struct u64 size; settings* usersettings; u8 counter[16]; - u8 key[16]; ctr_aes_context aes; int encrypted; diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 8d2a9ff6..b553b600 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -135,7 +135,8 @@ int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags) ctx->extractflags = flags; fseeko64(ctx->file, offset, SEEK_SET); ncch_get_counter(ctx, counter, type); - ctr_init_counter(&ctx->aes, ctx->key, counter); + ctr_init_key(&ctx->aes, ctx->key); + ctr_init_counter(&ctx->aes, counter); return 1; diff --git a/ctrtool/romfs.c b/ctrtool/romfs.c index f8c9ffb0..6f004674 100644 --- a/ctrtool/romfs.c +++ b/ctrtool/romfs.c @@ -38,6 +38,7 @@ void romfs_set_encrypted(romfs_context* ctx, u32 encrypted) void romfs_set_key(romfs_context* ctx, u8 key[16]) { memcpy(ctx->key, key, 16); + ctr_init_key(&ctx->aes, key); } void romfs_set_counter(romfs_context* ctx, u8 counter[16]) @@ -49,8 +50,11 @@ void romfs_fseek(romfs_context* ctx, u64 offset) { u64 data_pos = offset - ctx->offset; fseeko64(ctx->file, offset, SEEK_SET); - ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); - ctr_add_counter(&ctx->aes, (u32) (data_pos / 0x10)); + + if (ctx->encrypted) { + ctr_init_counter(&ctx->aes, ctx->counter); + ctr_add_counter(&ctx->aes, (u32)(data_pos / 0x10)); + } } size_t romfs_fread(romfs_context* ctx, void* buffer, size_t size, size_t count) From b85775479ba737ff824b4762e20729af74274639 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 27 Jun 2016 15:59:03 +0800 Subject: [PATCH 178/317] [makerom] Warn user for not specifying any services, instead of erroring out. --- .gitignore | 3 ++- makerom/exheader.c | 8 ++++++-- makerom/exheader.h | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index c990f923..39b344f0 100644 --- a/.gitignore +++ b/.gitignore @@ -226,4 +226,5 @@ pip-log.txt *.cia #exec -*.exe \ No newline at end of file +*.exe +*.db diff --git a/makerom/exheader.c b/makerom/exheader.c index fc0777cd..dbb183ec 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -617,8 +617,7 @@ int SetARM11ServiceAccessControl(exhdr_ARM11SystemLocalCapabilities *arm11, rsf_ } } else{ - ErrorParamNotFound("AccessControlInfo/ServiceAccessControl"); - return EXHDR_BAD_RSF_OPT; + WarnParamNotFound("AccessControlInfo/ServiceAccessControl"); } return 0; } @@ -1164,6 +1163,11 @@ void ErrorParamNotFound(char *string) fprintf(stderr,"[EXHEADER ERROR] Parameter Not Found: \"%s\"\n",string); } +void WarnParamNotFound(char *string) +{ + fprintf(stderr, "[EXHEADER WARNING] Parameter Not Found: \"%s\"\n", string); +} + /* ExHeader Binary Print Functions */ void exhdr_Print_ServiceAccessControl(extended_hdr *hdr) { diff --git a/makerom/exheader.h b/makerom/exheader.h index 8c72ff84..67b0662e 100644 --- a/makerom/exheader.h +++ b/makerom/exheader.h @@ -245,3 +245,4 @@ int GetSaveDataSizeFromString(u64 *out, char *string, char *moduleName); int GetRemasterVersion_rsf(u16 *RemasterVersion, user_settings *usrset); void ErrorParamNotFound(char *string); +void WarnParamNotFound(char *string); \ No newline at end of file From d24dda0bc02a816887e2f34c3790d7a401f8bd98 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 9 Jul 2016 16:38:05 +0800 Subject: [PATCH 179/317] Fixes issue #39 --- ctrtool/main.c | 2 +- ctrtool/settings.c | 16 ++++++++-------- ctrtool/settings.h | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ctrtool/main.c b/ctrtool/main.c index 8282c9b3..dd8ef88f 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -224,7 +224,7 @@ int main(int argc, char* argv[]) 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 7: settings_set_meta_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; diff --git a/ctrtool/settings.c b/ctrtool/settings.c index 7888d545..ddbf51bc 100644 --- a/ctrtool/settings.c +++ b/ctrtool/settings.c @@ -104,18 +104,18 @@ filepath* settings_get_tmd_path(settings* usersettings) return 0; } -filepath* settings_get_meta_path(settings* usersettings) +filepath* settings_get_content_path(settings* usersettings) { if (usersettings) - return &usersettings->metapath; + return &usersettings->contentpath; else return 0; } -filepath* settings_get_content_path(settings* usersettings) +filepath* settings_get_meta_path(settings* usersettings) { if (usersettings) - return &usersettings->contentpath; + return &usersettings->metapath; else return 0; } @@ -243,14 +243,14 @@ 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) +void settings_set_content_path(settings* usersettings, const char* path) { - filepath_set(&usersettings->metapath, path); + filepath_set(&usersettings->contentpath, path); } -void settings_set_content_path(settings* usersettings, const char* path) +void settings_set_meta_path(settings* usersettings, const char* path) { - filepath_set(&usersettings->contentpath, path); + filepath_set(&usersettings->metapath, path); } void settings_set_exefs_dir_path(settings* usersettings, const char* path) diff --git a/ctrtool/settings.h b/ctrtool/settings.h index 30b42d22..2822b93a 100644 --- a/ctrtool/settings.h +++ b/ctrtool/settings.h @@ -38,8 +38,8 @@ filepath* settings_get_logo_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_meta_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); @@ -62,8 +62,8 @@ void settings_set_logo_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_meta_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); From 02ee3b99c1e4e0490d050811220a43f42c35d64d Mon Sep 17 00:00:00 2001 From: infinicore Date: Wed, 20 Jul 2016 14:12:56 +0000 Subject: [PATCH 180/317] Fix build with gcc on some platforms. --- ctrtool/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 0f2361fa..c316463c 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -5,7 +5,7 @@ OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) $(foreach # Compiler Settings OUTPUT = ctrtool CXXFLAGS = -I. -CFLAGS = -O2 -flto -Wall -Wno-unused-variable -Wno-unused-result -I. +CFLAGS = -O2 -flto -Wall -Wno-unused-variable -Wno-unused-result -I. -std=c99 CC = gcc CXX = g++ ifeq ($(OS),Windows_NT) From 7b384c62139586ae2eee02d06cdc3dce4988c18b Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 6 Oct 2016 14:12:22 +0800 Subject: [PATCH 181/317] [makerom] Do not include logo as a ncch region if it is included in the ExeFS archive (toggled by cmd arg -exefslogo) --- makerom/ncch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/ncch.c b/makerom/ncch.c index ccc2e3c5..796a9cf6 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -383,7 +383,7 @@ int SetupNcch(ncch_settings *set, romfs_buildctx *romfs) else acexSize = 0; - if(set->sections.logo.size){ + if(set->sections.logo.size && set->options.IncludeExeFsLogo == false){ logoSize = set->sections.logo.size; logoOffset = align(ncchSize,set->options.blockSize); ncchSize = logoOffset + logoSize; From dc81220cf60f7d079c359003a48b995c10b1fed9 Mon Sep 17 00:00:00 2001 From: profi200 Date: Sat, 7 Jan 2017 21:31:05 +0100 Subject: [PATCH 182/317] Updated FIRM code in ctrtool according to 3dbrew/boot9. Removed -flto flag since this can cause problems. --- ctrtool/Makefile | 2 +- ctrtool/firm.c | 9 ++++++--- ctrtool/firm.h | 6 +++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index c316463c..7a534c98 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -5,7 +5,7 @@ OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) $(foreach # Compiler Settings OUTPUT = ctrtool CXXFLAGS = -I. -CFLAGS = -O2 -flto -Wall -Wno-unused-variable -Wno-unused-result -I. -std=c99 +CFLAGS = -O2 -Wall -Wno-unused-variable -Wno-unused-result -I. -std=c99 CC = gcc CXX = g++ ifeq ($(OS),Windows_NT) diff --git a/ctrtool/firm.c b/ctrtool/firm.c index e865cc0e..491f4a7e 100644 --- a/ctrtool/firm.c +++ b/ctrtool/firm.c @@ -206,9 +206,10 @@ void firm_print(firm_context* ctx) { u32 i; u32 address; - u32 type; + u32 copyMethod; u32 offset; u32 size; + u32 priority = getle32(ctx->header.priority); u32 entrypointarm11 = getle32(ctx->header.entrypointarm11); u32 entrypointarm9 = getle32(ctx->header.entrypointarm9); @@ -221,6 +222,7 @@ void firm_print(firm_context* ctx) memdump(stdout, "Signature (FAIL): ", ctx->header.signature, 0x100); fprintf(stdout, "\n"); + fprintf(stdout, "Priority: %u\n", priority); fprintf(stdout, "Entrypoint ARM9: 0x%08X\n", entrypointarm9); fprintf(stdout, "Entrypoint ARM11: 0x%08X\n", entrypointarm11); fprintf(stdout, "\n"); @@ -234,12 +236,13 @@ void firm_print(firm_context* ctx) offset = getle32(section->offset); size = getle32(section->size); address = getle32(section->address); - type = getle32(section->type); + copyMethod = getle32(section->copyMethod); if (size) { fprintf(stdout, "Section %d \n", i); - fprintf(stdout, " Type: %s\n", type==0? "ARM9" : type==1? "ARM11" : "UNKNOWN"); + fprintf(stdout, " Copy Method: %s\n", copyMethod==0 ? "NDMA" : copyMethod==1 ? "XDMA" : + copyMethod==2 ? "memcpy" : "UNKNOWN"); fprintf(stdout, " Address: 0x%08X\n", address); fprintf(stdout, " Offset: 0x%08X\n", offset); fprintf(stdout, " Size: 0x%08X\n", size); diff --git a/ctrtool/firm.h b/ctrtool/firm.h index 29af98f4..5d1ebbcf 100644 --- a/ctrtool/firm.h +++ b/ctrtool/firm.h @@ -13,7 +13,7 @@ typedef struct u8 offset[4]; u8 address[4]; u8 size[4]; - u8 type[4]; + u8 copyMethod[4]; u8 hash[32]; } firm_sectionheader; @@ -23,10 +23,10 @@ typedef struct typedef struct { u8 magic[4]; - u8 reserved1[4]; + u8 priority[4]; u8 entrypointarm11[4]; u8 entrypointarm9[4]; - u8 reserved2[0x30]; + u8 reserved1[0x30]; firm_sectionheader section[4]; u8 signature[0x100]; } firm_header; From dfabf8daa2d19d73271ce927740256a216556ff7 Mon Sep 17 00:00:00 2001 From: profi200 Date: Sat, 7 Jan 2017 22:01:23 +0100 Subject: [PATCH 183/317] Added unique ID range check in makerom. Removed -flto flag since this can cause problems. --- makerom/Makefile | 2 +- makerom/titleid.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/makerom/Makefile b/makerom/Makefile index 9af706b3..f4f6faf6 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -3,7 +3,7 @@ SRC_DIR = . polarssl libyaml OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) # Compiler Settings -CFLAGS = --std=c99 -O2 -flto -Wall -Wno-unused-value -Wno-unused-result -I. +CFLAGS = --std=c99 -O2 -Wall -Wno-unused-value -Wno-unused-result -I. CC = gcc ifeq ($(OS),Windows_NT) #Windows Build CFG diff --git a/makerom/titleid.c b/makerom/titleid.c index 251282be..aea6a158 100644 --- a/makerom/titleid.c +++ b/makerom/titleid.c @@ -61,6 +61,11 @@ int GetProgramID(u64 *dest, rsf_settings *rsf, bool IsForExheader) else uniqueId = DEFAULT_UNIQUE_ID; + if(uniqueId & 0xFFF00000u){ + fprintf(stderr,"[ID ERROR] Unique ID is out of range.\n"); + return PID_BAD_RSF_SET; + } + // Getting Variation if(SetTitleVariation(&variation,category,rsf) == PID_INVALID_VARIATION) return PID_BAD_RSF_SET; From cf1a6fb41ebd89502ac917122d4c4d37de9fd683 Mon Sep 17 00:00:00 2001 From: profi200 Date: Wed, 18 Jan 2017 23:13:54 +0100 Subject: [PATCH 184/317] Fix a few warnings (excluding polarssl ones). --- makerom/blz.c | 2 +- makerom/utils.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/makerom/blz.c b/makerom/blz.c index 1b64a071..9c63b3af 100644 --- a/makerom/blz.c +++ b/makerom/blz.c @@ -182,7 +182,7 @@ u8* BLZ_Encode(char *filename, u32* pak_len, int mode) { /*----------------------------------------------------------------------------*/ u8 *BLZ_Code(u8 *raw_buffer, int raw_len, u32 *new_len, int best) { - u8 *pak_buffer, *pak, *raw, *raw_end, *flg, *tmp; + u8 *pak_buffer, *pak, *raw, *raw_end, *flg = NULL, *tmp; u32 pak_len, inc_len, hdr_len, enc_len, len, pos, max; u32 len_best, pos_best, len_next, pos_next, len_post, pos_post; u32 pak_tmp, raw_tmp; diff --git a/makerom/utils.c b/makerom/utils.c index 999433e7..05e4d3c1 100644 --- a/makerom/utils.c +++ b/makerom/utils.c @@ -309,7 +309,7 @@ int fseek_64(FILE *fp, u64 file_pos) //Data Size conversion u16 u8_to_u16(const u8 *value, u8 endianness) { - u16 new_value; + u16 new_value = 0; switch(endianness){ case(BE): new_value = (value[1]<<0) | (value[0]<<8); break; case(LE): new_value = (value[0]<<0) | (value[1]<<8); break; @@ -319,7 +319,7 @@ u16 u8_to_u16(const u8 *value, u8 endianness) u32 u8_to_u32(const u8 *value, u8 endianness) { - u32 new_value; + u32 new_value = 0; switch(endianness){ case(BE): new_value = (value[3]<<0) | (value[2]<<8) | (value[1]<<16) | (value[0]<<24); break; case(LE): new_value = (value[0]<<0) | (value[1]<<8) | (value[2]<<16) | (value[3]<<24); break; From 70e0d1280ff761c41c4aba41b0bae22bc27603aa Mon Sep 17 00:00:00 2001 From: profi200 Date: Thu, 19 Jan 2017 00:47:58 +0100 Subject: [PATCH 185/317] Fixed more warnings. --- makerom/Makefile | 4 +--- makerom/blz.c | 2 +- makerom/ncch.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/makerom/Makefile b/makerom/Makefile index f4f6faf6..fd0832ff 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -3,7 +3,7 @@ SRC_DIR = . polarssl libyaml OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) # Compiler Settings -CFLAGS = --std=c99 -O2 -Wall -Wno-unused-value -Wno-unused-result -I. +CFLAGS = --std=gnu99 -O2 -Wall -Wno-unused-value -Wno-unused-result -I. CC = gcc ifeq ($(OS),Windows_NT) #Windows Build CFG @@ -13,12 +13,10 @@ else UNAME_S := $(shell uname -s) ifeq ($(UNAME_S),Darwin) # OS X - CFLAGS += LIBS += -liconv else # Linux CFLAGS += -Wno-unused-but-set-variable - LIBS += endif endif diff --git a/makerom/blz.c b/makerom/blz.c index 9c63b3af..7f4622d2 100644 --- a/makerom/blz.c +++ b/makerom/blz.c @@ -184,7 +184,7 @@ u8* BLZ_Encode(char *filename, u32* pak_len, int mode) { u8 *BLZ_Code(u8 *raw_buffer, int raw_len, u32 *new_len, int best) { u8 *pak_buffer, *pak, *raw, *raw_end, *flg = NULL, *tmp; u32 pak_len, inc_len, hdr_len, enc_len, len, pos, max; - u32 len_best, pos_best, len_next, pos_next, len_post, pos_post; + u32 len_best, pos_best = 0, len_next, pos_next, len_post, pos_post; u32 pak_tmp, raw_tmp; u8 mask; diff --git a/makerom/ncch.c b/makerom/ncch.c index 796a9cf6..8621b126 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -361,7 +361,7 @@ int SetupNcch(ncch_settings *set, romfs_buildctx *romfs) { u64 ncchSize = 0; u64 exhdrSize,acexSize,logoSize,plnRgnSize,exefsSize,romfsSize; - u64 exhdrOffset,acexOffset,logoOffset,plnRgnOffset,exefsOffset,romfsOffset; + u64 exhdrOffset,acexOffset = 0,logoOffset,plnRgnOffset,exefsOffset,romfsOffset; u32 exefsHashSize,romfsHashSize; ncchSize += sizeof(ncch_hdr); // Sig+Hdr From 90dca52c8d1f81fd3f7a938db22fdd2c56540a83 Mon Sep 17 00:00:00 2001 From: "James Pelster \"CaptainSwag101" Date: Sat, 21 Jan 2017 16:34:59 -0800 Subject: [PATCH 186/317] Edited Makefile --- ctrtool/Makefile | 4 ++++ ctrtool/ctrtool | Bin 0 -> 257872 bytes makerom/Makefile | 6 +++++- makerom/makerom | Bin 0 -> 429576 bytes 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100755 ctrtool/ctrtool create mode 100755 makerom/makerom diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 7a534c98..96d1c153 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -28,5 +28,9 @@ endif main: $(OBJS) $(CXX) -o $(OUTPUT) $(LIBS) $(OBJS) +install: $(OUTPUT) + @cp ./$(OUTPUT) -t $(DEVKITARM)/bin/ + @echo "Installed." + clean: rm -rf $(OUTPUT) $(OBJS) diff --git a/ctrtool/ctrtool b/ctrtool/ctrtool new file mode 100755 index 0000000000000000000000000000000000000000..c5495b63ed8f9d066f82f21a108cd71985510b4b GIT binary patch literal 257872 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 )~04~%X>mM<3kO+x37!IY{+v$*TIHCc-L`}6% zu)&qRy?h)Yorsf5mRS?*sdW2D9Em1}m_lB0-w`!7pq@UN$&n*I1C@1mDM&=20XpTN zzFy(@Bs`X^Q3=%2u>)~Hvo2{PJQL5_T@2V4ctO?p{JeH_E#qk_$}zyYKE%rbtf>5D z2o90uG`X6OIZKOi%op2*zB%yU*fnu!alx+vxOE0Yy3js=Z$S^o&TQhFM>&(>aWPrC z5`M~KznoP$izQoMSL9v{AEpnQJ80r?)x#W&Ht*MEIRx_gw6rhgM2sZ7;El}a2o}xv zIa-j6-;u}@p3WW0xo?G(65KzY;%ABr$yIPfN@tbiu$a(#CM13-pQ8}kzML&S zK5UT_pxwPzFoT{fWl#zE-y_06XciGq)^6NwtS<;ZL zS(8AcSt2>Z``=s0agY(KR*x}un>bNP$BXrdoO;}uNS$z^>C3FZ%*K`?Jo+3rH>Vw% zvMj!u#1>oThcR9bYx>($QGMOU6> z~%8mJ;+Zu;E!xK(uGVW=Vs;7rhN^~_2uFf;d)CGy!3w+GB~H`YD6(M)!W z72i0ud?1Q_TXh3X?3-)vZL)Yvp01ZAB!&=AD|A9oCOaM3*hR^T0p7V+44$FJ?*+?y zUXHb81(%gS2V9)Z%dB>1_2gfCJHwoLWQJ^7e9Fm7ZW`wxi zveb4$1pfKZE9iW>%VP&F6?S7yZNT#LYC{W7^D3cP#8Z1+yZkff#6t9)pC0-a7wx15 zWk1vBA@Xt|yFKMqP73hT9-4epi-t_3R~q1LQ}zor&?>VN%1dh9+?AEp(o71Qyji`; zJAAx)lO;w2MOhs!;x&iNC+v@#P$nB6jw$k+GjxKXn--*I*of-VN&l$tWh5%bHTZIN z6Qv06E)UOGyunC!i_W~z_c`*|9#31Us^qM}vN*pX5NY?UgqqLFonmv>OY$zGdAGx5 zIAD*lRl;!>XEck=*7J9(BB^+U59!jmX7Y!l@=3at{BovBg#_aG3n*753cM0Aefms~wLoq#PL*XDOQ z`6Ch0RE9q0;_>uFsktba7hcLG<)tC+3a+Qui@?jn*}_=OvRmWyi7^?fPF*-TD{(ex z(x%)=kX$uHTGZ$88RG0%BCFsZPTW{7NRFYCZRVhtiO-{5#`cp(TAQuBR72F2&r#|C z=eI^(&BjtY&l(x~gnUqknoCPbXGg(%*v21QtL8mNX{33nkf;1SD&cCg^MNdX(>vrC zM9%o~Nl!eC?2dKro`n#wAb2ek%=d~+z_S6wJ^-Bu<&P)LT})@6sZ;nX4$OgH@T9xQ z&}BICx%X_~S*g_)U|g91MUGPqY7;Rwak*LMt!A&)a37KY=YKr|`PZt>9zNC5)fRW0 z2XA$;`pKs_veR_#6iH*%chWECv@naqJBt`64lxOO72m0hEQ70;(kBaC>*YM-!%<96 z)Ltw%*V}if-aF4Z%Vw)0y{3!iJG#?b<8tpbzi9awNhPg{c!EA%NpF8l&(Oyb^ohQ; zSkcx^pX!)S;eZN$lAZgC_BCV;QSLhHgcM zhLTkw-d;s2As>XOh;-tspPlWL*E|u^FugU=%dh3*tCzw;M7!<<6fewk0it{8Qv9`;R|=10}t`b7#Voe@(G1 z#Mc1ebiT}YXU+F;6}2q-%r^aX>TLaoi*0(ctYPHej*O!TV>y}pUt=P=PNV<%Pc4}8 zH;=LD$+9+s(0o5$K>7!t#Wj!CH|W<+SN$*B-Ilb=xKebTt2d|qrW|u$Md>?S`OC)I zlA{bCjvkOF%i{WTJx%=qTWjeD%hd22ME*hK9|ECYey08BBmW%GCCAzFD>+VqGGyc% z=P~7*{1+3(GU591)i=S$U2c~7uDU*HUCnR4e~O9bor|JJa=zr5q&rc6a^7GRb@t0?w&HW{!-qV$T82N|4 zVE(^QUY4IJe>?JT{}=fwi(K!}zh-PPFoOIe$ZtB<=(nl2(W4J3kGuSlW3>Gv$7qcV z9rNbP%FMi! z+Ok^l_oAC;Dbic-%~0-rwD|8|bNQ7%+~lwJ;Y`byxsRvcrff;TKgn&1?r#Wz`yPdl pPep}t1L{A5KhSY7p*Pe2MsCJ_@%YYHe(z<9en{UlcVGVU{}1taru6^- literal 0 HcmV?d00001 diff --git a/makerom/Makefile b/makerom/Makefile index fd0832ff..0ecdb691 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -31,5 +31,9 @@ rebuild: clean build build: $(OBJS) $(CC) -o $(OUTPUT) $(LIBS) $(OBJS) +install: $(OUTPUT) + @cp ./$(OUTPUT) -t $(DEVKITARM)/bin/ + @echo "Installed." + clean: - rm -rf $(OUTPUT) $(OBJS) \ No newline at end of file + rm -rf $(OUTPUT) $(OBJS) diff --git a/makerom/makerom b/makerom/makerom new file mode 100755 index 0000000000000000000000000000000000000000..1a70e1ca349c305182e4a43d3831944a2eec5cc6 GIT binary patch literal 429576 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�X!TUAZTuRZCoFWo9SeywJypZQ^la4? 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 TmD*2jvG%v(_gs>%#`gaok@Dd; literal 0 HcmV?d00001 From 2b3bc5f2a2eb79e502241436e675b932d25a2fdf Mon Sep 17 00:00:00 2001 From: "James Pelster \"CaptainSwag101" Date: Sun, 22 Jan 2017 13:30:17 -0800 Subject: [PATCH 187/317] The '-micro' version arg no longer breaks the version number --- makerom/user_settings.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 6d19e059..303b61bb 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -508,6 +508,8 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } + set->cia.useFullTitleVer = true; + set->cia.useNormTitleVer = false; u32 ver = strtoul(argv[i + 1], NULL, 0); if (ver > VER_MICRO_MAX) { fprintf(stderr, "[SETTING ERROR] Micro version: '%d' is too large, max: '%d'\n", ver, VER_MICRO_MAX); From dbe356686cab47c4951d1e27f0657cd3611f500c Mon Sep 17 00:00:00 2001 From: "James Pelster \"CaptainSwag101" Date: Sat, 21 Jan 2017 16:34:59 -0800 Subject: [PATCH 188/317] Revert "Edited Makefile" This reverts commit 90dca52c8d1f81fd3f7a938db22fdd2c56540a83. --- ctrtool/Makefile | 4 ---- ctrtool/ctrtool | Bin 257872 -> 0 bytes makerom/Makefile | 6 +----- makerom/makerom | Bin 429576 -> 0 bytes 4 files changed, 1 insertion(+), 9 deletions(-) delete mode 100755 ctrtool/ctrtool delete mode 100755 makerom/makerom diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 96d1c153..7a534c98 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -28,9 +28,5 @@ endif main: $(OBJS) $(CXX) -o $(OUTPUT) $(LIBS) $(OBJS) -install: $(OUTPUT) - @cp ./$(OUTPUT) -t $(DEVKITARM)/bin/ - @echo "Installed." - clean: rm -rf $(OUTPUT) $(OBJS) diff --git a/ctrtool/ctrtool b/ctrtool/ctrtool deleted file mode 100755 index c5495b63ed8f9d066f82f21a108cd71985510b4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257872 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 )~04~%X>mM<3kO+x37!IY{+v$*TIHCc-L`}6% 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 diff --git a/makerom/Makefile b/makerom/Makefile index 0ecdb691..fd0832ff 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -31,9 +31,5 @@ rebuild: clean build build: $(OBJS) $(CC) -o $(OUTPUT) $(LIBS) $(OBJS) -install: $(OUTPUT) - @cp ./$(OUTPUT) -t $(DEVKITARM)/bin/ - @echo "Installed." - clean: - rm -rf $(OUTPUT) $(OBJS) + rm -rf $(OUTPUT) $(OBJS) \ No newline at end of file diff --git a/makerom/makerom b/makerom/makerom deleted file mode 100755 index 1a70e1ca349c305182e4a43d3831944a2eec5cc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 429576 zcmcG13t&{m)&JcjKtSL|M2&*FYSd6)iBe58=q@C3R~L;%l!^+9q+mfP>_RFEi@OWC zEURe+};>C_@2?GCYzyR(b_>%MIWVQ3hkGt)n{^yUs{oL{6?hIC*TUm6`xfk_6Z_32;E|75(KkE*@dYJT- z;v;gTxi|hJ%UOQkwV_P2&-;@f9awhsEq{3Z-4*%Y?PC2)@gH%>xR`L|WDr(@|N5OY zuW#|Uv7~qa(*AEP>i237lH85>_ek)49l(cl06(t-_)#6eZ|eX)t^+#X@1Xu`9n?2E zfPdBjo^Bn~&*%XDXa{(1?4bT_9l%fN0ME(}>R;4B{b?P*|J4Ef$qwL`bkJ|V4&aw{ zfM;z7@FzRKlixx8B^|&EJHWG`gZhtlQ2*8r;DbBBvjO$pC_DUbeg}A>sCcCH^>GLA zc^$y1HI9TQ(m}t?9n}Bt4&ZNg03X`{{OS(+o!0?;Vh3=m1N_H#04EQQ#0PH&_$PHx z|G5s}{W^g6?g0NEJAl^!&(Ycmn)dA&q+n;|Urjp=^$B+$2G?LeM$=qeq)iD-9zE%g z+L+350d2~p$>Z(}RA?i8*NmJL42;&MOuqHb+gat#(Gw=|IeGNh$=c}g6Q_*1ecWWN zA~0tBcvTD(fpHVYXlyq2*2&t0-?PHxF}IE%KM{BWlWz}B(ngNFb;^`6lLI3wZXGA> z0u#q;fyu!;N8cJ4qm5409Y5wyZNiudQ^tU#z-0VitC%uo%|8koZ{w@GGT}qKV4eq#<0?$u_J1(ri3)0|8ACUz`X>j(P{PU#2)BQ*z z4NjVpf8}ZLuK=nGMQQMKef&%s+;PpN)k|q``nt)#rD^bElR!M{(%{FX z!Plq3X(N(e%c)L&B?{KOPg)2uXjpEP)L8vLX*cuN}md;(JUynpBO}U-v>$>lwZAuYQULDdxwr z+-_>c-x=MQr|PvEC7;1ORjyqp`2(LKPgQHbB>8Wdr;4=~Nqz_ORIPTk71 z|1tAarS>GrZ(^P*)E*`I4a`$@+U1g8%{*16?UDR)=BX;}0?EJ3JXNIami%+fQ#IN- zl7E7Eszh6p{KL#s723@|0&vEknWqZ0nyt=R4KMw^3O3(RbuBz{t4!(LTpX)4>M2I zVK@IE^Upk0hTSCjxy(~l*o~6EmwBoPyH4_VF;CTCza;s|%u^-UizI&s^Hc?Pwd8MO zo+`i&N&ZIWx%BNx$k#?58>Dg3THhiUez(=S3q*Nu@LumTfAkLXUgtC49yW9t*NYz>#Q-`r>Q zoSCUb>-5MTH?<{LfrvK zGayX?J^Wcq;il03W_|u@qkdb4;c7IZtv*xe(xblxGwcEQCsfomIM|4l=9POJIunC0 zKEv&6xT0udS(%1s_WHqPny|@le&sW-@UZT1Bn&1-AyK~PslqyY0O*;U6FSf%P#ikY zEzm!7V0Ey2sL~bHqlYk(#Oa{}+w|zSNCFtrqn4yHo(XgdRc1V^M>iwyFxE9S#}^x% zV}$EE`HBZ;PdhJhObtg%Mn82A86Di3m}kU#=NYj;Ubf$CwJzJ4A^e|$5jngUVuGdU zty}Oi!51%g`OF(~)@j{*4Z>F7M4!Tu3_P2V%>c(O0l&yO&alrTofuUoj4F8P&){hl zJkI1Lv9#VtEVhk*$dD@(Lm0F88B9rvlpGv}>Mw!{tX#l^urh{}XKrOi{D$G71FmT^ zeX$#IjQEup>@|2Ac-}{0oU2u3r1uJmBv{o{HF{Mk&O4myphkPT(pafR>j=wk$jhFm z?02#HqI-jV&9l9++2vqn@!FYLt#xK*_)=|V=Y~u`w59w9<}`Q2&EA{5H+ygKj$Fef zI&7%5n$1sN)jy4!nURVdp51<==EXHR3 zSeVODmSlrxGm@GSdxcZm5WbGYF7IJudlPA<#7jP3m62se@0XX6^_QkKw04|w3^8wXSIUo_hTj>2? zbu%P>Yvz#n@Jwor@UBd!JHh8SpJbnY^AYLK6gG%j!f4)6qxlM>`B6smjvCEZ7|oAI z9L+LXc*ET8bWvF6Gr!#PJeZUNz8b}0Hy-9Wd4AKM=k}Y|<>mXKf(!iS?RiDadi>^1 zc}Cz&G+<4^<~Mg4@$z!RY*2z_KER?qPbJPa;xR!R7YJk`mz~5{WaDAoxPv+*_?nVQ zh~sc2j%(74#;~ErtgrLgC^jc4hJmX^QZhFjyP5HwAH8YTxYc!PRO+p%yTbfs?5|O+R6ZkV4 zB6-y+DQQXw{|H|sDV2UHExj}?UFW2MTXqtbu|0#%u3@gG-J7IGreK8`rs~m%dvXf5 z80Lq|ve1qdD$UJ%o)@yMo@p%=QF?SB8ry}$5L2K~Q8&j99B}LN>p};*!d7){)EC!v zwe;w_!sSRFsSOordgNmGu10LW4AF?q%j570>x}rLc_F0y@p*Y1j6WX9tHaaiw-K}% z=6o6Yp684>Mqr>4@%haIhWRN3#EODY8tw=Q$rJXSZ{;61PX6 zzqSMv*L7V>+?5`EvA74&pz~FwjFKLDbYnJc%*s5jNS8kQbvWD{{~}~*;EZC0QI(|d z(~#=XEvQWU1uW^!pq-QBqAd$646tC4ye6Q{@L|52k>H=NHIirX8Gb=X$bd3+D~W^o z#)=xBSMG;sjp76j=Q@lB@?g=8`24&^v@_yrdQ~Tm$~*l1(!;SApk|{^4-Izgqv9>Zl*&e+|0d6Dsz1?aV+%3{o-YfC0(bUT)5L_q(4RH z`%@I1ok-_KkXH~mA-uD|rXwv)^f_gy&w}73Ml37u2wv(C^G=VNDYK%SE~|EB86@dP zI;qg9&tdI23~;*#K8ZbkNR|H)N@_&^7ljA4(ZVdMpL+@gsk7U1&mG{pJKqj zCMyPHfy%_X_7kHhQ(o2Fqm|Trpys2^!hWFUBV}O^mzwp?WF)c*cNT6r!rtXK4h8o@ zzosZJaR(>0XIe)bbLjdXOY{0RISE$TcfxTm=^B_cCrAd6Zg*f8wYl32uGIXDX3~%T;-bY=lx0Iw-xf;8Th4;T~Dz z;T~D-VH>N&i>bNH{sZSS_=0_LHfi1#JY6~HBe~D2%y>qRoT^gT?_7nH-3$LfPew0e zrW4DsNyz4if~O>IAco$=5bVX)0qhFUR=WL_InW+sj&Q!0filZ7drg(($n3&%oRHUH_|D9!8m-2Zbg z)mcN)oiq#OqHLgG_B<8n?d)pe*L9*F;oAX&dJgpzEo%XdEnU@7%HsZK6T8p4ad(@E zZSIufcY3hluXif`ZbY#G1IkrUxYG~HD&Y+Gr6tFvxEY4iXTEL^ZvjHs=@4ouj|0xi z)V?W@<4v%5o#wV5riq6wgVA?Pv@*$`{9C-^_4Ww#TGCCAoV5=t7Hg2wAG!>A|rtZaWQgo|EP#Re5yWrP>z&rxCj@FNf@&1XseAP10=;~S|Is>6d_Fxo?)%nOM@wzDZW}M+6TJ>VI}Vqlgo^4e24yaTzZJTR zipqyVq@bvN1(e{=uhdHto^z=b;sr#Hk+NT38HN{XU2LzpZBl}Cxro87g=C#B7 z=6A%4fXiwoL@zEyYVPoi_)x}M^f}KWOBs=YI%2KT?Zx^?(7Z>8S4E{(h6D z*wk(qSs*e7?cg_?hM3>^%yr>|t$IWzimP3;2lr6q@!?*%5g(hQJafa`W&P%3wPmW1 zVXMh*eKYBo2R%R8i8-4tS<8ZEm(|n3ahikUCG^_npFb*FkHR_?8{me-Ft;)&z)0-M zJnq&-p+YGOJ$L%tawGe3xgTr|w+G0azG z@xa>-*JYs^xo17N8T*LXO0_LScjONL4&PFfkNmU7paEP8Mj3p?vl(~LBM+beFlL-+ z$ET~1#!8VOh0lDDMetPrMqmsYuW+Z;*Xdbi=*YW$j0_Dl>zfb%heXz)LE;i${5~Nw zF5P1%lnW|+@frz+4EczYY_7$&9d3!U0mCd<-*IO(>vhIJOe;NJCz1756}Cf=gv@8i z!g?}GS*sIq)Z%TJlMI(hoPc2rH^;z4elk&zOn8zBBbg{qCPpO_W0MKt1?MG12>zI^ zQb|cyJ8A0=AGB=_(Z3#f56X;Iv*4#Ojr_Jf31Ne_sE@1b&>Wr|B{e1)eTZed{LSw2fOx1-cU5I<dj#_edtFVKcb~EbKoZ1VY6*)(;&8BPJYI?21f886#Se3Q6?PBhzs<%8g89 zvLxU-`I!(-jzp>~`h_030^v}~iARECPamUlqe2XToz5J3D)1vHgFeMc^CNTv65NLQ zv9qXKJK4j-^BR6@Ymwg%^< zHHhSe&`;ogYFDnZ_RZ8PWXmD3zc-=S7yBn=*%!N?y!Ex-&p96w`zNQ!hf%>7oYRD~ zf%poNtULE;N7flQ`JZorDWonVep6mP@QG-_JBLX-MCUyFggOkZZ}|4Sz6ZldoaIb# z=G5bmV(-b75a?rngj@~e4Gi6F<&LIm3w~tJz^rlJ&kY`j`8-#TzKj~!m>|WmB~A(* zI1lkL0lJW)ML;o9CQ@_DGqJ*^^)i-eIL;NZ)1TZprAy*8thd1J&NeVB6dW0`6W##9%BB^ z<1;6!T@0*FJ)|X6nH3GZ0x1g){-qczJH|MXg6gP}X~&AINOsw`Doap~Vy>71Uu|;0 zcJ)CV-04q#sNdV0K~MTynM{bkwGf5&Himy4N%xz3t*bUFVSoAn3J>*yajDuUu@Nse z2VU$B^oWfjLdg_BaWJ~S3ECb|xcBL?HMs$QKTf&BH3bqDS6k>-I;h_6opc zWuFB?>4WN+T=<|K%%y#dz%U+eY5<3uP2(A=%!Hf0mn|zZ;b!+{-R;QMusgDA`6%V5 zHvwq(l>x4I2H1oFwqSsrf!kZP-WgyM2H1iDHlqPaObg4|p8!_UBT!P(El^a#li8Vc zlDW@LxaC`gn|JEz30 zM-DL61L66|jpXYi7-z|8dgKRWO3uk&7ei91S$ z>5;93+@wdoN5jPMk~^I~&Mdi2k8DT#Y28Y0)g!NyjhB>+fkk8o6?)`+*3T~q27V9B zll4du;Q>AJ7Hj5|Ow}WF5=tudNDw`AwB*by+ArdT9WS{bU_iVpshHFeOWHy??H#pH z)^YFlFVqv3mKA;h{}Xkf^POhM=m(Zo^j-Y z?)I1?7xb|GM=t1NUwGsKw|(M~3r@57lG+hQewHmR1I4dBuWqv61)Go1Vzt21xEi&e z`h_(YC1xA};kLsOh7Jcjlepr4)$Z9uPwz@do|fqBU70OJI<9D(1`loz?Up|l7h{?3 zit=6RsZF>jlB#?_Qfj*@D-nzG4JP^qga?bp5EwcZHVJD%&VpLni{B2y?zd9MGv zqw^urFB}b{M{*D#@E3nIxrgIz7SR7N~nK8zGkc3;S)P}ch4e!9sn@&?3#9<7>Zw29&nMQmvW*o+P>K_NNA9LNbqw05c4)5}KsxC-ei%y{h0qGvzh06;U zB=YKa^$PFGL6Vnqu%o(O=@@f${f_S8ZI_2vhpLYIX;J64BKF&KdAKf=>bfeU5OzR3 zad`4gkW6K1oc%$oY-;3K4dU0>&PMWPRAW2)_*1jf+Q755;{A?j1n1k|eS5g!nR+}g6!gyUZndQXmRJ#2%iYbY!&5eQcaH7FUDGrYtthd+6ls<2V(Q#@@ z^M46i?hR5Cd&Vzlk@#(LdjCoa{xjtaG^zoq;Gv{DnCkxzHIF#$XK`NIm544koW=XU zKACYAt=$0D$hP6NX1dh$VV{>2qS?`p;Zr9!B)DmTsv;{timc4f3jx4OADrOyOCRJL zZjQhOC{KoR41Mc-DRy9L*tl8*Hb+Bp<-P$wm37Q0O6vsemecd-EeFuR(=RuFc;gB?mGo9As;gjBertPCaLux7p z{46FlD{V_i#^&hM4Y+{KsWQ}5^}#IvEUqL~?HT1DyGM{=!}>20Co5?KQt4Bnk@(3}0;Z;EQU}u{y#Eh%(pY&!VtJKZtVN_RQuflXe`2_gAtwrwv>@#2PV|>3 zTGB#Wu6k!qm%t;Y^&Dm3j?&mRO@M(?4dAvR3yk!F@;%SlZ|#I9nT*RBjLVht+7uR` zipw!tagy|*N5(h=;*{iwF+9{|psn2cx=vPXq`x%ipzAhbdDo4X)r3_05>Vts3YAvl ziH@=e^YTJm6v>cb)amB!1R3|ARH6MDsA=cc`eJjMfX@ly`FYzoCK0y?)>h#As;{gT_&yea+jb}>hSsl+tJmqL80}YoVV@-$pWrw49 zc3}VlO@oO`COk}7*Gr|9c_Cz2JQhz$OiCt3F=3sDdSIM7Hk!lZAU*Od3IJm!d29qJ z#!;BM*6K4KVi5v(b*M;6+}8G0iYCvI&r{r}s9CF>N~3;`@W-Umehma+`7&c<;vbdwX9=5f+f8!|Mk&ukniwSc&^1Eq@d^iv`v z7_U>6BDf*1YkBdud}gct%+|zdU+`k)L^<0yl${!Pom&Vgy8T_ zhxsw~b=Gg6(MZPH%AxG}_rqN(R~A}fWoDzB=)Zt6yrK>n7jKZSSc;Uj2Pk;-%?~#i zu?Gk5(h!t7S3ML1jM|hQq z;7tJg%(V`@=Ffa@1>h}jVobajy$-iHtes#aa#k6>ui-0Rt49_|>mIK?cZRn74A3oZ zLTxoE`YR~nZOwigpb>AUEdXu)zG}F7)fH$N8{`hRWa^O*IO3KpJ%U}F7PegB7My1n ziustHTa*1Muni?0(K-;JM`lTjvUhd870B6bqHG*aQCiSl= zmS&^j@+|yQQ-E5AUe*Zyj3Mjz{#W5npSkfC!`xkiGtTqugRmK{0h9o zcu+fnyl_6uIG5jHFv2Z84fD&XeW(cKb;zS{u4O@MQ$=#o-n@oyLAEX8 z8kP?;X6+IL^*`bO?n}dLHtMa*4c9tjKHo|L8fNV88uhgumU4V(T zzFdn6co}X2daYeg$a{c*jGG8pLcnUL%9VgvuMzw-!OuJ33jqOt`BH@0OsyQQ>xtJQ z)N49)Ks@HRh!^FDS9gJre~#{3UEh)!zq|8Msqxfu!F?Q&`{Ordf<2{p z$-&(0FW%xeKb~@;zBpT7%)5gwoISXVVqC_Y)LM0=Yw}lg>hPXk_e`VsO+EY!b~FIs z-4JmA4)E#rnVZWB_rmB>|9}&CPXw+Ddg$$K+*N9RuSfbqQG4~pU3}*I@pDk4Zg~dk z8vQmK10} zJNNWN{4F2D&FS|e=A*Z-c)fma2G)Q#^rMSMpSTTV_~|wTdg+UYX<&+L6U&0%R}Ir( zj}{s*2$%_2&nbN|4dB$J1}OH0699kgsk#eiPg7izzwoUhU}+Ecn=R>HMp9pSi|h69cxYg+`7f_oZ{LcfKYoV`aUQ(+>8%NYbM{A2 zoMiVoWzjeE2tVafgL65}=W6z&`D|VpfK=2fDf-ly^`j$3`@%m$jK0%n|ArI1f@0)d zhGd$4EHgJypjv(VKE|ANul5%2*Js1tff*32i;)V%QB0W!% zpQMMu$rL?Qb?xc-!J%gvC{^^>Q(yE!BE)O+h7V?x>ZMz~;U6;mkcJ`&Wav?P7hcyI zZ}A>oZWgPVC^gsnTy@^K_YmXo@$2#qh2O?XIKO^JcGpI%3@ny1&5KRfAtYS(C$NRy z;vdM5Qhng|M95ow(8+9-7ty)C0`5}tol2sR2J?8t%(60t-EpLV6Wn(J~*Que( z@CMU$dENP{pvPs}w8IG&09Jg=)MHGjB)-kMrp;dCQ^hx;SuH{RuBSNN+g z^>(Ir8I7K|-?!n`jD*Jj=MV%}isNZV=*mnJqWr>ya?0(XJb6fvb=1_Lsq?aNdP#PbegYgO4a0_T} zhqtNJd;>VKD095JpQKS=pl4F(5cu9fZ<4+>!6hRUKbGkjU|HcCN&QViNo~(EeeoNm zuDVj!dat>rDhqK}+vTlKfVKNu%gm30ACyJ5_~XCP{kTgO7=X!+01?$-BXmOBd|@(h z;P)fVn7P5V-}P<1osIeM71x>X1h+-r2)?ribI?|QsiLPYi4U}N?}Gn2Ly?xc>dOl2 zy!|$Ki{H4Xhj-R$jharvBBfQm5$}bmgx3~)vn*0)#P8ArmsV68W=pBLZW(UUX^CUO zKTy9;YYkWcvOWf-FV684eLMLpaC(1hRZbwQaBt!Z5+(CoSf}KeWL(g_b5(bA!+CGX z34RT_t0~{_L&Qb;A>KvU3Ro9o*%x96kT>Y7tP6T)R$Z3`b>TEjQ0U!^9p%(C(I=dS z`c5cmM@>JFY6Y=?AnHZ!WS{b)FKLZu&4h04l!ep9HPu6n*u`)f#UNP!Tq$bPFb8F0 z78KR!KMx(H11>uMaBH7e_gB zRf8ZIWy7lmK~b0ewrbEoBNi;OF75BqVy`jYfzGePrubu=AH`nlt^ha6X@|8=V2qTY z(k%-1GGM^W3qW`kd7<=HHK-!?S{5MIe3mYGEkl*^YnecOjRLw|*nr1Z@RI-lUIL&I zf8cM(VM|HVtJUChx|&sgoJ$)~Sci>3n!F1CexQ=85ii5sYMAd^i_hl%d~4w*+;MYh z*6~CsAHEqf+=vg&y2IsZl8V=SKWwPbfa!tB zBr0oYvWvpK)*Rf40wXfK`jYQ3f=XnOe4SnKQylUB9vOyF|5cWC@L1CFdN!3PzY+j3 zPv2iwglotS#La3z6npo_pU!o;wP8cCO+)|c$~7oZ>%tp*^&h#ezB030ec{*L)~{6W zuTD7Gl}oC!K+#wDJSHf5bwXd)iG+0qY!hI<*sD9;t{j50X&u%SSeK%?4+fygw2bgt z9B>S6V%9>!Vxk#|W+rgSX=n?OE+D;ddGG%N1iIbG3hUNXbAQP%Am~dF0c4!zpfRh# zJ!zme-kli|e<6=+&c`Plz9I2f^El<; zu*Ew;`Vzbi&mIu0*k#cV!YzLZneDS4bg&1XK-9sFAR6EAs>d2n>rC-;D0j05tzdym zi&ts*B2ajLr)ig{GrstsyN&m@?OmN0bkl2zhuMAjvtGfYjah3vAjvS-hqrY_h++-3 zdk7PVsKy9rsW$y8vhxr*g*|#Nqtokju%)n18xAUN{B>f=xB1C{~=L8+{>j_xIW7GOeCG2Ln-HaJ^fvbSsR zmp}*X4eik(PI85LG2lkL?`sqT1~m$oP$bJ>!CwbSpN_a0?v~vh2g>c|`)iE-zG`W6 zfQo81#1M4IheI5F#r?E(no=aW?Z=O<)7!Ui2l&u4S6>s6MqrRuL66*xe~pGP z!{;oQN@64#1)h_t?3T*c;NRFQv{gRLSAncHzc~gWlrpz<3F zf;XkXds`2u;6J3nORRfSaG0PJJ$zQ?%g8xCc8uPYWJp z#D?XD=3WDJ;}T<~^p;aMBe6M|?{Vt`@H|dOpUU&d`3pRcA>h1t9tkHNNqMDN5vQAS zYs3~$0zG)EIWU4!d-lti*w`vVV6B0(3OB*v9S5N?;?sNTOPYm|R+QGzv1$-V59Dro zPbos}vF!^(APF=2YxB=%Xo*K<)wC@rWi3cZZ1yPh8Ez?m;XmCyITsmFn&g5cCzYbjXIj`1d`-O_}(z zL-@-*UDtZ5e&5rFZgMe(z^PKp{_j++@RvVyU0dQ}C4J5}U?aOMV3&PsB2Jgj{gb@Z zqfemA@RxcLp<9@89>iXdUWcyXz*P;>5rOvNNM9WT#%$m37mmTE53r zbB8nVK~6Uec6q~ZXOKVk0U%N9r>4>&r;k%_56JO`-*J(*_BUD78 zp9B2V0e*_cH4iwzHyq#_1TJ)duR6fD2z<-|KJEaQ5cq@xoa+EL5%_lp7<7QE2zRyHL6w=&IDN-e!HC9wI9Q1Qv1F51UFcr+sg!~)y#0(9^w#X)nmUnMg?zS&!E5Cr{I2Tdv11HvAOaS%) z4#HmE(E5`R8@G4XdJWpa-UI<*8a%bRhX6b6eR$Sh z+rB|}R)QO2|CNO!Sjaeu8j=)Z%VtMdcn1sH_#}lnDCDVV?W`l9b))b~7RFeJs_AGw zC_I;ij~t;eghGD9v-Wuww((f{#F*?okjvU-EbP%9Ee3CYz`}JbOmR1;T4rhXG8Vqc zLY!@-6QpHp_MBopVaWM2A%8p^b)Bx;%0N5 zeIn|$`4`Ia)A>>QX=rH1F`)9G5vvJ--r!k8_ZvaC@j2;Gxue(sjId7us$|?V+8R{n zm$d&Cc%6sA(|2CMkmJ);6SjB^1+osXX^^MV5Ad&NjOACyzGrtQNq6^x5%@q-Z=AIn zOYtFfs1zVO6)~iHCb8DhQtKwvIs?M48C_XYY1Dt-yRG!H^im*^zZ^Q%9$|>lb#v$* zorma%yJH99y~(}`6sGzA@CU*9aBV)8L}z`@Q^24vo_Gv6B*wiKx)LM zSF%-4X*Eg}1%L0vKvF%FvSyDzkd(_)q->^q?X*Uu6NCM7tcR~X$+;jI^)75zFSWr# z{S#RKh7{h)zha1;P}|PMJfM94oOb2w$$~GRMR^O>Cdxl*U+!l6wJhI^a`jQZy>tO- zIE4GL+2lUh6WN7calu8-YW2aZ`Q;-7P5tq6)TRrZp#jL=<8rQ0Z&4t^VjB zDJQ<-oxu;i;TmGiR2P4}wYfmxT?HQ#gNJ@^hv!Q;jcZ+3#!8BO;f8F>dM1TyZCUY) zL^JJOZ^)l_O$H@Z{zS+MAYb0l3<#LHOUWAq&9YBXdjk-5OK~6n>UpMil>WB5km^0$rem$6n)L4WF9M&_UfFmEyfM$IH-wf|WCUjd=< zzYkA*_qT8h-XcgqMlt{KyH74xn>TZpH~cz`|C(%T>fd0?ik~8isW>O;U zcU#?_B6nJ{r@jtNJRo`o2;EVW{e-wgJ)cHMOvp=y;85oT)LHI9g_^OdmIVw+{h177 zdT(K{dshTiF9wlH8#GeXKL$=gnRp*^=EN7$IGS@9N9QgU*^h1mn)X~>bW)0|Yw@x~ zN`ELu--|E)5cC||DNrp$grJAoK=U9b(K;_~fkkIY2ahb|Bf2GUb!s=4|0L@K{53Yr z7}fjn3NXE9j|1qe05{egasb~y!6w0O>!K6E>P)_A7k)jb)NGP1I>xOExJOQdQf~p^ zUCE7Rva*s#RX-!K)R9}2$Lt=~K23gdILVYB7$L|{HnNZ(iVeq4vDpc_Ry_aQbH7TI zCFgHn0_W1PsoC`{rzq)Jn_Yh(qu=Uy)^Ci_2kM(L`_(T6BJI7Vu30q@DPo;^B}7Vk zgSHXPybkg81ng=rR&DFu34@FL40sXEmM;`hQ$F}T zXsLw9iEB__;yO$+Y@sgY@l+GH&+3jrET7AgnJHgmaUTVLcpZ-7e#{Q!SCkg_&zrit z)cm%Tmy@^GZ_8!jtPN<;Z1H|F>jhyL5jEa!Jt>01`;1ucL0keK>^95;g?pX(h;^Iw z8O?BbzpEnrDHhb~EZpk}S{3+6@CXoCR@j<2n(~g5P+xdmmM{FHJMgQ4vECQ&!|89U z7T5uLE}hy0@j?uJpY>`2tCC$M-WET>3NI2RmMb?f`6XdTPnla0R|K+|1J-!=xxr-(E+A|* zcO+hQ<|j=r2|po|*M>wUjVDFbo&MLuWm4+l%mRAsOjKQdA?Q$gEC)@jULZh;;!}s) z2cABO+$ljc8nW`x7V*!dmH5hbjLvrJO(j?8<%n_>z9dbaRtK9!o(kW<#w5{8>_wP| znDw}SvELoIAl6$4Pj^F<%CxCp%-RMXARe+C64fkn-DKUwo<*+KD!DrQFOFPo7E6cP zl&7*(wZr5oJ}?s+gqPnk;@zV%s)fZHjMxqEvU}ef$cbgtH+AW{&b+{p&!wE%h78Vz zKw+~R*-DR$z=kUs?SftsS0^_%dp#LtmgJeMD_nR*?k&tYb=Z@5ikZV&vX>Eo@*{om zYh25@NrJz_8_|B;WiYT*o)vG|Tx+i8yA8OY{fI~`T$S+xp$(&H!$hXM{fqL0 zAZZ?o%njB+!0ao~k;sJ~aJFGgFdrpIcd`3OX$a(Mcnf8 zg)4Z&HFcy{k6aDk_HcR2kIhzA3ddoNz`X*oq`+N^rx-{KDREoMJ}I#v8>?F>6Oe{# zN<)+%)aQXzPD&)a6!4@-!MWPi0a~_9%#i0Jc~^zNV8V~ z0w3Om7gq&yhZ0la8$~9_6y}W5BuX*ABy6 zrSevE2nr30i7#2TaV^xwwf5Y49-Sn)75)HisHMP6iss+HfM~7;tfD%#3btCef`m3| zVe8TF2FRt^sHO)oj!>&2qO>u+vuU@nBX9gqT;W`>;fiE`XpSybd#ixmUWFq1YgFC# z)1*P<*uVuhT-8m2$m=RH zaq(7<@^HHX&iQ4qh`2U|ZylWA=$WiMFKi=jUHM=hON;AfQNxl9z+UH`N{n^sQp5Gox8@o%x}6-3AVp z1~}Wg-PW}WC{ck4&iUMK%cB6}YdVWQ-EEyOfZ!jOYOo!l?af`+i2uhoTklFr{1~bkC|}(Q z0eS_i@%f+3Pv7 z+*Uk;LY%ydCw&4y-dR8JRVl<8rwtpx)U>N*sB}w;K?;ZZ12cU8ry>l$CD~Rzw1oj#Z(}%#1&}*2*VzAZBS$TQ zYaTPo5$e+;#e<{@ml=urebf-%ByndMV! z4&>Jfg7BAy2p>iG<6x%)9|1VO35e(n-o-7fz}VLS>p?@%|f414ZA*T`cZN~s#NPc zyj>~VrR=`n{`Zs(3`xT5|#vZgV4hCd+=!`?WW@M9xct=48sY5)3 z4LsORsB=7D@#;W-p5yU|EbScccvN3@3}ABnqDTM6q;r;sHo>>l7LxAH;GP&$RL!ImdI#f5Y;w?aJNu%WdsPusn5q7&xc4q8%W3j+`gX zLIcrr>jvq#ajdRuu8|W)`i?v&d<0MEh=UV(mIax@QTDslj(V_z|)03QjJ}s=P zoyU3%0GHhl;))Z2PS{%nKj#2MHG06cmUi~ph|i&gYO`PAhMuAi?F;bLO}SrKhjIm) z36S*GMt^L!ye8ZQ2tr^w1;8-(2Ryhr<}uNcbCh-y8-eUCI zZ}eL~t3D*)b-3-1pQ|#Sa{M4!U!WBCko3soK&(zk4v`;NPp~{bbrE-YHxycDV7xU~ zv3~{y)>yv=XT(O{4Opn?s$h40ak&d$n!FXKB;6`toG-#hwAAa3;QvK;(gogDQ}DFR zV;6D{JWKPJljHN5H#?`|&i$d}xEpxfNwXF@FfjNRe9J)#%uhU6T{A=xWjz@eiUPNU ziiYUXyC@%uq6>I#V~-Po@z5?kJE5Jv8Vj1N1)xZ%v1g$oy3nH+Gr?|Z?Q^7Yhy>IO zcKW-}>F>Ny(O^B=jWtw%UD4mkHa~PLXquTI8q59?NpL0L21oKh1Ua%r0g91zur&RP6>`V6YKT~E?kutuI%#c0( z{PgJmR_h6x(%7ouf}u5VCN&Js?at;nlUJi~N^9N4dhiA0^s$<@jaQoy)J^q~{}*og zv9-XjPsl4U&c!P0^7N*Pe$jXEqw-K>-_yqDRo^3>c&K`4XeO>t^Za-k%;U`5U{CAW z%^X5t7;k)O)+3S&erv=A!N-|`1+kP1%KBUe8vHm7PS?}J7SrB4dSa_J-Rb8d=m{VRE_5*VP#AGN02sq)uclXUd|^)%$t+E+|E<=A4xZm( z{i7+q(#$G~y2}-=lZngj4|tnC2S59b13!yEtC}PYH-@ZZF|lfC=b*au{sU%3dVg82 z!=YjGSTrn1HC)fx{RA9U4Q11W_hsa+MS;Wo9PtwxKx>5w{Xb$2ym^&y^lIp?KALl< zs^Q*I6f#6hc)iLn-@`eu1A`+}qv0PVh4~!1DZj7iGja91lWJ3@$zhzDry9ak_5D@t zt2;9#m8uoghl6_6M;pZ-2A)(fForPgbi(duwGguFvylzHyl0KVlLJsW zVw~eK8Ew5U0_4UNBpY?|5s#y(;mcHNJ^!?0J#r@72tnzcXSp^Wk|oq|#c-gIW%Ik*tRLZcu>&aSJx{CqovG+vgEe{%XJOFr z*i{8o*Sn2<-zcwsoi!ZnP4gi`z@hF-*ATl^jAAMdu0tHFTwUakU4d=RdSj#IFRs(? z{S&JDX0_myZ>BznTnNqRs|ClYH^$6&trH-~T-hZ?zj`$D_p2ivHg`s%UrwliSQn$6 zS`W{_URj+WGfT0vYPHAUALx0A|2Jb$@6J@c+~-j5-xyVFN&%F)$GUbO#}3RVpb1QD zJ?i@rb+c#y>vwd619{(UpanUtc27ZzeSn;&?Zw8PYRexx(-(U*&m*g6OW^35C)gGn z;b26p#;rqQFXS00E!@IOwZX!@qS^S0%xIv-RT5~kX;T8nqWgj99=&e{ndrGUioNw; zbS~}sxe>ny?+jr(*mNkVuebw+2vNU0<@A`TdMr)$xWbK*qtjr_+}ynmw?O(Z9FurC z=^w=gY`mT0-BI(B>hX!F!@|U4pypK+;$@whDpeRw6^7dHuaf?R$OcR!*AT_eh)wla zGeIz3B;(OEM$Xk`^(82F4`MQ{ut2lcxB<2P{uYi`SPW4l_XazateRv24$xTeBChPG zL`%l+OoS2pOBLH^-ZF~yrmiNgt=MH-S0OpVh_BMHN0A`OI^MGS9^8`lxcQ}C=zto` zykZpDWW;N8dV;f1NFX)D6L!NK+#Ec|x*u;mU_9<*sWn3Z3JBmHYJ@!=5OT;`!=hNH z@qIJ#ICcUb@fE3xP=~~VpH$7qV}!jxnqfYLpYl?Ovq`n?LQ&|RJ{o=sAlB(nA3^sc zA7+>i1W?f9Cs(lCs`*4mERg}=w#`R-5TZ@$8{)@rAu9h59~VPFd%JZO;uJiHV$5O1 zL6O`WlKG<~2EpHQR^>-wIf%0c|3N|i$o1-^{~>9HCNRIi{i$^}7gy5#tEgTD^y&Gb zhH}(NQo|f*Gx@{&%4qx%w~ffn${YdBU#7?&JH z^wba7TEhsC2{glt(9|kz0}Lv%EbrxusW#IM@($^d7D{Za456ZGTo1*E z!AUp52DlJ`PldBET%-kd)=qu_s%DGT4}{j)7tkiASk7o zC>O-B0fA9xsLQc6m$VE*Z@o8Lr$WxK*>xGS4sr;=m1PlpI2F+!tyU-YdJvA6WyV6g zT*TI_RC5YTwtq{4hmAr>-&DytEO{FxwZ%+@RqNrX(=xJM>t|K#a+G|UDv{Rrv$c<{ z%cS*!YO*mlU`eW)bU#)QED-+320WZ96dYF($1TL+QWR7I!PHb8L2wr8jKBaUp=d<1 z2x_K=80@YnO|8G$ZIq40GlUgx1MwhywRpgpY(#Ob`JTleek1&Vl0`3{iahMph=xckBm{AL-BYWswS1Cn1#0aCO;90e^XeBcglK-y0OOW z`Y3Yp?`+Px8Hsh>cAZxpoK5KisPoycIs=!cF4 zj#2oHbvH(dNX4LhK9{-Q#<76=9dsk^Tzu$oR4ZTRewW}zfHPXjx}i7-m_BKl`%Qx1 z0yx1%6MsN7Lwp4%Ynl5E7H&Y{J5ng8HOsXuEKm1ZK#M>$Y%C{dIZvi)+^YdSO+h&w zPTg8Sy91g!iGh8`*`|3-uDuP2#s46)yek*gU=iWkuLnzHw~;xWC$7RJ-W|q8tQ_n0 z1o{l)*J?klm^B?t20u1cnD}B_ip3hcdyLgmEY2Zlu7DKRmMvP2p)FbgFyBQ0I~11| ztt5CAz@I3v;?km}1TO=4t$^EcY0+X9E&$04HnECi(S=^U@{y4Zn^phboy{4>1P5u2}!(L%48*y zxKEFy%&ZnDM~YLe;hu6>DAoewk>XsU?ri*voGht87T*+aN2`0|Aoyy;IGp~@UgV|X?${jh>AOG=~GbJuma2S`48O?;r}<4=FLCbp*dBfgUWtv^je ziMl1%)<0EvAH3w9qc7fM#7i1wmDo>PG9?(VcwA}wVF(Po#jYv;YiLb2=NMlIq5EOQ zD?yw&C@1xOfF%BccKEOOIs9-#JH&sk!jE^#NSo9Qlj=hj!=$oAq3$2-7pqtM%hQfw}=1d^uzYa^5G~iw-X3s9=W^{ z<&*3;k64~-#2?~1!j6wB(|$^7wb9x3zJ|=#tcGV$OVpx$;492ks{Baf?*pt{4x(J` z4Zo;f|7?Ap5Bp5{J;7RPbbiL;Up^D37jMHP=39LsbY4WEvzCQGfw(zq{jR;( zNaHL-kK7EQ@!`~UtXIAuGQ_m-O%vi_XjNMcS5bnxA55hOwbzF`GDXWg3X%HEA6fvB zpkPbx+{Bf#*3>ikY3-)HSnGQ~*Z6VR$U15209{|U)!0@#q)Lu^or zt3kbe__Pql$lpHai2V3Sp=+<7v3h(5-g*{2$ScDRV=yNC7?_1OK;^5`4VHyIsR=v? z;V@!cgr~%h^4r~g3Zb3DzXyTxaQ?yH1n1->9RzoJUx(n%$!MU0Ksld+YZ|yN@|1R; zBh7!Tb<+|+9*Zi%tN3XW*VKOUQYiH;uD)RB5-Vx2t^{@Z61<{;ZbC2^hlK^->ji3UCH!drlA7GpR$XQOX)i!enmcgD1GsRzsXYH zV=-~FJw6*62S@Bzd*pZxeot(+d>+@FLyX9+$LC)SbIv5>^u@D>vTcJ@As3NkIsdBW z&_d#u1{>xulHnm}a6XzaURw1KKC%OB*W=rYB^yxpL}Y{6{4Vhzq}B}@j{km(6Y9}d z;8Q}^qmyv6t7=dI_%j@t3T&Z~yip|wB6*WamRjFIF&WFD+wq4<%0XwiR%4vnr~;e( z2!+wL5itxZhFEkI+L>W4`dIXKq}PRCMH&zJT1RMAIbf*3RUQ~`b@^PwCmHg!;CP&- zxYvaj0gT__gI5@Pi8!pO6F92qShNxvu=AZZ#tiqxhj~;BDu1+CQHRoE?E zmK!KjqFO*v9R?v=7bdqg`$1)dBW#Y?`mn;Cj*WGPYI9j5*dtWimkB+J6%Ulti>xp1 zqIk4eE71(owOGFF5zLCz1#WH#cX!4U!5c84=>=%VkHG3nI9o$8O;?rHH{mE`cm1|3 zd?c-6*0$!%t{b|pt}q{X0`>6700QezIK+U|y4#(ngjP6|FmA5SvR+CZA~q0)1HRnl zSAaJ>;L8OB-`Dz#YX><~NOP#QZwA|_4{}jVQL=AomcG3Y;$+`8(i*t!#Y&Y7Z!iOPt*Y|j~VB&X?zt`frz7ZDHf_Tsc?jf zg16wxkl*}S3iB-#SA zYOYPliD9mACxBzp>27i>A9*fkE=_-IQW1XB6oOEDCD+$HbVoQApX=&mkYpsKD^^}) zz1W1&sM61eBumGsQjb-QQoBH8DoLMW5R@7h#djhrJ>Ede<>z4xYJEP#_4yjP9qaKT zi@4KPMVndlFp8Y7F+fgG$z-KdQ>4E_{>JhhJXc!V%)II#j5U<^{jJ2pqVyvtFh z#l%ljME*@v!}#j#J(v#b=_Y)~*5Qb4bApob;VWakar!i1*i{I~mCKA<{eVvnnV@Xj zdqj{ghiWuAjZLONwuu4}w{L$BGSEVrra4mRO5~DPy5AY6E?VM;669)iDNIHM=Qdj_ zi{F4(Qd{&$m__)YW?Aw6doGI2Th$vzvUnaGoxWP&{8AHVe=Vi`HW;o0$vCpBcwSUW zdt<=H=w^I08E-TU8C{?Fr*HH(`jYuUPCU%ayfP+gDltsZf7_L3Fi?1Dl5D7z)-tvFrxvLlLem{~ot2IGY3(3e1| zX5+>oHLyR9kM+Um%Go1c(3n|llO0$mp%oNMEpSxDz*+OA@H2fHjHi6juGDPiyK#7n z&{e#8#`Q+*d9egB;DwlEzHmB)v~bgvu~(6z;B2J~v8OkU-tdPV$nw{TT0E@PFkruFYD}?=xoA5-l&i z!))oZ`Fjnp3Kv04lvP*@VsVc&Q;&+BkFVkd0aeuPGd60l22SQvzFtWSh2}hCmO2~p zQQ6F@Fh4yBrvo2!9(FRmEr{zE6=%THXsj^9PoP6Q#LxII^o7gQg7)z{0G z0uQ{2l+{E-<(-E)i1x;kZnhjer})^z9^6hpFNHW-?_*)GD!(KHDQvr6nzHW_1a?n(niK@OhE~v@*8(-EDnmwp&(8x` zN3&+K-IZq6ch=>AK(*~L%vnmGLVh7rF8JMlQkqnR%-Xj_)YU6g;LzMJOyRn9D3psy zZSqu67Tt-rXP)N-$Y=tWIQr%lwF>4Zl-%w!G6P??7?~@P#}8FRe#c#vtRM9u%i`5D z-!$3@Ip)KYcEcK0OZ3=)epiTnd=aV-`r}}Q%v`&gSQA;O^nYl3^Y|uTzM*$k5^*q#HvZcQU??>YpT>V_|sr zJuz(NK&oD*NaFyp#~}5@xebuuvg`V0hutE*X(6A2#HeAEHYFIsB*iV*r7@U-;fj?l+ zxx~9h2i27*ly?didwQF`je|vWszPgd5#i3jdiUE{IL+A1{sGUibXH&9%J(!dZHUg& z0_zJvv=N+VXqy?A57!4VW!GC8_kEAyvcC~4XBZ2*dt?0Lz7-pi?z=e;PWcUdEI9=Y z+~(;R*F40egTv@9J?`c;_)Y4I-Lf|Vhf*?yp7Eo6VliWr8t@Cl?l+?$-M;}e9D5)D zJsX|J{rXL6f{Y;Ck3^pvT?YxG$_9tL5RzU9*nqOYSyN4aRLI1+> zekr038Sl#^S>;lvOM#Ry-sg<&)WO;y!!PvB7`YbE&cY>?cy_kVcf#HZmip~;3^lP) zwaEpOt3f+V&<6meYfx7ZLLD>!(pf`NG-NIy85*(&$$;}TAekC+5D?bIQ_Ne}Cq~;k zZaLz_{$ZsZBhARxkH81cXVGs`i;43B(<3s#Il341Y2Cj|SO1}OV*Fu@sqT*+Nqe`oT-Z zQ!7zoCp&ZFR&*!u71)S=z3WOc zl=3ZTDEX|&SZ|NXqR&{<1 zJGJQHA=F3eT}@&gT(k0$g7y? z==7Mptg zsRfrr)E9`s6MdacV2i71M?1vX7}s#*xb>i+USM2cW%wo%%)mbv9mF(!*G}<;A~6K6 z;5?$@ToB`dy@c}tR{$oPR}9n$BHD{GO7p*m5*kw=!(mrnQ=WNv3pV6Yki#}khTe{fpZj(hSEN`FfiIx+)+oJ0Std#*QLxH+KzV>t;>WId zadwXT9=QL*nuV9>XjWH^%^r6{023?h7i)_n%FeS$otL|CfPXy>j-X9{=0x9*GsPbx zO#-GK5ujaQ8BBdc5n+Khn^Yfo!x-C}8!8THyA$I#T(ljRgZ&65$q-d~yHGZ~uUriDmbU2y{rLsIT0 zc%?cU5Dc&iOHrx}nm(g`7L|P$`&?sOphF%&W2j>DygX#@%5|lO&DXT8zkYljPU(3C|1q*HpW$kXXwvG{CuaD3_h$S_dlx9{hb~zx_dFvecD;7pDpk{Q_2p3sGEc9^obPzwgCA zoJ)}r1`;b>fqAn)1GguaAsQ3Fm{VO>@WE^qK@VgMuE0^@9;54mS5pL-Ut6A`w?h_c zC2;b`R#a~`iYBloNEo_uW&(bduvi=r{HFweLbdoq6suy48^wE|_o6`_sq95Kcr6m` zZ1$f;c}Ni}b%rVi!Q#|$qau6~_lN|lOafh1CJR(F;Rscdss2E>hd5SASfdK@MxQw^ww}!MC^x!O@M=dkr?ks0*bdR|@~|TL|0f zEe&=D=hBs_*~@i_WRcl@Sy+P0yd(S73(w3H!HVEKRP}HsgK(Bd{AdWgf@p zL)!2L%S&e{j|!s5f!)*go%rF14$#LQ93ek>+b5M^DE97&*0UGNp=Ym9&%$qOCF_j*$Q|A% zv0rqyO_kw0yh6wA0d~X*wMfvvxV}7q&XoF6k*qI#-vbY?ihpNZdngXTan0|hl6)XD zjQhrvqJvJoZ#c_+PqWAuDzjbJ#t042g$N<@Hs#VFZDKU&GJu?hhk9i$%AN&?QH0Ac zgNK_MH3bSaDkW2`hD}2+7+4OGOhJ*H#!#q}2imkll}qtGm>0FFf1uB|l&PXPm~AQ( z`%jk|jOmtD*HET+@U8n+m!TkF=NNYIb%+6ojfdsDK;R?Ft32>C zr(t;ZWgp~}S(aKo+~|wBR~eoGhJ(jbpyf_uYBn6P^Ikf9|5#oF(GNYMmm?^}`UyQxJIvN~zaVg55tv81u;Y7gN}n#bG|Zu{57hlf6kf3cmR&_KLc)!UMPH?Iy4J1j;fc;h_Xjo{*X~D9T z%;<~Y7sl$6USH!q<_*w-gn1o?j!%J^wWY=|ncRU1#fgA3|JLkkyx8_04mx06CRX%F zS${n*5VvQ>BCA!S|LLq8=GY7oVnYJ&qnN<^$(mmaN6%D};T}dxI6v(ph4<`oG`$#E z8#2V?XaRfP>X)A(7L3C`gwzp5a+q=OygQSAxMO6!vAA3oW<996loBbtPv+#urK_zU4c&iLXCmorU8trjbFcbR$AIduXc4s|m z89b!H6m!0L^;%p5Q*XXPM&8SaZwO%FCQ+Yc{PxS>5dCDr&|-z)d5}@Bz=bTEq@(RY_ECg$HFb6@MZg8&4kO32CP;wYy4p3@~ zChMm(r|73NAHsGRgn;>0BUFC@l?XX=oCOsR0LG^_d#$6zeIH+V81tVRWpsr{X^aThbn+sU<7GE}g_=n(4u^^`XwZ1I? z;;H7=U@e8zt5Hw>y~}7&ZJv`{!AVo4l4w;Tosj`0>@v!;T)}ZuVeIC@Wbju*J3+6?*4fC3u9Jp z+u?NpxNH12+8u+r8WxVPT4F;F8z@)`^Q}zD-!T4k$D*Nlj}p`$*eXZm*{syNi=g=H zv|o1gZst6M@3&$-b_NW6pwXN>FNfcbc099y+1m#xWb+;Fxv6!1(Z*zZIv1bT`>wyA zowS=!u@%$d9sSIGKzZL)Och3IXCjxo6xr4-t1D&}yfOYD`o*9p2Xg0Aon!HZAbi$@ zx&l>=(BGZq8*iBo(BIl|#x?q*DIyMe1@lGHS%DuAnW{gs z5ebL&GHHnHMz18jV7xx#?~!7A!kkU)li5Uw-6O9v{NW0|z{FS(D_o`#%V_7+EcAmoK=iUG$42cxBDhTJ%0@;1 zw>%w-JYf0J2^B)PkteZpCG7}M+3v@TgWu+RqwUZ|{{QmcDBg#AjFtjtlr#GKu3l|} z7X&Od;Q3MvM1yrSju;w1{+J9(t>LguX{IUxyYk6WfKiwU{Aa%;_j}u)h26C(s5(y3 zZ79Nw%sOvM@wN1$GGe_vmp{!j@_CpZUJjIe*|6PdZ=R9E`vRf0kYURVE?Bo?iWzC< zErRA56%46e7?_C*P278{Ov}Il{V4G4JiYA-`gjPk*|!rA{Or;{-}A>skKt%uAn9vl zeh4-D3du*ak9QXU=M@0{wfJ}efPc-tp&El15AY?I0Al+H0c@kmm3Gh1*tfCD>l{`b z9B5}RCw5CNx@J1__d9|HD3*_0-QP%CGcedy29@5t3*FVe-6{Qp#mA?^g7(~38Z664 zPkQy5=<#}f>M|rBZ~leuj!v!&=C^vH1B0WEZ!H_SF?&SCWrN#wV>HOiRS3Lva zY!RNG{gWU`=7nAD8U(nI%yDK@peeb`_e{zjz6ZCO?*c@SpeBh(e%{o@@q ztR>N>@OT!9@|WT`YJNg6{8}`Xxg%t~8ItCx1`ew+CpHrbNEPJF) z`X%VAcmI8!pfLWFNnoKcUc4c_87uaXR_2kI3~i*B0oo5;sG9SkqM>aju!9Lai9lrn zf4oK)e+=lO_+Z84WgK`R2M#~inQ@>EGWrEb&8c>2Vv*~CU@~{WlIlaIRG$;Fo-yXV zV_iHxd>4L;LgJE?mYkmoXOhW`38~q~#%C8VOSfTvu-11azBQ4r&DGzY7;?+G#K>wu zLZ!F!^~&9-skDG)x!!BwW2$X}-zf1XTT@_(@8 zMHiYyA4bHLMC@Q8R%paof>>oDUS#Gm*~+6kAfa8R@I3`u_&(t+E6w8VR}?P%0W%PN zlqPefDWxDnTBfP@0blAQeQYAIul z7nL7+_0kdnPBwvK34F@Lyi)`HEFknIu8IozkksW?4qup%Yzy)pAx6Pj$z1*n0c$Q_ z*72#y@t;}oH9CGCaA=@_InRUsAAfJK%pAGByP}1HpuPqF`;7 z7N4lIb0a%As1D_BlT(`|R(8LjV4>$t!3?}gi*N^}b+#$Od=t11KotBulX07-^gCp! z3qB8&D7^icWNF4{0}|du#x0HT6v>0VvilJlISsc=%MhQ-WXfouNw2Y6V!u`Ifs7(U1)avJM!Yg-8V_&vdb& zx{wC?i|Wft4M(ZbHCxhxT5lx6Y1b~A#5qSGaXd(r2fi1g!VtQPN; z(2$m*-WMR5*+O;G?4jXj10LNDgtHj<27xmf_zZz82Hr>DGzOL}Y#%bZ$|D2p;7JOI6^zKq54reoW9G^FHA0oM5 z)a=V+?mF9Jm`O)7lTkX8+mMNLhQf3lY|_DuUyKuJS}Zp*(|l$c?`q;dfeGcH;mWo0xQTfjZ|1S!auMG1sG>KzX+3Dvjd~I)Qrn;N zk%0L=Xytn{^Bux`EyL;QCYq72N4utG?1ePKHxVx`=V5}^tJ&uSM<(8C;DLi2lLNDZ z+K=*U4qn&s>B;e5S@8kpGn)Byv^4cp=Hu{h0-)JaT8dA;J~2U zGJ)6dLf0)2lQw~lnl+L|i@K69m^6nx?NS=N*B*KhWGSgzFVpttVadD*ncK?Gzep^Y z6Lv_oF=@c(LPV)?IhF^D1Gp*EqOL$XrVr1PTNwVi;MHY3K|F}3<4KtOO!yX7braHS z$;{;XoKf(@iAilZY{lP)cx$k-s<#+`zRf$i@z4#Tst!Mv;n2^TL+fTA zXF+xq1us04isWuP?)tqCcS_Mcrki@pXOLlM+mh|c*qj63QBs16xotv#HpE|bAl9En9ux9s{UT6jxO?PCi7$W#EN{3PIb3V z#l9jmT&KFiO0@$;WsxtlQhg3c_!;sVH~js~BF|1PGH1tjwE@Hmc$2I8Mk+y{rk(8* z3vcR2WD4p2opj?ir%IQ9m|1@DrJ^1Iy@1TdweX6`qEYbD@w&c|QD~MfM%AUU!e^Pq z`0MUi;g2;TH(8KBP{in#4dyVStMOnt>HeI6qk;rD845(rW^ywR{eE zlx5_H7!v_Pi~@CBk`-8dNsR5gNO>VC#~Yuv*Tb59bjM&)b`oz%GG0=D((F^1S(u9( z97JHk$&m_-S-myo_#L7EH-Aay=6s8ruNe>ids_Uj1|;0beB*U?nXWUp^kYf+a$Iqw ze0`*{CYj2Ono8l}RLHhDT~6Y@z~s)@6_f7yCged2ax!!Jj5)=tSj%&aUgk07xfdl2 zf0Kmg&cG1tcJCDXU7nj{c_3y4^H415cd(pUZlKp`#rl|C#^Gwy#gG^`&+xnvc5qhO zquU_h{f?ySUXvTE?upDc1v6CB?JbsT9RBxFUAjwr5g`K?7t^u%=cwt5ud96D;>%I9 z9z2ukJcH>W&G91o1N6Pgh{{0=j)1%cbK*mPm0*fdwwsYb3IfK>?_43 zv$E88)+1hZ26q^+^DteYMINUNv{%Cbq$cdX2;TQ5*pZDSmGT0)G|(42O-!IJl;lg8 zl^%3Hjmp4LDZH*V6tjZ^51q~dzDNsOe>lGairD9bcbJvFop#Sw_-%pHeelatc@e2g zFbR-S+)U#-1+w8)?{xJx3djq@=(4e1bpWD3s_$@=fX5-(f4lfDjl+LB5^H`h)?&iG z8icqN_~_NmE`8L6$P2r8D*6)pbnc;nilgQ^VBxBDYngqU%6J))#lZ^{7ORFJdL<&& zPNrlH{S!r{F34iaXUqydrY)-x#9a=`@M~Oq&|T0ZLX3h9UCi;^cCp~|P-tzzN9g#5 zE%HlLxEKN? znICH+XIn%**YUj(pFH?T0g5^J=*H}~qy1p;5fwppNn@s*ndwZ|!$PY)@f6>6Mv8Qu z*J7m5CB6%J*&+KSRKsD64sfw%{|k^2EPhjluiz zWUTL?ef$6y7k*{X<-QXQq=RGnzhDvIGH1EXz0J2j)$*5ao za57`e;98k9jCsIx`NEtR0(Bj}jAvLZ-*0wzN8fKL4*xd6m`dCBPXrO+xjdG_eLF&& z-q}3E8#oz6*52PbJ7l8-_e9PDFbWoA*zAXIB<|n4#sE>TF;Ad@J~)*ePQ;oNPQW; zdNuwkRcN>B)Q-p8P=CJt?E(_x@Sh31Koi_7MYp24>VPGh8KB(2g;w!+Bf5%5G69V6pT#fz zZTbtzs?LlujQ%aYpMuiAo}QG?aOU%rmCqTVhWe=?5|?D8lGM`XG3aK6O1+g^V>T6>|lEv>q9p<^L3OFK}BwJCl7p@F+lc#^g_N3Z9~ zd4mHuKQTj^I2J%I@Ab7@3aLTnY$xI*1m_p9&wz({{&f!6pY}T)px|8|V;NJaX8<(( z>{PKq4HA74<>9A6EDmgwb0$sO(nw0(K{AF!p~u2O(IEg7=f6T4)wt)rjEPhq!CFQI zQI|3tT&BR2Y8e8?f>UzTv9FWNGJ5(_#{XUFwG@BekM z+Gk|Lxsaf>A7=XhZEv-nNJCOQ>>K$aCA0&iiQ8r%6vQ zM?~&UqDT80UIhYb9hhwBMnioT{)I2VKYeR5l82(i&I@pVQnk>K;0otef}=7KAKpV6 zkAX&b2%xHlp{$(lwO9@S+G^&rk(JE{19ux`v@qq}Q9FwS=9= zknMYfvKY!`=p=?J8S2c?15A;^&|Jb=Vfey|p&uB^+J(@!4Al~E14F|JTZ_<$@GJx) zD-o3aH7W0xY;W=M=8&?K_XVbv@;=GXT2gt0p^P68@-igl9nX-I_a26_SdQBnlJX8` zNXk2qq1oiBm?0ObT**)!L%9sCXXqS;vUVfXouNvGj%R2-L+K3J_8@fdEQDMP{mhU^ zawkI~$;}LjB)?!tB>4eDwM?;`A(3f4Lz@ZvH$#Pc5qg;+k@K?*iJa>g5;;$2XcqA% zGb9p!fT2fOwlNHe#BX6}E%Am3-j4|NXJ`yVg$y+^bU8z>GVKKn*?vN(2ScnZmB~;c zVaGBwfuS^p<}>tL4}=s$dl<_48KLbARWkHnhGsFej-mAotzszS7lc9#y~BLpWvGI% zHyEmAXbwUn!i@+`HCh zO?NK-2-)Cixn|tFE9S^sFDS^9b$sum=0{kb@Ml0WCu;(12MVRwS1-?B>-j)jrG~k} zqwYZQUEJ%a$=2iaYk^pfEr%R^--C;Rz2Nj(iaD43pG^vP$>4NmXUY&~SC;W7yCFQ+>l3yZU} zoOl~J!yaNUfdi0Nr&S;0Sl9>;aVyzNsCiES8S|IS=m~niZwlP~p#8O}X}_aPI8K4v z3JKfcR{v_@m7;Tseqq6JtllVi$_@iqh~5<=@Vih~(S^YdK5=O?Epy>>8rl5L3=8+Ky#jQE+gPZ3XE+6`07OHZ;)x48~%d>W69}B%UxF z2{cT>`^3*F;!38v5U0aku}jF9hpO*!XfDuLX5WlS9e3&>z4(ny=ow1%pbaI;CT-}5 zEnJ5ToZ6%Bt(4T7r1NQVyvG=X1AWyv@;4R7tW@M_l7S-(i$B$zCCsIapr?nE52vr% zfpR@`s4ej}p+s4wfg)ewCIR&38!Lszbe3FBq4Vvg((`(>?JT*YcT~qf$?&x=gB6zn#XwlHm}p`ay?z#6fM9aO~E94bcOe zMnv8RIAJ_R{gGqWaBb_>PDOU^5&XUg0R)}?uuFj zT&cZ$zhO>3ZjAd}&WwD^?Jy0FnpYSX?2Oy)`8H1&C}u(%r$=CX?-^i;Q_7-BM-A#& z`w!yHLh21xZ{XiN-96O1^n5QOZSL=J&~$>_N+>g4jxy)6)RCyOtCczlq8RJ97tu@Kid;SjiFZ~~kvP{pSPQXYsYOoMB7hcw<1jQ{2xKvkg#h#!w=&hb zJ6O2)a^a?{!}$4Ziadl1?eR)+@GUDsyh)Q-|GfWT+K>at7!7Y~rFs>{3tmKPrc>RG zDV6G%MygMX+O;0P0F+`$V6O?gYm|n;q^NI9*a8z)iZuJfOuLEJ;s=8H4$sQb;JLH< z2;aDJz*EvC6>2`daKP{vcnW4-Zy(f5oFhyOMU$1M$A49;z=U^7rmeO(+kvMO(O&qb zEpEhgws|g!oQLmtd%+-_x{Q}+aY3*pw=6Y#{pjNSd$Kz_rrUv7inj_(Y4asqkv0Mu z48T;w&n^O)2xw^|U`l%wc*S~+Nxr$}L`vCS7I^t{X%c}?;dT(00y?lBk?Mazxj!hE z2HGQSd0=~4s;BaQPvQ!Ypeo?f?Zw{7*$&J;<G9(yVp-GMeKT~0zL9j|X{ z?R-=Pl5g3c4f}(;So$TEXAYp%1|P)+@kgK#n{R-6PW-UCc&}ki{@)eje9e0eGY98z zwk-Q4@~-Wj(WMuXBs{*Dfn=%x1&*8!eqn_qp64WVKRSu!IV~oT$W90x zNS#R}*FsuPQ`L%`^^5g%cvvDhH^*2R$_T+?Rc#wc8zpumRcMXBbOyz3wjZ?DwFo*G;|0n+*7dFQwJM@HS~>e zP;9ghbl`aRvXi(}71N4@^Q)n(tQdv8$OH@^Uz`aim`3sgFi!&R2H}A0_lyOb)y5N{ zK37tLMwcIdg_uqQ16qCFW(98qQ%t=OczRL%K_Dmf59k}<3tyijUxG;dG3R|kh231> zbJbxcKxeL?)A#m;-UA)nJ+Uye%N6vbxtg};x;kz|OW^f28EEK2M#doxMmFr0)h#So zvxysBmz2Nmom_kq)c1WBR;99RZuq~Rfr4NyYzjs?`rZy!bxs65+}42?#kq*2PbjX* zc$c2$C9*TH(Y>Y2_}4cy&}FIjR`x>Ce^6tw)h07UOXbPQWC== zk604LQh zRHz3ki|zCB<7K{c39M@#;CwpNmVShnI6uM}V{Jxa)?Z$Vop^xweK{buU-BmQo<(?8 zWQ$fA-5*h6g@hMIR>$GY<&5#GB!@oZ*URtJ@p-VF$TolN_`dqJ0+ft?oU8lC`Wq7B z6b))xloy!*g3|91>=n=*%p<T$_%1}k7LVQwTYMgpXj`21JKD(gmSnCLqBV$) zxzh;oR6SjtWy$8|KBz+h%&@IGt5#m+jpx^#N(D%`Uyc3mJsd`bDv$}x$@{1tsQ`A^ zraw!XalQ;|%#y7my!e$DDn@m(Yg^)=-ne^(`kR0^1mL-_xx|(dn?@z%H|?Pc)@Jo) z#6$h9XI~2*02_Sptxoe%6yqbhG|Apee}jwIvWg`)eO$&HRZ@IxP2WsSs}N3ht1RKf zAupbB+YaZ}?P!KiW>*OMI`{SAyMSu0XMOS0tM2Q<4-lYFcL3n1`7e@rue=uTB22Uo zan#%aGEmy)-PyL#X3glcP(-b<9{{(Mvp3i#jHzeL!8h*jjBMxh{sDg`pWqWA?4Jw~ zDqXyw(!f1xUCWh!pqB4Jv7=S-`U(v(3p4mw-9BlhLpPhiYfRw3N#r&Y*i(S+p`klW zK&Hm>F9Q}tU|%N3AnAA<>qAQ+3+$F0u)8Pom1Ou^d9ty%c9ai#!`jjPPQ3n>VX|e5 zaMMe-tI}x)sP67%62Q*yyHMZPen7!)AWxKfIGqK1A|@t%gLoOUjqudF**;B8kO?>X zY41c{xcD35ol7Ol8`70}VAITj9XS3A^&pVUr+ zJJb#>)$%dNwcuYjn?vT$&{ui3hm3Fn4R1~}Dkv5RK#%etJQ{7k8 z>mE;gh;8xmCe*;9!Nq04xeTiE=`=Jr+5vx)82Gw^OL%91g<6466lxGawI*l}q{&s% z_dNNyhKeo_%mg;*OTY5OD26I7!!@DID6G2MuBP2%vkm_X3z=}=lAq3{7=80mht1MU zagA-k_o$W@IBZ41@YirU{^`o+VuOvDF*z)J&)a+}M6fWzc?VSzBa5uMJ)@Ef(41QEs#FoWC^D+RE>MalaQkFW8_+>qJ@%YR=$6)+6?I7>jVg)H`oD0V_ox%{;i^f=?mHs|kf4QzUs z;aI;02|%ON{(X7=chy@6x(CL`A+s^R0_T2^_aKyCWC!8{Vun zs58DJ-GOP2N5Z+f)IXB~4tnd+NW6c+_%c)lLDD*DbMyu%$ogzH<%?~AU`Y&*6El<0;@P3Yx^twtHr##93?BjTgHeL zb^PG~J;pzW4ak5a2l$UUY6i3oW(J)MxU}|v?dc)|I9wst{YYT9eT5oGp#~=u!mrNY zDK!4va_kzcIRP_JmWtR~UjmIrSAQ!cX+}o3Xn3zic=)XD7-%!nmNEL!_XMO$cs-A@ z=6k5DB00Z+Z|wdQ3pYvj2c<(H=>kmsn+f$+z)J{@C=V8A7sI~7vlLovLs}b7hZKTU z7`~!3+A=T=`qfM(#bU0J36r6yX8nWm?`jz1A$vHH-K6@Hu&Z4y$bdhY3?=S&==CHl z9Z1U4fQ&8@X>UZ@vf#M1WlX5rks=vgsrYWTDbuz|*kFf5ujDD%O==H5$sev}Vw_z!9{E)cm-Tgi;Dd6c03XkW4%`ow6p$iX(wi3DOl15 zXOb+Q-joq4xCr#aQCBnj8GA@x-j_a{g==BwCgNoVukh_| z$?I#;yZMrrkt&acgDf>l%?Bo0T4(fGmeP|!6KTGb0o$KO{T!lC6LilfM8PgZRt$|S zs!=);vr%{o!n1M4f3(uhNK)F9T!CS&F5~G|G8K3xrnC(k;6Z~pg9vbK_CE9UV2J{yMj`qV+pJ^`P_Qe8Fv zy&As)h7S0Bj)+gusc#<;`UM)lPUAa{h%fSZSL1il`0F+PCb(HRjGr!%jTp?JbJp4! z&H%3ZJWe^h&0dWB|7sN_{0jk}CIGsm0I)m(a4rFF#{u#B<#{FaaNJ_9|Msk6__LQb zA%1A`%8YJKV`6%V(Z6%Fo9=&nO?Fqi^fa`L{l~f=FslB@Kj?`T=cjwNyGE{d(IM~V z4la8W9>()snNk)^`*1UedWKlzf%+P-VrBuFb($WUqHli(N6klA`611Ho*ksQ)ituo zbys6L+ig295=M`KE!VW&3!J|Fr@F5XtvaRH7VZvli8IZpL-T1|2xey zbQzQhwkBVGUc3Z9WyV|7Xe?a4mMhS=dg;#>FV(-#^u}*tEp|L=%d(FHjXXh{d8LkC z#8<(PI(&tR<^bC9K)Xt#@!c+_J^|pa0AHiQv`Yk^1n`OAh(1ja>%xF)$ri zRII_6Bh0+k1Y;hssGkO7nlQ7-1fKzLe+|Z*VP=U5?g4PQ24i7oW?vJ0c4@FG9r>LO zm`g{)HqR{8!7~vYpo3>2Sf+#72oBW2a}XSa;7^YGX41@q(z)e^3p1tU-fd88d9XGe zh-G*oFsIz8wMls99<#<`)kScuy85HAm`s)hUMG~jOYLmdmt)~g*%^hkZ|hosT#IY} z!HO%aeO)T9cDB@8?cZ2!wX=1F-;{kElCVaKYu}a%t6j|cs-3Mh;il~4YiF}UNxf_B zY*reBs2Nt#Y}OXPyMcKQId}10=kdx0>_DC&e=W*)*_c(|u6)csy?+O-&s92lzK8Mi>0Z?{%NR!+ovko~STc12j}P zvtYcd9XqHM_3}T?ALmE}5*M@?1tpv=vJ$@Wh9?@iV_3 zY?BlL**!zOfBre@{Zim-Et>;2RTG&cCyC=#ap+VUXbo?Ajr(IiVh`z=M#tk#Jy$tG zjqJ|(Fz({xK`H!|U~rcy8H2~P<=TC??PhlAhhy- z+oyAJE@_m2hTw;Cb$qTF-vA>>{0#G38f+{}(049DQ)T+h70!lJbueRvbMbRAs$nkG zNQNQG1fg849ib5!1-YN11+CXDC=HKQOAGo7_((X<#d`ov;K4aGcrBgfV;>d%*MEX% z>E`rhH&QmB6}=>0etP5lbvYc=ZAEUrHFjh;G=Uy3Me%)}iI`}az9O(%bEvAg*K2h> zvZ}eC)v9`AHS=76GoR!E8>igoWRAWyDace<@l2=L)%^w9zs~2#>gc&dC*S})x?<|x$ ziS#}JwC>N;)YHS_uRZu{@2ELN_-n7Jax=6}&MJm0kEq0Mmo^(!M~wAUm~zZ;__-DB zTa%&b=7X*@Ep;Dy%E)>6G%?dX<9utdMdqZyqan+(qL)Soutu&G)`n079W{Lv8K^%_nhzjNVki~F09#0 znbldOv5}u-L3QQl$NVHU)sCNy_(ZdP@MAPv8%#ZB{N>oAm1Y|`2iddb`hRsF=UCJs z&~)bB(fJ>_(>peg4qSo4V~`ZVJM6%L)kx?c(R`&3`*im~={^79n?dr>^CZB_;J6N6+}E3D zON1l9Gd0{j5O1}Rf|`T$c$;V`%IyCr8>`Qu9F$t!2(WqLgy(%4WN-<3wb6gew~Q;X zbme!WK}MfNZ`=>58+wDJ?u{!*R^z5CDHFQk&sauZh!Z93 zI{u3PHL#2?5Na&U63+3u$z%M+ihd`R4N?1A4?1d&B?aFS4j3S}DJBBmZ`-Km?`vz@ zCrkc&!0j3$EOKXeB7u(BXjgRn=WG2#yFTJoq>g%f+lB$OtK(YuS-{&=ThkGAEEA8yrefozO2|vut1&yi zO2Q7mht*!sp$N)=%>!{aoNw(-&;@j!7Vb zje#%6>)#jgYvpjlk#f*SKza952>ewu{ssZxlj6++aG%U92;g4y>zJQJXT|Clm5;1j?d>OsW6>M;CF;(t0pWO^T2%OPO70NmY7 zj~p990_{u^tyB)kVG|Z`-SLvwQYacT}S+ZYxCqjdp94+=gTmbIzAb^eT4Iu$`AptuGSm}dPU^_R2gvM^<8QEqk z$8x=5Jz`aoa$FGkbLE(fb#&d1H-_hvjjJ?Y=aR3L1e~Y&x&VARn6m&q%=s?n9Gr42 zp_2AiLI(>!8^fEJco%d6w`SmEe@X_?1eiy(3rw`#L~D+t9Y-_)9!E62akw#@RtdBh z<7n8L+z=ArPVg7mh~A$ZpFp$;akLYNCcqU$TV$g3Alh|tw5~)G;PXVQGtqL1cAiGV zZt|2&EVBbqz=Hvf+-V}N!qimR-1~y!8X{g3N1UJ$10W6x1WhyO582?iyUAfmo);HElJ=fLjwY`o)GC~;twPK+i`p@M-X4& z5lAiIUj1!h9MAd3AO`(;h5RfSEB!nLH6$)k(DS8Fq?hXYjwDOXeR}m9^Jl4-eh&)&$ zpP@M-i1jlCPTf~3h2z1qlJE&)9hDRR^2Gib`1?u!r@sn!GQMknI{;e+;66jt zN9QW=PEyU4U|1fxrAYSOjnW$hb&W|~`E9s`@n_?_x5wISb(%0api2B3c}AW=Piu`o zV*0?lKC+gJb5d#_YWnEoJ7b;dAxM!%HuZrZ*6+Q5n$s{n-=eaeMdBwU;S)rG7Z6_`8;#>%O#E$7 zZ}b8uCse<{k70^-qSu?$!7)RN z(c8Na_y-g)*4_(9u}%PJ?=YGKuy#LyI0ODUAQx}%#Y7&dkX(Z-oS(_h0xn9G1>cl4 zt3&E{Gf3$A9ZmsUYc|{SSU!~V$I%JdTZ z%om7>*Y9ma_Gx5}F7{Z+67xD@lIr&$M_Pm9_^lSc#5_p+#QMFP^v@D}JztwZ!10>E zL&%UTa9R=)lScx3VRf;-gIJptD}+vA?58sO*`&JMAlF`6 z`O%ypy&NiVkbnmS;J!#RMi5p0c|apWk-G*nDVvR-;A@1bjp1jQ{8F7f(OwCB1Tnjs zn6DDEqhP`%f>h>)5P@thPACM%BPf03Tg+#q>7>rzPn^ByVgK?{J% zm+^>|m&BX|)VThBNaPh7nflA6GR>I8L=cmtzw3$rL>#}u!k3uE#81@UZNwib_*#E= z5pcaGFka_G5cOj+6aRwhHT6r!k^J>+Uflsk>rl z_mPeFq02G7Jq8O}szCtmzw4X{qB4C8Xyj(*%+a@wv(iV35%DMb)lKJd)b&d}z;U8H zrmbxDPk>DACHdbw{I49T>OJrB3I=f46Uxtmdii;CK7O>O$v6kq=>J%_T z{YtrA|E{?$L9LHsCyPuwDFkLiF=KYJ*WHj)9z3?+A@dJ*%^j zC1x68lJx8(;-4SKpJm}o%yq<1)U%U`|2ygz+{wCDcLH__z)d?j*CHS>86*(5lXI+C zA@n2CS$6WfTSeC%T9Bk`gTDB4U3;-I$xgOQVfi(GVkgfgfBiLorkx~+?ZD04<93os z*#K@PIA$kLBR%T#A>%rMUrNlFoxFmWTW%+&wv$>P2&Qs(Ag-c~6#rw`V#Yr6Qfu*C zzx8cQr!5PPkqOX6;_&2mgp^>F%u z9`W~YHDuh`q)|m7<2TRzt%PQxDrcI+-0;F=?HtF(o9u2bBQvR*8H}$ssdMAZ{40We z3>;*Q*%RXTBmSqFcpn08Bw)D!+0abm-##tPx%tgEk#zErfqZ_J`V?+B5w+OV|W}1oU93O zm0cCY5&23_gl>AmlYxco)E=lGTyF<9xldGGV(2MX00 zI^a?#=|F|*q65QKnhsQ|-`5JkG3rMhs8SIfn4mUGV2Kxsq(0N(T83BYa2>--b(jlM zYQ7H7VfbYop3Cqo9iGqdqX_HweQ|*I4DMh08UYXNq`xvef~&>?Az$7TuHNP{mN2r{ zE;Uq=t-=SAWl;|d-`b>fmr6QfNIFI$-D#4JYeDgJoADLT@Ar?HA2B2yBa!YqKnNdL=6%-_h7^sAH8=O(3pADH>` zh$!h9iS!EqMQyP=Q?KiAA;W*yVHd-H)8Pt+{W?6H;Rg{eHhh%`<0&0?4|9LqHwJ*X zyaxj-e;$z}Uq&L|0!^n1U#gc5Phj|L9riMOvJTfW+(n1$7`E&1EQWuXXtvC+UbG=@^N0ex@@TnQnC~-KwN? zBPAU%BpoA>uAJ$fSj%+lV(Hc=r8{5J5kt~366sC^6!o(iU#g=HD~1nzAn-j5@73W} zhQHHc8~POWl@6yf{GkqKFx;fWnG7$`;VgzwPd}|lyt=R31>Y1m2I2 zzqaeI3Hocjd@ZRZ-$J%U1Ef#_SV(|x$y{LIbjy4q`j*t|uSfLPDt>vY08eLqd2T=` zM?zO4lqaDc2)P(?cShr?KuG-yE^^SBkBGd4fY=dvJ6=7B&*)nAcS~}KSHtgx@}0qN zH@}~kZ+2a36u%#q??!$P!FS}2gmIOzglwy;3GWk!!>ejbzf5YBc8|)30K`3NzYNF1 ziqzU+xVcf$u67u%eN?2>4$Hz1F2{Ihfrr{*XBtoIcMWjlq#p_w;u8oq$5Z{z;tkK< z-aMQ8Vz^w*1I^}s+4kCgxH&ML53ttu!_AKAe0sIE-?=z^m2h4HXNUl{d0OHL@jgPz zQPO(gu8?OnyVSgn!!$W4p+0o8nFFyZXh8iQl$8z2JpYn#ehJAWFV^{m>a0GfR0tcH z!#!3VDLwvoOvlb=)%!6@^Vb6As2RcrYAj(%nebe-^O`syP?D=t{Q@yds*)T>O&f%T zBsP+kB#U$s0R&14b&};)k~~Mv8#+m=l}n9IA^`aA)JbMqNnDPap*l&rmCJQHi2wp6 zl{(2yR+8b48X5vH#wQOnW2fsR0tl2;=_Ea^Bx4*k|Ar8dB+E>4j*1q<0Rg|)lG{(K zViK6(z8NWU%oIJT?*f9#GGCGgZL#^l~h?epg1(l4pG5T^V8ZQh!ArdkQoQz&O3enwS(UFPN9^(lS zyHRikG3F5CmliYo$Z(Azb!ikFcoi5`#Q4<0;Pww_h{hNNUlXH{7;jq`T{VX2o>8!v z7#YNvX<__`!M)gXIAEJj?+@|idmMx5V0l-DCsotovg(07bF{)^b#DUO?uAsPZ_y5J z@Cf^Epa|7?`-}Oe6$d|M#+`ZP{%_qEyNvq^U4ge~@pvMt8hnX%%UIk!0=Kd0;aXlq zns+s(X2wVtpNrB-$5yxk^H!NjueOrXY`L=%lU5-qxsAKDrakNO`YUMKX8>C_?K!AG z=Q2pFP2>~(5LPpRe;mPv)5784^LO|4Ml}v8!oTkJ%gxmAh(V)F?XOc>mrUk>GjaOj^qIg!yk{_Ja!fjj~lAw5wnhy{Nb-0W3^c|{g#X*QXu+mYp zfb3?NU}b?f5`3}={=@?JBayRBaDxT*5`3Nse#ruVMIw17_)!bY6ICIn3HDgv0@1B5 zqh3xDVk3mOjHXcyfEXf~2$}Qe77>sw3+HtCPZ^l=>bllq;w^?yWhcCfZc7tE;@VLJ zYkEgZKj4t`XG;2=xcrike%8^`A9G0hEi8TIBgyH9A3gp1?2Th^1;Y&O+ny88Ahk1bZRCYGFD!vydmO1i>ROg`qYWsAU|BC=`AtlHY_u`;I z$X?e^y?n*v5B;V!zyJZqpHJ-dH8S^zJq)am`By;D%HflRsLE z&#r**uvde;wTqa6D7X4|6YvAOaS!Rq6>3WuA244gaZ`VSGWtzOGZASdfrXn`J~%Gm zQ3^cr1-CLhcB{V|@tVQr-%U`Wy~Dj=I*^HchGj7B|LO5fFq{UU*h>~)Eu?@KE+b}O zg_;Xo$(){MRzMO7@7w)*m-RD(Eb7XwiF1q!H$Jb6BD;JJmtQvcF#~;0?dV~ zKz@#z52_SpxbvTj2a(po=)NL&gY#U*1gw|+pbCJMD`ec!`%neG3al^`A3p9FnAk|o)1SZ|AOGK97Gqo42A(>Ow7reIqpMz7L#9b#N=5= zPj2cZ{8YvHO6+f9{!RXn9vQQzmg!WIfkj0w@6;YB;^R=PQe4_Z&&-{Pb*`8<8?1k^ zhlbPR<53$KR2zMC8Zw8B-0(vmT$Tx1(w5YJsDUPwQw$A#9?(*FszIkT*9)jTD0l^$ zI^O1RXP?P6>94S!$aUrW0R_bhrp5cd1We4FlTqmCpOP&ctCftKa7!ceO3bAht zF3Vt21SM$?#5&>aJPT#~2oVgQgYYRpX!cVz$U`x%1^Zd)@?Z$7xB>PBtHzzpw0`0{ zv1ya3_=;(*pGsOzd6gUg-pnwcNTlE4&|vA@cB@%Sr3KZDA#j)p^l6}*6?Nmk1s@ueQffS1i!T>1blr;{KrS50?L+hW)e6VsjXhU5 zLSNHq@c?2xK-1R+l%3qqK}OOgQ4RA6D4Bp;=P>SFe8Hnqc~QB?II;8DD8?CsofN5 z&6@xx$DsL*6BE%KWTMPuKcBRkD+j{ly^HWjK} zLm4=27Jbszjc?Cn*}{3FVT`9iAtV1uqMIK1XPnM$pp)PPHXUh}$PmcIgbxZLTG(aB z0WYzgsJsTme__Q(o|Sm3F{n(&@73{I)ESZU;`tK4p$8Z@r>5x1b-rt%vJpEFaopoQ zAi$3KmNmk}SDpAb2Yk?$drwG;3EFdlG;|EHfq6IiQDxq1?m zH3`XHy?==SXjgRq5GWa=77~aFpXrWFP2Yt~gG;C(f&?Fz>Js3L#@!jy^O^Ke?puIG zvm;n7C27)&x_}udNlz_FLu_3O7A zASh~YFEiEiF_4LtslJFs%{e40OD%~-y?jVio|+SjntMo8q4LL~<{uK}Qg_Fq-aRC$ zLJf*V)gKZyT=kZyGRIq+>AdQWN^?#j=XFX0ZJc(M27Z;%%9t|aEmn`qm_a)a`^Wv_ zEO<+xblvFNZpzLnvq#H~*K#EOTlcX}@6^*^dmn;;(NmQgZ{?8yET{iD1WJt=F8OZc zBNGmPAA$AFkfBa-Har%62VHS;wuF79#XVtDfB}tYX3iOEN4+^Lf}8!zkOd;REosA! z7W%K^?C#qCx)5Mzf=BS!tOs9 z@Lf%_eH{BMq~IHj@-7x$PJpwwjM89uIMQJU&Ftw_2Q+%*+a2BLm{=z&?hguqwMn^f zSt`0o&u_?Msqs*z`V@;r=0^sKFhnf}0N5eA+LR4?HJ=c8e}lw}vt4j6C%3TTbo7|< z5sOfmLI!Gu8gIftj}lj-g;Muy5-KF2hr}Gh&T66WR>f9|m1OxcCIE7tD|k~o+z|r0 zIX{C0Tux(ZCa0*Y;LuM$M!d?kZy#h%ZH)>1l8Z@wDki|FVPFdabvA}@91svUL~4r~ z>)cnJ8H@b+BJ>822$x={0(#^bG3AP2-n@9mp7G+BpQ<0#}2ki%3PWQfQ= zxPC^3vs}4Io~zknQ*Z^ZPG!<(%%p>mG$w-FUh6=D$o$jNn1!!cfm6YimTOsHvyP^n ziA8^VArf&Uax1bfGw#XGL-Q*H0+`OjPygW6=a&VxtL~EZ4)xhPI%{!mkBo^L&$upu z@*|)et5Bsgrdf<-JeCj&FE+Z{FrXe(wvAlO*AR1C38Zs9Vi5jvFk!JI(xec5}Zi8g%v_5ZA?z0gK z-N-Esc2A3pl}@b7rY8=Y)2jEuHAckUG3kUuRnR%CKcnwJc@+&4wZP|UXEqgYJ6Z|e zD*HQ_D6AbT_ti0ME;Q}w$J(Ca2y-l9Szu)ee74{;b7|mnJ}dE(kU5AT$lL%j<$*{! zYCZtBr5o_f1g3SxaG#W}8XPQV(#6Jv`BLn>Woh6KG2WPnbSO67x(UKE7FFVZv9V|r zeq6?jRro10Cg5R?R#)l_inH8)pbYK;Qs1IJ_~GMFZ1HVqS$Nz)pNRJV>hR~909GAe zAoO!VnHX%e0*H;jdjJd??z=eaquXh^pN74(0r76T-dMqFPkKXU?{TnJEBs#%|BL%J zj_Z1W$%3mN&$iX4Zz;ko7ym+dyvYV{nrm_FAO&g#IpoCWDKN3(kJ9vhcxXO`cMjAp zI#aJx{3`RxG|y6*2$Tg5s<$!R=e*>0&Q_3rci4HlF?XV5lpIQ0!+t@ERBAk$-3VZ> zb^0YGSO%0YPj!Y35d^yLK`@r4%-;6@(e^Ijbyd~gcUF>Kc5gdC(TWAS zC2Ffhx}-=W1+wVc&5oo{rE(EOj$nlYLWP9l0U@^7mBoNH@#oeYu?{~uC129-DMqo7|^KlfX>bKQ%$F3$%l(eQ!fk* z6qKn5PTq!?H$JoI-%J9#;TnCffv;f|lojzsEi8?^nj0EuYS}v4J@W4rzs$Bbq4N)- zW8=nFn)+WmRF*&V^C58L4;qe)+#pNnFj+FmE>x~ughKtE{Bh)$rY2q)RR5t0yg`9r zJmd>oCQn?pT-h)7)(`GU{@#CRd$_Q00Yyb<)u;O)?_brv$SsR!*o2(#9Ajx>c8dMS zSf=~1rhGf999|gmhK`+^I+5O3~wL#uA13@rznLB}! zjno5l)#t-aIbUZZBDU+7EC3&)Xc_-9?yqiRVzFif_g5!o?VZZI{~iq*(@q|5h#QPz z32aG^?KV1KQ~77;pgTne9|sF&1uPp!2Yx5GmPRvaCos%vR9ih3b?8`GwP70-C`;S@ z&*GjYBYH)9<+C5jDSupPPhX~mMA}d7x<7w{4q)3y60aLJz1<&uOOQ+438M)Ue}i9^ zs_^}8lepXeW>2xto0RST%|u9+mc4yuk03-D>>c%32>F+OA5kg_`SbU#nYHA8B#3Ju8~FFw8DXq`VhZ{4Yk2;rk@6fW(#(k z{7)U$c(&f6O|vkdD+u8PZpwz*rE z?|IOCvLfFsp_YWT&3}x{<5k*|=SQofn%dM4#Td+I>JIo<4-9+~#h1k<&i-D$H{a&U z`ELK4*>;$BxT(Q*Nw334q)i0$Dwhk;+nwW=Z#) z{>v92jV$i#sc_R^&CHkXIln$%v)3-6+zke9Lfm+~8+kHIBT80`f_W*x@`vN}p7&+5 z&-pxt8OfhZg)+t#V{F-rA$smG;A>P5Phllb03m@8kw$mGpUd48LV5 zJeXyTkgG1c89tw4{4gZ>?e9X8x>YP#ft_MTkIdH!Jm5RO9ZNC?bV zUpEA3h-88`UeI7(1*3-7w)m6D?V%FU8>p1{38`cN<|$!P*Z&DQZ@oriW&*0^l}H~C zsE^`4eQcm;(8tuUkDZ{urw`ft=wnB3AHRXZ)W;{oKAxhFOpMk8d1g>h?r5C%kh6#Y z$L`EHGdGo>Bd1S(5&44BRynvtcV=L3IdE9yorlALoX_|l1@z8e8@ue#4=KNw%PkO> z>!+b5hn}nb`>J~wSLaLF^NQZ7TBovX^TMg>P%}#@t0DaOw*v8m|2=-qg|eBi8F7hy ztY@l!aDM%P4^#j7u>PH4{o7QZ`~JE!9}VlDPtV{IjY`O91QDWz%ts6^STq6M7aO{V zAi{FG&rkE2xj!h^3s3Gp82f%mPUC4tPMhy=WPWaBWijm!ZGQ;_pdp0_xLXMLt7w!W z-I;Pzu+MmZt>T3}#V2UI-wWHHPWu|g{ptsv*`2vA?57vLubyB30oA`PtpBmF{uQc^ zTH2jChScw^`!UtMs;BQ-)x9{ZyXW7i!--0FCLQc@PQFOt^TmSyKC~u8{8fc1+nt#k z6khcY3NI32nnF(mg?{@F3jHu_@ljgT&>!q+`LpMN{;2B4dO;VK?+WW)qdJB>`co&+ z(>c%J`-8@ecnl?g4bo|w@1t%&^{@2*@V7t+{Xxr9yJ2&UOX`mUd0&jEFnshUAdwBb zzB69^bET#JI3k-bN%k%Bbw)Icyz?Yk&DSZ`!vgA6mG6BFP9)>BTctkaQ82hPnU5i* zN^J(*bILPQl5xJU6SB|?WQOkh4I5>rbEP=^-pZ_%r~#o_zBf|=wnO`hWbf{Tx7Giz zJ)!J9kjQ%sn@;Zg!Pxfz7yT7WGlk!4Y3NwyZnR&f;W?1Km*VaH)t>gs z^2%xNzd~I8v~PP-p=m2rq2T~yY0ntOkel5Zsha_u#`$0zU|)#c4>PC2?r;&AtnNYi zfSz-5r|a;Q3ej=SgFp~!{x zpmWfFwk^lUw2DrJ*LE|WWltJ#S!>dkO6$9+$nW>b{em^Ml!`q+^ zzArfqa^%|qsjmzSJ_0Zp=>`>r`}!t-Fv=P?ROX{5Y6hC`O+@x{j+UvSoa&>0_1IVW ztR{E!Egc2rU=NsrJz&<}1TP|Q4(CSo9?jJZr&T2_t^{uFR(~^3zwFVs`wxR3QQj2q zUCadz2KW7Cd{|+WzEH`f9nTex^WXmi^=CESP2*Wo3+`h*4<4z60><@LVwEU0@Qeo;#r6lv+-6p4;MZTS` z{YM^RTvc)l>iYX}O=112)ne1?aerjcYNKOx?9LVg0%FcYS!SML=FYb-&BB&q<&5^kRt#GN;5iNY+%BmD7YR5#5pY&K4?nXGsFwMPNA$;a4ReC>PmT`S zT?FsRdHV985I1klELemFOFKR1V;9~J8`d8+t2>u=7eN6zPhUE#yB{wz2QM0>%7#y? zo<=e&qyDQ-=FYraBx&~Z{%;X}27I9{TR!*==!fhv zCSNVA1P9UTI(m)1_-p=Y{Q03z#EDl#;gG)eI+?RNRm)ih z_PeW2=gthYWEe!KKb}DPkY4I#O1&K)T4yh>+Bp~xmWnT#_P8l;@>l7BEs<8Fe-AIL zEiK#&NupQ~YQmy5x`qo?+U(WD2Mj*KTh&||P*aBR{pI(V#yfvy?1u)(&R=HrH6hjO zHirWKJe>@HQP|B8uvy8-xL+;3V@RL*yu%!#a0TwNdF1-vph~#BswM!ilV37P8Tuq-+f&KpedD!n2HO`XbTuK?FKGmbLvb=AbxPsJHbB|o#yv8bS(7b)O)N7d{N*=E^|>fT)- zc_xHpm;X3Ta_dn+FflnFFmJ=nn^)CAgZ^Hzdv00^({?CL`*qTS>GZ1JQCee=Rv4E1 zo6;5pX`{omy-Ir}NE;WX`AVw|(#D5r2bESHq)iCZx|H@nkTx+)JF2v@AZ=urc1CFv zl;$1qsvu0_SAQ06GYhiZ3|az_BXeMwNRT1+a#wQZfIkBn43!9<>_EBJ3zKA(knBHA z8>$hl{vqNwuzl?$&BT`l_ONrG)HR2G*(>ZmUhAa~2b61w8+{TWoXY%utQ-@c4o~V^6$%+x)`m*E$9K&t*M-J^-P&L^Fm0*l! zLDiTA#bL;nkVL;n&b@YBAOB4jmEM5-3(x-j@NUXN-nltk-vOhPZQp!RNrwk;1ixPZ zd-P4;AH-wLKBpyLdy^c8vyA-g12)wlWuP8P$tuu8v^vSZsc#cwZy)a*ZRRWb=9)xm zh?hu>Y)#&pKkHo(_d;1wrhIHwvqfIf>ZiX{bh2V|^vz9))bJlrtc_w-C-cj<_mr*J zP<4Y$XV$wP$`;&PMyaxOG{ zU*R+IozeLQ^jtosgH!8|!WUlBDv8#7lhwhUofBP7*X}do`lVs_HTmhZnnrMS+W z?=*~OVPU1f$7WrCmF25s0ucPH`bx5S)PaN?iD-iu{b_|`?d95(Z(fIS*Lx{kmSaic z%`D69T%{A%bE#*BZ(z~T8S=Qe_gitNxc47oG>-R6@eB|k)<*tB9(M$fQ+X8ABK1BV z#jALn!K2s_kI(TaE>wy`G9SGW<)fNKtA9qCxv=&YjVJgv0ehZVjhenJ$1%ESB?-U? zs+`o=kp|(eg|9(?;3N3tf>Q7+EYn~rLANB)hQXBM(dj2mZ8G5bdSvvddC1$OmwdPQhYp}An667aVEAl3xg z&RG`nwQ{9ANXy|m4R-yP_S6e@@wzwX@A!|sPd=gJMdb@uLBOXgZcbD!6NYli zFZU;O1=e2Ump87*T3xAZ@t~KuAr?wt$gWF<|NF zpQNJX=u7}P`le;J+nalr1J z7x4We<GtQT&?BOz9`#x06?Y^D)YLtH69*Rh769LQ)Cp3S-jdJ# z+)e(em304ISd(KldL43hE{f;8@LnH}&Y)H{p|=CI9sORj+U?y*mZjd9j(>8zB`4IMUWeQDMHQT{4C*5XCiJbxv8to*0R8=T~u8eAR`-WKEY zCKYzh7R8ID2eQujvU7j$dcSc0lKCXdv(I_9!R0M-MmlO7$qY)F_oVythmzBN?y|nO za;mr_@bvl=MA(bZw|hiwF>5*8Z`_ILT_&ZC(Yf``G&Xx*yaT`Hf%O%S8rSQyT`(Zi3Hbt&iHl_Nt~=K>>*d*{DrqqKA? zRArc?Q2;gwMA=z>2_cjw`FynIxWP%rMYTxh=?Cq|BP-(V8oQcg>i2-_HY; zwPiQ&-{U=*SJj0K`rka7kE{ojX?%^IA2lcW-0N=gk7~fbC_kWQU!U8(A@4aHlrc2@ zRr?~N+4nuI;|e!KKMR=`>Cptv`!OzgPTgsGDZhfjLg)x8+!- zo2E;XKN3eZ1958dBu zx|NC<@&Q)T8QMB_hyAu$m#OD24@{*{s=xpHrlrf&QexI=^;@wuY%Qpdnss^TUT|n& zPEJ2_I&NioesNAfpU3pwi8J&(#((WX%D;Ht1!fiScMEs5(8cVn<*8p#8@p z|Xi4FCOjuK0g7p1?su3paB)W1{yy>+OGR zf9d!BFYuLSNcS3qX*$ArltK%NP?>I)C9A-Hl*u%wetaz!Ncapu!o%$+;SDeJzfYdo zSmY5#hxq~B290ufVdrwQz3E(T?%PO>yk1Dx5#x;rpO^YD^@$Vg5&5c=zB_zgdF7Lr%Dn6gvjU#8METwx^dF;mwD_lI z>wF?p=IvZMcm5=)E~t5LKqne4u66}fE;-;S3@t08`0lAA^DmfAB zYOw%dH3gia?W{LGn9eb7j^w|87pWXPxZaV9UxJ3dcjoJ(1%Li5_dqTEbP%XSjhI<_P(Iq=+Z8K9&B);g~+Yi${aQ@j{JNgHU1W=n0!Y5*c@Sx`-jXn{sag- z;;%@WHCfD;u$j2BRC%5SKB7x+@Xng=Bh%Fo^eU&S(Q;}Z>xV>Wu*!;#z~?44yC2QT zG1Kha-$3Qgz0&7^i-sZ7W9A^CfZg8^{~>w_zSr#SfrfAm@DEjSVG-YEUc5={J5D4x zUwqb5?>pbZ6E@_3V`7B}uO+jGminwO;c+Gi4%aH0+5fm!gH>}u{p&BNZ&l{>UF*?c ztruvmUs(SR{}iC%NOMFGKDZwD!$;Qjt-mFGO7+v$dLR@739_SazMW+yWLs_N9R-mN zzIT0x|9h}Od%xia9zH6eNP=qB7L0;i#u3-!f+Q1y4F zs&pJ&1zmQzy-gJ7EbXR;%*QAa@~;PvXGVWlR~-cFoz31!UdhbL7b7f*sDayPPR`Vvr|p1m@%3o$U865ARdQn1zTl@pKYm4Tg_#>DXf|NxqX(u+=8NQ; zb_^jOgWk^T?+rluzv{21wx_~B?C)#jXZs82`xxdGgZ;l4#Le0}NBCnVzC_U)BJ%Dpd z`^(QitZStX<=T86=e3-zr@tgL%Q#EXu=E8>QB5W8KgN6l)8tfen@%EveuMcbZRzN|rj^OY6_xbmb zUsLx#um76t`nT(*SKXXb{=qE%Q&X+!RTJ-m5Zfxu{WWHl^g@J^CXq$lCd$WGXeRYc zplL`8xcx=579jibq28WQ;rMQ~bOEDRKrfOihx*W`l)kuHRC@%VnD*G&VbE%%#j~VH zy}1Ibr9NIbXL@w$kV~d}L#8J9!er6LnFox7vFkcdn3<5xt^8S|Nl4O|u}vd)PU)17WUvV(?Ol}Zv_rtKK&vJd&hwXLQ^3gZT_#9lezx6W@gyP#&3OK+$LCuQy z>F3=5ie7y6=x+qeVhyY@Oy#uyQIL{)*fOmcE^)xWIA{isukzcLZh!$S+rmFi)!(`k zvI6oETp9gFlzD}yI*;K9|4p#1b8aji_~c3$Q=&n7F!q+7gBPey5N@aOTEdMqzE-IB5J`!qt>0qtc)REx8SsGOX|?=dJe zzdr~}(@Pi}+z_;H4sNBk(ms3Nu>Im-P+A$+@#v-=>KF7SIdBzyl`6Oay&Y{_CwS-g zbxTm&zl^a2eHv+@8SLS9+V3o#5YUu*Zz&lirM9J{kbiTGg6l7)ot8=Cl%DV=jaWL0 zhn7j>`8Oya@P4em5OUt6(NZ}u$P83X@Nb zzu~EIPsI~e&yL6`zf-%mXGiDUOA9ADvigd}A=3CJb>PADzIMs0$jswwk-Wvn2-t*IA_@H%UMSq z6@?v1s{b5w2Q!CN-oGX2Gg|#CwTgB55D5?cYWrfh}MuNh8Wq6YHmV*ZW&J zX*Xp`y%N!GwjACZ4kt~0i~EOOIM;2};pyvmB?WOd#fAQU#;y@<5{(G9PXS}7k8xfp zh?vEm`&*3RG0-34STe`cVQG4IzbSbHdkGqS21S5mGyUhoE>H9nQ{TQmvq8BLPL*3Y z-pb&;*1hatutFZAhGv1d-GBN@A@^zVzAU{1)npwpcKBca6$rC8VY$Yy{fPft+FLf( zpghn2ET;07Nd*R5-Xyc2GqQ&TaAUmGe$hJ?7(VWZR(DcTww!=ImW|?-9gTnF+uG@E zt=Jy~x+kw&cR%3#l@S73Gta^5SOrv?fbpDce$`2RgFVx6*@f)4QS~mFKBuR>-_Rb- z9Z;OWXm*OUQb{X`^n3lxOeLQiG0?wX2vmV(!!?>@`G^s4jl1b1UQ}KfAcW4<`*K-#iul&|%#hu%En9X4Ez@WojfR)1UJ!2#zq})P z7n6IMF|63aQw^-T8cE{&m$!{ zsSrT*QIG(?96^~jdcjR3??CvBHEWz^>-|T9W(jmuv-j!^BSGG?BmA#09=a%;`DeN? zyX?$FvU^vj_?eui&Wf2Ol2<~Y{&%qh-#ED}{=(wpRFBrIWr8c8&F71)ZRV*TPdX1s zq@KMvuz8H2#`LPL(c$-0ad8ULbAt5T#d0!^*1Q;03S0yHtH`8@v8JdOFYwn|QlF-VtErFmG&MrX zh8C&U2A4(O<8d*dbenfhZ`e`)-GTncnft|*>F#V2KQuiDf+%2$Bi!T@pbtGXzp&J5 zioRK*6IqV(pW`Isvj8*QA&z&rV!lx+21cvpLs{l^Li>1Hf^^ej*_eHzY$C73AV&BW z6{%s4aZ}IcC-U+GZ;n!LKu?*iy2>Q=Y+xaUS8yI2)@}%Lf~g>9NKel1f}Fsugd8FQ z!#dv&a%?OB}12AqJY8vHV5fmtlzBy@>LD!|AOB%LN zg$ynZz9%kwE#hJ-XP9^lDtJRCI)8={hTQKAw}#IR0nf&W+m}B{&zBnDPkKho=UMJQ z)&nGyXu&lJJhx2JqJzaHs&;oJ#+**wIxd~bBf7}ggQ4Z&eecKNqL>kV-^m}<_nrLF zeH5lSi53~w>E)$|wad_>cQQf9V79-Lv(huj3A$|Pi5D^(6u43%f%66&bD64Zvr*uG zYbFC|sp@G@{GTfsyHFDhyHM1)N2I>K_$a+#j0XHNq2h@mv~}YDtA0pM3l6ISC*;>l z$Aucmz5haKuPKqtFO*(ZU8GjVaK_Y?7yaHay`0TA#nNWsfURr_;l%mguELs6b$9*_w&oFW1Sae z`G09uGiNNv%0FA0yh7ahKKdelpLO2oPvtpY^nR(=Qqhq>ref4Lmiz7gkDr!aRZ;I+ zc=;)We}t@#iK3B((U&fU#85_pRg0B2@nE;#-w;Q+@OYoFCnJ{kqA=Wpmr36;g9J6v zYFpzIg0gryE_7Ugmjb^IC=6ZEhB}(KQLE~n(I%Z4hm;+ALfn5KiLSZnGeGv37 zZfXX}mz6$fS{MN`tkU$Z{8DRMXEfjoIyovW+E#vhBKb*aW)tZXM?Ua_p1<3_n%8*I zfdD^9NV8`%k*%!f3S(%(jeTInq5!N1+jT1Svfu_0qk55cOp0GsVhIB?P*hX)A$0r}?FO^j`HJ z59p_1PUcaV4*S67mvn2emCb%B$eunYNWZXJ-}Zy{!U}WnWPekDz?S?4UB&}?NN|Ze zWXZRdq49e&B|L`k+!4S{@v5%=-^iE0wcYwBDn0ob1~zoeAB?y*>-`wrO*EI*fKFDGbtwigU#l)a@y0fZ}8U>tRZP+ zE=lb#%(I*Dnpq!y$D`dIYsHfW5dUJ0mD$g_1TdrM`w$wtW54Ma z)c>JrdR_T))SPK|pyu=jakW7d0sYY8GhvH$Poj^SPStKetU;^2^Bc++;}(CF{|jPQ zIq&!gp=%wwx;6Qf0vdf_y*Kk~h9LW;#7Z$)cc3xdxcUo3|8}YK-vIt1id^u$?SOv! zv&Rj34&EadybrqIeZU3p1AE?$-D@U-GeGYEFjzj=|1k-IZ2BBO$)hCwU4+ao|Ac>(FOA%MVku{l1mMhvXA{KSPo7ev~|G zGy3KSraJ|g6eMIwC3-|27@KV(iKUBr$Qk;oaKm@m|KjY2n+u) z%KZIzu2zJ^2XD-;xUVev8v(0I;;EYrK;{3g%%b#D-ynqLk<_4Y$0gh{BF<}~B;dfw6yUEg@}c1_0~GY~e8D?nH8P|hpP`cxZMpG4~aIjnyZ z-BJI$=hfGB5`Fc%Ui-{ZsZ1gaWIcpIJo=4Kn3hBschi<)Np9?EEToMQ=Z6r?(O%Gd z%il;gN1qYu$Q77#EJ?Rzck_?Gnc*9;PJG+thLvwG{cATl%tN>TVay3-HLr_7Ris1w zMr(#rE+iw7(2pNCIQ~^KDK?1fu&ibDg?Ce-0>bm<6{Sxl=9sxF+#cUexnQ^SjHdSK<@YLyd8YTAmy!OV5ea z49i2Uk-e;;XO!j7h|NBQ%|yR9@C!`&Hy92ct-cqKlzs>4WnM*2=C|T=Jr=`WI%Rkn zPTjKSfXN%%r|8PFomo2hHIVhvNd}u)_$th+@@=FT{g{igMCRG~W-AfS_Y1TbFoNuS zPw8nioNtU0UA<~Y!yL}C#Sd2dm#{*Z@u+6}tzP*1%=^3F2V(Oj1Nqn0q^Gg`Pjm9L>U(MJoAyPm?)m4$AYf7DP&lzgdV z0}Yw@mAtB2*P~&wsN}d`s@gZOe~*6WemZP_s4&>x0org{Po#I}`Aup`TY%>BxJY({ zy)Mqps;T@Y9DH)hFYDoVrOENbOTF*-sX0D|U&0j!PNXR2JWx`P(BBaK$-+ z)a<1n>`TGNc&c6~$SEID$^M=eD%g*X!c<`H`ud$#5VB7vCJp|h^o*nKt}GtR_OYcT zUx2lg479F_f4!w-i1+&H11%*5=iXWTa!bkZr>A|ofLr_zzPqmBLj5GmAMuiXe5{+i z6I0D?`N{i7yUE9gyUG61)p2iA^0tBSoz~>ze0rkmJyf_bx;(lf z+Ui8tc%4pkZFFUHjU%JaX##?hFAPjRMKA)-hmy}0B%k6#>-QCQsiD0tPrl=w@{T2+ zf=f>;Jm__jbQH1uj(5%_+;e}rrJoyHWADd@{hid38-Mbx?|jK3XKi|Iu4^?r-bvSc z%eB|KvF)z?*5uUOi&P>t{|5bFz**p(pattJZN4tvkHMy?6 za&l_+AXRX!^^SMgwa7p1*hidLXL_}2IQE{@k_jpiw^lmDgT}Tv>2yEGKCE}^6zi)l zPBLlfmAP?ih2v#X3$IrN$KD)|k+YI^GLCh~^){uO`|;pybM0+hWRzO?DV1Y zC%q!q-fo?6y)~&{Mn$y=x-YHR`-PI@^&Z<`zzAh*%l=AC9V);8B$&v;^|g&uo- zNowu~)rJ|7881)ZK|_iiVeD};_MssFt#OT?q1E>@Pp23lLp89U3XxB5s~BK7S~Ju` zeaCnxweY&IxkZkp*#ZANc+AiCo&koSn{hJ0K7(UmCE48Upv-{Hz#O~Pz1A&U*k^#l z&mW*;`{5Yd>&#rch4US+t7o{5eek?d#tDmrna%GRXGvXw*q!dR z_tE>c<%J9FQ(E*Jw5r>@joudf7-T}DPJ08?beY=Ks;zUajdAa=Tb9?ny1Y$@F0#bqqak2Ug5!jgEB`ap-Wvo826@ zPdL^F7Ejzhz+ppsvqOWFOTBWf>cVXnVl{!cxrMEcwVe(YFp1V%_DY~*Ik|SGy}{e! z@p>{f|5DX)?e_rD>SQ@AG1y$>q*t-zHrtFSW1nT3fqz4L6P(6d?QJeeExaPAr=dAw zMAm-CTEn!Q6cXOGo33S*yY|WUCZcnMmUwFZb*f@-rH0m^wU6EwQLK$Wh-|?@(YrP-$_B!cCmfv1#&KFc0U3)8)4tQHB6;Ca>IRI*(FydNg zIw=r&0A3Rua`vXBs zFwa&{68Bm}`kJ;@kYOf`wMw^f>!9OpbD(9OTqc4gUS7DwP@`j?bK0AQ3~!fX?*}DA z!%@zM)DReLASP}di+dEhuW*A}VLDr(fE^^OwFQpd?AmR#VIKs<`F1<2bP)mdTVN30 z8m}E{U1C-%9ogxU)T`swg_CY&81I5X*M=iD_7mOJxhxM0vfTieY;Sb!`dd=-KN_HL zk5Fjs@m8ms`tgINPH7(otF^UCOQbRN$|qG;Jgls6Aza+sWOe9ykp^!c0TJyDj#eN{ znv;{Aj=eITsvcqBX@~!?THiuI#H|ho-%VC7Jm2BEJUc@Y%l<}F(ynJM0X|4rfE-~W z8{^g%2R_0O+Tval3obR=He~@eZl~h_ERa2&)yM74Ze))F&^C}^?`LA?^S8h| z49i0;Jir)$wEGP=u&w8riCg#0?Df&{I(SgRXrb0fSSwAzI1NEb{j21U-j zOkKvnJ*tlowSxh$ia7drt#yu9@5ESD@Ih-G1k(yp*n61Mg%sbxSV)4~1yHY}n1wqh zQxG{6JLyI;Lck)c9Z}=4rZSrHjtEbzlDdHPdTmbZpdK4zZNeUs#emrgwly-bMzs#i zLbsOS6?#Vw8^W|us9MZ=v|3aEUr$3*P2NFojT?c^yd&NkgX2xH^^qf1143^J1j>>? zk~KN)5bS;juiwaONC94mUUn8_IkDAFq$ySXY4rxxTTO6N1_;{k+lPd|RWXnWhjk+BZ%NH9GSHobX2rKU9P}6qZoOQBdK(KR zwQ!(HxZY-p#O*Vw;lrS3n>l7~9+K#D5qGUo@v!SaV?NQmR4G%=oWS`V^RW`_9Y}DCE;Qvsp{)gJkHfSEIWhY zWAVsV*P>yteJmc^3Ms}TdY0_6>cmpaqFF>~)r&MC#T>EW{d$ctZ!K;Nt#6N(wctH` zOKPFP1~NrMqYe1cVJb)&co)oZYtJzeU;uPX_X9z5npS$THX`;t=xL8@A4eBw9oe90zxXIDa6kPIHUOf)Q9(0ltw+?7 zK0xV?b%dpl(f}CSVD>6xuYsU~i9&oz5pk@;5{3BexHgh!B{Sxv=9<}(tkjZQ?^>Oj zttDCI1{DcyMhfYgxV^m{t<>!Hs5jn>z${itNi9;gVkcl-aaOy{wlCeDPR$*vVzh{E zy9pL+(8a?Zy0=-z8_^2q+h_?}?Nf34taQxwdX$1o%y6x(anI}kbfDX$)A{@1PD!j3C+exYo(Z zsU;sYBe1qh^RbWQVE|$AaZ%Vo^krt#TF+N)yu=W1MuCgz;;2j3JFfTeaMzpB*SSRY zp3pwZq1_knSX_+VNH#0ZU6KPN%&XqN*&RctJJ`Vucq+fDB;Qgjalf*n_n-c!(yB8z zKV4jkmv7w^Sqm{XR_&-OO+D7XI5>YW_egE$GUH9x`-)`en&T)@N6&l)>Ngrd~@PwiqZ*h;-{ZHkV%jgjFa)? zS3J+TRP}h$0PF*=<8Ij z*SNZ?G!+Mw9bKhknp|tolrcVF2?5`^oT@wn$yy+pk{mLjhy(Z`6M!UFy(T~4T|{R| z_XB6>>P+T$*fE`Cf4TaQ_^C78;&Jtv%v(wYpiiBfu7q=$W@GLQ&VvX5EFYZN#VJxS zt_#1f75FzB`WTV?V6r6NYftaUOJ^>Rw5Em_7gDde)-B8<0;0C8a6obG_(HEay`NZ! z@t!a}W=;@xf$%ur@w~%n#JYhbAWtH<`nDsEtR-{BUiY z`^hF#G=oH|Cun)N%tkZ$sDfoT@P>a2>wR#hXciA+)XA=NG!T9{Eth7UZG`vUJZWc z2R{pgpC!RhWAL*o_}RdZ?oOOTY~z4&)AH^!zjO0ULwtcaQPle6-vWoKmPrN|7~r}6 z&0u4aL5AT(INBP7ObYNZ$)Lk8(dlHBZUp2mGDQ>oSeQul9pRsc8$dM1-3;gd$6;o4 zX%UO1nhrI|>naADO9_Mb%+EOA>l07XXZ?WcUH*JcXX=jug14WCkq{jr9s&aShv=9< z7vXx)sQuCA-GrV`QS()eqvg99oM)$I-q-TqpAiI|{iB0|%sLM2rU&ts8=x}ikx*F9 zh?)rT*`bj`oX=}z5}*RT23IQW_J3y10Re{l2|-krUKMw#^N-Nqt&~$JoZQO1bRR#r zQE>FY&&}}`jNrxb=IHBgef@R9@3cj$s|12R^#%j`m*Y8BdByQut9%CEKYgOW9Ksk! zf&>0vrcxHM|a)0{Ydb=@)Bb}Mo$s<$8KtBmeSWjQ2w1~W5Z|07R-O|d5TVoXYG}%gfD*`dBf`~Tikp6a8B^SvP9KiD;WiEPXrlL zc{Cq`;O?Y?veY+bl%>9QxBsoLQp?<{^hEeH)&E-fG{e8wJS{1lLqq;;;mZQOIN?j1 zUdD$n2levt@TEY&T^7FR?goEo_@e6>{G9M*hF*^Rlc1mYQC=fOa{0}^$^xzuFAZ6}X1eUH&{ciX))vpYnX82E=r;wT+ z311BG_k}NQ8p-Y9i%{a<6254_{&nHYIK7MxU#9Bi!{N&ey$lOq=IF)ZCG#f(_n&kw ztg)FYcRy^PuQ3r}sXxsN^Gpj9!#wGUiPWDKg?XmIQDNR7lefgo6qj3$36N29#Gp5r z6ytW_N5h=WVU7kF^=LpfO56(fhH$)Z16&Y+n7V`YYqk;z z;@Xmrq8Xg!S=uFjccFfcp~+_&pOQm*d%(wO3k{` z)K_wy<`OHnyre8O_7c(A(uc_Eyws`MZ>g&#yqOPV{E-L4v5jZw?(U71H{_IWR*|2m zEps_VXGL9)KlzA6>RGEf4}S8w&?|;<6W)4?D@x9VkqXXNGKEG{0+12|h{dJERy}Ye zP>!36;RX1*5W^KZsnh5)Rx`_EU+MqU=zq{?h{vldT|pWO5MbOlxdes+U zFLo-sh=+QM!ndZTUDI8fnwiVB;YEjMUaZ^GDwzlFrP-8Ko0=_Z^-ol#`TA&AclrDH zYsI-)xJH~1O|Ix%^au5^Hh!Oqp~hzG6Ryqmzm@;e%~xoDR99xD6Gf*gGPB<0W5PKR zF6C*qxR9$_H&zinF9t9MQ$c;AXiM}Za~+rWMWo+uzbwH7&1y85@(%Vve%Oz}M2Lk^ zY-5CldBATk5AXn$j?fBBkEJIi=8BoKwHbXUxFs%dB-% zidrlDSv$?Z+l^sC>%(i#6e5Sb*382|)3e_R5R`d`G_${OmWsWFH*`UQUVB(#u_?hi z%UMFcBovP-x`xMFQ_p3mH8YiWE_dgmi0<1{WnuEV%tSip@{hQ3_5Bv1sV=jXSMRM^ zyA7}>GhgIY+~ptAw++$#9ero%KU%XQAA7^8VPY%Mm;TC&NT1_JJO>?pX%l*esv&8) z0J53rF5Ea`T<`sh>;ugDI&aF0VUG{<6}htmp5B%y@}JyXnkwa{g*{F)*IPF`mbJ87 zQ{}Do$1|`*^3pm!ZboS3XqZ*PyUQZw4W)Q@MWSzB`bQq8dv{$FedE&K^H_V=C8bGX zB)q$ZD`po#cGpNf!1nGcRLtmz%97lXl_i&qY%YoBdv}c$W>~lE%>pm?X!S4hL?~Hb zb=loAkFsB5zI8`?ePYa>vedtvEv@Pv5Pk77CZMdSEBc*_c+fal$bUbM$6Ut2C01r1 zP-YRj^ev0Qn(=jhXZoD7)H81f?X6(0OnYe_{@eCOs=ef;Kl-OUeYbEz)!Dp6s>JF= zPc<0{Zz|@SOK0&o-J4oank4WpKuF0Y;Nrv4>TR%+sCvVUD8Tu|s(N;XKWrWZ)UAZhPk`dFfRo1haIx0pqTr%+hckYwwDNvjkwlERE3Q z6nJ-y%HVAp+Sz~utwC_;i^K+w-|_I@(o22-$D9Ywg9A*xsFVPbDjq|6`4=AlM|u%# zqL)$7iUrM#qK&bcHGq({8$7(&Ky``0Gk`|^C-@$^#o+1SS1yEamr?@w-s3TZZwrtA z&+rK*0V?3e>tQqyj|1XdAjWES*Tl?7fC}`BhkqY{`Bj-fG_UvY@ZaKdp=01J{yTU- z(+lslz3_hG|0BGDQGg3rR}T{yK`|r9V+6${mSw&Kn7#b#g4MZ?7+1q*A>!Q3H53iy z3h()uD|_D!FCso8;BOq;%U@u*zPZYG9{DIP5`Rdh8$(f8Ka~4@;|T+K-^(B$LH2P! z{6NgrsizMnW}OQJ{uh*-;wlX`ZhWX=4SW&>3{%b?)*al<{}AEAT*i-oZ?xt=NeGyC z&AQ<6A|$3YjCN&Y<#}IE@DL9FUP5>oow!1Ht?7m@WRm18t6YNQl{}BW zaqFwVNecawo_ZS)P^%HXS&Cvz*Vt~#4rF&c0$SzJIx zVQZ5q7p-0gKMTH#pvlV+#M51QGQXl9Y`c6-<;i~0ntSLIPF!A>xt%BAhbhNR+OTNN zL<7*I+~!G9;K{ux_3(k>&pvudwB}Ya43tZc1Q7ZU1wGc-;^-UZD>2UoR{rFv0*Wc+6 zX6F`sFxAhwT|{KUxBR2GnP(|yz8^emZ|}boJkQf}Tku?~=SK7V>b{%=%&;S0k)PxaXk_xl;uU*BAHCDlK)ddIzC^)TMzbQo`;L&V8L z+~m{4lQ$(FAL%A1k4oM));M=3?;OWh;^i~WzMz&OBk=WJ&2DW8F5`HE^FRE+{_r%*a?j*Q^q?-q+!N2KMt^twb zSCtpyzrq>wTcrDuHPn@(7U%b|Iu8D?`B(S)Z{nPoUfbWQH#zCGg9T+vx@CZ!_PV?i zb}Q|!&4o$q@J`~)zIJG8Y%U)2C+rpJ7P{MPpNeqoyghazwmCx06LGs4PqRH%nn|GR zn4exTSo3wz?!rCfqP3^%{)DeTK4$&#Ddx$-$)|n*j%j$l+imz1##UHe_}8zwvt4TC92-XU|pr<%8dXn^V7g>+dYWEgt_8 zijV^Upn<2x0i4q-6;I7oxi+q9q;GI7JRw#Mb*$5IZzu0@Z>xhNL%MMghv@y~2kEVH zBDjc9c$E{|fTvu9$Gte6xK^87-_#-@0Dz%4+?Tdeb1+UIrYU@*n+D*g3}sGAO)S9a z&N`>#d0cIV#&H(l#5~qWnL*}dBVJb2B!B@_#Br^S&_5oO#!tM~3`-7C0xfnT9^2rg z=8ryf(V=_rDU_E%>tNH$F$)h2^@`sX{!q{L%b~(hQ7ZmDsj)@IB^D=K9MtftptZU| zyZ~A}g3n!BJhCGWk}SE-I^I5pg+XO@EQ1GvI>s3)^~#m6e&^;N$j!}^V7!c+vN-J0 zv1`kTOQTE;xgqfA5&)@@(2RV(aFbxbxF!Hz9Jex6J%+;i#09)<$pj)09Imk^74|4Q2)J=_qYk=yrc5BcvcmeHa zoCvWQ8h{&X$92Vx;Bxy8E_09*6d>$po8U@UNhLWcd3|Ygpuxo zEK>QO77gL!i08AY=!|{XTjRu5u_zq~^ssj>vPKT35lX*fZKB;dar+$$|2|U2J#Yx@3AcGhm1pH<%5EAnA;zkEYy9b3_4+gBYl{hexZ9LCU-OeM7594G7Tc z(by@uh2h)tiiJlJ6TT5biZwZ@S4J+au?xX89@+Fa7`Gg;s~zc;{UK(Xfi{>98V1-M zsr&lncpKH1?`^cU)0Gg3FCe`gWn~1jz>+ws)zQl8uzVba=HXqeu!)A)X$6Zke0#_j zJwRL^!$-k{xX>2_u3(pR1Ejija%$u!SqaNH#1f*_nr%fGzzNpfCuVz`l!wcaf3RT`%o>zoXWIQO61D&rWN>(Q>YkOT>E5S7{T8NRorrsGRhHn&nVO!kVCUrb7rD)hQH zwd981_J8MR@d)iU#tE8%twiv3TaQZ=5cZE-w23<~91C{4o~0{X%I6)<0SH!CJocW$ zwo|@I@(RSW(%WI-8rRAs;;rQypQ%CS;g}nF-;J>Xjf>XHE|du5cAMgta4?%axct!1 z#3PJjW`FyHwbu3YPGf`VQj9!qP{cXvrl_( z*)7&eA|n=Fk$&W7+)#f2Ct4($-A;G|66pXk&4?J9dWA?W+};S=7;+Os!V41FmR>V7 z9;?R_i!oYjkZSfh+KAxTy4#Jsg%md7LFwkGwH6mXoco;goL0-%X;*3*w%n*O-U4V72w zaTmX=d9WBlt-Qn12R5aK+X_ZI31=aS!MGI0x(!>|Adj!+$T>VcBj=0*o3$qJ&07Kb z*U1kW6s7BjA!_3&7$6jvxOLu^xYZ8frR$?0W~fPFNc<9Q6em`~2FB`w=AWmdgy+O@ zxbdQZPU-mIKJAP=Q9jy8%NCfLeQ(OMJ{Y1 zgiue6L-A1x{0Ni}=G~R&-u$7fel?_!E@JS1=AX6L0#9!8eAc90&rjU|xT}~d0YPFt zMz*XD9H42IrFd5Ub`i)O&+-#aY^~U;!DlV*R#tu7$Q-8KH&0CGCuE-X3&l!N5 zhtU{o_ZSHRx>i8Xbs+0FbStDZMdKD_5$bw@F){=M{Qh_>DAw zRK;-V-A@NcfzJ3RpCPE`>UVy(;65q%wa8F36R~bsFOf_fJ5myFCZ!gmM7>e+yEwv|~)?Q_HY&GyO zXEEdpOdVD&aTGjB95qc&xmQDc_$~{bxl)<9oSt`yBPnXUoQ+40;qxCyNC43s0>X?N ztK)I-s9$RJDwdw%1A!z5NZO8;KkTJ$%(h%?s#+a+XCLtpi_^Mqb^j>{P7e>H@tQY3MIICIx4{} zeNRxlBC%&F>h62BK3AWUl*&v)JQE7{4g8FOi zw}^Y_|BYt*HM6gbA>Mp@Z9KMv_{e71yM2tG<^eYI{J!FccMHeJMeBq@0&!X%q+cX2 z@)s%GB&Xte)>-+I2U2Q4x_OupQIdJ*SfUG|>sMeQbgws8k_)sN$QLpS&1)^Kwi$-j3ghzP$iDf)s zbZp7)$O-y>&00(32$ctT?W#M>Uf~@=px7(eR?Nv`*U#Sq#0YU}BuMcP$aCNblCAr# z6^LRZl!!G+RS!Hect+(8w14gC+m^+l_7v0^e;Un|AWt&tL*6k$;k)yJ4X0=1E0%NlROcfJ086AOsB> zl#G^ClZ<9Z&&G@pDOjwEbUU#VMn<2s+O0G+`Lf~1Ygz6TYcYTWF$&-TR!tW`&*g76 z@lw(&m}&(qLLn-krU9Q#9TbgIx_s(y`uu}3mEdeI*?fXwIbcQj#A*Pm&q`;c7~1U` zxxyorp@QHA38%=tdY0oRnFj)?Y>~SSdj<~rYlK_j#UsFTKOmX_&qiQ;nLn0Eb5ud4 z%sA}58CGqiAJ39&1WT+19MByET@d}*@9Sg649BvwDvmH?-xqr;l*SUp68k2o z*rs*mDfSKU;Y}tVgTnrx!A({Jkf0KBaYgFYku`(f{5TPBu`UmdnNT=lUfJPUjb0av z-tIu3)(+2A`Bb>4xRgB%+Ae74~wnRyEkC5xdt-h<*4|QX4kJh-_(K zUw0(E5-m=pX+(Xn1+%n28|pO~bwL|6%w%Am0IjA$>=k7*+ZSnyZG@Y~*)&@0ZySdx zI0Pa1p3xP~m_n(&maiRvdb7d(BxYO7){4v#~hu}KRg{l6IB&ea6-5`ivsssfOv3HzU9R?-Dr34FseDFM@DZtx60PlNH7FZ~- ztrJoiSb)efu^7~KHXsD-qwQv(#1#grQ)r2X1G|OqFoOQmo*MKuu`e+^Vx!s+xYQQI zMcrbnL~rcsV8mtxf_uZHkt|3vf}*s^h;)N#J;dO79dWc|i$Gs^TdlNL>F8_J3fkof zB^b?^sD6dh&6Tp;sw)O0BH4#9A7c8GtKFvd-~?i^Ed{F! z3E_fvhkM$!0Nh+sE+-nWBS6O&ew8j|nE4%=KE zQ9ZO~?2|t*3NhLXTpfzqYDR^{@|d{1Hp+UE?$5B0YpY2nPET8Cqm83E%ko(GK?Y+C zoU6GhCeWtYN1fG*b|S><4>L`1I|4jxwdRbm2SMAf1nOv0aDj=nkxjq`mb!^9JOM(X zGdA{My-C|t*(E$;@6ZQ!vLeyX?JcOv?Bz%Q%3t-ZGE(};nsw-*k!Bq zUQ@go)4s&k{ybeUP_L~7@t9oJv=hbQ6^&}PlpZu4W6|fSSq2!h!DT($yX=n1slrcV z=vUeK42wa1ys-aIz!RHkO~xvXr;Wi$XP3f`v52wg1a3_(I%ysddut%{HJA!zdR!51 zEA~ggK6JA`#)(75iEw!g9BHTEwbBB2X9zC0hcNPkKnzQhlwvb?kSB0o$CFHB{vb?1 zcAc=4%Fh+P+hS1n+`O9WhHIS6(DVv(s zFNd{-Rgc5amGQ_*#bje4#h`?VUdsRec%(y27-f+?c^Zq9bZ^wV&e*m}M^jj3b35IZ=v@RKv<{d0kg3P7{L zwSWdesv{5dWgy}O>$y?7JG8zhrOGeQ3DkP1C{*ylRwrp_b;dHn^XfQTc4e=|W~T!X z9^dEA3lvq@N5e3elMq6i1@u0nJ7DD_0$2ADFaUmE(+L9B9S%GI9oUFM?u11edlE-n zIxl&fua)Uat;;@;e)U7*GmBzJbXEZ*r-PfrBz+kwC#kR#D5jpF?8St)9;NfcVxTBI zD(_jqf_pT$LgKP7IR_}T#hxHLs1k>&JRR6Xf)V{bc+%u z2K34^aqEoK_jj-p4bkOkQeja%jR9~K2|B3}%V8T}uM$_nFm%FRi<$l-Wit~WcVj0w z=|BNo-XGd9irpb38}DrN+9h$`HZs2*DWfxIl?-e9%itzKVn^ivBBB|sG%@Kn!B-;y5M0=aM)qB$?mdN(wyJ7kmVd*BOPTgc^UQm z2x4ZX#EiW~#~r)Uoq23DB~agUEoAFZ*vA}OIa01Ml<;_*16eUU9b%+rf9Q`xMizO^ zv3e=u_F?=14$BJ0nT8nW0BPbftgxa^%20$Q8Ac|}+gdO2w}!p2#E999zY-j2oYZze zEWijrtT>1uCj9`gyv*MMbn~cWNN@mPtPMIm5FbPYfEJX^g~LAg`;ObRw~-%#F@cEn zgB5e5!4{dAD9^db0Mt{HuMxS{VRqe^Cs1&>i-Dd3gq&0~a0|q1 zgh+5;THq{0MhMv_fcX{v77A{O3d8Ei4-F43T+tBpi+){j`-)fYWxx4y9IW78j0$Eq z%RmBfaSIj>7eOL2L_NvufHjHBtni^wx3ZA{z04%2YEqB;7)t<{!xY3Al_gl&=g}Zf zb!>Ia_HnMzuKNwxn9VZ=U3O_kx&eB~j#VZ=3I>sb219BFv4`-%PB}TXaNLn6KJ{>% z`(sSo0}t%m+6D89hMyphTh`Ac?_pNtKsdO zBi*q7JKrzIHo(;@tThB9E$Vt6vtwZd4MqqeM|6Xb4p*AAt$~RS1v(waA~3X>*{b8) zNP`ZiBS(xr6#qZ$y?cCJRhj>vv}v0{VIQyq0jmU!nks5SQ4?Dvr%C8OC6Pe6Q~~YC zh@BbK0_V`8Ai;Ce=D0ah1qM_`>!2eupfcaVT5fI9(56Mv(sGkqL%DRHriB7R(?aR* z{dv|tmzLrnGxL3Y|M*4eS$plZpY^Qgwx0E@8zGI#0UJ)ZsTldgsN)T|V)99Q=@k$h zLeq6OSV<)%ZT0u=h zeUR%9NDV-8N|0W(jyIkD3w?dt2oyZXMp6qKc zzv5)z7W?AOh)13oUubL+5&TNU+lCZlQ5{2a=q%`Fl`t(-`FQr_#^2G~eW-fW;FU+y z^U>RBih_9oehzFLG(S#@jn-6ZG{O`HVgaHGyW#xYr{N084Q=BYsXW#;-p*}VTsff9 z@*obj8Rf9?5xGZLW1Rb8ynJG!$ip>^UwGVhfCbp-yYFj!0%ERdT$j7yW4rb&UyPH- zxJq*nYt)iHh5gVJn~q>K!u%YT*v{wT$fp6^H4e5TD0l#ek7R#0{xy2F2%}qdyyw%E ztEpP^SaOoYYf{@ro5RK&Bh7f4DMPtri-i!s*W*_~Gh8bze_)h5fL-8p#G_focEi!b z=Xd>#1qL9dQ3x>y{Sx|pMbi;~BKFa^Ci|kW!w--$v6SXdJb)ex1kfHrbB=$dIAc~INrFUpm~gQWDbMi%@KKw}Ms z1)iU;TxX{D6^7_-x#Q}II1mt!x5@8;xJKCr8&?ttXw>n0ga9gE(y5E|>nIYdAnt2q z%Ig3^_OEcb*>LuiqwhQVHIUA3gWUIxt9&%8w7C@9MmOs08qQ(d)|R{FBQGtufDz== z03E>4GkdsML2Y#yqNfIRn%Vlj(_A~p3-gNP6H|qVNS4if;gg}(_l!F+cT7Dbw0PqK zN2}OD1g^F#Zir()qyQ*J{_MKfHF#{V3{5PvzvK1L_zk=akA4}VZ5=;cIY6mr{Gh5X z+chEU$J#0fG%Yu8BIn1nWm{_6vi_vDY*%etb~b0bly4QW{rlP^^MC97m%<=*=NPoI^@uJg06 zX@8q8MCCF79g*`%wpDO|%-nsC$eH`L&yKkXlsO{jR`+B3__OIg_q4|SXfpss9&tZI z{3PqR-`F@1os%;+Iw!}Vu{E=!x$nYfdbaI#E==-NPKGg>v^tWsI*PP9g0wn%v^sLM zI%>4KHU2k?^nP~Z9WDHZEjlO09S#FTTlU}T*xKs2+Ul6v>Ui4fSla41+G-3PzNJHV z_-qRA5#u0WdUs`shMhc8xE2%UPyk@de2mAKH^>-n*f>p7ob&fT>l+3|?#Ud(u> zgyYN~HL)#w<6bQJC09AHbIM&mAO6Ii*s!@VyvOmXwy?bvz3t|EnCY;aL^}Klh4#hn z9SKVQ9PPx>eFniI;Wt>m$Ap6t*aPy;cA5CO`E#=d(nK(SH&<3zJ@0hi$?z}t=6CTt zh3I@DX2{$TMKUQmgy=OHo4t=HY61zP_~vk1nf=jm2i(8pYv1<_+vHYtA3)ePeZPOl z&HwKH*k|VN*yQ(b8O7xlr}~wBiOFm>8(}y5k!Dh+kK(Dy_uAHJGq&G+nD^~7^`Z#C z&#pNg2(#K7AJZX7_iyoYlTHOejs&8v#hfIkWA5UWiFE(yc5mq^dlKn^(Wp)p?&_wh zmcDJH(wiDMqt9FV%f80;ig@qb*R!koheq}9u1XJ0tK8zByWU&+efO$sH}Mm1>B|0* zvGMe#6DrsF*LzF5-Mf}Ic=K(gJDMMh_qObrpXq4+)5R_1QuhFs=ug>|^Jn)DRrPHd z6<^&l>iG0f13)4G#X4Fl6SPILf3DKuEB)+Y%N{~>b=Xx# zU^(mo?)@2xpFT?``&2MYWhZ>@6!yk%tsZV$9Pb`E-phQAS2`o0_jnSCb06`R{-}jo zx?j88%Y2wD&O036{n`oMLfzw%$im@%0Q6NRn%7)(Y-e^|M{f2W$9SDR&^BsM@&EuI zT@;uH&odiwpnI*w-nv)JdF_t4jF6!G5l-Vw|7uuku+8z~N}#qE>41mX1mg z;9CpIbXoB`*sWbL?Vyy-tB^ExG_Op4oifu7N^YNaKyoXr5Y|Z2G}v~{K>t%?IY3Xi zwoiU^e19U%Ry<-+Yuh;@YXzh3Fcwems_Y%@Wu6AGGOEcK)!WM11G;Ekb@ZMzik+3C zsVa4??W>*qny#ICuNw*sEbg8*>Hsfu6f}=7)6Pm}T?o>#%W6dhsBj(AYh&10%eZ0L z1C%j;qnlzC=~b0%Bb@)~J6m{&FFT%p^E$$-J@tYs!R`b-etH*p{Y+c2DmQUF8`?B8ziS-3vAm`08`t$NT=K5-P_dTYTbGO?p_3%8ctfp8^|l4q z)b-KWw&2RTTli%fFvzgVjh^BzmNAm?YJQ^H9qC_?$%r;=2B}V*yMge!|M%;!=GjXuz|Akw2fL4oQ$`=TZ?<3n zpRJz!;N)S7F~K>CZB3_L(@Thb_fh?uVjOT!e)REMd_`n$idD0&P>I8;a%DMElOJ(3 z?jNcl97xqn7tLR@@ z4aft}gU$VGs>vID9`<3O7}UQq#;bAX>7=6`U75w&|HjAoUtj)~vm8;rSj)BE*x=I6-Q6}$Q*Wf{4vSL`w&&gf*%D|U7B@3J94CTX>fUp#N|;5c1K z<>yY0^>5)^XiUS&)zXjYn8AgWC$e#P;i�X!TUAZTuRZCoFWo9SeywJypZQ^la4? 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 TmD*2jvG%v(_gs>%#`gaok@Dd; From a521cd7abdea70455b4a19fd845dbbf9f105b875 Mon Sep 17 00:00:00 2001 From: Thog Date: Wed, 17 May 2017 23:56:38 +0200 Subject: [PATCH 189/317] Cygwin support --- ctrtool/Makefile | 28 ++++++++++++++-------------- ctrtool/oschar.c | 2 ++ ctrtool/utils.h | 2 +- makerom/Makefile | 26 ++++++++++++++------------ makerom/oschar.c | 2 ++ 5 files changed, 33 insertions(+), 27 deletions(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 7a534c98..55bc16b2 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -8,25 +8,25 @@ CXXFLAGS = -I. CFLAGS = -O2 -Wall -Wno-unused-variable -Wno-unused-result -I. -std=c99 CC = gcc CXX = g++ -ifeq ($(OS),Windows_NT) - #Windows Build CFG +SYS := $(shell gcc -dumpmachine) +ifneq (, $(findstring linux, $(SYS))) + # Linux CFLAGS += -Wno-unused-but-set-variable - LIBS += -static-libgcc -static-libstdc++ +else ifneq(, $(findstring cygwin, $(SYS))) + # Cygwin + CFLAGS += -Wno-unused-but-set-variable -DUSE_FILE32API + LIBS += -liconv +else ifneq(, $(findstring darwin, $(SYS))) + # OS X + LIBS += -liconv else - UNAME_S := $(shell uname -s) - ifeq ($(UNAME_S),Darwin) - # OS X - CFLAGS += - LIBS += -liconv - else - # Linux - CFLAGS += -Wno-unused-but-set-variable - LIBS += - endif + #Windows Build CFG + CFLAGS += -Wno-unused-but-set-variable + LIBS += -static-libgcc endif main: $(OBJS) - $(CXX) -o $(OUTPUT) $(LIBS) $(OBJS) + $(CXX) -o $(OUTPUT) $(OBJS) $(LIBS) clean: rm -rf $(OUTPUT) $(OBJS) diff --git a/ctrtool/oschar.c b/ctrtool/oschar.c index f850ef38..b712ff21 100644 --- a/ctrtool/oschar.c +++ b/ctrtool/oschar.c @@ -1,6 +1,8 @@ #include #ifndef _WIN32 +#ifndef __CYGWIN__ #define LIBICONV_PLUG +#endif #include #endif #include "oschar.h" diff --git a/ctrtool/utils.h b/ctrtool/utils.h index 990d900b..eb68fcbc 100644 --- a/ctrtool/utils.h +++ b/ctrtool/utils.h @@ -46,7 +46,7 @@ inline int fseeko64(FILE *__stream, long long __off, int __whence) { return _fseeki64(__stream, __off, __whence); } -#elif __APPLE__ +#elif __APPLE__ || __CYGWIN__ #define fseeko64 fseek // OS X file I/O is 64bit #elif __linux__ extern int fseeko64 (FILE *__stream, __off64_t __off, int __whence); diff --git a/makerom/Makefile b/makerom/Makefile index fd0832ff..f7ae6262 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -5,22 +5,24 @@ OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) # Compiler Settings CFLAGS = --std=gnu99 -O2 -Wall -Wno-unused-value -Wno-unused-result -I. CC = gcc -ifeq ($(OS),Windows_NT) + +SYS := $(shell gcc -dumpmachine) +ifneq (, $(findstring linux, $(SYS))) + # Linux + CFLAGS += -Wno-unused-but-set-variable +else ifneq(, $(findstring cygwin, $(SYS))) + # Cygwin + CFLAGS += -Wno-unused-but-set-variable + LIBS += -liconv +else ifneq(, $(findstring darwin, $(SYS))) + # OS X + LIBS += -liconv +else #Windows Build CFG CFLAGS += -Wno-unused-but-set-variable LIBS += -static-libgcc -else - UNAME_S := $(shell uname -s) - ifeq ($(UNAME_S),Darwin) - # OS X - LIBS += -liconv - else - # Linux - CFLAGS += -Wno-unused-but-set-variable - endif endif - # MAKEROM Build Settings OUTPUT = makerom @@ -29,7 +31,7 @@ main: build rebuild: clean build build: $(OBJS) - $(CC) -o $(OUTPUT) $(LIBS) $(OBJS) + $(CC) -o $(OUTPUT) $(OBJS) $(LIBS) clean: rm -rf $(OUTPUT) $(OBJS) \ No newline at end of file diff --git a/makerom/oschar.c b/makerom/oschar.c index f850ef38..b712ff21 100644 --- a/makerom/oschar.c +++ b/makerom/oschar.c @@ -1,6 +1,8 @@ #include #ifndef _WIN32 +#ifndef __CYGWIN__ #define LIBICONV_PLUG +#endif #include #endif #include "oschar.h" From 4c6356122422f59f831db86ae4ec3c5e4a5ce766 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 19 May 2017 08:34:50 -0700 Subject: [PATCH 190/317] ctrtool: Add support for inline decryption --- ctrtool/aes_keygen.c | 127 ++++++++++++++++++++++++++++++++++ ctrtool/aes_keygen.h | 8 +++ ctrtool/cia.c | 4 +- ctrtool/exefs.c | 34 ++++------ ctrtool/exefs.h | 3 +- ctrtool/exheader.c | 18 ----- ctrtool/exheader.h | 1 - ctrtool/keyset.cpp | 125 +++++++++++++++++++++++++--------- ctrtool/keyset.h | 22 +++--- ctrtool/main.c | 28 ++++++-- ctrtool/ncch.c | 157 ++++++++++++++++++++++++++++++++++++------- ctrtool/ncch.h | 15 +++++ ctrtool/settings.c | 55 +++++++++------ ctrtool/settings.h | 8 ++- ctrtool/tik.c | 24 +++++-- ctrtool/tik.h | 2 +- ctrtool/types.h | 1 + 17 files changed, 493 insertions(+), 139 deletions(-) create mode 100644 ctrtool/aes_keygen.c create mode 100644 ctrtool/aes_keygen.h diff --git a/ctrtool/aes_keygen.c b/ctrtool/aes_keygen.c new file mode 100644 index 00000000..d8bda853 --- /dev/null +++ b/ctrtool/aes_keygen.c @@ -0,0 +1,127 @@ +#include "aes_keygen.h" + +// 128bit wrap-around math +int32_t wrap_index(int32_t i) +{ + return i < 0 ? ((i % 16) + 16) % 16 : (i > 15 ? i % 16 : i); +} + +void n128_rrot(const uint8_t *in, uint32_t rot, uint8_t *out) +{ + uint32_t bit_shift, byte_shift; + + rot = rot % 128; + byte_shift = rot / 8; + bit_shift = rot % 8; + + for (int32_t i = 0; i < 16; i++) { + out[i] = (in[wrap_index(i - byte_shift)] >> bit_shift) | (in[wrap_index(i - byte_shift - 1)] << (8 - bit_shift)); + } + +} + +void n128_lrot(const uint8_t *in, uint32_t rot, uint8_t *out) +{ + uint32_t bit_shift, byte_shift; + + rot = rot % 128; + byte_shift = rot / 8; + bit_shift = rot % 8; + + for (int32_t i = 0; i < 16; i++) { + out[i] = (in[wrap_index(i + byte_shift)] << bit_shift) | (in[wrap_index(i + byte_shift + 1)] >> (8 - bit_shift)); + } +} + +/* out = a + b +*/ +void n128_add(const uint8_t *a, const uint8_t *b, uint8_t *out) +{ + uint8_t carry = 0; + uint32_t sum = 0; + + for (int i = 15; i >= 0; i--) { + sum = a[i] + b[i] + carry; + carry = sum >> 8; + out[i] = sum & 0xff; + } + + while (carry != 0) { + for (int i = 15; i >= 0; i--) { + sum = out[i] + carry; + carry = sum >> 8; + out[i] = sum & 0xff; + } + } +} + +/* out = a - b +*/ +void n128_sub(const uint8_t *a, const uint8_t *b, uint8_t *out) +{ + uint8_t carry = 0; + uint32_t sum = 0; + + for (int i = 15; i >= 0; i--) { + sum = a[i] - (b[i] + carry); + + // check to see if anything was borrowed from next byte + if (a[i] < (b[i] + carry)) { + sum += 0x100; + carry = 1; + } + else { + carry = 0; + } + + // set value + out[i] = sum & 0xff; + } + + + while (carry != 0) { + for (int i = 15; i >= 0; i--) { + sum = out[i] - carry; + + // check to see if anything was borrowed from next byte + if (out[i] < carry) { + sum += 0x100; + carry = 1; + } + else { + carry = 0; + } + + out[i] = sum & 0xff; + } + } + +} + +void n128_xor(const uint8_t *a, const uint8_t *b, uint8_t *out) +{ + for (int i = 0; i < 16; i++) { + out[i] = a[i] ^ b[i]; + } +} + +// keygen algorithm +void ctr_aes_keygen(const uint8_t *x, const uint8_t *y, uint8_t *key) +{ + static const uint8_t KEYGEN_CONST[16] = { 0x1F, 0xF9, 0xE9, 0xAA, 0xC5, 0xFE, 0x04, 0x08, 0x02, 0x45, 0x91, 0xDC, 0x5D, 0x52, 0x76, 0x8A }; + + // overall algo: + // key = (((x <<< 2) ^ y) + KEYGEN_CONST) >>> 41 + uint8_t x_rot[16], key_xy[16], key_xyc[16]; + + // x_rot = x <<< 2 + n128_lrot(x, 2, x_rot); + + // key_xy = x_rot ^ y + n128_xor(x_rot, y, key_xy); + + // key_xyc = key_xy + KEYGEN_CONST + n128_add(key_xy, KEYGEN_CONST, key_xyc); + + n128_rrot(key_xyc, 41, key); +} \ No newline at end of file diff --git a/ctrtool/aes_keygen.h b/ctrtool/aes_keygen.h new file mode 100644 index 00000000..dae87a3c --- /dev/null +++ b/ctrtool/aes_keygen.h @@ -0,0 +1,8 @@ +#pragma once +#include + +/* + AES Key generator for the Nintendo 3DS (CTR) Consoles +*/ + +void ctr_aes_keygen(const uint8_t *x, const uint8_t *y, uint8_t *key); diff --git a/ctrtool/cia.c b/ctrtool/cia.c index bf757557..4723c709 100644 --- a/ctrtool/cia.c +++ b/ctrtool/cia.c @@ -214,8 +214,8 @@ void cia_process(cia_context* ctx, u32 actions) - if (settings_get_common_key(ctx->usersettings)) - tik_get_decrypted_titlekey(&ctx->tik, ctx->titlekey); + if (settings_get_common_keyX(ctx->usersettings)) + tik_get_titlekey(&ctx->tik, ctx->titlekey); else if(settings_get_title_key(ctx->usersettings)) memcpy(ctx->titlekey, settings_get_title_key(ctx->usersettings), 16); diff --git a/ctrtool/exefs.c b/ctrtool/exefs.c index e732e4cb..150a405d 100644 --- a/ctrtool/exefs.c +++ b/ctrtool/exefs.c @@ -48,9 +48,10 @@ void exefs_set_encrypted(exefs_context* ctx, u32 encrypted) ctx->encrypted = encrypted; } -void exefs_set_key(exefs_context* ctx, u8 key[16]) +void exefs_set_keys(exefs_context* ctx, u8 key[16], u8 special_key[16]) { memcpy(ctx->key, key, 16); + memcpy(ctx->special_key, special_key, 16); } void exefs_set_counter(exefs_context* ctx, u8 counter[16]) @@ -58,22 +59,6 @@ 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); @@ -146,8 +131,14 @@ void exefs_save(exefs_context* ctx, u32 index, u32 flags) goto clean; } - if (ctx->encrypted) + if (ctx->encrypted) { + // .code and .firm use a special key on 7.0+ + if (ctx->encrypted & NCCHCRYPTO_SPECIAL_FSES) + ctr_init_key(&ctx->aes, ctx->special_key); ctr_crypt_counter(&ctx->aes, compressedbuffer, compressedbuffer, compressedsize); + if (ctx->encrypted & NCCHCRYPTO_SPECIAL_FSES) + ctr_init_key(&ctx->aes, ctx->key); + } decompressedsize = lzss_get_decompressed_size(compressedbuffer, compressedsize); @@ -228,8 +219,6 @@ void exefs_process(exefs_context* ctx, u32 actions) { u32 i; - exefs_determine_key(ctx, actions); - exefs_read_header(ctx, actions); if (actions & VerifyFlag) @@ -272,7 +261,10 @@ int exefs_verify(exefs_context* ctx, u32 index, u32 flags) return 0; fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); - ctr_init_key(&ctx->aes, ctx->key); + if (index == 0 && (ctx->encrypted & NCCHCRYPTO_SPECIAL_FSES)) + ctr_init_key(&ctx->aes, ctx->special_key); + else + ctr_init_key(&ctx->aes, ctx->key); ctr_init_counter(&ctx->aes, ctx->counter); ctr_add_counter(&ctx->aes, offset / 0x10); diff --git a/ctrtool/exefs.h b/ctrtool/exefs.h index 28afffb6..16e6152e 100644 --- a/ctrtool/exefs.h +++ b/ctrtool/exefs.h @@ -30,6 +30,7 @@ typedef struct u8 partitionid[8]; u8 counter[16]; u8 key[16]; + u8 special_key[16]; u64 offset; u64 size; exefs_header header; @@ -48,7 +49,7 @@ 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_keys(exefs_context* ctx, u8 key[16], u8 special_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]); diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index 4a96f0f1..21473de0 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -70,22 +70,6 @@ void exheader_set_key(exheader_context* ctx, u8 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) @@ -183,8 +167,6 @@ void exheader_deserialise_arm11localcaps_permissions(exheader_arm11systemlocalca int exheader_process(exheader_context* ctx, u32 actions) { - exheader_determine_key(ctx, actions); - exheader_read(ctx, actions); if (ctx->header.codesetinfo.flags.flag & 1) diff --git a/ctrtool/exheader.h b/ctrtool/exheader.h index 9ac656f0..09f751ea 100644 --- a/ctrtool/exheader.h +++ b/ctrtool/exheader.h @@ -198,6 +198,5 @@ void exheader_print(exheader_context* ctx, u32 actions); void exheader_verify(exheader_context* ctx); int exheader_hash_valid(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/keyset.cpp b/ctrtool/keyset.cpp index f11e185d..0341cbe5 100644 --- a/ctrtool/keyset.cpp +++ b/ctrtool/keyset.cpp @@ -33,9 +33,57 @@ static unsigned char hextobin(char c) return 0; } -void keyset_init(keyset* keys) +void keyset_init(keyset* keys, u32 actions) { + const key128 defaultkeys_retail[] = { + // common keyX + {{0x61, 0x70, 0x85, 0x71, 0x9b, 0x7c, 0xfb, 0x31, 0x6d, 0xf4, 0xdf, 0x2e, 0x83, 0x62, 0xc6, 0xe2}, 1}, + // fixed system key - unknown if used/correct? + {{0x52, 0x7c, 0xe6, 0x30, 0xa9, 0xca, 0x30, 0x5f, 0x36, 0x96, 0xf3, 0xcd, 0xe9, 0x54, 0x19, 0x4b}, 1}, + // NCCH 0x2c keyX + {{0xb9, 0x8e, 0x95, 0xce, 0xca, 0x3e, 0x4d, 0x17, 0x1f, 0x76, 0xa9, 0x4d, 0xe9, 0x34, 0xc0, 0x53}, 1}, + // NCCH 0x25 keyX 7.x + {{0xce, 0xe7, 0xd8, 0xab, 0x30, 0xc0, 0x0d, 0xae, 0x85, 0x0e, 0xf5, 0xe3, 0x82, 0xac, 0x5a, 0xf3}, 1}, + // NCCH 0x18 keyX N9.3 + {{0x82, 0xe9, 0xc9, 0xbe, 0xbf, 0xb8, 0xbd, 0xb8, 0x75, 0xec, 0xc0, 0xa0, 0x7d, 0x47, 0x43, 0x74}, 1}, + // NCCH 0x1B keyX N9.6 + {{0x45, 0xad, 0x04, 0x95, 0x39, 0x92, 0xc7, 0xc8, 0x93, 0x72, 0x4a, 0x9a, 0x7b, 0xce, 0x61, 0x82}, 1} + }; + const key128 defaultkeys_dev[] = { + // common keyX + {{0xbd, 0x4f, 0xe7, 0xe7, 0x33, 0xc7, 0x55, 0xfc, 0xe7, 0x54, 0x0e, 0xab, 0xbd, 0x8a, 0xc3, 0x0d}, 1}, + // fixed system key + {{0x52, 0x7c, 0xe6, 0x30, 0xa9, 0xca, 0x30, 0x5f, 0x36, 0x96, 0xf3, 0xcd, 0xe9, 0x54, 0x19, 0x4b}, 1}, + // NCCH 0x2c keyX + {{0x51, 0x02, 0x07, 0x51, 0x55, 0x07, 0xcb, 0xb1, 0x8e, 0x24, 0x3d, 0xcb, 0x85, 0xe2, 0x3a, 0x1d}, 1}, + // NCCH 0x25 keyX 7.x + {{0x81, 0x90, 0x7a, 0x4b, 0x6f, 0x1b, 0x47, 0x32, 0x3a, 0x67, 0x79, 0x74, 0xce, 0x4a, 0xd7, 0x1b}, 1}, + // NCCH 0x18 keyX N9.3 + {{0x30, 0x4b, 0xf1, 0x46, 0x83, 0x72, 0xee, 0x64, 0x11, 0x5e, 0xbd, 0x40, 0x93, 0xd8, 0x42, 0x76}, 1}, + // NCCH 0x1B keyX N9.6 + {{0x6c, 0x8b, 0x29, 0x44, 0xa0, 0x72, 0x60, 0x35, 0xf9, 0x41, 0xdf, 0xc0, 0x18, 0x52, 0x4f, 0xb6}, 1} + }; + memset(keys, 0, sizeof(keyset)); + + if (actions & PlainFlag) + return; + + if (!(actions & DevFlag)) { + memcpy(&keys->commonkeyX, &defaultkeys_retail[0], sizeof(key128)); + memcpy(&keys->ncchfixedsystemkey, &defaultkeys_retail[1], sizeof(key128)); + memcpy(&keys->ncchkeyX_old, &defaultkeys_retail[2], sizeof(key128)); + memcpy(&keys->ncchkeyX_seven, &defaultkeys_retail[3], sizeof(key128)); + memcpy(&keys->ncchkeyX_ninethree, &defaultkeys_retail[4], sizeof(key128)); + memcpy(&keys->ncchkeyX_ninesix, &defaultkeys_retail[5], sizeof(key128)); + } else { + memcpy(&keys->commonkeyX, &defaultkeys_dev[0], sizeof(key128)); + memcpy(&keys->ncchfixedsystemkey, &defaultkeys_dev[1], sizeof(key128)); + memcpy(&keys->ncchkeyX_old, &defaultkeys_dev[2], sizeof(key128)); + memcpy(&keys->ncchkeyX_seven, &defaultkeys_dev[3], sizeof(key128)); + memcpy(&keys->ncchkeyX_ninethree, &defaultkeys_dev[4], sizeof(key128)); + memcpy(&keys->ncchkeyX_ninesix, &defaultkeys_dev[5], sizeof(key128)); + } } int keyset_load_key(TiXmlHandle node, unsigned char* key, unsigned int size, int* valid) @@ -158,9 +206,12 @@ int keyset_load(keyset* keys, const char* fname, int verbose) 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("commonkeyx"), &keys->commonkeyX); keyset_load_key128(root.FirstChild("ncchfixedsystemkey"), &keys->ncchfixedsystemkey); + keyset_load_key128(root.FirstChild("ncchkeyxold"), &keys->ncchkeyX_old); + keyset_load_key128(root.FirstChild("ncchkeyxseven"), &keys->ncchkeyX_seven); + keyset_load_key128(root.FirstChild("ncchkeyxninethree"), &keys->ncchkeyX_ninethree); + keyset_load_key128(root.FirstChild("ncchkeyxninesix"), &keys->ncchkeyX_ninesix); return 1; @@ -169,14 +220,21 @@ int keyset_load(keyset* keys, const char* fname, int verbose) 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); - if (src->titlekey.valid) - keyset_set_key128(&keys->titlekey, src->titlekey.data); +#define COPY_IF_VALID(v) do {\ + if (src->v.valid && !keys->v.valid)\ + keyset_set_key128(&keys->v, src->v.data);\ +} while (0) + + COPY_IF_VALID(titlekey); + COPY_IF_VALID(commonkeyX); + COPY_IF_VALID(ncchfixedsystemkey); + COPY_IF_VALID(ncchkeyX_old); + COPY_IF_VALID(ncchkeyX_seven); + COPY_IF_VALID(ncchkeyX_ninethree); + COPY_IF_VALID(ncchkeyX_ninesix); + COPY_IF_VALID(seed); + +#undef COPY_IF_VALID } void keyset_set_key128(key128* key, unsigned char* keydata) @@ -190,44 +248,44 @@ 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) +void keyset_parse_commonkeyX(keyset* keys, char* keytext, int keylen) { - keyset_set_key128(&keys->commonkey, keydata); + keyset_parse_key128(&keys->commonkeyX, keytext, keylen); } -void keyset_parse_commonkey(keyset* keys, char* keytext, int keylen) +void keyset_parse_titlekey(keyset* keys, char* keytext, int keylen) { - keyset_parse_key128(&keys->commonkey, keytext, keylen); + keyset_parse_key128(&keys->titlekey, keytext, keylen); } -void keyset_set_titlekey(keyset* keys, unsigned char* keydata) +void keyset_parse_ncchkeyX_old(keyset* keys, char* keytext, int keylen) { - keyset_set_key128(&keys->titlekey, keydata); + keyset_parse_key128(&keys->ncchkeyX_old, keytext, keylen); } -void keyset_parse_titlekey(keyset* keys, char* keytext, int keylen) +void keyset_parse_ncchfixedsystemkey(keyset* keys, char* keytext, int keylen) { - keyset_parse_key128(&keys->titlekey, keytext, keylen); + keyset_parse_key128(&keys->ncchfixedsystemkey, keytext, keylen); } -void keyset_set_ncchkey(keyset* keys, unsigned char* keydata) +void keyset_parse_ncchkeyX_seven(keyset* keys, char* keytext, int keylen) { - keyset_set_key128(&keys->ncchkey, keydata); + keyset_parse_key128(&keys->ncchkeyX_seven, keytext, keylen); } -void keyset_parse_ncchkey(keyset* keys, char* keytext, int keylen) +void keyset_parse_ncchkeyX_ninethree(keyset* keys, char* keytext, int keylen) { - keyset_parse_key128(&keys->ncchkey, keytext, keylen); + keyset_parse_key128(&keys->ncchkeyX_ninethree, keytext, keylen); } -void keyset_set_ncchfixedsystemkey(keyset* keys, unsigned char* keydata) +void keyset_parse_ncchkeyX_ninesix(keyset* keys, char* keytext, int keylen) { - keyset_set_key128(&keys->ncchfixedsystemkey, keydata); + keyset_parse_key128(&keys->ncchkeyX_ninesix, keytext, keylen); } -void keyset_parse_ncchfixedsystemkey(keyset* keys, char* keytext, int keylen) +void keyset_parse_seed(keyset* keys, char* keytext, int keylen) { - keyset_parse_key128(&keys->ncchfixedsystemkey, keytext, keylen); + keyset_parse_key128(&keys->seed, keytext, keylen); } void keyset_dump_rsakey(rsakey2048* key, const char* keytitle) @@ -261,10 +319,17 @@ void keyset_dump_key128(key128* key, const char* keytitle) void keyset_dump(keyset* keys) { +#define DUMP_KEY(n, s) do {\ + keyset_dump_key128(&keys->n, (s));\ +} while(0) 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"); + DUMP_KEY(ncchkeyX_old, "NCCH OLD KEYX"); + DUMP_KEY(ncchkeyX_seven, "NCCH 7.0 KEYX"); + DUMP_KEY(ncchkeyX_ninethree, "NCCH N9.3 KEYX"); + DUMP_KEY(ncchkeyX_ninesix, "NCCH N9.6 KEYX"); + DUMP_KEY(ncchfixedsystemkey, "NCCH FIXEDSYSTEMKEY"); + DUMP_KEY(commonkeyX, "COMMON KEYX"); +#undef DUMP_KEY keyset_dump_rsakey(&keys->ncsdrsakey, "NCSD RSA KEY"); keyset_dump_rsakey(&keys->ncchdescrsakey, "NCCH DESC RSA KEY"); diff --git a/ctrtool/keyset.h b/ctrtool/keyset.h index 2cf28668..5f71d907 100644 --- a/ctrtool/keyset.h +++ b/ctrtool/keyset.h @@ -42,27 +42,31 @@ typedef struct typedef struct { - key128 commonkey; key128 titlekey; - key128 ncchkey; + key128 seed; + key128 commonkeyX; key128 ncchfixedsystemkey; + key128 ncchkeyX_old; + key128 ncchkeyX_seven; + key128 ncchkeyX_ninethree; + key128 ncchkeyX_ninesix; rsakey2048 ncsdrsakey; rsakey2048 ncchrsakey; rsakey2048 ncchdescrsakey; rsakey2048 firmrsakey; } keyset; -void keyset_init(keyset* keys); +void keyset_init(keyset* keys, u32 actions); 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_titlekey(keyset* keys, unsigned char* keydata); +void keyset_parse_commonkeyX(keyset* keys, char* keytext, int keylen); void keyset_parse_titlekey(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_ncchkeyX_old(keyset* keys, char* keytext, int keylen); void keyset_parse_ncchfixedsystemkey(keyset* keys, char* keytext, int keylen); +void keyset_parse_ncchkeyX_seven(keyset* keys, char* keytext, int keylen); +void keyset_parse_ncchkeyX_ninethree(keyset* keys, char* keytext, int keylen); +void keyset_parse_ncchkeyX_ninesix(keyset* keys, char* keytext, int keylen); +void keyset_parse_seed(keyset* keys, char* keytext, int keylen); void keyset_dump(keyset* keys); #ifdef __cplusplus diff --git a/ctrtool/main.c b/ctrtool/main.c index dd8ef88f..393d272d 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -52,11 +52,13 @@ static void usage(const char *argv0) " -k, --keyset=file Specify keyset file.\n" " -v, --verbose Give verbose output.\n" " -y, --verify Verify hashes and signatures.\n" + " -d, --dev Decrypt with development keys instead of retail.\n" " --unitsize=size Set media unit size (default 0x200).\n" " --commonkey=key Set common key.\n" " --titlekey=key Set tik title key.\n" " --ncchkey=key Set ncch key.\n" " --ncchsyskey=key Set ncch fixed system key.\n" + " --seed=key Set seed for ncch seed crypto.\n" " --showkeys Show the keys being used.\n" " --showsyscalls Show system call names instead of numbers.\n" " -t, --intype=type Specify input file type [ncsd, ncch, exheader, cia, tmd, lzss,\n" @@ -109,8 +111,7 @@ int main(int argc, char* argv[]) ctx.filetype = FILETYPE_UNKNOWN; settings_init(&ctx.usersettings); - keyset_init(&ctx.usersettings.keys); - keyset_init(&tmpkeys); + keyset_init(&tmpkeys, 0); while (1) @@ -137,8 +138,8 @@ int main(int argc, char* argv[]) {"raw", 0, NULL, 'r'}, {"unitsize", 1, NULL, 9}, {"showkeys", 0, NULL, 10}, - {"commonkey", 1, NULL, 11}, - {"ncchkey", 1, NULL, 12}, + {"commonkeyx", 1, NULL, 11}, + {"ncchkeyxold", 1, NULL, 12}, {"intype", 1, NULL, 't'}, {"lzssout", 1, NULL, 13}, {"firmdir", 1, NULL, 14}, @@ -152,10 +153,14 @@ int main(int argc, char* argv[]) {"titlekey", 1, NULL, 22}, {"plainrgn", 1, NULL, 23}, {"showsyscalls", 0, NULL, 24}, + {"ncchkeyxseven", 1, NULL, 25}, + {"ncchkeyxninethree", 1, NULL, 26}, + {"ncchkeyxninesix", 1, NULL, 27}, + {"seed", 1, NULL, 28}, {NULL}, }; - c = getopt_long(argc, argv, "ryxivpk:n:t:", long_options, &option_index); + c = getopt_long(argc, argv, "dryxivpk:n:t:", long_options, &option_index); if (c == -1) break; @@ -173,6 +178,10 @@ int main(int argc, char* argv[]) ctx.actions |= VerifyFlag; break; + case 'd': + ctx.actions |= DevFlag; + break; + case 'p': ctx.actions |= PlainFlag; break; @@ -228,8 +237,8 @@ int main(int argc, char* argv[]) 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 11: keyset_parse_commonkeyX(&tmpkeys, optarg, strlen(optarg)); break; + case 12: keyset_parse_ncchkeyX_old(&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; @@ -242,6 +251,10 @@ int main(int argc, char* argv[]) case 22: keyset_parse_titlekey(&tmpkeys, optarg, strlen(optarg)); break; case 23: settings_set_plainrgn_path(&ctx.usersettings, optarg); break; case 24: ctx.actions |= ShowSyscallsFlag; break; + case 25: keyset_parse_ncchkeyX_seven(&tmpkeys, optarg, strlen(optarg)); break; + case 26: keyset_parse_ncchkeyX_ninethree(&tmpkeys, optarg, strlen(optarg)); break; + case 27: keyset_parse_ncchkeyX_ninesix(&tmpkeys, optarg, strlen(optarg)); break; + case 28: keyset_parse_seed(&tmpkeys, optarg, strlen(optarg)); break; default: usage(argv[0]); @@ -259,6 +272,7 @@ int main(int argc, char* argv[]) usage(argv[0]); } + keyset_init(&ctx.usersettings.keys, ctx.actions); keyset_load(&ctx.usersettings.keys, keysetfname, (ctx.actions & VerboseFlag) | checkkeysetfile); keyset_merge(&ctx.usersettings.keys, &tmpkeys); if (ctx.actions & ShowKeysFlag) diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index b553b600..1c9f9ee8 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -6,6 +6,7 @@ #include "utils.h" #include "ctr.h" #include "settings.h" +#include "aes_keygen.h" #include static int programid_is_system(u8 programid[8]) @@ -333,6 +334,14 @@ void ncch_process(ncch_context* ctx, u32 actions) ncch_get_counter(ctx, exefscounter, NCCHTYPE_EXEFS); ncch_get_counter(ctx, romfscounter, NCCHTYPE_ROMFS); + if (actions & ShowKeysFlag) + { + fprintf(stdout, "Counter(s):\n"); + memdump(stdout, " exheader: ", exheadercounter, 0x10); + memdump(stdout, " ExeFS: ", exefscounter, 0x10); + memdump(stdout, " RomFS: ", romfscounter, 0x10); + } + exheader_set_file(&ctx->exheader, ctx->file); exheader_set_offset(&ctx->exheader, ncch_get_exheader_offset(ctx) ); @@ -351,7 +360,7 @@ void ncch_process(ncch_context* ctx, u32 actions) 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_keys(&ctx->exefs, ctx->key, ctx->special_key); exefs_set_encrypted(&ctx->exefs, ctx->encrypted); romfs_set_file(&ctx->romfs, ctx->file); @@ -359,7 +368,10 @@ void ncch_process(ncch_context* ctx, u32 actions) romfs_set_size(&ctx->romfs, ncch_get_romfs_size(ctx)); romfs_set_usersettings(&ctx->romfs, ctx->usersettings); romfs_set_counter(&ctx->romfs, romfscounter); - romfs_set_key(&ctx->romfs, ctx->key); + if (ctx->encrypted & NCCHCRYPTO_SPECIAL_FSES) + romfs_set_key(&ctx->romfs, ctx->special_key); + else + romfs_set_key(&ctx->romfs, ctx->key); romfs_set_encrypted(&ctx->romfs, ctx->encrypted); exheader_read(&ctx->exheader, actions); @@ -371,6 +383,23 @@ void ncch_process(ncch_context* ctx, u32 actions) if (actions & InfoFlag) ncch_print(ctx); + if (ctx->encrypted == NCCHCRYPTO_BROKEN) + { + fprintf(stderr, "Error, NCCH encryption broken.\n"); + return; + } + + if ((actions & ShowKeysFlag) && ctx->encrypted) + { + fprintf(stdout, "Using key(s):\n"); + memdump(stdout, " 0x2C: ", ctx->key, 0x10); + if (ctx->encrypted & NCCHCRYPTO_SPECIAL_FSES) + { + fprintf(stdout, " special (%02x): ", ctx->header.flags[3]); + memdump(stdout, "", ctx->special_key, 0x10); + } + } + if (actions & ExtractFlag) { ncch_save(ctx, NCCHTYPE_EXEFS, actions); @@ -482,8 +511,12 @@ u64 ncch_get_mediaunit_size(ncch_context* ctx) void ncch_determine_key(ncch_context* ctx, u32 actions) { exheader_header exheader; - u8* key = settings_get_ncch_key(ctx->usersettings); + u8* key; + u8* seed; ctr_ncchheader* header = &ctx->header; + u8 seedbuf[0x20]; + u8 seedhash[0x20]; + u8 keyX[0x10], keyY[0x10], seedKeyY[0x10]; ctx->encrypted = 0; memset(ctx->key, 0, 0x10); @@ -492,17 +525,58 @@ void ncch_determine_key(ncch_context* ctx, u32 actions) { 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 - + // In almost all of these scenarios, the normal 0x2C NCCH keyX will be the default, + // except for the old fixedkey crypto, where we'll override it anyway, so let's just + // set the 0x2C keyX first. + key = settings_get_ncchkeyX_old(ctx->usersettings); + if (key) + memcpy(keyX, key, 0x10); + else + fprintf(stderr, "Warning, could not read NCCH base key. Decryption will likely fail.\n"); + + // The keyY is normally the beginning of the NCCH header signature. In case seed crypto + // changes that, we'll override it below. + memcpy(keyY, header->signature, 0x10); + memcpy(seedKeyY, keyY, 0x10); - // Firstly, check if the NCCH is already decrypted, by reading the programid in the exheader + // 0x2c crypto is normally used; we override it where necessary + ctr_aes_keygen(keyX, keyY, ctx->key); + + // Seed crypto can be used alongside any other crypto type, so we'll need to figure this out early. + if (header->flags[7] & 0x20) + { + ctx->encrypted = NCCHCRYPTO_SEED; + seed = settings_get_seed(ctx->usersettings); + if (!seed) + { + fprintf(stderr, "This title uses seed crypto, but no seed is set, unable to decrypt.\n" + "Use -p to avoid decryption or use --seed=SEEDHERE to provide the seed.\n"); + ctx->encrypted = NCCHCRYPTO_BROKEN; + return; + } + + memcpy(seedbuf, seed, 0x10); + // Assumes running on little endian + memcpy(seedbuf + 0x10, header->programid, sizeof(header->programid)); + ctr_sha_256(seedbuf, 0x18, seedhash); + if (memcmp(seedhash, header->seedcheck, sizeof(header->seedcheck))) { + fprintf(stderr, "Seed check mismatch. (Got: %02x%02x%02x%02x, expected: %02x%02x%02x%02x)\n", + seedhash[0], seedhash[1], seedhash[2], seedhash[3], + header->seedcheck[0], header->seedcheck[1], header->seedcheck[2], header->seedcheck[3]); + ctx->encrypted = NCCHCRYPTO_BROKEN; + return; + } + + memcpy(seedbuf, header->signature, 0x10); + memcpy(seedbuf + 0x10, seed, 0x10); + ctr_sha_256(seedbuf, 0x20, seedhash); + memcpy(seedKeyY, seedhash, 0x10); + } + + // Check if the NCCH is already decrypted, by reading the programid in the exheader // Otherwise, use determination rules fseeko64(ctx->file, ncch_get_exheader_offset(ctx), SEEK_SET); memset(&exheader, 0, sizeof(exheader)); @@ -511,37 +585,74 @@ void ncch_determine_key(ncch_context* ctx, u32 actions) if (!memcmp(exheader.arm11systemlocalcaps.programid, ctx->header.programid, 8)) { // program id's match, so it's probably not encrypted - ctx->encrypted = 0; + ctx->encrypted = NCCHCRYPTO_NONE; + if (!(header->flags[7] & 4)) + fprintf(stderr, "Warning, exheader seems decrypted but the NCCH says it isn't.\n" + "This NCCH will likely break on console.\n"); } - else if (header->flags[7] & 4) + else if (header->flags[7] & 4) // no crypto { - ctx->encrypted = 0; // not encrypted + ctx->encrypted = NCCHCRYPTO_NONE; } - else if (header->flags[7] & 1) + else if (header->flags[7] & 1) // fixed key crypto { + ctx->encrypted = NCCHCRYPTO_FIXED; 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 + if (!key) { + fprintf(stderr, "Error, could not read system fixed key.\n"); + ctx->encrypted = NCCHCRYPTO_BROKEN; + } else { memcpy(ctx->key, key, 0x10); + } } else { // null key - ctx->encrypted = 1; memset(ctx->key, 0, 0x10); } } + else if (header->flags[3] == 0x01) // 7.0 crypto + { + ctx->encrypted = NCCHCRYPTO_SEVEN; + key = settings_get_ncchkeyX_seven(ctx->usersettings); + if (!key) { + fprintf(stderr, "Error, could not read NCCH 7.0 keyX.\n"); + return; + } + ctr_aes_keygen(key, seedKeyY, ctx->special_key); + } + else if (header->flags[3] == 0x0A) // N9.3 crypto + { + ctx->encrypted = NCCHCRYPTO_NINETHREE; + key = settings_get_ncchkeyX_ninethree(ctx->usersettings); + if (!key) { + fprintf(stderr, "Error, could not read NCCH 9.3 keyX.\n"); + return; + } + ctr_aes_keygen(key, seedKeyY, ctx->special_key); + } + else if (header->flags[3] == 0x0B) // N9.6 crypto + { + ctx->encrypted = NCCHCRYPTO_NINESIX; + key = settings_get_ncchkeyX_ninesix(ctx->usersettings); + if (!key) { + fprintf(stderr, "Error, could not read NCCH 9.6 keyX.\n"); + return; + } + ctr_aes_keygen(key, seedKeyY, ctx->special_key); + } + else if (header->flags[3] != 0) // unknown special crypto + { + fprintf(stderr, "Warning, unknown NCCH crypto method.\n"); + ctx->encrypted = NCCHCRYPTO_BROKEN; + } else { - // secure key (cannot decrypt!) - fprintf(stdout, "Warning, could not read secure key.\n"); - ctx->encrypted = 1; - memset(ctx->key, 0, 0x10); + // old/normal NCCH crypto + ctx->encrypted = NCCHCRYPTO_OLD; } } } diff --git a/ctrtool/ncch.h b/ctrtool/ncch.h index 64dc4901..79f89578 100644 --- a/ctrtool/ncch.h +++ b/ctrtool/ncch.h @@ -20,6 +20,19 @@ typedef enum NCCHTYPE_PLAINRGN = 5, } ctr_ncchtypes; +typedef enum +{ + NCCHCRYPTO_NONE = 0, //< already decrypted + NCCHCRYPTO_FIXED = 1, //< fixed key crypto, used for SDK-made application titles and very very old system titles + NCCHCRYPTO_OLD = (1<<1), //< crypto used before 7.0 + NCCHCRYPTO_SEVEN = (1<<2), //< crypto used starting with 7.0 + NCCHCRYPTO_NINETHREE = (1<<3), //< crypto used on N3DS starting with 9.3 + NCCHCRYPTO_NINESIX = (1<<4), //< crypto used on N3DS starting with 9.6 + NCCHCRYPTO_SEED = (1<<5), //< crypto used starting with 9.6 for preloading titles + NCCHCRYPTO_SPECIAL_FSES = NCCHCRYPTO_SEVEN | NCCHCRYPTO_NINETHREE | NCCHCRYPTO_NINESIX | NCCHCRYPTO_SEED, //< ExeFS and RomFS need new keys + NCCHCRYPTO_BROKEN = 0xFF //< Internal: seed crypto required but no seed set +} ctr_ncchcryptotype; + typedef struct { u8 signature[0x100]; @@ -58,6 +71,8 @@ typedef struct { FILE* file; u8 key[16]; + u8 special_key[16]; // used with the 7.x+ crypto methods + u8 seed[16]; u32 encrypted; u64 offset; u64 size; diff --git a/ctrtool/settings.c b/ctrtool/settings.c index ddbf51bc..95f7dbd3 100644 --- a/ctrtool/settings.c +++ b/ctrtool/settings.c @@ -136,38 +136,55 @@ unsigned int settings_get_mediaunit_size(settings* usersettings) return 0; } -unsigned char* settings_get_ncch_key(settings* usersettings) +#define GETKEY(s, k) do {\ + if ((s) && (s)->keys.k.valid)\ + return (s)->keys.k.data;\ + else\ + return NULL;\ +} while (0) + +unsigned char* settings_get_ncch_fixedsystemkey(settings* usersettings) { - if (usersettings && usersettings->keys.ncchkey.valid) - return usersettings->keys.ncchkey.data; - else - return 0; + GETKEY(usersettings, ncchfixedsystemkey); } -unsigned char* settings_get_ncch_fixedsystemkey(settings* usersettings) +unsigned char* settings_get_ncchkeyX_old(settings* usersettings) { - if (usersettings && usersettings->keys.ncchfixedsystemkey.valid) - return usersettings->keys.ncchfixedsystemkey.data; - else - return 0; + GETKEY(usersettings, ncchkeyX_old); } -unsigned char* settings_get_common_key(settings* usersettings) +unsigned char* settings_get_ncchkeyX_seven(settings* usersettings) { - if (usersettings && usersettings->keys.commonkey.valid) - return usersettings->keys.commonkey.data; - else - return 0; + GETKEY(usersettings, ncchkeyX_seven); +} + +unsigned char* settings_get_ncchkeyX_ninethree(settings* usersettings) +{ + GETKEY(usersettings, ncchkeyX_ninethree); +} + +unsigned char* settings_get_ncchkeyX_ninesix(settings* usersettings) +{ + GETKEY(usersettings, ncchkeyX_ninesix); +} + +unsigned char* settings_get_common_keyX(settings* usersettings) +{ + GETKEY(usersettings, commonkeyX); +} + +unsigned char* settings_get_seed(settings* usersettings) +{ + GETKEY(usersettings, seed); } unsigned char* settings_get_title_key(settings* usersettings) { - if (usersettings && usersettings->keys.titlekey.valid) - return usersettings->keys.titlekey.data; - else - return 0; + GETKEY(usersettings, titlekey); } +#undef GETKEY + int settings_get_ignore_programid(settings* usersettings) { if (usersettings) diff --git a/ctrtool/settings.h b/ctrtool/settings.h index 2822b93a..c3f819f0 100644 --- a/ctrtool/settings.h +++ b/ctrtool/settings.h @@ -46,9 +46,13 @@ filepath* settings_get_firm_dir_path(settings* usersettings); filepath* settings_get_wav_path(settings* usersettings); filepath* settings_get_plainrgn_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); +unsigned char* settings_get_ncchkeyX_old(settings* usersettings); +unsigned char* settings_get_ncchkeyX_seven(settings* usersettings); +unsigned char* settings_get_ncchkeyX_ninethree(settings* usersettings); +unsigned char* settings_get_ncchkeyX_ninesix(settings* usersettings); +unsigned char* settings_get_common_keyX(settings* usersettings); +unsigned char* settings_get_seed(settings* usersettings); unsigned char* settings_get_title_key(settings* usersettings); int settings_get_ignore_programid(settings* usersettings); int settings_get_list_romfs_files(settings* usersettings); diff --git a/ctrtool/tik.c b/ctrtool/tik.c index ce582c38..c9a1e5bd 100644 --- a/ctrtool/tik.c +++ b/ctrtool/tik.c @@ -2,6 +2,7 @@ #include #include +#include "aes_keygen.h" #include "tik.h" #include "ctr.h" #include "utils.h" @@ -31,9 +32,9 @@ void tik_set_usersettings(tik_context* ctx, settings* usersettings) ctx->usersettings = usersettings; } -void tik_get_decrypted_titlekey(tik_context* ctx, u8 decryptedkey[0x10]) +void tik_get_titlekey(tik_context* ctx, u8 key[0x10]) { - memcpy(decryptedkey, ctx->titlekey, 16); + memcpy(ctx->titlekey, key, 16); } void tik_get_titleid(tik_context* ctx, u8 titleid[8]) @@ -50,16 +51,29 @@ void tik_get_iv(tik_context* ctx, u8 iv[16]) void tik_decrypt_titlekey(tik_context* ctx, u8 decryptedkey[0x10]) { u8 iv[16]; - u8* key = settings_get_common_key(ctx->usersettings); + u8* keyX = settings_get_common_keyX(ctx->usersettings); + const u8 keyYs[6][16] = { + // application titles (eShop titles) + {0xd0, 0x7b, 0x33, 0x7f, 0x9c, 0xa4, 0x38, 0x59, 0x32, 0xa2, 0xe2, 0x57, 0x23, 0x23, 0x2e, 0xb9}, + // system titles + {0x0c, 0x76, 0x72, 0x30, 0xf0, 0x99, 0x8f, 0x1c, 0x46, 0x82, 0x82, 0x02, 0xfa, 0xac, 0xbe, 0x4c}, + // these are unused + {0xc4, 0x75, 0xcb, 0x3a, 0xb8, 0xc7, 0x88, 0xbb, 0x57, 0x5e, 0x12, 0xa1, 0x09, 0x07, 0xb8, 0xa4}, + {0xe4, 0x86, 0xee, 0xe3, 0xd0, 0xc0, 0x9c, 0x90, 0x2f, 0x66, 0x86, 0xd4, 0xc0, 0x6f, 0x64, 0x9f}, + {0xed, 0x31, 0xba, 0x9c, 0x04, 0xb0, 0x67, 0x50, 0x6c, 0x44, 0x97, 0xa3, 0x5b, 0x78, 0x04, 0xfc}, + {0x5e, 0x66, 0x99, 0x8a, 0xb4, 0xe8, 0x93, 0x16, 0x06, 0x85, 0x0f, 0xd7, 0xa1, 0x6d, 0xd7, 0x55}, + }; + u8 key[16]; memset(decryptedkey, 0, 0x10); - if (!key) + if (!keyX) { fprintf(stdout, "Warning, could not read common key.\n"); } else { + ctr_aes_keygen(keyX, keyYs[(ctx->tik.title_id[3] & 0x10) ? 1 : 0], key); memset(iv, 0, 0x10); memcpy(iv, ctx->tik.title_id, 8); @@ -108,7 +122,7 @@ void tik_print(tik_context* ctx) memdump(stdout, "Encrypted Titlekey: ", tik->encrypted_title_key, 0x10); - if (settings_get_common_key(ctx->usersettings)) + if (settings_get_common_keyX(ctx->usersettings)) memdump(stdout, "Decrypted Titlekey: ", ctx->titlekey, 0x10); memdump(stdout, "Ticket ID: ", tik->ticket_id, 0x08); diff --git a/ctrtool/tik.h b/ctrtool/tik.h index edd72c5b..1aa05966 100644 --- a/ctrtool/tik.h +++ b/ctrtool/tik.h @@ -54,7 +54,7 @@ void tik_set_file(tik_context* ctx, FILE* file); void tik_set_offset(tik_context* ctx, u64 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_titlekey(tik_context* ctx, u8 key[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]); diff --git a/ctrtool/types.h b/ctrtool/types.h index 5904a080..c35e2ba8 100644 --- a/ctrtool/types.h +++ b/ctrtool/types.h @@ -25,6 +25,7 @@ enum flags ShowKeysFlag = (1<<6), DecompressCodeFlag = (1<<7), ShowSyscallsFlag = (1<<8), + DevFlag = (1<<9), }; enum validstate From 876c197387717185b01c4f089eb2eaad20def16e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 23 May 2017 01:00:43 -0700 Subject: [PATCH 191/317] Update .vcproj with new files --- ctrtool/ctrtool.vcxproj | 2 ++ ctrtool/ctrtool.vcxproj.filters | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/ctrtool/ctrtool.vcxproj b/ctrtool/ctrtool.vcxproj index 71596d61..21abef9d 100644 --- a/ctrtool/ctrtool.vcxproj +++ b/ctrtool/ctrtool.vcxproj @@ -91,6 +91,7 @@ + @@ -124,6 +125,7 @@ + diff --git a/ctrtool/ctrtool.vcxproj.filters b/ctrtool/ctrtool.vcxproj.filters index f7862f27..8f0b3927 100644 --- a/ctrtool/ctrtool.vcxproj.filters +++ b/ctrtool/ctrtool.vcxproj.filters @@ -120,6 +120,9 @@ Source Files + + Source Files + @@ -215,5 +218,8 @@ Header Files + + Header Files + \ No newline at end of file From fae366c1270d3d143c95d3a804b5fbbe32247f95 Mon Sep 17 00:00:00 2001 From: Reisyukaku Date: Wed, 24 May 2017 00:35:38 -0400 Subject: [PATCH 192/317] fixed subtle titlekey issue --- ctrtool/tik.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/tik.c b/ctrtool/tik.c index c9a1e5bd..0c1b69ce 100644 --- a/ctrtool/tik.c +++ b/ctrtool/tik.c @@ -34,7 +34,7 @@ void tik_set_usersettings(tik_context* ctx, settings* usersettings) void tik_get_titlekey(tik_context* ctx, u8 key[0x10]) { - memcpy(ctx->titlekey, key, 16); + memcpy(key, ctx->titlekey, 0x10); } void tik_get_titleid(tik_context* ctx, u8 titleid[8]) From 183d95bffa2c67a933dafa51282a1c9980f40129 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 25 May 2017 12:56:51 +0800 Subject: [PATCH 193/317] [ctrtool] fixed keygen add()/sub() functions --- ctrtool/aes_keygen.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/ctrtool/aes_keygen.c b/ctrtool/aes_keygen.c index d8bda853..eb81bdff 100644 --- a/ctrtool/aes_keygen.c +++ b/ctrtool/aes_keygen.c @@ -45,14 +45,6 @@ void n128_add(const uint8_t *a, const uint8_t *b, uint8_t *out) carry = sum >> 8; out[i] = sum & 0xff; } - - while (carry != 0) { - for (int i = 15; i >= 0; i--) { - sum = out[i] + carry; - carry = sum >> 8; - out[i] = sum & 0xff; - } - } } /* out = a - b @@ -77,25 +69,6 @@ void n128_sub(const uint8_t *a, const uint8_t *b, uint8_t *out) // set value out[i] = sum & 0xff; } - - - while (carry != 0) { - for (int i = 15; i >= 0; i--) { - sum = out[i] - carry; - - // check to see if anything was borrowed from next byte - if (out[i] < carry) { - sum += 0x100; - carry = 1; - } - else { - carry = 0; - } - - out[i] = sum & 0xff; - } - } - } void n128_xor(const uint8_t *a, const uint8_t *b, uint8_t *out) From 4f875b0d0b82ba0e2635e23736f7bbf110214081 Mon Sep 17 00:00:00 2001 From: jakcron Date: Thu, 25 May 2017 13:50:03 +0800 Subject: [PATCH 194/317] [makerom] Implemented support for secure crypto. Secure crypto is now default. Seeded keyY crypto not yet supported. Time for version 0.16!!! --- makerom/aes_keygen.c | 27 ------------------------ makerom/keyset.c | 38 +++++++++++++++++++++++++--------- makerom/ncch.c | 46 +++++++++++++++++++++++++++-------------- makerom/ncch.h | 8 +++++++ makerom/pki/dev.h | 24 +++++++++++---------- makerom/pki/prod.h | 27 +++++++++++------------- makerom/user_settings.c | 4 ++-- 7 files changed, 94 insertions(+), 80 deletions(-) diff --git a/makerom/aes_keygen.c b/makerom/aes_keygen.c index c9934101..fc2310ec 100644 --- a/makerom/aes_keygen.c +++ b/makerom/aes_keygen.c @@ -45,14 +45,6 @@ void n128_add(const uint8_t *a, const uint8_t *b, uint8_t *out) carry = sum >> 8; out[i] = sum & 0xff; } - - while (carry != 0) { - for (int i = 15; i >= 0; i--) { - sum = out[i] + carry; - carry = sum >> 8; - out[i] = sum & 0xff; - } - } } /* out = a - b @@ -77,25 +69,6 @@ void n128_sub(const uint8_t *a, const uint8_t *b, uint8_t *out) // set value out[i] = sum & 0xff; } - - - while (carry != 0) { - for (int i = 15; i >= 0; i--) { - sum = out[i] - carry; - - // check to see if anything was borrowed from next byte - if (out[i] < carry) { - sum += 0x100; - carry = 1; - } - else { - carry = 0; - } - - out[i] = sum & 0xff; - } - } - } void n128_xor(const uint8_t *a, const uint8_t *b, uint8_t *out) diff --git a/makerom/keyset.c b/makerom/keyset.c index 85cc3f7a..b8c26716 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -101,10 +101,10 @@ int LoadKeysFromResources(keys_struct *keys) SetNormalKey(keys, dev_fixed_ncch_key[0]); SetSystemFixedKey(keys, dev_fixed_ncch_key[1]); - /* - for(int i = 0; i < 2; i++) + + for(int i = 0; i < 4; i++) SetNcchKeyX(keys, dev_unfixed_ncch_keyX[i],i); - */ + /* RSA Keys */ // CIA @@ -124,20 +124,19 @@ int LoadKeysFromResources(keys_struct *keys) keys->keysetLoaded = true; /* AES Keys */ // CIA - //for(int i = 0; i < 6; i++){ - // keys->aes.commonKey[i] = malloc(16); - // ctr_aes_keygen(ctr_common_etd_keyX_ppki, ctr_common_etd_keyY_ppki[i], keys->aes.commonKey[i]); - //} + for (int i = 0; i < 6; i++) + SetCommonKey(keys, ctr_common_etd_key_ppki[i], i); + if(keys->aes.currentCommonKey > 0xff) SetCurrentCommonKey(keys,0); // NCCH keys->aes.normalKey = NULL; keys->aes.systemFixedKey = NULL; - /* - for(int i = 0; i < 2; i++) + + for(int i = 0; i < 4; i++) SetNcchKeyX(keys, prod_unfixed_ncch_keyX[i],i); - */ + /* RSA Keys */ // CIA @@ -200,6 +199,7 @@ void DumpKeyset(keys_struct *keys) { bool showNcchFixedKeys = (keys->aes.normalKey || keys->aes.systemFixedKey); bool showCommonKeys = false; + bool showNcchKeyXs = false; for(int i = 0; i < 256; i++){ if(keys->aes.commonKey[i]){ showCommonKeys = true; @@ -207,6 +207,13 @@ void DumpKeyset(keys_struct *keys) } } + for (int i = 0; i < 256; i++) { + if (keys->aes.ncchKeyX[i]) { + showNcchKeyXs = true; + break; + } + } + printf("[*] Keyset\n"); if(showCommonKeys){ @@ -218,6 +225,17 @@ void DumpKeyset(keys_struct *keys) } } } + + if (showNcchKeyXs) { + printf(" > Unfixed NCCH KeyXs\n"); + for (int i = 0; i < 256; i++) { + if (keys->aes.ncchKeyX[i]) { + printf(" [0x%02x] ", i); + memdump(stdout, "", keys->aes.ncchKeyX[i], 16); + } + } + } + if(showNcchFixedKeys){ printf(" > Fixed NCCH Keys\n"); if(keys->aes.normalKey) diff --git a/makerom/ncch.c b/makerom/ncch.c index 8621b126..b400ec86 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -1024,33 +1024,49 @@ bool IsNcchEncrypted(ncch_hdr *hdr) bool SetNcchKeys(keys_struct *keys, ncch_hdr *hdr) { - if(!IsNcchEncrypted(hdr)) + if (!IsNcchEncrypted(hdr)) return true; - - if((hdr->flags[ncchflag_OTHER_FLAG] & otherflag_FixedCryptoKey) == otherflag_FixedCryptoKey){ - if((hdr->programId[4] & 0x10) == 0x10){ - if(!keys->aes.systemFixedKey) + + if ((hdr->flags[ncchflag_OTHER_FLAG] & otherflag_FixedCryptoKey) == otherflag_FixedCryptoKey) { + if ((hdr->programId[4] & 0x10) == 0x10) { + if (!keys->aes.systemFixedKey) return false; - memcpy(keys->aes.ncchKey0,keys->aes.systemFixedKey,AES_128_KEY_SIZE); - memcpy(keys->aes.ncchKey1,keys->aes.systemFixedKey,AES_128_KEY_SIZE); + memcpy(keys->aes.ncchKey0, keys->aes.systemFixedKey, AES_128_KEY_SIZE); + memcpy(keys->aes.ncchKey1, keys->aes.systemFixedKey, AES_128_KEY_SIZE); return true; } - else{ - if(!keys->aes.normalKey) + else { + if (!keys->aes.normalKey) return false; - memcpy(keys->aes.ncchKey0,keys->aes.normalKey,AES_128_KEY_SIZE); - memcpy(keys->aes.ncchKey1,keys->aes.normalKey,AES_128_KEY_SIZE); + memcpy(keys->aes.ncchKey0, keys->aes.normalKey, AES_128_KEY_SIZE); + memcpy(keys->aes.ncchKey1, keys->aes.normalKey, AES_128_KEY_SIZE); return true; } } - + + u8 ncch_keyx_index = 0; + switch (hdr->flags[ncchflag_CONTENT_KEYX]) + { + case (keyx_7_0): + ncch_keyx_index = 1; + break; + case (keyx_9_3): + ncch_keyx_index = 2; + break; + case (keyx_9_6): + ncch_keyx_index = 3; + break; + default: + ncch_keyx_index = 0; + } + if(keys->aes.ncchKeyX[0]) ctr_aes_keygen(keys->aes.ncchKeyX[0],hdr->signature,keys->aes.ncchKey0); else return false; - - if(keys->aes.ncchKeyX[hdr->flags[ncchflag_CONTENT_KEYX]]) - ctr_aes_keygen(keys->aes.ncchKeyX[ncchflag_CONTENT_KEYX], hdr->signature, keys->aes.ncchKey0); + + if(keys->aes.ncchKeyX[ncch_keyx_index]) + ctr_aes_keygen(keys->aes.ncchKeyX[ncch_keyx_index], hdr->signature, keys->aes.ncchKey0); else return false; diff --git a/makerom/ncch.h b/makerom/ncch.h index 93c56860..b2066dfe 100644 --- a/makerom/ncch.h +++ b/makerom/ncch.h @@ -72,6 +72,14 @@ typedef enum platform_SNAKE = 0x2 } ncch_platform; +typedef enum +{ + keyx_regular = 0x00, + keyx_7_0 = 0x01, + keyx_9_3 = 0x0A, + keyx_9_6 = 0x0B, +} ncch_keyx_id; + typedef struct { u16 formatVersion; diff --git a/makerom/pki/dev.h b/makerom/pki/dev.h index 1099a23b..eb793850 100644 --- a/makerom/pki/dev.h +++ b/makerom/pki/dev.h @@ -6,26 +6,28 @@ #endif // AES KEYS -static const unsigned char dev_unfixed_ncch_keyX[2][16] = // Dummy +static const unsigned char dev_unfixed_ncch_keyX[4][16] = { - {0x82, 0xAD, 0xED, 0xC7, 0xBA, 0x0A, 0x3F, 0x3D, 0x5F, 0xDD, 0x30, 0x0F, 0x0E, 0x9B, 0xE1, 0x5B} , // Normal - {0xE5, 0x70, 0x6F, 0x65, 0x6A, 0xF4, 0xD9, 0x3F, 0x1E, 0x2F, 0x29, 0x3F, 0x16, 0x15, 0x4E, 0xD8} , // 7.X new Crypto + { 0x51, 0x02, 0x07, 0x51, 0x55, 0x07, 0xcb, 0xb1, 0x8e, 0x24, 0x3d, 0xcb, 0x85, 0xe2, 0x3a, 0x1d }, // Regular + { 0x81, 0x90, 0x7a, 0x4b, 0x6f, 0x1b, 0x47, 0x32, 0x3a, 0x67, 0x79, 0x74, 0xce, 0x4a, 0xd7, 0x1b }, // >=7.0 + { 0x30, 0x4b, 0xf1, 0x46, 0x83, 0x72, 0xee, 0x64, 0x11, 0x5e, 0xbd, 0x40, 0x93, 0xd8, 0x42, 0x76 }, // >=9.3 (New3DS) + { 0x6c, 0x8b, 0x29, 0x44, 0xa0, 0x72, 0x60, 0x35, 0xf9, 0x41, 0xdf, 0xc0, 0x18, 0x52, 0x4f, 0xb6 } // >=9.6 (New3DS) }; static const unsigned char dev_fixed_ncch_key[2][16] = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} , // Normal FixedKey - {0x52, 0x7C, 0xE6, 0x30, 0xA9, 0xCA, 0x30, 0x5F, 0x36, 0x96, 0xF3, 0xCD, 0xE9, 0x54, 0x19, 0x4B} , // System FixedKey + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Normal FixedKey + {0x52, 0x7C, 0xE6, 0x30, 0xA9, 0xCA, 0x30, 0x5F, 0x36, 0x96, 0xF3, 0xCD, 0xE9, 0x54, 0x19, 0x4B} // System FixedKey }; static const unsigned char ctr_common_etd_key_dpki[6][16] = { - {0x55, 0xA3, 0xF8, 0x72, 0xBD, 0xC8, 0x0C, 0x55, 0x5A, 0x65, 0x43, 0x81, 0x13, 0x9E, 0x15, 0x3B} , // 0 - eShop Titles - {0x44, 0x34, 0xED, 0x14, 0x82, 0x0C, 0xA1, 0xEB, 0xAB, 0x82, 0xC1, 0x6E, 0x7B, 0xEF, 0x0C, 0x25} , // 1 - System Titles - {0xF6, 0x2E, 0x3F, 0x95, 0x8E, 0x28, 0xA2, 0x1F, 0x28, 0x9E, 0xEC, 0x71, 0xA8, 0x66, 0x29, 0xDC} , // 2 - {0x2B, 0x49, 0xCB, 0x6F, 0x99, 0x98, 0xD9, 0xAD, 0x94, 0xF2, 0xED, 0xE7, 0xB5, 0xDA, 0x3E, 0x27} , // 3 - {0x75, 0x05, 0x52, 0xBF, 0xAA, 0x1C, 0x04, 0x07, 0x55, 0xC8, 0xD5, 0x9A, 0x55, 0xF9, 0xAD, 0x1F} , // 4 - {0xAA, 0xDA, 0x4C, 0xA8, 0xF6, 0xE5, 0xA9, 0x77, 0xE0, 0xA0, 0xF9, 0xE4, 0x76, 0xCF, 0x0D, 0x63} , // 5 + { 0x55, 0xA3, 0xF8, 0x72, 0xBD, 0xC8, 0x0C, 0x55, 0x5A, 0x65, 0x43, 0x81, 0x13, 0x9E, 0x15, 0x3B }, // 0 - eShop Titles + { 0x44, 0x34, 0xED, 0x14, 0x82, 0x0C, 0xA1, 0xEB, 0xAB, 0x82, 0xC1, 0x6E, 0x7B, 0xEF, 0x0C, 0x25 }, // 1 - System Titles + { 0xF6, 0x2E, 0x3F, 0x95, 0x8E, 0x28, 0xA2, 0x1F, 0x28, 0x9E, 0xEC, 0x71, 0xA8, 0x66, 0x29, 0xDC }, + { 0x2B, 0x49, 0xCB, 0x6F, 0x99, 0x98, 0xD9, 0xAD, 0x94, 0xF2, 0xED, 0xE7, 0xB5, 0xDA, 0x3E, 0x27 }, + { 0x75, 0x05, 0x52, 0xBF, 0xAA, 0x1C, 0x04, 0x07, 0x55, 0xC8, 0xD5, 0x9A, 0x55, 0xF9, 0xAD, 0x1F }, + { 0xAA, 0xDA, 0x4C, 0xA8, 0xF6, 0xE5, 0xA9, 0x77, 0xE0, 0xA0, 0xF9, 0xE4, 0x76, 0xCF, 0x0D, 0x63 } }; //RSA Keys diff --git a/makerom/pki/prod.h b/makerom/pki/prod.h index 31250811..790bd4df 100644 --- a/makerom/pki/prod.h +++ b/makerom/pki/prod.h @@ -5,25 +5,22 @@ #endif // AES KEYS -static const unsigned char prod_unfixed_ncch_keyX[2][16] = // Dummy +static const unsigned char prod_unfixed_ncch_keyX[4][16] = { - {0x81, 0x50, 0xA9, 0x78, 0x53, 0x3B, 0xA5, 0xE9, 0xA5, 0x0A, 0x23, 0x16, 0xB9, 0x3A, 0xED, 0x5A} , // Normal - {0xB4, 0xD1, 0xCF, 0x58, 0x49, 0xCE, 0x8A, 0x2D, 0x71, 0x58, 0xF6, 0x66, 0x77, 0x5D, 0x16, 0x3D} , // 7.X new Crypto + { 0xb9, 0x8e, 0x95, 0xce, 0xca, 0x3e, 0x4d, 0x17, 0x1f, 0x76, 0xa9, 0x4d, 0xe9, 0x34, 0xc0, 0x53 }, // Regular + { 0xce, 0xe7, 0xd8, 0xab, 0x30, 0xc0, 0x0d, 0xae, 0x85, 0x0e, 0xf5, 0xe3, 0x82, 0xac, 0x5a, 0xf3 }, // >=7.0 + { 0x82, 0xe9, 0xc9, 0xbe, 0xbf, 0xb8, 0xbd, 0xb8, 0x75, 0xec, 0xc0, 0xa0, 0x7d, 0x47, 0x43, 0x74 }, // >=9.3 (New3DS) + { 0x45, 0xad, 0x04, 0x95, 0x39, 0x92, 0xc7, 0xc8, 0x93, 0x72, 0x4a, 0x9a, 0x7b, 0xce, 0x61, 0x82 } // >=9.6 (New3DS) }; -static const unsigned char ctr_common_etd_keyX_ppki[16] = // Dummy +static const unsigned char ctr_common_etd_key_ppki[6][16] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -static const unsigned char ctr_common_etd_keyY_ppki[6][16] = -{ - {0xD0, 0x7B, 0x33, 0x7F, 0x9C, 0xA4, 0x38, 0x59, 0x32, 0xA2, 0xE2, 0x57, 0x23, 0x23, 0x2E, 0xB9} , // 0 - eShop Titles - {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C} , // 1 - System Titles - {0xC4, 0x75, 0xCB, 0x3A, 0xB8, 0xC7, 0x88, 0xBB, 0x57, 0x5E, 0x12, 0xA1, 0x09, 0x07, 0xB8, 0xA4} , // 2 - {0xE4, 0x86, 0xEE, 0xE3, 0xD0, 0xC0, 0x9C, 0x90, 0x2F, 0x66, 0x86, 0xD4, 0xC0, 0x6F, 0x64, 0x9F} , // 3 - {0xED, 0x31, 0xBA, 0x9C, 0x04, 0xB0, 0x67, 0x50, 0x6C, 0x44, 0x97, 0xA3, 0x5B, 0x78, 0x04, 0xFC} , // 4 - {0x5E, 0x66, 0x99, 0x8A, 0xB4, 0xE8, 0x93, 0x16, 0x06, 0x85, 0x0F, 0xD7, 0xA1, 0x6D, 0xD7, 0x55} , // 5 + { 0x64, 0xC5, 0xFD, 0x55, 0xDD, 0x3A, 0xD9, 0x88, 0x32, 0x5B, 0xAA, 0xEC, 0x52, 0x43, 0xDB, 0x98 } , // 0 - eShop Titles + { 0x4A, 0xAA, 0x3D, 0x0E, 0x27, 0xD4, 0xD7, 0x28, 0xD0, 0xB1, 0xB4, 0x33, 0xF0, 0xF9, 0xCB, 0xC8 } , // 1 - System Titles + { 0xFB, 0xB0, 0xEF, 0x8C, 0xDB, 0xB0, 0xD8, 0xE4, 0x53, 0xCD, 0x99, 0x34, 0x43, 0x71, 0x69, 0x7F } , // 2 + { 0x25, 0x95, 0x9B, 0x7A, 0xD0, 0x40, 0x9F, 0x72, 0x68, 0x41, 0x98, 0xBA, 0x2E, 0xCD, 0x7D, 0xC6 } , // 3 + { 0x7A, 0xDA, 0x22, 0xCA, 0xFF, 0xC4, 0x76, 0xCC, 0x82, 0x97, 0xA0, 0xC7, 0xCE, 0xEE, 0xEE, 0xBE } , // 4 + { 0xA5, 0x05, 0x1C, 0xA1, 0xB3, 0x7D, 0xCF, 0x3A, 0xFB, 0xCF, 0x8C, 0xC1, 0xED, 0xD9, 0xCE, 0x02 } , // 5 }; // RSA KEYS diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 303b61bb..5089591d 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -87,7 +87,7 @@ void SetDefaults(user_settings *set) set->common.keys.accessDescSign.presetType = desc_NotSpecified; // Build NCCH Info - set->ncch.useSecCrypto = false; + set->ncch.useSecCrypto = true; set->ncch.buildNcch0 = true; set->ncch.includeExefsLogo = false; set->common.outFormat = NCCH; @@ -900,7 +900,7 @@ void PrintNoNeedParam(char *arg) void DisplayBanner(void) { - printf("CTR MAKEROM v0.15 (C) 3DSGuy 2014\n"); + printf("CTR MAKEROM v0.16 (C) 3DSGuy 2017\n"); printf("Built: %s %s\n\n", __TIME__, __DATE__); } From 6e858be07bb9faee8d616be0c57fae839ff3a5e2 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 28 May 2017 10:27:55 +0800 Subject: [PATCH 195/317] [makerom] Relaxed support for bad signatures (-ignoresign). Warns user when something cannot be signed, instead of treating it like a fatal error. --- makerom/accessdesc.c | 76 +++++++++-------- makerom/exheader.c | 4 +- makerom/keyset.c | 177 +++++++++++++++++----------------------- makerom/keyset.h | 33 ++++---- makerom/ncch.c | 57 +++++++++---- makerom/ncsd.c | 7 +- makerom/tik.c | 8 +- makerom/tmd.c | 8 +- makerom/user_settings.c | 14 ++-- 9 files changed, 206 insertions(+), 178 deletions(-) diff --git a/makerom/accessdesc.c b/makerom/accessdesc.c index 847118f4..20c4e876 100644 --- a/makerom/accessdesc.c +++ b/makerom/accessdesc.c @@ -19,30 +19,28 @@ const CtrSdkDepList* accessdesc_GetPresetDependencyList(keys_struct *keys); int set_AccessDesc(exheader_settings *exhdrset) { - if(exhdrset->useAccessDescPreset) // Use AccessDesc Template + if(exhdrset->useAccessDescPreset == true) // Use AccessDesc Template return accessdesc_GetSignFromPreset(exhdrset); - else if(exhdrset->rsf->CommonHeaderKey.Found) // Keydata exists in RSF + else if(exhdrset->rsf->CommonHeaderKey.Found == true) // Keydata exists in RSF return accessdesc_GetSignFromRsf(exhdrset); - else if(!exhdrset->keys->rsa.requiresPresignedDesc) // Else if The AccessDesc can be signed with key + else if (Rsa2048Key_CanSign(&exhdrset->keys->rsa.acex) == false) // sign using rsa key return accessdesc_SignWithKey(exhdrset); - else{ // No way the access desc signature can be 'obtained' - fprintf(stderr,"[ACEXDESC ERROR] Current keyset cannot sign AccessDesc, please appropriately set-up RSF, or specify a preset with \"-desc\"\n"); - return CANNOT_SIGN_ACCESSDESC; - } + + return 1; } int accessdesc_SignWithKey(exheader_settings *exhdrset) { /* Set RSA Keys */ - memcpy(exhdrset->keys->rsa.cxiHdrPvt,exhdrset->keys->rsa.cciCfaPvt,0x100); - memcpy(exhdrset->keys->rsa.cxiHdrPub,exhdrset->keys->rsa.cciCfaPub,0x100); - memcpy(&exhdrset->acexDesc->ncchRsaPubKey,exhdrset->keys->rsa.cxiHdrPub,0x100); + memcpy(exhdrset->keys->rsa.cxi.pvt, exhdrset->keys->rsa.cciCfa.pvt, 0x100); + memcpy(exhdrset->keys->rsa.cxi.pub, exhdrset->keys->rsa.cciCfa.pub, 0x100); + memcpy(&exhdrset->acexDesc->ncchRsaPubKey, exhdrset->keys->rsa.cxi.pub, 0x100); /* Copy Data From ExHeader */ - memcpy(&exhdrset->acexDesc->arm11SystemLocalCapabilities,&exhdrset->exHdr->arm11SystemLocalCapabilities,sizeof(exhdr_ARM11SystemLocalCapabilities)); - memcpy(&exhdrset->acexDesc->arm11KernelCapabilities,&exhdrset->exHdr->arm11KernelCapabilities,sizeof(exhdr_ARM11KernelCapabilities)); - memcpy(&exhdrset->acexDesc->arm9AccessControlInfo,&exhdrset->exHdr->arm9AccessControlInfo,sizeof(exhdr_ARM9AccessControlInfo)); - + memcpy(&exhdrset->acexDesc->arm11SystemLocalCapabilities, &exhdrset->exHdr->arm11SystemLocalCapabilities, sizeof(exhdr_ARM11SystemLocalCapabilities)); + memcpy(&exhdrset->acexDesc->arm11KernelCapabilities, &exhdrset->exHdr->arm11KernelCapabilities, sizeof(exhdr_ARM11KernelCapabilities)); + memcpy(&exhdrset->acexDesc->arm9AccessControlInfo, &exhdrset->exHdr->arm9AccessControlInfo, sizeof(exhdr_ARM9AccessControlInfo)); + /* Adjust Data */ exhdr_ARM11SystemLocalCapabilities *arm11 = &exhdrset->acexDesc->arm11SystemLocalCapabilities; @@ -50,7 +48,13 @@ int accessdesc_SignWithKey(exheader_settings *exhdrset) arm11->threadPriority /= 2; /* Sign AccessDesc */ - return SignAccessDesc(exhdrset->acexDesc,exhdrset->keys); + if (SignAccessDesc(exhdrset->acexDesc, exhdrset->keys) != 0) + { + printf("[ACEXDESC WARNING] Failed to sign access descriptor\n"); + memset(exhdrset->acexDesc->signature, 0xFF, 0x100); + } + + return 0; } int accessdesc_GetSignFromRsf(exheader_settings *exhdrset) @@ -100,10 +104,10 @@ int accessdesc_GetSignFromRsf(exheader_settings *exhdrset) /* Set RSA Keys */ int result = 0; // NCCH Header pubk - result = b64_decode(exhdrset->keys->rsa.cxiHdrPub,exhdrset->rsf->CommonHeaderKey.Modulus,0x100); + result = b64_decode(exhdrset->keys->rsa.cxi.pub,exhdrset->rsf->CommonHeaderKey.Modulus,0x100); if(result) return result; // NCCH Header privk - result = b64_decode(exhdrset->keys->rsa.cxiHdrPvt,exhdrset->rsf->CommonHeaderKey.D,0x100); + result = b64_decode(exhdrset->keys->rsa.cxi.pvt,exhdrset->rsf->CommonHeaderKey.D,0x100); if(result) return result; /* Set AccessDesc */ @@ -111,7 +115,7 @@ int accessdesc_GetSignFromRsf(exheader_settings *exhdrset) result = b64_decode(exhdrset->acexDesc->signature,exhdrset->rsf->CommonHeaderKey.AccCtlDescSign,0x100); if(result) return result; // NCCH Header pubk - memcpy(exhdrset->acexDesc->ncchRsaPubKey,exhdrset->keys->rsa.cxiHdrPub,0x100); + memcpy(exhdrset->acexDesc->ncchRsaPubKey,exhdrset->keys->rsa.cxi.pub,0x100); // Access Control result = b64_decode((u8*)&exhdrset->acexDesc->arm11SystemLocalCapabilities,exhdrset->rsf->CommonHeaderKey.AccCtlDescBin,0x200); if(result) return result; @@ -127,16 +131,11 @@ int accessdesc_GetSignFromPreset(exheader_settings *exhdrset) // Error Checking - if(!desc || !dependency_list){ - fprintf(stderr,"[ACEXDESC ERROR] AccessDesc template is unavailable, please configure RSF file\n"); + if (!desc || !dependency_list) { + fprintf(stderr, "[ACEXDESC ERROR] AccessDesc template is unavailable, please configure RSF file\n"); return CANNOT_SIGN_ACCESSDESC; } - if(!pre_sign && exhdrset->keys->rsa.requiresPresignedDesc){ - fprintf(stderr,"[ACEXDESC ERROR] This AccessDesc template needs to be signed, the current keyset is incapable of doing so. Please configure RSF file with the appropriate signature data.\n"); - return CANNOT_SIGN_ACCESSDESC; - } - // Setting data in Exheader // Dependency List memcpy(exhdrset->exHdr->dependencyList, dependency_list->dependency, 0x180); @@ -148,7 +147,7 @@ int accessdesc_GetSignFromPreset(exheader_settings *exhdrset) memcpy(ProgramID, exhdrset->exHdr->arm11SystemLocalCapabilities.programId, 8); memcpy(&StorageInfoBackup, &exhdrset->exHdr->arm11SystemLocalCapabilities.storageInfo, sizeof(exhdr_StorageInfo)); memcpy(&Arm9Desc, &exhdrset->exHdr->arm9AccessControlInfo, sizeof(exhdr_ARM9AccessControlInfo)); - + // Setting Preset Data memcpy(&exhdrset->exHdr->arm11SystemLocalCapabilities, desc->exheader_desc, 0x200); @@ -158,17 +157,22 @@ int accessdesc_GetSignFromPreset(exheader_settings *exhdrset) memcpy(&exhdrset->exHdr->arm9AccessControlInfo, &Arm9Desc, sizeof(exhdr_ARM9AccessControlInfo)); - // Setting AccessDesc Area - // Signing normally if possible - if(!exhdrset->keys->rsa.requiresPresignedDesc) + // Setting AccessDesc Area + // If presign available set static data & ncch hdr sig info + if (pre_sign) + { + memcpy(exhdrset->keys->rsa.cxi.pub, pre_sign->modulus, 0x100); + memcpy(exhdrset->keys->rsa.cxi.pvt, pre_sign->priv_exponent, 0x100); + memcpy(&exhdrset->acexDesc->signature, pre_sign->access_desc_signature, 0x100); + memcpy(&exhdrset->acexDesc->ncchRsaPubKey, pre_sign->modulus, 0x100); + memcpy(&exhdrset->acexDesc->arm11SystemLocalCapabilities, desc->signed_desc, 0x200); + } + // otherwise sign properly + else + { return accessdesc_SignWithKey(exhdrset); - - // Otherwise set static data & ncch hdr sig info - memcpy(exhdrset->keys->rsa.cxiHdrPub, pre_sign->modulus, 0x100); - memcpy(exhdrset->keys->rsa.cxiHdrPvt, pre_sign->priv_exponent, 0x100); - memcpy(&exhdrset->acexDesc->signature, pre_sign->access_desc_signature, 0x100); - memcpy(&exhdrset->acexDesc->ncchRsaPubKey, pre_sign->modulus, 0x100); - memcpy(&exhdrset->acexDesc->arm11SystemLocalCapabilities, desc->signed_desc, 0x200); + } + return 0; } diff --git a/makerom/exheader.c b/makerom/exheader.c index dbb183ec..f84f0981 100644 --- a/makerom/exheader.c +++ b/makerom/exheader.c @@ -58,14 +58,14 @@ int SignAccessDesc(access_descriptor *acexDesc, keys_struct *keys) { u8 *data = (u8*) &acexDesc->ncchRsaPubKey; u8 *sign = (u8*) &acexDesc->signature; - return RsaSignVerify(data,0x300,sign,keys->rsa.acexPub,keys->rsa.acexPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + return RsaSignVerify(data, 0x300, sign, keys->rsa.acex.pub, keys->rsa.acex.pvt, RSA_2048_SHA256, CTR_RSA_SIGN); } int CheckAccessDescSignature(access_descriptor *acexDesc, keys_struct *keys) { u8 *data = (u8*) &acexDesc->ncchRsaPubKey; u8 *sign = (u8*) &acexDesc->signature; - return RsaSignVerify(data,0x300,sign,keys->rsa.acexPub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); + return RsaSignVerify(data,0x300,sign,keys->rsa.acex.pub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); } /* ExHeader Build Functions */ diff --git a/makerom/keyset.c b/makerom/keyset.c index b8c26716..dbdec4d3 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -15,12 +15,6 @@ int SetNcchKeyX(keys_struct *keys, const u8 *keyX, u8 index); void keysetOpenError(char *file); FILE* keyset_OpenFile(char *dir, char *name, bool FileRequired); -int SetTIK_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus); -int SetTMD_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus); -int Set_CCI_CFA_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus); -int SetAccessDesc_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus); -int SetCXI_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus); - int SetCaCert(keys_struct *keys, const u8 *cert); int SetTikCert(keys_struct *keys, const u8 *cert); int SetTmdCert(keys_struct *keys, const u8 *cert); @@ -28,17 +22,21 @@ int SetTmdCert(keys_struct *keys, const u8 *cert); int LoadKeysFromResources(keys_struct *keys); void SetDummyRsaData(keys_struct *keys); int LoadKeysFromKeyfile(keys_struct *keys); -void CheckAccessDescKey(keys_struct *keys); void DumpKeyset(keys_struct *keys); + + // Code void InitKeys(keys_struct *keys) { memset(keys,0,sizeof(keys_struct)); InitCommonKeySlots(keys); InitNcchKeyXSlots(keys); - keys->rsa.cxiHdrPub = malloc(RSA_2048_KEY_SIZE); - keys->rsa.cxiHdrPvt = malloc(RSA_2048_KEY_SIZE); + Rsa2048Key_Alloc(&keys->rsa.xs); + Rsa2048Key_Alloc(&keys->rsa.cp); + Rsa2048Key_Alloc(&keys->rsa.cciCfa); + Rsa2048Key_Alloc(&keys->rsa.acex); + Rsa2048Key_Alloc(&keys->rsa.cxi); keys->aes.ncchKey0 = malloc(AES_128_KEY_SIZE); keys->aes.ncchKey1 = malloc(AES_128_KEY_SIZE); } @@ -50,23 +48,21 @@ void PrintBadKeySize(char *path, u32 size) int SetKeys(keys_struct *keys) { - int result = 0; - result = LoadKeysFromResources(keys); - if(result) return KEYSET_ERROR; + if (LoadKeysFromResources(keys) != 0) + { + return KEYSET_ERROR; + } - if(!keys->keysetLoaded){ - result = LoadKeysFromKeyfile(keys); - if(result) return KEYSET_ERROR; + if (!keys->keysetLoaded) + { + return KEYSET_ERROR; } - - if(keys->rsa.isFalseSign) - SetDummyRsaData(keys); - CheckAccessDescKey(keys); - - if(keys->dumpkeys) + if (keys->dumpkeys) + { DumpKeyset(keys); - + } + return 0; } @@ -84,8 +80,10 @@ int LoadKeysFromResources(keys_struct *keys) SetNormalKey(keys,zeros_aesKey); SetSystemFixedKey(keys,zeros_aesKey); - /* RSA Keys */ - keys->rsa.isFalseSign = true; + /* Certs */ + SetCaCert(keys, ca3_tpki_cert); + SetTikCert(keys, xsC_tpki_cert); + SetTmdCert(keys, cpB_tpki_cert); } else if(keys->keyset == pki_DEVELOPMENT){ keys->keysetLoaded = true; @@ -105,15 +103,14 @@ int LoadKeysFromResources(keys_struct *keys) for(int i = 0; i < 4; i++) SetNcchKeyX(keys, dev_unfixed_ncch_keyX[i],i); - /* RSA Keys */ // CIA - SetTIK_RsaKey(keys, xs9_dpki_rsa.priv_exponent, xs9_dpki_rsa.modulus); - SetTMD_RsaKey(keys, cpA_dpki_rsa.priv_exponent, cpA_dpki_rsa.modulus); + Rsa2048Key_Set(&keys->rsa.xs, xs9_dpki_rsa.priv_exponent, xs9_dpki_rsa.modulus); + Rsa2048Key_Set(&keys->rsa.cp, cpA_dpki_rsa.priv_exponent, cpA_dpki_rsa.modulus); // CCI/CFA - Set_CCI_CFA_RsaKey(keys, dev_ncsd_cfa_rsa.priv_exponent, dev_ncsd_cfa_rsa.modulus); + Rsa2048Key_Set(&keys->rsa.cciCfa, dev_ncsd_cfa_rsa.priv_exponent, dev_ncsd_cfa_rsa.modulus); // CXI - SetAccessDesc_RsaKey(keys, dev_accessdesc_rsa.priv_exponent, dev_accessdesc_rsa.modulus); + Rsa2048Key_Set(&keys->rsa.acex, dev_accessdesc_rsa.priv_exponent, dev_accessdesc_rsa.modulus); /* Certs */ SetCaCert(keys, ca4_dpki_cert); @@ -140,12 +137,12 @@ int LoadKeysFromResources(keys_struct *keys) /* RSA Keys */ // CIA - SetTIK_RsaKey(keys, xsC_ppki_rsa.priv_exponent, xsC_ppki_rsa.modulus); - SetTMD_RsaKey(keys, cpB_ppki_rsa.priv_exponent, cpB_ppki_rsa.modulus); + Rsa2048Key_Set(&keys->rsa.xs, xsC_ppki_rsa.priv_exponent, xsC_ppki_rsa.modulus); + Rsa2048Key_Set(&keys->rsa.cp, cpB_ppki_rsa.priv_exponent, cpB_ppki_rsa.modulus); // CCI/CFA - Set_CCI_CFA_RsaKey(keys, prod_ncsd_cfa_rsa.priv_exponent, prod_ncsd_cfa_rsa.modulus); + Rsa2048Key_Set(&keys->rsa.cciCfa, prod_ncsd_cfa_rsa.priv_exponent, prod_ncsd_cfa_rsa.modulus); // CXI - SetAccessDesc_RsaKey(keys, prod_accessdesc_rsa.priv_exponent, prod_accessdesc_rsa.modulus); + Rsa2048Key_Set(&keys->rsa.acex, prod_accessdesc_rsa.priv_exponent, prod_accessdesc_rsa.modulus); /* Certs */ SetCaCert(keys, ca3_ppki_cert); @@ -155,20 +152,22 @@ int LoadKeysFromResources(keys_struct *keys) return 0; } +/* void SetDummyRsaData(keys_struct *keys) { - if(!keys->rsa.xsPvt || !keys->rsa.xsPub) - SetTIK_RsaKey(keys, tpki_rsa.priv_exponent, tpki_rsa.modulus); - if(!keys->rsa.cpPvt || !keys->rsa.cpPub) - SetTMD_RsaKey(keys, tpki_rsa.priv_exponent, tpki_rsa.modulus); - - if(!keys->rsa.cciCfaPvt || !keys->rsa.cciCfaPub) - Set_CCI_CFA_RsaKey(keys, tpki_rsa.priv_exponent, tpki_rsa.modulus); - - if(!keys->rsa.acexPvt || !keys->rsa.acexPub) - SetAccessDesc_RsaKey(keys, tpki_rsa.priv_exponent, tpki_rsa.modulus); + // CIA + if (Rsa2048Key_CanSign(&keys->rsa.xs) == false) + Rsa2048Key_Set(&keys->rsa.xs, tpki_rsa.priv_exponent, tpki_rsa.modulus); + if (Rsa2048Key_CanSign(&keys->rsa.cp) == false) + Rsa2048Key_Set(&keys->rsa.cp, tpki_rsa.priv_exponent, tpki_rsa.modulus); + // CCI/CFA + if (Rsa2048Key_CanSign(&keys->rsa.cciCfa) == false) + Rsa2048Key_Set(&keys->rsa.cciCfa, tpki_rsa.priv_exponent, tpki_rsa.modulus); + // CXI + if (Rsa2048Key_CanSign(&keys->rsa.acex) == false) + Rsa2048Key_Set(&keys->rsa.acex, tpki_rsa.priv_exponent, tpki_rsa.modulus); - /* Certs */ + // Certs if(!keys->certs.caCert) SetCaCert(keys, ca3_tpki_cert); if(!keys->certs.xsCert) @@ -176,24 +175,7 @@ void SetDummyRsaData(keys_struct *keys) if(!keys->certs.cpCert) SetTmdCert(keys, cpB_tpki_cert); } - -int LoadKeysFromKeyfile(keys_struct *keys) -{ - printf("[KEYSET ERROR] Custom keys not supported\n"); - return -1; -} - -void CheckAccessDescKey(keys_struct *keys) -{ - // Checking if AccessDesc can be signed - u8 *tmp = calloc(1,RSA_2048_KEY_SIZE); - if(memcmp(tmp,keys->rsa.acexPvt,RSA_2048_KEY_SIZE) == 0) - keys->rsa.requiresPresignedDesc = true; - else - keys->rsa.requiresPresignedDesc = false; - - free(tmp); -} +*/ void DumpKeyset(keys_struct *keys) { @@ -245,17 +227,17 @@ void DumpKeyset(keys_struct *keys) } printf(" > TIK RSA Keys\n"); - memdump(stdout," [PUB] ",keys->rsa.xsPub,0x100); - memdump(stdout," [PVT] ",keys->rsa.xsPvt,0x100); + memdump(stdout," [PUB] ",keys->rsa.xs.pub,0x100); + memdump(stdout," [PVT] ",keys->rsa.xs.pvt,0x100); printf(" > TMD RSA Keys\n"); - memdump(stdout," [PUB] ",keys->rsa.cpPub,0x100); - memdump(stdout," [PVT] ",keys->rsa.cpPvt,0x100); + memdump(stdout," [PUB] ",keys->rsa.cp.pub,0x100); + memdump(stdout," [PVT] ",keys->rsa.cp.pvt,0x100); printf(" > AcexDesc RSA Keys\n"); - memdump(stdout," [PUB] ",keys->rsa.acexPub,0x100); - memdump(stdout," [PVT] ",keys->rsa.acexPvt,0x100); + memdump(stdout," [PUB] ",keys->rsa.acex.pub,0x100); + memdump(stdout," [PVT] ",keys->rsa.acex.pvt,0x100); printf(" > NcsdCfa RSA Keys\n"); - memdump(stdout," [PUB] ",keys->rsa.cciCfaPub,0x100); - memdump(stdout," [PVT] ",keys->rsa.cciCfaPvt,0x100); + memdump(stdout," [PUB] ",keys->rsa.cciCfa.pub,0x100); + memdump(stdout," [PVT] ",keys->rsa.cciCfa.pvt,0x100); } void keysetOpenError(char *file) @@ -301,18 +283,11 @@ void FreeKeys(keys_struct *keys) free(keys->aes.ncchKey1); // RSA - free(keys->rsa.xsPvt); - free(keys->rsa.xsPub); - free(keys->rsa.cpPvt); - free(keys->rsa.cpPub); - - free(keys->rsa.cciCfaPvt); - free(keys->rsa.cciCfaPub); - - free(keys->rsa.acexPvt); - free(keys->rsa.acexPub); - free(keys->rsa.cxiHdrPub); - free(keys->rsa.cxiHdrPvt); + Rsa2048Key_Free(&keys->rsa.xs); + Rsa2048Key_Free(&keys->rsa.cp); + Rsa2048Key_Free(&keys->rsa.cciCfa); + Rsa2048Key_Free(&keys->rsa.acex); + Rsa2048Key_Free(&keys->rsa.cxi); // Certs free(keys->certs.caCert); @@ -378,43 +353,43 @@ int SetSystemFixedKey(keys_struct *keys, const u8 *key) return CopyData(&keys->aes.systemFixedKey,key,16); } -int SetTIK_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus) +int SetCaCert(keys_struct *keys, const u8 *cert) { if(!keys) return -1; - return SetRsaKeySet(&keys->rsa.xsPvt,priv_exp,&keys->rsa.xsPub,modulus); + return CopyData(&keys->certs.caCert,cert,0x400); } - -int SetTMD_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus) +int SetTikCert(keys_struct *keys, const u8 *cert) { if(!keys) return -1; - return SetRsaKeySet(&keys->rsa.cpPvt,priv_exp,&keys->rsa.cpPub,modulus); + return CopyData(&keys->certs.xsCert,cert,0x300); } -int Set_CCI_CFA_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus) +int SetTmdCert(keys_struct *keys, const u8 *cert) { if(!keys) return -1; - return SetRsaKeySet(&keys->rsa.cciCfaPvt,priv_exp,&keys->rsa.cciCfaPub,modulus); + return CopyData(&keys->certs.cpCert,cert,0x400); } -int SetAccessDesc_RsaKey(keys_struct *keys, const u8 *priv_exp, const u8 *modulus) +void Rsa2048Key_Alloc(rsa2048_key* key) { - if(!keys) return -1; - return SetRsaKeySet(&keys->rsa.acexPvt,priv_exp,&keys->rsa.acexPub,modulus); + key->pub = malloc(RSA_2048_KEY_SIZE); + key->pvt = malloc(RSA_2048_KEY_SIZE); } -int SetCaCert(keys_struct *keys, const u8 *cert) +void Rsa2048Key_Free(rsa2048_key* key) { - if(!keys) return -1; - return CopyData(&keys->certs.caCert,cert,0x400); + free(key->pub); + free(key->pvt); } -int SetTikCert(keys_struct *keys, const u8 *cert) + +void Rsa2048Key_Set(rsa2048_key* key, const u8* pvt, const u8* pub) { - if(!keys) return -1; - return CopyData(&keys->certs.xsCert,cert,0x300); + memcpy(key->pub, pub, RSA_2048_KEY_SIZE); + memcpy(key->pvt, pvt, RSA_2048_KEY_SIZE); } -int SetTmdCert(keys_struct *keys, const u8 *cert) +bool Rsa2048Key_CanSign(const rsa2048_key* key) { - if(!keys) return -1; - return CopyData(&keys->certs.cpCert,cert,0x400); + static const u8 rsa2048[RSA_2048_KEY_SIZE] = { 0 }; + return memcmp(key->pub, rsa2048, RSA_2048_KEY_SIZE) != 0 || memcmp(key->pvt, rsa2048, RSA_2048_KEY_SIZE) != 0; } \ No newline at end of file diff --git a/makerom/keyset.h b/makerom/keyset.h index 4ab18ff9..ba3f8748 100644 --- a/makerom/keyset.h +++ b/makerom/keyset.h @@ -31,11 +31,18 @@ typedef enum // Structs +typedef struct +{ + u8 *pub; + u8 *pvt; +} rsa2048_key; + typedef struct { pki_keyset keyset; bool keysetLoaded; bool dumpkeys; + bool ignore_sign; struct { @@ -60,23 +67,16 @@ typedef struct struct { - bool isFalseSign; // CIA RSA - u8 *cpPvt; - u8 *cpPub; - u8 *xsPvt; - u8 *xsPub; + rsa2048_key cp; + rsa2048_key xs; // CCI/CFA - u8 *cciCfaPvt; - u8 *cciCfaPub; - + rsa2048_key cciCfa; + // CXI - bool requiresPresignedDesc; - u8 *acexPvt; - u8 *acexPub; - u8 *cxiHdrPub; - u8 *cxiHdrPvt; + rsa2048_key acex; + rsa2048_key cxi; } rsa; struct @@ -96,4 +96,9 @@ void FreeKeys(keys_struct *keys); int SetCommonKey(keys_struct *keys, const u8 *key, u8 Index); int SetCurrentCommonKey(keys_struct *keys, u8 Index); int SetNormalKey(keys_struct *keys, const u8 *key); -int SetSystemFixedKey(keys_struct *keys, const u8 *key); \ No newline at end of file +int SetSystemFixedKey(keys_struct *keys, const u8 *key); + +void Rsa2048Key_Alloc(rsa2048_key* key); +void Rsa2048Key_Free(rsa2048_key* key); +void Rsa2048Key_Set(rsa2048_key* key, const u8* pvt, const u8* pub); +bool Rsa2048Key_CanSign(const rsa2048_key* key); \ No newline at end of file diff --git a/makerom/ncch.c b/makerom/ncch.c index b400ec86..43646d60 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -36,17 +36,27 @@ bool IsValidProductCode(char *ProductCode, bool FreeProductCode); // Code int SignCFA(ncch_hdr *hdr, keys_struct *keys) { - return RsaSignVerify(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cciCfaPub,keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + if (RsaSignVerify(GetNcchHdrData(hdr), GetNcchHdrDataLen(hdr), GetNcchHdrSig(hdr), keys->rsa.cciCfa.pub, keys->rsa.cciCfa.pvt, RSA_2048_SHA256, CTR_RSA_SIGN) != 0) + { + printf("[NCCH WARNING] Failed to sign CFA header\n"); + memset(GetNcchHdrSig(hdr), 0xFF, 0x100); + } + return 0; } int CheckCFASignature(ncch_hdr *hdr, keys_struct *keys) { - return RsaSignVerify(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cciCfaPub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); + return RsaSignVerify(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cciCfa.pub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); } int SignCXI(ncch_hdr *hdr, keys_struct *keys) { - return RsaSignVerify(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cxiHdrPub,keys->rsa.cxiHdrPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + if (RsaSignVerify(GetNcchHdrData(hdr), GetNcchHdrDataLen(hdr), GetNcchHdrSig(hdr), keys->rsa.cxi.pub, keys->rsa.cxi.pvt, RSA_2048_SHA256, CTR_RSA_SIGN) != 0) + { + printf("[NCCH WARNING] Failed to sign CXI header\n"); + memset(GetNcchHdrSig(hdr), 0xFF, 0x100); + } + return 0; } int CheckCXISignature(ncch_hdr *hdr, u8 *pubk) @@ -748,11 +758,14 @@ int VerifyNcch(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) } if(IsCfa(hdr)){ - if(CheckCFASignature(hdr,keys) != Good && !keys->rsa.isFalseSign){ + if(CheckCFASignature(hdr,keys) != Good){ if(!SuppressOutput) - fprintf(stderr,"[NCCH ERROR] CFA Sigcheck Failed\n"); - free(ncchInfo); - return NCCH_HDR_SIG_BAD; + fprintf(keys->ignore_sign ? stdout :stderr,"[NCCH %s] CFA Sigcheck Failed\n", keys->ignore_sign ? "WARNING" : "ERROR"); + if (!keys->ignore_sign) + { + free(ncchInfo); + return NCCH_HDR_SIG_BAD; + } } if(!ncchInfo->romfsSize){ if(!SuppressOutput) @@ -813,19 +826,29 @@ int VerifyNcch(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) if(IsNcchEncrypted(hdr)) CryptNcchRegion((u8*)acexDesc,ncchInfo->acexSize,ncchInfo->exhdrSize,ncchInfo->titleId,keys->aes.ncchKey0,ncch_exhdr); - if(CheckAccessDescSignature(acexDesc,keys) != 0 && !keys->rsa.isFalseSign){ - if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] AccessDesc Sigcheck Failed\n"); - free(ncchInfo); - free(acexDesc); - return ACCESSDESC_SIG_BAD; + + if(CheckAccessDescSignature(acexDesc,keys) != Good){ + if (!SuppressOutput) + fprintf(keys->false_sign? stdout : stderr, "[NCCH %s] AccessDesc Sigcheck Failed\n", keys->ignore_sign ? "WARNING" : "ERROR"); + if (!keys->ignore_sign) + { + free(ncchInfo); + free(acexDesc); + return ACCESSDESC_SIG_BAD; + } } - if(CheckCXISignature(hdr,GetAcexNcchPubKey(acexDesc)) != 0 && !keys->rsa.isFalseSign){ - if(!SuppressOutput) fprintf(stderr,"[NCCH ERROR] CXI Header Sigcheck Failed\n"); - free(ncchInfo); - free(acexDesc); - return NCCH_HDR_SIG_BAD; + if(CheckCXISignature(hdr,GetAcexNcchPubKey(acexDesc)) != Good){ + if (!SuppressOutput) + fprintf(keys->ignore_sign ? stdout : stderr,"[NCCH %s] CXI Header Sigcheck Failed\n", keys->ignore_sign ? "WARNING" : "ERROR"); + if (!keys->ignore_sign) + { + free(ncchInfo); + free(acexDesc); + return NCCH_HDR_SIG_BAD; + } } + } if(!CheckHash) diff --git a/makerom/ncsd.c b/makerom/ncsd.c index ba2a23bb..51e4b9ce 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -579,7 +579,12 @@ int GenCciHdr(cci_settings *set) // Sign Header - RsaSignVerify(&hdr->magic,sizeof(cci_hdr)-RSA_2048_KEY_SIZE,hdr->signature,set->keys->rsa.cciCfaPub,set->keys->rsa.cciCfaPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + if (RsaSignVerify(&hdr->magic, sizeof(cci_hdr) - RSA_2048_KEY_SIZE, hdr->signature, set->keys->rsa.cciCfa.pub, set->keys->rsa.cciCfa.pvt, RSA_2048_SHA256, CTR_RSA_SIGN) != 0) + { + printf("[NCSD WARNING] Failed to sign header\n"); + memset(hdr->signature, 0xFF, 0x100); + } + return 0; } diff --git a/makerom/tik.c b/makerom/tik.c index 9267baa9..fb219ae5 100644 --- a/makerom/tik.c +++ b/makerom/tik.c @@ -81,7 +81,13 @@ int SignTicketHeader(buffer_struct *tik, keys_struct *keys) clrmem(sig,sizeof(tik_signature)); u32_to_u8(sig->sigType,RSA_2048_SHA256,BE); - return RsaSignVerify(data,len,sig->data,keys->rsa.xsPub,keys->rsa.xsPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + if (RsaSignVerify(data, len, sig->data, keys->rsa.xs.pub, keys->rsa.xs.pvt, RSA_2048_SHA256, CTR_RSA_SIGN) != 0) + { + printf("[TIK WARNING] Failed to sign header\n"); + memset(sig->data, 0xFF, 0x100); + } + + return 0; } int CryptTitleKey(u8 *input, u8 *output, u8 *titleId, keys_struct *keys, u8 mode) diff --git a/makerom/tmd.c b/makerom/tmd.c index 9407ef60..c7c54492 100644 --- a/makerom/tmd.c +++ b/makerom/tmd.c @@ -71,7 +71,13 @@ int SignTMDHeader(tmd_hdr *hdr, tmd_signature *sig, keys_struct *keys) clrmem(sig,sizeof(tmd_signature)); u32_to_u8(sig->sigType,RSA_2048_SHA256,BE); - return RsaSignVerify((u8*)hdr,sizeof(tmd_hdr),sig->data,keys->rsa.cpPub,keys->rsa.cpPvt,RSA_2048_SHA256,CTR_RSA_SIGN); + if (RsaSignVerify((u8*)hdr, sizeof(tmd_hdr), sig->data, keys->rsa.cp.pub, keys->rsa.cp.pvt, RSA_2048_SHA256, CTR_RSA_SIGN) != 0) + { + printf("[TMD WARNING] Failed to sign header\n"); + memset(sig->data, 0xFF, 0x100); + } + + return 0; } int SetupTMDInfoRecord(tmd_content_info_record *info_record, u8 *content_record, u16 ContentCount) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 5089591d..f995857a 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -85,6 +85,7 @@ void SetDefaults(user_settings *set) // Target Info set->common.keys.keyset = pki_TEST; set->common.keys.accessDescSign.presetType = desc_NotSpecified; + set->common.keys.ignore_sign = false; // Build NCCH Info set->ncch.useSecCrypto = true; @@ -195,6 +196,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } return 2; } + /* else if (strcmp(argv[i], "-ckeyid") == 0) { if (ParamNum != 1) { PrintArgReqParam(argv[i], 1); @@ -222,6 +224,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } return 2; } + */ else if (strcmp(argv[i], "-showkeys") == 0) { if (ParamNum) { PrintNoNeedParam(argv[i]); @@ -230,12 +233,12 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) set->common.keys.dumpkeys = true; return 1; } - else if (strcmp(argv[i], "-fsign") == 0) { + else if (strcmp(argv[i], "-ignoresign") == 0) { if (ParamNum) { PrintNoNeedParam(argv[i]); return USR_BAD_ARG; } - set->common.keys.rsa.isFalseSign = true; + set->common.keys.ignore_sign = true; return 1; } @@ -949,10 +952,11 @@ void DisplayExtendedHelp(char *app_name) printf(" 't' Test(false) Keys & prod Certs\n"); printf(" 'd' Development Keys & Certs\n"); printf(" 'p' Production Keys & Certs\n"); - printf(" -ckeyid Override the automatic common key selection\n"); - printf(" -ncchseckey Ncch keyX index ('0'=1.0+, '1'=7.0+)\n"); + //printf(" -ckeyid Override the automatic common key selection\n"); + //printf(" -ncchseckey Ncch keyX index ('0'=1.0+, '1'=7.0+)\n"); printf(" -showkeys Display the loaded key chain\n"); - printf(" -fsign Ignore invalid signatures\n"); + //printf(" -fsign False sign digital signatures\n"); + printf(" -ignoresign Ignore invalid signatures\n"); printf("NCCH OPTIONS:\n"); printf(" -elf ELF file\n"); printf(" -icon Icon file\n"); From 7e4c0d7a608663d26570b76aaff6739e57f4e921 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 28 May 2017 11:39:14 +0800 Subject: [PATCH 196/317] [ctrtool] Replace specifying a specific seed, with a seeddb. --- ctrtool/keyset.cpp | 33 ++++++++++++++++++++++++++++++--- ctrtool/keyset.h | 20 ++++++++++++++++++-- ctrtool/main.c | 6 +++--- ctrtool/ncch.c | 6 +++--- ctrtool/settings.c | 13 ++++++++++--- ctrtool/settings.h | 2 +- 6 files changed, 65 insertions(+), 15 deletions(-) diff --git a/ctrtool/keyset.cpp b/ctrtool/keyset.cpp index 0341cbe5..609b7d70 100644 --- a/ctrtool/keyset.cpp +++ b/ctrtool/keyset.cpp @@ -232,7 +232,12 @@ void keyset_merge(keyset* keys, keyset* src) COPY_IF_VALID(ncchkeyX_seven); COPY_IF_VALID(ncchkeyX_ninethree); COPY_IF_VALID(ncchkeyX_ninesix); - COPY_IF_VALID(seed); + if (src->seed_num > 0) + { + keys->seed_num = src->seed_num; + keys->seed_db = (seeddb_entry*)calloc(src->seed_num, sizeof(seeddb_entry)); + memcpy(keys->seed_db, src->seed_db, src->seed_num * sizeof(seeddb_entry)); + } #undef COPY_IF_VALID } @@ -283,9 +288,31 @@ void keyset_parse_ncchkeyX_ninesix(keyset* keys, char* keytext, int keylen) keyset_parse_key128(&keys->ncchkeyX_ninesix, keytext, keylen); } -void keyset_parse_seed(keyset* keys, char* keytext, int keylen) +void keyset_parse_seeddb(keyset* keys, char* path) { - keyset_parse_key128(&keys->seed, keytext, keylen); + //keyset_parse_key128(&keys->seed, keytext, keylen); + FILE* fp = fopen(path, "rb"); + if (fp == NULL) + { + printf("[ERROR] Failed to load SeedDB (failed to open file)\n"); + return; + } + + seeddb_header hdr; + fread(&hdr, sizeof(seeddb_header), 1, fp); + + u32 n_entries = getle32(hdr.n_entries); + for (u32 i = 0; i < 0xC; i++) + { + if (hdr.padding[i] != 0x00) + { + printf("[ERROR] SeedDB is corrupt. (padding malformed)\n"); + return; + } + } + + keys->seed_db = (seeddb_entry*)calloc(n_entries, sizeof(seeddb_entry)); + fread(keys->seed_db, n_entries * sizeof(seeddb_entry), 1, fp); } void keyset_dump_rsakey(rsakey2048* key, const char* keytitle) diff --git a/ctrtool/keyset.h b/ctrtool/keyset.h index 5f71d907..3d6610fb 100644 --- a/ctrtool/keyset.h +++ b/ctrtool/keyset.h @@ -21,6 +21,20 @@ typedef enum RSAKEY_PUB } rsakeytype; +typedef struct +{ + u8 title_id[8]; + u8 seed[0x10]; + u8 padding[0x8]; +} seeddb_entry; + +typedef struct +{ + u8 n_entries[4]; + u8 padding[0xC]; +} seeddb_header; + + typedef struct { unsigned char n[256]; @@ -42,8 +56,10 @@ typedef struct typedef struct { + u32 seed_num; + seeddb_entry* seed_db; + key128 titlekey; - key128 seed; key128 commonkeyX; key128 ncchfixedsystemkey; key128 ncchkeyX_old; @@ -66,7 +82,7 @@ void keyset_parse_ncchfixedsystemkey(keyset* keys, char* keytext, int keylen); void keyset_parse_ncchkeyX_seven(keyset* keys, char* keytext, int keylen); void keyset_parse_ncchkeyX_ninethree(keyset* keys, char* keytext, int keylen); void keyset_parse_ncchkeyX_ninesix(keyset* keys, char* keytext, int keylen); -void keyset_parse_seed(keyset* keys, char* keytext, int keylen); +void keyset_parse_seeddb(keyset* keys, char* path); void keyset_dump(keyset* keys); #ifdef __cplusplus diff --git a/ctrtool/main.c b/ctrtool/main.c index 393d272d..17113746 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -58,7 +58,7 @@ static void usage(const char *argv0) " --titlekey=key Set tik title key.\n" " --ncchkey=key Set ncch key.\n" " --ncchsyskey=key Set ncch fixed system key.\n" - " --seed=key Set seed for ncch seed crypto.\n" + " --seeddb=file Set seeddb for ncch seed crypto.\n" " --showkeys Show the keys being used.\n" " --showsyscalls Show system call names instead of numbers.\n" " -t, --intype=type Specify input file type [ncsd, ncch, exheader, cia, tmd, lzss,\n" @@ -156,7 +156,7 @@ int main(int argc, char* argv[]) {"ncchkeyxseven", 1, NULL, 25}, {"ncchkeyxninethree", 1, NULL, 26}, {"ncchkeyxninesix", 1, NULL, 27}, - {"seed", 1, NULL, 28}, + {"seeddb", 1, NULL, 28}, {NULL}, }; @@ -254,7 +254,7 @@ int main(int argc, char* argv[]) case 25: keyset_parse_ncchkeyX_seven(&tmpkeys, optarg, strlen(optarg)); break; case 26: keyset_parse_ncchkeyX_ninethree(&tmpkeys, optarg, strlen(optarg)); break; case 27: keyset_parse_ncchkeyX_ninesix(&tmpkeys, optarg, strlen(optarg)); break; - case 28: keyset_parse_seed(&tmpkeys, optarg, strlen(optarg)); break; + case 28: keyset_parse_seeddb(&tmpkeys, optarg); break; default: usage(argv[0]); diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 1c9f9ee8..a847ac20 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -549,11 +549,11 @@ void ncch_determine_key(ncch_context* ctx, u32 actions) if (header->flags[7] & 0x20) { ctx->encrypted = NCCHCRYPTO_SEED; - seed = settings_get_seed(ctx->usersettings); + seed = settings_get_seed(ctx->usersettings, getle64(header->partitionid)); if (!seed) { - fprintf(stderr, "This title uses seed crypto, but no seed is set, unable to decrypt.\n" - "Use -p to avoid decryption or use --seed=SEEDHERE to provide the seed.\n"); + fprintf(stderr, "This title uses seed crypto, but no seed is set in seedDB, unable to decrypt.\n" + "Use -p to avoid decryption or use --seeddb=dbfile to specify the seedDB.\n"); ctx->encrypted = NCCHCRYPTO_BROKEN; return; } diff --git a/ctrtool/settings.c b/ctrtool/settings.c index 95f7dbd3..15523b01 100644 --- a/ctrtool/settings.c +++ b/ctrtool/settings.c @@ -173,9 +173,16 @@ unsigned char* settings_get_common_keyX(settings* usersettings) GETKEY(usersettings, commonkeyX); } -unsigned char* settings_get_seed(settings* usersettings) -{ - GETKEY(usersettings, seed); +unsigned char* settings_get_seed(settings* usersettings, u64 title_id) +{ + for (u32 i = 0; i < usersettings->keys.seed_num; i++) + { + if (title_id == getle64(usersettings->keys.seed_db[i].title_id)) + { + return usersettings->keys.seed_db[i].seed; + } + } + return NULL; } unsigned char* settings_get_title_key(settings* usersettings) diff --git a/ctrtool/settings.h b/ctrtool/settings.h index c3f819f0..c90947b2 100644 --- a/ctrtool/settings.h +++ b/ctrtool/settings.h @@ -52,7 +52,7 @@ unsigned char* settings_get_ncchkeyX_seven(settings* usersettings); unsigned char* settings_get_ncchkeyX_ninethree(settings* usersettings); unsigned char* settings_get_ncchkeyX_ninesix(settings* usersettings); unsigned char* settings_get_common_keyX(settings* usersettings); -unsigned char* settings_get_seed(settings* usersettings); +unsigned char* settings_get_seed(settings* usersettings, u64 title_id); unsigned char* settings_get_title_key(settings* usersettings); int settings_get_ignore_programid(settings* usersettings); int settings_get_list_romfs_files(settings* usersettings); From b0896ecee25d8d08bbc633161fd151d7958c0b24 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 28 May 2017 11:57:14 +0800 Subject: [PATCH 197/317] [makerom] Typo --- makerom/ncch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/ncch.c b/makerom/ncch.c index 43646d60..af7996e6 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -829,7 +829,7 @@ int VerifyNcch(u8 *ncch, keys_struct *keys, bool CheckHash, bool SuppressOutput) if(CheckAccessDescSignature(acexDesc,keys) != Good){ if (!SuppressOutput) - fprintf(keys->false_sign? stdout : stderr, "[NCCH %s] AccessDesc Sigcheck Failed\n", keys->ignore_sign ? "WARNING" : "ERROR"); + fprintf(keys->ignore_sign ? stdout : stderr, "[NCCH %s] AccessDesc Sigcheck Failed\n", keys->ignore_sign ? "WARNING" : "ERROR"); if (!keys->ignore_sign) { free(ncchInfo); From d185b18f27945d52d868f9c28b610b56e3cb5222 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 28 May 2017 13:52:32 +0800 Subject: [PATCH 198/317] [ctrtool] fix bug with seeddb --- .gitignore | 1 + .vs/ProjectSettings.json | 3 +++ .vs/VSWorkspaceState.json | 9 +++++++++ ctrtool/ctrtool | Bin 0 -> 262759 bytes ctrtool/keyset.cpp | 6 +++--- ctrtool/seeddb.bin | Bin 0 -> 40752 bytes makerom/makerom | Bin 0 -> 438222 bytes 7 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 .vs/ProjectSettings.json create mode 100644 .vs/VSWorkspaceState.json create mode 100644 ctrtool/ctrtool create mode 100644 ctrtool/seeddb.bin create mode 100644 makerom/makerom diff --git a/.gitignore b/.gitignore index 39b344f0..7872f095 100644 --- a/.gitignore +++ b/.gitignore @@ -228,3 +228,4 @@ pip-log.txt #exec *.exe *.db +/.vs/slnx.sqlite diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json new file mode 100644 index 00000000..f8b48885 --- /dev/null +++ b/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": null +} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 00000000..acb3db8b --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,9 @@ +{ + "ExpandedNodes": [ + "", + "\\ctrtool", + "\\makerom" + ], + "SelectedNode": "\\makerom\\keyset.c", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/ctrtool/ctrtool b/ctrtool/ctrtool new file mode 100644 index 0000000000000000000000000000000000000000..441e0dd4d0cd351d8af7ceb575de8d8cbd1a70e8 GIT binary patch literal 262759 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

9vN;}2zW6O`ZKnZtH1ee-Xf3Sf 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

Oadf1c4vpErB>6}Aa`!C zGl9q&8(ZJ949B2{5N`3YEo4closZy$GB6qK5mbF6av?acE~|bs4lVjUl<8Gcws1vb zTX1Y9TjI_w(SAUz^D%H7q2Ma}2a+QFgxMhn4`{KpgobuDAOmM+=kLp; z`_YaDCx?Sibd$wTqXtTgTbJ3b7*vv zRtL}Z<2mrK;g*~dazAzs5No7-)<{n`Y6u=28wzrG7+^UuC!c_{;g!ekolOWJd}k1| z*>FO%p{}X5BevVp$LE$?9vt4gsSwW?q(bu=tpnt=>9_VIR$peKb>@QjPcz zzI9=^_$2pQBGlMK@^IimP60-nA&j9(5~`XWG{+NaO_HeVK#^EYZb=ELt^*#`|JbBk z-DAbfL#PKF6yh6=TYuCTj9kRsg=*xUO?TbMOgV2ai3>?F`A$gtpiiyvu;0v8Av);h zbUVIENYxTq@pEP1?R|H(o)@uoz)@{Te?2FA6u?!88js|=Jj)>{;KMt zOUwNE5C21M7*kQsV@0;8`sGjS$RzetJT`e~ef+fNU?YlD;^Ku! z?*YnQ%_X@sVl>%qn(Bh>KR~sEFhRZ@d!Xp}27w0@79^VjTMH&@7E=gYeYyBR39OMt z$<_!oyX8U`weSO*f!b*eLHf*e;r3g?ICz7Q^ZR&0W|nQc;ZH3MWbD*s2fpV6U&p8C z<&$T%T!;t3_+)AazHbTRR8fJ?mllWOQ5PG;0>p4a*^xL)hsvVqo;&xKXa#v^2E>vk z^-asq9_psXJe_>w22_?M$($=7z~I?#N9K*lh9|aJoqxlp+)!k^184>L8Jb#vbhYodk zE>5*W#^|jWsy?Shx23X>BmER#ixf7Z&?b4Rt(xkJ!VhRd-^n5?zf4+d6`!w3qKPJq zVRTO_CEAcohl}5=2p_I9e}EVej{paI%y6d>&$HI}Ih1MGmM#>}gjD4UL@-e~@*zFz zsQDaD*zji0-~M7&cosNI=LUNu`sc8ki|2IAkaYdL&gRNx^uB&dAYzP>hkH7vgQA%% zW~md#ZE=F(oyKYh*XgG(S(0BR5pwjg9d#(rhN)-vh>x1;_(oLbl0dOkOf b&~EVQv%rk}0~g`%`Y?l9sUMtBxBUMA`Wbe` literal 0 HcmV?d00001 diff --git a/ctrtool/keyset.cpp b/ctrtool/keyset.cpp index 609b7d70..dd793003 100644 --- a/ctrtool/keyset.cpp +++ b/ctrtool/keyset.cpp @@ -301,7 +301,7 @@ void keyset_parse_seeddb(keyset* keys, char* path) seeddb_header hdr; fread(&hdr, sizeof(seeddb_header), 1, fp); - u32 n_entries = getle32(hdr.n_entries); + keys->seed_num = getle32(hdr.n_entries); for (u32 i = 0; i < 0xC; i++) { if (hdr.padding[i] != 0x00) @@ -311,8 +311,8 @@ void keyset_parse_seeddb(keyset* keys, char* path) } } - keys->seed_db = (seeddb_entry*)calloc(n_entries, sizeof(seeddb_entry)); - fread(keys->seed_db, n_entries * sizeof(seeddb_entry), 1, fp); + keys->seed_db = (seeddb_entry*)calloc(keys->seed_num, sizeof(seeddb_entry)); + fread(keys->seed_db, keys->seed_num * sizeof(seeddb_entry), 1, fp); } void keyset_dump_rsakey(rsakey2048* key, const char* keytitle) diff --git a/ctrtool/seeddb.bin b/ctrtool/seeddb.bin new file mode 100644 index 0000000000000000000000000000000000000000..4bf2190e01856357ed6b8129aae8d7935314c302 GIT binary patch literal 40752 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 z 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�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

rY4|M-zD_}}v3J}db5FZ$62 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 literal 0 HcmV?d00001 From 5757ba20de28000981a419bfcf63b1f658b20528 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 28 May 2017 20:46:30 +0800 Subject: [PATCH 199/317] [makerom] Fix encryption for production target. --- makerom/accessdesc.c | 10 ++++------ makerom/keyset.c | 2 +- makerom/keyset.h | 1 - makerom/ncch.c | 16 ++++++++++------ makerom/ncsd.c | 8 ++++---- makerom/tik.c | 5 +++-- makerom/tmd.c | 5 +++-- 7 files changed, 25 insertions(+), 22 deletions(-) diff --git a/makerom/accessdesc.c b/makerom/accessdesc.c index 20c4e876..0e44788b 100644 --- a/makerom/accessdesc.c +++ b/makerom/accessdesc.c @@ -23,10 +23,7 @@ int set_AccessDesc(exheader_settings *exhdrset) return accessdesc_GetSignFromPreset(exhdrset); else if(exhdrset->rsf->CommonHeaderKey.Found == true) // Keydata exists in RSF return accessdesc_GetSignFromRsf(exhdrset); - else if (Rsa2048Key_CanSign(&exhdrset->keys->rsa.acex) == false) // sign using rsa key - return accessdesc_SignWithKey(exhdrset); - - return 1; + return accessdesc_SignWithKey(exhdrset); } int accessdesc_SignWithKey(exheader_settings *exhdrset) @@ -48,13 +45,14 @@ int accessdesc_SignWithKey(exheader_settings *exhdrset) arm11->threadPriority /= 2; /* Sign AccessDesc */ - if (SignAccessDesc(exhdrset->acexDesc, exhdrset->keys) != 0) + if (Rsa2048Key_CanSign(&exhdrset->keys->rsa.acex) == false) { printf("[ACEXDESC WARNING] Failed to sign access descriptor\n"); memset(exhdrset->acexDesc->signature, 0xFF, 0x100); + return 0; } - return 0; + return SignAccessDesc(exhdrset->acexDesc, exhdrset->keys); } int accessdesc_GetSignFromRsf(exheader_settings *exhdrset) diff --git a/makerom/keyset.c b/makerom/keyset.c index dbdec4d3..19887fb9 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -391,5 +391,5 @@ void Rsa2048Key_Set(rsa2048_key* key, const u8* pvt, const u8* pub) bool Rsa2048Key_CanSign(const rsa2048_key* key) { static const u8 rsa2048[RSA_2048_KEY_SIZE] = { 0 }; - return memcmp(key->pub, rsa2048, RSA_2048_KEY_SIZE) != 0 || memcmp(key->pvt, rsa2048, RSA_2048_KEY_SIZE) != 0; + return memcmp(key->pub, rsa2048, RSA_2048_KEY_SIZE) != 0 && memcmp(key->pvt, rsa2048, RSA_2048_KEY_SIZE) != 0; } \ No newline at end of file diff --git a/makerom/keyset.h b/makerom/keyset.h index ba3f8748..2a35b382 100644 --- a/makerom/keyset.h +++ b/makerom/keyset.h @@ -30,7 +30,6 @@ typedef enum } pki_keyset; // Structs - typedef struct { u8 *pub; diff --git a/makerom/ncch.c b/makerom/ncch.c index af7996e6..d054cacf 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -36,27 +36,31 @@ bool IsValidProductCode(char *ProductCode, bool FreeProductCode); // Code int SignCFA(ncch_hdr *hdr, keys_struct *keys) { - if (RsaSignVerify(GetNcchHdrData(hdr), GetNcchHdrDataLen(hdr), GetNcchHdrSig(hdr), keys->rsa.cciCfa.pub, keys->rsa.cciCfa.pvt, RSA_2048_SHA256, CTR_RSA_SIGN) != 0) + if (Rsa2048Key_CanSign(&keys->rsa.cciCfa) == false) { printf("[NCCH WARNING] Failed to sign CFA header\n"); memset(GetNcchHdrSig(hdr), 0xFF, 0x100); + return 0; } - return 0; + + return RsaSignVerify(GetNcchHdrData(hdr), GetNcchHdrDataLen(hdr), GetNcchHdrSig(hdr), keys->rsa.cciCfa.pub, keys->rsa.cciCfa.pvt, RSA_2048_SHA256, CTR_RSA_SIGN); } int CheckCFASignature(ncch_hdr *hdr, keys_struct *keys) { - return RsaSignVerify(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr),keys->rsa.cciCfa.pub,NULL,RSA_2048_SHA256,CTR_RSA_VERIFY); + return RsaSignVerify(GetNcchHdrData(hdr),GetNcchHdrDataLen(hdr),GetNcchHdrSig(hdr), keys->rsa.cciCfa.pub, keys->rsa.cciCfa.pvt, RSA_2048_SHA256,CTR_RSA_VERIFY); } int SignCXI(ncch_hdr *hdr, keys_struct *keys) { - if (RsaSignVerify(GetNcchHdrData(hdr), GetNcchHdrDataLen(hdr), GetNcchHdrSig(hdr), keys->rsa.cxi.pub, keys->rsa.cxi.pvt, RSA_2048_SHA256, CTR_RSA_SIGN) != 0) + if (Rsa2048Key_CanSign(&keys->rsa.cxi) == false) { printf("[NCCH WARNING] Failed to sign CXI header\n"); memset(GetNcchHdrSig(hdr), 0xFF, 0x100); + return 0; } - return 0; + + return RsaSignVerify(GetNcchHdrData(hdr), GetNcchHdrDataLen(hdr), GetNcchHdrSig(hdr), keys->rsa.cxi.pub, keys->rsa.cxi.pvt, RSA_2048_SHA256, CTR_RSA_SIGN); } int CheckCXISignature(ncch_hdr *hdr, u8 *pubk) @@ -1089,7 +1093,7 @@ bool SetNcchKeys(keys_struct *keys, ncch_hdr *hdr) return false; if(keys->aes.ncchKeyX[ncch_keyx_index]) - ctr_aes_keygen(keys->aes.ncchKeyX[ncch_keyx_index], hdr->signature, keys->aes.ncchKey0); + ctr_aes_keygen(keys->aes.ncchKeyX[ncch_keyx_index], hdr->signature, keys->aes.ncchKey1); else return false; diff --git a/makerom/ncsd.c b/makerom/ncsd.c index 51e4b9ce..b57d8f9d 100644 --- a/makerom/ncsd.c +++ b/makerom/ncsd.c @@ -579,14 +579,14 @@ int GenCciHdr(cci_settings *set) // Sign Header - if (RsaSignVerify(&hdr->magic, sizeof(cci_hdr) - RSA_2048_KEY_SIZE, hdr->signature, set->keys->rsa.cciCfa.pub, set->keys->rsa.cciCfa.pvt, RSA_2048_SHA256, CTR_RSA_SIGN) != 0) + if (Rsa2048Key_CanSign(&set->keys->rsa.cciCfa) == false) { printf("[NCSD WARNING] Failed to sign header\n"); memset(hdr->signature, 0xFF, 0x100); + return 0; } - - - return 0; + + return RsaSignVerify(&hdr->magic, sizeof(cci_hdr) - RSA_2048_KEY_SIZE, hdr->signature, set->keys->rsa.cciCfa.pub, set->keys->rsa.cciCfa.pvt, RSA_2048_SHA256, CTR_RSA_SIGN); } char* GetMediaSizeStr(u64 mediaSize) diff --git a/makerom/tik.c b/makerom/tik.c index fb219ae5..df955992 100644 --- a/makerom/tik.c +++ b/makerom/tik.c @@ -81,13 +81,14 @@ int SignTicketHeader(buffer_struct *tik, keys_struct *keys) clrmem(sig,sizeof(tik_signature)); u32_to_u8(sig->sigType,RSA_2048_SHA256,BE); - if (RsaSignVerify(data, len, sig->data, keys->rsa.xs.pub, keys->rsa.xs.pvt, RSA_2048_SHA256, CTR_RSA_SIGN) != 0) + if (Rsa2048Key_CanSign(&keys->rsa.xs) == false) { printf("[TIK WARNING] Failed to sign header\n"); memset(sig->data, 0xFF, 0x100); + return 0; } - return 0; + return RsaSignVerify(data, len, sig->data, keys->rsa.xs.pub, keys->rsa.xs.pvt, RSA_2048_SHA256, CTR_RSA_SIGN); } int CryptTitleKey(u8 *input, u8 *output, u8 *titleId, keys_struct *keys, u8 mode) diff --git a/makerom/tmd.c b/makerom/tmd.c index c7c54492..0b5fa837 100644 --- a/makerom/tmd.c +++ b/makerom/tmd.c @@ -71,13 +71,14 @@ int SignTMDHeader(tmd_hdr *hdr, tmd_signature *sig, keys_struct *keys) clrmem(sig,sizeof(tmd_signature)); u32_to_u8(sig->sigType,RSA_2048_SHA256,BE); - if (RsaSignVerify((u8*)hdr, sizeof(tmd_hdr), sig->data, keys->rsa.cp.pub, keys->rsa.cp.pvt, RSA_2048_SHA256, CTR_RSA_SIGN) != 0) + if (Rsa2048Key_CanSign(&keys->rsa.cp) == false) { printf("[TMD WARNING] Failed to sign header\n"); memset(sig->data, 0xFF, 0x100); + return 0; } - return 0; + return RsaSignVerify((u8*)hdr, sizeof(tmd_hdr), sig->data, keys->rsa.cp.pub, keys->rsa.cp.pvt, RSA_2048_SHA256, CTR_RSA_SIGN); } int SetupTMDInfoRecord(tmd_content_info_record *info_record, u8 *content_record, u16 ContentCount) From de705925743ea452c38fa0b8e4b3ac1419f26c08 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 28 May 2017 20:53:13 +0800 Subject: [PATCH 200/317] [makerom] Unbroke -target t --- makerom/keyset.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/makerom/keyset.c b/makerom/keyset.c index 19887fb9..4ff402ec 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -80,6 +80,15 @@ int LoadKeysFromResources(keys_struct *keys) SetNormalKey(keys,zeros_aesKey); SetSystemFixedKey(keys,zeros_aesKey); + /* RSA Keys */ + // CIA + Rsa2048Key_Set(&keys->rsa.xs, tpki_rsa.priv_exponent, tpki_rsa.modulus); + Rsa2048Key_Set(&keys->rsa.cp, tpki_rsa.priv_exponent, tpki_rsa.modulus); + // CCI/CFA + Rsa2048Key_Set(&keys->rsa.cciCfa, tpki_rsa.priv_exponent, tpki_rsa.modulus); + // CXI + Rsa2048Key_Set(&keys->rsa.acex, tpki_rsa.priv_exponent, tpki_rsa.modulus); + /* Certs */ SetCaCert(keys, ca3_tpki_cert); SetTikCert(keys, xsC_tpki_cert); From 7c08e895d0c449167f8dfea52cf4eef5e14ec564 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 30 May 2017 13:29:28 +0800 Subject: [PATCH 201/317] Remove unwanted files --- ctrtool/ctrtool | Bin 262759 -> 0 bytes ctrtool/seeddb.bin | Bin 40752 -> 0 bytes makerom/makerom | Bin 438222 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ctrtool/ctrtool delete mode 100644 ctrtool/seeddb.bin delete mode 100644 makerom/makerom diff --git a/ctrtool/ctrtool b/ctrtool/ctrtool deleted file mode 100644 index 441e0dd4d0cd351d8af7ceb575de8d8cbd1a70e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262759 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

9vN;}2zW6O`ZKnZtH1ee-Xf3Sf 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

Oadf1c4vpErB>6}Aa`!C zGl9q&8(ZJ949B2{5N`3YEo4closZy$GB6qK5mbF6av?acE~|bs4lVjUl<8Gcws1vb zTX1Y9TjI_w(SAUz^D%H7q2Ma}2a+QFgxMhn4`{KpgobuDAOmM+=kLp; z`_YaDCx?Sibd$wTqXtTgTbJ3b7*vv zRtL}Z<2mrK;g*~dazAzs5No7-)<{n`Y6u=28wzrG7+^UuC!c_{;g!ekolOWJd}k1| z*>FO%p{}X5BevVp$LE$?9vt4gsSwW?q(bu=tpnt=>9_VIR$peKb>@QjPcz zzI9=^_$2pQBGlMK@^IimP60-nA&j9(5~`XWG{+NaO_HeVK#^EYZb=ELt^*#`|JbBk z-DAbfL#PKF6yh6=TYuCTj9kRsg=*xUO?TbMOgV2ai3>?F`A$gtpiiyvu;0v8Av);h zbUVIENYxTq@pEP1?R|H(o)@uoz)@{Te?2FA6u?!88js|=Jj)>{;KMt zOUwNE5C21M7*kQsV@0;8`sGjS$RzetJT`e~ef+fNU?YlD;^Ku! z?*YnQ%_X@sVl>%qn(Bh>KR~sEFhRZ@d!Xp}27w0@79^VjTMH&@7E=gYeYyBR39OMt z$<_!oyX8U`weSO*f!b*eLHf*e;r3g?ICz7Q^ZR&0W|nQc;ZH3MWbD*s2fpV6U&p8C z<&$T%T!;t3_+)AazHbTRR8fJ?mllWOQ5PG;0>p4a*^xL)hsvVqo;&xKXa#v^2E>vk z^-asq9_psXJe_>w22_?M$($=7z~I?#N9K*lh9|aJoqxlp+)!k^184>L8Jb#vbhYodk zE>5*W#^|jWsy?Shx23X>BmER#ixf7Z&?b4Rt(xkJ!VhRd-^n5?zf4+d6`!w3qKPJq zVRTO_CEAcohl}5=2p_I9e}EVej{paI%y6d>&$HI}Ih1MGmM#>}gjD4UL@-e~@*zFz zsQDaD*zji0-~M7&cosNI=LUNu`sc8ki|2IAkaYdL&gRNx^uB&dAYzP>hkH7vgQA%% zW~md#ZE=F(oyKYh*XgG(S(0BR5pwjg9d#(rhN)-vh>x1;_(oLbl0dOkOf b&~EVQv%rk}0~g`%`Y?l9sUMtBxBUMA`Wbe` diff --git a/ctrtool/seeddb.bin b/ctrtool/seeddb.bin deleted file mode 100644 index 4bf2190e01856357ed6b8129aae8d7935314c302..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40752 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 z 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�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

rY4|M-zD_}}v3J}db5FZ$62 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 From c629eb35f1e65a7cef8fbbf12b4844db204edf35 Mon Sep 17 00:00:00 2001 From: Myria Date: Sat, 3 Jun 2017 10:15:12 -0700 Subject: [PATCH 202/317] Added 11.3 and 11.4 system calls SetGpuProt and SetWifiEnabled. Also, switched from C99 to C11 to get _Static_assert. --- ctrtool/Makefile | 2 +- ctrtool/syscalls.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 55bc16b2..8f07f97e 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -5,7 +5,7 @@ OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) $(foreach # Compiler Settings OUTPUT = ctrtool CXXFLAGS = -I. -CFLAGS = -O2 -Wall -Wno-unused-variable -Wno-unused-result -I. -std=c99 +CFLAGS = -O2 -Wall -Wno-unused-variable -Wno-unused-result -I. -std=c11 CC = gcc CXX = g++ SYS := $(shell gcc -dumpmachine) diff --git a/ctrtool/syscalls.c b/ctrtool/syscalls.c index edf8e8c4..cd37cf81 100644 --- a/ctrtool/syscalls.c +++ b/ctrtool/syscalls.c @@ -95,8 +95,8 @@ static const char *const syscall_list[NUM_SYSCALLS] = "StopDma", // 56 "GetDmaState", // 57 "RestartDma", // 58 - NULL, // 59 - NULL, // 5A + "SetGpuProt", // 59 + "SetWifiEnabled", // 5A NULL, // 5B NULL, // 5C NULL, // 5D @@ -139,7 +139,8 @@ static const char *const syscall_list[NUM_SYSCALLS] = void syscall_get_name(char *output, size_t size, unsigned int call_num) { - typedef char StaticAssert[sizeof(syscall_list) / sizeof(syscall_list[0]) == NUM_SYSCALLS ? 1 : -1]; + _Static_assert(sizeof(syscall_list) / sizeof(syscall_list[0]) == NUM_SYSCALLS, + "syscall table length mismatch"); if (size == 0) { From 4d5d9582a207eaaec4aa7fd141d5407205ed552d Mon Sep 17 00:00:00 2001 From: Myria Date: Sat, 3 Jun 2017 10:17:27 -0700 Subject: [PATCH 203/317] Warning fix. Uninitialized variable shouldn't have happened because it'd mean header was corrupt...? --- ctrtool/cwav.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ctrtool/cwav.c b/ctrtool/cwav.c index cf9f59b8..c87ede4e 100644 --- a/ctrtool/cwav.c +++ b/ctrtool/cwav.c @@ -814,6 +814,8 @@ int cwav_pcm_setup(cwav_pcmstate* state, cwav_context* ctx, int isloop) startoffset = getle32(ctx->infoheader.loopstart); else if (ctx->infoheader.encoding == CWAV_ENCODING_PCM16) startoffset = getle32(ctx->infoheader.loopstart) * 2; + else + startoffset = 0; } else { From 3cca6a7a9330e43e6d70c13710d957df0bff484b Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 5 Jun 2017 11:27:23 +0800 Subject: [PATCH 204/317] [ctrtool] Fix uninitialised variable warning. --- ctrtool/cwav.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/cwav.c b/ctrtool/cwav.c index cf9f59b8..4b644d43 100644 --- a/ctrtool/cwav.c +++ b/ctrtool/cwav.c @@ -792,7 +792,7 @@ int cwav_pcm_setup(cwav_pcmstate* state, cwav_context* ctx, int isloop) { u32 channelcount = ctx->channelcount; u32 i; - u32 startoffset; + u32 startoffset = 0; if (ctx->channel == 0) From 56ef8b41d18f598afa5cb0c777e4a24a10f899d8 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 5 Jun 2017 11:31:18 +0800 Subject: [PATCH 205/317] [ctrtool] Re-added "--seed" option, simplified/corrected common-key selection logic. --- ctrtool/cia.c | 12 +++--- ctrtool/keyset.cpp | 91 ++++++++++++++++++++++++++++++---------------- ctrtool/keyset.h | 7 +++- ctrtool/main.c | 31 +++++++++------- ctrtool/settings.c | 7 ++-- ctrtool/settings.h | 2 +- ctrtool/tik.c | 46 ++++++++--------------- ctrtool/tik.h | 6 +-- 8 files changed, 111 insertions(+), 91 deletions(-) diff --git a/ctrtool/cia.c b/ctrtool/cia.c index 4723c709..cac32601 100644 --- a/ctrtool/cia.c +++ b/ctrtool/cia.c @@ -211,14 +211,12 @@ void cia_process(cia_context* ctx, u32 actions) tik_process(&ctx->tik, actions); memset(ctx->iv, 0, 16); - - - - if (settings_get_common_keyX(ctx->usersettings)) - tik_get_titlekey(&ctx->tik, ctx->titlekey); - else if(settings_get_title_key(ctx->usersettings)) - memcpy(ctx->titlekey, settings_get_title_key(ctx->usersettings), 16); + if (tik_get_titlekey(&ctx->tik)) + memcpy(ctx->titlekey, tik_get_titlekey(&ctx->tik), 16); + else if (settings_get_title_key(ctx->usersettings)) + memcpy(ctx->titlekey, settings_get_title_key(ctx->usersettings), 16); + tmd_set_file(&ctx->tmd, ctx->file); tmd_set_offset(&ctx->tmd, ctx->offsettmd); tmd_set_size(&ctx->tmd, ctx->sizetmd); diff --git a/ctrtool/keyset.cpp b/ctrtool/keyset.cpp index dd793003..b4e67633 100644 --- a/ctrtool/keyset.cpp +++ b/ctrtool/keyset.cpp @@ -36,8 +36,6 @@ static unsigned char hextobin(char c) void keyset_init(keyset* keys, u32 actions) { const key128 defaultkeys_retail[] = { - // common keyX - {{0x61, 0x70, 0x85, 0x71, 0x9b, 0x7c, 0xfb, 0x31, 0x6d, 0xf4, 0xdf, 0x2e, 0x83, 0x62, 0xc6, 0xe2}, 1}, // fixed system key - unknown if used/correct? {{0x52, 0x7c, 0xe6, 0x30, 0xa9, 0xca, 0x30, 0x5f, 0x36, 0x96, 0xf3, 0xcd, 0xe9, 0x54, 0x19, 0x4b}, 1}, // NCCH 0x2c keyX @@ -47,11 +45,21 @@ void keyset_init(keyset* keys, u32 actions) // NCCH 0x18 keyX N9.3 {{0x82, 0xe9, 0xc9, 0xbe, 0xbf, 0xb8, 0xbd, 0xb8, 0x75, 0xec, 0xc0, 0xa0, 0x7d, 0x47, 0x43, 0x74}, 1}, // NCCH 0x1B keyX N9.6 - {{0x45, 0xad, 0x04, 0x95, 0x39, 0x92, 0xc7, 0xc8, 0x93, 0x72, 0x4a, 0x9a, 0x7b, 0xce, 0x61, 0x82}, 1} + {{0x45, 0xad, 0x04, 0x95, 0x39, 0x92, 0xc7, 0xc8, 0x93, 0x72, 0x4a, 0x9a, 0x7b, 0xce, 0x61, 0x82}, 1}, + // common key index0 (application) + {{0x64, 0xC5, 0xFD, 0x55, 0xDD, 0x3A, 0xD9, 0x88, 0x32, 0x5B, 0xAA, 0xEC, 0x52, 0x43, 0xDB, 0x98}, 1}, + // common key index1 (system) + {{0x4A, 0xAA, 0x3D, 0x0E, 0x27, 0xD4, 0xD7, 0x28, 0xD0, 0xB1, 0xB4, 0x33, 0xF0, 0xF9, 0xCB, 0xC8}, 1}, + // common key index2 + {{0xFB, 0xB0, 0xEF, 0x8C, 0xDB, 0xB0, 0xD8, 0xE4, 0x53, 0xCD, 0x99, 0x34, 0x43, 0x71, 0x69, 0x7F}, 1}, + // common key index3 + {{0x25, 0x95, 0x9B, 0x7A, 0xD0, 0x40, 0x9F, 0x72, 0x68, 0x41, 0x98, 0xBA, 0x2E, 0xCD, 0x7D, 0xC6}, 1}, + // common key index4 + {{0x7A, 0xDA, 0x22, 0xCA, 0xFF, 0xC4, 0x76, 0xCC, 0x82, 0x97, 0xA0, 0xC7, 0xCE, 0xEE, 0xEE, 0xBE}, 1}, + // common key index5 + {{0xA5, 0x05, 0x1C, 0xA1, 0xB3, 0x7D, 0xCF, 0x3A, 0xFB, 0xCF, 0x8C, 0xC1, 0xED, 0xD9, 0xCE, 0x02}, 1}, }; const key128 defaultkeys_dev[] = { - // common keyX - {{0xbd, 0x4f, 0xe7, 0xe7, 0x33, 0xc7, 0x55, 0xfc, 0xe7, 0x54, 0x0e, 0xab, 0xbd, 0x8a, 0xc3, 0x0d}, 1}, // fixed system key {{0x52, 0x7c, 0xe6, 0x30, 0xa9, 0xca, 0x30, 0x5f, 0x36, 0x96, 0xf3, 0xcd, 0xe9, 0x54, 0x19, 0x4b}, 1}, // NCCH 0x2c keyX @@ -61,7 +69,19 @@ void keyset_init(keyset* keys, u32 actions) // NCCH 0x18 keyX N9.3 {{0x30, 0x4b, 0xf1, 0x46, 0x83, 0x72, 0xee, 0x64, 0x11, 0x5e, 0xbd, 0x40, 0x93, 0xd8, 0x42, 0x76}, 1}, // NCCH 0x1B keyX N9.6 - {{0x6c, 0x8b, 0x29, 0x44, 0xa0, 0x72, 0x60, 0x35, 0xf9, 0x41, 0xdf, 0xc0, 0x18, 0x52, 0x4f, 0xb6}, 1} + {{0x6c, 0x8b, 0x29, 0x44, 0xa0, 0x72, 0x60, 0x35, 0xf9, 0x41, 0xdf, 0xc0, 0x18, 0x52, 0x4f, 0xb6}, 1}, + // common key index0 + {{0x55, 0xA3, 0xF8, 0x72, 0xBD, 0xC8, 0x0C, 0x55, 0x5A, 0x65, 0x43, 0x81, 0x13, 0x9E, 0x15, 0x3B}, 1}, + // common key index1 + {{0x44, 0x34, 0xED, 0x14, 0x82, 0x0C, 0xA1, 0xEB, 0xAB, 0x82, 0xC1, 0x6E, 0x7B, 0xEF, 0x0C, 0x25}, 1}, + // common key index2 + {{0xF6, 0x2E, 0x3F, 0x95, 0x8E, 0x28, 0xA2, 0x1F, 0x28, 0x9E, 0xEC, 0x71, 0xA8, 0x66, 0x29, 0xDC}, 1}, + // common key index3 + {{0x2B, 0x49, 0xCB, 0x6F, 0x99, 0x98, 0xD9, 0xAD, 0x94, 0xF2, 0xED, 0xE7, 0xB5, 0xDA, 0x3E, 0x27}, 1}, + // common key index4 + {{0x75, 0x05, 0x52, 0xBF, 0xAA, 0x1C, 0x04, 0x07, 0x55, 0xC8, 0xD5, 0x9A, 0x55, 0xF9, 0xAD, 0x1F}, 1}, + // common key index5 + {{0xAA, 0xDA, 0x4C, 0xA8, 0xF6, 0xE5, 0xA9, 0x77, 0xE0, 0xA0, 0xF9, 0xE4, 0x76, 0xCF, 0x0D, 0x63}, 1}, }; memset(keys, 0, sizeof(keyset)); @@ -69,21 +89,23 @@ void keyset_init(keyset* keys, u32 actions) if (actions & PlainFlag) return; - if (!(actions & DevFlag)) { - memcpy(&keys->commonkeyX, &defaultkeys_retail[0], sizeof(key128)); - memcpy(&keys->ncchfixedsystemkey, &defaultkeys_retail[1], sizeof(key128)); - memcpy(&keys->ncchkeyX_old, &defaultkeys_retail[2], sizeof(key128)); - memcpy(&keys->ncchkeyX_seven, &defaultkeys_retail[3], sizeof(key128)); - memcpy(&keys->ncchkeyX_ninethree, &defaultkeys_retail[4], sizeof(key128)); - memcpy(&keys->ncchkeyX_ninesix, &defaultkeys_retail[5], sizeof(key128)); - } else { - memcpy(&keys->commonkeyX, &defaultkeys_dev[0], sizeof(key128)); - memcpy(&keys->ncchfixedsystemkey, &defaultkeys_dev[1], sizeof(key128)); - memcpy(&keys->ncchkeyX_old, &defaultkeys_dev[2], sizeof(key128)); - memcpy(&keys->ncchkeyX_seven, &defaultkeys_dev[3], sizeof(key128)); - memcpy(&keys->ncchkeyX_ninethree, &defaultkeys_dev[4], sizeof(key128)); - memcpy(&keys->ncchkeyX_ninesix, &defaultkeys_dev[5], sizeof(key128)); + // select keyset + const key128* default_keys = (actions & DevFlag)? defaultkeys_dev : defaultkeys_retail; + + u32 key_index = 0; + // set ncch keys + memcpy(&keys->ncchfixedsystemkey, &default_keys[key_index++], sizeof(key128)); + memcpy(&keys->ncchkeyX_old, &default_keys[key_index++], sizeof(key128)); + memcpy(&keys->ncchkeyX_seven, &default_keys[key_index++], sizeof(key128)); + memcpy(&keys->ncchkeyX_ninethree, &default_keys[key_index++], sizeof(key128)); + memcpy(&keys->ncchkeyX_ninesix, &default_keys[key_index++], sizeof(key128)); + + // set common keys + for (u32 i = 0; i < COMMONKEY_NUM; i++) + { + memcpy(&keys->commonkey[i], &default_keys[key_index++], sizeof(key128)); } + } int keyset_load_key(TiXmlHandle node, unsigned char* key, unsigned int size, int* valid) @@ -206,13 +228,13 @@ int keyset_load(keyset* keys, const char* fname, int verbose) 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("commonkeyx"), &keys->commonkeyX); + /* keyset_load_key128(root.FirstChild("ncchfixedsystemkey"), &keys->ncchfixedsystemkey); keyset_load_key128(root.FirstChild("ncchkeyxold"), &keys->ncchkeyX_old); keyset_load_key128(root.FirstChild("ncchkeyxseven"), &keys->ncchkeyX_seven); keyset_load_key128(root.FirstChild("ncchkeyxninethree"), &keys->ncchkeyX_ninethree); keyset_load_key128(root.FirstChild("ncchkeyxninesix"), &keys->ncchkeyX_ninesix); - + */ return 1; } @@ -225,8 +247,15 @@ void keyset_merge(keyset* keys, keyset* src) keyset_set_key128(&keys->v, src->v.data);\ } while (0) + // todo complete key copy + /* COPY_IF_VALID(commonkey[0]); */ + //if () + for (size_t i = 0; i < COMMONKEY_NUM; i++) + { + COPY_IF_VALID(commonkey[i]); + } COPY_IF_VALID(titlekey); - COPY_IF_VALID(commonkeyX); + COPY_IF_VALID(seed_fallback); COPY_IF_VALID(ncchfixedsystemkey); COPY_IF_VALID(ncchkeyX_old); COPY_IF_VALID(ncchkeyX_seven); @@ -235,8 +264,8 @@ void keyset_merge(keyset* keys, keyset* src) if (src->seed_num > 0) { keys->seed_num = src->seed_num; - keys->seed_db = (seeddb_entry*)calloc(src->seed_num, sizeof(seeddb_entry)); - memcpy(keys->seed_db, src->seed_db, src->seed_num * sizeof(seeddb_entry)); + keys->seed_db = (seeddb_entry*)calloc(keys->seed_num, sizeof(seeddb_entry)); + memcpy(keys->seed_db, src->seed_db, keys->seed_num * sizeof(seeddb_entry)); } #undef COPY_IF_VALID @@ -253,11 +282,6 @@ void keyset_parse_key128(key128* key, char* keytext, int keylen) keyset_parse_key(keytext, keylen, key->data, 16, &key->valid); } -void keyset_parse_commonkeyX(keyset* keys, char* keytext, int keylen) -{ - keyset_parse_key128(&keys->commonkeyX, keytext, keylen); -} - void keyset_parse_titlekey(keyset* keys, char* keytext, int keylen) { keyset_parse_key128(&keys->titlekey, keytext, keylen); @@ -315,6 +339,12 @@ void keyset_parse_seeddb(keyset* keys, char* path) fread(keys->seed_db, keys->seed_num * sizeof(seeddb_entry), 1, fp); } +void keyset_parse_seed_fallback(keyset* keys, char* keytext, int keylen) +{ + keyset_parse_key128(&keys->seed_fallback, keytext, keylen); +} + + void keyset_dump_rsakey(rsakey2048* key, const char* keytitle) { if (key->keytype == RSAKEY_INVALID) @@ -355,7 +385,6 @@ void keyset_dump(keyset* keys) DUMP_KEY(ncchkeyX_ninethree, "NCCH N9.3 KEYX"); DUMP_KEY(ncchkeyX_ninesix, "NCCH N9.6 KEYX"); DUMP_KEY(ncchfixedsystemkey, "NCCH FIXEDSYSTEMKEY"); - DUMP_KEY(commonkeyX, "COMMON KEYX"); #undef DUMP_KEY keyset_dump_rsakey(&keys->ncsdrsakey, "NCSD RSA KEY"); diff --git a/ctrtool/keyset.h b/ctrtool/keyset.h index 3d6610fb..4d9235dd 100644 --- a/ctrtool/keyset.h +++ b/ctrtool/keyset.h @@ -7,6 +7,8 @@ extern "C" { #endif +#define COMMONKEY_NUM 6 + typedef enum { KEY_ERR_LEN_MISMATCH, @@ -59,8 +61,9 @@ typedef struct u32 seed_num; seeddb_entry* seed_db; + key128 commonkey[COMMONKEY_NUM]; key128 titlekey; - key128 commonkeyX; + key128 seed_fallback; key128 ncchfixedsystemkey; key128 ncchkeyX_old; key128 ncchkeyX_seven; @@ -75,7 +78,6 @@ typedef struct void keyset_init(keyset* keys, u32 actions); int keyset_load(keyset* keys, const char* fname, int verbose); void keyset_merge(keyset* keys, keyset* src); -void keyset_parse_commonkeyX(keyset* keys, char* keytext, int keylen); void keyset_parse_titlekey(keyset* keys, char* keytext, int keylen); void keyset_parse_ncchkeyX_old(keyset* keys, char* keytext, int keylen); void keyset_parse_ncchfixedsystemkey(keyset* keys, char* keytext, int keylen); @@ -83,6 +85,7 @@ void keyset_parse_ncchkeyX_seven(keyset* keys, char* keytext, int keylen); void keyset_parse_ncchkeyX_ninethree(keyset* keys, char* keytext, int keylen); void keyset_parse_ncchkeyX_ninesix(keyset* keys, char* keytext, int keylen); void keyset_parse_seeddb(keyset* keys, char* path); +void keyset_parse_seed_fallback(keyset* keys, char* keytext, int keylen); void keyset_dump(keyset* keys); #ifdef __cplusplus diff --git a/ctrtool/main.c b/ctrtool/main.c index 17113746..3c949586 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -54,11 +54,12 @@ static void usage(const char *argv0) " -y, --verify Verify hashes and signatures.\n" " -d, --dev Decrypt with development keys instead of retail.\n" " --unitsize=size Set media unit size (default 0x200).\n" - " --commonkey=key Set common key.\n" +// " --commonkey=key Set common key.\n" " --titlekey=key Set tik title key.\n" - " --ncchkey=key Set ncch key.\n" - " --ncchsyskey=key Set ncch fixed system key.\n" +// " --ncchkey=key Set ncch key.\n" +// " --ncchsyskey=key Set ncch fixed system key.\n" " --seeddb=file Set seeddb for ncch seed crypto.\n" + " --seed=key Set specific seed for ncch seed crypto.\n" " --showkeys Show the keys being used.\n" " --showsyscalls Show system call names instead of numbers.\n" " -t, --intype=type Specify input file type [ncsd, ncch, exheader, cia, tmd, lzss,\n" @@ -138,8 +139,8 @@ int main(int argc, char* argv[]) {"raw", 0, NULL, 'r'}, {"unitsize", 1, NULL, 9}, {"showkeys", 0, NULL, 10}, - {"commonkeyx", 1, NULL, 11}, - {"ncchkeyxold", 1, NULL, 12}, + //{"commonkeyx", 1, NULL, 11}, + //{"ncchkeyxold", 1, NULL, 12}, {"intype", 1, NULL, 't'}, {"lzssout", 1, NULL, 13}, {"firmdir", 1, NULL, 14}, @@ -153,10 +154,11 @@ int main(int argc, char* argv[]) {"titlekey", 1, NULL, 22}, {"plainrgn", 1, NULL, 23}, {"showsyscalls", 0, NULL, 24}, - {"ncchkeyxseven", 1, NULL, 25}, - {"ncchkeyxninethree", 1, NULL, 26}, - {"ncchkeyxninesix", 1, NULL, 27}, + //{"ncchkeyxseven", 1, NULL, 25}, + //{"ncchkeyxninethree", 1, NULL, 26}, + //{"ncchkeyxninesix", 1, NULL, 27}, {"seeddb", 1, NULL, 28}, + {"seed", 1, NULL, 29 }, {NULL}, }; @@ -237,11 +239,11 @@ int main(int argc, char* argv[]) 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_commonkeyX(&tmpkeys, optarg, strlen(optarg)); break; - case 12: keyset_parse_ncchkeyX_old(&tmpkeys, optarg, strlen(optarg)); break; + //case 11: keyset_parse_commonkeyX(&tmpkeys, optarg, strlen(optarg)); break; + //case 12: keyset_parse_ncchkeyX_old(&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 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; @@ -251,10 +253,11 @@ int main(int argc, char* argv[]) case 22: keyset_parse_titlekey(&tmpkeys, optarg, strlen(optarg)); break; case 23: settings_set_plainrgn_path(&ctx.usersettings, optarg); break; case 24: ctx.actions |= ShowSyscallsFlag; break; - case 25: keyset_parse_ncchkeyX_seven(&tmpkeys, optarg, strlen(optarg)); break; - case 26: keyset_parse_ncchkeyX_ninethree(&tmpkeys, optarg, strlen(optarg)); break; - case 27: keyset_parse_ncchkeyX_ninesix(&tmpkeys, optarg, strlen(optarg)); break; + //case 25: keyset_parse_ncchkeyX_seven(&tmpkeys, optarg, strlen(optarg)); break; + //case 26: keyset_parse_ncchkeyX_ninethree(&tmpkeys, optarg, strlen(optarg)); break; + //case 27: keyset_parse_ncchkeyX_ninesix(&tmpkeys, optarg, strlen(optarg)); break; case 28: keyset_parse_seeddb(&tmpkeys, optarg); break; + case 29: keyset_parse_seed_fallback(&tmpkeys, optarg, strlen(optarg)); break; default: usage(argv[0]); diff --git a/ctrtool/settings.c b/ctrtool/settings.c index 15523b01..9fbf0567 100644 --- a/ctrtool/settings.c +++ b/ctrtool/settings.c @@ -168,9 +168,10 @@ unsigned char* settings_get_ncchkeyX_ninesix(settings* usersettings) GETKEY(usersettings, ncchkeyX_ninesix); } -unsigned char* settings_get_common_keyX(settings* usersettings) +unsigned char* settings_get_common_key(settings* usersettings, u8 index) { - GETKEY(usersettings, commonkeyX); + if (index > (COMMONKEY_NUM - 1)) return NULL; + GETKEY(usersettings, commonkey[index]); } unsigned char* settings_get_seed(settings* usersettings, u64 title_id) @@ -182,7 +183,7 @@ unsigned char* settings_get_seed(settings* usersettings, u64 title_id) return usersettings->keys.seed_db[i].seed; } } - return NULL; + GETKEY(usersettings, seed_fallback); } unsigned char* settings_get_title_key(settings* usersettings) diff --git a/ctrtool/settings.h b/ctrtool/settings.h index c90947b2..e634eaa3 100644 --- a/ctrtool/settings.h +++ b/ctrtool/settings.h @@ -51,7 +51,7 @@ unsigned char* settings_get_ncchkeyX_old(settings* usersettings); unsigned char* settings_get_ncchkeyX_seven(settings* usersettings); unsigned char* settings_get_ncchkeyX_ninethree(settings* usersettings); unsigned char* settings_get_ncchkeyX_ninesix(settings* usersettings); -unsigned char* settings_get_common_keyX(settings* usersettings); +unsigned char* settings_get_common_key(settings* usersettings, u8 index); unsigned char* settings_get_seed(settings* usersettings, u64 title_id); unsigned char* settings_get_title_key(settings* usersettings); int settings_get_ignore_programid(settings* usersettings); diff --git a/ctrtool/tik.c b/ctrtool/tik.c index 0c1b69ce..285f403c 100644 --- a/ctrtool/tik.c +++ b/ctrtool/tik.c @@ -32,9 +32,9 @@ void tik_set_usersettings(tik_context* ctx, settings* usersettings) ctx->usersettings = usersettings; } -void tik_get_titlekey(tik_context* ctx, u8 key[0x10]) +const unsigned char* tik_get_titlekey(tik_context* ctx) { - memcpy(key, ctx->titlekey, 0x10); + return ctx->titlekey.valid ? ctx->titlekey.data : NULL; } void tik_get_titleid(tik_context* ctx, u8 titleid[8]) @@ -48,38 +48,24 @@ void tik_get_iv(tik_context* ctx, u8 iv[16]) memcpy(iv, ctx->tik.title_id, 8); } -void tik_decrypt_titlekey(tik_context* ctx, u8 decryptedkey[0x10]) +int tik_decrypt_titlekey(tik_context* ctx, u8 decryptedkey[0x10]) { u8 iv[16]; - u8* keyX = settings_get_common_keyX(ctx->usersettings); - const u8 keyYs[6][16] = { - // application titles (eShop titles) - {0xd0, 0x7b, 0x33, 0x7f, 0x9c, 0xa4, 0x38, 0x59, 0x32, 0xa2, 0xe2, 0x57, 0x23, 0x23, 0x2e, 0xb9}, - // system titles - {0x0c, 0x76, 0x72, 0x30, 0xf0, 0x99, 0x8f, 0x1c, 0x46, 0x82, 0x82, 0x02, 0xfa, 0xac, 0xbe, 0x4c}, - // these are unused - {0xc4, 0x75, 0xcb, 0x3a, 0xb8, 0xc7, 0x88, 0xbb, 0x57, 0x5e, 0x12, 0xa1, 0x09, 0x07, 0xb8, 0xa4}, - {0xe4, 0x86, 0xee, 0xe3, 0xd0, 0xc0, 0x9c, 0x90, 0x2f, 0x66, 0x86, 0xd4, 0xc0, 0x6f, 0x64, 0x9f}, - {0xed, 0x31, 0xba, 0x9c, 0x04, 0xb0, 0x67, 0x50, 0x6c, 0x44, 0x97, 0xa3, 0x5b, 0x78, 0x04, 0xfc}, - {0x5e, 0x66, 0x99, 0x8a, 0xb4, 0xe8, 0x93, 0x16, 0x06, 0x85, 0x0f, 0xd7, 0xa1, 0x6d, 0xd7, 0x55}, - }; - u8 key[16]; + u8* commonkey = settings_get_common_key(ctx->usersettings, ctx->tik.commonkey_idx); memset(decryptedkey, 0, 0x10); - - if (!keyX) + if (!commonkey) { - fprintf(stdout, "Warning, could not read common key.\n"); + fprintf(stdout, "Error, could not read common key.\n"); + return 1; } - else - { - ctr_aes_keygen(keyX, keyYs[(ctx->tik.title_id[3] & 0x10) ? 1 : 0], key); - memset(iv, 0, 0x10); - memcpy(iv, ctx->tik.title_id, 8); + + 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); - } + ctr_init_cbc_decrypt(&ctx->aes, commonkey, iv); + ctr_decrypt_cbc(&ctx->aes, ctx->tik.encrypted_title_key, decryptedkey, 0x10); + return 0; } void tik_process(tik_context* ctx, u32 actions) @@ -93,7 +79,7 @@ void tik_process(tik_context* ctx, u32 actions) fseeko64(ctx->file, ctx->offset, SEEK_SET); fread((u8*)&ctx->tik, 1, sizeof(eticket), ctx->file); - tik_decrypt_titlekey(ctx, ctx->titlekey); + ctx->titlekey.valid = tik_decrypt_titlekey(ctx, ctx->titlekey.data) == 0 ? 1 : 0; if (actions & InfoFlag) { @@ -122,8 +108,8 @@ void tik_print(tik_context* ctx) memdump(stdout, "Encrypted Titlekey: ", tik->encrypted_title_key, 0x10); - if (settings_get_common_keyX(ctx->usersettings)) - memdump(stdout, "Decrypted Titlekey: ", ctx->titlekey, 0x10); + if (ctx->titlekey.valid) + memdump(stdout, "Decrypted Titlekey: ", ctx->titlekey.data, 0x10); memdump(stdout, "Ticket ID: ", tik->ticket_id, 0x08); fprintf(stdout, "Ticket Version: %d\n", getle16(tik->ticket_version)); diff --git a/ctrtool/tik.h b/ctrtool/tik.h index 1aa05966..4d85aaa2 100644 --- a/ctrtool/tik.h +++ b/ctrtool/tik.h @@ -43,7 +43,7 @@ typedef struct FILE* file; u64 offset; u32 size; - u8 titlekey[16]; + key128 titlekey; eticket tik; ctr_aes_context aes; settings* usersettings; @@ -54,10 +54,10 @@ void tik_set_file(tik_context* ctx, FILE* file); void tik_set_offset(tik_context* ctx, u64 offset); void tik_set_size(tik_context* ctx, u32 size); void tik_set_usersettings(tik_context* ctx, settings* usersettings); -void tik_get_titlekey(tik_context* ctx, u8 key[0x10]); +const unsigned char* tik_get_titlekey(tik_context* ctx); 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]); +int tik_decrypt_titlekey(tik_context* ctx, u8 decryptedkey[0x10]); void tik_print(tik_context* ctx); void tik_process(tik_context* ctx, u32 actions); From b8b92d8bfafeb5229894affbba423aebf0c67eba Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 5 Jun 2017 11:32:13 +0800 Subject: [PATCH 206/317] [ctrtool] Rewrote NCCH crypto. "--exefs" is still broken. --- ctrtool/exefs.c | 26 ++--- ctrtool/exefs.h | 3 +- ctrtool/ncch.c | 284 ++++++++++++++++++++++++++---------------------- ctrtool/ncch.h | 12 +- 4 files changed, 168 insertions(+), 157 deletions(-) diff --git a/ctrtool/exefs.c b/ctrtool/exefs.c index 150a405d..cca86270 100644 --- a/ctrtool/exefs.c +++ b/ctrtool/exefs.c @@ -48,10 +48,10 @@ void exefs_set_encrypted(exefs_context* ctx, u32 encrypted) ctx->encrypted = encrypted; } -void exefs_set_keys(exefs_context* ctx, u8 key[16], u8 special_key[16]) +void exefs_set_keys(exefs_context* ctx, u8 key0[16], u8 key1[16]) { - memcpy(ctx->key, key, 16); - memcpy(ctx->special_key, special_key, 16); + memcpy(ctx->key[0], key0, 16); + memcpy(ctx->key[1], key1, 16); } void exefs_set_counter(exefs_context* ctx, u8 counter[16]) @@ -109,7 +109,7 @@ void exefs_save(exefs_context* ctx, u32 index, u32 flags) } fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); - ctr_init_key(&ctx->aes, ctx->key); + ctr_init_key(&ctx->aes, ctx->key[0]); ctr_init_counter(&ctx->aes, ctx->counter); ctr_add_counter(&ctx->aes, offset / 0x10); @@ -132,12 +132,10 @@ void exefs_save(exefs_context* ctx, u32 index, u32 flags) } if (ctx->encrypted) { - // .code and .firm use a special key on 7.0+ - if (ctx->encrypted & NCCHCRYPTO_SPECIAL_FSES) - ctr_init_key(&ctx->aes, ctx->special_key); - ctr_crypt_counter(&ctx->aes, compressedbuffer, compressedbuffer, compressedsize); - if (ctx->encrypted & NCCHCRYPTO_SPECIAL_FSES) - ctr_init_key(&ctx->aes, ctx->key); + if (strncmp((const char*)section->name, "icon", 8) == 0 || strncmp((const char*)section->name, "banner", 8) == 0) + ctr_init_key(&ctx->aes, ctx->key[0]); + else + ctr_init_key(&ctx->aes, ctx->key[1]); } @@ -203,7 +201,7 @@ void exefs_read_header(exefs_context* ctx, u32 flags) fread(&ctx->header, 1, sizeof(exefs_header), ctx->file); if (ctx->encrypted) { - ctr_init_key(&ctx->aes, ctx->key); + ctr_init_key(&ctx->aes, ctx->key[0]); ctr_init_counter(&ctx->aes, ctx->counter); ctr_crypt_counter(&ctx->aes, (u8*)&ctx->header, (u8*)&ctx->header, sizeof(exefs_header)); } @@ -261,10 +259,10 @@ int exefs_verify(exefs_context* ctx, u32 index, u32 flags) return 0; fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); - if (index == 0 && (ctx->encrypted & NCCHCRYPTO_SPECIAL_FSES)) - ctr_init_key(&ctx->aes, ctx->special_key); + if (strncmp((const char*)section->name, "icon", 8) == 0 || strncmp((const char*)section->name, "banner", 8) == 0) + ctr_init_key(&ctx->aes, ctx->key[0]); else - ctr_init_key(&ctx->aes, ctx->key); + ctr_init_key(&ctx->aes, ctx->key[1]); ctr_init_counter(&ctx->aes, ctx->counter); ctr_add_counter(&ctx->aes, offset / 0x10); diff --git a/ctrtool/exefs.h b/ctrtool/exefs.h index 16e6152e..cef22ce7 100644 --- a/ctrtool/exefs.h +++ b/ctrtool/exefs.h @@ -29,8 +29,7 @@ typedef struct settings* usersettings; u8 partitionid[8]; u8 counter[16]; - u8 key[16]; - u8 special_key[16]; + u8 key[2][16]; u64 offset; u64 size; exefs_header header; diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index a847ac20..09bab9b7 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -79,7 +79,7 @@ void ncch_get_counter(ncch_context* ctx, u8 counter[16], u8 type) } - +/* this is broken */ int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags) { u64 offset = 0; @@ -93,6 +93,7 @@ int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags) { offset = ncch_get_exefs_offset(ctx); size = ncch_get_exefs_size(ctx); + ctr_init_key(&ctx->aes, ctx->key[0]); } break; @@ -100,6 +101,7 @@ int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags) { offset = ncch_get_romfs_offset(ctx); size = ncch_get_romfs_size(ctx); + ctr_init_key(&ctx->aes, ctx->key[1]); } break; @@ -107,6 +109,7 @@ int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags) { offset = ncch_get_exheader_offset(ctx); size = ncch_get_exheader_size(ctx) * 2; + ctr_init_key(&ctx->aes, ctx->key[0]); } break; @@ -136,7 +139,7 @@ int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags) ctx->extractflags = flags; fseeko64(ctx->file, offset, SEEK_SET); ncch_get_counter(ctx, counter, type); - ctr_init_key(&ctx->aes, ctx->key); + ctr_init_counter(&ctx->aes, counter); return 1; @@ -351,7 +354,7 @@ void ncch_process(ncch_context* ctx, u32 actions) exheader_set_programid(&ctx->exheader, ctx->header.programid); exheader_set_hash(&ctx->exheader, ctx->header.extendedheaderhash); exheader_set_counter(&ctx->exheader, exheadercounter); - exheader_set_key(&ctx->exheader, ctx->key); + exheader_set_key(&ctx->exheader, ctx->key[0]); exheader_set_encrypted(&ctx->exheader, ctx->encrypted); exefs_set_file(&ctx->exefs, ctx->file); @@ -360,7 +363,7 @@ void ncch_process(ncch_context* ctx, u32 actions) exefs_set_partitionid(&ctx->exefs, ctx->header.partitionid); exefs_set_usersettings(&ctx->exefs, ctx->usersettings); exefs_set_counter(&ctx->exefs, exefscounter); - exefs_set_keys(&ctx->exefs, ctx->key, ctx->special_key); + exefs_set_keys(&ctx->exefs, ctx->key[0], ctx->key[1]); exefs_set_encrypted(&ctx->exefs, ctx->encrypted); romfs_set_file(&ctx->romfs, ctx->file); @@ -368,10 +371,7 @@ void ncch_process(ncch_context* ctx, u32 actions) romfs_set_size(&ctx->romfs, ncch_get_romfs_size(ctx)); romfs_set_usersettings(&ctx->romfs, ctx->usersettings); romfs_set_counter(&ctx->romfs, romfscounter); - if (ctx->encrypted & NCCHCRYPTO_SPECIAL_FSES) - romfs_set_key(&ctx->romfs, ctx->special_key); - else - romfs_set_key(&ctx->romfs, ctx->key); + romfs_set_key(&ctx->romfs, ctx->key[1]); romfs_set_encrypted(&ctx->romfs, ctx->encrypted); exheader_read(&ctx->exheader, actions); @@ -392,11 +392,18 @@ void ncch_process(ncch_context* ctx, u32 actions) if ((actions & ShowKeysFlag) && ctx->encrypted) { fprintf(stdout, "Using key(s):\n"); - memdump(stdout, " 0x2C: ", ctx->key, 0x10); - if (ctx->encrypted & NCCHCRYPTO_SPECIAL_FSES) + if (ctx->encrypted == NCCHCRYPTO_FIXED) { - fprintf(stdout, " special (%02x): ", ctx->header.flags[3]); - memdump(stdout, "", ctx->special_key, 0x10); + memdump(stdout, " Key: ", ctx->key[0], 0x10); + } + else + { + memdump(stdout, " 0x2C: ", ctx->key[0], 0x10); + if (memcmp(ctx->key[0], ctx->key[1], 0x10) != 0) + { + fprintf(stdout, " special (%02x): ", ctx->header.flags[3]); + memdump(stdout, "", ctx->key[1], 0x10); + } } } @@ -510,150 +517,163 @@ u64 ncch_get_mediaunit_size(ncch_context* ctx) void ncch_determine_key(ncch_context* ctx, u32 actions) { - exheader_header exheader; - u8* key; - u8* seed; - ctr_ncchheader* header = &ctx->header; + u8 exheader_buffer[0x400]; u8 seedbuf[0x20]; - u8 seedhash[0x20]; - u8 keyX[0x10], keyY[0x10], seedKeyY[0x10]; - - ctx->encrypted = 0; - memset(ctx->key, 0, 0x10); - - if (actions & PlainFlag) + u8* seed; + u8 hash[0x20]; + ctr_ncchheader* header = &ctx->header; + struct sNcchKeyslot { - ctx->encrypted = 0; - } - else + u8 x[0x10]; + u8 y[0x10]; + u8 key[0x10]; + } key[2]; + + + // Check if the NCCH is already decrypted, by checking if the exheader hash matches + // Otherwise, use determination rules + fseeko64(ctx->file, ncch_get_exheader_offset(ctx), SEEK_SET); + memset(exheader_buffer, 0, getle32(header->extendedheadersize)); + fread(exheader_buffer, 1, getle32(header->extendedheadersize), ctx->file); + ctr_sha_256(exheader_buffer, getle32(header->extendedheadersize), hash); + if (!memcmp(hash, header->extendedheaderhash, 32)) { - // No explicit NCCH key defined, so we try to decide - // In almost all of these scenarios, the normal 0x2C NCCH keyX will be the default, - // except for the old fixedkey crypto, where we'll override it anyway, so let's just - // set the 0x2C keyX first. - key = settings_get_ncchkeyX_old(ctx->usersettings); - if (key) - memcpy(keyX, key, 0x10); - else - fprintf(stderr, "Warning, could not read NCCH base key. Decryption will likely fail.\n"); + // exheader hash matches, so probably decrypted + ctx->encrypted = NCCHCRYPTO_NONE; + if (!(header->flags[7] & 4)) + fprintf(stderr, "Warning, exheader is decrypted but the NCCH says it isn't.\n" + "This NCCH will likely break on console.\n"); + return; + } - // The keyY is normally the beginning of the NCCH header signature. In case seed crypto - // changes that, we'll override it below. - memcpy(keyY, header->signature, 0x10); - memcpy(seedKeyY, keyY, 0x10); + // if not plain flag set and not ncch unencrypted flag set + // determine the keys + if (!(actions & PlainFlag) && !(header->flags[7] & 4)) + { + // fixed key crypto + if (header->flags[7] & 1) + { + ctx->encrypted = NCCHCRYPTO_FIXED; + if (programid_is_system(header->programid)) + { + if (settings_get_ncch_fixedsystemkey(ctx->usersettings) == NULL) + { + fprintf(stderr, "Error, could not read system fixed key.\n"); + ctx->encrypted = NCCHCRYPTO_BROKEN; + return; + } - // 0x2c crypto is normally used; we override it where necessary - ctr_aes_keygen(keyX, keyY, ctx->key); + memcpy(key[0].key, settings_get_ncch_fixedsystemkey(ctx->usersettings), 0x10); + } + else + { + memset(key[0].key, 0x00, 0x10); + } - // Seed crypto can be used alongside any other crypto type, so we'll need to figure this out early. - if (header->flags[7] & 0x20) + memcpy(key[1].key, key[0].key, 0x10); + } + // secure crypto + else { - ctx->encrypted = NCCHCRYPTO_SEED; - seed = settings_get_seed(ctx->usersettings, getle64(header->partitionid)); - if (!seed) + ctx->encrypted = NCCHCRYPTO_SECURE; + if (settings_get_ncchkeyX_old(ctx->usersettings) == NULL) { - fprintf(stderr, "This title uses seed crypto, but no seed is set in seedDB, unable to decrypt.\n" - "Use -p to avoid decryption or use --seeddb=dbfile to specify the seedDB.\n"); + fprintf(stderr, "Error, could not read NCCH base keyX.\n"); ctx->encrypted = NCCHCRYPTO_BROKEN; return; } - memcpy(seedbuf, seed, 0x10); - // Assumes running on little endian - memcpy(seedbuf + 0x10, header->programid, sizeof(header->programid)); - ctr_sha_256(seedbuf, 0x18, seedhash); - if (memcmp(seedhash, header->seedcheck, sizeof(header->seedcheck))) { - fprintf(stderr, "Seed check mismatch. (Got: %02x%02x%02x%02x, expected: %02x%02x%02x%02x)\n", - seedhash[0], seedhash[1], seedhash[2], seedhash[3], - header->seedcheck[0], header->seedcheck[1], header->seedcheck[2], header->seedcheck[3]); + // setup regular keyslot seeds (exheader, exefs:icon|banner) + memcpy(key[0].x, settings_get_ncchkeyX_old(ctx->usersettings), 0x10); + memcpy(key[0].y, header->signature, 0x10); + + // setup second keyslot seed (romfs, exefs:!(icon|banner)) + // - get keyX + if (header->flags[3] == 0) + { + memcpy(key[1].x, key[0].x, 0x10); + } + else if (header->flags[3] == 1) + { + if (settings_get_ncchkeyX_seven(ctx->usersettings) == NULL) + { + fprintf(stderr, "Error, could not read NCCH 7.0 keyX.\n"); + return; + } + memcpy(key[1].x, settings_get_ncchkeyX_seven(ctx->usersettings), 0x10); + } + else if (header->flags[3] == 10) + { + if (settings_get_ncchkeyX_ninethree(ctx->usersettings) == NULL) + { + fprintf(stderr, "Error, could not read NCCH 9.3 keyX.\n"); + return; + } + memcpy(key[1].x, settings_get_ncchkeyX_ninethree(ctx->usersettings), 0x10); + } + else if (header->flags[3] == 11) + { + if (settings_get_ncchkeyX_ninesix(ctx->usersettings) == NULL) + { + fprintf(stderr, "Error, could not read NCCH 9.6 keyX.\n"); + return; + } + memcpy(key[1].x, settings_get_ncchkeyX_ninesix(ctx->usersettings), 0x10); + } + else + { + fprintf(stderr, "Warning, unknown NCCH crypto method.\n"); ctx->encrypted = NCCHCRYPTO_BROKEN; return; } - memcpy(seedbuf, header->signature, 0x10); - memcpy(seedbuf + 0x10, seed, 0x10); - ctr_sha_256(seedbuf, 0x20, seedhash); - memcpy(seedKeyY, seedhash, 0x10); - } - - // Check if the NCCH is already decrypted, by reading the programid in the exheader - // Otherwise, use determination rules - fseeko64(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 = NCCHCRYPTO_NONE; - if (!(header->flags[7] & 4)) - fprintf(stderr, "Warning, exheader seems decrypted but the NCCH says it isn't.\n" - "This NCCH will likely break on console.\n"); - } - else if (header->flags[7] & 4) // no crypto - { - ctx->encrypted = NCCHCRYPTO_NONE; - } - else if (header->flags[7] & 1) // fixed key crypto - { - ctx->encrypted = NCCHCRYPTO_FIXED; - if (programid_is_system(header->programid)) + // - get keyY + if (header->flags[7] & 0x20) { - // fixed system key - key = settings_get_ncch_fixedsystemkey(ctx->usersettings); - if (!key) { - fprintf(stderr, "Error, could not read system fixed key.\n"); + seed = settings_get_seed(ctx->usersettings, getle64(header->partitionid)); + if (!seed) + { + fprintf(stderr, "This title uses seed crypto, but no seed is set, unable to decrypt.\n" + "Use -p to avoid decryption or use --seeddb=dbfile or --seed=SEEDHERE.\n"); ctx->encrypted = NCCHCRYPTO_BROKEN; - } else { - memcpy(ctx->key, key, 0x10); + return; } + + memcpy(seedbuf, seed, 0x10); + // Assumes running on little endian + memcpy(seedbuf + 0x10, header->programid, sizeof(header->programid)); + ctr_sha_256(seedbuf, 0x18, hash); + if (memcmp(hash, header->seedcheck, sizeof(header->seedcheck))) { + fprintf(stderr, "Seed check mismatch. (Got: %02x%02x%02x%02x, expected: %02x%02x%02x%02x)\n", + hash[0], hash[1], hash[2], hash[3], + header->seedcheck[0], header->seedcheck[1], header->seedcheck[2], header->seedcheck[3]); + ctx->encrypted = NCCHCRYPTO_BROKEN; + return; + } + + memcpy(seedbuf, header->signature, 0x10); + memcpy(seedbuf + 0x10, seed, 0x10); + ctr_sha_256(seedbuf, 0x20, hash); + memcpy(key[1].y, hash, 0x10); } else { - // null key - memset(ctx->key, 0, 0x10); + memcpy(key[1].y, key[0].y, 0x10); } + + + // generate keys + ctr_aes_keygen(key[0].x, key[0].y, key[0].key); + ctr_aes_keygen(key[1].x, key[1].y, key[1].key); } - else if (header->flags[3] == 0x01) // 7.0 crypto - { - ctx->encrypted = NCCHCRYPTO_SEVEN; - key = settings_get_ncchkeyX_seven(ctx->usersettings); - if (!key) { - fprintf(stderr, "Error, could not read NCCH 7.0 keyX.\n"); - return; - } - ctr_aes_keygen(key, seedKeyY, ctx->special_key); - } - else if (header->flags[3] == 0x0A) // N9.3 crypto - { - ctx->encrypted = NCCHCRYPTO_NINETHREE; - key = settings_get_ncchkeyX_ninethree(ctx->usersettings); - if (!key) { - fprintf(stderr, "Error, could not read NCCH 9.3 keyX.\n"); - return; - } - ctr_aes_keygen(key, seedKeyY, ctx->special_key); - } - else if (header->flags[3] == 0x0B) // N9.6 crypto - { - ctx->encrypted = NCCHCRYPTO_NINESIX; - key = settings_get_ncchkeyX_ninesix(ctx->usersettings); - if (!key) { - fprintf(stderr, "Error, could not read NCCH 9.6 keyX.\n"); - return; - } - ctr_aes_keygen(key, seedKeyY, ctx->special_key); - } - else if (header->flags[3] != 0) // unknown special crypto - { - fprintf(stderr, "Warning, unknown NCCH crypto method.\n"); - ctx->encrypted = NCCHCRYPTO_BROKEN; - } - else - { - // old/normal NCCH crypto - ctx->encrypted = NCCHCRYPTO_OLD; - } + + // save keys + memcpy(ctx->key[0], key[0].key, 0x10); + memcpy(ctx->key[1], key[1].key, 0x10); + } + else + { + ctx->encrypted = NCCHCRYPTO_NONE; } } diff --git a/ctrtool/ncch.h b/ctrtool/ncch.h index 79f89578..d480ed6e 100644 --- a/ctrtool/ncch.h +++ b/ctrtool/ncch.h @@ -24,13 +24,8 @@ typedef enum { NCCHCRYPTO_NONE = 0, //< already decrypted NCCHCRYPTO_FIXED = 1, //< fixed key crypto, used for SDK-made application titles and very very old system titles - NCCHCRYPTO_OLD = (1<<1), //< crypto used before 7.0 - NCCHCRYPTO_SEVEN = (1<<2), //< crypto used starting with 7.0 - NCCHCRYPTO_NINETHREE = (1<<3), //< crypto used on N3DS starting with 9.3 - NCCHCRYPTO_NINESIX = (1<<4), //< crypto used on N3DS starting with 9.6 - NCCHCRYPTO_SEED = (1<<5), //< crypto used starting with 9.6 for preloading titles - NCCHCRYPTO_SPECIAL_FSES = NCCHCRYPTO_SEVEN | NCCHCRYPTO_NINETHREE | NCCHCRYPTO_NINESIX | NCCHCRYPTO_SEED, //< ExeFS and RomFS need new keys - NCCHCRYPTO_BROKEN = 0xFF //< Internal: seed crypto required but no seed set + NCCHCRYPTO_SECURE = (1<<1), //< hardware generated key + NCCHCRYPTO_BROKEN = 0xFF //< Internal: failed to generate key, but encryption still used } ctr_ncchcryptotype; typedef struct @@ -70,8 +65,7 @@ typedef struct typedef struct { FILE* file; - u8 key[16]; - u8 special_key[16]; // used with the 7.x+ crypto methods + u8 key[2][16]; u8 seed[16]; u32 encrypted; u64 offset; From 20f708450b9c6e7f64eafa6c2a8eeb25a630c69a Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 6 Jun 2017 12:44:12 +0800 Subject: [PATCH 207/317] [ctrtool] Fixed seed crypto. --- ctrtool/exefs.c | 4 ++-- ctrtool/exefs.h | 4 ++-- ctrtool/exheader.c | 4 ++-- ctrtool/exheader.h | 4 ++-- ctrtool/ncch.c | 14 +++++++------- ctrtool/ncch.h | 2 +- ctrtool/ncsd.c | 2 +- ctrtool/ncsd.h | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ctrtool/exefs.c b/ctrtool/exefs.c index cca86270..58e344ca 100644 --- a/ctrtool/exefs.c +++ b/ctrtool/exefs.c @@ -33,9 +33,9 @@ void exefs_set_usersettings(exefs_context* ctx, settings* usersettings) ctx->usersettings = usersettings; } -void exefs_set_partitionid(exefs_context* ctx, u8 partitionid[8]) +void exefs_set_titleid(exefs_context* ctx, u8 titleid[8]) { - memcpy(ctx->partitionid, partitionid, 8); + memcpy(ctx->titleid, titleid, 8); } void exefs_set_compressedflag(exefs_context* ctx, int compressedflag) diff --git a/ctrtool/exefs.h b/ctrtool/exefs.h index cef22ce7..2d1c4102 100644 --- a/ctrtool/exefs.h +++ b/ctrtool/exefs.h @@ -27,7 +27,7 @@ typedef struct { FILE* file; settings* usersettings; - u8 partitionid[8]; + u8 titleid[8]; u8 counter[16]; u8 key[2][16]; u64 offset; @@ -45,7 +45,7 @@ void exefs_set_file(exefs_context* ctx, FILE* file); void exefs_set_offset(exefs_context* ctx, u64 offset); void exefs_set_size(exefs_context* ctx, u64 size); void exefs_set_usersettings(exefs_context* ctx, settings* usersettings); -void exefs_set_partitionid(exefs_context* ctx, u8 partitionid[8]); +void exefs_set_titleid(exefs_context* ctx, u8 titleid[8]); void exefs_set_counter(exefs_context* ctx, u8 counter[16]); void exefs_set_compressedflag(exefs_context* ctx, int compressedflag); void exefs_set_keys(exefs_context* ctx, u8 key[16], u8 special_key[16]); diff --git a/ctrtool/exheader.c b/ctrtool/exheader.c index 21473de0..86609d84 100644 --- a/ctrtool/exheader.c +++ b/ctrtool/exheader.c @@ -34,9 +34,9 @@ void exheader_set_usersettings(exheader_context* ctx, settings* usersettings) ctx->usersettings = usersettings; } -void exheader_set_partitionid(exheader_context* ctx, u8 partitionid[8]) +void exheader_set_titleid(exheader_context* ctx, u8 titleid[8]) { - memcpy(ctx->partitionid, partitionid, 8); + memcpy(ctx->titleid, titleid, 8); } void exheader_set_programid(exheader_context* ctx, u8 programid[8]) diff --git a/ctrtool/exheader.h b/ctrtool/exheader.h index 09f751ea..7f4ce496 100644 --- a/ctrtool/exheader.h +++ b/ctrtool/exheader.h @@ -149,7 +149,7 @@ typedef struct int haveread; FILE* file; settings* usersettings; - u8 partitionid[8]; + u8 titleid[8]; u8 programid[8]; u8 hash[32]; u8 counter[16]; @@ -183,7 +183,7 @@ void exheader_init(exheader_context* ctx); void exheader_set_file(exheader_context* ctx, FILE* file); void exheader_set_offset(exheader_context* ctx, u64 offset); void exheader_set_size(exheader_context* ctx, u64 size); -void exheader_set_partitionid(exheader_context* ctx, u8 partitionid[8]); +void exheader_set_titleid(exheader_context* ctx, u8 titleid[8]); void exheader_set_counter(exheader_context* ctx, u8 counter[16]); void exheader_set_programid(exheader_context* ctx, u8 programid[8]); void exheader_set_hash(exheader_context* ctx, u8 hash[32]); diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 09bab9b7..14ff9a93 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -50,7 +50,7 @@ void ncch_get_counter(ncch_context* ctx, u8 counter[16], u8 type) { u32 version = getle16(ctx->header.version); u32 mediaunitsize = (u32) ncch_get_mediaunit_size(ctx); - u8* partitionid = ctx->header.partitionid; + u8* titleid = ctx->header.titleid; u32 i; u64 x = 0; @@ -59,7 +59,7 @@ void ncch_get_counter(ncch_context* ctx, u8 counter[16], u8 type) if (version == 2 || version == 0) { for(i=0; i<8; i++) - counter[i] = partitionid[7-i]; + counter[i] = titleid[7-i]; counter[8] = type; } else if (version == 1) @@ -72,7 +72,7 @@ void ncch_get_counter(ncch_context* ctx, u8 counter[16], u8 type) x = getle32(ctx->header.romfsoffset) * mediaunitsize; for(i=0; i<8; i++) - counter[i] = partitionid[i]; + counter[i] = titleid[i]; for(i=0; i<4; i++) counter[12+i] = (u8) (x>>((3-i)*8)); } @@ -350,7 +350,7 @@ void ncch_process(ncch_context* ctx, u32 actions) 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_titleid(&ctx->exheader, ctx->header.titleid); exheader_set_programid(&ctx->exheader, ctx->header.programid); exheader_set_hash(&ctx->exheader, ctx->header.extendedheaderhash); exheader_set_counter(&ctx->exheader, exheadercounter); @@ -360,7 +360,7 @@ void ncch_process(ncch_context* ctx, u32 actions) 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_titleid(&ctx->exefs, ctx->header.titleid); exefs_set_usersettings(&ctx->exefs, ctx->usersettings); exefs_set_counter(&ctx->exefs, exefscounter); exefs_set_keys(&ctx->exefs, ctx->key[0], ctx->key[1]); @@ -630,7 +630,7 @@ void ncch_determine_key(ncch_context* ctx, u32 actions) // - get keyY if (header->flags[7] & 0x20) { - seed = settings_get_seed(ctx->usersettings, getle64(header->partitionid)); + seed = settings_get_seed(ctx->usersettings, getle64(header->programid)); if (!seed) { fprintf(stderr, "This title uses seed crypto, but no seed is set, unable to decrypt.\n" @@ -735,7 +735,7 @@ void ncch_print(ncch_context* ctx) else memdump(stdout, "Signature (FAIL): ", header->signature, 0x100); fprintf(stdout, "Content size: 0x%08"PRIx64"\n", getle32(header->contentsize)*mediaunitsize); - fprintf(stdout, "Partition id: %016"PRIx64"\n", getle64(header->partitionid)); + fprintf(stdout, "Title id: %016"PRIx64"\n", getle64(header->titleid)); fprintf(stdout, "Maker code: %.2s\n", header->makercode); fprintf(stdout, "Version: %d\n", getle16(header->version)); fprintf(stdout, "Title seed check: %08x\n", getle32(header->seedcheck)); diff --git a/ctrtool/ncch.h b/ctrtool/ncch.h index d480ed6e..94d29687 100644 --- a/ctrtool/ncch.h +++ b/ctrtool/ncch.h @@ -33,7 +33,7 @@ typedef struct u8 signature[0x100]; u8 magic[4]; u8 contentsize[4]; - u8 partitionid[8]; + u8 titleid[8]; u8 makercode[2]; u8 version[2]; u8 seedcheck[4]; diff --git a/ctrtool/ncsd.c b/ctrtool/ncsd.c index 4a7d9284..ab9fae8f 100644 --- a/ctrtool/ncsd.c +++ b/ctrtool/ncsd.c @@ -147,7 +147,7 @@ void ncsd_print(ncsd_context* ctx) if (partitionsize != 0) { fprintf(stdout, "Partition %d \n", i); - memdump(stdout, " Id: ", header->partitionid+i*8, 8); + memdump(stdout, " Id: ", header->titleid+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]); diff --git a/ctrtool/ncsd.h b/ctrtool/ncsd.h index afd895a6..2d6ee53b 100644 --- a/ctrtool/ncsd.h +++ b/ctrtool/ncsd.h @@ -25,7 +25,7 @@ typedef struct u8 additionalheadersize[4]; u8 sectorzerooffset[4]; u8 flags[8]; - u8 partitionid[0x40]; + u8 titleid[0x40]; u8 reserved[0x30]; } ctr_ncsdheader; From b53b2ce8fea4a4e25fdc971ca865425c5e9dcc12 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 6 Jun 2017 12:57:10 +0800 Subject: [PATCH 208/317] [ctrtool] Fix typo. --- ctrtool/ncch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 14ff9a93..ff3f6696 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -735,7 +735,7 @@ void ncch_print(ncch_context* ctx) else memdump(stdout, "Signature (FAIL): ", header->signature, 0x100); fprintf(stdout, "Content size: 0x%08"PRIx64"\n", getle32(header->contentsize)*mediaunitsize); - fprintf(stdout, "Title id: %016"PRIx64"\n", getle64(header->titleid)); + fprintf(stdout, "Title id: %016"PRIx64"\n", getle64(header->titleid)); fprintf(stdout, "Maker code: %.2s\n", header->makercode); fprintf(stdout, "Version: %d\n", getle16(header->version)); fprintf(stdout, "Title seed check: %08x\n", getle32(header->seedcheck)); From cbfa5398a921817064dc2dd3862d654c408bde7f Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 6 Jun 2017 12:58:49 +0800 Subject: [PATCH 209/317] [ctrtool] Fix compiling on Visual Studio. --- ctrtool/syscalls.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ctrtool/syscalls.c b/ctrtool/syscalls.c index cd37cf81..f4505357 100644 --- a/ctrtool/syscalls.c +++ b/ctrtool/syscalls.c @@ -139,8 +139,13 @@ static const char *const syscall_list[NUM_SYSCALLS] = void syscall_get_name(char *output, size_t size, unsigned int call_num) { +#ifdef _MSC_VER + typedef char StaticAssert[sizeof(syscall_list) / sizeof(syscall_list[0]) == NUM_SYSCALLS ? 1 : -1]; +#else _Static_assert(sizeof(syscall_list) / sizeof(syscall_list[0]) == NUM_SYSCALLS, "syscall table length mismatch"); +#endif + if (size == 0) { From e5b79babc8f0a958d1ba0a6d14e0d62bc7bc8f2f Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 6 Jun 2017 14:00:46 +0800 Subject: [PATCH 210/317] [ctrtool] Catch failure to load ncch keyX properly. --- ctrtool/ncch.c | 55 ++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index ff3f6696..67e684b8 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -520,6 +520,7 @@ void ncch_determine_key(ncch_context* ctx, u32 actions) u8 exheader_buffer[0x400]; u8 seedbuf[0x20]; u8* seed; + u8* keyX = NULL; u8 hash[0x20]; ctr_ncchheader* header = &ctx->header; struct sNcchKeyslot @@ -589,44 +590,36 @@ void ncch_determine_key(ncch_context* ctx, u32 actions) // setup second keyslot seed (romfs, exefs:!(icon|banner)) // - get keyX - if (header->flags[3] == 0) - { - memcpy(key[1].x, key[0].x, 0x10); - } - else if (header->flags[3] == 1) - { - if (settings_get_ncchkeyX_seven(ctx->usersettings) == NULL) - { - fprintf(stderr, "Error, could not read NCCH 7.0 keyX.\n"); - return; - } - memcpy(key[1].x, settings_get_ncchkeyX_seven(ctx->usersettings), 0x10); - } - else if (header->flags[3] == 10) - { - if (settings_get_ncchkeyX_ninethree(ctx->usersettings) == NULL) - { - fprintf(stderr, "Error, could not read NCCH 9.3 keyX.\n"); - return; - } - memcpy(key[1].x, settings_get_ncchkeyX_ninethree(ctx->usersettings), 0x10); - } - else if (header->flags[3] == 11) + + switch (header->flags[3]) { - if (settings_get_ncchkeyX_ninesix(ctx->usersettings) == NULL) - { - fprintf(stderr, "Error, could not read NCCH 9.6 keyX.\n"); - return; - } - memcpy(key[1].x, settings_get_ncchkeyX_ninesix(ctx->usersettings), 0x10); + case(0): + keyX = settings_get_ncchkeyX_old(ctx->usersettings); + break; + case(1): + keyX = settings_get_ncchkeyX_seven(ctx->usersettings); + break; + case(10): + keyX = settings_get_ncchkeyX_ninethree(ctx->usersettings); + break; + case(11): + keyX = settings_get_ncchkeyX_ninesix(ctx->usersettings); + break; + default: + fprintf(stderr, "Warning, unknown NCCH crypto method.\n"); + ctx->encrypted = NCCHCRYPTO_BROKEN; + return; } - else + + if (keyX == NULL) { - fprintf(stderr, "Warning, unknown NCCH crypto method.\n"); + fprintf(stderr, "Error, could not read NCCH keyX.\n"); ctx->encrypted = NCCHCRYPTO_BROKEN; return; } + memcpy(key[1].x, keyX, 0x10); + // - get keyY if (header->flags[7] & 0x20) { From b14842897c14fb86048e70bc0cf0331dfd6358fb Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 12 Jul 2017 16:34:07 +1000 Subject: [PATCH 211/317] [ctrtool] Fixed decryption of exefs. --- ctrtool/exefs.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/ctrtool/exefs.c b/ctrtool/exefs.c index 58e344ca..51e522f6 100644 --- a/ctrtool/exefs.c +++ b/ctrtool/exefs.c @@ -73,7 +73,7 @@ void exefs_save(exefs_context* ctx, u32 index, u32 flags) u8* decompressedbuffer = 0; filepath* dirpath = 0; - + // determine offset/size of target offset = getle32(section->offset) + sizeof(exefs_header); size = getle32(section->size); dirpath = settings_get_exefs_dir_path(ctx->usersettings); @@ -87,6 +87,7 @@ void exefs_save(exefs_context* ctx, u32 index, u32 flags) return; } + // create new file memset(name, 0, sizeof(name)); memcpy(name, section->name, 8); @@ -108,11 +109,24 @@ void exefs_save(exefs_context* ctx, u32 index, u32 flags) goto clean; } + // seek in source file to location of target data fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); - ctr_init_key(&ctx->aes, ctx->key[0]); - ctr_init_counter(&ctx->aes, ctx->counter); - ctr_add_counter(&ctx->aes, offset / 0x10); + // do decryption prep + if (ctx->encrypted) + { + // setup aes counter + ctr_init_counter(&ctx->aes, ctx->counter); + ctr_add_counter(&ctx->aes, offset / 0x10); + + // setup key + if (strncmp((const char*)section->name, "icon", 8) == 0 || strncmp((const char*)section->name, "banner", 8) == 0) + ctr_init_key(&ctx->aes, ctx->key[0]); + else + ctr_init_key(&ctx->aes, ctx->key[1]); + } + + // if this is file0, and compression is set or forced: decompress section if (index == 0 && (ctx->compressedflag || (flags & DecompressCodeFlag)) && ((flags & RawFlag) == 0)) { fprintf(stdout, "Decompressing section %s to %s...\n", name, outfname); @@ -131,12 +145,9 @@ void exefs_save(exefs_context* ctx, u32 index, u32 flags) goto clean; } - if (ctx->encrypted) { - if (strncmp((const char*)section->name, "icon", 8) == 0 || strncmp((const char*)section->name, "banner", 8) == 0) - ctr_init_key(&ctx->aes, ctx->key[0]); - else - ctr_init_key(&ctx->aes, ctx->key[1]); - } + // decrypt if required + if (ctx->encrypted) + ctr_crypt_counter(&ctx->aes, compressedbuffer, compressedbuffer, compressedsize); decompressedsize = lzss_get_decompressed_size(compressedbuffer, compressedsize); From 61bf664fdacca5d0c2aafcb5e28faf5253e772db Mon Sep 17 00:00:00 2001 From: d0k3 Date: Sun, 29 Oct 2017 14:58:55 +0100 Subject: [PATCH 212/317] Always link compiler libs statically on Windows. --- ctrtool/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 8f07f97e..798f688b 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -15,14 +15,14 @@ ifneq (, $(findstring linux, $(SYS))) else ifneq(, $(findstring cygwin, $(SYS))) # Cygwin CFLAGS += -Wno-unused-but-set-variable -DUSE_FILE32API - LIBS += -liconv + LIBS += -liconv -static-libgcc -static-libstdc++ else ifneq(, $(findstring darwin, $(SYS))) # OS X LIBS += -liconv else #Windows Build CFG CFLAGS += -Wno-unused-but-set-variable - LIBS += -static-libgcc + LIBS += -static-libgcc -static-libstdc++ endif main: $(OBJS) From acb110323e1b89c20c14002ca9f4b6a77dfabf62 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 12 Jan 2018 21:00:53 +0800 Subject: [PATCH 213/317] [ctrtool] Fixes issue #60 --- ctrtool/exefs.c | 2 +- ctrtool/exefs.h | 7 ++-- ctrtool/ncch.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 96 insertions(+), 10 deletions(-) diff --git a/ctrtool/exefs.c b/ctrtool/exefs.c index 51e522f6..465bc8b3 100644 --- a/ctrtool/exefs.c +++ b/ctrtool/exefs.c @@ -315,7 +315,7 @@ void exefs_print(exefs_context* ctx) u32 sectsize; fprintf(stdout, "\nExeFS:\n"); - for(i=0; i<8; i++) + for(i=0; iheader.section + i); diff --git a/ctrtool/exefs.h b/ctrtool/exefs.h index 2d1c4102..fd6e8f09 100644 --- a/ctrtool/exefs.h +++ b/ctrtool/exefs.h @@ -7,6 +7,7 @@ #include "filepath.h" #include "settings.h" +#define EXEFS_SECTION_NUM 8 typedef struct { @@ -18,9 +19,9 @@ typedef struct typedef struct { - exefs_sectionheader section[8]; + exefs_sectionheader section[EXEFS_SECTION_NUM]; u8 reserved[0x80]; - u8 hashes[8][0x20]; + u8 hashes[EXEFS_SECTION_NUM][0x20]; } exefs_header; typedef struct @@ -35,7 +36,7 @@ typedef struct exefs_header header; ctr_aes_context aes; ctr_sha256_context sha; - int hashcheck[8]; + int hashcheck[EXEFS_SECTION_NUM]; int compressedflag; int encrypted; } exefs_context; diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 67e684b8..7099788c 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -182,6 +182,7 @@ void ncch_save(ncch_context* ctx, u32 type, u32 flags) FILE* fout = 0; filepath* path = 0; u8 buffer[16*1024]; + exefs_header exefs_hdr; if (0 == ncch_extract_prepare(ctx, type, flags)) @@ -215,22 +216,106 @@ void ncch_save(ncch_context* ctx, u32 type, u32 flags) case NCCHTYPE_PLAINRGN: fprintf(stdout, "Saving Plain Region...\n"); break; } - while(1) + // special crypto considerations for exefs when two keys are used + if (type == NCCHTYPE_EXEFS && ctx->header.flags[3] > 0 && ctx->encrypted) { u32 read_len; - if (0 == ncch_extract_buffer(ctx, buffer, sizeof(buffer), &read_len, type == NCCHTYPE_LOGO || type == NCCHTYPE_PLAINRGN)) + // read header + if (0 == ncch_extract_buffer(ctx, (u8*)&exefs_hdr, sizeof(exefs_hdr), &read_len, 0)) goto clean; - if (read_len == 0) - break; - - if (read_len != fwrite(buffer, 1, read_len, fout)) + if (read_len != fwrite(&exefs_hdr, 1, read_len, fout)) { fprintf(stdout, "Error writing output file\n"); goto clean; } + + for (int i = 0; i < 8; i++) + { + + // get section size + u32 section_size = getle32(exefs_hdr.section[i].size); + u32 section_padding = align(section_size, 0x200)-section_size; + + // skip empty sections + if (section_size == 0) + continue; + + // select correct key + if (strncmp((char*)exefs_hdr.section[i].name, "icon", 8) == 0 || strncmp((char*)exefs_hdr.section[i].name, "banner", 8) == 0) + ctr_init_key(&ctx->aes, ctx->key[0]); + else + ctr_init_key(&ctx->aes, ctx->key[1]); + + // set counter + u8 ctr[16]; + ncch_get_counter(ctx, ctr, NCCHTYPE_EXEFS); + ctr_init_counter(&ctx->aes, ctr); + ctr_add_counter(&ctx->aes, (getle32(exefs_hdr.section[i].offset) + sizeof(exefs_header)) / 0x10); + + // extract data + while (section_size > 0) + { + read_len = sizeof(buffer); + if (read_len > section_size) + read_len = section_size; + + + if (read_len != fread(buffer, 1, read_len, ctx->file)) + { + fprintf(stdout, "Error reading input file\n"); + goto clean; + } + + ctr_crypt_counter(&ctx->aes, buffer, buffer, read_len); + + if (read_len != fwrite(buffer, 1, read_len, fout)) + { + fprintf(stdout, "Error writing output file\n"); + goto clean; + } + + + section_size -= read_len; + } + + // skip the padding + if (section_padding) + { + fseeko64(ctx->file, section_padding, SEEK_CUR); + memset(buffer, 0, section_padding); + if (section_padding != fwrite(buffer, 1, section_padding, fout)) + { + fprintf(stdout, "Error writing output file\n"); + goto clean; + } + + } + + ctx->extractsize -= section_size + section_padding; + } } + else + { + while (1) + { + u32 read_len; + + if (0 == ncch_extract_buffer(ctx, buffer, sizeof(buffer), &read_len, (type == NCCHTYPE_LOGO || type == NCCHTYPE_PLAINRGN))) + goto clean; + + if (read_len == 0) + break; + + if (read_len != fwrite(buffer, 1, read_len, fout)) + { + fprintf(stdout, "Error writing output file\n"); + goto clean; + } + } + } + clean: if (fout) fclose(fout); From 148811d8b2daefc03e896bb8e83ef06d2172943a Mon Sep 17 00:00:00 2001 From: Jonir Rings Date: Wed, 16 May 2018 01:04:33 +0800 Subject: [PATCH 214/317] makefile fix: make complains "Extraneous text after `else' directive" and cause incorrect platform judgement on osx --- ctrtool/Makefile | 4 ++-- makerom/Makefile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index 798f688b..be6e85b7 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -12,11 +12,11 @@ SYS := $(shell gcc -dumpmachine) ifneq (, $(findstring linux, $(SYS))) # Linux CFLAGS += -Wno-unused-but-set-variable -else ifneq(, $(findstring cygwin, $(SYS))) +else ifneq (, $(findstring cygwin, $(SYS))) # Cygwin CFLAGS += -Wno-unused-but-set-variable -DUSE_FILE32API LIBS += -liconv -static-libgcc -static-libstdc++ -else ifneq(, $(findstring darwin, $(SYS))) +else ifneq (, $(findstring darwin, $(SYS))) # OS X LIBS += -liconv else diff --git a/makerom/Makefile b/makerom/Makefile index f7ae6262..5192bf4c 100644 --- a/makerom/Makefile +++ b/makerom/Makefile @@ -10,11 +10,11 @@ SYS := $(shell gcc -dumpmachine) ifneq (, $(findstring linux, $(SYS))) # Linux CFLAGS += -Wno-unused-but-set-variable -else ifneq(, $(findstring cygwin, $(SYS))) +else ifneq (, $(findstring cygwin, $(SYS))) # Cygwin CFLAGS += -Wno-unused-but-set-variable LIBS += -liconv -else ifneq(, $(findstring darwin, $(SYS))) +else ifneq (, $(findstring darwin, $(SYS))) # OS X LIBS += -liconv else From 046bb359ee95423938886dbf477d00690aaecd3e Mon Sep 17 00:00:00 2001 From: Khangaroo Date: Sun, 3 Jun 2018 19:02:50 -0400 Subject: [PATCH 215/317] Always assume exheader is 1024 bytes long (#70) * always assume exheader is 1024 bytes long * oops --- ctrtool/ncch.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 7099788c..781cc96e 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -416,6 +416,12 @@ void ncch_process(ncch_context* ctx, u32 actions) return; } + if (getle32(ctx->header.extendedheadersize) != 0x400) + { + fprintf(stdout, "Error, exheader is 0x%02x bytes long, expected 0x0400\n", getle32(ctx->header.extendedheadersize)); + return; + } + ncch_determine_key(ctx, actions); ncch_get_counter(ctx, exheadercounter, NCCHTYPE_EXHEADER); @@ -619,9 +625,9 @@ void ncch_determine_key(ncch_context* ctx, u32 actions) // Check if the NCCH is already decrypted, by checking if the exheader hash matches // Otherwise, use determination rules fseeko64(ctx->file, ncch_get_exheader_offset(ctx), SEEK_SET); - memset(exheader_buffer, 0, getle32(header->extendedheadersize)); - fread(exheader_buffer, 1, getle32(header->extendedheadersize), ctx->file); - ctr_sha_256(exheader_buffer, getle32(header->extendedheadersize), hash); + memset(exheader_buffer, 0, 0x400); + fread(exheader_buffer, 1, 0x400, ctx->file); + ctr_sha_256(exheader_buffer, 0x400, hash); if (!memcmp(hash, header->extendedheaderhash, 32)) { // exheader hash matches, so probably decrypted @@ -825,7 +831,7 @@ void ncch_print(ncch_context* ctx) else memdump(stdout, "Logo hash (FAIL): ", header->logohash, 0x20); fprintf(stdout, "Product code: %.16s\n", header->productcode); - fprintf(stdout, "Exheader size: %08x\n", getle32(header->extendedheadersize)); + fprintf(stdout, "Exheader size: 00000400\n"); //this is always the same if (ctx->exheaderhashcheck == Unchecked) memdump(stdout, "Exheader hash: ", header->extendedheaderhash, 0x20); else if (ctx->exheaderhashcheck == Good) From 52dbe5e187a2d5b1ea2fbf6537db7cc058d09f66 Mon Sep 17 00:00:00 2001 From: Steveice10 Date: Fri, 6 Jul 2018 17:01:51 -0700 Subject: [PATCH 216/317] Only extract contents marked in the CIA content index. --- ctrtool/cia.c | 32 +++++++++++++++++--------------- ctrtool/ctrtool | Bin 0 -> 279024 bytes 2 files changed, 17 insertions(+), 15 deletions(-) create mode 100755 ctrtool/ctrtool diff --git a/ctrtool/cia.c b/ctrtool/cia.c index cac32601..a0347633 100644 --- a/ctrtool/cia.c +++ b/ctrtool/cia.c @@ -102,23 +102,25 @@ void cia_save(cia_context* ctx, u32 type, u32 flags) 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); - - contentflags = getbe16(chunk->type); - docrypto = contentflags & 1 && !(flags & PlainFlag); - - if(docrypto) // Decrypt if needed - { - 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); - } + if(ctx->header.contentindex[i >> 3] & (0x80 >> (i & 7))) { + 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); + + contentflags = getbe16(chunk->type); + docrypto = contentflags & 1 && !(flags & PlainFlag); + + if(docrypto) // Decrypt if needed + { + ctx->iv[0] = (getbe16(chunk->index) >> 8) & 0xff; + ctx->iv[1] = getbe16(chunk->index) & 0xff; - cia_save_blob(ctx, tmpname, offset, getbe64(chunk->size) & 0xffffffff, docrypto); + ctr_init_cbc_decrypt(&ctx->aes, ctx->titlekey, ctx->iv); + } - offset += getbe64(chunk->size) & 0xffffffff; + cia_save_blob(ctx, tmpname, offset, getbe64(chunk->size) & 0xffffffff, docrypto); + + offset += getbe64(chunk->size) & 0xffffffff; + } chunk++; } diff --git a/ctrtool/ctrtool b/ctrtool/ctrtool new file mode 100755 index 0000000000000000000000000000000000000000..484368dfcfdb5c3c2a3f19265852a7e7e0c8a2e7 GIT binary patch literal 279024 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

C-H!Uv`HEcCcq!OfEqLKYdpS8U?cxmeDQ&= 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

>Hvql zAodU+L&LcB5m=YK&7m&XVngZz-Pi3rRF@ruEx8zUH7FF5<8JUWGn+?jk|?uKW(jcS zKqF(E`Q4Ik6hDVb&pxcwWP^XcUfPY;vM#35XzZ4e4(y|%U};EbmU%IfU`n50#PdS; zLLBP$40C0;oLGX(w>;^=pIG!gW^@514`u!ba2b^>xE?ZduYSj~jB}DPl_!0JCmn+l z@D=XfNUN-jSM_lzL+!q@q1c)^748uPTm(|H#50kVaR&N6yLTGnU+B9RXwehnpB@xBajeirAAkAShSGBod*@J5kV5QgMNPcOp`a#Mfk=N5d!6x!0 zavvVQtYJa3uVp~8T%FyU({r@hxaF{*ErRUoGL-7nJgs0rhJt0 zct21Es5o3=u)P2Yl$sHO82u?q{0R}?$k}$ZLfrFtn;gbFU$q!Vld!Nc`WaO2S~ktJ z>~~t$^VGonR2$|Kp-Oy;G6WZIuRw~>82Iq~UM{%asm35+f$!q*{Xcid=&G>Q%cvG} zQ{j^P@nwZ@)5Lf{5MvbgTAlwZsY$ZNDkx8^AFwR zWLM+Cp&HQ#o&vsa~ zgZVw3v{!oPv3;rA)li6X%h#W4qGVVS!}0^UeNsuJggr)LmgTxRHxSVXLey>y1aBit1JO z*tM863(`>X`#+pdPW}n>;ufSDWAvi7)aRucm8_gOuYy3;^(o~4=$Q|IhN<4fr*V%q z-4OxZse#1IdNpOe4Uyn6h%L<;M``gXlsOSpT+vL0G#J*S8Rti+ti1G?byDijVG~1n zPE0|9&>cvclLkuDAj8`*1z86PQMDH&(g=7SsbG0e1!n9b8hrnIkVMa%Ysx%|=leNe zlN;*_dZwT9y&W8Ikb}2x&Rd{juEtD?I|E70lmCw+#w_k#6!{s|?#h2(M-`^$%m#$6 zu;dAJ`XY`w=soK zy^`G`-R}gu*mg-6gBmwsXt_>5F20K0%SZ2g{5F7+#!vE^1L$w(L_3k5b3f#Y>;zNH zlXcJ49Y?-8uN9-K8YDLTMr4(6je2AUzQ|(ZuX|^IJ=9%Qw#B|L4AIqBmhl(rj#$h3 zg0iY}dW6gB!%51juo%zMd;!5u{8TBvtbS`!86&D8ac#;62w3t{l@C}FQ=hO8{n-7; zG_yR6?`D=El|>WOIF|xI*THo}dmd+}VeUi)q9dNt13lxQaWVRUs%>J646h_t55|V`!T8?-007bV9~7=C=#xBgLWDYkv-YRa8r`@11k*sbfpQ&Lj9b<-TucU z;bZ<7*1=&)lP`fDhLg%V>-X3JHh!iS36<4tz{J=V>4swPc_;?Ra)?OZA|axyfwB_x z89U%UB+^2AI$hQydONp!n2!6C(AkOACSx0@&GCq;gSV1=fq`zDida)Khe)VvQA~&k z6$T~1$M8V{vx zpuuhME6(Ti;Tr$@qo}b}zaZ%u#^-qVXl)+lbqc4lFkbrQ>Q`Q6ypKrTcPR|n9EY;1 z&3FxAyr0jRKFveU4U_q1-Bj$xn!BmVXa^T* zD*8c*O-B6Rn7!cc62JGBjc44&h#MwFRpAcV>8KHWD!lI$V-!-T`p|={xiswL4m{-5 zwje6rxC&9;z+7hqE(xPv05R~*N4&b4-uVaWxhcLY3UCeIxy&h|n`P;uH}hNHsJ@C} z{QE8nXN0-fWJ%P(y%q4pKS5`Pznp|u{1OCSufrGwY*e4w*$-AY-!J{k5K8wf$=yoQVwdWA2f!? zIMR;dhQ?7~IBpM2_{sQ=A-H9KtHQ#Y{!y~H-4He-0gt5CWku#$ALrrO+-}ig7X6kY;kQ<*0rL;p^Z*G|q9y zPJvAydqxs2Kj~#}KzB*g#tuv%&#x~jP<_syjb5x^tf($t)T2kJ@X?~OUqy@QCu1F= z3-rkoVA-(c^%_EW8@R=-O-nVNMrh5$PVYl{pDiTmNBgy=BVX6=kAt9t*yoD4&z$=a zX!k7EQ_LQVpFm;t5C7*I$^XeZjqKkNNQ^{gXJK0|xF5j0Uz9F8qwc6`bU_c4eW67I z)Hq4Ak*Z?7>ltz-sduUPJ(ik>S%^BAhPTkYj!;Tt zfuG>BER|i9l&|~ysdSr8xibyk5na#<+n!)#&}tM;h0oXOao0jPl2E4Qp2T&uW!m+} zz1d&+Kc;$M;-@Pc!P%p19E&Y>{mLr50_D%ZjY}aezNl~rC7tS5^?Wg2WI^~AO911J>o3QJQo#(CCKiT*tzG^w? z&2I9)S?1R8xi9SrkpEE_sz{+wQdyoh1H@>ZEHxXgtpv+3Utolxs#NVL`a=>#FCTc+ zeo`w;)hbd*A01%+J=c!dQl}0H<;}O${h2Vn)O#(X0heri3d{RSGe!f>2}Lchs$ev1 z`77LkH;r@aNXd7aPmA$DcjB{&b>IM6VSbWjFrZJ+&&A>1VB;yQHn30EwePW@t!+Z% z^`h_2`F&-^htQtXNBX0Wm|i1316OK1)OF!cW#Hz5Bf&O!K*}Qxh6_fD$Xg*TAVe9r zV&RRTF9D&I9Kr2M&5w8oH#7$J645CV+^AGX#_j_9L3k*GBW(G|^e5n7!9FP~e>O&A zu@I}3IhfD6vvKv7vak*ew9mo8TNHei)P%2e7HZy_fxJl zd`t%H#okFTjj$4=AjI|amOY>U3;TW-ar%AxPWk_@+xL3tg}N^6J3jegSMPbXw%8FH z+^5X6GOPv6<_=Pc%1V`j`Y%s|BK}res1i0Ew)z(=+eB@85{~a9FJ{vR!KQx) zn?4;j9k&1NR@ihqv+1-W)XApL>vf&pFKW|6_on0z{D$m0oC5s9jOVN*qZp}K7eoh1 zFh(JSLDr9~_%MC!pOZZ3oe6IHr*3;AdKfOooBJnb%+r`LpQjqvB1NZiU59c-1|T}> ze;LOPk^L?c_WK}0G5fs-p^pq?-cNkq=t2u@PV2%>_x?h%)BAd27+s>H3`o@3PDjr- zVgP=&8|lc4H0Ki6Qo6&7e=i;P7cA=)E@frM>LMhzpA4KVQf_D^t%(~@6z^8%KUn+$ zXg*o%3HyJDbuH#yejDv&pHnXzZhDfagh8PZsieCerwF)I^wUEp+}a^pa=~@m_+b*E zbnqLGSH4eTklW#)8`-a>w^Vm%Tp!QCqjccs(J&T&@C^;vre!xNi-r?VWI_DN^~7AX zKsylm08GTZ4zVf9%6#Y5G)q0`!DWMKBlV$G)yIlh@iFE8Ycf9P9I;DTxhd2KtKqm$ zXB*HVKw(bf`z>O0fn%toUHY)@!QE~}{St%5+fd&g-&Sw;B?out_rxpSqxBte_V%{= zINvGSr}c;W+duuN!`^OhXsb`a#;Ue|V1wOgt53p79J0}FtREZDJwcF?pN)D@YDhm9gc6(u_SOP@3cOJlu3TCL?*r-&lNc!GBIc>27+bNp zMZaB$vtoygKOsQYJPDN({FzzS9_Xu95@lwXZ{!LX&kEg|Jj~yd6E({d)jl?Z|rG(H#E-qnowrO@Q>Erc zL~~o3Pj5_Tk~a*Cijzw?@^bGaGN*emV2#fs%$#B~p3Es!APy$Kig#7dQ=z74PC?UX zdZx6*;~26R7g>664Snd$>-C{4VdK~=74uds9VDpn^D6QO9)_J84(r-0KQt{RX76So zE3$W~bnx(Y1mGq0@`kQu^J${jh%+`(HqsYA?2LQ$9!?0&PK9TI%jCgea0Si&K-`K(E{Fj(kfGK177+8w+ zQB&XWm5omJi_Y5f`Rfg=Ibpxh;7SdCOF%U4)p!ftl(Pg8qtR| z$K%wjdLDXiz7v~_xS|Y4r7l!4DaFZg=N81mjKSIR4%~d5p1|A{YHBRf;71mCPkdzzUJL4`a!T>Z<@m(K6gCz$}1f z&e~RbyRb(q6CqNCC}ZJ$rv5S8^S`qnr(%)g59VVtumnjm{g?KmS&&=iV}JkOvmXtt zF8&|qV?maf*^hTV`i=cqU_*DM$`C+D#y zI*vl$L%h{Nn<_>-q(SJ77CZ2Lh8ICvn5CY;hKAO_6ejiBX~=a0t#(qNm2!kPN*j#E zbB|D$%`3vOlA1?BFcgQmpQfjM*4cCm_#R^c(vtr*bw1{-qp63(Ut;$=9x^gnzw!Nt z*wGL17-PYX|9-*El#6x<6EsA8b;(#~61#`UxEDDE@(_CI+P>q6jCRs)!x!Lo zADu0nO#yrpj-OpkEa4GxzB3Swb2y2m0C3{&Dcp4+&KR;uYrZ~vsIaoEQ{Y~)$+)EN zi1GAZpcNpc+a~KXYHKYCg}1|&g&nXZ!^v!f$2m5 z-Gm45ae$zI81(EYz|nhI{KZBIkH1HX|D4AUGS1`iH%swrd3?HI=keD_@sIQPE6w<` zr1*Pye3tR`*G#^n?{}8(X2etBpXJfJdGr!f{2@HK%earnuan}>=J6gg{s}4m*gPtJ z9;We)%nljXa6XlsZn2rqPDY2x{aHMIj1>PS#fLJ70_KMUh5gr-jg%@3nCsw1UibIIT%sKk8`Oe z=8ZS9xFmP+Wba`2l691ic>Dw-mB)|b@u!)R{)@*?GTQgBl4MBnf2H_P&RKxPS0o{@ zHtu0?UIo)PlcBE2AuPr-SI@toA9hgTI`q-6$5_Wn8oG3|8d?R_msjD{KE|;z8cz(#A;;}a)ikwUG7ma)tg#}d_Q`aIeLic6D z-8>3OE8fK+H2fy7o_X>ptGW-NA=J>k3i^T1D_Fvue9(ZP;Do58R~bo8CSdd1no)=| zmc)`G-z4MbuUKUy^W-DV?}{jj7``%q zz;hU25R`Y}|NX7~oHJ+4?ms^tne*(|wbxpE?X}ikd++~H^^25V{`Kn-cv4iuzq{%$ zQ;y!xD^x#;VhBpy$Td%rWwf>W1Ib9|E{W;P)1XAtyn{4{@>rtr6!k;i3o#wPc#ph` zN1@HJ;WrGt-nLUX4-(P&E!E-bAJJOwmNDU>cQq(N&rsSqlvRF1WjNf`AbY|yL#SZ2 z!be<Z`R9OqLCE*G5ogFuXH!W6To(Alz!(?0A z2D|lG%hpnArl?vs`f9wuR~^!RYiOY4hST8&n&EbiC0)ZrVZ3MLP8o$pY$HBZV-wuj z-q!ED0W{}o5Fa4upBl$1*>JxVvX&~t4evyIbq#lPBo7GxOtK-A4n|n(?`Wu{t=nR3 ztqcy&BQt*n@Rr&SKoWH_S)DlN;n5T-?P&Zzb#%igI}$!!OJ2AUEj0foOa#8Md6zWN zn$!h(I0|Qa5>rnXmnmj$O*oL9JG=c;AA|Ptp(|c&KXyycXI5On=Gm@r#f_AQ+Fow< zER4r|8388j&rj$S&lB8K`-yL_^LOX-2C0jJ6?W_n|CK#K^FTA5d81dD(=l<@Tfq_R zCENQq9S_RX$@%(WDtMO{Yf#L1bHvyd<)}{fRJiFTSOO1HpY-TO4PVHP9|a?I8;@;w zzCY|w)_&u{K_yNvr5`sJqa!I%|!kmqQ%` zMWm&5^vC-;wO%*$=fe3WNrx#*R?}%%_Liex(A*IZ7M` zI$PdE0JxXjCN;9QaeQvlZbH=>-b_4|-ADSTT%2w4Y$fnneVVzT!SVIrd5X>bp zK20+q3gb-eUi2}a4P(7rqZStqEPGQHXnAq-J@M2i_l{;utajQDIk38*VN2b#|13%z z`kV}@kOv>f8aAgUABfdohED?U_s|C4UFyHdzvcB8;=4$`!2d+704EnxlwU!KX&vt| z_H5XEY2CDjMD4WO`qWNqsF;@NTZpFUz%Q-L0l7>E-f;Ol-Qb7hJoRcAF7fzwPIhw} zI&b+To?7m;v=8DLrpmUTm!6*~*n@BbS3;lhiHIGK1fDtJ3G7?ZJ6}grNb&4$upe1} z_LW`#U{^XKZ%l59+pcYp7H+K99wu)PhVy(yv!OlwHTnMN*2L5=Ghd_geHEh@!rwW1*|Fhh;83?8IuZz;Jgu{yI6{8n0{>qZ^FWIf}*K?=q-1R_ImYBMcf3xEd8;PlJ z^HW!KbNOhqd?vEy?2M)GfBJrBkS{=3YAch>e1v>1UfUcVL!)UbVHx$QPUr5j>y^yc zf@DfGT_*vYArv-Qtz*gYDsk?w@=MPwPt~JTv&Y@P^t#YMw8liH_R8H}K#rD3V za;5xygvvdna>taEt4UGL5A?v;+>cCMocVot;G3uBI$5qr6^i3tvP$*6DuPwkvQZ3>P|6&+A_l7C+b*u~&Q81?c_J zdm6rrHa#3>;{=O3=*^ypHVVaxSo2Zwec`wqI)1JP|&_H=1a=o1*@(hqPkm?*~s1@4)jQyL53p?QM|#Wnpq2r3rgx4`+Ay zFoVcz?Zc!|eX<9qy^1P$_?)xKl? z%Ms({o=0ME2^(u$cw(_?QdOm%BpzSD`Af$i#r`Cx)!!)fx9hb+e+TybTmAJ>ZR>At zv1*H|{>%QhN8_8I{_ZGNy6fNT@5AEw&M8(s|Nrbyr%pA#iR$n0VkO0c{Tuk)Qta>T zR|}|Fi%$5@d-ih)*4->*v89JvBRL$7V`{Q| zs$-Up@G9#TKk{RmcD5ft_kQB>RzENXG&S2Nt}>Xr6n+eEKvu9ByvnYhS)-`jDS}s% zpgsID&UaHxq&jsR??^Po1zTsssvt+un{v;uCO?9~&pn|z8NRqefNFNP2ZYU3bN&KV zDgU3qkNgZz)V#>e6+=c9HJtlZN!rfB@PBY!MfEkD@TaPMyw!%Y!%JO#F<43ci#qCm z&DGb|sMSAP^>skOW=5EG^~ErBoPW|XY9;r-|149rbf0|!5oclcdy3i3uNRxFxjJg4 zWPX(#NUx*}Dxt-3+Btqs8I1wS<`;zVnPkQIyP`Ep6dtNViJO$zI}&hg-2IVZ$^;!+ z;16E0&q@ZKczh9STV;5;OA--W(tyx+Ng5tS28Cnz)=FDh$G*aV#N#1hW`k2xK*FIO zi96pbgLiaef<9j5Ly~Y1*LJ&<=Se9P)&4{sta2$hsM8H42)*!J`;aaZj^k%Q0PK=* zG{4yDn85K~T@^4U@l@jmz-eqP*FHxZ?P&EJ!nvYZ1SW|6&O-Vjj>VtXV5bgDboC2mBFZ-du@ z#yilI$>gFj;3|3_#3WeX4&GPW6sFn99M=O-Lcbc#Q zEl#vn{zfa2{);auJp4N%(C)CW{XGqK@goe5sRu0cp9|o_z53BdDBJ=TI z%`UV1vSU58$eDm6HEuG0x$@P7*mwq*Y@TCDK|8k~$oruk;0XBk@JYIq>ENQd+r#zf z^j`M9>S<~Prn9Oi->VpG@8m`S&ec15ruaTPN}wW&_wY`>SJyO9L{VE5arYbXa7x$u zlb4JeMUhYA;lIP9op`ul*D={P(a@}}4fDCW)b=}Qg%pN1x1PV@3CqCU{Fcpq8~0T< zTtBO_o-M~)CWH$xjm_uLQ%x)7FG|Ty=;>w4+Wdlk9D%gUdf_1J74RXlbL7*=&MWD~ z;U`z~M`DJ;^T1L5bV_v4KiRAYweg{#8WS=faGW!^fepl4hU5oQa60YVbPxbn6hL}F zs8Pzfr0^o#ITQH@leAIhM`9RLBQdONwWQ0hNYI^$+TS13%Z-0b?}i^*?U{x4?kATP zsKwz9H+DY@hFzw+sWx!57%_!2g?VxBZ45Mb(FDG0@hXo9f1(6L&=7@C z4%L?D1Z}@f>>NJ^ZjN{g+#FYen+d|r$^S*zz_?L|NY5VyhZpTFhPedW(sq=v*K%{O z2iow$7u4G=2-U^mD2*f9&}wu;86uH-g?u2{MwOqZbqvueF@1ev*j3e6gl8btBfcEn zcnxI|O;1q>JX|g3$-E)1Mq(HZk=OuY>#MI1ds7XpJX<}P{KT+Zt84S?4BV-Fi!2qR z|IYY6&+h{5y={!|f=M0YJB>M7I=-hd^YTBS71^VV_a=RRi*H(M`x~uQm9%!YTI*KQ z+E&Cew7*V6cL3<@=#H+&J&@jcWP_adet=KWH*ddC!Gx@iv4EJ9+z`k7o>@R5B;Wu4< z^KM1;-}|fi`=`7U?KYh5iik+zPh1ysU6C9u*sje{u82*Fv98DrSLDBoQ0)GeP@pv| zTG)h%!yG2Artq90kjc<+n-Y1yNAP{H)L=i`TDx}^FpzTj#Ws!(rerU3+)xkGm`7IM+})(9qDGof>+E zHLp4BGN#*2XHfS}Se97M;UT1J4wgF~HP4Xf+PZQ|Uhf^T$-RnQ;%5!&Vk6C+*poc~V+a?lCYSIT;RE z$b!#_zeLlIG&8a_<6N&Ff)&x9sxl~Yk4du*H!d)`9P7&Lp-gd>+~P`x3n=0m#W2bBI!eWE6gu?q>gD<->yGt89A!_gk#Rg}HVBCM^3h!_YmKPh8X%yDE25)m^ zc9k~x=HK!%D&R zM^|japA8uYON*r_mTi>&$~2Nvi!F6MP0pcg$BcbaK;%Y#7Fvg6nKoHdx2Yk$fzsJR zX$l=qAsXq=&uV@$c|9W~_Oz-*1r? zCAsLw*5x;9Epz&^bEJn0JLR8Sn!jBH|6Qm2cbNdi`phZr2kex8O-FvV;`e`Iq2Xr5 zq7ZtO$q=sp1`|ofb<|F_W(wsBGM`b8LmGq?3?*NQ-d!@kzN&JPh!m6p3 zz-Yy2^>=hhe=Ak!2K6TwRyht(km^4cz{SNe~w z&&hP9@`KgJWlCRFn(nmNtD42c0~fDl>b11w`!?>aY^az;WM;QHBt_Hz_CFezrD%vb zy!JAh>SN&4(*D4m6wE)^@%^s8@9OwoqVHQfzMs(dH67p2>iaUjJMUK%_L~bgnM~Q@ zOoE$g*>Nth^Xv2?LHG_pVOwYitK=rainLp>y0m%O&&DwhV z*rhQ4LlD`?WBf&EnzcF(g)X`#ybD7(+FmjHefS2xnThmZhVo#(y&Q+Wp24h;)o04T zd-z0#>)$+SwmYqwIV4XBie>b5E`&Q_N z392wbzjOCqVdL8**E4~cL&CHrKPintrQ{F7_b@y9Jcmzn1#d#Q%To}3)e4Nmfkhiw zD)O{6ke0n+lm*LAK>gZ_-<8soH=T4 zvi&ALP9IN3AB<#ocn2TW{qFE;e%hJE@cgOnp6x`_KOnu`U)l5=zt||Mk=es#LqNgU z*)XZ^sipVlU`-ccCYrur)HxL1_N@6?h6(Xiv?tA~KSIv+aOfeI5+kJ`PcXqoglAIC zVZ3K}_Bt?5lpVua!Pa=9=~dB&Vxz)iT`_qbt0^USVMecw{^fw{VR!heFJPYimd^tJ zi`?Fa2$C-!GP&lwR-fNO%8{gC&s4-`F+~*-T~HF=(thE%uZnGIwmhm$mCc1KR9;fn z&2TeWuUgiQLe@NGeciGIJ@?;}U&wq+nYCo*ml|-Whd^eYr0_d!KyRO#a^csCpNyOL z$cvv{^*P2q=OHVMlfS@czVTqmc#;3~`;5-z7RED5o!>B#TPAK96>|`QV6Gk(odL8b z6%ZlARO~_V7S6j$W^zyOWG-dOp3h+LGJ7ZUL;j9l>Ml}Kvo*^evC1F4XEOT>m#8|v zA&sA$!uU^nO5>;P{7XiAaC#`5@Qm1@VT|)>-}4zrH^7EcC$K8+rhcb)0?V`@Jw^}t zW)>K?j)~C}Kg;A|w!91(+n#W7&NWE~SsKW~MQ~33e7X*oT}0KQdyDti&GqytzJB>a zuhOgRJV3bb_8V{1<(qxyOax_#r(W}BUv%x&*IswiwG$<8`r=@aqH-TXO4o1t)rW^W zaVX-4@EY4<;b$(?Z#x(~dV$B8CcB@S#MI)hgBdD4X}FnDjiP0;F)O{$JnpL zyR@oAZZ1kO`pw_$_v+(Weew^K+(-L~`)-Jb>2!bAC-0;8f~T2I3eK1crtnsCXycXQ zp$)=G?arH_wZKq*35+$i4sh8%ek1u>$yz(LdJrWT#P^^U1AgFnbOdJdNkXJ#N;l>P z=M%jg-k(gF)67 z^edA%b4j7r_{5p*elR}+L)}J))NdHdQbMZ0{Rt?;+Z2 z`!n*ZCQZJe;TFn&*d&|MQ26>ujJp_5#kQkHF$?IA)* z`ELA}wy_-4_8{6WX_JN?olHY@*{3I#wD$|w9^)hbdYA8)~ikzK`TY_R%X`xw?{ae~!wHE0#;=p3@v4WFUpSl+m{ zhux?%arR^yUb&}fe*IEJhM(hl#yV;sBiS+j8ozYwCMP73s%RcT3#5Jon+Qu87XRsa zRx@g%?932bYVFVrnU94Ms~5xAneE4>k~R zCk_p>j}phg^(5)+-!;+XeP6({7d^vo0X>?acu;$SM3GgBDdD}!wox?hAq>7rYewJ6Mxk;2iHn0f) zq}mmQys_$SzEyKlv1WWB>kL(U)K!yUrP{DU-cVJ`S~VwZYx)+lhN;^1uA116YC9f` z#@0{OE_8WL=-0ea$QwxBrTMRr8Lbb|{r$;ZF7R?JxA%{V;OYI$5N3uSY_fGR{H$86 zQBwU0teOwevH(w_V>905%xXo}51&jPm^>EM=M#I{mu@+FnxcjUN8io3B-{Y3+ z@g$Ev+uY}bpi!s_pLL%;*&0ju65rXmYG+<8%pWugGvSS-y6={9Yt;*~B00O|mzHK3 z(SoeVN>*(#Yks-)<^@JNvW_ceA+s9|!{r75MqN0FOj1QIXLU(0$F`PY7TMM#AEQY2 zNp&O)e3{Selj>bxqgX!y2xZ zjo2_y0-i(sYr=`FDEYx|9-HCAVtXNR7tRP`DJToa7eBk`^UKB0uKGN__$iA??dAuN zq;*%|?ExO{k~8`3?7cg%7Yg?e&NYqY6x<|ElLQVg>k^M8(e@epT`?Gj z;=kg3vmpup$9=P(h!6j#c=FErH192Nmb0io2T!-sdPh8S@bvqgz5%y;qTg9V`P(be z@4Qh|tSgNti}D8*_M2_}(WN9Va7coUuI1~b6@HsUdlQQHpa>7`F)@w((KGxN zmVtit3A$E?)A`^yN>7xOZkJ%nq5Neam!Y+eAKBTa1#*P<%Y5men~ut!)-WN_SV2N2 z+oxLGsWW#&bQzRvD;6}C6d5~L;Qp|;4if!Pw0x!L*C_qt`Pf)Pc6twUrwP|UH8yt* zK9hL*Vs2Qvq$>|ZbRmv#+F9MRz4X>jZpxv92M_LkwQA{clliYcFaKlZ?Y(lPSS#KI%a()`-@FI#7+ z`d1yW620_SIkVp?U()5IZ+++5#b63^7f8#fL~Og+bdzfew~y>veY zBVV^8J!`L2gtyUk+fcKcN&>47h2!ZBijsUX9d!|RW zo+csmixW*vl*rz1 zoPm??dA?*RYzlwMrROM`QuzLt1`B}`Pg9@{77pgSVvQ72M-M{*!m=5N8!lL$W?d}d zzB}?j$k6*ffsiSWgOG1&p#SAcbXSQY>4cA(aVm4V%8YYm*4;~)K|o>sSeZ%MzdurC zj#U}Q+{L3Z-xS)08YU|q?o^rPPgK=4qTc%;HXMlsYLeUZC#O5zmMaQuJ6P$BbUEpryLyeW)bcIeQhdLYi$>aWjHYpk5l zkX+)w2zGF^Y@!9_=b!&Z1XV|V>XKl)bLZe1b54a?C?_9I^l~64MQ_t}Y<=vm8ZWr3 zdK4v+FNEKGM6le(%Ug@Wi}TmX;9CMw|C9zHz+$|5>2;_;1*ha*5T*mUQ4+3p%pNaI3Lfm?P5{I?b zUy@>D;oZW6gf8&i98hUi!vYgaRFt0~3A(4@+7uEnlAnL|7`Av_jfOF3tApp0%lJ== zXr(%z3)ju=LCT4uiMe81TDyUZt`j9)5FeH1$vruSGq>K|QQ5dXmcJ1Wc6RaJ>7|na zULD0E5S|P?#MPc>#cFBDf~XWub@KW6hEtU^*YFpvnfR;mvG6sjh{+qlO#Uxa4gFfU ztu&tIl8%Rd?d2N!``c>tekmx)trzfO#ueDbqnO6u1g;Vt1*Jd>*)`S2h7Y8qmE1AP zrU&w|?I@5R$-~A)*wx(O(fBco;bLJbJ8lnJXln6z-H%yE^`#GqF>kTw;hyC;(uZB> zMXavdes11>#N#kvLfB;hpY%6KC-HIfx21CT*;7m52$wmB%)+~Ef1*SG5{b^~737I!@jHc`j+boC_LIa&hgkE=cm_OOR$jt0;)7I9+u6(M!gLRWVq zR0gNH$M4ZYR^vh#|Jm{>Gb`{``s_lv#7WBU>9IPIP*7LlC%2KPUX?3bzg5rmy+Yv z;p)(_dxW!>ILNW;f;x@{=n%&wYU(zL_F(>TP>{!Y2Bn5A9<~zPgr!(;%ptprXaU!i`rWvVeDMvV*?k*1rzbQVKlO#1$CBN+IW|7_0T(wxq?KE{!M-c_QP-`k zWmC^MN3-@!*~G&_WvQ01PbybiK6=GXm*nbCMGn55t2?zz9Yf)=@X55I`_gYbEtsDh zQ_jt?1nboe1f+d+)3{1~^x}rKb-_wN6=+TW$=D`M2e`zYNANTM4f|%E|My+UpDNla z=r4m<4#FpEB!4=>K#KR{escewQ?^{#q|@Rfs6r@vLw{;Thik2t`De4-diZ!+6H}-5 z>fgyub}sAs*O(5+KdLpPJ)C!1hi0zzp)Pw%9~2X)9hO5Vt;wXKMG*kK1l1?ZC{>gC zEC%vKmVq2z7zj5BAln{%ov~~(k{=4Mqj{~;|HWUyh}`d3?(!|C;=ey9ck7q~;phJs zu81MF&}d<4ZO1oKM_##H%@s6B96!?O^3lQ(hE}fTQuT8l-|UXmUn8AlAEnjV*)Ww9 zr(3``>m+4cSDI|AqO`ossCO(ehaz?At#Gu)GqmmZ<7{*|l0o--bI^Vr!{`2Ew?Aj| zh#T-aKg~SJuFw1p7%t9#-twE&IOYPVx5CQ}Kdgawkha=Lw5BE)p;J)n&xOiq#tm@i z=nsB=0MT-Wa}^A50wXLFLWsp5Vz2E9^QHvg@+ZQ0Nf;B=aA)L42`!};-}pf*=0{N? zOE2Sye{yU72aG;U(`*smfA4<#H>`_KTxwr(qx~-OX|fKv{%_rve+T9N{rmD~P`a?5 z&N~Fd=O=xo&>!2K5TSlhA>ZNu65+pLCz6t}167Cqh*9{X)yBEA0urpxv8`|9&E3pB)r-O}02mKcXhv5CXu9|u zmV_aNlxQt4wKpKqN48p4vl=D2v*AS5;|jI5)pnFbc|kfxBI|L#!r8VsWa$6jBP43H}o=+I&Qb-O{uL7 zJ=-qR{0Q1|<6@IijbrLS<)Vh|xm&upXn}Jgh|bns^j_?3AgU05C7!-!fZi7FGN~>Y zUg?cq*|5cdqY2gFKe9%cYIxCNuBC?61$eC_0GHxBntz}6F1kjd^@UM{BS#kSHc@oY zwD>Ja&Z|*>A#gI^*1bYZ-v%V!RWhM#UgT5rj>%M*cVUi7|HqX3nUQx*c%zC*14RQG zU>cAggrAslPXE)@8mq!LC?Cxa=-oU2$o|6jC+VYA{G*66wsc>5iexmgJ`2JL{B+EA zO*uffhtHFck+9S|7{j}@uwSTx9Oe3JfP_Ur zn3>G4lG_}Z5AfnMGd(hPh0W?(RdTaeFema)T%@Kt-*16_HLCOd7yooWMyK~Kf~RND zcn3eqKgm{@cP8Gres{LcVTnkW`uqRMe|Zt>ed(K83c$xY7#p6!0JDv1otP5z{lcv+ zP681uraqi4+#Y@;9TS>S3MBi(HeUL59Zt6R(qZHk<#UlAUcbmttO$l>+HJSXJQAYN zS0RG#ZRnM~-+F(o;V|0mxXoiSa|DfQE&c(4;H#$C zd-V;UYx}M}cR$~_N(_~;v2bsjW{)>)L4DervR|)2V5#yiC=j|5XY7ykp1tAQCczhB zs@6Zr(*Mp+So%wYAAW_Q9qxjYWSnkGYOoI64&&u(c=I%(4&A4NKWH-eWRu@elMm5k z+hfE(RD4JI#{#d70>QE!pI+Fg{CmT9=&@9u`Ps8>&LGggQkUyPluX@eKc7s^8}-18 zew3k6F#`w%3|{lWs0_#7PZ;I_^^?mCs?m?cS@Yrap!w|FnAoJ?GhX%r z`gyJXPpRA~T~B*b&ENDJUQY31Z#3@ddBX?}kWFM*0}@XiKJB7Gv%1vxIwSFPUH5~$ z=5g>+8-dwe#rruudCo5ybawW_?x>ZQ?7=ZJB-+s6N#|Hgu#^CWpamCuTXVKMmk6Jc zt?PauIO{;$Ul>h?{PMGRP6HO9^pqk>r;~DHH-^nR))}A6zxe;fr?qD&eS&5Tr9%v* z)@q`NQYYUFc;&og30^_(xs{22XIF+>?(IPAkWWYKy&8aU`#XnQ^>fpV4&0ssZZ9k1 zcF4cPt?32D_dg83LrU?>Wwo33c*FL762H$1ze9@n{Zl9Wy8W7$Qx|1;_(6{VdASGJ zL9j>n+=2ZUnSh$wdT$#Mqif~$PEGIBWMGs3QCYd?2=6Nk zC*Om;wdYWM|4b0#;>Usw2*+&?(c?PQx}tvn18Qo$DadIv&8G9vlVqyiqtSw@fdv{^ zw&Gj#ZfC3t(|o4Hz=MDbXyeJ$D);dhAa_?+AxT=H>hBh#@Ql-q!eh~WU<6ci)_J3^E)@&EM%Q(tw>E4AyuJqQp>XkVgZ9v^ z1p;@Y5Co=$@I~e2=FX(!@YmDr_wV@4;?*bonfso_H%_zgc0P>(au>xM4+w{A!mnU* zrWy|&svl6ysB>qAKZbc({Q9WYVHpKReGqJRm2_%RYa*j9LEIRiG5fK&!a7Gl!Tt!Z z-f4qB-o=QA5AHH8R5JqiYI`__>^y^`k;y;}E;ogeacL;?jbreNd# zt_`!)@%3q(1f#-xT=`{`r}QZ*-R$M=D_7echJ5}gs+Nv7x?jW3Ju02DaOgr$V`Og{ zCCq;J96@|Tt$tr?zwLCv4KCT#-mmbRY$Q9CsTYd;L5Sd(@>=PHDQZ<%d zU6>zqqKG|J?4)3MTBb)`uK^~4CuQt~9#!ckt>Gi&L$m-bK>`i5y*O!G|_@Mx;+_-O>pGe0bw%ui!*MuAR0sHGk zxVQKj%(nyGY~_Wg4Kthp7jVmk^*}RhFO^xAt=Vkc&jC zv8r>GipR>;{cq|DO(|8(J|uis;_0RY1$!&?wI-;Zr8WOI_@8?(MNRuaLQ$L+&}zf5{L;NeFuMdibHxb_0K32)1Ad%av$Y( zXK{>#L9R}>jyj#-we?g!Lv2|&Bh7&CgdMyy;SD1-T^2E({)wk@8c*suu{r}vt=y1p z8Z5MO{-Z(S?ZRqqwjKHB=2+R{y6PTU`^sse7IbBshJqR{8nBy97V9_Xf2m|A`4oIP z^}nsa$cF<~5##UNb3uTgZ4AA8GTDA&MSE^N{n=A4SSLEly zft7hqWwImBB;9Fdz9rO*B%!UJ@EL8uB&IH6aCO<=y<1Cr;}ef>koLE>F8l1eV@NQG zzZt&}!}^YEVn_XF*$*A?p4)0jJkGULBiB(P{9)F>eT{sQJ_jtx=5e_`$Ft`Zeu282 z>OXooZ8D@kQ^w*W=c{66lzEEFyt|OOfK0&|wpZI|ZYINx=Es&$^Iz(Uot{J!E?01J z|E;tzXNa-e$cNPQhMyWtr>kJ@{s)!a)n)%m*^6EFu`YXNNw!WFgpa!HGMD{3W%qQo z?^gCSm%a7|?dxr}@!X^ITA)ij)%1JH!^J;D9pq^93Hwc1;x}my%(s8e8hn%ndo?}gnmxrea4gk7ub+i8-j%MckAG5ZD@9ud z+!qDhL$qang$_I0lcv%k?#w=;^PNnk@OLm+{#)p2Mfq3r!4D!Xrp!|+Gg)O$r%ZUq zXAAnU-QRLyw2ZUt4L0*R+JJf7VBl7_O(ka~Pf%iRuI5*G?j3ekl8dREPUIUQUdffx z*(W7#rkVsBvG9n<|18*{KV}*i7f+KIEfK9Sxvb(^AF5qopqY4p6jxIT%bu2o`?1S=>l&t5T$tmHQe(1 zPu3I|;lTfY&H2=fhN|J+t4~0-;0#Bk{N;-}=)roU_geFD(_N*fp8tI}$d2Qsm!d4r zMT*%bN`4B1EyORTd1fwmJ95uka_1~qTmSiTiJL__rDZnXE`%`^JfV+NF#TEnEa2Xq zO0DiUdD%;b$Vk3+S6Qt7JG;8X>aV)U%U##g76_el!W;kiFLh1B?LR(MhSP8PsF4bE z`-x0$Hdoe!KeznYkB`*HPwb=icD?IbaF^E_)n@BrC3BzGmf7!;`)iBx-TIxfWe-4> z$o8q#Aqh++B4*q^K5v0S5U=r;TCBVGMDoU4Ch&8m`#FN2OZn;N9;`N%gw6cPG$AMS z^&VS2SEPd#8J(xFQ?yrcik6Xnm-4ra@(BEBRart72VE@R7`GFxCP5nf->O&W*To*u zBulN(t<^&>0Wmt{Isxe|_g}3}NnBPvQHMgk@QGgtj&Yi%BkVdw%5V^g6DHyKM?{kO z5aP#m)E^-rGQrwp^LNV*(K%lFi!YM?Rjb9FxFL0c{y$M#)rhdi17hk638|#z)RY&6 z-|^h&L*R|jg@kZ{H)wc?CKP!Ep$V~_WQEf|{2ZrQa~53W2QMlNb5uC%;Rv`y)8(LD zpwn$-tN=9d5>4Q9S!#06qw(*Wk|`CL1{-U1nV^?PePSW zfvK*Mx_IskR0DLR`cd{42J`A!N=#YaOdsO3j;jat*2f(T7HEbP*hc!C%vP|sh6cm! zzmkCdA+h)QG%6J$f$h09ncP&ngljxh?HXP)+x6J+GYg1V!!7jTY@o3IgGE4aDiC1J zPtG^M57Q&Ko-0Ea5051)e-+8$hNBAjxFq?4y{McH-mtx_NH3h&2?L(iO?ILzq)WHq z!h1TA%bX66ycetPqOP!&Ff@j#mO$gzz}yRYN)fv18#DwrFOY~jfTi*)pJO^hEQ)8YjLU7z!G1VEzc7EOp;#-r@F z-7zc5O1ev&^bD&yN@DvmEr}YFMHkMVS!}{wbGeIdc3p+<{ic|6j2h1lz-eN2ran>3 z>1sI`h*72AcT~#5b{HFubm0n^+i;a=T0w>(5jXgna1(;AfIXS4hQM##qw53NQ+$rH z7Az+FDWWgi4W z?X}rLS=%yUsl!Gs!Pk^6L(!4zve&`ez)&(FrdAIkhpo)q`0X{@%W)&$xkLD3E>|{j zChT?I<*L&*7<1(1#8ja?yqC^!FDMrM*74b=m1b8s%l|x?)c{B3O!u=N${TV^sfV%p zLejGIm5Yd}=1!oxZfvD(zo~EAO$9x4^y2!f^&aEo)5{tNP35U(BC>{ssbPy!qu;%q zr;3MHn+#50xhlNwGve6?4SMF^VA~nyU}nQvENB{R$@Gib58uWW0NnX;a5eHOHP4X8 z-QEpd+NKlSE*w6Vnm1i2eJIS)1Eo6f*}-wfY~$&qabMrWopt;G*TFv&8@5>P`{fo& zr~8!${_o##=oJmrD^K|=yEun7I)5Q5x)H7usI{=$mm2r<_;Ks=WeA9LF5Shua`n7J zpbnlKZD)~R2`di=xThfa<-3IWNcj*RA3Or2qiqG(w=vNz?=Jj99HFBxMb$t{4 zyaDO#xokmsye)R_pvkpe22ZZ-KRED)FRo2=73Y}LP-GhBB6G=P&gq_^FrejCHj zTWflajm$#f1+(z(EB#_#8i{c?k{v z^hCd#D^uBvD}xC`@*pnq?>B~DwtnmBcV0=ij38+jghV6w-2lUNGy4kQdf+Vnmf-p^ zJ`?>Wl;HZ9M8Amx)bFH9uskHu?}}7v+HE#R%CN7h7D; zHSmYZ>u=%TuJz+JH#Tr2BJuRVZTi!XK$~DfrMmCCcwFBu9ER(k?@F)YLu71;$(;Wa z+blLuY2>(>u94qcBUct1If+KPCHh_6KmR;HMsh}|x!#rkp_RX&SpFEwcTM!Ww158R z9pz1i;|Vlb@agtXL4M!VRa5L!#rK&7@jVD3F1-fv9jrup`@texr;*-@YYbQIk`!EI z*Y|2aX?*r^C&9VScQoIT1V73B>__70em8O|!o%c!WN+fz8@)8q_?~`nE;;d3<*^B%VI{%Y087 zeR&@Tf8wd5k0)W&<$V)P!&E;vq0-BqO_*pc;qV!5IvHQ)#aj|h(!a9fD@UKfOPeOd z{&_h;(|uh{e9Dqi%lTS7m9{2R5Szp$rS?M zUUO_aN9$PQ!7tWl`1j<5sf3enPQ)5|HSX_SKhF3Ut)mao#EGU$5q+H8Yr25H-jyq) zr{A2A+aocxjHJ|aGg+AjTa7qpL>(7)jgy;UQ> z&&d_PWLupofxFpK@xgpO=Pvtiq2A4tL$e2AJ z4CZ~l4Q@FY%>RHD6W$@4vbo_uq)Cp5Qn!WIyVNoA9pJFgDOK`^hO=EddZ(R%!5JaY z|N2+!(IN;s)+_L*@lumEKtCnb(ojN=3^CU<2_eusya#5j6KYaZUT9^`Ns$%JOBC6q ziKcfo#3>8KCM6nTM}KzaPM49hp9)}^+$87K*cATu??q~US4aV%Ay^_4W#c~b+hX3i zQQiphzOB5UcH|L7#?1OL)*`Ccws1m6i`X&B=_x~k_plw2huCjW={oY zc4acuPnGGxX!GbXhtMuG1?AfePv}#+-j~=bja#nBeuKw|d>xTfiDki?yzrb# zyqrqBG@kPkiKTTxGpSsGv6FJV8ggOv((JW^DKS3SLCOd*c9=bLKw`Q0az?NQwcczvg%pmwYFYZ{eeO@xo1TT{{ zH1VRBy?q1`>y*c3cX6$1^XlfgJnFWGY3yZh^wKS@xJ|issIGdt*OEug3Dx~5)nRHM zQ4SbNNr^&)3Fc*$i}q~q@x0LtXO9kASk-ofr#pB|Eo&DG2w zUM$v2#YAw!go?%Yrh}Gr)n32q126fWAAcdkwP!Khv~LKKmW@aH0i)a8opp{hH*6TE zcRK#M7wjZcsQ@OXUk5)UotF8@b!knLmW@49HZR(`q~S1Pi;~;Y$yc9w`0c;vy>f&y zyyeIC!T1>A>~|&sD?@B(S_|PasvqOEv{j@;O^y2Z%fvVO$(8A(s{2)x-h70Yg!NzNo98PL1M7PD@pTlC=nc=#-Ph&P`4^A& zVsE8$gAeyHx#dP_3A+#sY`_`&6BxZJ2p|l2h^?xn zUVOD5YxQ!2O>iQ#HQbi1P?a)!rfi60vLmIYEr{PkcLSlNE0d0Ip%Rjvx*K{lw%(pe ze(1%+=YIE-vG;i8*Hq7-J3m;OW`YLq`nasC_Gs*}6=_1d=(=SqXV{l6ggA19VFvn6y)kr!qOeHW(xLdhu*oL{BmKmi6t6{GC#^A>^%+=|n*d-m~ zP{Rf<368dQ0l&a#@WMyMVfnG8Y?c@XG#Hky{%(vrh_NmVlV(==$>kJuO;D=SFj-UI z_2U~d$q$gZ`dzglUGw5OR(s`j)vaFDM}Dk112-l=W|@a{As}nmjL%>uw0VBXu+uv4 zp#o6zgTgSWv)oTEqM4{l2PIotd!^&#Z}8)*GRf^$eS=@MDqZ!apbS=JVl8ZQ(!;TxcqWh-L>bbf6p2p-#$RaS35 zj#|>@atFEvrBHh75$QOBK$+ic>|9xAeY@kHVN?<~T}Ol#k{RWty~==D@+9WhhMG z)UR6LONID{fQeB9b){X?N172Mv$_%gt9em#EhOH+YWb#NT}nfnvB*v@Nk?m`%Ie8D zQ7YrhJyv8X3#>6O%b*=@(rIXvpID$&1CP)@BT4{XB!IGM7(0<@ot>pEgf%RXX z_XEnaob2$D3s`5FuJ5v_o@4_DG^TsLpfAw#G3nTwPA#HqPudtd4s$mTJ$FQkP|G@q zNzPs`37$Uo;ve~N5z0_}xsQ$+Q}OPs?DS-We1Y}8cLy1z5J5i24jN~CrICfLDt-R^fn?*-2wmO=dhKzhV zh8Pwpce=3|9yA2_^;PWusJ;fqNTXr8!N*ZWc{2~{ejuH*YQGm3UwbhYS&eeOS`>du z!-wENEUmjYD+j+!`T!AwWVLLrz)nfWUIJiY&#n9P(uMkdY?FcS=7kgn3?j7jBr+1Lyr~gM!O^d^y{Ik@ zWsL)0apcdNa(`641^N6WssY&8r?psXz0|zcBmLM~r@|IieOA(t;q8mYCJmM)hqZm3 z6RHQ&E$?e=s8^|H>p%Eqi(h^fMxa*7r5P0Vf+k=q73u-%I&=&x=n#cK3$%}Fi!zOF zSkPNrQPxeR1#gV56=ly9JJuVO15PNnnTaTr74%tXAr0>xuWB>8EW<;$lj=2%<%!Er zIri+w@XP>qYa-pUt)FRct!N{F682BW0eE#Li7BUn^~eP8q+2%kVsL#)lyS1uhfFii z%MGAk^uE=!gV=Sy!o$=z3yG51uT3sKC)f2$=>XkC8mu#rLRJ+vCwItlS_(Q*vzRIA zxU{rXG)+)N{5Wgm4nGNKn_09zOffZt<4k;A>e-g>{PKM`275CT`!Jo{mx--SSMA6o z`Fu4Uyhar)A}x~GrhEhAF@9`0$%RR8AiU;dr{aN`9${Q68O=*OwKfiSr6SCd+lyS3 zXLKK_NL8_cUALGw@_}3;bv00g5y|q6-lUzmtn5RXVOiQVH zmW4V;E+s{>bk+QH)ywFk#&ek%5UtF_&_7<4^3>ASPu<_H+N}v6h4cw<0A^7)$Xy4S z*CKA^OUNX_DR6JbHUOBhWtOln1D6N;DGI9F7&?XaQRp!1fM4tS0uyNtT(C{=tgEQ) zTf4*jgVMp<5YZCW56tB5I(6}{T9{8#Sg<+|m2ryqMEjmlcZ09Br5VC&F+Z2%>$PAzNaY)u9g7yTr;DHCr?$M#E8T9Jt@ z<6JA1`q^kdrmDIE@vtTuA45iHw5+X2#}D}Bw^q+qi-!1?O!6hizP&QR2IEZOp2f0V zu1_EU9R6i|2#}U7jNGu%vX%)E9!8htD2bpbjQ~x8GNy0qktBD|#181ba$!@lqrLaJ z1U93hW#XWGdCGa#zyxak$7#;kBp1;HZ5|;s19Ojbat#AbSG@v0g_~qMmmnoy`VG%x z%cPERUVKf2*G%rJ8ioqlHyL7O=Co|;g$u^@KQkqe`)rm@4J zB2ER4)fO-vr)x5|dsRy^h>j$6RtXrx@Cq-+@ryUqG#JLOk20d87?+d-4 z06|R<+p&b(CBF_PU0R1kVEfQ(5p1QU~JZh*E30wZ{UU^T(c04Y&nt!&&zQW z48b-+c3QqegUHtVjkE(Tl+`;Ez%{EriSIR;t)k&bW0NJAg* zpANS0E5bNhA^>av<4jbN83UU4)?VVmv1NX8E6Aih&|_s^S*jDBpIo;*0)|9Z%q7dTdmwS)@1wIW?5&O@TV;>Y&8WIwi^fm5o41}0ske{JZW z#wRAZ!mQa^($ogSzLOCuExC?{95)J?OTu?!Uk!rNF)%1&u3tL#uDP0kUDjSgRpI<} z+$C)74|-vD41)?Uj^(3Dz0yhicYJ)T1!%0GI>Jw`7iEwlRuxKY>PykX(s2wd7-f?* zAas(g-9;}+jV%x(VhxBf9ht(wS;jR5oA(9eSoz+7`S`S7+ofi{oz#YN{ zD{*atNJ6TCbhLLm0D`@!CdTJ$;d2Z>C8?2_jHH)rEkq0^3ci1uLua8eFusyBhw;r| zoa~iyBvWTI7+>k3f41tM6=vNmGqYXR&DtZS2w}a!=Bxy4UF=QA;Yyir`_x&%MUVLe z*qGhPeP#>tfrW}oMId8qz2zq{q?XP3kMmETAq%zDS*S7>%|hL2jbouQ^JJc~dby1z zx$uAo1wxiNZYf!*;yUAUx-D9$+E<|zc20?fDpJcr-6s^`0|uJ4n5lkM8<1h63RNwe z!8Xq4Jvc?|dtHL|g>&%Ba!T594&@j=GJ5u?u~}sGGV53=PG6LP<0Y|hg$uBb&B`(= z`66>?k325;&r}nvw+Ih2eRf2&6NR%ahcn)m&NG+WHb9!_$CZltwLoCbPyN2$B%u$jcYO} zWpWsAuVXF6)?%7y8kW~GJ>lHL@IFl`1rHRR_&HOvItq=T~&g-w{6xAge( zC(P8|lZhSllG`(}oj!Z3NnDf*gc5Da;0p;b!3db-dmU&+u7(9fo58!3Nhz+vti_Bk zAbh*C;VBHZp_MsQ4c*$D*{;0|ZO*)f`6Di=+AOnQn=^Z;($9~*CA&qY_nrvT7z2q6 zj!#n(_ZwG}ghsv~lYEcD0%0~*1lpl3nAFT9cm5lwo=uZ7;)UubFupM6PsWZjRJYOv z4m%n1^s!#3*3m&Nt!$EscCFT(WQmMy0T}DWy#AOwC(z`&XZbdDF6X z4rW+YV!Ja{>(fb07c7~TR6$sSW=vXr%b53KEb-d$Gp+^%(wU1IDWMsLY~aHl+-VJ~ z4XhrOQm&K}_KL{GS(L^>C}aMOf-%3{kI6Lj@JA!E35c)}BGTG1bk?Jy7<FpErX=<}ohc>4IYtBKo{;W%8#K_Ps${t00`8+9A7 zbQh9mnt8|P`Xg=ddFZ1rrgAF6Gw&Nag~bD zPA)tS+TZY!`#Ij8u3F5lx>xmz{OYkawpd~TAq3VZ*gWpXW;N_L6i`3 z6ZBGPCVMk-RzNj5t|Yqg*_Y~qX`)lm1*fy#!QL+mzti*&YxkR(81|j1gR+C<6r2Ui zm@V?ILjod2+dhnn?$~Yy+nHc@CXPAuuB=L|AKFP__y7R%JD(1=W>ArnuOQXsYnYX; zdheXvUB{h&<%`|fFR)ed1CatP<=ouxFR{iN;#rS7t(4|i-C;dPkp%0&_Byvd7iu`& z#06VKmXAfr;3z!cDJW$h`>{e9s{tE6_>>N4#f;%6#OHxSj$(M5WKSAV55?GPcE-$M zYHzH}AwmXhB>{t*FpTja-QQ4IA}GQI4$oNBvqlgO%?QrN6s7w z7!@=ls|YE#1rN<8>z9OOk^pUJ@2D6CrK1A7+ zAtr_ZCGQ)?bhGJjuSy>h&o18ObJ+ep&&Q}LAIQe8$L!Evt;XR zrzPrv;eM=I9oDTHLljj2WPe+)S-TXb5b2Rvc1w!1IB<5Z?l|Tc6YN7eLzg7D%q$Vi z9C-?a8FYCdl1a}*iQ*U{k~kAsCJ^()54PJhVdq!7+yp3WRSx~qA7W=6{|ncAMV z<(D04TYk}tLtB2;Y!Bik@SNiKr7b3hQ34mUCA6_i{jSUEONS7}Ckn#AfgZRI5N8PI zL&t)o1We}JrMc|M;L^lYm#+X#wZq#WmjU!BcpPNdC%4FeWKr1)LE72#i)cauO9ehi zrO{e}1W3^YS52_P&p*p{cc9TYR)$8LidY8gQ#Oul4BEk|eKJ@>U_Px0`ILM%FSZ2X}=1Pmu z7MMl1Ds77CItYS2wJJjeHkX2boR_Y&!nq%XOg=!v3=*}t{PaDK}pBrVy_S< zu6<}%M5q}{9hVCo@a#mna%W)&&=Wfgz?4p+%)7kJ%#Wy4FG?{-44(OdT(NSCe0kAc z^U)q)49P6H0*??)(y_O!Q|Pe{*N^gXefcPMR;F)(B<_2wNP@8Uv%{++vVcr0E#694 zNY7SLMnv&t&eD2;GAN!~F~&=%nkq1H?sjx ztjmNf@?gd1CR5y%4z$L!-@FXhTdSg$QWC=3IVW?ygh%SB$zKU4<}VKIWlK z1+;k;0H6)he7Su{r;2FvvdgW=#JAC%%PrDo8QXN)6@0=7#CDV`kcU9}MH?o-P^}aZ z^QI997Xu+l#Oko#gXWLYxF^Kk8$joqZr)%(bj%wV=78h$PnkE8O(IeUbqI_>y8FZ- zVFMOsijf-Eyj*wi3@dpfyPHLG4vC)pe2A#nB- z2!c`E@!7fIWhN>4+5}Z3*vI1JciB*7gD2Zbxsn31obe8@lY>Z#ulABl3YFUr&#;Ua z%J!nHR(h5FhaZ21vVPSXc94ChAd-9rRwStMV@ue0^5raO>!IBdb#W-h0^Qfw{$U7u z(7alm<^^u>5iC%!wVA`mQTABSNL-1OXS@%mDL!nLkM*jM=hg(xFWdeaoF`-)-OVBp;mjc@NnDmN`xXxSH@ZQav(6(fXAB2$|CT)AP8nrN_L4}62_26WnXob)hN)jETZ%YLoz8);-w28+MNv&}G& z(a2U4_X9QdNPoi_It_?0Ewz0H70|+plpzp~G7JE%tQTY+TZV1|eUD-rmVFgeCmG#j zLKK)uj8Kk`g>=$-bM+X%49tSd1LST;{ZVzJraT{VVDR?l?PkZ}hz6|~Zmn7HsDT(LdF9w{5iD0-SYVOmjZ_+xR1;tDmFX3QL- z@M~C5jnyXBs7hMzC!RhT6l^!q@Y~csH5(KhkfPp zi{pO9mrNj#rpTP8;Xk-X3wqUxbTY(=Yu54nf_Wf95F`Q7alSbs$QuPA0 zM*(`3CmV#~@?#Wx3_z@Vh3#~>vnlieHl~A2zpM79tDuG|D}+m^S=*OW8uHQ_>Noc= z`!8l8NJ3lz_12&5x3~bUWAR&`o$GUgSI)adz%fGxUr)Tq;G6wXjI@5Vq_ti2vCtn` z`bARsW=%QbpnNT|1ysrxrw_mkmv*f36^EoKMd@JL;$bleUUU>9tjD-xslY8zLfKw& zsSLzUF>iBz6F&c2Y%|^;^Vu2WOB7jx(VhKcvpkSaDm(*-2r8!I2Y`isCrgx$cQ0C34$AMsx0M zVgo~$^tH4fMuooYRm(ik*_p9YTWNBgZB}eX1mW9iFWBgS*pB(PHbF^H&GCh!q+YW= zDfR&u77FkaK-M4lkCnp9=7;h~0_V$^a0Z*WA4WHi9a~gb)<{VlAFP_~AhWj|d-T*j z!ZoyLjZ8zG>7mG`fd(X|`8qbw=%FR89VAprFuW!;_nb*b&hqg#^JG_y37oIM_ZXoR zZK%@x-SZU}g38MZ4%f5mFkb{QR~h1HrM~g~fq2~+fb&`|Ly)+Q4z&@2MLypO$7tAu zj|EjiX2Up+z!p1Xa|rBche>;j8tM)^3&i(2%yUukFzOrDQM`zWdl{I@9lkj#b9QdP z=iPoUn4Qh>I6pw&V%6B@$YB0k!@T@dYiX_&^-+>gAK~5Sn zLv>~}puayyHF-VhHS4s>R2(HAV+!QaJq1uh3A4r~jdm9iWPFH&2*^Rk967YW21o}K zUW)|63^QSc+K96z`slAOoY`SO-rB_Wv0DDFPOv$q7OK!$Ey+O-q#m5GhafQ4mY7%b zG-0|LV(79bn1)3fjRi%DGFk1?R3M6wk&$3>1(YBQ6E4yk=PlE&*a%ZXy8%(hlpn+U zbt1V;#nM$@2lUtC#KET6LG>Cl`_`i^{u2l=T&+I3o)K2K3scVkF+7Ijlob=rT9D#( zoFW|kF}#xae9>V!jo=XJG9pU#3UhfPUK)L*8-XUfVCf_TS-Y}PR`S1(@>*E9!VdfRf~Ld z((^OJIf|G4P*R2NfyykGA)=@b5hX{VrGr_;YvWtNj10obhcASqe^j67upTuPIxi=X zIKf{G1q!e-hn{j}a4%0krg)3*1QH@a8qmM=U|NHZ(MySg2 z_P_<9SDHo;kjH}WGR?=VP=j^tpAh2t(UsqRNISkB^dhRJ4v-Jr61 z)@Ra@gSfEZHE6@e&|1%_tj|r#j5 zsMlJMgRBi@DMS@4JP-g1@Ano@V)8WR(I)iPnUNlAEhPZd>@V!032ZrfAo6S;K<}+F zMU2<_$K26F358~1Nzth5@Xf_TMBKp?YZa+F;$0J{ij|?t`ZY;wm+Mb)cKB{{{twZE z?@$IuDW(H3cd0>{bMo45phlYor-#1t(dbH>2APlGt}~8vkih~k3}lF|Zwi^!*9AEH zWX35lNY*tjk31mrZtAHD` zXoPi01#_hHW)- zGWQ;L&3)fy_xLE(Zn;xOyrpQ^(54GAQ%){2;}Rbo%i8#9i4~y;@6@^>K+Yoqe7qJ# z@QBbVeRSj}W^(PA6GW>^QXiI?qCF>^R5d#xFQeENoCQJQ|;TDDD}ha$5X^NV@ewj zUyP5in?Bj!^){dqR&9KI+7+j=kVI=6j0>C{gk!Lpi;rB8jyo z2%@hc#&F#|Gakl`I(vcI-%_@t{Od)@eT&)AHl{94D|=mMTHcj z!eE`l$^B+xhtpqCujPT-j;Zw-OmE^br~x&1%ZY618=bh(`6Nd~@z&y}7}M`4mKw+z zXCYph**4g|lQDt_MR2KO9Zdt@Hdf-60YoCR3lV=gar4+_l)>MsXbengtW7Dl3a|)AlN-&`)GI4qBFc4G5SXlKZ{LWO5V#K^`V_Rif{r0)g`;56;$fvFU?#10Oz&}3CiHci_exf(7vL6D6-owI*t#b<3lKIOeF&k zv9@AHCu@~Jqv>Qb6e1>-npuyKkkXUB8Y#7TP<%u$85n>G%s;|ngdn~IYbu}vmh4c2H`Uxn3*0ssW_TR5Nr~2&ptlS$w%MuVgBm6nq|HPO>a#*7 zA1I)SAuomUq-atczpZHdAg~au!`@HKL)d+^26?d7dD2lC0eOH%pDcOs^yq6Jd6eCd z2TNU$Dz_z%40a}9R{Y4r?k@Wvp}m&sHco{=&C@UJcaVa z&Jji=v&7M4wBQKj*`;Wbd$1X44f1&Aso=B^pfNTh{WMXZ{E%%z$cY4*vK#W~D5&Gb`QoK0l7O~W!twtIdM47b7 zx($ZTN7JXtY@#w`#-1jY3u2_(t{^9%%1eGeH70?S%%co4D*SMO1n^FvBb>Nbz;*%dZO4kxZdQGMUP6*Bf5}b zfUGt4q=0R$&w6HElej?kgia##XYcpUx?1L7$iwoc0W3)RK}|^OeE~73pc zC}KMS(wdG-IcqzZBTRJiBiM~47?T1_;WU`C*oZ_mpnz#h zslq?0pS3>}fF`YX7MhTRvJ&bSSdaGr6ZZV8{ImiFvb;?N+8+uN?ikh-+J-U~R4FA4 zeJxkzn?oqg$T3kT`D%HGNPE>(Cr``UlQP3mu~AcG9ukvlP+-hS3nuNOyXceDF%vtg zuM8R_9+WgTCtpv1HLZ~eYigq$nIO(4(;I6?zN#e~ENq0!GgvUk?6~dDX8e+qGGCV2 zqAjfqlo#19%MaV%@z_nLn9vh_W$f=aW(z-FH!=Y#86(9^0DcpjF><`7DT*G-cvNaYp4U~Yxj1sYoRiXe!Ln-zQLjvq1^BR{JJK($~XirLTEft^5G3 zN@vBIG0=Jt&y8*u-`UFHgL(oW`JZ!&^f`G4+^%aq4mHDA#xqwLI!4@+U{4PTt z>^+qyJLC!S_K}CY?S?$K5Fn4@PXh8V#H1dWt->E94@~oq$b+Nn|A0JlCmco|#m5Ka zvG&a-w9-x1M#9(`lvAN!!2=3;)`~oaEIbvEm0Eg~O(Vn843$EON2~zB>4A69(nbEKzTpC0c%k94LtYc( zojO+a1d?qjZW@ur#wF&+g7*pE4JWEV7WDlm%KCNOj>J!d*dDn`8*ni&Ig7@6Ihk^p z2xth*DB4ttB9SLxT5?}hF)atdjpRATxcbZ_dLLdmn-6kW2$j!RI&xhiO;$NFCia@M z6<0ah5B$7CXV}#W9L5w>c3I-5z z*oG{CZa$Im^F_-j_)F<3^QRy_3PE7$fns8{`@k zP(^E!%B2M_j2Sjhj#we;5Iz^;oWrarf`8mnWGDSQj3Qfx9)TiwBzI~<2ZYi5`GD10 zpvl1tGPC7;-J}hl?D>_pAdgG|LHJ?`0@@HS+X8|Ze}EZi%q#@NN#U@POVgL)k()xa zsc|A-A)F9oV4UD@us=5C$B7Eyd8c_IAkcR$L9|g@;{<{rSWwxvJ<&aL^8JYrP5@%) zo^n`wBA77~fnUHHzFPdp9B&a~ih$`?Vs_X8BX(uLFoIT+2jbX)uu^PcwKS0hpOnUm zvM^TYuYLqnk=7At!N^GJwmV-Crs4)(HNEuMPph4 zNDZMu3;`o4q@v;9X85wdA5C(pmP0sXtad4@&Xv}f!PH<~OqNU#PA6ZvHFF#_q9rs# zNV)sFjHeJOy~_@xwWH2_5L!{8K6 zdD97YWZ>RH`^w8Jty4(09i#+~OT?B58I|r;yWz1tv##|_h+KkY0;!%j`%f}6l(xaK z{2+-OxRY~hIXlfvV#b)Pl|Xr>2_{@tkzgOA>`gPF;@jd(a>TTAHjLu&bgm)%e1<%( z@~z6(%&s=!tQh{28DxB{9OQvz2=cKR43%|qx+1rhCFI$HWC9H38B{JMYKtEPrz~qS zy9anh&5(v;>I^mPDhZU3gtg;AgWas)*xBtko5d1<$`}kno1p+P@r<-(4#NYMc7!Q* zgd*f?WS3VemZwVotdV#}e8xsYSPH2)VVeB)Z8&m zsYwk@sY@M>9iLusTy(*GyiUgej*>2NI?gRq%Iz&pi`+kc^D$nOfAh_O-*%|lIrIES zj)!&59qF9;$J@^v#J8&J_I`I9Y0cb=hF-{*F+4Z?{>9{}q|OPex-L)st(+sgmo8Ye z{^zCbysR9sZuaNMSnia5?a?wljdV(T{`6l^1PP>}4 z;lJ*D=Sp&L3#zH^ss-HWbH_9tMy`8=7dHTMl-oX0kftJ2E{5IH$2&9NfpsAcN#8u) zNgW@n-r>}D-caf!&n*ja4|KRyhi5m|?+xFs^TOkq=E&rx8Hd;`$5HWR;oCk-?K)OJ zo@pv0M8HYD))P;~xcQM7iz%f%>reHnrfNT?BAYRP50+h5si)eu)RJUdSv)!Xfhk)GDSj~3RgSt!mtl#MH`SEi{xEtnkarrzghCY=fgj|6H+f4!+WQi2*@Se z7WtJX?kq@c^zY^{c88mxH7NAk)$jDga%5;F-3lJd=f}9n$-FI_AM2;b`LSY>N}I1F zc;szq@JPP?dYm8QQ1ppaHXqb5kK`K|Jd$sa9_Po7A{lA(4Gtd3Hzar@-%vfyk8%6L z#2TBAt1ZkU`Gy6L#E|R}nmt@7Umxe8=f=e(ZRX2AdB|n@94U5ImCa zL_N-ros>>$Y;t@)N6t@lY>DB)BPC7_9x1^k&l8>bu~W4vjQghQO3VdKEhpJaR$x8u zdz_UgM|LK3_fPucp~Cyaq5ryzxhG*HO%@QpHB!k7=-*Kip#tO?E$G2r5q7y#z`QU5 z%}O_TATQP$IqE!+YmJdWuyIF%m2BLDpt=It))+Cu%}H${Vk>nhb$diz7lT}>6Z_?y z-{^nI@QQ_~uajTbeIaBH?zL2J<={ z3x(1k1g)OADWuihKf;TV66tyHt8#)}7|Vm-&)&S1rITFS1sJpq0nD%zx2bp1{*1p8#TB z?{$7p%&h;lKhV3>&eT#}M8S1?MgCWR&l@hmRQI9OV8f27^g~`0#6=j2;Vt@7Oo0^uk=wz zcOE5GBs?s}Wl*sy{?+KypX?`96Acf8{N^tgEbv_8u9;I++ZJ9}F`(wIVpG~a+D6+# z_1@gqoeuO$A!(u+d|W5gGAB?cCI;#RvN6CHQY}E -7-3af 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%0D7FZ1O?HCv4gU%DvVk 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 O4VAQ~ss`2D!T$h|d)hGo literal 0 HcmV?d00001 From f585df73a4cc9c54235aa589fd4924f8c2663588 Mon Sep 17 00:00:00 2001 From: profi200 Date: Sat, 28 Jul 2018 22:17:33 +0200 Subject: [PATCH 217/317] Fixed CFA handling which broke in commit 046bb359. Fixes #73. --- ctrtool/ctrtool | Bin 279024 -> 0 bytes ctrtool/ncch.c | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) delete mode 100755 ctrtool/ctrtool diff --git a/ctrtool/ctrtool b/ctrtool/ctrtool deleted file mode 100755 index 484368dfcfdb5c3c2a3f19265852a7e7e0c8a2e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 279024 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

C-H!Uv`HEcCcq!OfEqLKYdpS8U?cxmeDQ&= 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

>Hvql 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 -7-3af zM%tqds@)E=JzT{kK{7Hw#=`C+`kZ{KCO5U_^^b{x{(-Cx^bcAOxD-(t=pPe zVH1M^Hqpu63=xF?xI%vS_$~05F!r5u8CJ=IAx>dCVPAmNN>HiWc3WOgxsBJU%vn@90*&KbM%Hqji z)x*_+R-dz!NgWaYhFaj<(x~JAgdV>(k}vZYbtoXqM*j+~k>ZN%fI^e`rC@CaY798u zPpn%6V^XfNKT=a}FHhb6a5>+?v5w5Bqn%`X zsCHLvXLwbq_KW5eIjFLY)_oQdG zmZ!Qijb|3MzdK;y`tZW_(bNVuc18KwIBL=;ehyz62mzp{Al+T<7FsC2? z$CEy|m7F{f5TCqt-n@Azx?~<@z(0BK5M@1fTj^;kN)_5C~f)82DxV`xwNfdSP^Ql0HxW!0V0YERl*(~Nx=%$WUf zd0}Q&9?OncfXwI3XnYv$xvQKr0n#NAiq&A*4*tk?a>Xbja8kPrB(#tRrrL96>~m%` ztLir^6CA-Vmfyu%dYSZ4miz}oetanGHRvj1nXZ!6#(`MRtU-3*z~T%xwIaw=9zk(tQM5v$!-+g7`#c3<{$h&2ZSYf$Cq#g?NrT6vS37P)=c zU-~PJSY1QtsretG+WQgbKS z`DMYln!ufcZs?rw!i3w8=h-d`sjrD~$`S*Yo7csT7KmS~*G|MJTx@iIs-7cF741Ddwm?Fy%pHFeD$R|4UBUpTfNIke+lb`{&-l-?Nv_Q zoSyK)4iMar6S=|*-wiJu_XQSSDs>NqHhJwVhpbq{<}KIm1#c@0-j;__5*I>+ zfRVf3y|P5zIXO6qa!1N61ve$QqPk$PHXD^*9t$sQ%e^kkzpgBNU2bCJ1*?D)tX&3H z3wvb%dzFQKq*MP6x1PgdwQX5VpumM_thVrWT6k~%Y!2QrPP!I&8z4|wyzZJDyc~HJ zUbr{BaMWitX8EP?y7nEy>%QPEcn#hPuerBikhg+$c>rr?0PFhjLY$w`%J9NnpknZ+ zOf_}acEKj}`sZZ(DfSI^)aUF zpR`?XDDX!V+)sv6;?`}`nDY*s-W*=AJHrmBcy2)54YCRbRJgTS*yF}GWoO{ptBb?# zEWN(i(QJ`?7)6%ww2FyqE!XB>Zi-QaiQb%WQ$Q-clDJIO;;Ef2v? zRq0j$evb|J`&lb)0&G2k#R6aU27}gXwW450%*;84?0t z#{h|<>|gqRo1tj*@%y-GypO&KTubTk=@ZivO5$~uO_|A6tI?4X><6oBDB~*8#>}MN z^hJZ?bxwJ_ZgM4_Fn2sNDzo<0{e_^@zjjEtIrB0!6tH&jjuBjty&`-^47Aepwj{FI=m=L@Z;br>p~~tKxNp zZuvmLY?69M!<}gk8Jd+uKz6Ng>NNXkRAfQ;3No$?FJw@0Q&f223-NS9b5nO^ye`hb z9sFE)p>8NBKd&yaj0v%a`oarYKJkqtfk;asUdN<#TkN%YYhf=ny|Q#M58a&vi`Wlj=&K|Cg%5 z3!jSD4QK77O<>TxQ%4@wF*K^xySm{^!wcKvf*@WO9T~4HdCy7hm#~f_f7EeWw;cy4 zHuv($NNjSx%WT>c7dI+Y%8%FG1VP7fDAp1oExhn~!FgUC-{}n1z|Xb=OhWXgZj?E0 z%2zIWva0C(x^b*G7)Ivf+NSzv!tSMT7n%v7Ex`4T(e5BF+b`)evQRr-*u-b*HFW!| zM3uo!>RBo_A}crb(8xjhUeJY6Oj5Q7@w)HB@EBaw+G{A79`_!!a%%U}0}*d|cp)1v zeEBEM^g!hRECQmMt_o;4K{R{|0PD|rjT zU*Nauc_ChR9^y*$s^DjSE>^8nts4W zhE$0FB=^wu0`0aPktDB|h*M zF^+?Mf*MSSxMxH?D66r6d@)QENRV;RjjKTmWeCD^H|XKiMFwLaOJI6~6pf8UfdDO` z_NNVCLXeT5NKaZ=g8+RQu0VKdzT*TgBV?}a0U~pfH%p3i4_;HfaFZrI#`r6YHA#=% z+;3edJy`g_Hq1#6M8}Je?rmy*tEmq4FKdP_yRE5hMNX@Nc5l;PY@i)RpMrRMQI)`! z3chWmM%}o#Yz{*RFr*T%8VuP&Ad^BwkxLX933wgEVZ$=&8UL?|r$@b&mk|LWIkNIR z$waGwfkaI=NN=Qu;5#3pSjo|eFy>f4@d!`9|Dv9_bbJ|Iw9-DG?QKFhewrock>*hW2`aLZiy!4Xeu4oz9eack} zjCamS_d{tWgXdHed39Y6^_XU;{rd^)NlSvv6_z1QxS z5Yk(fTE-8SlMU_^39)$qmtUS0*zr#Ki$woMbpYv3hH+0*_)l-e!+&ZgeU0=0k>q$o zsQDpUglpm zsCS8?`Pw&CnWzrcyMuZOoh2@+J24Cg44*=q9Nh3GLUOI1;>EMjnr5*GJEvT~1EAry z_f-ziA5l5|2`WDv?gmVc2JEqODnAa$F=u8mJI<;4I3NeX4OKus^5cMfB#^UWJO|V6 ze;?;HKwbmnV?GYZ#{fCbjX4b;2jmSv-T>sz#{szmqW`7|J( z2ISK}4#=ki`E($^`s0B7Y9PNF$glZ0AioC4uL1H|g4{{&nD@UU83ih0ky=TU{yYV* zrDicryc%{-V<8?Twg-*;JpxDDx0F=B%Z=@6jz?LY>>Uuk^F^jCw>NypZ%Mg8BXenQ zYDG-P3Uo6OuzT*)>olna^TY+|$SE9=5Yt&R+p0KL2AR9Z3IAzz!u}%~G?*yZwOue# z>}eXtk)p3NQLN!Q!FX~_k=YB^DTouHCFiFqPO!i)Et`}sIU!O1O3Pi&4Ag|gJ7wKR z_t{rX>Chr9s3LAh54DAV%w7}}f%Y;7RpN~TeJ8IkCDQ*ypQ75Y*Uc=mHb#7(W`%84 z0B$A>00(H!#MnZ2UeOFhkYH6@*OG2$<|E$qVAC*+@J+lUR+i!~JIQsL>WHF_C)e`S z=A;NF`feHL)G3~qF6q?9?NwCW#lKgmW(hbV6!J|%`~kx>V<%CzB6Lw@Vi>{lYJzxA zUzjkvpUixhD4xJMZ4;R9zFV#@QCm6io61Q)ymA5``zklVmcGCX&Q<2gDV~5cl7H#% zR(=eC<9(RGbobq0x-(e$UjrQWoWOMV-H{&y;3EMX2mU!V!g!;!9$)0#4Djm>SA_qx z^h3zGxoMcAH&^ML z7w!@tw%%$kFIhHOl1NWz&|{+bBBoE$)J0?DM@#N3%SLc9Tfc5wb|B9Sus)^{b0|l$ zaUv6qs*QvV2|S}tfg_ywAymaoV`MrJS{nZ*R!1+ux`LnF-sW>qNNvPjCJ~w5T_JlS z)hR`l;{eiQl;sn|t7^Cs8>&i*k_SF>11^K*tTVvR%F^4_z^`!W^F1Tvm@ z|EeD-@aX0;|2sgHCU-}%?Ujw2N%nl^hCj+{N}t&9&rk(L)U=`H5AW+-e>yk$@}>UU zRA}IcUc%W;Mt^Tif3qZ7|JLjwIWcD9>wq{j?rp)5dfQk-2t|Ltd#D#(+6Y1-WSdtd z($|+YVOgCKZGVG9Yxe1Y+!=2mo&CWyM6e5wwfp>EP|kGYKY5$_=aSmCMCzZ*4r?!1 z_s2$c>i36R|3Jg(D@rmGS*VyZ+DTtf=G@~X$F&y`JUC-*V`kzZC%HA0s9w@N%=)LC zq>ojq3N{I@q}fdTmM;D{m>S>@!K#X;ma%4eoUFdaly6(l$7`iyB~JR5vcs_N zw+ifASQluHlw~Sj5jI4X8g=KeTCnfR_nW)#HwQKD9Dr+Iw@dSJ|4{^f3UVZ(}r8zue6xJVVd9Jo? z=~V^sMbxLdGET2EW3`j6c-~zx=btqDjYx9z);+jDZM=h_1Fr|+^h#HFZrj}Li`a8n z)IHSJ4=~A|-sb)^y5#yj?XUIEG?s;E$^RJ?O12GX?%6HLS>>4)yUtTWH?w;$HE=GN-=1Wd{_g_A}QErE+5Y!tq`0hl>4! zW(ll$n8+4zf`w0)v25|LfE>``wP>cPw>~@jMQ6qed{*4+Q@tG*NoXbO+57Q@7iR{W zA%d%|y|*~|8e*^;y7b1^b|nUu9B8cH(egq;3zAbnQ@cI;9XhLDee}xr_bmdr7zKVB z5HiySERy(Yf44YV|H|x(<(@ehpTQ9XTm18HW6>$of{%vK1}>5$;=5LMd8$opk{N7d z2qcbzQ3KhIP}7XI3(_S^;%oiJ`R9b{*SD;ZRh~z{Vf+(#QRF;g;XiZ5cte4G;rOd; z6dvzyC)y`{X-UX$f46`8>;3DOgl}I1pvi;d=X@gu2S+ordh6evTN6(=l`&wQ86D34 zmz>O~)qVbn3Xjl)cJWL7(mA_Q>l>@vLLF}JtkdJb4-$#RKENO^3;42w>KT4XANK5Q z*+m#z^WLwlo(8pu`sQmneGwwDt#sbqH|lUR*_tZ8I8HZIi2pKu|RWKeh*UU+_jW=A=-|s-BRoLsXknb?WHGubBYzP^j zJT!hzb7Oi^N&BJxF`OaQdt%y&@JE13q`ykHo{z${I!`fEvKExEt6(K-39l0jUS@$y zl%qcHYS|g9Up?nj(PWmu@k`6%GoFrBujbrG`on3uQ=Tq!a`>P_^-F}0w&n%~Dd?Ov zCTar566r}u&D!2r^^&U=;C5|SpVj%TjYPhi&c_1^IBU1KZ8vRT%;)G6t6h`5$#ALl z|50^n33!$%KB+7*W660&*iN^je+cqV@^^N7Sy2n?HdMAFeL)G(hv(mA`i?(lMteLt zuC1tLR3_Ss#2{=J|DVdmle60>H$2s@aBQdx%Q)0Y#n!85|H3h%R zIf=t;&0J>0PdxoOEUMvj_ES#!6#SI<)XLD^UW^x?D%Tzjl%c|XIa1|+d0Q_FAT4F& zM;AVT;}ph!(U0+3XLEu#T4UQ!PrReV?(C_b87XhMhy5NXS)tjH8tgVa_*`bjevt^_ z0k0TsjOnkI-q|>6|4qbz+cxX80J@ekLSK?kIk}lV`qk`|ZY$~3-WW5H3L+<6-p#sn zf&Babo4?WnV6LrmC#Q!pZC>QAm~{ml#w|MM()~H&Fb2?ar+(D4*Dbs846zX`b^<-% zb*Cd9tAFOk-&_8A=DI=OVQOiSMmut;AG(E|5$u8r z{NiRht$(5Ucy>sjYc&t{=i%VC%EwGJ|Mc}G(TwX#m5gOdG42f( zC{pj%FS}(;W-5f*4y0M5UuT>dTQO*gyGJa*H(p%XNTYbiQaT%#I*j&`Pa6 zka`D6JL@du2`aEx)B=CVnc`GG-FML-Qv=T-Rf|Wy*QQ~*{KeeCV*uSAK zLm&SAlZ+^8UJ?}ju=eNV+CSq1?bm;F`zL;M`#-Z9<&thY-j6t*I}6A2lIkU9Jg?~+ z&lrY|PH^uV&&(K*!frUpjpy2%!0-Qa{Tt|x3I6&({i|n5UTEJW_94;XwQDd{-WDs< z=;5~d7%VyVMG{-a^k?A4G>73~rK5IHe|>kj^&$jVqJC4j^;#a%u|SQeb&O`X2cFzr z)ba_dMjY2i&jNj-zttzA%pfIE7WtApL$m4(_s7u^lj9D-@E=y*Lm&;H5RA8dLZGOJZoV4e=RVVrd0u_mIB+DT-GXg=6RFlJnAoG1e|F;}kdlzBdp7nM-?` z@KU_M=ErFGPbHC5arRX_-VJfB{-d=|W`0f&+~VJp z&)nKfhHSA$>!urwzr)g7yPGjgXY8om7tnj!VRFk6y|0W^skTq;P5?hQ6-^~V4^uVwi=AHI?-cdtvV*E*dnYksJIY;rxW~vkWG&sRHs4;wmS0UWG zki3EXniyW37+sXPursxM-hqMPAHKz>WVQyw%&F%==cJh(4Ps57rH`2k z30IsMY4A@+N(ii$+2Vb?kC4FbWk@a8B->ui%u*6bj%BBgN>V*_0~^{)xb;so;Qxx% zc2h-tJabMcF}68U8E&0Iw?KlRb}t02(RsHNof&q|;ju$#?(JS<>G5cP>VVl~W~M44 z{$1NKq$&WJo!ohTW9O_pks9H+X*sz|EVeAh%!N`)>LfR>-qtziL6!|2#Is z;=KNl^lSM2$NcXA;o2By5l%^s0>2nme?%KHvTLDzK7Zz%L&=_@HsAxejr=nae<>M05i2bI|1V41F7q0#3+o3a7#`^A_eOj^uyDmTU*&$xL|n5$N%{3(BK2@& zJ*j^@>XrNlkMYCF+#AszuSCXLkVI=pxVA>x^ijf#P|z=HYP~)3kjZ~nhcs6*g^Y}&zja9%TFc6YhY)tb^>u;zzj9zsa4jE@~pslcLN~!SHr-Zr!k(D2B zG(%PXu|Z2MKedKtYKPx1_v(jchS&Q_kf%(&(@gFe!aMN>u=$NLcX|fc8X?~NY9iXV^Fp%&a3sEtla{6>GIPq#OON}ME?;_^Fw4)PB0X%nn#h!#<<$3F zKZCU4w2AVR;ybVfeQ5VJ41ms9h7| zewY1wS@l|O)`bVINz^|bzHJReb#iKH_DN<|XbZchzrCv}$vSUvav$n%VtYsKp~CLDs0s{#nPPs1f`*D3(E(w2{v~fY z&H{B%Lf1colYXM00f^ukZ4 z0Af~ooPtTO)gUhqep?`TCQ@zSxq)1x`GptP7*e4FgaIyz*RKx$;CMRGM0Dcu*&&7c zkooqm=p{#P=m3P>&M*I|Z)*+A($BvX0EOf*B!?D|#(&N*RM>rw$}Gx7l0*J`j0a_X zL+^_pw6ZGsouW|oXNF&nG{2{o7uv_D;>JE+k;>2U(%5n0BSgylu{~;#nTc7b;xK>L zFhItBji+`;pERx*Ysfw>#?wFM`*p^;A;{ziUN`OB&)^G9q#{rPJzZ1-uyTqP`SNGt z@x*z?5&lmjXlr}@hX_D5HfA%fvO;_ZEJZT_^fg(MplYXo~p5t=JmL;uhin26i&%;gIVYc;o0~8&QZ&Mf3Y*CjoZA zAKa~HmAbtQ7dp~`Y^^b$rB^_^w$BHJPUNkB@;5(3zn%Jo&?g_9bqj1`4%pG0mdLbDWlDrLS` z=?Oeii3Vf zpVVrE)~hZln~)iNP<=&wp2(^dn+FDV;=bI`7&_E)GGwNyzg{WvWMG)p&rrz`h2$fO zCVP8Z7SR@qvSo5d-|uA34e4SFvU>vz6)K%GN+LQ(N^r6GPmRR@FEyvn`;+p+u8@92 z|Bops$I;l0AdLWnmWP>Jt9o0?!~_yICWn(jQl(zC{s-l{!(hT3$7A?kKs-@K_U_w) z{!C`1P{=DfQy6Sr3?Fp=$RG6$5xRl+YxeTtZqB*&t_{1u4mXo@BKu&dQ6a|D|FUUKr>GDA)tPCD;Svx>I#A6l~)%bI;D zux2-4&31c@!^?;8XV{=7_lQDYBuFVFpE;f*a*e_x80qTRX+z=fvW%-NgJ z+F*lmUq95_{ZoG#{kHVU`$PXOUw}?W@)=p;wz57m@!5RD5HbWIQ83(EBkRcz%cmYR z%GTlYc^l|(GCAW9jYj$`3V?)_{_|h9Q}6sYP}R|jBV}e>C(<>XoQ61mgFGsKpGhA! z79(@ND28q>2L*bGf8$GNbZw8!BNIe5?uw26X)yfRzd-#B-|;LT;E!pTaz)W6tN8m^ z1AllmS#GPmeO5+Xfw5O={D(ILY(E?&p(dWWrQH8z@G9K8g+h(>@6I`+X~q@;5KB1w zJcob8q=s8G*%9&=02Z4f%A@rgXK#^#TF#+*x4`oQWS2;z;>?0J|662}$|^kOCw&dh z$ztbQpAtO^CR#hMoV^nm=0=k}lfrkdr<2K^;~GyaG0FP4NaSkInP?J5qnotv3G zSB~3hyvZ~c>2Y#)fDyW&L-YS*Rs0c#Xv&~Pn99Dv&{XfukPhwh?*mYYg0L(1oQ5-D zYnc7BWMgQJEZq7XJ?aB7K}F$ej!ggxwnU)evpXE+98%cKa1S7ogVK4Mq&4`)l1cpD> z(XMzkCq_*z(<8S^CohSBG7!5ss?_vf_mrF_Tn#6Ry{LBa%RkskM#iY0OHRyOM1sF=A}R09P%* zW8KoCvyxiB28N@_ZK3F5xd9A%;xnFd(sA^e5@ITLJ8OMyD^D#;ZVqv3aJW_7dmx05 z&S+XX295ehqN`5UxLJ*hdF|$bucW%MrCKY=ON%279ci>VEeC7&J!W|4RP;u*J4JS= z#sYpK|0^%xzu$?1o*PfmC&kr|iAlwupu+H{_%z#S%rNX&8*6s!i#hh3&W9*cs-wua6k!M?M;!WIwZL z!OEmCU53?UsMs92+F$T0=0w0&hF`vPMRs;Da>{BqpnzlfHAk+2%jI(b@66zh_#<#t z@Ji-bO_YdNmJv;Sk8xxyRY)4~Rsrp!?A)?&>@inezC+L@ZCf@91HiP)8tb>u?UWni zYFTt6mAiIBBe`ZZrtgo;1C#8@>Q$lku96(Q<`CQ*KN{dN*Od5+U+L>n^fJ?@=;b-y z%-#ov)cQ!jzsOh{;Ij-JWMnkOasBV%f;H@kqfHFUp~cji>s*!TzkVrt{$Kds$iQXe z0~amMj7*ND)^#7ffbQnHPGHB#<}cgMGvV%wg|gJ#!v`1+Vps4W@)*?R}u4-jnzFmvUiq3 ze866fdc@%krB(4vjQ!7uh3;59b0hM%*Wd7*F%#ngiQ_!5b$nh=&OsH=v{XPYi3x(R zJIejBf@oUwOHK8Mnj;1|WEWognTneK%McAHrl*!=FU6S7|IYsH02al{%dnyg{pGbQ zOhlBzNZvNb%q6#7?WD#LYapK>s%4EmmeClfOz&4B9`mNgG-TF0tL5pywXxA33JiAj zbv$cftg+$lM#}8;-8J$o_`(h=kNM+GN~H)SpSrqWde7TQ*dROoC)NfPw)_cnN0>10 z_%}1fLUkM(OazL6b<#s?+q#dDTtE^l1h%Ka3V6a%+pq7Y=qMG{;VR;lF)GS=T8$kP z{X9i0^Zsm=Z@1-#D0leR-u2z}%mN$37p&HX#uwn9!Tx3jxanxvJqAU>I8Ao?&-T4N zh@3+()Mzxw_T+FBJfra^^jrVaET|f=k$uTG1hQ6~2eAtl}Are+2iF+-Lr`gF1g=>ttn$I*sd6lvSP9zxxmBO$PO9 z<3Rnh1RnF71GH9~*9~xG@Lq=!^WFeWuwA^qC+v10=<%R#mb_2Ge)WeqsYmbDWPd~g zV`LSGj2Ia#i09`gc%lyJ%zEojS zd|sdbOkj`%@#KVbD`UhYB-(ZH(KP=LtAx_0stgNllpwD1zl5-jnw9yBj6-#!W*T*c z|LMwu1{wJ-r8Nd0)geAu<0DKB#s=K)x8ve7^)!FKPyRIuWV%q>_HB+TL<<w|L5Fuwc>o1O`vfpvlb zQ6sKc+4p{W@ScflhyRzp_fGJBjMe*6&*p17EBMmDg2Z)wua6I2m-G7azSl*;>q=Ws zwC~HCFum4{rfK@BB$`Z1mUQ=aGt=9JBt&_+{Ny$JLKv#m^j6=OgLv_=Ub^2yf}$Nf z#XDBauvTXm?-_-Jt%4au$TH0;>08Ur}Y!nXvF+OBz!q$|3&}#;54= z?zg4G^Y=;w8mtN^Tf;vppIs~=3W!W(8fgH3f&o&eiKFubBrdh@II!F-M4E|Ux0%tJ zr!%I_`ejzvpVUUzK#f?03F(3(rL?UpE&Cj;uT6jdZ zdN+G_)Dotg7L}w8oX4$GOifof$;S;=fB6A%bYoJqJ51voyQ)Ame7*N z+yf{MY`}UDU#AHaGkSyRM|PJ{Uyvi<28F9e=3u!A_dwHP3Cj$!6uVNGno-?%c-X0r zQ>H*32do&7Oti=Wk_X^MqtFn1+43{$pCaAi1ZncLKwwfRR{MsNY0jyptZo4hdTCS# ztVl8MvWoSR>$hrX8Gfwv*Md_+P(tK#^AgcZ{>^{Szdu9oQDvPKMzzXDeVW{PQ->1IjeE z9Sxbs$q6wkk&Z-U-;L2Ig~N3v4r_*Xo*O3cl}D+(kEz1 zRIL&zVONg6hqTrNm#&p8EA~HW7Nx@Oe#urzfc<_m>I2J7WBWvylO9Ef_UFc?2rZNT zKBd%8hix>1UvG-GoStN5Nh|1MST!FPb`JoEI8aw*^yyRh>_&x*_8PuJk&6m1X^;z{ z+*madR5*6PXLqyV3nnHyAQ|;_BRRwF1OqhDF9@>z{$O0CLhz@}G+`mmOW`BIp1>Js zbjZX10bWMf23)y`tr*Fs5cZZoto^{* zfBSjGfXEJ`0|m{A@de4tus3v>*ZqWFdwyShws8{%*0=&|h8IXzqcbND^_vqVbeqG6 zC8VmfWVfn`V{u^EGXJV2fRwpmJfnpTYyOs{8bf%lt(IUH#P*O!^JZUoDh4{2WoU3F zKM{w~;iK zjA7s29$rz{R5Xw~j@~1tw)-=tD!n{sZt(K+-6xvL^zy3Q%S*cptHV6s z1umV`6O|w%_16mi7L3?fPi>p$*6{Ko&#fhy?YW;NdDC<2NNPNH6v=NrcQnb$zu>O!3?=lAPeV<4Ml%aCJwLX7d`hpk|xhZ67?SMxe1a}J-3PE2G6~K1Q;ig zkYzFnEu+}>j`G}#Na*6lBo}wMQ%G#f(|DqhFOh8VTwIU6>pk~!lHYsomr3aQS4b#& z1<4T4{VK^HJ$E{Z>$zVe8S1&L+4jEexid&Mc`l&=y|;NTE62SLdhS&umw7I661{sn z+^b1!%QJZ*mp0%7=66X}c<%Q|7JKeBBu{$owIqu?mq_K_Cp`Ch`>UCx#dBLows|g_ zCnP8)$lO@!Xq8&h^~ylT7s7n@O5GTrF(amgn&l^W0lW{@}T{k(79@+)W_8 zm89NtU6LPrE~h5+c6ja&NtS!=e3F0l+!V=mp8GE(=XtKSi~rPfZzs9gGinCtyn|m~ z^V~a0ZtHLtkl2>*;^|7y>=Ie#xj*68LC^g+lBu4156LT@`|l)Q@Z6u0yy&?4CxLq0(bM}$&;0|*bDsN0k{3MpPb4Ef_aTx_&;4(b|MuKJlRWIXe<7*!j7M&l z=gPJBj^{2US?RfdBU#?zE+VllKhD#;p8EvJL!PT>ik+VOB+2bWJdglwyZyF=1nyf( z@`mR=MRKv{E+bjxxgN>Cdu|5_+`OEG&aNQ2)^ndG`IhIdB$?ac{+-0O+{x4Fp1Ya^ z%B~^#ndh!0`M&2qM?$~Wku-Sj^CZ9U+!sio?0S-`J@-YDsOP>!a*yY3AbHetUnV)r zb6+6=s#i(iw~ZuThucMBTYiHl8re+plIOlja+>FEA$i(!eUe_!&5}Ijxm!sXy4@sy z_uOqHj0sJ8|K_R?5HNxpFl@r4R`(`;kDNVv=^xEg{+NxuqnqWPcLi z(DHo04!4ZNwp`8=d^n5*>3K8>(o??o8qcjD`K{+3O9BRtvoDV)`Ksquk{~@#AVGSb zNV3;+Pa*-o!$}YhCzDL^+*3$S@Z3{Lke*c}w&e&<7kcjLB>g=1pGXFH?kDZbkt7VR zYWwn2B=D&06u9}*BzJi3nIsXp zRwO^k0wg~PRM&)gK9Zm0dq{qglaTx*=OXz@7|0DI)F%bto1QBr@@lMDmlQk^CgpNPdz= zBtHop`wbFWzKUczlAmO>=Uz?nj^}=hgi$30ZlmY^GfA80N-3#iE+B!0zDqKRxqxJ0 zhkFf)ZMm5zXxT#YYa~BO50amRwq}vglbcA8)ZZr=jN~Vwz-*GsJogroZ+Pw;5@g3* zl3TslykorBtv~l-w;k)nl4Cn!t){8i{6;U98s)|QWxE$+!*lHR+q_t2uot`ImtO47 zZZEdr_g?Hry!&yx7yH-mda=81_F{MM^kP3D&%d4C5xd7$bMHPc_8(_?v7e9kV!ycC zi`{pJ7yIQ%FZQcnd9h!g>c#Ho`8TxwpWD3He_iXv9(cfu{q`y^_Pc5?_WPGRVh`GC z9y-H|{de4p{duJq`^)#d*u($s#r}Ga7hCwE7yH{Eyx5|`%^F0$?w(gc(FA!vG&xC*mJg;^%Q%N+Fwd|u?+zHGM#*7vln~yw_a@H zjb5y)%Zt5spBHu|o9!q<8ZD72@eXsHww^1h`K=p{LRtzx97rdHQaZdLNlGlkq!Q z;)FU2TRejnlcvQ`_niy42(|~mqIGOGm--pLt}qrv5PEydawpZ&mYVvwlWKa1-2CkD zQ^Uk?u`VZh%R@!Y+~XO0r~?%7GI>i^QOh7>h-Bq<;!~GO)P2sB@cHfQ_vU&IhKjA{ zl&Xi)O^}|vNZQIY<_n5*|1*2mZjs< zx;WxR?wI+_2EFSrfcUfMlzGCYF@q$KhLh`puv>DXIiV{cslsr1yhOn*Y1;e|!v#cja-`8~$8ySuHmt+^x_doPrX zy%p+y1}ucy>SOPPTW>YxyE1HnOqYJ|Uw}6=x~dS^m0y4(4r}L=?{CLRA3Itl+11tI zk0rlpgww4`Pcg6CO;DC`Fc28p^Ad*m)Eo-3&j3JBe@CpRuRGzSf@j~~6b)RtyRSemG ziv?~UjI;Zy8h2P#^ZH=?L19&A_f?fVysEmss&-#s8?l%xsA_CsRc{w`P+t5#sJ6b4 z@8ExaSXH|Y6m(F>`vpWu9$wY2`l`D8!>amCUsWyN3aTlFJqjSYqOY2Y53A;if@)6h zqaz3_>OO8k_c05)%NKMHSdK~Hc;_1AG8*7Qg)5ua+u(b21C&8{kn~O3jnyqm@=b zQTD;Z%GzJ~viIi8nkQTK&V1Rn!^+xU`LYk?%bF)!_Tqe5MSAqXY=7m;N{v=$oKo{- z%WA>O_SZkGto@ZQi+~NvnkQQp9VI9$-Kei!`zv2|TfVG$vSojfFIx@-eP!*hd|5NC z7?|w@W2WaBxw0yEiGiM3Jm#m&-*i!`ZE=;dz?S7TaAXQaP&%f4%6_vva&KGl~O*OmB=HO@je}g|ivWRGVcoDe} zKC*~d>F^@8|EP$#;P4`Hetl#sBL3k;MeaGQNCT)|4td%N=N}dxz;hc) z^p83%6Lg;?CP4R0-m<=^Wf%(Zt$gwC{|t9e{}!mAPN4m*Hu3F?bM(269FBAYIO!)p z#yk-tEl-tWxKpesdCPN0-B^{5aa&wt=^M;jL#g@-RUM~b_k~I9`|g)ib^G zPv;YZ#_XH+M}jGoo;Mz*0-^6sHHF7lBR?dyEA1J|> z%(0QhR9@fNd{QnmL<;Agsq@MYYcJV0Mr|&jy9H9*wAfIYm(v9-0FUddotdgDii&cO z=6aDi_R?#_8Onq(CrP$VQyX%e+1DNZmyaswW3Hqr(&7Ix$hY_!VV;5mKBb}i!JY^8 zJ_kh#U^g=mt!gx;%5!r~-{1v)3zqr#!CJv5$cq|CgPgoF_@AaE^+GTE-P@W^{m2}_ z`mcrS9~#%|)y?lq8Lp3YhW||#YwIr!-&ujWR%VW@da{jIHaQ5UT7%fm!^g5yGneCJ za&T%)?q`n*OBc0u{e`b)CEeKRm*c)Nt zIT=EE4_I3Dz2C%la&r$(qD6)Ck992|+B^K&21-Co#}+wbV_i2M|NpV~HgHx|Rpb9b z!I02FlOjdEDCnRA4zKbu=nTx@pffP?q9~9IGZ&b=^Ww}MUJR51jcJ(DqN3vIp|YaF zqBJ9#1Qi)FQc6=Q(&@?SPD4s%Iz9pS_g!o6eeOAT?g%|SpU?mE|NMUFo^{WDS$plZ z*Is+=wfEVF7VZ*bx7S`VKG*LZ1asO~EP*uT!yZ{dN)~-q%0D7FZ1O?HCv4gU%DvVk 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 O4VAQ~ss`2D!T$h|d)hGo diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c index 781cc96e..a5516dd8 100644 --- a/ctrtool/ncch.c +++ b/ctrtool/ncch.c @@ -416,9 +416,10 @@ void ncch_process(ncch_context* ctx, u32 actions) return; } - if (getle32(ctx->header.extendedheadersize) != 0x400) + const u32 exheaderSize = getle32(ctx->header.extendedheadersize); + if (exheaderSize != 0x400 && exheaderSize != 0) { - fprintf(stdout, "Error, exheader is 0x%02x bytes long, expected 0x0400\n", getle32(ctx->header.extendedheadersize)); + fprintf(stdout, "Error, exheader is 0x%02x bytes long, expected 0x400 or 0\n", getle32(ctx->header.extendedheadersize)); return; } @@ -612,8 +613,9 @@ void ncch_determine_key(ncch_context* ctx, u32 actions) u8 seedbuf[0x20]; u8* seed; u8* keyX = NULL; - u8 hash[0x20]; - ctr_ncchheader* header = &ctx->header; + u8 hash[0x20] = {0}; + ctr_ncchheader* header = &ctx->header; + const u32 exheaderSize = getle32(header->extendedheadersize); struct sNcchKeyslot { u8 x[0x10]; @@ -625,9 +627,9 @@ void ncch_determine_key(ncch_context* ctx, u32 actions) // Check if the NCCH is already decrypted, by checking if the exheader hash matches // Otherwise, use determination rules fseeko64(ctx->file, ncch_get_exheader_offset(ctx), SEEK_SET); - memset(exheader_buffer, 0, 0x400); - fread(exheader_buffer, 1, 0x400, ctx->file); - ctr_sha_256(exheader_buffer, 0x400, hash); + memset(exheader_buffer, 0, exheaderSize); + fread(exheader_buffer, 1, exheaderSize, ctx->file); + ctr_sha_256(exheader_buffer, exheaderSize, hash); if (!memcmp(hash, header->extendedheaderhash, 32)) { // exheader hash matches, so probably decrypted @@ -831,7 +833,7 @@ void ncch_print(ncch_context* ctx) else memdump(stdout, "Logo hash (FAIL): ", header->logohash, 0x20); fprintf(stdout, "Product code: %.16s\n", header->productcode); - fprintf(stdout, "Exheader size: 00000400\n"); //this is always the same + fprintf(stdout, "Exheader size: 0x%x\n", getle32(header->extendedheadersize)); if (ctx->exheaderhashcheck == Unchecked) memdump(stdout, "Exheader hash: ", header->extendedheaderhash, 0x20); else if (ctx->exheaderhashcheck == Good) From b941838adbdee83d9ec91b735bed71004109bc1c Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 20 Aug 2018 14:17:32 +0800 Subject: [PATCH 218/317] [ctrtool] Fix ctrtool bug where content wouldn't extract. --- ctrtool/cia.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/ctrtool/cia.c b/ctrtool/cia.c index a0347633..6d4cdb88 100644 --- a/ctrtool/cia.c +++ b/ctrtool/cia.c @@ -42,6 +42,7 @@ void cia_save(cia_context* ctx, u32 type, u32 flags) u64 offset; u64 size; u16 contentflags; + u16 contentindex; u8 docrypto; filepath* path = 0; ctr_tmd_body *body; @@ -102,17 +103,18 @@ void cia_save(cia_context* ctx, u32 type, u32 flags) chunk = (ctr_tmd_contentchunk*)(body->contentinfo + (sizeof(ctr_tmd_contentinfo) * TMD_MAX_CONTENTS)); for(i = 0; i < getbe16(body->contentcount); i++) { - if(ctx->header.contentindex[i >> 3] & (0x80 >> (i & 7))) { - 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); - - contentflags = getbe16(chunk->type); - docrypto = contentflags & 1 && !(flags & PlainFlag); - + contentflags = getbe16(chunk->type); + contentindex = getbe16(chunk->index); + docrypto = (contentflags & 1) && !(flags & PlainFlag); + + if(ctx->header.contentindex[contentindex >> 3] & (0x80 >> (contentindex & 7))) { + sprintf(tmpname, "%s.%04x.%08x", path->pathname, contentindex, getbe32(chunk->id)); + fprintf(stdout, "Saving content #%04x to %s\n", contentindex, tmpname); + if(docrypto) // Decrypt if needed { - ctx->iv[0] = (getbe16(chunk->index) >> 8) & 0xff; - ctx->iv[1] = getbe16(chunk->index) & 0xff; + ctx->iv[0] = (contentindex >> 8) & 0xff; + ctx->iv[1] = contentindex & 0xff; ctr_init_cbc_decrypt(&ctx->aes, ctx->titlekey, ctx->iv); } From 7b0a7bfaab2ec4c04167b23c557e438bbaca4fde Mon Sep 17 00:00:00 2001 From: Steven Smith Date: Wed, 17 Oct 2018 05:08:29 -0700 Subject: [PATCH 219/317] Fix verifying incomplete DLC CIAs. (#76) --- ctrtool/cia.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/ctrtool/cia.c b/ctrtool/cia.c index 6d4cdb88..85b16b89 100644 --- a/ctrtool/cia.c +++ b/ctrtool/cia.c @@ -251,6 +251,7 @@ void cia_process(cia_context* ctx, u32 actions) void cia_verify_contents(cia_context *ctx, u32 actions) { u16 contentflags; + u16 contentindex; ctr_tmd_body *body; ctr_tmd_contentchunk *chunk; u8 *verify_buf; @@ -264,30 +265,34 @@ void cia_verify_contents(cia_context *ctx, u32 actions) fseeko64(ctx->file, ctx->offset + ctx->offsetcontent, SEEK_SET); for(i = 0; i < getbe16(body->contentcount); i++) { - content_size = getbe64(chunk->size) & 0xffffffff; + contentindex = getbe16(chunk->index); - contentflags = getbe16(chunk->type); + if(ctx->header.contentindex[contentindex >> 3] & (0x80 >> (contentindex & 7))) + { + content_size = getbe64(chunk->size) & 0xffffffff; - verify_buf = malloc(content_size); - fread(verify_buf, content_size, 1, ctx->file); + contentflags = getbe16(chunk->type); - if(contentflags & 1 && !(actions & PlainFlag)) // Decrypt if needed - { - ctx->iv[0] = (getbe16(chunk->index) >> 8) & 0xff; - ctx->iv[1] = getbe16(chunk->index) & 0xff; + verify_buf = malloc(content_size); + fread(verify_buf, content_size, 1, ctx->file); - ctr_init_cbc_decrypt(&ctx->aes, ctx->titlekey, ctx->iv); - - ctr_decrypt_cbc(&ctx->aes, verify_buf, verify_buf, content_size); - } + if(contentflags & 1 && !(actions & PlainFlag)) // Decrypt if needed + { + ctx->iv[0] = (contentindex >> 8) & 0xff; + ctx->iv[1] = contentindex & 0xff; - 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; + ctr_init_cbc_decrypt(&ctx->aes, ctx->titlekey, ctx->iv); + + ctr_decrypt_cbc(&ctx->aes, verify_buf, verify_buf, content_size); + } - free(verify_buf); + 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++; } } From f19dea3482e74e78ac284ad43b15dc5af44e443f Mon Sep 17 00:00:00 2001 From: Margen67 Date: Sat, 27 Jul 2019 02:37:35 -0700 Subject: [PATCH 220/317] Create .appveyor.yml for CI --- .appveyor.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .appveyor.yml diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..8ed1c448 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,29 @@ +version: '{build}' + +os: +- Visual Studio 2017 +- Ubuntu1804 + +install: +- cmd: set PATH=%PATH%;C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin + +build_script: + - cmd: | + mingw32-make -j 6 -C ctrtool + mingw32-make -j 6 -C makerom + - sh: | + make -j 2 -C ctrtool + make -j 2 -C makerom + +after_build: + - cmd: | + move ctrtool\ctrtool.exe && move makerom\makerom.exe + 7z a Project_CTR.zip ctrtool.exe makerom.exe + - sh: | + cd ctrtool && 7z u ../Project_CTR.zip ctrtool + cd ../makerom && 7z u ../Project_CTR.zip makerom + +test: off + +artifacts: + - path: Project_CTR.zip From 3083d87871b4fde35a2ad4de45fb8c37f59cd737 Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 2 Aug 2019 11:08:11 +0800 Subject: [PATCH 221/317] Update .appveyor.yml --- .appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 8ed1c448..7c0550fe 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,8 +1,8 @@ version: '{build}' os: -- Visual Studio 2017 -- Ubuntu1804 +- MinGW-w64 (Windows) +- Ubuntu1804 (Linux) install: - cmd: set PATH=%PATH%;C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin From 06d44bf5f034575683e41ecbf2192c79d04bdca1 Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 2 Aug 2019 11:11:15 +0800 Subject: [PATCH 222/317] Revert changes --- .appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 7c0550fe..8ed1c448 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,8 +1,8 @@ version: '{build}' os: -- MinGW-w64 (Windows) -- Ubuntu1804 (Linux) +- Visual Studio 2017 +- Ubuntu1804 install: - cmd: set PATH=%PATH%;C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin From ce4f09ba0e77cf463eb58a84c9d416155a7adda7 Mon Sep 17 00:00:00 2001 From: Jack Date: Sat, 3 Aug 2019 11:48:38 +0800 Subject: [PATCH 223/317] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index bbbe88e9..cb927b98 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,8 @@ Project_CTR ctrtool - updated version of neimod's ctrtool. makerom - creates CTR cxi/cfa/cci/cia files. + +## CI Builds +Thanks to @Margen67 we have AppVeyor make new builds for each commit to master. + +Download them here: https://ci.appveyor.com/project/jakcron/project-ctr From 501c1714b386ae3ad8433ad3dd867f2261530f84 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 13 Aug 2019 21:17:40 +0800 Subject: [PATCH 224/317] Undo regression and re-add `-ckeyid` and `-ncchseckey` --- makerom/keyset.c | 6 +++--- makerom/keyset.h | 2 +- makerom/user_settings.c | 19 ++++++++++++------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/makerom/keyset.c b/makerom/keyset.c index 4ff402ec..c706c912 100644 --- a/makerom/keyset.c +++ b/makerom/keyset.c @@ -73,7 +73,7 @@ int LoadKeysFromResources(keys_struct *keys) /* AES Keys */ // CIA //SetCommonKey(keys, zeros_aesKey,1); - if(keys->aes.currentCommonKey > 0xff) + if(keys->aes.currentCommonKey > MAX_CMN_KEY) SetCurrentCommonKey(keys,0); // NCCH @@ -101,7 +101,7 @@ int LoadKeysFromResources(keys_struct *keys) for(int i = 0; i < 6; i++) SetCommonKey(keys, ctr_common_etd_key_dpki[i],i); - if(keys->aes.currentCommonKey > 0xff) + if(keys->aes.currentCommonKey > MAX_CMN_KEY) SetCurrentCommonKey(keys,0); // NCCH @@ -133,7 +133,7 @@ int LoadKeysFromResources(keys_struct *keys) for (int i = 0; i < 6; i++) SetCommonKey(keys, ctr_common_etd_key_ppki[i], i); - if(keys->aes.currentCommonKey > 0xff) + if(keys->aes.currentCommonKey > MAX_CMN_KEY) SetCurrentCommonKey(keys,0); // NCCH diff --git a/makerom/keyset.h b/makerom/keyset.h index 2a35b382..887f3de4 100644 --- a/makerom/keyset.h +++ b/makerom/keyset.h @@ -4,7 +4,7 @@ typedef enum { AES_128_KEY_SIZE = 16, - MAX_CMN_KEY = MAX_U8, + MAX_CMN_KEY = 0x05, MAX_NCCH_KEYX = MAX_U8 } keydata_limits; diff --git a/makerom/user_settings.c b/makerom/user_settings.c index f995857a..67f4a5d2 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -89,6 +89,7 @@ void SetDefaults(user_settings *set) // Build NCCH Info set->ncch.useSecCrypto = true; + set->ncch.keyXID = 0; set->ncch.buildNcch0 = true; set->ncch.includeExefsLogo = false; set->common.outFormat = NCCH; @@ -115,7 +116,7 @@ void SetDefaults(user_settings *set) set->cia.useDataTitleVer = false; set->cia.useFullTitleVer = false; set->cia.randomTitleKey = false; - set->common.keys.aes.currentCommonKey = MAX_U8 + 1; // invalid so changes can be detected + set->common.keys.aes.currentCommonKey = 0; for (int i = 0; i < CIA_MAX_CONTENT; i++) set->cia.contentId[i] = MAX_U32 + 1; // invalid so changes can be detected } @@ -196,7 +197,6 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } return 2; } - /* else if (strcmp(argv[i], "-ckeyid") == 0) { if (ParamNum != 1) { PrintArgReqParam(argv[i], 1); @@ -224,7 +224,6 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) } return 2; } - */ else if (strcmp(argv[i], "-showkeys") == 0) { if (ParamNum) { PrintNoNeedParam(argv[i]); @@ -903,7 +902,7 @@ void PrintNoNeedParam(char *arg) void DisplayBanner(void) { - printf("CTR MAKEROM v0.16 (C) 3DSGuy 2017\n"); + printf("CTR MAKEROM v0.16.1 (C) 3DSGuy 2017\n"); printf("Built: %s %s\n\n", __TIME__, __DATE__); } @@ -952,10 +951,16 @@ void DisplayExtendedHelp(char *app_name) printf(" 't' Test(false) Keys & prod Certs\n"); printf(" 'd' Development Keys & Certs\n"); printf(" 'p' Production Keys & Certs\n"); - //printf(" -ckeyid Override the automatic common key selection\n"); - //printf(" -ncchseckey Ncch keyX index ('0'=1.0+, '1'=7.0+)\n"); + printf(" -ckeyid Ticket Common Key Index, defaults to 0x00\n"); + printf(" 0x00 : Applications / eShop\n"); + printf(" 0x01 : System Titles\n"); + printf(" 0x02-0x05 : Unused\n"); + printf(" -ncchseckey NCCH KeyX index, defaults to 0x00\n"); + printf(" 0x00 : FW 1.0.0+\n"); + printf(" 0x01 : FW 7.0.0+\n"); + printf(" 0x0a : FW 9.3.0+ (New3DS only)\n"); + printf(" 0x0b : FW 9.6.0+ (New3DS only)\n"); printf(" -showkeys Display the loaded key chain\n"); - //printf(" -fsign False sign digital signatures\n"); printf(" -ignoresign Ignore invalid signatures\n"); printf("NCCH OPTIONS:\n"); printf(" -elf ELF file\n"); From b3cb4aa1dca1defa5b014d476cc4b42e2a93c026 Mon Sep 17 00:00:00 2001 From: 3DSGuy <3dsguy.dev@gmail.com> Date: Tue, 28 Apr 2020 21:17:22 +0800 Subject: [PATCH 225/317] Add version text to CTRTool. --- ctrtool/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/main.c b/ctrtool/main.c index 3c949586..1cbc04b8 100644 --- a/ctrtool/main.c +++ b/ctrtool/main.c @@ -38,7 +38,7 @@ typedef struct static void usage(const char *argv0) { fprintf(stderr, - "CTRTOOL (c) neimod, 3DSGuy.\n" + "CTRTOOL v0.7 (c) neimod, 3DSGuy.\n" "Built: %s %s\n" "\n" "Usage: %s [options...] \n" From e6baadf069ccb5c0dd3d1a78606a160f5e3f4f27 Mon Sep 17 00:00:00 2001 From: Jack Date: Tue, 28 Apr 2020 21:27:29 +0800 Subject: [PATCH 226/317] Bump version to v0.16.2 --- makerom/user_settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 67f4a5d2..c6df9663 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -902,7 +902,7 @@ void PrintNoNeedParam(char *arg) void DisplayBanner(void) { - printf("CTR MAKEROM v0.16.1 (C) 3DSGuy 2017\n"); + printf("CTR MAKEROM v0.16.2 (C) 3DSGuy 2020\n"); printf("Built: %s %s\n\n", __TIME__, __DATE__); } From 81fb1774d569f1d68ffe2848784fcae135377950 Mon Sep 17 00:00:00 2001 From: Jhynjhiruu Date: Sat, 9 May 2020 17:07:31 +0100 Subject: [PATCH 227/317] Change homebrew logo Fix the crash on CHN 3DSes by replacing the existing homebrew logo.bin with PabloMK7's one --- makerom/ncch_logo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/ncch_logo.h b/makerom/ncch_logo.h index 018d2a86..c28e10ee 100644 --- a/makerom/ncch_logo.h +++ b/makerom/ncch_logo.h @@ -27,5 +27,5 @@ static const unsigned char iQue_without_ISBN_LZ[0x2000] = static const unsigned char Homebrew_LZ[0x2000] = { - 0x11, 0x9C, 0x21, 0x00, 0x00, 0x64, 0x61, 0x72, 0x63, 0xFF, 0xFE, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7C, 0x21, 0x00, 0x00, 0x83, 0x30, 0x09, 0x24, 0x03, 0x00, 0x00, 0x40, 0x20, 0x03, 0x30, 0x13, 0xAB, 0x30, 0x18, 0x0F, 0x20, 0x1D, 0x02, 0xA0, 0x0B, 0x06, 0x20, 0x2B, 0x30, 0x18, 0x5A, 0x05, 0x20, 0x35, 0x10, 0x20, 0x39, 0x30, 0x2B, 0xA4, 0x20, 0x20, 0x40, 0xEA, 0x30, 0x45, 0x20, 0x1C, 0x30, 0x0B, 0x70, 0x60, 0x23, 0x08, 0x20, 0x59, 0x7A, 0x85, 0x30, 0x5D, 0x09, 0x00, 0x00, 0x28, 0x20, 0x2C, 0xA2, 0x20, 0x69, 0x21, 0x80, 0x19, 0x20, 0x0B, 0x04, 0x00, 0x00, 0xC4, 0x60, 0x47, 0xA0, 0x30, 0x5F, 0xCE, 0x20, 0x81, 0xC0, 0x1D, 0x00, 0x00, 0x9C, 0xA5, 0x20, 0x89, 0x12, 0x20, 0x75, 0x60, 0x1E, 0x50, 0x0B, 0x56, 0x30, 0x81, 0x55, 0x1F, 0x50, 0x17, 0x9A, 0x20, 0x8D, 0xA0, 0x60, 0x0B, 0xDE, 0x20, 0x99, 0x2A, 0x40, 0x20, 0x50, 0x2F, 0x22, 0x20, 0x9C, 0xE0, 0x60, 0x0B, 0x00, 0x20, 0x00, 0x2E, 0x20, 0xCB, 0x62, 0x00, 0x6C, 0x00, 0x79, 0x20, 0x00, 0x74, 0x20, 0xD5, 0x4E, 0x00, 0x69, 0x00, 0x6E, 0xA0, 0x20, 0x09, 0x65, 0x20, 0x05, 0x64, 0x00, 0x6F, 0x00, 0x4C, 0xA2, 0x20, 0x03, 0x67, 0x20, 0x07, 0x5F, 0x00, 0x55, 0x20, 0x03, 0x30, 0xAA, 0x20, 0x01, 0x2E, 0x20, 0x2D, 0x63, 0x01, 0x20, 0x2F, 0x44, 0x00, 0x40, 0x2F, 0x74, 0xA3, 0x20, 0x5F, 0x6D, 0x20, 0x51, 0x00, 0x00, 0x68, 0x40, 0x75, 0x70, 0x5D, 0x57, 0x62, 0x20, 0x6B, 0x74, 0x20, 0x81, 0x6F, 0x20, 0x1D, 0x70, 0x61, 0x30, 0x29, 0xD7, 0xF0, 0x27, 0x30, 0x21, 0x70, 0xE0, 0x21, 0x61, 0x20, 0xB1, 0x50, 0x2B, 0x01, 0x10, 0x8D, 0x18, 0x5F, 0x00, 0x53, 0x20, 0xBD, 0x30, 0xDD, 0x65, 0x00, 0x4F, 0x2F, 0x00, 0x75, 0x20, 0xF3, 0x43, 0x80, 0xD1, 0x30, 0x47, 0x01, 0x31, 0x01, 0x00, 0x10, 0x43, 0x6F, 0x42, 0x01, 0x80, 0x43, 0x00, 0x90, 0x87, 0x41, 0x03, 0x20, 0x43, 0x01, 0x90, 0x87, 0x00, 0x90, 0xCB, 0x01, 0x90, 0x87, 0xE0, 0x00, 0x91, 0x0F, 0xF1, 0x53, 0x90, 0x02, 0x43, 0x4C, 0x59, 0x54, 0xFF, 0x2C, 0xFE, 0x14, 0x33, 0x21, 0x02, 0x33, 0x03, 0x33, 0x0F, 0x6C, 0x79, 0x31, 0x74, 0x31, 0x30, 0x11, 0x43, 0x3C, 0x00, 0xC8, 0x43, 0x23, 0x0D, 0x03, 0x43, 0x74, 0x78, 0x6C, 0x31, 0x24, 0x63, 0x50, 0x22, 0xFA, 0x00, 0x00, 0x68, 0x62, 0x6C, 0x6F, 0x67, 0x6F, 0x5F, 0x00, 0x74, 0x6F, 0x70, 0x2E, 0x62, 0x63, 0x6C, 0x69, 0x81, 0x32, 0x18, 0x00, 0x6D, 0x61, 0x74, 0x31, 0x60, 0x63, 0x74, 0x86, 0x33, 0x57, 0x48, 0x62, 0x4D, 0x61, 0x32, 0xC3, 0xB0, 0x70, 0xFF, 0x35, 0xFF, 0xFF, 0x30, 0x03, 0x00, 0x40, 0x02, 0x15, 0x43, 0xB2, 0x04, 0x30, 0x5E, 0x98, 0xA0, 0xA3, 0x80, 0x3F, 0x50, 0x03, 0x23, 0x93, 0x61, 0x6E, 0x31, 0x48, 0x4C, 0x33, 0xE8, 0x04, 0xFF, 0x20, 0x5B, 0x52, 0x6F, 0x6F, 0x07, 0x74, 0x50, 0x61, 0x6E, 0x65, 0xE0, 0x60, 0x00, 0x80, 0x0E, 0x70, 0x47, 0x86, 0x50, 0xCF, 0x70, 0x61, 0x73, 0x31, 0x33, 0xDB, 0x70, 0x53, 0x03, 0xB3, 0x80, 0x53, 0x30, 0x01, 0x70, 0x50, 0xA0, 0x9B, 0x20, 0x42, 0x30, 0x03, 0x80, 0x53, 0x0B, 0x69, 0x63, 0x31, 0x80, 0x34, 0x90, 0x07, 0x30, 0xA7, 0x00, 0x11, 0x03, 0xC7, 0x01, 0x50, 0xA7, 0x23, 0x08, 0x00, 0x80, 0x41, 0xF1, 0x2B, 0x71, 0x95, 0x91, 0x1B, 0x83, 0xF1, 0x27, 0x80, 0x3F, 0x70, 0x61, 0x65, 0x60, 0xDB, 0x50, 0x07, 0x0C, 0x67, 0x72, 0x70, 0x31, 0x35, 0x21, 0x51, 0x33, 0x47, 0x72, 0xCD, 0x24, 0xDB, 0x82, 0x03, 0x67, 0x72, 0x51, 0x07, 0x30, 0x23, 0x3C, 0x25, 0x45, 0x07, 0x47, 0x5F, 0x41, 0x5F, 0x30, 0xA1, 0x02, 0x25, 0x37, 0x00, 0x01, 0x17, 0xD7, 0xF1, 0xD7, 0x30, 0x5F, 0x2C, 0x40, 0x3B, 0x42, 0xC0, 0x3B, 0x35, 0x7C, 0x00, 0x90, 0x2B, 0x67, 0x43, 0x00, 0x20, 0x2B, 0xD1, 0x7F, 0x67, 0x72, 0x50, 0xC7, 0x00, 0xB1, 0xE1, 0x01, 0x12, 0xBF, 0x40, 0xA0, 0x00, 0xB2, 0xBF, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0xF2, 0x62, 0xC2, 0x09, 0x52, 0xBF, 0x50, 0xCF, 0x07, 0x52, 0xBF, 0xF0, 0xC2, 0x00, 0xE2, 0xBF, 0x42, 0xC3, 0x10, 0x00, 0xF2, 0xBF, 0x10, 0x76, 0x70, 0x1E, 0xF0, 0xF0, 0x0F, 0x0F, 0x30, 0x03, 0x02, 0x70, 0x1F, 0x9B, 0x4E, 0x44, 0xD7, 0x00, 0x2C, 0x8F, 0x00, 0x2D, 0x7F, 0x7D, 0x9D, 0xEE, 0x00, 0x4D, 0x9D, 0x11, 0x90, 0x00, 0xEF, 0x4E, 0x84, 0x06, 0x00, 0xA1, 0x00, 0x4D, 0xBD, 0x2B, 0xEE, 0x00, 0x00, 0xDB, 0xF6, 0xC5, 0x60, 0x77, 0x8D, 0x00, 0x30, 0x67, 0x3D, 0x23, 0xD5, 0x3D, 0x27, 0x40, 0x67, 0xFE, 0x00, 0x4E, 0x1D, 0x2A, 0x7A, 0xBE, 0x00, 0x00, 0x50, 0xFF, 0x57, 0xE9, 0x80, 0x17, 0xC5, 0x00, 0x4E, 0x5D, 0xFF, 0xA0, 0x79, 0x00, 0x50, 0x1F, 0x5F, 0x63, 0x1E, 0xD0, 0x00, 0xCF, 0x00, 0x7B, 0x38, 0x4F, 0x84, 0x00, 0xD1, 0x5D, 0x03, 0xF1, 0xEF, 0xFF, 0x1F, 0xF0, 0xF0, 0xFF, 0x81, 0xEF, 0x81, 0xFF, 0x3F, 0x8E, 0x21, 0xC6, 0x20, 0x1A, 0xE0, 0x20, 0x20, 0x92, 0x0F, 0x92, 0x1F, 0xC0, 0xF7, 0x00, 0x00, 0xFD, 0x02, 0xFF, 0xFF, 0x6E, 0xFF, 0x11, 0x06, 0x4E, 0x7A, 0xFF, 0x00, 0xFD, 0x00, 0x00, 0xF7, 0xC0, 0x01, 0x05, 0x00, 0x00, 0x00, 0x5E, 0xFF, 0x11, 0xFF, 0xFF, 0xF6, 0x0B, 0x08, 0x7F, 0x60, 0x10, 0xCF, 0x61, 0x73, 0xFF, 0xFF, 0x10, 0x01, 0x60, 0xFF, 0xCF, 0xE6, 0xFF, 0x7F, 0x0B, 0x71, 0x87, 0x04, 0xFA, 0x2C, 0xFF, 0x90, 0x03, 0x2D, 0x95, 0x4F, 0xFF, 0x04, 0xFC, 0x2C, 0xFF, 0xFF, 0x03, 0x00, 0x01, 0xA5, 0xFF, 0x90, 0x04, 0x5F, 0xCF, 0x10, 0x00, 0xEF, 0x2E, 0xD7, 0xA0, 0xF6, 0x18, 0x00, 0x00, 0xFC, 0x80, 0x47, 0x2D, 0x69, 0xFC, 0x00, 0x00, 0x01, 0xF6, 0x90, 0xFF, 0x5E, 0xFF, 0x01, 0x03, 0x3D, 0x72, 0x02, 0xC3, 0x0D, 0x8F, 0x20, 0xFF, 0xDF, 0x20, 0x42, 0xFF, 0x08, 0x00, 0x6E, 0xFF, 0x02, 0x40, 0xB3, 0x20, 0xFF, 0xC6, 0x83, 0x80, 0x77, 0xFA, 0x3E, 0xFF, 0x40, 0x04, 0x50, 0x9F, 0x41, 0xFF, 0x40, 0x01, 0x2D, 0xE4, 0x3E, 0xFA, 0x40, 0xFF, 0xEF, 0xFE, 0x04, 0x01, 0x08, 0xF5, 0xF1, 0x0D, 0x22, 0xEB, 0xAF, 0xEF, 0x00, 0xF0, 0xF0, 0x5F, 0x1F, 0xF1, 0xF5, 0x0F, 0x0D, 0x09, 0xFE, 0xEF, 0x08, 0x01, 0x72, 0xFF, 0xFF, 0x04, 0x53, 0x02, 0x07, 0xA0, 0xF6, 0xFF, 0x5E, 0xFC, 0x30, 0x5C, 0x7D, 0xE7, 0x20, 0x0C, 0x10, 0xF6, 0x90, 0x6E, 0x20, 0x48, 0xFF, 0xC3, 0x00, 0xFF, 0x01, 0x20, 0xFF, 0x0D, 0x8F, 0x00, 0x00, 0xDF, 0x32, 0x59, 0xB8, 0x22, 0xF2, 0x02, 0x20, 0x0F, 0x32, 0xF8, 0x30, 0x7D, 0xFC, 0xF8, 0x04, 0x00, 0x07, 0xF4, 0xF0, 0x0A, 0x0E, 0xF1, 0xF4, 0xFF, 0x00, 0xFD, 0xF8, 0xEC, 0xF5, 0xE0, 0xC0, 0x90, 0x2F, 0x00, 0x9F, 0x50, 0x10, 0xFF, 0xFF, 0xAF, 0x5F, 0xA0, 0x10, 0x50, 0x1F, 0x0D, 0x2E, 0x8D, 0x04, 0xF3, 0xF7, 0x08, 0x0C, 0x0C, 0xFA, 0xFD, 0x0C, 0x2F, 0x90, 0x3E, 0x77, 0x1F, 0x8F, 0x03, 0xCF, 0x8F, 0xFF, 0xFD, 0x5F, 0x1F, 0x06, 0x03, 0xEF, 0x00, 0xE2, 0x6C, 0xF4, 0x00, 0xF0, 0x1F, 0x02, 0x73, 0xEA, 0x01, 0x10, 0x1D, 0x00, 0xD0, 0x37, 0xA4, 0x63, 0xC2, 0xDF, 0x00, 0x6E, 0x28, 0x00, 0xC3, 0x6C, 0x00, 0x73, 0xEA, 0x2B, 0x01, 0x60, 0xFF, 0x30, 0x69, 0x00, 0x3F, 0x89, 0xFE, 0xFC, 0x20, 0x79, 0x3F, 0xFF, 0x00, 0x70, 0x7D, 0x23, 0x1B, 0x4F, 0xF5, 0x00, 0x7D, 0xE7, 0xF8, 0x00, 0x40, 0x0D, 0x10, 0x14, 0xCE, 0x01, 0x43, 0x4C, 0x49, 0x4D, 0xFF, 0xFE, 0x46, 0x14, 0x2F, 0xFF, 0x02, 0x02, 0x28, 0x24, 0x6E, 0x35, 0xA2, 0x69, 0x10, 0x6D, 0x61, 0x67, 0x24, 0x79, 0x00, 0x80, 0x00, 0x40, 0xFF, 0x52, 0x7D, 0x30, 0x0C, 0x07, 0x8F, 0xFF, 0x57, 0x9F, 0x38, 0x79, 0x00, 0x58, 0x7D, 0x00, 0xF0, 0x1F, 0x40, 0xD8, 0xB6, 0x53, 0x74, 0xDF, 0x00, 0x88, 0x27, 0x57, 0x5F, 0xFF, 0x28, 0x69, 0x00, 0x3F, 0xFF, 0xEF, 0xDD, 0x28, 0x79, 0x3F, 0xFF, 0xB2, 0x63, 0xDC, 0xFF, 0xFF, 0x98, 0x7D, 0xC7, 0x00, 0x67, 0xFF, 0x7F, 0x9D, 0x4F, 0xFF, 0x47, 0x0F, 0x48, 0xE5, 0x89, 0x47, 0x30, 0xD9, 0x50, 0xDD, 0x2F, 0xFF, 0x17, 0xE8, 0x00, 0xBF, 0x00, 0x4F, 0xFF, 0x02, 0x00, 0x10, 0x37, 0x00, 0x68, 0x7F, 0x01, 0x50, 0x7F, 0xB1, 0x00, 0x14, 0x92, 0x2F, 0x84, 0xBE, 0x00, 0x3F, 0xFF, 0x0E, 0x00, 0x20, 0x69, 0x9D, 0xF9, 0x05, 0x24, 0x9C, 0x61, 0xEF, 0x38, 0x67, 0x20, 0x04, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x38, 0x7F, 0x83, 0x28, 0x7D, 0x10, 0x90, 0x0F, 0x0F, 0xE0, 0x29, 0x17, 0x72, 0x1F, 0x50, 0xE0, 0x20, 0x0F, 0x10, 0x38, 0x79, 0x1B, 0xFF, 0xFF, 0x02, 0x02, 0x00, 0xFF, 0xB1, 0xDF, 0xFF, 0x20, 0x40, 0x2C, 0x02, 0x01, 0x1B, 0xFD, 0x80, 0xFF, 0xFD, 0x20, 0xB1, 0x20, 0x38, 0x00, 0xDF, 0xDF, 0x08, 0x01, 0x09, 0xF0, 0xF0, 0x0E, 0xD5, 0x2A, 0xC9, 0x28, 0x69, 0x1A, 0x30, 0x27, 0x0E, 0x20, 0x0F, 0x01, 0x2A, 0xDD, 0x90, 0x88, 0x5F, 0xF5, 0x7F, 0x38, 0xF7, 0xFF, 0xFE, 0x1A, 0xFF, 0x3A, 0xF5, 0x02, 0x2A, 0xF5, 0x30, 0x29, 0x50, 0x1D, 0xF0, 0x29, 0x83, 0xF0, 0x30, 0xF0, 0x7F, 0x25, 0xDE, 0x3A, 0x31, 0xD1, 0xF9, 0xFF, 0x7F, 0x60, 0xFE, 0x30, 0x8B, 0x6A, 0x3D, 0xFE, 0xF8, 0x2F, 0xCF, 0xC0, 0x44, 0x00, 0x28, 0x60, 0x01, 0xFF, 0xF5, 0x30, 0xA2, 0x2E, 0xAF, 0x80, 0x6A, 0x55, 0x03, 0x00, 0x30, 0xFF, 0xFE, 0xFF, 0xCF, 0x90, 0x32, 0x19, 0xFF, 0x38, 0x3A, 0x6D, 0xAF, 0xBF, 0xFF, 0xFF, 0x21, 0x2F, 0x0F, 0x29, 0x0A, 0xFB, 0x00, 0x00, 0xF2, 0x20, 0x87, 0x40, 0x2F, 0x20, 0x0F, 0xAF, 0x0F, 0x00, 0x11, 0xF2, 0xFB, 0x03, 0xFF, 0xE8, 0xFF, 0xBF, 0x1E, 0x9F, 0x22, 0x6E, 0x4A, 0x91, 0x01, 0xAF, 0xCF, 0xFF, 0xFF, 0x3F, 0x0F, 0xEF, 0x20, 0x0F, 0x67, 0x2E, 0x21, 0xFF, 0x72, 0x27, 0xFF, 0x03, 0x5B, 0x82, 0x70, 0x7F, 0x86, 0x77, 0x80, 0x01, 0x60, 0x7F, 0xF7, 0xF2, 0x7F, 0xBF, 0xD0, 0x70, 0xFF, 0x21, 0xFF, 0x20, 0x20, 0x03, 0xB1, 0xF5, 0xBF, 0x2F, 0x21, 0x23, 0x80, 0x39, 0x9F, 0xF2, 0xFD, 0xFF, 0x0B, 0x06, 0xFF, 0xDF, 0x00, 0x02, 0x00, 0x2F, 0x6F, 0x70, 0xB0, 0xBF, 0xFF, 0x40, 0xF1, 0x20, 0xC3, 0x07, 0x02, 0xDF, 0x7F, 0x00, 0x00, 0x02, 0xFB, 0xF6, 0xFD, 0xFF, 0xF2, 0xD0, 0x20, 0xB1, 0x0C, 0x1E, 0x00, 0x00, 0x07, 0x72, 0x87, 0x03, 0x74, 0x7F, 0x29, 0x73, 0xD4, 0x7F, 0x10, 0xE5, 0x56, 0xFD, 0x38, 0xF8, 0x00, 0x84, 0xBF, 0x41, 0x4E, 0x74, 0xBF, 0x9C, 0x63, 0x1A, 0x05, 0x70, 0x61, 0x74, 0x31, 0x3C, 0x63, 0x60, 0x1C, 0x67, 0x7B, 0x60, 0xF1, 0x22, 0x27, 0x3A, 0x7E, 0x53, 0x63, 0x65, 0x6E, 0x65, 0x08, 0x4F, 0x75, 0x74, 0x43, 0x2F, 0xFF, 0x47, 0x5F, 0x43, 0x10, 0x5F, 0x30, 0x30, 0xDF, 0xFF, 0x70, 0x61, 0x69, 0x31, 0x4D, 0x4C, 0x8B, 0x5A, 0x02, 0x00, 0x35, 0x19, 0x30, 0x43, 0x40, 0x2F, 0xFF, 0x04, 0x48, 0x62, 0x4D, 0x61, 0x74, 0x00, 0x2F, 0xFF, 0x48, 0x62, 0x0D, 0x52, 0x6F, 0x6F, 0x74, 0xE0, 0x48, 0x02, 0xE0, 0x9F, 0x42, 0x40, 0x9F, 0x56, 0x42, 0x08, 0x80, 0x9F, 0x41, 0x41, 0x3F, 0x41, 0x10, 0x0C, 0x81, 0x3F, 0x05, 0x33, 0x1F, 0x77, 0x00, 0xA2, 0x13, 0x49, 0x99, 0x58, 0x3D, 0x71, 0x8A, 0x00, 0x3A, 0x75, 0x0A, 0xEF, 0xE4, 0xC9, 0xFC, 0xB1, 0x00, 0x00, 0x99, 0x02, 0x63, 0xA9, 0x9B, 0x74, 0xE0, 0x00, 0x38, 0xD3, 0x33, 0xC0, 0x52, 0x6A, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x11, 0x40, 0xE6, 0x00, 0x00, 0x64, 0x61, 0x72, 0x63, 0xFF, 0xFE, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0xE6, 0x00, 0x00, 0x83, 0x30, 0x09, 0x64, 0x03, 0x00, 0x00, 0x80, 0x20, 0x03, 0x30, 0x13, 0xAB, 0x30, 0x18, 0x11, 0x20, 0x1D, 0x02, 0xA0, 0x0B, 0x06, 0x20, 0x2B, 0x30, 0x18, 0x5A, 0x09, 0x20, 0x35, 0x10, 0x20, 0x39, 0x30, 0x2B, 0xAC, 0x20, 0x20, 0x54, 0xAA, 0x20, 0x45, 0x40, 0x20, 0x1C, 0x0C, 0x20, 0x2C, 0x98, 0x20, 0x51, 0x60, 0x55, 0x08, 0x50, 0x38, 0xDC, 0x30, 0x0B, 0x0A, 0x50, 0x23, 0x20, 0x20, 0x51, 0x05, 0x20, 0x0D, 0x00, 0x00, 0xE8, 0x20, 0x50, 0x64, 0x30, 0x0B, 0xA5, 0x20, 0x40, 0x78, 0x20, 0x6F, 0xA8, 0x01, 0x50, 0x53, 0x0E, 0x20, 0x89, 0x40, 0xB2, 0x30, 0x75, 0x14, 0x00, 0x00, 0x28, 0x04, 0x00, 0x24, 0x00, 0xC8, 0x20, 0x81, 0x80, 0x18, 0x20, 0x0B, 0x80, 0x00, 0x28, 0x00, 0xE4, 0x30, 0x8D, 0x99, 0x20, 0x17, 0x20, 0x00, 0x00, 0x53, 0xFA, 0x30, 0x17, 0xB9, 0x50, 0x0B, 0x12, 0x02, 0x50, 0x8F, 0x30, 0xA7, 0x48, 0x1C, 0x20, 0xA8, 0xC0, 0xD9, 0x20, 0xBF, 0x05, 0x00, 0x00, 0x41, 0x4C, 0x20, 0xB4, 0x40, 0xDF, 0x00, 0x00, 0xE0, 0x20, 0xB0, 0x10, 0x00, 0x00, 0x2E, 0x20, 0xE3, 0x61, 0x00, 0x6E, 0x00, 0x14, 0x69, 0x00, 0x6D, 0x20, 0xED, 0x4E, 0x20, 0x07, 0x6E, 0x00, 0x10, 0x74, 0x00, 0x65, 0x20, 0x11, 0x64, 0x00, 0x6F, 0x00, 0x51, 0x4C, 0x20, 0x03, 0x67, 0x20, 0x07, 0x5F, 0x00, 0x44, 0x20, 0x03, 0x41, 0x30, 0x20, 0x01, 0x5F, 0x00, 0x53, 0x00, 0x63, 0x40, 0x1F, 0x05, 0x65, 0x00, 0x4F, 0x00, 0x75, 0x20, 0x2B, 0x41, 0x20, 0x43, 0x5A, 0x62, 0x20, 0x13, 0x6C, 0x40, 0x47, 0x02, 0x50, 0x43, 0x42, 0x03, 0x20, 0x43, 0x43, 0xBD, 0x01, 0x80, 0x87, 0x55, 0x04, 0xC0, 0x43, 0x01, 0x90, 0xCB, 0x00, 0x90, 0x87, 0xF1, 0x53, 0x74, 0x41, 0x9F, 0x51, 0x67, 0x22, 0x8F, 0x6D, 0x21, 0xAD, 0x73, 0x00, 0x6B, 0x81, 0x73, 0xAA, 0x51, 0xB7, 0x62, 0x21, 0x89, 0x62, 0x21, 0x85, 0x6C, 0x21, 0xB9, 0x73, 0xB5, 0xE0, 0x1B, 0x6C, 0x61, 0xC3, 0xD0, 0x31, 0x77, 0x21, 0xF5, 0x76, 0x00, 0x20, 0x2D, 0xBF, 0x30, 0x43, 0x79, 0x22, 0x01, 0x01, 0x32, 0x0B, 0x71, 0xF7, 0x00, 0xF0, 0x2F, 0x71, 0x6F, 0xD0, 0x2F, 0x81, 0x00, 0xB0, 0x02, 0x43, 0x4C, 0x41, 0x4E, 0xFF, 0xFE, 0x22, 0xEC, 0x18, 0x00, 0x02, 0x02, 0x33, 0x43, 0x23, 0x67, 0x00, 0x70, 0x61, 0x1A, 0x74, 0x31, 0x3C, 0x63, 0x7E, 0x33, 0x99, 0x28, 0x23, 0x9D, 0xA1, 0x20, 0x00, 0xF0, 0x43, 0x8C, 0x53, 0x63, 0x65, 0x6E, 0x65, 0x08, 0x4F, 0x75, 0x74, 0x41, 0x23, 0xB1, 0x47, 0x5F, 0x41, 0x10, 0x5F, 0x30, 0x30, 0xD0, 0x60, 0x70, 0x61, 0x69, 0x31, 0x54, 0x5C, 0x23, 0xAC, 0x50, 0x43, 0xBE, 0x06, 0x33, 0x46, 0x00, 0x2C, 0xB5, 0x23, 0xDD, 0xCC, 0x33, 0xAB, 0x23, 0xCD, 0x54, 0x23, 0xD1, 0xC4, 0x23, 0xD5, 0x43, 0x10, 0x23, 0xD0, 0x6D, 0x61, 0x73, 0x6B, 0xF0, 0x40, 0x33, 0xF0, 0x85, 0x34, 0x0D, 0x43, 0x4C, 0x56, 0x43, 0x33, 0xFC, 0x0C, 0x34, 0x19, 0xF2, 0x20, 0x2C, 0x33, 0xEB, 0x40, 0x0B, 0x44, 0x16, 0x7F, 0x43, 0x20, 0x51, 0xC1, 0xF9, 0x23, 0xCD, 0x30, 0x86, 0x30, 0x5D, 0xA0, 0x0B, 0x34, 0x4A, 0x82, 0x42, 0x00, 0x10, 0x0B, 0x5C, 0x7F, 0x20, 0x13, 0x8A, 0x20, 0x17, 0x30, 0x3B, 0x90, 0x0B, 0xAB, 0xAA, 0x5C, 0xE2, 0x20, 0x47, 0x9C, 0x40, 0x2F, 0xB0, 0x0B, 0x34, 0x90, 0x62, 0x75, 0x06, 0x62, 0x62, 0x6C, 0x65, 0x73, 0xC0, 0xE3, 0xA0, 0x9F, 0x49, 0xF9, 0x80, 0x9F, 0x24, 0xA6, 0x31, 0x37, 0x90, 0x9F, 0x34, 0xCE, 0x8C, 0x42, 0x34, 0xBC, 0xBC, 0x80, 0x43, 0x31, 0x02, 0x90, 0x43, 0x00, 0x41, 0x27, 0x25, 0x19, 0x51, 0x27, 0x54, 0x53, 0x8D, 0x81, 0x27, 0x01, 0x02, 0x00, 0x24, 0x76, 0x61, 0x27, 0x1C, 0x21, 0x03, 0x2D, 0x60, 0xC0, 0x51, 0xFE, 0x44, 0xE0, 0x0B, 0x72, 0x14, 0xA0, 0xE0, 0x0B, 0x03, 0x90, 0xC0, 0x29, 0xA5, 0x14, 0xBE, 0x00, 0x40, 0xF7, 0x00, 0x60, 0x6F, 0x8A, 0xF0, 0xF7, 0x75, 0x64, 0x4F, 0x22, 0x3A, 0x9E, 0x21, 0x7F, 0x80, 0x79, 0x3F, 0x30, 0x0B, 0x00, 0x40, 0xFF, 0x01, 0x50, 0x4B, 0x20, 0x3F, 0xCF, 0x3B, 0x90, 0x4B, 0xFF, 0x30, 0x0B, 0x00, 0xD2, 0xBF, 0x35, 0xF7, 0xB2, 0xBF, 0x25, 0xD2, 0x82, 0xBF, 0x20, 0xEB, 0xC2, 0xBF, 0x8B, 0x32, 0x20, 0x47, 0x5F, 0x42, 0x00, 0x42, 0xBF, 0xBC, 0x26, 0x75, 0x50, 0x2D, 0xAA, 0x92, 0xBF, 0x6C, 0x26, 0xA1, 0xA8, 0x26, 0xA5, 0xE4, 0x26, 0xA9, 0x24, 0xBF, 0x26, 0x95, 0x70, 0x26, 0x99, 0x01, 0xB2, 0xBF, 0x92, 0x2B, 0x93, 0x90, 0x01, 0xB2, 0x5F, 0xB0, 0x3F, 0xF7, 0x01, 0xF2, 0x57, 0xF0, 0x3B, 0x01, 0xB2, 0x4F, 0x92, 0xE3, 0x48, 0x23, 0x73, 0x52, 0x4F, 0x02, 0xB2, 0x1F, 0x14, 0xCD, 0xCC, 0x4C, 0x24, 0x5A, 0x20, 0x23, 0xBF, 0x00, 0x40, 0xEC, 0x30, 0x0B, 0x02, 0xA2, 0x1F, 0x20, 0x4B, 0xCC, 0x22, 0x1F, 0x30, 0x4B, 0x80, 0x3F, 0xFC, 0x50, 0x0B, 0x00, 0xD4, 0xDF, 0x38, 0x44, 0xB4, 0xDF, 0x31, 0x95, 0x74, 0xDF, 0xF1, 0x00, 0x62, 0xFF, 0xC4, 0xDF, 0x30, 0xA0, 0x47, 0x5F, 0x43, 0x00, 0x44, 0xDF, 0xB0, 0xAA, 0x28, 0x95, 0x0F, 0xE4, 0xDF, 0x78, 0x28, 0xC1, 0xB4, 0x28, 0xC5, 0xF0, 0xB1, 0x28, 0xC9, 0x30, 0x02, 0x22, 0x1F, 0xF4, 0x3F, 0xDB, 0xB6, 0x91, 0x25, 0x62, 0x4C, 0x60, 0x25, 0x66, 0x7F, 0x43, 0x30, 0x0B, 0x02, 0x52, 0x2B, 0x60, 0x41, 0xFC, 0x02, 0x92, 0x2B, 0x50, 0x3B, 0x02, 0x52, 0x2B, 0x39, 0xCE, 0x02, 0x14, 0x4B, 0x95, 0x4F, 0x80, 0xBF, 0xFE, 0x51, 0xD3, 0x01, 0xD2, 0x1F, 0x00, 0x30, 0x3F, 0x00, 0xB6, 0xDF, 0x02, 0x71, 0xFF, 0x36, 0xDF, 0x51, 0xFF, 0x08, 0xB5, 0x46, 0xDF, 0x34, 0x3A, 0x83, 0x2A, 0xC1, 0xBC, 0x2A, 0xC5, 0xF8, 0x30, 0x0F, 0xAE, 0x2A, 0xB5, 0x74, 0x2A, 0xB9, 0xB4, 0x35, 0xA3, 0x01, 0xE6, 0xE7, 0x00, 0xF2, 0x07, 0x6C, 0x1C, 0x6F, 0x67, 0x6F, 0x00, 0xA7, 0x33, 0x01, 0x52, 0x07, 0x0D, 0xE2, 0x43, 0x77, 0x61, 0x7F, 0x76, 0xE7, 0xC5, 0xD7, 0x3F, 0x38, 0xF3, 0x3C, 0x47, 0x56, 0x27, 0xF2, 0x87, 0x44, 0xB3, 0x60, 0xBC, 0xD6, 0xEB, 0x40, 0x17, 0x40, 0x0D, 0xD7, 0xA3, 0xBB, 0x87, 0x37, 0x67, 0x66, 0x66, 0xE6, 0x3F, 0xC8, 0x2F, 0x02, 0x62, 0xAB, 0x00, 0xF9, 0x9F, 0xDE, 0x3C, 0xB3, 0x03, 0x36, 0xDF, 0x98, 0x2D, 0x4C, 0x57, 0x0D, 0x92, 0xBF, 0x3A, 0x87, 0xB0, 0xB7, 0x2D, 0x85, 0xEC, 0x39, 0xEF, 0x2D, 0x75, 0x68, 0x62, 0xBF, 0x3C, 0xC3, 0x02, 0xF6, 0xE7, 0xFF, 0x02, 0x52, 0xB3, 0xE6, 0xE7, 0x0E, 0xA7, 0x23, 0x01, 0x72, 0xBF, 0x34, 0x87, 0x3F, 0x29, 0xEA, 0x8B, 0x37, 0x67, 0xFD, 0x24, 0x9A, 0x47, 0x73, 0x82, 0xCB, 0x9A, 0x27, 0x50, 0x23, 0x52, 0xCB, 0x20, 0x82, 0xCB, 0xE0, 0x6A, 0x1B, 0x32, 0xE3, 0x29, 0x83, 0xF0, 0x42, 0xCD, 0xCC, 0x0C, 0x7F, 0x40, 0x5C, 0x4A, 0x57, 0xBB, 0x32, 0xFB, 0x04, 0xF7, 0xBB, 0xFC, 0x9F, 0x3F, 0xA7, 0x03, 0x3C, 0x9F, 0x75, 0x28, 0x2F, 0xEF, 0x5C, 0x9F, 0x95, 0xBF, 0xD4, 0x2F, 0xFB, 0x18, 0x2F, 0xF7, 0x77, 0x5C, 0x39, 0xFB, 0x6C, 0x9F, 0x3C, 0xC7, 0xDC, 0x09, 0x2C, 0xA7, 0x01, 0xB6, 0x13, 0x01, 0x0C, 0xA7, 0xFF, 0x10, 0x02, 0xAC, 0xEB, 0x02, 0xD3, 0xA7, 0x4D, 0x2F, 0x28, 0x42, 0x3D, 0x3B, 0x4D, 0x47, 0x46, 0x73, 0x22, 0xF2, 0x84, 0xCE, 0x57, 0xE1, 0x65, 0x2D, 0x3E, 0x33, 0xA7, 0xA4, 0x70, 0x7C, 0x0D, 0x63, 0x8F, 0x40, 0x2F, 0x02, 0xF3, 0x8F, 0x01, 0x4D, 0x6B, 0x04, 0x30, 0x13, 0xFF, 0xFF, 0x30, 0xFF, 0xFF, 0x3F, 0x3B, 0x10, 0x13, 0x70, 0x07, 0x77, 0x77, 0x77, 0x77, 0x60, 0x6E, 0xA0, 0x07, 0x00, 0xF0, 0x1F, 0xBB, 0xBB, 0x55, 0x55, 0x03, 0x12, 0x23, 0x23, 0x23, 0x70, 0x07, 0xDD, 0xDD, 0x20, 0x0F, 0x5B, 0x1C, 0x5B, 0x5B, 0xFD, 0x60, 0x07, 0x00, 0x70, 0x1F, 0x70, 0x27, 0xAA, 0xAA, 0x8C, 0x20, 0x3F, 0x8B, 0x8B, 0x8B, 0x70, 0x07, 0x40, 0x4F, 0xC3, 0xC3, 0x60, 0xC3, 0x70, 0x07, 0x00, 0xF0, 0x1F, 0x11, 0x11, 0x11, 0x11, 0x02, 0x00, 0xF8, 0xF8, 0xF8, 0x11, 0x00, 0x11, 0x11, 0x0A, 0xFE, 0x20, 0x07, 0x4A, 0x81, 0xA0, 0x07, 0x70, 0x17, 0x70, 0x27, 0xF0, 0x1F, 0x0A, 0xF0, 0x0F, 0x43, 0x12, 0x4C, 0x49, 0x4D, 0x7F, 0x9F, 0x28, 0x04, 0x5F, 0x1F, 0x69, 0x15, 0x6D, 0x61, 0x67, 0x3B, 0x8F, 0x10, 0x2D, 0x55, 0x0A, 0x3F, 0xFB, 0xE0, 0x20, 0x17, 0x10, 0x19, 0x74, 0x7F, 0x10, 0x26, 0x72, 0x4F, 0x7F, 0xBB, 0xFF, 0x37, 0x0B, 0x10, 0x02, 0x02, 0x02, 0x7A, 0x4F, 0x01, 0xFE, 0x03, 0x00, 0x00, 0x62, 0x21, 0x21, 0x21, 0x33, 0x33, 0x33, 0x33, 0x00, 0xB6, 0x18, 0x18, 0x18, 0x33, 0x33, 0xFF, 0xFF, 0x10, 0x01, 0x03, 0x03, 0x2E, 0x41, 0x11, 0x01, 0x02, 0x30, 0xFD, 0x5F, 0xD8, 0x30, 0x07, 0x70, 0x17, 0x90, 0x1F, 0x50, 0x17, 0x00, 0x00, 0x1F, 0x73, 0x00, 0x00, 0x1F, 0x40, 0x10, 0x40, 0x3F, 0xBB, 0x33, 0x73, 0xAA, 0x1F, 0x1F, 0x0D, 0x1F, 0xFF, 0xFF, 0xF7, 0x4A, 0xC7, 0x20, 0x5F, 0x11, 0x30, 0x5F, 0x06, 0x03, 0xFF, 0x10, 0xFF, 0x57, 0x27, 0xDC, 0x10, 0x25, 0x76, 0x5F, 0x0E, 0x37, 0x00, 0x1F, 0x43, 0xD7, 0x7E, 0x4F, 0x0F, 0x20, 0x01, 0xB3, 0xE7, 0x04, 0xF0, 0x07, 0x5E, 0xF0, 0x2E, 0xC8, 0x00, 0x00, 0x20, 0x0F, 0x10, 0x25, 0x7A, 0x5F, 0x73, 0xEF, 0x10, 0x2F, 0x73, 0xFF, 0x01, 0x10, 0x00, 0xEF, 0x0E, 0x10, 0x2D, 0xB7, 0xFF, 0xBC, 0x08, 0xCE, 0x88, 0x08, 0x4A, 0x13, 0x13, 0x13, 0xFF, 0x6F, 0x00, 0x00, 0x88, 0x70, 0x88, 0x5F, 0xEF, 0x50, 0x07, 0x04, 0x00, 0x1F, 0x80, 0x88, 0x88, 0x0A, 0x00, 0x37, 0x37, 0x37, 0xCB, 0xFF, 0xEC, 0xFF, 0x42, 0x18, 0x16, 0x16, 0x16, 0x10, 0x25, 0x7F, 0xEF, 0x10, 0x07, 0x0F, 0x57, 0x7F, 0xFF, 0x7F, 0x40, 0x06, 0x00, 0x2F, 0xFF, 0xB7, 0xBB, 0x37, 0x33, 0x6A, 0x0A, 0x00, 0x0A, 0x0A, 0xBB, 0xBB, 0x33, 0x33, 0x05, 0x02, 0x38, 0x02, 0x02, 0xFF, 0xFF, 0x70, 0x17, 0x02, 0x80, 0x1F, 0x7B, 0x33, 0x77, 0x01, 0x4E, 0x1F, 0x1F, 0x1F, 0xF7, 0xFF, 0xF7, 0x10, 0x25, 0xD5, 0x67, 0x01, 0xE8, 0xFF, 0x01, 0x6B, 0x11, 0x11, 0x11, 0x8F, 0xFF, 0x44, 0x0F, 0x2C, 0xEF, 0x03, 0x03, 0x03, 0x05, 0x7D, 0x5F, 0x1E, 0xFF, 0x18, 0x10, 0xFF, 0x6B, 0x24, 0x57, 0x7D, 0xCF, 0x0F, 0xFF, 0x00, 0x07, 0xFF, 0x20, 0x20, 0x20, 0x20, 0x10, 0x25, 0x89, 0x6F, 0x63, 0xEF, 0x06, 0x73, 0xFF, 0xC1, 0x73, 0xEF, 0x07, 0x73, 0xFF, 0x7F, 0xB3, 0x7F, 0x37, 0x0A, 0x00, 0x28, 0xDF, 0x04, 0x31, 0x33, 0x9D, 0xD9, 0x21, 0x2E, 0x87, 0x11, 0x11, 0x1C, 0xDD, 0xDD, 0xA1, 0x00, 0x2E, 0x8F, 0x70, 0x17, 0x01, 0x20, 0x1F, 0x9D, 0x81, 0x80, 0x00, 0x2E, 0xCF, 0xDD, 0x7B, 0x31, 0x73, 0x4E, 0x1E, 0x1E, 0x41, 0x1E, 0x10, 0x25, 0xF3, 0xFF, 0xCF, 0x01, 0x0F, 0xF8, 0x40, 0xA3, 0xCF, 0x00, 0x0F, 0x00, 0x0F, 0x0F, 0x82, 0x1B, 0x1B, 0x1B, 0xA0, 0x05, 0x28, 0xFF, 0x10, 0x39, 0x5F, 0xFE, 0xFF, 0xF0, 0xFF, 0x22, 0x15, 0x16, 0x16, 0x16, 0x79, 0x6F, 0xF0, 0x20, 0x0F, 0xA2, 0x20, 0x0F, 0x84, 0x10, 0x15, 0x8F, 0xFF, 0xF0, 0xFF, 0x08, 0x04, 0xBF, 0xCF, 0xDE, 0xFF, 0x04, 0xCE, 0x0E, 0x01, 0x01, 0x01, 0xFB, 0xFF, 0x88, 0xCC, 0x00, 0x8E, 0x88, 0x0B, 0x34, 0x34, 0x34, 0xCC, 0xCC, 0x78, 0x88, 0x40, 0x07, 0xFC, 0x1F, 0x70, 0x17, 0x01, 0xF0, 0x1F, 0x0F, 0xFF, 0x80, 0xD0, 0x4B, 0xEF, 0x20, 0x56, 0xE8, 0x30, 0x5F, 0xFD, 0xFF, 0xFC, 0xFF, 0x1F, 0x62, 0x0F, 0x0F, 0x2C, 0x85, 0x05, 0x5F, 0xFF, 0x73, 0xEF, 0x06, 0x23, 0xFF, 0xC3, 0xEF, 0x83, 0x10, 0x01, 0x73, 0xFF, 0xB7, 0xDD, 0x37, 0x13, 0x6A, 0x2E, 0x2F, 0x02, 0xA9, 0x7F, 0x84, 0x00, 0x49, 0xBF, 0x33, 0x13, 0xDD, 0xFD, 0x39, 0xDF, 0x7B, 0xFF, 0x75, 0x73, 0x05, 0xC2, 0x7F, 0x10, 0x06, 0xFE, 0xE7, 0x00, 0x23, 0xFF, 0x07, 0x10, 0x07, 0xB3, 0xFF, 0x01, 0x29, 0x91, 0xF8, 0x3E, 0x87, 0x7F, 0xFF, 0x39, 0xA1, 0x0C, 0xBE, 0x7F, 0x10, 0x05, 0xFD, 0xFF, 0x7F, 0x00, 0x0F, 0x22, 0xFF, 0x20, 0xBF, 0xCF, 0xFE, 0xFF, 0xEE, 0x00, 0x38, 0xFF, 0xCC, 0x08, 0xCC, 0xCE, 0xCC, 0x61, 0x2C, 0xA8, 0xEE, 0xEE, 0xFF, 0x38, 0xFF, 0x01, 0x00, 0x2D, 0x17, 0x70, 0x17, 0x01, 0xF0, 0x1F, 0xFF, 0xFF, 0xF8, 0x20, 0xFF, 0x4B, 0x28, 0xEF, 0xCC, 0xEE, 0xEC, 0xFE, 0x63, 0x1E, 0x24, 0x24, 0x24, 0x0F, 0xFF, 0xFF, 0x73, 0xEF, 0x10, 0x14, 0x73, 0xFF, 0x10, 0x08, 0xFF, 0x67, 0x08, 0x80, 0xE7, 0xFF, 0xCE, 0xCC, 0xEF, 0x88, 0x4B, 0x2C, 0x2C, 0x64, 0x2C, 0x03, 0x9E, 0x7F, 0x57, 0xFF, 0xCC, 0x8C, 0x5E, 0xD7, 0xCB, 0xFF, 0x1A, 0xE8, 0xFF, 0x47, 0x10, 0x00, 0xAD, 0x7F, 0x10, 0x26, 0x25, 0x17, 0x7F, 0xCF, 0xFF, 0xEF, 0x00, 0xFF, 0x0F, 0x0A, 0x02, 0x02, 0x02, 0x17, 0x33, 0x10, 0x17, 0x11, 0x71, 0x20, 0x07, 0x11, 0x11, 0x11, 0x11, 0x7E, 0xA3, 0x4E, 0xAF, 0xDD, 0x17, 0x70, 0x17, 0x90, 0x1F, 0xDE, 0xCF, 0x01, 0x70, 0x1F, 0xFF, 0x0D, 0x7D, 0x11, 0x71, 0x65, 0x20, 0x6F, 0xAF, 0x0F, 0xF0, 0x5D, 0x8F, 0x54, 0xF7, 0xCF, 0xFF, 0xFE, 0x2E, 0xC7, 0x42, 0x10, 0x24, 0x35, 0x37, 0x0F, 0xFF, 0x07, 0x0F, 0x12, 0x03, 0x03, 0x03, 0xF0, 0x0F, 0x00, 0x1D, 0x07, 0x03, 0xDD, 0x17, 0xD0, 0xF3, 0xEF, 0xF3, 0xFF, 0xF0, 0x23, 0xEF, 0x82, 0x1D, 0x1D, 0x1D, 0xE0, 0x7F, 0xFF, 0xF0, 0x0F, 0x10, 0x4C, 0x03, 0xFF, 0xF7, 0xFF, 0x77, 0x0A, 0x01, 0x00, 0x01, 0x01, 0x77, 0x77, 0x77, 0x77, 0xDA, 0x08, 0x38, 0x08, 0x08, 0xFF, 0xFF, 0x70, 0x17, 0x00, 0xF0, 0x1F, 0xFF, 0x7F, 0x77, 0x30, 0xF7, 0x07, 0x2A, 0xBF, 0x10, 0x02, 0x87, 0xFF, 0xCF, 0xFF, 0x0F, 0xCB, 0xD4, 0x21, 0x7F, 0x00, 0x17, 0xFF, 0x01, 0x06, 0xC7, 0xFF, 0xF7, 0x10, 0x0B, 0xEB, 0xFF, 0x01, 0x00, 0x00, 0x13, 0x00, 0x62, 0x22, 0x22, 0x22, 0x33, 0x33, 0x03, 0x00, 0x00, 0x03, 0x29, 0x29, 0x29, 0xFE, 0x5F, 0x70, 0x17, 0xE0, 0x90, 0x1F, 0xDE, 0x5F, 0x00, 0xF0, 0x1F, 0x1E, 0xFF, 0x10, 0xFF, 0x6B, 0x10, 0x0A, 0x0A, 0x0A, 0x7F, 0xFF, 0x0F, 0xFF, 0x00, 0xFF, 0x08, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x0F, 0xFF, 0xEF, 0xFF, 0xEF, 0x40, 0x0E, 0x00, 0x2F, 0x4F, 0xCE, 0xC8, 0xCE, 0x88, 0x4F, 0x2C, 0x00, 0x2C, 0x2C, 0x88, 0x88, 0xAA, 0xAA, 0x1B, 0x34, 0x38, 0x34, 0x34, 0xFF, 0x77, 0x70, 0x17, 0x02, 0xA0, 0x1F, 0x8A, 0x17, 0x35, 0x01, 0x35, 0x35, 0xCC, 0xE8, 0x88, 0xE8, 0x4B, 0x20, 0x67, 0xBF, 0x00, 0x1F, 0xD7, 0xFE, 0x10, 0x0C, 0xCF, 0xE7, 0x04, 0xFE, 0x5F, 0xF3, 0xEF, 0x10, 0x00, 0x83, 0xFF, 0x10, 0x16, 0x6F, 0x4F, 0xFE, 0x5F, 0x00, 0x0A, 0x00, 0x8F, 0x00, 0x0B, 0x37, 0x37, 0x37, 0x81, 0x04, 0x93, 0xFF, 0x80, 0xFF, 0x4B, 0x1F, 0x1F, 0x1F, 0x10, 0x27, 0x73, 0xFF, 0xB3, 0x2F, 0xFF, 0xEE, 0x3F, 0xEF, 0x20, 0x06, 0xEE, 0x43, 0x00, 0x4D, 0xD7, 0x70, 0x17, 0xAB, 0x00, 0xE0, 0x1F, 0xEF, 0x00, 0x50, 0x3F, 0xFE, 0x2A, 0x3F, 0x22, 0x10, 0x27, 0x53, 0xE7, 0x10, 0x01, 0xDD, 0xC7, 0x00, 0xE9, 0xFF, 0x01, 0x6B, 0x0A, 0x0A, 0x0A, 0xF7, 0x01, 0x77, 0x77, 0x77, 0x5A, 0x08, 0x08, 0x08, 0x20, 0x06, 0x21, 0x77, 0xDA, 0x20, 0x07, 0x00, 0x00, 0x70, 0x07, 0xBD, 0x2F, 0xF0, 0x70, 0x17, 0x90, 0x1F, 0xDD, 0x37, 0x70, 0x37, 0xFF, 0xF7, 0x77, 0xF7, 0x08, 0x4E, 0x0F, 0x0F, 0x0F, 0x8D, 0x67, 0x10, 0x00, 0xD0, 0x08, 0x43, 0x29, 0x29, 0x29, 0xFF, 0xEF, 0xFB, 0xFF, 0xF1, 0x30, 0xFF, 0x0F, 0x45, 0x8F, 0x10, 0x25, 0xED, 0x1F, 0x0F, 0xFF, 0xFF, 0x00, 0x1D, 0x03, 0x03, 0x03, 0xF0, 0x0F, 0x03, 0x7D, 0x07, 0x2F, 0xFA, 0xF0, 0x00, 0x30, 0x0F, 0x00, 0xF0, 0xFF, 0xF0, 0xFF, 0x82, 0x1D, 0x1D, 0x1D, 0xE8, 0x7F, 0xFF, 0xF0, 0x0F, 0x10, 0x2C, 0xA3, 0xFF, 0x70, 0x10, 0x07, 0xC3, 0xFF, 0xBF, 0xFF, 0x3F, 0xC0, 0x5F, 0x07, 0x8F, 0xFF, 0x0F, 0x0A, 0x02, 0x02, 0x02, 0x01, 0x00, 0xEF, 0x13, 0x11, 0x82, 0x21, 0x21, 0x21, 0x11, 0x1C, 0x11, 0x11, 0x11, 0x00, 0x36, 0x07, 0x70, 0x17, 0x01, 0x00, 0x1F, 0x10, 0x11, 0x04, 0x11, 0x0A, 0x37, 0x37, 0x37, 0xFA, 0x1F, 0x3D, 0xFF, 0x1F, 0x71, 0xFF, 0x8B, 0x20, 0x67, 0x95, 0xFF, 0x26, 0x87, 0x2A, 0x5F, 0x10, 0x06, 0x0F, 0x2F, 0x08, 0x00, 0xFF, 0x08, 0x0C, 0xA1, 0x87, 0xFF, 0xDF, 0xFF, 0x30, 0xCF, 0x0E, 0x3B, 0xE4, 0xEB, 0xCF, 0xCC, 0xCC, 0xCE, 0xCC, 0x00, 0x61, 0x20, 0x20, 0x20, 0xEE, 0xEE, 0xFF, 0xFF, 0x74, 0x01, 0x00, 0x28, 0x17, 0x70, 0x17, 0x00, 0x00, 0x1F, 0x80, 0xD0, 0x1F, 0xCC, 0xED, 0x01, 0xCC, 0xEC, 0xAA, 0x1F, 0x1F, 0x1F, 0xF3, 0x27, 0xFF, 0x63, 0x57, 0x3C, 0x1F, 0x6F, 0xFF, 0xFE, 0xFF, 0xFE, 0x10, 0x06, 0x7F, 0xFF, 0x43, 0xEF, 0xFC, 0xF3, 0xFF, 0x03, 0xA9, 0xFF, 0x00, 0x4A, 0x47, 0xF3, 0xEF, 0x10, 0x06, 0x83, 0xFF, 0x0E, 0xEF, 0x6F, 0xB7, 0xBB, 0x00, 0x37, 0x33, 0x6A, 0x0A, 0x0A, 0x0A, 0xFF, 0xEF, 0xA7, 0x82, 0xF7, 0x10, 0x3C, 0xF7, 0x77, 0x77, 0x24, 0xE7, 0x2D, 0x17, 0x70, 0x07, 0x38, 0x00, 0x00, 0x77, 0x07, 0x57, 0x0F, 0x70, 0x1F, 0xBB, 0xBB, 0x33, 0x30, 0x73, 0x05, 0x43, 0x2F, 0xD0, 0x1F, 0xBB, 0x7B, 0x33, 0xF7, 0x6A, 0x0B, 0xAD, 0x57, 0x20, 0x3F, 0x01, 0x3D, 0x4F, 0xFE, 0x2C, 0xF7, 0x4B, 0xDA, 0xA4, 0xF7, 0x0F, 0x73, 0xFF, 0x0F, 0x26, 0x88, 0x26, 0x5E, 0x20, 0x10, 0x15, 0xAC, 0xF7, 0x0F, 0x70, 0x16, 0xAC, 0xE7, 0xF0, 0x0F, 0x03, 0x96, 0xE7, 0xF0, 0xFF, 0xA2, 0x16, 0x31, 0x16, 0x16, 0xF0, 0x0F, 0x07, 0x8F, 0xFF, 0xEF, 0xFF, 0xEF, 0x30, 0x9C, 0x80, 0xFF, 0xFF, 0x8A, 0xCC, 0x8E, 0x88, 0x0B, 0x34, 0x34, 0x0F, 0x34, 0xCC, 0xCC, 0x88, 0x40, 0x07, 0xFF, 0xFF, 0x70, 0x17, 0x01, 0x00, 0x1F, 0x41, 0x8C, 0x00, 0x50, 0x3F, 0xC9, 0xFF, 0xEC, 0xFF, 0x62, 0x07, 0xB1, 0x07, 0xC0, 0x10, 0x1E, 0x73, 0xFF, 0x10, 0x1E, 0x70, 0x87, 0xBF, 0xFF, 0x8F, 0x4F, 0x0F, 0x0F, 0x00, 0x0F, 0x08, 0x08, 0x8C, 0x88, 0x62, 0x29, 0x29, 0x43, 0x29, 0xFF, 0xFF, 0x00, 0x00, 0x88, 0x88, 0x5E, 0xFF, 0x50, 0x07, 0x80, 0x02, 0x00, 0x1F, 0x80, 0x00, 0xA8, 0x0F, 0x36, 0x36, 0x36, 0x07, 0xFD, 0xFF, 0xF8, 0xFF, 0x43, 0x20, 0x67, 0x10, 0x27, 0x03, 0xEF, 0x0B, 0xEE, 0x57, 0x01, 0x43, 0x4C, 0x49, 0x4D, 0xFF, 0xFE, 0x14, 0x3F, 0xA5, 0x02, 0x02, 0x28, 0x80, 0x00, 0x00, 0x01, 0x2F, 0xFB, 0x69, 0x08, 0x6D, 0x61, 0x67, 0x10, 0x3F, 0xB8, 0x01, 0x00, 0x01, 0x70, 0x0A, 0x54, 0x83, 0x44, 0xC9, 0x04, 0x30, 0x05, 0x1F, 0xA2, 0xFF, 0x11, 0x00, 0x7D, 0x17, 0x17, 0x17, 0xCC, 0xDC, 0x55, 0x55, 0x00, 0xC9, 0x3D, 0x3D, 0x3D, 0xFF, 0xF0, 0x0F, 0x0F, 0x60, 0x70, 0x20, 0x07, 0x3F, 0xB9, 0x02, 0xF8, 0xF8, 0xF8, 0x1B, 0x01, 0xFF, 0x71, 0xFF, 0x59, 0x14, 0x14, 0x14, 0x85, 0x4F, 0x01, 0xF0, 0xF0, 0xF0, 0xE4, 0x91, 0x91, 0x91, 0x10, 0x07, 0xFF, 0xDF, 0x00, 0xBF, 0x93, 0x3F, 0x37, 0x18, 0x04, 0x04, 0x04, 0xC0, 0x7F, 0xFF, 0x2F, 0x16, 0x0F, 0x70, 0x08, 0x08, 0x08, 0x99, 0x00, 0x99, 0x55, 0x55, 0xED, 0x2C, 0x2C, 0x2C, 0x2A, 0x10, 0xF1, 0x11, 0xFF, 0x31, 0xE7, 0xF0, 0xFF, 0x00, 0x00, 0x40, 0x22, 0x21, 0xC3, 0x0F, 0xFF, 0xF0, 0xF0, 0x8C, 0xD3, 0x3C, 0xD3, 0xD3, 0x10, 0x0F, 0x0D, 0xB7, 0xE3, 0xEF, 0xF3, 0xFF, 0x83, 0xEF, 0x77, 0x7F, 0x20, 0x77, 0x12, 0x2F, 0xF7, 0x03, 0x55, 0x50, 0x77, 0xCD, 0x00, 0x7E, 0x7E, 0x7E, 0x9D, 0xEE, 0x55, 0x11, 0xA5, 0x82, 0x20, 0x07, 0xFF, 0x7F, 0x77, 0x77, 0x26, 0x22, 0x67, 0xFF, 0x20, 0xFF, 0xF7, 0x4F, 0xFF, 0xCC, 0x8C, 0x11, 0x11, 0xED, 0x00, 0x9E, 0x9E, 0x9E, 0xAE, 0x51, 0x10, 0xF5, 0xB9, 0x10, 0x3B, 0x3B, 0x3B, 0xFF, 0xFF, 0xFB, 0xFF, 0xF3, 0xFF, 0x41, 0x62, 0x29, 0xAF, 0x7F, 0x93, 0x7F, 0x37, 0xB8, 0x2D, 0xB7, 0x90, 0x20, 0x36, 0x77, 0x06, 0x2F, 0xFF, 0x77, 0xFF, 0x77, 0x77, 0x40, 0x66, 0x22, 0xAF, 0x95, 0xEA, 0x57, 0x01, 0xB1, 0x5D, 0x00, 0x5D, 0x5D, 0x00, 0x00, 0xDD, 0xDD, 0xE1, 0x9F, 0x35, 0x9F, 0x9F, 0x40, 0x17, 0x29, 0xDF, 0xF7, 0x20, 0x5F, 0x22, 0x3D, 0xE4, 0x5C, 0x40, 0x50, 0x17, 0xAA, 0xF0, 0x5F, 0x20, 0x40, 0x35, 0x2B, 0x7B, 0xFF, 0x10, 0x73, 0xFF, 0x82, 0x2A, 0x0F, 0xC3, 0xD1, 0x1F, 0x13, 0x00, 0x98, 0x2A, 0x2A, 0x2A, 0x77, 0x77, 0x77, 0x77, 0x60, 0x6E, 0x2F, 0xFF, 0x70, 0x3F, 0x33, 0x73, 0xDD, 0xDD, 0x81, 0x00, 0x8F, 0x8F, 0x8F, 0x4A, 0xD5, 0x50, 0x57, 0xAD, 0x08, 0x3C, 0x3C, 0x3C, 0x7F, 0x20, 0xD7, 0x2A, 0x01, 0x01, 0x41, 0x01, 0x70, 0x67, 0x9D, 0xEA, 0x57, 0x11, 0xA9, 0x20, 0xDF, 0xE0, 0x70, 0x67, 0x70, 0x77, 0x7F, 0xFF, 0x8E, 0x54, 0x10, 0xF7, 0xD5, 0x80, 0x2A, 0x67, 0x55, 0xA8, 0x7F, 0x15, 0xB5, 0x2B, 0x2B, 0x2E, 0x2B, 0xF7, 0x20, 0x60, 0x2E, 0x30, 0x3F, 0x21, 0x07, 0x30, 0xA7, 0x35, 0x08, 0x22, 0x55, 0x11, 0x85, 0x20, 0x5F, 0xAA, 0x1B, 0x11, 0x04, 0x51, 0x8D, 0x8E, 0x8E, 0x8E, 0xFF, 0xFF, 0x39, 0x7B, 0x00, 0x73, 0xF3, 0xCC, 0x61, 0x61, 0x61, 0xFF, 0x7F, 0x38, 0xFF, 0x7F, 0x31, 0x4F, 0x7F, 0xFF, 0x70, 0x47, 0x93, 0x9B, 0x37, 0x06, 0x33, 0xD8, 0x38, 0x38, 0x38, 0x70, 0x47, 0x21, 0x16, 0x77, 0x41, 0x2E, 0x23, 0xC7, 0x7F, 0xF7, 0x77, 0xFF, 0x03, 0x20, 0xA7, 0x10, 0x00, 0x00, 0x5D, 0x50, 0xFF, 0x59, 0x11, 0x75, 0xAD, 0xC0, 0x31, 0x7F, 0xE3, 0xEF, 0x39, 0xFF, 0x73, 0xF7, 0xF0, 0x51, 0x30, 0x51, 0x51, 0x73, 0xFF, 0xB3, 0xEF, 0x6C, 0xD4, 0xD4, 0xD4, 0x80, 0x73, 0xFF, 0x23, 0x10, 0xC4, 0xFD, 0xA1, 0xCF, 0xCF, 0x57, 0xCF, 0x31, 0x1F, 0x96, 0x61, 0x7F, 0xA6, 0x4B, 0x2F, 0x51, 0x77, 0x00, 0x70, 0xFF, 0xF7, 0x71, 0x77, 0x71, 0xC7, 0x71, 0x77, 0x71, 0x67, 0xC1, 0x61, 0x77, 0x71, 0x67, 0x81, 0x3F, 0x80, 0x61, 0xE7, 0x0E, 0x15, 0x50, 0x77, 0xB5, 0x28, 0x28, 0x5A, 0x28, 0x21, 0xE7, 0x11, 0x31, 0xE7, 0x20, 0xD7, 0xF7, 0xB1, 0x5F, 0x1D, 0x00, 0x3C, 0x31, 0xF1, 0xB0, 0xA2, 0xA2, 0xA2, 0xB7, 0x83, 0x22, 0x1F, 0x78, 0x15, 0x15, 0x15, 0x7F, 0x22, 0x1F, 0x30, 0xF7, 0x8D, 0x82, 0x1F, 0xAA, 0x57, 0x11, 0x30, 0xF7, 0x22, 0x1F, 0x5D, 0x42, 0x1F, 0xC2, 0x61, 0x77, 0x7F, 0x87, 0x22, 0x53, 0x11, 0x55, 0x31, 0x7F, 0xBD, 0x01, 0x39, 0x33, 0x73, 0xD8, 0x83, 0x83, 0x83, 0x31, 0x67, 0xC0, 0x42, 0x5F, 0x62, 0x07, 0x9F, 0xE9, 0x97, 0x1F, 0x30, 0x05, 0x01, 0x05, 0x05, 0xFB, 0xFF, 0x1D, 0x11, 0x85, 0x21, 0x9F, 0x60, 0x7F, 0x22, 0x38, 0xBF, 0xBF, 0x9D, 0x3C, 0x31, 0x71, 0xD4, 0x00, 0xA3, 0xA3, 0xA3, 0xF7, 0x6F, 0xF7, 0x1F, 0x22, 0xDA, 0x32, 0x17, 0x22, 0xF7, 0x07, 0x32, 0x1F, 0x20, 0xA7, 0x66, 0x2F, 0xFC, 0xE1, 0x00, 0xDD, 0x1F, 0x11, 0xB4, 0x6B, 0x6B, 0x6B, 0xEE, 0x01, 0x50, 0x11, 0xF1, 0xB5, 0x6C, 0x6C, 0x6C, 0xFF, 0xFF, 0x40, 0x79, 0x32, 0xDF, 0x16, 0x16, 0x16, 0xFF, 0x83, 0xFF, 0x31, 0x1F, 0x14, 0x22, 0xDF, 0xF0, 0x3F, 0xA5, 0xAA, 0x1D, 0x40, 0xBF, 0x00, 0x1D, 0x1E, 0x11, 0xF1, 0xD0, 0xB4, 0xB4, 0xB4, 0x83, 0xFF, 0xFF, 0x99, 0x99, 0x99, 0x99, 0x23, 0xA0, 0x07, 0x05, 0x00, 0x1F, 0xA0, 0x97, 0xEF, 0xBF, 0x00, 0x33, 0xFF, 0x7F, 0xB7, 0x00, 0x88, 0x13, 0x10, 0xF5, 0xF5, 0xF5, 0x21, 0xF9, 0xBB, 0x1D, 0xF9, 0xF9, 0x02, 0xF9, 0xCF, 0xED, 0xE0, 0xEC, 0x94, 0x27, 0xFF, 0xEF, 0x10, 0xFE, 0xEE, 0xEE, 0x33, 0xF7, 0x3B, 0xF7, 0x88, 0x00, 0x00, 0x86, 0xE2, 0xE2, 0xE2, 0x00, 0x1F, 0x1F, 0xF0, 0x00, 0x0E, 0xFD, 0xFD, 0xFD, 0x3E, 0xF0, 0x0E, 0x0E, 0x41, 0x8C, 0x23, 0xBF, 0xFF, 0x1F, 0x00, 0x10, 0x07, 0x28, 0x2B, 0x00, 0xFE, 0xBF, 0xF0, 0x3F, 0x68, 0x40, 0x40, 0x40, 0x00, 0xC3, 0x01, 0x17, 0x13, 0xB4, 0x6E, 0x6E, 0x6E, 0x04, 0xFE, 0xE3, 0xF0, 0x0F, 0x8C, 0x21, 0x97, 0xE1, 0x00, 0x00, 0x0F, 0x8F, 0x80, 0xDF, 0xDF, 0xDF, 0x00, 0xB7, 0x10, 0x01, 0x80, 0x26, 0x2F, 0xFC, 0x57, 0xBB, 0x80, 0xAA, 0x00, 0x35, 0xE7, 0xE7, 0xE7, 0x00, 0xE3, 0xF0, 0xE0, 0x00, 0x10, 0xFA, 0xFA, 0xFA, 0xF1, 0xFF, 0xF0, 0xFF, 0x40, 0x93, 0x21, 0x47, 0xB9, 0xF7, 0x8C, 0x00, 0x35, 0xEA, 0x01, 0xEA, 0xEA, 0x00, 0xDE, 0xFF, 0x10, 0x06, 0x2F, 0xFC, 0x00, 0xDE, 0x3C, 0xCE, 0x0E, 0x54, 0x06, 0x06, 0x06, 0x00, 0x32, 0x33, 0x0E, 0x00, 0x13, 0xF7, 0xF7, 0xF7, 0x05, 0x1C, 0x79, 0x71, 0xF3, 0xD0, 0x22, 0x2F, 0xFF, 0x26, 0xEF, 0x00, 0x68, 0x4E, 0x4E, 0x4E, 0x1F, 0x3C, 0x70, 0xF0, 0x0C, 0x54, 0xE6, 0xE6, 0xE6, 0x70, 0x0F, 0x78, 0xC7, 0x80, 0xB3, 0x19, 0x88, 0x88, 0x32, 0x20, 0x9F, 0x20, 0x97, 0x00, 0x06, 0x28, 0xC3, 0x04, 0xE1, 0xC1, 0xE0, 0xEE, 0xB4, 0x22, 0x67, 0xCC, 0x4C, 0x10, 0xBB, 0xBB, 0x0D, 0x20, 0x6F, 0x7B, 0x00, 0x08, 0xFF, 0x00, 0x22, 0xF1, 0xF1, 0xF1, 0xEE, 0x1E, 0xFE, 0x0E, 0x00, 0x8F, 0x1E, 0x1E, 0x1E, 0x3E, 0x00, 0x0F, 0xEF, 0x00, 0x80, 0xAF, 0xAF, 0xAF, 0x00, 0x7E, 0x10, 0x11, 0x80, 0x30, 0x9F, 0x45, 0x80, 0x91, 0x88, 0x35, 0xFC, 0xFC, 0x02, 0xFC, 0xCC, 0xE0, 0x00, 0xF0, 0x6F, 0x20, 0x77, 0xC1, 0x08, 0xEF, 0xE0, 0xEE, 0xD0, 0x20, 0xD7, 0x88, 0x44, 0xBB, 0x00, 0x19, 0x15, 0xFB, 0xFB, 0xFB, 0x00, 0x00, 0x08, 0x21, 0xDD, 0x22, 0x35, 0x3F, 0x10, 0xEE, 0x0E, 0x58, 0x27, 0x87, 0x04, 0xCE, 0xCC, 0x0F, 0x00, 0x4B, 0x20, 0x9F, 0x1E, 0x7C, 0x00, 0x70, 0xF0, 0x70, 0xE5, 0xE5, 0xE5, 0xB7, 0xC3, 0x10, 0x37, 0x1F, 0x34, 0x20, 0xB7, 0x10, 0xFF, 0xFF, 0xF0, 0x41, 0x10, 0x29, 0x57, 0xC1, 0xE1, 0x1F, 0x07, 0x70, 0x24, 0x3F, 0x00, 0xE9, 0xFE, 0x03, 0x00, 0x86, 0xDB, 0xDB, 0xDB, 0x06, 0x4C, 0xA0, 0x00, 0xEA, 0x79, 0x20, 0xF7, 0x79, 0x77, 0xBD, 0x00, 0xB8, 0x70, 0x33, 0x78, 0xC5, 0xC5, 0xC5, 0xBB, 0x08, 0x5B, 0xAA, 0xAA, 0x51, 0x20, 0x3F, 0x39, 0xFF, 0x0C, 0x00, 0x00, 0x33, 0xF6, 0xF6, 0xF6, 0x11, 0x31, 0x55, 0x00, 0x45, 0x3D, 0x19, 0x19, 0x19, 0xCB, 0x00, 0x07, 0x00, 0xFF, 0x80, 0xBF, 0xBF, 0xBF, 0xCE, 0x3C, 0x10, 0x00, 0x71, 0x74, 0xE9, 0xE9, 0xE9, 0x08, 0xFF, 0x73, 0x04, 0x0F, 0x50, 0x18, 0x18, 0x18, 0x21, 0x77, 0xF0, 0x0A, 0xC2, 0x31, 0xAF, 0x29, 0xC8, 0xD0, 0x48, 0x48, 0x48, 0x77, 0xEF, 0xBF, 0x01, 0x93, 0x80, 0xC8, 0x3A, 0xF4, 0xF4, 0xF4, 0xF4, 0x0F, 0x06, 0xF5, 0x5F, 0xA8, 0x8A, 0x31, 0x20, 0x9F, 0x2F, 0x3F, 0x0C, 0x41, 0xC4, 0x21, 0x8F, 0xFE, 0xEF, 0xFE, 0xEF, 0x26, 0x2F, 0xFF, 0x04, 0x1E, 0x3C, 0x8F, 0x0E, 0x90, 0x25, 0x1F, 0x00, 0x10, 0x10, 0x00, 0x70, 0x67, 0x21, 0x17, 0x3C, 0xFD, 0xF1, 0xF1, 0x08, 0xA4, 0x70, 0x70, 0x70, 0x7A, 0x17, 0x3C, 0xFC, 0x70, 0x04, 0xF1, 0x88, 0xB2, 0xB2, 0xB2, 0x91, 0x67, 0x0F, 0xB0, 0xE0, 0x46, 0x3F, 0x61, 0x87, 0x71, 0x67, 0xDC, 0x01, 0x11, 0x11, 0x2A, 0x82, 0x2A, 0x33, 0x80, 0x00, 0x89, 0x88, 0x3A, 0x2F, 0xFC, 0x1F, 0x00, 0xE8, 0x80, 0xEE, 0xB9, 0xC6, 0xC6, 0xC6, 0xD9, 0x00, 0xEE, 0xEA, 0xEE, 0xAD, 0x60, 0x60, 0x60, 0x87, 0x08, 0xBE, 0x88, 0x30, 0x6C, 0x20, 0xE7, 0x87, 0xFE, 0x1F, 0x00, 0x00, 0xD1, 0xAD, 0xAD, 0xAD, 0xFF, 0xC6, 0xEE, 0x27, 0x0F, 0x90, 0x23, 0xF7, 0x37, 0x33, 0x21, 0x3F, 0x20, 0xD7, 0x2F, 0x7F, 0x00, 0xE8, 0x37, 0xFE, 0xFE, 0xFE, 0x15, 0x19, 0xA0, 0x20, 0xAA, 0x79, 0x24, 0xBF, 0x00, 0x38, 0xFF, 0x70, 0x14, 0x84, 0x21, 0x6F, 0x12, 0x11, 0x54, 0x55, 0x31, 0x07, 0x1B, 0xF7, 0x10, 0x8E, 0x00, 0x39, 0x22, 0x1F, 0xEF, 0x9E, 0x00, 0x30, 0x41, 0x32, 0x22, 0xA7, 0x8B, 0x83, 0x37, 0x07, 0xCC, 0x44, 0x1F, 0x20, 0xEE, 0xDF, 0x40, 0xA7, 0x16, 0x71, 0xCF, 0xB0, 0x52, 0x01, 0x52, 0x52, 0x3C, 0x00, 0x0E, 0x08, 0xA4, 0x21, 0x2F, 0x00, 0x3C, 0xFC, 0xF0, 0xF1, 0x8C, 0xC2, 0xC2, 0xC2, 0x00, 0x3C, 0x78, 0x8F, 0x0E, 0xB0, 0x4D, 0x4D, 0x4D, 0x04, 0xEF, 0x3E, 0x00, 0x70, 0x70, 0x21, 0x3F, 0x7A, 0xEF, 0x00, 0xF0, 0xFF, 0x2F, 0x09, 0x09, 0x09, 0x33, 0x52, 0x10, 0x00, 0xF0, 0x67, 0x22, 0x6F, 0x3C, 0xBB, 0xF0, 0x33, 0x00, 0xA4, 0x50, 0x50, 0x50, 0x3E, 0xF0, 0x0F, 0x0E, 0x41, 0x90, 0x20, 0x27, 0xFF, 0x3F, 0x00, 0x70, 0x0A, 0x21, 0x7F, 0x04, 0xC3, 0xF3, 0x1F, 0x07, 0xA8, 0x22, 0xC7, 0xFF, 0xEF, 0x82, 0x52, 0x47, 0x7C, 0xFF, 0xF0, 0xF7, 0x80, 0x20, 0x27, 0x9F, 0x08, 0xE3, 0x3F, 0x0F, 0x30, 0x21, 0x7F, 0x3C, 0x78, 0x70, 0x20, 0xF3, 0xB0, 0x21, 0x17, 0xC3, 0xE1, 0x0F, 0x0F, 0x90, 0x84, 0x2B, 0x4F, 0x01, 0x00, 0x03, 0xFF, 0x32, 0x17, 0xC3, 0xE1, 0x11, 0xE0, 0xF8, 0x90, 0x31, 0x2F, 0xA3, 0x00, 0xE0, 0x31, 0xC7, 0x06, 0xC3, 0xFE, 0xF8, 0xFE, 0xAC, 0x20, 0x5F, 0x09, 0x27, 0xFF, 0xFF, 0xEC, 0xBB, 0xEF, 0xF0, 0x0F, 0x81, 0xEF, 0x0F, 0x53, 0x57, 0x00, 0x00, 0x0F, 0x00, 0xF0, 0x02, 0x0F, 0x98, 0x84, 0x84, 0x84, 0xFF, 0x2A, 0x91, 0x42, 0x00, 0xE3, 0xE3, 0xE3, 0xFF, 0x30, 0xF0, 0x8F, 0x8C, 0x00, 0x82, 0x82, 0x82, 0x78, 0xF7, 0x0E, 0x00, 0x8C, 0x00, 0xBE, 0xBE, 0xBE, 0x00, 0x00, 0xFE, 0xF0, 0x1C, 0xAE, 0x24, 0x57, 0xF0, 0x23, 0xEF, 0x42, 0x28, 0x17, 0x41, 0xDF, 0x23, 0x67, 0x38, 0x00, 0xBB, 0x73, 0x77, 0xD6, 0x44, 0x44, 0x44, 0xFF, 0x0C, 0x00, 0xFF, 0x0F, 0x1C, 0x2A, 0xD7, 0x2A, 0xD1, 0x00, 0xB3, 0x00, 0xE8, 0xE8, 0xE8, 0x9B, 0xCB, 0x37, 0x17, 0xD4, 0x10, 0x3A, 0x3A, 0x3A, 0x71, 0x57, 0x80, 0xF7, 0xF0, 0xF0, 0x68, 0x30, 0x38, 0xA7, 0x2A, 0xEF, 0xC8, 0x23, 0xFF, 0x87, 0x87, 0xF0, 0x38, 0xF0, 0x90, 0x4A, 0xD7, 0xE0, 0x97, 0x2C, 0xB7, 0x8C, 0x80, 0x80, 0x44, 0x80, 0x00, 0x10, 0x0F, 0xFF, 0x0F, 0x93, 0x4D, 0x5E, 0x0F, 0xFF, 0x6E, 0xE0, 0x28, 0x6F, 0x00, 0x00, 0x0F, 0xF0, 0x23, 0xEF, 0x20, 0x77, 0x23, 0xB6, 0xFF, 0x0D, 0x60, 0x30, 0x30, 0x30, 0x00, 0x00, 0x0F, 0x24, 0x78, 0x0C, 0x50, 0x9F, 0x3E, 0xFF, 0xA2, 0x30, 0xDF, 0xE0, 0x0F, 0x2F, 0xF8, 0x43, 0xEF, 0x21, 0x06, 0x0F, 0x60, 0xD4, 0x3D, 0x57, 0x63, 0xFF, 0x30, 0x7C, 0x8E, 0x0E, 0x70, 0x14, 0x1A, 0x1A, 0x1A, 0x2F, 0xF9, 0x7F, 0x3D, 0x67, 0x5F, 0x99, 0x1C, 0x00, 0x88, 0x71, 0x34, 0xE7, 0x34, 0x87, 0x2D, 0x77, 0x8F, 0x70, 0x10, 0x00, 0xF7, 0xF9, 0x26, 0x67, 0xCC, 0xCC, 0xAA, 0xAA, 0x60, 0x31, 0x2D, 0x87, 0x70, 0x07, 0xAA, 0xAA, 0x77, 0x77, 0xB2, 0x14, 0x20, 0x20, 0x20, 0x30, 0x07, 0x96, 0x20, 0x07, 0x77, 0xA1, 0x00, 0x88, 0xF8, 0x95, 0xD6, 0xD6, 0xD6, 0x09, 0xFF, 0x10, 0xEC, 0x0F, 0x90, 0x27, 0x57, 0xBB, 0x72, 0x66, 0xF6, 0x0E, 0x11, 0x02, 0x02, 0x02, 0x00, 0x37, 0xFF, 0x3B, 0xEF, 0x8B, 0xFF, 0x38, 0x02, 0x70, 0x70, 0x78, 0xD5, 0xD5, 0xD5, 0x7F, 0xFF, 0x87, 0x08, 0xF7, 0x0F, 0x0F, 0x6C, 0x20, 0x8F, 0xFF, 0xDF, 0x77, 0x24, 0x57, 0x0D, 0x2C, 0x47, 0x83, 0xC3, 0x73, 0x47, 0xB8, 0xBB, 0xB9, 0x3E, 0x07, 0x87, 0x21, 0x67, 0x3C, 0x2F, 0x2F, 0xF9, 0x80, 0x06, 0x2E, 0x17, 0x43, 0xCF, 0x21, 0x77, 0x88, 0x90, 0x90, 0x90, 0x01, 0x01, 0xFF, 0x2E, 0x2F, 0x70, 0x88, 0x31, 0x77, 0x8A, 0x6F, 0xD0, 0x0F, 0x7F, 0x7F, 0x0F, 0x0F, 0x00, 0x10, 0x07, 0x07, 0x07, 0xF8, 0x00, 0x0D, 0xFF, 0x41, 0x02, 0x25, 0x7F, 0xFF, 0x7C, 0xFF, 0x0F, 0x10, 0x2A, 0x17, 0x50, 0x78, 0x22, 0x50, 0x82, 0x25, 0x17, 0x33, 0x91, 0x00, 0xC8, 0x65, 0x37, 0x24, 0xF7, 0x28, 0x79, 0xAA, 0x3D, 0x3A, 0x8F, 0x3C, 0x53, 0xFF, 0x06, 0x2B, 0x62, 0x60, 0x66, 0x59, 0x2C, 0xEF, 0x81, 0x1F, 0xAC, 0xA1, 0x91, 0x1F, 0x92, 0x21, 0x1F, 0xFF, 0x6F, 0x66, 0x66, 0x30, 0xC7, 0x00, 0xCB, 0xE9, 0xEC, 0xFC, 0xAC, 0x71, 0x71, 0x71, 0x06, 0x9F, 0x3C, 0x8F, 0x8F, 0x54, 0x2D, 0x0F, 0x3A, 0x47, 0xA2, 0x84, 0x3F, 0xFF, 0x5C, 0xFF, 0x0F, 0x8B, 0x25, 0xE7, 0x08, 0x00, 0x10, 0x3F, 0x33, 0x0B, 0x2F, 0xFC, 0x3C, 0x9B, 0x70, 0x33, 0x09, 0xB4, 0xB6, 0xB6, 0xB6, 0x24, 0x27, 0xFF, 0xA0, 0x25, 0x57, 0x04, 0x10, 0xDD, 0xFF, 0x11, 0x02, 0x2F, 0xFC, 0xC1, 0x7F, 0x00, 0x03, 0x00, 0xAC, 0xCE, 0xCE, 0xCE, 0xC4, 0xC4, 0x10, 0xBB, 0x13, 0x03, 0x2F, 0xFC, 0xCC, 0xA0, 0x00, 0xF0, 0x41, 0x57, 0x25, 0x47, 0xEA, 0xF0, 0xEE, 0x0A, 0x95, 0x20, 0x2F, 0x04, 0xBF, 0xB8, 0x30, 0x71, 0x34, 0x22, 0x97, 0x07, 0x33, 0x14, 0x5F, 0x00, 0xD5, 0x32, 0xCF, 0x10, 0x51, 0xBF, 0x01, 0x00, 0x22, 0xFF, 0xFE, 0x47, 0x3F, 0x87, 0x80, 0xF0, 0x30, 0x1F, 0xF5, 0xA0, 0x26, 0xCF, 0x47, 0x2A, 0xF7, 0x83, 0xC1, 0xE0, 0xF8, 0xB4, 0x12, 0xC3, 0xC3, 0xC3, 0x07, 0xFB, 0xFF, 0xFF, 0x7F, 0x57, 0x17, 0xDF, 0x08, 0x9D, 0xCF, 0xCC, 0x98, 0x2F, 0xEF, 0x88, 0x88, 0xAA, 0x36, 0xAA, 0xE5, 0x2F, 0xE7, 0x31, 0xB0, 0xE4, 0x41, 0xE7, 0x5F, 0xFF, 0xC9, 0x0E, 0xFC, 0xE8, 0xFF, 0xC9, 0x31, 0x5F, 0x00, 0x63, 0xFF, 0x30, 0x3F, 0xB8, 0x84, 0xA0, 0x3F, 0xFF, 0xE0, 0xF0, 0xE0, 0x31, 0xFF, 0x3C, 0x39, 0x10, 0x8E, 0x8C, 0xB8, 0x2C, 0x0F, 0xAD, 0xFF, 0xA8, 0xFF, 0x00, 0xC3, 0x24, 0x24, 0x24, 0xFF, 0xFE, 0xFF, 0xEE, 0x41, 0x0E, 0x2B, 0x77, 0x08, 0x00, 0x88, 0xFF, 0x62, 0x27, 0x77, 0x04, 0x10, 0xFF, 0x11, 0x11, 0x4A, 0x27, 0xCF, 0xAE, 0xD9, 0x10, 0xAF, 0x8A, 0xA9, 0x26, 0x67, 0xDB, 0x55, 0xAA, 0xAA, 0x41, 0x6D, 0x25, 0xA7, 0x01, 0x00, 0x11, 0x01, 0x46, 0x27, 0x97, 0x42, 0x37, 0x26, 0xBF, 0x78, 0xEC, 0xEC, 0xEC, 0x20, 0x16, 0xAA, 0x00, 0x8D, 0xB1, 0xB1, 0xB1, 0xD9, 0xED, 0xCC, 0xEC, 0x41, 0xD4, 0x23, 0x8F, 0xCB, 0xFD, 0xE8, 0xFC, 0xA8, 0x45, 0x47, 0xBF, 0x57, 0xFF, 0xFE, 0x20, 0x58, 0x3F, 0xFF, 0x70, 0xC7, 0xF3, 0xFF, 0x70, 0xD7, 0x80, 0x87, 0x0A, 0xCF, 0xFF, 0x0F, 0x87, 0x53, 0xFF, 0xEF, 0x43, 0xFF, 0xDF, 0x20, 0xFF, 0xCF, 0x3F, 0xFF, 0xCB, 0xDD, 0xAB, 0xAA, 0xA5, 0x86, 0x36, 0xEF, 0xFF, 0xDC, 0xF0, 0x0A, 0x27, 0xF7, 0x2F, 0x2F, 0xFF, 0x40, 0x93, 0x27, 0x5F, 0x99, 0xA9, 0xAA, 0xEA, 0xA5, 0x81, 0x01, 0x81, 0x81, 0xF9, 0xFF, 0xF8, 0xFF, 0x63, 0x5A, 0x77, 0xA1, 0x73, 0xFF, 0xDB, 0x43, 0xFF, 0x9F, 0xFF, 0x8F, 0x87, 0x29, 0xFF, 0x04, 0xBA, 0xBB, 0xAE, 0xAA, 0x85, 0x2D, 0x37, 0x80, 0xFF, 0x40, 0xFD, 0x47, 0xEF, 0xF0, 0x58, 0xFF, 0x8E, 0xC9, 0x31, 0x03, 0x31, 0x31, 0xDD, 0xFA, 0xAA, 0xFA, 0x30, 0x57, 0x30, 0xA7, 0x82, 0x3C, 0xCF, 0x38, 0x7B, 0x8C, 0x88, 0xC8, 0x52, 0x37, 0x11, 0x40, 0x26, 0x2D, 0x7F, 0xDE, 0xDC, 0xEF, 0xCE, 0x58, 0x13, 0x00, 0x13, 0x13, 0xBA, 0x55, 0xAE, 0x8A, 0x8D, 0xA1, 0x01, 0xA1, 0xA1, 0x32, 0x33, 0xDD, 0x1D, 0x43, 0x44, 0x47, 0x10, 0x11, 0x11, 0xCA, 0x24, 0xFF, 0x33, 0x33, 0xAA, 0xAA, 0x42, 0x59, 0x2F, 0xB7, 0x55, 0xAB, 0xA8, 0xAA, 0x30, 0x1F, 0x1E, 0x08, 0x3E, 0x11, 0xF0, 0x70, 0x23, 0xC7, 0x8F, 0x78, 0x8F, 0x20, 0x0F, 0x74, 0x23, 0x47, 0x9D, 0xEC, 0xA8, 0xFE, 0xC5, 0xC8, 0x29, 0x6F, 0x91, 0x5F, 0xAB, 0x33, 0x3B, 0x1C, 0x00, 0x10, 0x10, 0x22, 0x11, 0x2A, 0x2F, 0xEC, 0xAE, 0xDD, 0xAF, 0x40, 0xD7, 0xAA, 0xC1, 0x21, 0x5F, 0x31, 0x47, 0xFF, 0x00, 0x11, 0x11, 0x56, 0x25, 0x57, 0xB4, 0x21, 0x67, 0xDC, 0x38, 0xBF, 0xA0, 0x5F, 0xEA, 0x30, 0x7F, 0x7F, 0xA1, 0x00, 0x00, 0xF8, 0x59, 0xEB, 0xEB, 0xEB, 0x85, 0xFF, 0x10, 0xE8, 0xFF, 0xCD, 0x20, 0xBF, 0xCD, 0xED, 0xEC, 0xFE, 0x68, 0xCC, 0x20, 0xC7, 0x00, 0x35, 0xFF, 0x23, 0x2E, 0x2B, 0xDE, 0x9D, 0xCE, 0xFD, 0xC2, 0x3F, 0xF3, 0xFF, 0x81, 0xFF, 0x7F, 0xFF, 0x21, 0x6F, 0x3D, 0xB7, 0xBC, 0x32, 0x1F, 0x10, 0x5C, 0x5C, 0x5C, 0x7F, 0xFF, 0xEF, 0xDE, 0xEF, 0xCE, 0x66, 0x16, 0x2D, 0x97, 0x29, 0x97, 0x10, 0x26, 0x32, 0x1F, 0x20, 0xA7, 0x5A, 0x82, 0x29, 0xEF, 0x9E, 0xD9, 0xAE, 0x8A, 0xB1, 0x25, 0xC7, 0x3B, 0x82, 0x61, 0x07, 0xEE, 0xEE, 0x11, 0x11, 0x83, 0x29, 0x3F, 0x00, 0x08, 0x1E, 0x11, 0xF0, 0xAC, 0x22, 0x1F, 0xD5, 0xAA, 0xAA, 0x25, 0xEA, 0x8D, 0x27, 0xDF, 0xDD, 0xEA, 0x51, 0x67, 0x1E, 0x2A, 0x47, 0x44, 0x80, 0x34, 0x0F, 0xEF, 0xFF, 0xEF, 0x3E, 0x67, 0xED, 0xFF, 0x1D, 0xEC, 0xFF, 0x62, 0xAF, 0xFF, 0x78, 0x07, 0x2F, 0xF9, 0xFD, 0x3E, 0xFF, 0x06, 0xDF, 0x9C, 0xCF, 0xCE, 0x58, 0x2E, 0x97, 0x72, 0xFF, 0x97, 0x08, 0xC3, 0xC0, 0xE8, 0x94, 0x25, 0x27, 0xFE, 0x70, 0xFF, 0x00, 0x0E, 0xB3, 0x35, 0x35, 0x35, 0x8D, 0xFE, 0xA8, 0x61, 0xFF, 0x41, 0x5F, 0x60, 0xB7, 0x78, 0x00, 0x0E, 0x1C, 0x3A, 0xC7, 0x05, 0xCC, 0xA4, 0x00, 0xF0, 0x2F, 0x28, 0xA7, 0xDA, 0x62, 0x37, 0x83, 0x72, 0x27, 0xC3, 0xFD, 0xF8, 0xFC, 0xA4, 0x28, 0xE7, 0x7F, 0xFF, 0x1E, 0xFD, 0xFF, 0xFC, 0x4E, 0x9F, 0x07, 0x8F, 0xFF, 0x07, 0x64, 0x87, 0x37, 0xDF, 0x02, 0xE7, 0x2F, 0xFF, 0xF0, 0x0F, 0x2C, 0xA0, 0xFF, 0x02, 0xAD, 0xDF, 0xF0, 0x0F, 0x0B, 0x81, 0x47, 0xBB, 0x2F, 0xA8, 0x03, 0x00, 0x20, 0x0F, 0x3F, 0x92, 0x31, 0x17, 0x0F, 0x29, 0x08, 0x00, 0x30, 0x0F, 0xFE, 0x03, 0x70, 0x5F, 0x71, 0x67, 0xF0, 0x0F, 0x07, 0x0A, 0x9F, 0x29, 0x9F, 0xC2, 0x1F, 0xE0, 0x0F, 0x00, 0xBC, 0x29, 0x9F, 0x06, 0xB2, 0x1F, 0xE0, 0x0F, 0x10, 0x09, 0x70, 0x07, 0x10, 0x66, 0x73, 0xFF, 0x43, 0x4C, 0x04, 0x49, 0x4D, 0xFF, 0xFE, 0x14, 0x3A, 0x65, 0x02, 0x28, 0x08, 0x20, 0x00, 0x00, 0x01, 0x2F, 0xE3, 0x69, 0x6D, 0x61, 0x21, 0x67, 0x10, 0x2F, 0xEB, 0xE1, 0x00, 0x28, 0x00, 0x3D, 0x9B, 0x72, 0x00, 0x20, 0x17, 0x04, 0x90, 0x01, 0x2E, 0x47, 0xF8, 0x80, 0x10, 0x5B, 0x80, 0x07, 0x80, 0x00, 0x88, 0x77, 0x77, 0x4A, 0xF8, 0x88, 0x09, 0x84, 0x85, 0x20, 0x07, 0x29, 0xFF, 0x8B, 0x04, 0xF6, 0xDF, 0x88, 0x30, 0x17, 0x00, 0xFE, 0x8B, 0x03, 0x04, 0x00, 0xDF, 0xDD, 0x81, 0x11, 0xFF, 0x9D, 0x25, 0x00, 0x16, 0xFF, 0xDD, 0xDD, 0xA1, 0x40, 0x17, 0xC0, 0x70, 0x07, 0x00, 0x50, 0x1F, 0xEE, 0xC8, 0x11, 0x71, 0x45, 0xFE, 0x60, 0x8C, 0x27, 0xB8, 0xF7, 0x3F, 0x77, 0x77, 0x31, 0xFF, 0x8C, 0x0B, 0x04, 0x88, 0x4C, 0x33, 0x20, 0x7F, 0x8A, 0x2E, 0x52, 0xE0, 0x7F, 0x03, 0x00, 0x77, 0xF7, 0x0B, 0xF8, 0x81, 0x2F, 0xFA, 0x10, 0x1C, 0xE7, 0x77, 0x28, 0x80, 0x7F, 0x23, 0x07, 0x89, 0x00, 0x02, 0xE7, 0xCC, 0xE8, 0x77, 0x00, 0x11, 0x45, 0xFF, 0x8C, 0x14, 0xFD, 0x6E, 0x11, 0x00, 0x00, 0x45, 0xFE, 0xAC, 0x34, 0x80, 0x88, 0x7F, 0x05, 0x77, 0x0B, 0xFF, 0x81, 0x02, 0x23, 0xAF, 0x17, 0x33, 0xAF, 0x00, 0x01, 0x80, 0xBB, 0xBB, 0x0B, 0xF8, 0xDE, 0x55, 0x00, 0x44, 0x20, 0x00, 0x22, 0x29, 0xFE, 0xDA, 0x40, 0x10, 0x00, 0x20, 0xDD, 0x53, 0xA7, 0x80, 0x11, 0x99, 0x87, 0x00, 0xF8, 0xC2, 0x41, 0x11, 0x00, 0x20, 0x22, 0x45, 0x00, 0xFF, 0xBA, 0x20, 0x11, 0x01, 0xEE, 0x00, 0x22, 0x00, 0xF8, 0xB7, 0x0F, 0x44, 0x80, 0x09, 0xC8, 0x2B, 0x00, 0xF1, 0xD6, 0x4C, 0x00, 0x00, 0xBB, 0xBB, 0x0D, 0x13, 0xFF, 0xDB, 0x51, 0x4D, 0xAF, 0xF8, 0xA8, 0x5D, 0xB7, 0x20, 0x07, 0x20, 0x17, 0x11, 0x20, 0x47, 0xEE, 0xCA, 0x30, 0x11, 0x11, 0x02, 0x22, 0x22, 0x25, 0xFF, 0xCA, 0x30, 0xF0, 0x1F, 0x00, 0x01, 0x00, 0x22, 0x22, 0x65, 0xFF, 0xCA, 0x40, 0x90, 0x07, 0xC3, 0x00, 0x50, 0x1F, 0x00, 0x70, 0x3F, 0x11, 0x71, 0x22, 0x00, 0x30, 0x5F, 0x00, 0x70, 0x7F, 0x00, 0x48, 0xDC, 0x88, 0x99, 0x47, 0xF8, 0xD7, 0x4E, 0x00, 0x2A, 0x12, 0x99, 0x11, 0x47, 0xF8, 0xBB, 0x41, 0x86, 0x80, 0x9F, 0x11, 0x23, 0x22, 0x23, 0x20, 0xBF, 0x74, 0x47, 0x88, 0x00, 0x88, 0x73, 0x77, 0x8A, 0xF8, 0x9E, 0x1F, 0x12, 0x08, 0x51, 0x22, 0x02, 0x21, 0x30, 0xA7, 0x40, 0x88, 0x08, 0x00, 0x69, 0xEF, 0xCB, 0x41, 0x08, 0x00, 0xF7, 0xFF, 0x40, 0x22, 0xAB, 0x9F, 0x2A, 0x12, 0x98, 0x11, 0x47, 0xF1, 0x3B, 0xBB, 0x3A, 0x44, 0x7F, 0x21, 0x47, 0xFB, 0xBF, 0x08, 0x24, 0x5F, 0x00, 0x31, 0x77, 0x80, 0x01, 0x7B, 0xE7, 0x06, 0x44, 0x51, 0x77, 0x49, 0xFF, 0x8C, 0x00, 0x15, 0x08, 0x08, 0x77, 0xF7, 0x82, 0xF8, 0x96, 0x41, 0x16, 0x10, 0x0C, 0x93, 0x9F, 0xFF, 0x77, 0x0A, 0xF8, 0x81, 0x2E, 0x6E, 0xA0, 0xDD, 0xFF, 0x44, 0x23, 0x8F, 0x2D, 0xFF, 0x8B, 0x03, 0x15, 0x00, 0x22, 0xDF, 0xCD, 0x61, 0xFF, 0xAD, 0x25, 0x00, 0xC8, 0x33, 0x9F, 0x26, 0xAF, 0x84, 0xA8, 0x53, 0x9F, 0xC5, 0x84, 0x91, 0x40, 0x88, 0x32, 0xCF, 0x00, 0x20, 0x88, 0xEE, 0x2D, 0xFF, 0x28, 0xDB, 0x41, 0x33, 0xC7, 0x65, 0x23, 0xC7, 0x44, 0x80, 0x89, 0x80, 0x43, 0x87, 0x21, 0x22, 0x22, 0x32, 0x23, 0xF8, 0xBE, 0x41, 0x1D, 0xF3, 0x7F, 0x01, 0x00, 0x22, 0x32, 0x43, 0xA0, 0x17, 0xF0, 0x10, 0x01, 0x80, 0x07, 0x64, 0x27, 0x34, 0x17, 0x00, 0x34, 0xBF, 0x08, 0x10, 0xBF, 0x3B, 0x00, 0x0F, 0xF8, 0xDE, 0x54, 0x22, 0x51, 0xDC, 0xFD, 0xA0, 0x41, 0xAF, 0x10, 0x24, 0xFF, 0xFF, 0xA8, 0x00, 0x10, 0x11, 0x03, 0x22, 0x00, 0x45, 0xEF, 0xBA, 0x20, 0x74, 0x57, 0x34, 0x47, 0x89, 0x48, 0xFF, 0x40, 0xCE, 0x88, 0x35, 0x2F, 0x2A, 0x11, 0x30, 0x2F, 0x20, 0xBD, 0x35, 0x74, 0xBF, 0x08, 0x10, 0x88, 0x19, 0x4B, 0xE8, 0x31, 0xD7, 0x75, 0x3F, 0x20, 0x47, 0x06, 0x20, 0x47, 0xEE, 0x8C, 0x10, 0x07, 0x31, 0x85, 0xEE, 0x9C, 0x24, 0x74, 0x9F, 0x80, 0x57, 0x60, 0x47, 0xC0, 0x70, 0x57, 0x84, 0x9F, 0x11, 0x98, 0x31, 0x65, 0xFF, 0xBD, 0x06, 0x34, 0x8E, 0xCC, 0x11, 0x77, 0x00, 0x44, 0x9F, 0x24, 0x57, 0x86, 0x86, 0x10, 0x01, 0x34, 0x57, 0x80, 0xFF, 0x7F, 0x03, 0x00, 0x2F, 0xFF, 0x2A, 0xBF, 0x37, 0x00, 0x52, 0xF8, 0x8A, 0x11, 0x8C, 0xEE, 0x17, 0x11, 0xD0, 0x4A, 0x7F, 0x20, 0x27, 0x06, 0xA0, 0x17, 0x21, 0xA2, 0x11, 0x89, 0x80, 0x36, 0x1F, 0x44, 0x00, 0xB3, 0xBB, 0x09, 0xFF, 0xDB, 0x10, 0x52, 0x04, 0x20, 0x5A, 0xE7, 0x11, 0x88, 0x91, 0x89, 0x05, 0x47, 0xF8, 0xC9, 0x4F, 0x17, 0x23, 0x88, 0x41, 0x22, 0x17, 0x21, 0x01, 0x00, 0x57, 0x3F, 0x04, 0x20, 0xBB, 0xFB, 0x20, 0x27, 0x71, 0x51, 0x77, 0x57, 0xF7, 0x3F, 0x24, 0x20, 0x00, 0x02, 0xF1, 0x10, 0x01, 0xA3, 0xAF, 0xF8, 0x08, 0x64, 0xDF, 0x24, 0x0F, 0x38, 0x6F, 0x27, 0xCF, 0x03, 0xE5, 0x7F, 0x10, 0x23, 0x02, 0x00, 0x43, 0xF1, 0xB7, 0x16, 0x11, 0x75, 0x22, 0x00, 0x60, 0x25, 0x49, 0x07, 0xF4, 0x9F, 0x8C, 0x19, 0x4F, 0xF8, 0xD7, 0x2E, 0x4D, 0x22, 0x24, 0x91, 0x8D, 0x24, 0x47, 0x44, 0x9F, 0x44, 0x5F, 0x22, 0x7C, 0x22, 0x36, 0x0F, 0x74, 0x27, 0x28, 0xB7, 0x4A, 0x2F, 0x89, 0x4F, 0x44, 0xBB, 0x60, 0x3B, 0x32, 0xBF, 0xF8, 0xFF, 0xFE, 0x7F, 0x98, 0x19, 0x27, 0x18, 0xF8, 0xC1, 0x47, 0x80, 0x4F, 0x00, 0x6D, 0xBF, 0x88, 0x88, 0x71, 0xFE, 0x4D, 0xE7, 0x00, 0x4D, 0xFF, 0x2E, 0x17, 0x70, 0x07, 0x02, 0x40, 0x1F, 0x00, 0xBE, 0x3F, 0x7A, 0xAF, 0x88, 0x58, 0x91, 0x3A, 0xAF, 0x40, 0xFE, 0x8F, 0x7A, 0xAF, 0xCC, 0xE8, 0x88, 0x30, 0xEE, 0x21, 0x27, 0x3F, 0x73, 0x9F, 0x33, 0xAB, 0x91, 0x89, 0x5E, 0x27, 0x20, 0x2F, 0x11, 0x27, 0x28, 0x35, 0xB7, 0x73, 0x9F, 0x73, 0xC7, 0x15, 0x3F, 0x21, 0x20, 0x00, 0x5A, 0x7F, 0x27, 0x57, 0x3A, 0x47, 0x10, 0x0A, 0xF3, 0x9F, 0x10, 0x00, 0x89, 0x17, 0x68, 0xCF, 0xC1, 0x74, 0x2F, 0x01, 0xFD, 0x9F, 0xCA, 0xCC, 0xBF, 0xBB, 0x05, 0x4E, 0x17, 0xE1, 0x00, 0x5E, 0x1F, 0x9E, 0x37, 0x00, 0x5E, 0x3F, 0x15, 0x11, 0x20, 0x22, 0x3D, 0x8F, 0xF8, 0x00, 0x8D, 0xFF, 0x6E, 0x8F, 0x77, 0x4F, 0x10, 0x1C, 0x83, 0x87, 0x10, 0x2C, 0x62, 0xD7, 0x43, 0x4C, 0x49, 0x10, 0x4D, 0xFF, 0xFE, 0x2F, 0xA6, 0x00, 0x02, 0x02, 0x28, 0xC3, 0x2D, 0xFD, 0x3D, 0xEF, 0x69, 0x6D, 0x61, 0x67, 0x2F, 0xFE, 0x20, 0x0C, 0x1C, 0x40, 0x00, 0x0A, 0x3E, 0x10, 0x4E, 0x15, 0x00, 0x50, 0x03, 0x43, 0x4C, 0x24, 0x59, 0x54, 0x70, 0x3F, 0x80, 0x05, 0x47, 0xAE, 0x00, 0x6C, 0x18, 0x79, 0x74, 0x31, 0x30, 0x51, 0x3E, 0x3B, 0x00, 0x00, 0xA0, 0x00, 0x43, 0x00, 0x00, 0x70, 0x43, 0x74, 0x78, 0x6C, 0x32, 0x31, 0x40, 0x2F, 0xED, 0x2B, 0x90, 0x00, 0x0C, 0x2F, 0xF5, 0x1A, 0xC0, 0x2F, 0xF9, 0x2B, 0x3C, 0x00, 0x62, 0x75, 0x62, 0x62, 0x6C, 0x00, 0x65, 0x73, 0x2E, 0x62, 0x63, 0x6C, 0x69, 0x6D, 0x04, 0x00, 0x6D, 0x61, 0x73, 0x6B, 0x60, 0x0A, 0x77, 0x61, 0x60, 0x76, 0x80, 0x16, 0x2F, 0xFD, 0x6D, 0x61, 0x74, 0x31, 0x8C, 0xCD, 0x2E, 0x8C, 0x2B, 0xB0, 0x00, 0x1C, 0x30, 0x63, 0x2F, 0xF9, 0xD4, 0x2F, 0xFD, 0x66, 0x24, 0x2E, 0xA0, 0x30, 0x35, 0x5F, 0x30, 0x4C, 0x7A, 0xC0, 0xA8, 0xFF, 0x56, 0xFF, 0x00, 0x50, 0x01, 0x15, 0x40, 0x9B, 0x04, 0x30, 0x4A, 0x2F, 0xFF, 0x60, 0x4F, 0xC0, 0x50, 0xDD, 0x80, 0x3F, 0x50, 0x03, 0x80, 0x93, 0x00, 0x00, 0x52, 0x00, 0x60, 0x4F, 0x17, 0x80, 0x55, 0x06, 0x3F, 0x35, 0x05, 0x20, 0xFD, 0xB1, 0x27, 0x90, 0x4F, 0x81, 0x20, 0xB4, 0x22, 0x40, 0x24, 0x02, 0x10, 0x66, 0x2F, 0xFD, 0x47, 0x07, 0x61, 0x4A, 0x01, 0x04, 0x05, 0x50, 0xE2, 0x00, 0x20, 0xB8, 0x00, 0xB0, 0xB7, 0x1E, 0x02, 0x00, 0x05, 0x31, 0x02, 0x31, 0x4A, 0x31, 0x4E, 0x00, 0x50, 0xB7, 0x31, 0x82, 0x04, 0xD0, 0xB7, 0x70, 0x61, 0x6E, 0x31, 0x4C, 0x40, 0xC3, 0xFF, 0x00, 0x00, 0x52, 0x6F, 0x6F, 0x74, 0x50, 0x61, 0x6E, 0x78, 0x65, 0x00, 0x92, 0x21, 0xF2, 0x3B, 0x71, 0x67, 0x52, 0x17, 0x70, 0x61, 0x73, 0x21, 0x31, 0x08, 0x2F, 0xFD, 0x70, 0x69, 0x63, 0x31, 0x3D, 0xE5, 0xE0, 0x30, 0x53, 0x00, 0x71, 0x17, 0x2F, 0xFC, 0xC0, 0x80, 0x00, 0x80, 0xBF, 0xA4, 0x00, 0x90, 0x53, 0xF2, 0x22, 0x6B, 0x9A, 0x43, 0x71, 0xF7, 0x6F, 0xBB, 0x5F, 0xE7, 0x40, 0x03, 0x02, 0x62, 0x89, 0x62, 0xC5, 0x61, 0x37, 0x71, 0xF3, 0x31, 0x4B, 0x2E, 0x80, 0x3F, 0x30, 0x7F, 0xA0, 0x60, 0xD3, 0x00, 0x71, 0x47, 0x2F, 0xFC, 0xB8, 0x0B, 0x00, 0x00, 0x48, 0x42, 0x22, 0xCF, 0xC1, 0xE3, 0x11, 0x61, 0x8F, 0x5A, 0x80, 0x22, 0xEB, 0x80, 0x80, 0x7F, 0x72, 0x7F, 0x03, 0x21, 0xB5, 0xCD, 0x23, 0xCC, 0x6C, 0x42, 0x5B, 0x33, 0x33, 0x5B, 0x41, 0xB7, 0x50, 0x0F, 0x2F, 0x20, 0x40, 0x50, 0x0F, 0x20, 0xF0, 0x93, 0xE2, 0x43, 0x00, 0x60, 0x9F, 0x00, 0x12, 0xF2, 0xBB, 0x02, 0x40, 0x9F, 0x01, 0x22, 0x55, 0x2F, 0xFF, 0x31, 0x93, 0x3E, 0x50, 0x8B, 0x30, 0x07, 0xBE, 0x30, 0x0F, 0x14, 0x60, 0x9B, 0x30, 0x07, 0x01, 0x10, 0x9F, 0x51, 0xBF, 0x00, 0x93, 0x8F, 0x80, 0x03, 0xFD, 0x3F, 0x40, 0x00, 0xFE, 0x0F, 0xA1, 0x7F, 0xD3, 0x2B, 0x01, 0x10, 0x7E, 0x1B, 0x44, 0x98, 0x3D, 0xFC, 0x00, 0x01, 0x3F, 0xF9, 0x74, 0x49, 0x23, 0xA3, 0x41, 0xAB, 0x50, 0x07, 0xE1, 0x1F, 0x61, 0x65, 0x42, 0x47, 0x0C, 0x67, 0x72, 0x70, 0x31, 0x34, 0x1F, 0x32, 0x97, 0x47, 0x72, 0x13, 0x6F, 0x75, 0x70, 0xA4, 0xBA, 0x67, 0x72, 0x52, 0x6B, 0x30, 0x23, 0x8F, 0x32, 0xBF, 0x47, 0x5F, 0x41, 0xC4, 0x36, 0x34, 0x9B, 0xF3, 0xFB, 0xF4, 0x5B, 0xD5, 0xF3, 0x63, 0x90, 0x4B, 0x42, 0x03, 0x40, 0x4B, 0x2C, 0x40, 0x97, 0x43, 0xC4, 0xCE, 0xCD, 0x35, 0x93, 0xF4, 0xE3, 0x67, 0x72, 0x50, 0xEF, 0xB5, 0x7F, 0xE0, 0x24, 0x87, 0x5E, 0x11, 0x00, 0x05, 0x7F, 0xC8, 0x85, 0x7F, 0x33, 0xB3, 0x35, 0x3F, 0x35, 0xDB, 0x1E, 0xA8, 0x2F, 0xFF, 0x29, 0x2F, 0xFD, 0x34, 0x00, 0x05, 0x83, 0x6C, 0x6F, 0x67, 0x6F, 0x6F, 0x00, 0xD5, 0x8E, 0x35, 0x8B, 0x30, 0x22, 0x32, 0x34, 0xF0, 0x36, 0x1A, 0x31, 0x63, 0x55, 0xD8, 0x2F, 0xFF, 0x5C, 0x26, 0x3C, 0xAC, 0x02, 0x65, 0x8F, 0x02, 0x00, 0xA5, 0x8F, 0xFA, 0x30, 0x91, 0x01, 0xB5, 0xDF, 0x35, 0x8F, 0x95, 0xDF, 0x00, 0x15, 0x8F, 0x44, 0x26, 0x44, 0x40, 0x77, 0x24, 0x51, 0x2B, 0xB5, 0x8F, 0x01, 0xF5, 0xF7, 0x6A, 0x65, 0xF7, 0x90, 0xBB, 0x00, 0x25, 0xFB, 0xE1, 0x00, 0x36, 0x0F, 0x37, 0x64, 0x26, 0x13, 0x10, 0x24, 0x02, 0x11, 0x03, 0x36, 0x13, 0x7C, 0x03, 0x03, 0xA6, 0x13, 0x04, 0x30, 0xD3, 0x03, 0x56, 0x2F, 0x52, 0xC7, 0x04, 0x36, 0x2F, 0x6E, 0xAB, 0x6F, 0xE1, 0xE5, 0xAF, 0x76, 0x2F, 0x48, 0x38, 0x1B, 0x28, 0xB6, 0x40, 0x07, 0x05, 0x86, 0x2F, 0x57, 0x04, 0x06, 0x06, 0x2F, 0x20, 0x01, 0xC6, 0xCF, 0x02, 0x86, 0x2F, 0x4A, 0x1C, 0x77, 0x4F, 0xEE, 0x67, 0x4B, 0x46, 0xCB, 0x01, 0xA6, 0x2F, 0xFF, 0x00, 0x83, 0xDF, 0x39, 0x94, 0x50, 0x03, 0xF0, 0x86, 0xC6, 0x33, 0xCD, 0xCC, 0x4C, 0x3F, 0x30, 0x03, 0x57, 0x6F, 0x38, 0x7F, 0x42, 0x00, 0x16, 0xCF, 0xB7, 0xEF, 0x0B, 0x56, 0xAF, 0x3A, 0xEF, 0xF6, 0xAF, 0x3A, 0x70, 0xFA, 0x43, 0xEF, 0xFA, 0xBB, 0xF5, 0x3B, 0x01, 0x36, 0xCF, 0x5C, 0x00, 0x26, 0xCF, 0x3B, 0x77, 0xFB, 0x17, 0xFA, 0xBF, 0x80, 0x04, 0x36, 0xDF, 0x1F, 0xBC, 0xAB, 0xC4, 0xF4, 0xA7, 0x73, 0x00, 0x0A, 0x1A, 0x5D, 0x17, 0xE0, 0x62, 0x0D, 0xCB, 0x00, 0x15, 0x4B, 0xAE, 0x36, 0xE9, 0x22, 0xD6, 0xBC, 0x00, 0x84, 0x95, 0x43, 0x89, 0x8A, 0x34, 0x9F, 0xB6, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; \ No newline at end of file From d9204e52ac1c470b0ffee9f5e1f2c12a7c1d9951 Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 10 May 2020 10:30:37 +0800 Subject: [PATCH 228/317] Update version string to 0.17 --- makerom/user_settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index c6df9663..3e498b6e 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -902,7 +902,7 @@ void PrintNoNeedParam(char *arg) void DisplayBanner(void) { - printf("CTR MAKEROM v0.16.2 (C) 3DSGuy 2020\n"); + printf("CTR MAKEROM v0.17 (C) 3DSGuy 2020\n"); printf("Built: %s %s\n\n", __TIME__, __DATE__); } From 45f06e32b2f5b79b0423c2489411e130d401b143 Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 10 May 2020 12:50:35 +0800 Subject: [PATCH 229/317] Add old homebrew logo under name "HomebrewLegacy" --- makerom/ncch.c | 9 +++++++++ makerom/ncch_logo.h | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/makerom/ncch.c b/makerom/ncch.c index d054cacf..da77ff0d 100644 --- a/makerom/ncch.c +++ b/makerom/ncch.c @@ -363,6 +363,15 @@ int ImportLogo(ncch_settings *set) } memcpy(set->sections.logo.buffer, Homebrew_LZ, 0x2000); } + else if (strcasecmp(set->rsfSet->BasicInfo.Logo, "homebrewlegacy") == 0) { + set->sections.logo.size = 0x2000; + set->sections.logo.buffer = malloc(set->sections.logo.size); + if (!set->sections.logo.buffer) { + fprintf(stderr, "[NCCH ERROR] Not enough memory\n"); + return MEM_ERROR; + } + memcpy(set->sections.logo.buffer, HomebrewLegacy_LZ, 0x2000); + } else if(strcasecmp(set->rsfSet->BasicInfo.Logo,"none") != 0){ fprintf(stderr,"[NCCH ERROR] Invalid logo name\n"); return NCCH_BAD_RSF_SET; diff --git a/makerom/ncch_logo.h b/makerom/ncch_logo.h index c28e10ee..67518170 100644 --- a/makerom/ncch_logo.h +++ b/makerom/ncch_logo.h @@ -28,4 +28,9 @@ static const unsigned char iQue_without_ISBN_LZ[0x2000] = static const unsigned char Homebrew_LZ[0x2000] = { 0x11, 0x40, 0xE6, 0x00, 0x00, 0x64, 0x61, 0x72, 0x63, 0xFF, 0xFE, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0xE6, 0x00, 0x00, 0x83, 0x30, 0x09, 0x64, 0x03, 0x00, 0x00, 0x80, 0x20, 0x03, 0x30, 0x13, 0xAB, 0x30, 0x18, 0x11, 0x20, 0x1D, 0x02, 0xA0, 0x0B, 0x06, 0x20, 0x2B, 0x30, 0x18, 0x5A, 0x09, 0x20, 0x35, 0x10, 0x20, 0x39, 0x30, 0x2B, 0xAC, 0x20, 0x20, 0x54, 0xAA, 0x20, 0x45, 0x40, 0x20, 0x1C, 0x0C, 0x20, 0x2C, 0x98, 0x20, 0x51, 0x60, 0x55, 0x08, 0x50, 0x38, 0xDC, 0x30, 0x0B, 0x0A, 0x50, 0x23, 0x20, 0x20, 0x51, 0x05, 0x20, 0x0D, 0x00, 0x00, 0xE8, 0x20, 0x50, 0x64, 0x30, 0x0B, 0xA5, 0x20, 0x40, 0x78, 0x20, 0x6F, 0xA8, 0x01, 0x50, 0x53, 0x0E, 0x20, 0x89, 0x40, 0xB2, 0x30, 0x75, 0x14, 0x00, 0x00, 0x28, 0x04, 0x00, 0x24, 0x00, 0xC8, 0x20, 0x81, 0x80, 0x18, 0x20, 0x0B, 0x80, 0x00, 0x28, 0x00, 0xE4, 0x30, 0x8D, 0x99, 0x20, 0x17, 0x20, 0x00, 0x00, 0x53, 0xFA, 0x30, 0x17, 0xB9, 0x50, 0x0B, 0x12, 0x02, 0x50, 0x8F, 0x30, 0xA7, 0x48, 0x1C, 0x20, 0xA8, 0xC0, 0xD9, 0x20, 0xBF, 0x05, 0x00, 0x00, 0x41, 0x4C, 0x20, 0xB4, 0x40, 0xDF, 0x00, 0x00, 0xE0, 0x20, 0xB0, 0x10, 0x00, 0x00, 0x2E, 0x20, 0xE3, 0x61, 0x00, 0x6E, 0x00, 0x14, 0x69, 0x00, 0x6D, 0x20, 0xED, 0x4E, 0x20, 0x07, 0x6E, 0x00, 0x10, 0x74, 0x00, 0x65, 0x20, 0x11, 0x64, 0x00, 0x6F, 0x00, 0x51, 0x4C, 0x20, 0x03, 0x67, 0x20, 0x07, 0x5F, 0x00, 0x44, 0x20, 0x03, 0x41, 0x30, 0x20, 0x01, 0x5F, 0x00, 0x53, 0x00, 0x63, 0x40, 0x1F, 0x05, 0x65, 0x00, 0x4F, 0x00, 0x75, 0x20, 0x2B, 0x41, 0x20, 0x43, 0x5A, 0x62, 0x20, 0x13, 0x6C, 0x40, 0x47, 0x02, 0x50, 0x43, 0x42, 0x03, 0x20, 0x43, 0x43, 0xBD, 0x01, 0x80, 0x87, 0x55, 0x04, 0xC0, 0x43, 0x01, 0x90, 0xCB, 0x00, 0x90, 0x87, 0xF1, 0x53, 0x74, 0x41, 0x9F, 0x51, 0x67, 0x22, 0x8F, 0x6D, 0x21, 0xAD, 0x73, 0x00, 0x6B, 0x81, 0x73, 0xAA, 0x51, 0xB7, 0x62, 0x21, 0x89, 0x62, 0x21, 0x85, 0x6C, 0x21, 0xB9, 0x73, 0xB5, 0xE0, 0x1B, 0x6C, 0x61, 0xC3, 0xD0, 0x31, 0x77, 0x21, 0xF5, 0x76, 0x00, 0x20, 0x2D, 0xBF, 0x30, 0x43, 0x79, 0x22, 0x01, 0x01, 0x32, 0x0B, 0x71, 0xF7, 0x00, 0xF0, 0x2F, 0x71, 0x6F, 0xD0, 0x2F, 0x81, 0x00, 0xB0, 0x02, 0x43, 0x4C, 0x41, 0x4E, 0xFF, 0xFE, 0x22, 0xEC, 0x18, 0x00, 0x02, 0x02, 0x33, 0x43, 0x23, 0x67, 0x00, 0x70, 0x61, 0x1A, 0x74, 0x31, 0x3C, 0x63, 0x7E, 0x33, 0x99, 0x28, 0x23, 0x9D, 0xA1, 0x20, 0x00, 0xF0, 0x43, 0x8C, 0x53, 0x63, 0x65, 0x6E, 0x65, 0x08, 0x4F, 0x75, 0x74, 0x41, 0x23, 0xB1, 0x47, 0x5F, 0x41, 0x10, 0x5F, 0x30, 0x30, 0xD0, 0x60, 0x70, 0x61, 0x69, 0x31, 0x54, 0x5C, 0x23, 0xAC, 0x50, 0x43, 0xBE, 0x06, 0x33, 0x46, 0x00, 0x2C, 0xB5, 0x23, 0xDD, 0xCC, 0x33, 0xAB, 0x23, 0xCD, 0x54, 0x23, 0xD1, 0xC4, 0x23, 0xD5, 0x43, 0x10, 0x23, 0xD0, 0x6D, 0x61, 0x73, 0x6B, 0xF0, 0x40, 0x33, 0xF0, 0x85, 0x34, 0x0D, 0x43, 0x4C, 0x56, 0x43, 0x33, 0xFC, 0x0C, 0x34, 0x19, 0xF2, 0x20, 0x2C, 0x33, 0xEB, 0x40, 0x0B, 0x44, 0x16, 0x7F, 0x43, 0x20, 0x51, 0xC1, 0xF9, 0x23, 0xCD, 0x30, 0x86, 0x30, 0x5D, 0xA0, 0x0B, 0x34, 0x4A, 0x82, 0x42, 0x00, 0x10, 0x0B, 0x5C, 0x7F, 0x20, 0x13, 0x8A, 0x20, 0x17, 0x30, 0x3B, 0x90, 0x0B, 0xAB, 0xAA, 0x5C, 0xE2, 0x20, 0x47, 0x9C, 0x40, 0x2F, 0xB0, 0x0B, 0x34, 0x90, 0x62, 0x75, 0x06, 0x62, 0x62, 0x6C, 0x65, 0x73, 0xC0, 0xE3, 0xA0, 0x9F, 0x49, 0xF9, 0x80, 0x9F, 0x24, 0xA6, 0x31, 0x37, 0x90, 0x9F, 0x34, 0xCE, 0x8C, 0x42, 0x34, 0xBC, 0xBC, 0x80, 0x43, 0x31, 0x02, 0x90, 0x43, 0x00, 0x41, 0x27, 0x25, 0x19, 0x51, 0x27, 0x54, 0x53, 0x8D, 0x81, 0x27, 0x01, 0x02, 0x00, 0x24, 0x76, 0x61, 0x27, 0x1C, 0x21, 0x03, 0x2D, 0x60, 0xC0, 0x51, 0xFE, 0x44, 0xE0, 0x0B, 0x72, 0x14, 0xA0, 0xE0, 0x0B, 0x03, 0x90, 0xC0, 0x29, 0xA5, 0x14, 0xBE, 0x00, 0x40, 0xF7, 0x00, 0x60, 0x6F, 0x8A, 0xF0, 0xF7, 0x75, 0x64, 0x4F, 0x22, 0x3A, 0x9E, 0x21, 0x7F, 0x80, 0x79, 0x3F, 0x30, 0x0B, 0x00, 0x40, 0xFF, 0x01, 0x50, 0x4B, 0x20, 0x3F, 0xCF, 0x3B, 0x90, 0x4B, 0xFF, 0x30, 0x0B, 0x00, 0xD2, 0xBF, 0x35, 0xF7, 0xB2, 0xBF, 0x25, 0xD2, 0x82, 0xBF, 0x20, 0xEB, 0xC2, 0xBF, 0x8B, 0x32, 0x20, 0x47, 0x5F, 0x42, 0x00, 0x42, 0xBF, 0xBC, 0x26, 0x75, 0x50, 0x2D, 0xAA, 0x92, 0xBF, 0x6C, 0x26, 0xA1, 0xA8, 0x26, 0xA5, 0xE4, 0x26, 0xA9, 0x24, 0xBF, 0x26, 0x95, 0x70, 0x26, 0x99, 0x01, 0xB2, 0xBF, 0x92, 0x2B, 0x93, 0x90, 0x01, 0xB2, 0x5F, 0xB0, 0x3F, 0xF7, 0x01, 0xF2, 0x57, 0xF0, 0x3B, 0x01, 0xB2, 0x4F, 0x92, 0xE3, 0x48, 0x23, 0x73, 0x52, 0x4F, 0x02, 0xB2, 0x1F, 0x14, 0xCD, 0xCC, 0x4C, 0x24, 0x5A, 0x20, 0x23, 0xBF, 0x00, 0x40, 0xEC, 0x30, 0x0B, 0x02, 0xA2, 0x1F, 0x20, 0x4B, 0xCC, 0x22, 0x1F, 0x30, 0x4B, 0x80, 0x3F, 0xFC, 0x50, 0x0B, 0x00, 0xD4, 0xDF, 0x38, 0x44, 0xB4, 0xDF, 0x31, 0x95, 0x74, 0xDF, 0xF1, 0x00, 0x62, 0xFF, 0xC4, 0xDF, 0x30, 0xA0, 0x47, 0x5F, 0x43, 0x00, 0x44, 0xDF, 0xB0, 0xAA, 0x28, 0x95, 0x0F, 0xE4, 0xDF, 0x78, 0x28, 0xC1, 0xB4, 0x28, 0xC5, 0xF0, 0xB1, 0x28, 0xC9, 0x30, 0x02, 0x22, 0x1F, 0xF4, 0x3F, 0xDB, 0xB6, 0x91, 0x25, 0x62, 0x4C, 0x60, 0x25, 0x66, 0x7F, 0x43, 0x30, 0x0B, 0x02, 0x52, 0x2B, 0x60, 0x41, 0xFC, 0x02, 0x92, 0x2B, 0x50, 0x3B, 0x02, 0x52, 0x2B, 0x39, 0xCE, 0x02, 0x14, 0x4B, 0x95, 0x4F, 0x80, 0xBF, 0xFE, 0x51, 0xD3, 0x01, 0xD2, 0x1F, 0x00, 0x30, 0x3F, 0x00, 0xB6, 0xDF, 0x02, 0x71, 0xFF, 0x36, 0xDF, 0x51, 0xFF, 0x08, 0xB5, 0x46, 0xDF, 0x34, 0x3A, 0x83, 0x2A, 0xC1, 0xBC, 0x2A, 0xC5, 0xF8, 0x30, 0x0F, 0xAE, 0x2A, 0xB5, 0x74, 0x2A, 0xB9, 0xB4, 0x35, 0xA3, 0x01, 0xE6, 0xE7, 0x00, 0xF2, 0x07, 0x6C, 0x1C, 0x6F, 0x67, 0x6F, 0x00, 0xA7, 0x33, 0x01, 0x52, 0x07, 0x0D, 0xE2, 0x43, 0x77, 0x61, 0x7F, 0x76, 0xE7, 0xC5, 0xD7, 0x3F, 0x38, 0xF3, 0x3C, 0x47, 0x56, 0x27, 0xF2, 0x87, 0x44, 0xB3, 0x60, 0xBC, 0xD6, 0xEB, 0x40, 0x17, 0x40, 0x0D, 0xD7, 0xA3, 0xBB, 0x87, 0x37, 0x67, 0x66, 0x66, 0xE6, 0x3F, 0xC8, 0x2F, 0x02, 0x62, 0xAB, 0x00, 0xF9, 0x9F, 0xDE, 0x3C, 0xB3, 0x03, 0x36, 0xDF, 0x98, 0x2D, 0x4C, 0x57, 0x0D, 0x92, 0xBF, 0x3A, 0x87, 0xB0, 0xB7, 0x2D, 0x85, 0xEC, 0x39, 0xEF, 0x2D, 0x75, 0x68, 0x62, 0xBF, 0x3C, 0xC3, 0x02, 0xF6, 0xE7, 0xFF, 0x02, 0x52, 0xB3, 0xE6, 0xE7, 0x0E, 0xA7, 0x23, 0x01, 0x72, 0xBF, 0x34, 0x87, 0x3F, 0x29, 0xEA, 0x8B, 0x37, 0x67, 0xFD, 0x24, 0x9A, 0x47, 0x73, 0x82, 0xCB, 0x9A, 0x27, 0x50, 0x23, 0x52, 0xCB, 0x20, 0x82, 0xCB, 0xE0, 0x6A, 0x1B, 0x32, 0xE3, 0x29, 0x83, 0xF0, 0x42, 0xCD, 0xCC, 0x0C, 0x7F, 0x40, 0x5C, 0x4A, 0x57, 0xBB, 0x32, 0xFB, 0x04, 0xF7, 0xBB, 0xFC, 0x9F, 0x3F, 0xA7, 0x03, 0x3C, 0x9F, 0x75, 0x28, 0x2F, 0xEF, 0x5C, 0x9F, 0x95, 0xBF, 0xD4, 0x2F, 0xFB, 0x18, 0x2F, 0xF7, 0x77, 0x5C, 0x39, 0xFB, 0x6C, 0x9F, 0x3C, 0xC7, 0xDC, 0x09, 0x2C, 0xA7, 0x01, 0xB6, 0x13, 0x01, 0x0C, 0xA7, 0xFF, 0x10, 0x02, 0xAC, 0xEB, 0x02, 0xD3, 0xA7, 0x4D, 0x2F, 0x28, 0x42, 0x3D, 0x3B, 0x4D, 0x47, 0x46, 0x73, 0x22, 0xF2, 0x84, 0xCE, 0x57, 0xE1, 0x65, 0x2D, 0x3E, 0x33, 0xA7, 0xA4, 0x70, 0x7C, 0x0D, 0x63, 0x8F, 0x40, 0x2F, 0x02, 0xF3, 0x8F, 0x01, 0x4D, 0x6B, 0x04, 0x30, 0x13, 0xFF, 0xFF, 0x30, 0xFF, 0xFF, 0x3F, 0x3B, 0x10, 0x13, 0x70, 0x07, 0x77, 0x77, 0x77, 0x77, 0x60, 0x6E, 0xA0, 0x07, 0x00, 0xF0, 0x1F, 0xBB, 0xBB, 0x55, 0x55, 0x03, 0x12, 0x23, 0x23, 0x23, 0x70, 0x07, 0xDD, 0xDD, 0x20, 0x0F, 0x5B, 0x1C, 0x5B, 0x5B, 0xFD, 0x60, 0x07, 0x00, 0x70, 0x1F, 0x70, 0x27, 0xAA, 0xAA, 0x8C, 0x20, 0x3F, 0x8B, 0x8B, 0x8B, 0x70, 0x07, 0x40, 0x4F, 0xC3, 0xC3, 0x60, 0xC3, 0x70, 0x07, 0x00, 0xF0, 0x1F, 0x11, 0x11, 0x11, 0x11, 0x02, 0x00, 0xF8, 0xF8, 0xF8, 0x11, 0x00, 0x11, 0x11, 0x0A, 0xFE, 0x20, 0x07, 0x4A, 0x81, 0xA0, 0x07, 0x70, 0x17, 0x70, 0x27, 0xF0, 0x1F, 0x0A, 0xF0, 0x0F, 0x43, 0x12, 0x4C, 0x49, 0x4D, 0x7F, 0x9F, 0x28, 0x04, 0x5F, 0x1F, 0x69, 0x15, 0x6D, 0x61, 0x67, 0x3B, 0x8F, 0x10, 0x2D, 0x55, 0x0A, 0x3F, 0xFB, 0xE0, 0x20, 0x17, 0x10, 0x19, 0x74, 0x7F, 0x10, 0x26, 0x72, 0x4F, 0x7F, 0xBB, 0xFF, 0x37, 0x0B, 0x10, 0x02, 0x02, 0x02, 0x7A, 0x4F, 0x01, 0xFE, 0x03, 0x00, 0x00, 0x62, 0x21, 0x21, 0x21, 0x33, 0x33, 0x33, 0x33, 0x00, 0xB6, 0x18, 0x18, 0x18, 0x33, 0x33, 0xFF, 0xFF, 0x10, 0x01, 0x03, 0x03, 0x2E, 0x41, 0x11, 0x01, 0x02, 0x30, 0xFD, 0x5F, 0xD8, 0x30, 0x07, 0x70, 0x17, 0x90, 0x1F, 0x50, 0x17, 0x00, 0x00, 0x1F, 0x73, 0x00, 0x00, 0x1F, 0x40, 0x10, 0x40, 0x3F, 0xBB, 0x33, 0x73, 0xAA, 0x1F, 0x1F, 0x0D, 0x1F, 0xFF, 0xFF, 0xF7, 0x4A, 0xC7, 0x20, 0x5F, 0x11, 0x30, 0x5F, 0x06, 0x03, 0xFF, 0x10, 0xFF, 0x57, 0x27, 0xDC, 0x10, 0x25, 0x76, 0x5F, 0x0E, 0x37, 0x00, 0x1F, 0x43, 0xD7, 0x7E, 0x4F, 0x0F, 0x20, 0x01, 0xB3, 0xE7, 0x04, 0xF0, 0x07, 0x5E, 0xF0, 0x2E, 0xC8, 0x00, 0x00, 0x20, 0x0F, 0x10, 0x25, 0x7A, 0x5F, 0x73, 0xEF, 0x10, 0x2F, 0x73, 0xFF, 0x01, 0x10, 0x00, 0xEF, 0x0E, 0x10, 0x2D, 0xB7, 0xFF, 0xBC, 0x08, 0xCE, 0x88, 0x08, 0x4A, 0x13, 0x13, 0x13, 0xFF, 0x6F, 0x00, 0x00, 0x88, 0x70, 0x88, 0x5F, 0xEF, 0x50, 0x07, 0x04, 0x00, 0x1F, 0x80, 0x88, 0x88, 0x0A, 0x00, 0x37, 0x37, 0x37, 0xCB, 0xFF, 0xEC, 0xFF, 0x42, 0x18, 0x16, 0x16, 0x16, 0x10, 0x25, 0x7F, 0xEF, 0x10, 0x07, 0x0F, 0x57, 0x7F, 0xFF, 0x7F, 0x40, 0x06, 0x00, 0x2F, 0xFF, 0xB7, 0xBB, 0x37, 0x33, 0x6A, 0x0A, 0x00, 0x0A, 0x0A, 0xBB, 0xBB, 0x33, 0x33, 0x05, 0x02, 0x38, 0x02, 0x02, 0xFF, 0xFF, 0x70, 0x17, 0x02, 0x80, 0x1F, 0x7B, 0x33, 0x77, 0x01, 0x4E, 0x1F, 0x1F, 0x1F, 0xF7, 0xFF, 0xF7, 0x10, 0x25, 0xD5, 0x67, 0x01, 0xE8, 0xFF, 0x01, 0x6B, 0x11, 0x11, 0x11, 0x8F, 0xFF, 0x44, 0x0F, 0x2C, 0xEF, 0x03, 0x03, 0x03, 0x05, 0x7D, 0x5F, 0x1E, 0xFF, 0x18, 0x10, 0xFF, 0x6B, 0x24, 0x57, 0x7D, 0xCF, 0x0F, 0xFF, 0x00, 0x07, 0xFF, 0x20, 0x20, 0x20, 0x20, 0x10, 0x25, 0x89, 0x6F, 0x63, 0xEF, 0x06, 0x73, 0xFF, 0xC1, 0x73, 0xEF, 0x07, 0x73, 0xFF, 0x7F, 0xB3, 0x7F, 0x37, 0x0A, 0x00, 0x28, 0xDF, 0x04, 0x31, 0x33, 0x9D, 0xD9, 0x21, 0x2E, 0x87, 0x11, 0x11, 0x1C, 0xDD, 0xDD, 0xA1, 0x00, 0x2E, 0x8F, 0x70, 0x17, 0x01, 0x20, 0x1F, 0x9D, 0x81, 0x80, 0x00, 0x2E, 0xCF, 0xDD, 0x7B, 0x31, 0x73, 0x4E, 0x1E, 0x1E, 0x41, 0x1E, 0x10, 0x25, 0xF3, 0xFF, 0xCF, 0x01, 0x0F, 0xF8, 0x40, 0xA3, 0xCF, 0x00, 0x0F, 0x00, 0x0F, 0x0F, 0x82, 0x1B, 0x1B, 0x1B, 0xA0, 0x05, 0x28, 0xFF, 0x10, 0x39, 0x5F, 0xFE, 0xFF, 0xF0, 0xFF, 0x22, 0x15, 0x16, 0x16, 0x16, 0x79, 0x6F, 0xF0, 0x20, 0x0F, 0xA2, 0x20, 0x0F, 0x84, 0x10, 0x15, 0x8F, 0xFF, 0xF0, 0xFF, 0x08, 0x04, 0xBF, 0xCF, 0xDE, 0xFF, 0x04, 0xCE, 0x0E, 0x01, 0x01, 0x01, 0xFB, 0xFF, 0x88, 0xCC, 0x00, 0x8E, 0x88, 0x0B, 0x34, 0x34, 0x34, 0xCC, 0xCC, 0x78, 0x88, 0x40, 0x07, 0xFC, 0x1F, 0x70, 0x17, 0x01, 0xF0, 0x1F, 0x0F, 0xFF, 0x80, 0xD0, 0x4B, 0xEF, 0x20, 0x56, 0xE8, 0x30, 0x5F, 0xFD, 0xFF, 0xFC, 0xFF, 0x1F, 0x62, 0x0F, 0x0F, 0x2C, 0x85, 0x05, 0x5F, 0xFF, 0x73, 0xEF, 0x06, 0x23, 0xFF, 0xC3, 0xEF, 0x83, 0x10, 0x01, 0x73, 0xFF, 0xB7, 0xDD, 0x37, 0x13, 0x6A, 0x2E, 0x2F, 0x02, 0xA9, 0x7F, 0x84, 0x00, 0x49, 0xBF, 0x33, 0x13, 0xDD, 0xFD, 0x39, 0xDF, 0x7B, 0xFF, 0x75, 0x73, 0x05, 0xC2, 0x7F, 0x10, 0x06, 0xFE, 0xE7, 0x00, 0x23, 0xFF, 0x07, 0x10, 0x07, 0xB3, 0xFF, 0x01, 0x29, 0x91, 0xF8, 0x3E, 0x87, 0x7F, 0xFF, 0x39, 0xA1, 0x0C, 0xBE, 0x7F, 0x10, 0x05, 0xFD, 0xFF, 0x7F, 0x00, 0x0F, 0x22, 0xFF, 0x20, 0xBF, 0xCF, 0xFE, 0xFF, 0xEE, 0x00, 0x38, 0xFF, 0xCC, 0x08, 0xCC, 0xCE, 0xCC, 0x61, 0x2C, 0xA8, 0xEE, 0xEE, 0xFF, 0x38, 0xFF, 0x01, 0x00, 0x2D, 0x17, 0x70, 0x17, 0x01, 0xF0, 0x1F, 0xFF, 0xFF, 0xF8, 0x20, 0xFF, 0x4B, 0x28, 0xEF, 0xCC, 0xEE, 0xEC, 0xFE, 0x63, 0x1E, 0x24, 0x24, 0x24, 0x0F, 0xFF, 0xFF, 0x73, 0xEF, 0x10, 0x14, 0x73, 0xFF, 0x10, 0x08, 0xFF, 0x67, 0x08, 0x80, 0xE7, 0xFF, 0xCE, 0xCC, 0xEF, 0x88, 0x4B, 0x2C, 0x2C, 0x64, 0x2C, 0x03, 0x9E, 0x7F, 0x57, 0xFF, 0xCC, 0x8C, 0x5E, 0xD7, 0xCB, 0xFF, 0x1A, 0xE8, 0xFF, 0x47, 0x10, 0x00, 0xAD, 0x7F, 0x10, 0x26, 0x25, 0x17, 0x7F, 0xCF, 0xFF, 0xEF, 0x00, 0xFF, 0x0F, 0x0A, 0x02, 0x02, 0x02, 0x17, 0x33, 0x10, 0x17, 0x11, 0x71, 0x20, 0x07, 0x11, 0x11, 0x11, 0x11, 0x7E, 0xA3, 0x4E, 0xAF, 0xDD, 0x17, 0x70, 0x17, 0x90, 0x1F, 0xDE, 0xCF, 0x01, 0x70, 0x1F, 0xFF, 0x0D, 0x7D, 0x11, 0x71, 0x65, 0x20, 0x6F, 0xAF, 0x0F, 0xF0, 0x5D, 0x8F, 0x54, 0xF7, 0xCF, 0xFF, 0xFE, 0x2E, 0xC7, 0x42, 0x10, 0x24, 0x35, 0x37, 0x0F, 0xFF, 0x07, 0x0F, 0x12, 0x03, 0x03, 0x03, 0xF0, 0x0F, 0x00, 0x1D, 0x07, 0x03, 0xDD, 0x17, 0xD0, 0xF3, 0xEF, 0xF3, 0xFF, 0xF0, 0x23, 0xEF, 0x82, 0x1D, 0x1D, 0x1D, 0xE0, 0x7F, 0xFF, 0xF0, 0x0F, 0x10, 0x4C, 0x03, 0xFF, 0xF7, 0xFF, 0x77, 0x0A, 0x01, 0x00, 0x01, 0x01, 0x77, 0x77, 0x77, 0x77, 0xDA, 0x08, 0x38, 0x08, 0x08, 0xFF, 0xFF, 0x70, 0x17, 0x00, 0xF0, 0x1F, 0xFF, 0x7F, 0x77, 0x30, 0xF7, 0x07, 0x2A, 0xBF, 0x10, 0x02, 0x87, 0xFF, 0xCF, 0xFF, 0x0F, 0xCB, 0xD4, 0x21, 0x7F, 0x00, 0x17, 0xFF, 0x01, 0x06, 0xC7, 0xFF, 0xF7, 0x10, 0x0B, 0xEB, 0xFF, 0x01, 0x00, 0x00, 0x13, 0x00, 0x62, 0x22, 0x22, 0x22, 0x33, 0x33, 0x03, 0x00, 0x00, 0x03, 0x29, 0x29, 0x29, 0xFE, 0x5F, 0x70, 0x17, 0xE0, 0x90, 0x1F, 0xDE, 0x5F, 0x00, 0xF0, 0x1F, 0x1E, 0xFF, 0x10, 0xFF, 0x6B, 0x10, 0x0A, 0x0A, 0x0A, 0x7F, 0xFF, 0x0F, 0xFF, 0x00, 0xFF, 0x08, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x0F, 0xFF, 0xEF, 0xFF, 0xEF, 0x40, 0x0E, 0x00, 0x2F, 0x4F, 0xCE, 0xC8, 0xCE, 0x88, 0x4F, 0x2C, 0x00, 0x2C, 0x2C, 0x88, 0x88, 0xAA, 0xAA, 0x1B, 0x34, 0x38, 0x34, 0x34, 0xFF, 0x77, 0x70, 0x17, 0x02, 0xA0, 0x1F, 0x8A, 0x17, 0x35, 0x01, 0x35, 0x35, 0xCC, 0xE8, 0x88, 0xE8, 0x4B, 0x20, 0x67, 0xBF, 0x00, 0x1F, 0xD7, 0xFE, 0x10, 0x0C, 0xCF, 0xE7, 0x04, 0xFE, 0x5F, 0xF3, 0xEF, 0x10, 0x00, 0x83, 0xFF, 0x10, 0x16, 0x6F, 0x4F, 0xFE, 0x5F, 0x00, 0x0A, 0x00, 0x8F, 0x00, 0x0B, 0x37, 0x37, 0x37, 0x81, 0x04, 0x93, 0xFF, 0x80, 0xFF, 0x4B, 0x1F, 0x1F, 0x1F, 0x10, 0x27, 0x73, 0xFF, 0xB3, 0x2F, 0xFF, 0xEE, 0x3F, 0xEF, 0x20, 0x06, 0xEE, 0x43, 0x00, 0x4D, 0xD7, 0x70, 0x17, 0xAB, 0x00, 0xE0, 0x1F, 0xEF, 0x00, 0x50, 0x3F, 0xFE, 0x2A, 0x3F, 0x22, 0x10, 0x27, 0x53, 0xE7, 0x10, 0x01, 0xDD, 0xC7, 0x00, 0xE9, 0xFF, 0x01, 0x6B, 0x0A, 0x0A, 0x0A, 0xF7, 0x01, 0x77, 0x77, 0x77, 0x5A, 0x08, 0x08, 0x08, 0x20, 0x06, 0x21, 0x77, 0xDA, 0x20, 0x07, 0x00, 0x00, 0x70, 0x07, 0xBD, 0x2F, 0xF0, 0x70, 0x17, 0x90, 0x1F, 0xDD, 0x37, 0x70, 0x37, 0xFF, 0xF7, 0x77, 0xF7, 0x08, 0x4E, 0x0F, 0x0F, 0x0F, 0x8D, 0x67, 0x10, 0x00, 0xD0, 0x08, 0x43, 0x29, 0x29, 0x29, 0xFF, 0xEF, 0xFB, 0xFF, 0xF1, 0x30, 0xFF, 0x0F, 0x45, 0x8F, 0x10, 0x25, 0xED, 0x1F, 0x0F, 0xFF, 0xFF, 0x00, 0x1D, 0x03, 0x03, 0x03, 0xF0, 0x0F, 0x03, 0x7D, 0x07, 0x2F, 0xFA, 0xF0, 0x00, 0x30, 0x0F, 0x00, 0xF0, 0xFF, 0xF0, 0xFF, 0x82, 0x1D, 0x1D, 0x1D, 0xE8, 0x7F, 0xFF, 0xF0, 0x0F, 0x10, 0x2C, 0xA3, 0xFF, 0x70, 0x10, 0x07, 0xC3, 0xFF, 0xBF, 0xFF, 0x3F, 0xC0, 0x5F, 0x07, 0x8F, 0xFF, 0x0F, 0x0A, 0x02, 0x02, 0x02, 0x01, 0x00, 0xEF, 0x13, 0x11, 0x82, 0x21, 0x21, 0x21, 0x11, 0x1C, 0x11, 0x11, 0x11, 0x00, 0x36, 0x07, 0x70, 0x17, 0x01, 0x00, 0x1F, 0x10, 0x11, 0x04, 0x11, 0x0A, 0x37, 0x37, 0x37, 0xFA, 0x1F, 0x3D, 0xFF, 0x1F, 0x71, 0xFF, 0x8B, 0x20, 0x67, 0x95, 0xFF, 0x26, 0x87, 0x2A, 0x5F, 0x10, 0x06, 0x0F, 0x2F, 0x08, 0x00, 0xFF, 0x08, 0x0C, 0xA1, 0x87, 0xFF, 0xDF, 0xFF, 0x30, 0xCF, 0x0E, 0x3B, 0xE4, 0xEB, 0xCF, 0xCC, 0xCC, 0xCE, 0xCC, 0x00, 0x61, 0x20, 0x20, 0x20, 0xEE, 0xEE, 0xFF, 0xFF, 0x74, 0x01, 0x00, 0x28, 0x17, 0x70, 0x17, 0x00, 0x00, 0x1F, 0x80, 0xD0, 0x1F, 0xCC, 0xED, 0x01, 0xCC, 0xEC, 0xAA, 0x1F, 0x1F, 0x1F, 0xF3, 0x27, 0xFF, 0x63, 0x57, 0x3C, 0x1F, 0x6F, 0xFF, 0xFE, 0xFF, 0xFE, 0x10, 0x06, 0x7F, 0xFF, 0x43, 0xEF, 0xFC, 0xF3, 0xFF, 0x03, 0xA9, 0xFF, 0x00, 0x4A, 0x47, 0xF3, 0xEF, 0x10, 0x06, 0x83, 0xFF, 0x0E, 0xEF, 0x6F, 0xB7, 0xBB, 0x00, 0x37, 0x33, 0x6A, 0x0A, 0x0A, 0x0A, 0xFF, 0xEF, 0xA7, 0x82, 0xF7, 0x10, 0x3C, 0xF7, 0x77, 0x77, 0x24, 0xE7, 0x2D, 0x17, 0x70, 0x07, 0x38, 0x00, 0x00, 0x77, 0x07, 0x57, 0x0F, 0x70, 0x1F, 0xBB, 0xBB, 0x33, 0x30, 0x73, 0x05, 0x43, 0x2F, 0xD0, 0x1F, 0xBB, 0x7B, 0x33, 0xF7, 0x6A, 0x0B, 0xAD, 0x57, 0x20, 0x3F, 0x01, 0x3D, 0x4F, 0xFE, 0x2C, 0xF7, 0x4B, 0xDA, 0xA4, 0xF7, 0x0F, 0x73, 0xFF, 0x0F, 0x26, 0x88, 0x26, 0x5E, 0x20, 0x10, 0x15, 0xAC, 0xF7, 0x0F, 0x70, 0x16, 0xAC, 0xE7, 0xF0, 0x0F, 0x03, 0x96, 0xE7, 0xF0, 0xFF, 0xA2, 0x16, 0x31, 0x16, 0x16, 0xF0, 0x0F, 0x07, 0x8F, 0xFF, 0xEF, 0xFF, 0xEF, 0x30, 0x9C, 0x80, 0xFF, 0xFF, 0x8A, 0xCC, 0x8E, 0x88, 0x0B, 0x34, 0x34, 0x0F, 0x34, 0xCC, 0xCC, 0x88, 0x40, 0x07, 0xFF, 0xFF, 0x70, 0x17, 0x01, 0x00, 0x1F, 0x41, 0x8C, 0x00, 0x50, 0x3F, 0xC9, 0xFF, 0xEC, 0xFF, 0x62, 0x07, 0xB1, 0x07, 0xC0, 0x10, 0x1E, 0x73, 0xFF, 0x10, 0x1E, 0x70, 0x87, 0xBF, 0xFF, 0x8F, 0x4F, 0x0F, 0x0F, 0x00, 0x0F, 0x08, 0x08, 0x8C, 0x88, 0x62, 0x29, 0x29, 0x43, 0x29, 0xFF, 0xFF, 0x00, 0x00, 0x88, 0x88, 0x5E, 0xFF, 0x50, 0x07, 0x80, 0x02, 0x00, 0x1F, 0x80, 0x00, 0xA8, 0x0F, 0x36, 0x36, 0x36, 0x07, 0xFD, 0xFF, 0xF8, 0xFF, 0x43, 0x20, 0x67, 0x10, 0x27, 0x03, 0xEF, 0x0B, 0xEE, 0x57, 0x01, 0x43, 0x4C, 0x49, 0x4D, 0xFF, 0xFE, 0x14, 0x3F, 0xA5, 0x02, 0x02, 0x28, 0x80, 0x00, 0x00, 0x01, 0x2F, 0xFB, 0x69, 0x08, 0x6D, 0x61, 0x67, 0x10, 0x3F, 0xB8, 0x01, 0x00, 0x01, 0x70, 0x0A, 0x54, 0x83, 0x44, 0xC9, 0x04, 0x30, 0x05, 0x1F, 0xA2, 0xFF, 0x11, 0x00, 0x7D, 0x17, 0x17, 0x17, 0xCC, 0xDC, 0x55, 0x55, 0x00, 0xC9, 0x3D, 0x3D, 0x3D, 0xFF, 0xF0, 0x0F, 0x0F, 0x60, 0x70, 0x20, 0x07, 0x3F, 0xB9, 0x02, 0xF8, 0xF8, 0xF8, 0x1B, 0x01, 0xFF, 0x71, 0xFF, 0x59, 0x14, 0x14, 0x14, 0x85, 0x4F, 0x01, 0xF0, 0xF0, 0xF0, 0xE4, 0x91, 0x91, 0x91, 0x10, 0x07, 0xFF, 0xDF, 0x00, 0xBF, 0x93, 0x3F, 0x37, 0x18, 0x04, 0x04, 0x04, 0xC0, 0x7F, 0xFF, 0x2F, 0x16, 0x0F, 0x70, 0x08, 0x08, 0x08, 0x99, 0x00, 0x99, 0x55, 0x55, 0xED, 0x2C, 0x2C, 0x2C, 0x2A, 0x10, 0xF1, 0x11, 0xFF, 0x31, 0xE7, 0xF0, 0xFF, 0x00, 0x00, 0x40, 0x22, 0x21, 0xC3, 0x0F, 0xFF, 0xF0, 0xF0, 0x8C, 0xD3, 0x3C, 0xD3, 0xD3, 0x10, 0x0F, 0x0D, 0xB7, 0xE3, 0xEF, 0xF3, 0xFF, 0x83, 0xEF, 0x77, 0x7F, 0x20, 0x77, 0x12, 0x2F, 0xF7, 0x03, 0x55, 0x50, 0x77, 0xCD, 0x00, 0x7E, 0x7E, 0x7E, 0x9D, 0xEE, 0x55, 0x11, 0xA5, 0x82, 0x20, 0x07, 0xFF, 0x7F, 0x77, 0x77, 0x26, 0x22, 0x67, 0xFF, 0x20, 0xFF, 0xF7, 0x4F, 0xFF, 0xCC, 0x8C, 0x11, 0x11, 0xED, 0x00, 0x9E, 0x9E, 0x9E, 0xAE, 0x51, 0x10, 0xF5, 0xB9, 0x10, 0x3B, 0x3B, 0x3B, 0xFF, 0xFF, 0xFB, 0xFF, 0xF3, 0xFF, 0x41, 0x62, 0x29, 0xAF, 0x7F, 0x93, 0x7F, 0x37, 0xB8, 0x2D, 0xB7, 0x90, 0x20, 0x36, 0x77, 0x06, 0x2F, 0xFF, 0x77, 0xFF, 0x77, 0x77, 0x40, 0x66, 0x22, 0xAF, 0x95, 0xEA, 0x57, 0x01, 0xB1, 0x5D, 0x00, 0x5D, 0x5D, 0x00, 0x00, 0xDD, 0xDD, 0xE1, 0x9F, 0x35, 0x9F, 0x9F, 0x40, 0x17, 0x29, 0xDF, 0xF7, 0x20, 0x5F, 0x22, 0x3D, 0xE4, 0x5C, 0x40, 0x50, 0x17, 0xAA, 0xF0, 0x5F, 0x20, 0x40, 0x35, 0x2B, 0x7B, 0xFF, 0x10, 0x73, 0xFF, 0x82, 0x2A, 0x0F, 0xC3, 0xD1, 0x1F, 0x13, 0x00, 0x98, 0x2A, 0x2A, 0x2A, 0x77, 0x77, 0x77, 0x77, 0x60, 0x6E, 0x2F, 0xFF, 0x70, 0x3F, 0x33, 0x73, 0xDD, 0xDD, 0x81, 0x00, 0x8F, 0x8F, 0x8F, 0x4A, 0xD5, 0x50, 0x57, 0xAD, 0x08, 0x3C, 0x3C, 0x3C, 0x7F, 0x20, 0xD7, 0x2A, 0x01, 0x01, 0x41, 0x01, 0x70, 0x67, 0x9D, 0xEA, 0x57, 0x11, 0xA9, 0x20, 0xDF, 0xE0, 0x70, 0x67, 0x70, 0x77, 0x7F, 0xFF, 0x8E, 0x54, 0x10, 0xF7, 0xD5, 0x80, 0x2A, 0x67, 0x55, 0xA8, 0x7F, 0x15, 0xB5, 0x2B, 0x2B, 0x2E, 0x2B, 0xF7, 0x20, 0x60, 0x2E, 0x30, 0x3F, 0x21, 0x07, 0x30, 0xA7, 0x35, 0x08, 0x22, 0x55, 0x11, 0x85, 0x20, 0x5F, 0xAA, 0x1B, 0x11, 0x04, 0x51, 0x8D, 0x8E, 0x8E, 0x8E, 0xFF, 0xFF, 0x39, 0x7B, 0x00, 0x73, 0xF3, 0xCC, 0x61, 0x61, 0x61, 0xFF, 0x7F, 0x38, 0xFF, 0x7F, 0x31, 0x4F, 0x7F, 0xFF, 0x70, 0x47, 0x93, 0x9B, 0x37, 0x06, 0x33, 0xD8, 0x38, 0x38, 0x38, 0x70, 0x47, 0x21, 0x16, 0x77, 0x41, 0x2E, 0x23, 0xC7, 0x7F, 0xF7, 0x77, 0xFF, 0x03, 0x20, 0xA7, 0x10, 0x00, 0x00, 0x5D, 0x50, 0xFF, 0x59, 0x11, 0x75, 0xAD, 0xC0, 0x31, 0x7F, 0xE3, 0xEF, 0x39, 0xFF, 0x73, 0xF7, 0xF0, 0x51, 0x30, 0x51, 0x51, 0x73, 0xFF, 0xB3, 0xEF, 0x6C, 0xD4, 0xD4, 0xD4, 0x80, 0x73, 0xFF, 0x23, 0x10, 0xC4, 0xFD, 0xA1, 0xCF, 0xCF, 0x57, 0xCF, 0x31, 0x1F, 0x96, 0x61, 0x7F, 0xA6, 0x4B, 0x2F, 0x51, 0x77, 0x00, 0x70, 0xFF, 0xF7, 0x71, 0x77, 0x71, 0xC7, 0x71, 0x77, 0x71, 0x67, 0xC1, 0x61, 0x77, 0x71, 0x67, 0x81, 0x3F, 0x80, 0x61, 0xE7, 0x0E, 0x15, 0x50, 0x77, 0xB5, 0x28, 0x28, 0x5A, 0x28, 0x21, 0xE7, 0x11, 0x31, 0xE7, 0x20, 0xD7, 0xF7, 0xB1, 0x5F, 0x1D, 0x00, 0x3C, 0x31, 0xF1, 0xB0, 0xA2, 0xA2, 0xA2, 0xB7, 0x83, 0x22, 0x1F, 0x78, 0x15, 0x15, 0x15, 0x7F, 0x22, 0x1F, 0x30, 0xF7, 0x8D, 0x82, 0x1F, 0xAA, 0x57, 0x11, 0x30, 0xF7, 0x22, 0x1F, 0x5D, 0x42, 0x1F, 0xC2, 0x61, 0x77, 0x7F, 0x87, 0x22, 0x53, 0x11, 0x55, 0x31, 0x7F, 0xBD, 0x01, 0x39, 0x33, 0x73, 0xD8, 0x83, 0x83, 0x83, 0x31, 0x67, 0xC0, 0x42, 0x5F, 0x62, 0x07, 0x9F, 0xE9, 0x97, 0x1F, 0x30, 0x05, 0x01, 0x05, 0x05, 0xFB, 0xFF, 0x1D, 0x11, 0x85, 0x21, 0x9F, 0x60, 0x7F, 0x22, 0x38, 0xBF, 0xBF, 0x9D, 0x3C, 0x31, 0x71, 0xD4, 0x00, 0xA3, 0xA3, 0xA3, 0xF7, 0x6F, 0xF7, 0x1F, 0x22, 0xDA, 0x32, 0x17, 0x22, 0xF7, 0x07, 0x32, 0x1F, 0x20, 0xA7, 0x66, 0x2F, 0xFC, 0xE1, 0x00, 0xDD, 0x1F, 0x11, 0xB4, 0x6B, 0x6B, 0x6B, 0xEE, 0x01, 0x50, 0x11, 0xF1, 0xB5, 0x6C, 0x6C, 0x6C, 0xFF, 0xFF, 0x40, 0x79, 0x32, 0xDF, 0x16, 0x16, 0x16, 0xFF, 0x83, 0xFF, 0x31, 0x1F, 0x14, 0x22, 0xDF, 0xF0, 0x3F, 0xA5, 0xAA, 0x1D, 0x40, 0xBF, 0x00, 0x1D, 0x1E, 0x11, 0xF1, 0xD0, 0xB4, 0xB4, 0xB4, 0x83, 0xFF, 0xFF, 0x99, 0x99, 0x99, 0x99, 0x23, 0xA0, 0x07, 0x05, 0x00, 0x1F, 0xA0, 0x97, 0xEF, 0xBF, 0x00, 0x33, 0xFF, 0x7F, 0xB7, 0x00, 0x88, 0x13, 0x10, 0xF5, 0xF5, 0xF5, 0x21, 0xF9, 0xBB, 0x1D, 0xF9, 0xF9, 0x02, 0xF9, 0xCF, 0xED, 0xE0, 0xEC, 0x94, 0x27, 0xFF, 0xEF, 0x10, 0xFE, 0xEE, 0xEE, 0x33, 0xF7, 0x3B, 0xF7, 0x88, 0x00, 0x00, 0x86, 0xE2, 0xE2, 0xE2, 0x00, 0x1F, 0x1F, 0xF0, 0x00, 0x0E, 0xFD, 0xFD, 0xFD, 0x3E, 0xF0, 0x0E, 0x0E, 0x41, 0x8C, 0x23, 0xBF, 0xFF, 0x1F, 0x00, 0x10, 0x07, 0x28, 0x2B, 0x00, 0xFE, 0xBF, 0xF0, 0x3F, 0x68, 0x40, 0x40, 0x40, 0x00, 0xC3, 0x01, 0x17, 0x13, 0xB4, 0x6E, 0x6E, 0x6E, 0x04, 0xFE, 0xE3, 0xF0, 0x0F, 0x8C, 0x21, 0x97, 0xE1, 0x00, 0x00, 0x0F, 0x8F, 0x80, 0xDF, 0xDF, 0xDF, 0x00, 0xB7, 0x10, 0x01, 0x80, 0x26, 0x2F, 0xFC, 0x57, 0xBB, 0x80, 0xAA, 0x00, 0x35, 0xE7, 0xE7, 0xE7, 0x00, 0xE3, 0xF0, 0xE0, 0x00, 0x10, 0xFA, 0xFA, 0xFA, 0xF1, 0xFF, 0xF0, 0xFF, 0x40, 0x93, 0x21, 0x47, 0xB9, 0xF7, 0x8C, 0x00, 0x35, 0xEA, 0x01, 0xEA, 0xEA, 0x00, 0xDE, 0xFF, 0x10, 0x06, 0x2F, 0xFC, 0x00, 0xDE, 0x3C, 0xCE, 0x0E, 0x54, 0x06, 0x06, 0x06, 0x00, 0x32, 0x33, 0x0E, 0x00, 0x13, 0xF7, 0xF7, 0xF7, 0x05, 0x1C, 0x79, 0x71, 0xF3, 0xD0, 0x22, 0x2F, 0xFF, 0x26, 0xEF, 0x00, 0x68, 0x4E, 0x4E, 0x4E, 0x1F, 0x3C, 0x70, 0xF0, 0x0C, 0x54, 0xE6, 0xE6, 0xE6, 0x70, 0x0F, 0x78, 0xC7, 0x80, 0xB3, 0x19, 0x88, 0x88, 0x32, 0x20, 0x9F, 0x20, 0x97, 0x00, 0x06, 0x28, 0xC3, 0x04, 0xE1, 0xC1, 0xE0, 0xEE, 0xB4, 0x22, 0x67, 0xCC, 0x4C, 0x10, 0xBB, 0xBB, 0x0D, 0x20, 0x6F, 0x7B, 0x00, 0x08, 0xFF, 0x00, 0x22, 0xF1, 0xF1, 0xF1, 0xEE, 0x1E, 0xFE, 0x0E, 0x00, 0x8F, 0x1E, 0x1E, 0x1E, 0x3E, 0x00, 0x0F, 0xEF, 0x00, 0x80, 0xAF, 0xAF, 0xAF, 0x00, 0x7E, 0x10, 0x11, 0x80, 0x30, 0x9F, 0x45, 0x80, 0x91, 0x88, 0x35, 0xFC, 0xFC, 0x02, 0xFC, 0xCC, 0xE0, 0x00, 0xF0, 0x6F, 0x20, 0x77, 0xC1, 0x08, 0xEF, 0xE0, 0xEE, 0xD0, 0x20, 0xD7, 0x88, 0x44, 0xBB, 0x00, 0x19, 0x15, 0xFB, 0xFB, 0xFB, 0x00, 0x00, 0x08, 0x21, 0xDD, 0x22, 0x35, 0x3F, 0x10, 0xEE, 0x0E, 0x58, 0x27, 0x87, 0x04, 0xCE, 0xCC, 0x0F, 0x00, 0x4B, 0x20, 0x9F, 0x1E, 0x7C, 0x00, 0x70, 0xF0, 0x70, 0xE5, 0xE5, 0xE5, 0xB7, 0xC3, 0x10, 0x37, 0x1F, 0x34, 0x20, 0xB7, 0x10, 0xFF, 0xFF, 0xF0, 0x41, 0x10, 0x29, 0x57, 0xC1, 0xE1, 0x1F, 0x07, 0x70, 0x24, 0x3F, 0x00, 0xE9, 0xFE, 0x03, 0x00, 0x86, 0xDB, 0xDB, 0xDB, 0x06, 0x4C, 0xA0, 0x00, 0xEA, 0x79, 0x20, 0xF7, 0x79, 0x77, 0xBD, 0x00, 0xB8, 0x70, 0x33, 0x78, 0xC5, 0xC5, 0xC5, 0xBB, 0x08, 0x5B, 0xAA, 0xAA, 0x51, 0x20, 0x3F, 0x39, 0xFF, 0x0C, 0x00, 0x00, 0x33, 0xF6, 0xF6, 0xF6, 0x11, 0x31, 0x55, 0x00, 0x45, 0x3D, 0x19, 0x19, 0x19, 0xCB, 0x00, 0x07, 0x00, 0xFF, 0x80, 0xBF, 0xBF, 0xBF, 0xCE, 0x3C, 0x10, 0x00, 0x71, 0x74, 0xE9, 0xE9, 0xE9, 0x08, 0xFF, 0x73, 0x04, 0x0F, 0x50, 0x18, 0x18, 0x18, 0x21, 0x77, 0xF0, 0x0A, 0xC2, 0x31, 0xAF, 0x29, 0xC8, 0xD0, 0x48, 0x48, 0x48, 0x77, 0xEF, 0xBF, 0x01, 0x93, 0x80, 0xC8, 0x3A, 0xF4, 0xF4, 0xF4, 0xF4, 0x0F, 0x06, 0xF5, 0x5F, 0xA8, 0x8A, 0x31, 0x20, 0x9F, 0x2F, 0x3F, 0x0C, 0x41, 0xC4, 0x21, 0x8F, 0xFE, 0xEF, 0xFE, 0xEF, 0x26, 0x2F, 0xFF, 0x04, 0x1E, 0x3C, 0x8F, 0x0E, 0x90, 0x25, 0x1F, 0x00, 0x10, 0x10, 0x00, 0x70, 0x67, 0x21, 0x17, 0x3C, 0xFD, 0xF1, 0xF1, 0x08, 0xA4, 0x70, 0x70, 0x70, 0x7A, 0x17, 0x3C, 0xFC, 0x70, 0x04, 0xF1, 0x88, 0xB2, 0xB2, 0xB2, 0x91, 0x67, 0x0F, 0xB0, 0xE0, 0x46, 0x3F, 0x61, 0x87, 0x71, 0x67, 0xDC, 0x01, 0x11, 0x11, 0x2A, 0x82, 0x2A, 0x33, 0x80, 0x00, 0x89, 0x88, 0x3A, 0x2F, 0xFC, 0x1F, 0x00, 0xE8, 0x80, 0xEE, 0xB9, 0xC6, 0xC6, 0xC6, 0xD9, 0x00, 0xEE, 0xEA, 0xEE, 0xAD, 0x60, 0x60, 0x60, 0x87, 0x08, 0xBE, 0x88, 0x30, 0x6C, 0x20, 0xE7, 0x87, 0xFE, 0x1F, 0x00, 0x00, 0xD1, 0xAD, 0xAD, 0xAD, 0xFF, 0xC6, 0xEE, 0x27, 0x0F, 0x90, 0x23, 0xF7, 0x37, 0x33, 0x21, 0x3F, 0x20, 0xD7, 0x2F, 0x7F, 0x00, 0xE8, 0x37, 0xFE, 0xFE, 0xFE, 0x15, 0x19, 0xA0, 0x20, 0xAA, 0x79, 0x24, 0xBF, 0x00, 0x38, 0xFF, 0x70, 0x14, 0x84, 0x21, 0x6F, 0x12, 0x11, 0x54, 0x55, 0x31, 0x07, 0x1B, 0xF7, 0x10, 0x8E, 0x00, 0x39, 0x22, 0x1F, 0xEF, 0x9E, 0x00, 0x30, 0x41, 0x32, 0x22, 0xA7, 0x8B, 0x83, 0x37, 0x07, 0xCC, 0x44, 0x1F, 0x20, 0xEE, 0xDF, 0x40, 0xA7, 0x16, 0x71, 0xCF, 0xB0, 0x52, 0x01, 0x52, 0x52, 0x3C, 0x00, 0x0E, 0x08, 0xA4, 0x21, 0x2F, 0x00, 0x3C, 0xFC, 0xF0, 0xF1, 0x8C, 0xC2, 0xC2, 0xC2, 0x00, 0x3C, 0x78, 0x8F, 0x0E, 0xB0, 0x4D, 0x4D, 0x4D, 0x04, 0xEF, 0x3E, 0x00, 0x70, 0x70, 0x21, 0x3F, 0x7A, 0xEF, 0x00, 0xF0, 0xFF, 0x2F, 0x09, 0x09, 0x09, 0x33, 0x52, 0x10, 0x00, 0xF0, 0x67, 0x22, 0x6F, 0x3C, 0xBB, 0xF0, 0x33, 0x00, 0xA4, 0x50, 0x50, 0x50, 0x3E, 0xF0, 0x0F, 0x0E, 0x41, 0x90, 0x20, 0x27, 0xFF, 0x3F, 0x00, 0x70, 0x0A, 0x21, 0x7F, 0x04, 0xC3, 0xF3, 0x1F, 0x07, 0xA8, 0x22, 0xC7, 0xFF, 0xEF, 0x82, 0x52, 0x47, 0x7C, 0xFF, 0xF0, 0xF7, 0x80, 0x20, 0x27, 0x9F, 0x08, 0xE3, 0x3F, 0x0F, 0x30, 0x21, 0x7F, 0x3C, 0x78, 0x70, 0x20, 0xF3, 0xB0, 0x21, 0x17, 0xC3, 0xE1, 0x0F, 0x0F, 0x90, 0x84, 0x2B, 0x4F, 0x01, 0x00, 0x03, 0xFF, 0x32, 0x17, 0xC3, 0xE1, 0x11, 0xE0, 0xF8, 0x90, 0x31, 0x2F, 0xA3, 0x00, 0xE0, 0x31, 0xC7, 0x06, 0xC3, 0xFE, 0xF8, 0xFE, 0xAC, 0x20, 0x5F, 0x09, 0x27, 0xFF, 0xFF, 0xEC, 0xBB, 0xEF, 0xF0, 0x0F, 0x81, 0xEF, 0x0F, 0x53, 0x57, 0x00, 0x00, 0x0F, 0x00, 0xF0, 0x02, 0x0F, 0x98, 0x84, 0x84, 0x84, 0xFF, 0x2A, 0x91, 0x42, 0x00, 0xE3, 0xE3, 0xE3, 0xFF, 0x30, 0xF0, 0x8F, 0x8C, 0x00, 0x82, 0x82, 0x82, 0x78, 0xF7, 0x0E, 0x00, 0x8C, 0x00, 0xBE, 0xBE, 0xBE, 0x00, 0x00, 0xFE, 0xF0, 0x1C, 0xAE, 0x24, 0x57, 0xF0, 0x23, 0xEF, 0x42, 0x28, 0x17, 0x41, 0xDF, 0x23, 0x67, 0x38, 0x00, 0xBB, 0x73, 0x77, 0xD6, 0x44, 0x44, 0x44, 0xFF, 0x0C, 0x00, 0xFF, 0x0F, 0x1C, 0x2A, 0xD7, 0x2A, 0xD1, 0x00, 0xB3, 0x00, 0xE8, 0xE8, 0xE8, 0x9B, 0xCB, 0x37, 0x17, 0xD4, 0x10, 0x3A, 0x3A, 0x3A, 0x71, 0x57, 0x80, 0xF7, 0xF0, 0xF0, 0x68, 0x30, 0x38, 0xA7, 0x2A, 0xEF, 0xC8, 0x23, 0xFF, 0x87, 0x87, 0xF0, 0x38, 0xF0, 0x90, 0x4A, 0xD7, 0xE0, 0x97, 0x2C, 0xB7, 0x8C, 0x80, 0x80, 0x44, 0x80, 0x00, 0x10, 0x0F, 0xFF, 0x0F, 0x93, 0x4D, 0x5E, 0x0F, 0xFF, 0x6E, 0xE0, 0x28, 0x6F, 0x00, 0x00, 0x0F, 0xF0, 0x23, 0xEF, 0x20, 0x77, 0x23, 0xB6, 0xFF, 0x0D, 0x60, 0x30, 0x30, 0x30, 0x00, 0x00, 0x0F, 0x24, 0x78, 0x0C, 0x50, 0x9F, 0x3E, 0xFF, 0xA2, 0x30, 0xDF, 0xE0, 0x0F, 0x2F, 0xF8, 0x43, 0xEF, 0x21, 0x06, 0x0F, 0x60, 0xD4, 0x3D, 0x57, 0x63, 0xFF, 0x30, 0x7C, 0x8E, 0x0E, 0x70, 0x14, 0x1A, 0x1A, 0x1A, 0x2F, 0xF9, 0x7F, 0x3D, 0x67, 0x5F, 0x99, 0x1C, 0x00, 0x88, 0x71, 0x34, 0xE7, 0x34, 0x87, 0x2D, 0x77, 0x8F, 0x70, 0x10, 0x00, 0xF7, 0xF9, 0x26, 0x67, 0xCC, 0xCC, 0xAA, 0xAA, 0x60, 0x31, 0x2D, 0x87, 0x70, 0x07, 0xAA, 0xAA, 0x77, 0x77, 0xB2, 0x14, 0x20, 0x20, 0x20, 0x30, 0x07, 0x96, 0x20, 0x07, 0x77, 0xA1, 0x00, 0x88, 0xF8, 0x95, 0xD6, 0xD6, 0xD6, 0x09, 0xFF, 0x10, 0xEC, 0x0F, 0x90, 0x27, 0x57, 0xBB, 0x72, 0x66, 0xF6, 0x0E, 0x11, 0x02, 0x02, 0x02, 0x00, 0x37, 0xFF, 0x3B, 0xEF, 0x8B, 0xFF, 0x38, 0x02, 0x70, 0x70, 0x78, 0xD5, 0xD5, 0xD5, 0x7F, 0xFF, 0x87, 0x08, 0xF7, 0x0F, 0x0F, 0x6C, 0x20, 0x8F, 0xFF, 0xDF, 0x77, 0x24, 0x57, 0x0D, 0x2C, 0x47, 0x83, 0xC3, 0x73, 0x47, 0xB8, 0xBB, 0xB9, 0x3E, 0x07, 0x87, 0x21, 0x67, 0x3C, 0x2F, 0x2F, 0xF9, 0x80, 0x06, 0x2E, 0x17, 0x43, 0xCF, 0x21, 0x77, 0x88, 0x90, 0x90, 0x90, 0x01, 0x01, 0xFF, 0x2E, 0x2F, 0x70, 0x88, 0x31, 0x77, 0x8A, 0x6F, 0xD0, 0x0F, 0x7F, 0x7F, 0x0F, 0x0F, 0x00, 0x10, 0x07, 0x07, 0x07, 0xF8, 0x00, 0x0D, 0xFF, 0x41, 0x02, 0x25, 0x7F, 0xFF, 0x7C, 0xFF, 0x0F, 0x10, 0x2A, 0x17, 0x50, 0x78, 0x22, 0x50, 0x82, 0x25, 0x17, 0x33, 0x91, 0x00, 0xC8, 0x65, 0x37, 0x24, 0xF7, 0x28, 0x79, 0xAA, 0x3D, 0x3A, 0x8F, 0x3C, 0x53, 0xFF, 0x06, 0x2B, 0x62, 0x60, 0x66, 0x59, 0x2C, 0xEF, 0x81, 0x1F, 0xAC, 0xA1, 0x91, 0x1F, 0x92, 0x21, 0x1F, 0xFF, 0x6F, 0x66, 0x66, 0x30, 0xC7, 0x00, 0xCB, 0xE9, 0xEC, 0xFC, 0xAC, 0x71, 0x71, 0x71, 0x06, 0x9F, 0x3C, 0x8F, 0x8F, 0x54, 0x2D, 0x0F, 0x3A, 0x47, 0xA2, 0x84, 0x3F, 0xFF, 0x5C, 0xFF, 0x0F, 0x8B, 0x25, 0xE7, 0x08, 0x00, 0x10, 0x3F, 0x33, 0x0B, 0x2F, 0xFC, 0x3C, 0x9B, 0x70, 0x33, 0x09, 0xB4, 0xB6, 0xB6, 0xB6, 0x24, 0x27, 0xFF, 0xA0, 0x25, 0x57, 0x04, 0x10, 0xDD, 0xFF, 0x11, 0x02, 0x2F, 0xFC, 0xC1, 0x7F, 0x00, 0x03, 0x00, 0xAC, 0xCE, 0xCE, 0xCE, 0xC4, 0xC4, 0x10, 0xBB, 0x13, 0x03, 0x2F, 0xFC, 0xCC, 0xA0, 0x00, 0xF0, 0x41, 0x57, 0x25, 0x47, 0xEA, 0xF0, 0xEE, 0x0A, 0x95, 0x20, 0x2F, 0x04, 0xBF, 0xB8, 0x30, 0x71, 0x34, 0x22, 0x97, 0x07, 0x33, 0x14, 0x5F, 0x00, 0xD5, 0x32, 0xCF, 0x10, 0x51, 0xBF, 0x01, 0x00, 0x22, 0xFF, 0xFE, 0x47, 0x3F, 0x87, 0x80, 0xF0, 0x30, 0x1F, 0xF5, 0xA0, 0x26, 0xCF, 0x47, 0x2A, 0xF7, 0x83, 0xC1, 0xE0, 0xF8, 0xB4, 0x12, 0xC3, 0xC3, 0xC3, 0x07, 0xFB, 0xFF, 0xFF, 0x7F, 0x57, 0x17, 0xDF, 0x08, 0x9D, 0xCF, 0xCC, 0x98, 0x2F, 0xEF, 0x88, 0x88, 0xAA, 0x36, 0xAA, 0xE5, 0x2F, 0xE7, 0x31, 0xB0, 0xE4, 0x41, 0xE7, 0x5F, 0xFF, 0xC9, 0x0E, 0xFC, 0xE8, 0xFF, 0xC9, 0x31, 0x5F, 0x00, 0x63, 0xFF, 0x30, 0x3F, 0xB8, 0x84, 0xA0, 0x3F, 0xFF, 0xE0, 0xF0, 0xE0, 0x31, 0xFF, 0x3C, 0x39, 0x10, 0x8E, 0x8C, 0xB8, 0x2C, 0x0F, 0xAD, 0xFF, 0xA8, 0xFF, 0x00, 0xC3, 0x24, 0x24, 0x24, 0xFF, 0xFE, 0xFF, 0xEE, 0x41, 0x0E, 0x2B, 0x77, 0x08, 0x00, 0x88, 0xFF, 0x62, 0x27, 0x77, 0x04, 0x10, 0xFF, 0x11, 0x11, 0x4A, 0x27, 0xCF, 0xAE, 0xD9, 0x10, 0xAF, 0x8A, 0xA9, 0x26, 0x67, 0xDB, 0x55, 0xAA, 0xAA, 0x41, 0x6D, 0x25, 0xA7, 0x01, 0x00, 0x11, 0x01, 0x46, 0x27, 0x97, 0x42, 0x37, 0x26, 0xBF, 0x78, 0xEC, 0xEC, 0xEC, 0x20, 0x16, 0xAA, 0x00, 0x8D, 0xB1, 0xB1, 0xB1, 0xD9, 0xED, 0xCC, 0xEC, 0x41, 0xD4, 0x23, 0x8F, 0xCB, 0xFD, 0xE8, 0xFC, 0xA8, 0x45, 0x47, 0xBF, 0x57, 0xFF, 0xFE, 0x20, 0x58, 0x3F, 0xFF, 0x70, 0xC7, 0xF3, 0xFF, 0x70, 0xD7, 0x80, 0x87, 0x0A, 0xCF, 0xFF, 0x0F, 0x87, 0x53, 0xFF, 0xEF, 0x43, 0xFF, 0xDF, 0x20, 0xFF, 0xCF, 0x3F, 0xFF, 0xCB, 0xDD, 0xAB, 0xAA, 0xA5, 0x86, 0x36, 0xEF, 0xFF, 0xDC, 0xF0, 0x0A, 0x27, 0xF7, 0x2F, 0x2F, 0xFF, 0x40, 0x93, 0x27, 0x5F, 0x99, 0xA9, 0xAA, 0xEA, 0xA5, 0x81, 0x01, 0x81, 0x81, 0xF9, 0xFF, 0xF8, 0xFF, 0x63, 0x5A, 0x77, 0xA1, 0x73, 0xFF, 0xDB, 0x43, 0xFF, 0x9F, 0xFF, 0x8F, 0x87, 0x29, 0xFF, 0x04, 0xBA, 0xBB, 0xAE, 0xAA, 0x85, 0x2D, 0x37, 0x80, 0xFF, 0x40, 0xFD, 0x47, 0xEF, 0xF0, 0x58, 0xFF, 0x8E, 0xC9, 0x31, 0x03, 0x31, 0x31, 0xDD, 0xFA, 0xAA, 0xFA, 0x30, 0x57, 0x30, 0xA7, 0x82, 0x3C, 0xCF, 0x38, 0x7B, 0x8C, 0x88, 0xC8, 0x52, 0x37, 0x11, 0x40, 0x26, 0x2D, 0x7F, 0xDE, 0xDC, 0xEF, 0xCE, 0x58, 0x13, 0x00, 0x13, 0x13, 0xBA, 0x55, 0xAE, 0x8A, 0x8D, 0xA1, 0x01, 0xA1, 0xA1, 0x32, 0x33, 0xDD, 0x1D, 0x43, 0x44, 0x47, 0x10, 0x11, 0x11, 0xCA, 0x24, 0xFF, 0x33, 0x33, 0xAA, 0xAA, 0x42, 0x59, 0x2F, 0xB7, 0x55, 0xAB, 0xA8, 0xAA, 0x30, 0x1F, 0x1E, 0x08, 0x3E, 0x11, 0xF0, 0x70, 0x23, 0xC7, 0x8F, 0x78, 0x8F, 0x20, 0x0F, 0x74, 0x23, 0x47, 0x9D, 0xEC, 0xA8, 0xFE, 0xC5, 0xC8, 0x29, 0x6F, 0x91, 0x5F, 0xAB, 0x33, 0x3B, 0x1C, 0x00, 0x10, 0x10, 0x22, 0x11, 0x2A, 0x2F, 0xEC, 0xAE, 0xDD, 0xAF, 0x40, 0xD7, 0xAA, 0xC1, 0x21, 0x5F, 0x31, 0x47, 0xFF, 0x00, 0x11, 0x11, 0x56, 0x25, 0x57, 0xB4, 0x21, 0x67, 0xDC, 0x38, 0xBF, 0xA0, 0x5F, 0xEA, 0x30, 0x7F, 0x7F, 0xA1, 0x00, 0x00, 0xF8, 0x59, 0xEB, 0xEB, 0xEB, 0x85, 0xFF, 0x10, 0xE8, 0xFF, 0xCD, 0x20, 0xBF, 0xCD, 0xED, 0xEC, 0xFE, 0x68, 0xCC, 0x20, 0xC7, 0x00, 0x35, 0xFF, 0x23, 0x2E, 0x2B, 0xDE, 0x9D, 0xCE, 0xFD, 0xC2, 0x3F, 0xF3, 0xFF, 0x81, 0xFF, 0x7F, 0xFF, 0x21, 0x6F, 0x3D, 0xB7, 0xBC, 0x32, 0x1F, 0x10, 0x5C, 0x5C, 0x5C, 0x7F, 0xFF, 0xEF, 0xDE, 0xEF, 0xCE, 0x66, 0x16, 0x2D, 0x97, 0x29, 0x97, 0x10, 0x26, 0x32, 0x1F, 0x20, 0xA7, 0x5A, 0x82, 0x29, 0xEF, 0x9E, 0xD9, 0xAE, 0x8A, 0xB1, 0x25, 0xC7, 0x3B, 0x82, 0x61, 0x07, 0xEE, 0xEE, 0x11, 0x11, 0x83, 0x29, 0x3F, 0x00, 0x08, 0x1E, 0x11, 0xF0, 0xAC, 0x22, 0x1F, 0xD5, 0xAA, 0xAA, 0x25, 0xEA, 0x8D, 0x27, 0xDF, 0xDD, 0xEA, 0x51, 0x67, 0x1E, 0x2A, 0x47, 0x44, 0x80, 0x34, 0x0F, 0xEF, 0xFF, 0xEF, 0x3E, 0x67, 0xED, 0xFF, 0x1D, 0xEC, 0xFF, 0x62, 0xAF, 0xFF, 0x78, 0x07, 0x2F, 0xF9, 0xFD, 0x3E, 0xFF, 0x06, 0xDF, 0x9C, 0xCF, 0xCE, 0x58, 0x2E, 0x97, 0x72, 0xFF, 0x97, 0x08, 0xC3, 0xC0, 0xE8, 0x94, 0x25, 0x27, 0xFE, 0x70, 0xFF, 0x00, 0x0E, 0xB3, 0x35, 0x35, 0x35, 0x8D, 0xFE, 0xA8, 0x61, 0xFF, 0x41, 0x5F, 0x60, 0xB7, 0x78, 0x00, 0x0E, 0x1C, 0x3A, 0xC7, 0x05, 0xCC, 0xA4, 0x00, 0xF0, 0x2F, 0x28, 0xA7, 0xDA, 0x62, 0x37, 0x83, 0x72, 0x27, 0xC3, 0xFD, 0xF8, 0xFC, 0xA4, 0x28, 0xE7, 0x7F, 0xFF, 0x1E, 0xFD, 0xFF, 0xFC, 0x4E, 0x9F, 0x07, 0x8F, 0xFF, 0x07, 0x64, 0x87, 0x37, 0xDF, 0x02, 0xE7, 0x2F, 0xFF, 0xF0, 0x0F, 0x2C, 0xA0, 0xFF, 0x02, 0xAD, 0xDF, 0xF0, 0x0F, 0x0B, 0x81, 0x47, 0xBB, 0x2F, 0xA8, 0x03, 0x00, 0x20, 0x0F, 0x3F, 0x92, 0x31, 0x17, 0x0F, 0x29, 0x08, 0x00, 0x30, 0x0F, 0xFE, 0x03, 0x70, 0x5F, 0x71, 0x67, 0xF0, 0x0F, 0x07, 0x0A, 0x9F, 0x29, 0x9F, 0xC2, 0x1F, 0xE0, 0x0F, 0x00, 0xBC, 0x29, 0x9F, 0x06, 0xB2, 0x1F, 0xE0, 0x0F, 0x10, 0x09, 0x70, 0x07, 0x10, 0x66, 0x73, 0xFF, 0x43, 0x4C, 0x04, 0x49, 0x4D, 0xFF, 0xFE, 0x14, 0x3A, 0x65, 0x02, 0x28, 0x08, 0x20, 0x00, 0x00, 0x01, 0x2F, 0xE3, 0x69, 0x6D, 0x61, 0x21, 0x67, 0x10, 0x2F, 0xEB, 0xE1, 0x00, 0x28, 0x00, 0x3D, 0x9B, 0x72, 0x00, 0x20, 0x17, 0x04, 0x90, 0x01, 0x2E, 0x47, 0xF8, 0x80, 0x10, 0x5B, 0x80, 0x07, 0x80, 0x00, 0x88, 0x77, 0x77, 0x4A, 0xF8, 0x88, 0x09, 0x84, 0x85, 0x20, 0x07, 0x29, 0xFF, 0x8B, 0x04, 0xF6, 0xDF, 0x88, 0x30, 0x17, 0x00, 0xFE, 0x8B, 0x03, 0x04, 0x00, 0xDF, 0xDD, 0x81, 0x11, 0xFF, 0x9D, 0x25, 0x00, 0x16, 0xFF, 0xDD, 0xDD, 0xA1, 0x40, 0x17, 0xC0, 0x70, 0x07, 0x00, 0x50, 0x1F, 0xEE, 0xC8, 0x11, 0x71, 0x45, 0xFE, 0x60, 0x8C, 0x27, 0xB8, 0xF7, 0x3F, 0x77, 0x77, 0x31, 0xFF, 0x8C, 0x0B, 0x04, 0x88, 0x4C, 0x33, 0x20, 0x7F, 0x8A, 0x2E, 0x52, 0xE0, 0x7F, 0x03, 0x00, 0x77, 0xF7, 0x0B, 0xF8, 0x81, 0x2F, 0xFA, 0x10, 0x1C, 0xE7, 0x77, 0x28, 0x80, 0x7F, 0x23, 0x07, 0x89, 0x00, 0x02, 0xE7, 0xCC, 0xE8, 0x77, 0x00, 0x11, 0x45, 0xFF, 0x8C, 0x14, 0xFD, 0x6E, 0x11, 0x00, 0x00, 0x45, 0xFE, 0xAC, 0x34, 0x80, 0x88, 0x7F, 0x05, 0x77, 0x0B, 0xFF, 0x81, 0x02, 0x23, 0xAF, 0x17, 0x33, 0xAF, 0x00, 0x01, 0x80, 0xBB, 0xBB, 0x0B, 0xF8, 0xDE, 0x55, 0x00, 0x44, 0x20, 0x00, 0x22, 0x29, 0xFE, 0xDA, 0x40, 0x10, 0x00, 0x20, 0xDD, 0x53, 0xA7, 0x80, 0x11, 0x99, 0x87, 0x00, 0xF8, 0xC2, 0x41, 0x11, 0x00, 0x20, 0x22, 0x45, 0x00, 0xFF, 0xBA, 0x20, 0x11, 0x01, 0xEE, 0x00, 0x22, 0x00, 0xF8, 0xB7, 0x0F, 0x44, 0x80, 0x09, 0xC8, 0x2B, 0x00, 0xF1, 0xD6, 0x4C, 0x00, 0x00, 0xBB, 0xBB, 0x0D, 0x13, 0xFF, 0xDB, 0x51, 0x4D, 0xAF, 0xF8, 0xA8, 0x5D, 0xB7, 0x20, 0x07, 0x20, 0x17, 0x11, 0x20, 0x47, 0xEE, 0xCA, 0x30, 0x11, 0x11, 0x02, 0x22, 0x22, 0x25, 0xFF, 0xCA, 0x30, 0xF0, 0x1F, 0x00, 0x01, 0x00, 0x22, 0x22, 0x65, 0xFF, 0xCA, 0x40, 0x90, 0x07, 0xC3, 0x00, 0x50, 0x1F, 0x00, 0x70, 0x3F, 0x11, 0x71, 0x22, 0x00, 0x30, 0x5F, 0x00, 0x70, 0x7F, 0x00, 0x48, 0xDC, 0x88, 0x99, 0x47, 0xF8, 0xD7, 0x4E, 0x00, 0x2A, 0x12, 0x99, 0x11, 0x47, 0xF8, 0xBB, 0x41, 0x86, 0x80, 0x9F, 0x11, 0x23, 0x22, 0x23, 0x20, 0xBF, 0x74, 0x47, 0x88, 0x00, 0x88, 0x73, 0x77, 0x8A, 0xF8, 0x9E, 0x1F, 0x12, 0x08, 0x51, 0x22, 0x02, 0x21, 0x30, 0xA7, 0x40, 0x88, 0x08, 0x00, 0x69, 0xEF, 0xCB, 0x41, 0x08, 0x00, 0xF7, 0xFF, 0x40, 0x22, 0xAB, 0x9F, 0x2A, 0x12, 0x98, 0x11, 0x47, 0xF1, 0x3B, 0xBB, 0x3A, 0x44, 0x7F, 0x21, 0x47, 0xFB, 0xBF, 0x08, 0x24, 0x5F, 0x00, 0x31, 0x77, 0x80, 0x01, 0x7B, 0xE7, 0x06, 0x44, 0x51, 0x77, 0x49, 0xFF, 0x8C, 0x00, 0x15, 0x08, 0x08, 0x77, 0xF7, 0x82, 0xF8, 0x96, 0x41, 0x16, 0x10, 0x0C, 0x93, 0x9F, 0xFF, 0x77, 0x0A, 0xF8, 0x81, 0x2E, 0x6E, 0xA0, 0xDD, 0xFF, 0x44, 0x23, 0x8F, 0x2D, 0xFF, 0x8B, 0x03, 0x15, 0x00, 0x22, 0xDF, 0xCD, 0x61, 0xFF, 0xAD, 0x25, 0x00, 0xC8, 0x33, 0x9F, 0x26, 0xAF, 0x84, 0xA8, 0x53, 0x9F, 0xC5, 0x84, 0x91, 0x40, 0x88, 0x32, 0xCF, 0x00, 0x20, 0x88, 0xEE, 0x2D, 0xFF, 0x28, 0xDB, 0x41, 0x33, 0xC7, 0x65, 0x23, 0xC7, 0x44, 0x80, 0x89, 0x80, 0x43, 0x87, 0x21, 0x22, 0x22, 0x32, 0x23, 0xF8, 0xBE, 0x41, 0x1D, 0xF3, 0x7F, 0x01, 0x00, 0x22, 0x32, 0x43, 0xA0, 0x17, 0xF0, 0x10, 0x01, 0x80, 0x07, 0x64, 0x27, 0x34, 0x17, 0x00, 0x34, 0xBF, 0x08, 0x10, 0xBF, 0x3B, 0x00, 0x0F, 0xF8, 0xDE, 0x54, 0x22, 0x51, 0xDC, 0xFD, 0xA0, 0x41, 0xAF, 0x10, 0x24, 0xFF, 0xFF, 0xA8, 0x00, 0x10, 0x11, 0x03, 0x22, 0x00, 0x45, 0xEF, 0xBA, 0x20, 0x74, 0x57, 0x34, 0x47, 0x89, 0x48, 0xFF, 0x40, 0xCE, 0x88, 0x35, 0x2F, 0x2A, 0x11, 0x30, 0x2F, 0x20, 0xBD, 0x35, 0x74, 0xBF, 0x08, 0x10, 0x88, 0x19, 0x4B, 0xE8, 0x31, 0xD7, 0x75, 0x3F, 0x20, 0x47, 0x06, 0x20, 0x47, 0xEE, 0x8C, 0x10, 0x07, 0x31, 0x85, 0xEE, 0x9C, 0x24, 0x74, 0x9F, 0x80, 0x57, 0x60, 0x47, 0xC0, 0x70, 0x57, 0x84, 0x9F, 0x11, 0x98, 0x31, 0x65, 0xFF, 0xBD, 0x06, 0x34, 0x8E, 0xCC, 0x11, 0x77, 0x00, 0x44, 0x9F, 0x24, 0x57, 0x86, 0x86, 0x10, 0x01, 0x34, 0x57, 0x80, 0xFF, 0x7F, 0x03, 0x00, 0x2F, 0xFF, 0x2A, 0xBF, 0x37, 0x00, 0x52, 0xF8, 0x8A, 0x11, 0x8C, 0xEE, 0x17, 0x11, 0xD0, 0x4A, 0x7F, 0x20, 0x27, 0x06, 0xA0, 0x17, 0x21, 0xA2, 0x11, 0x89, 0x80, 0x36, 0x1F, 0x44, 0x00, 0xB3, 0xBB, 0x09, 0xFF, 0xDB, 0x10, 0x52, 0x04, 0x20, 0x5A, 0xE7, 0x11, 0x88, 0x91, 0x89, 0x05, 0x47, 0xF8, 0xC9, 0x4F, 0x17, 0x23, 0x88, 0x41, 0x22, 0x17, 0x21, 0x01, 0x00, 0x57, 0x3F, 0x04, 0x20, 0xBB, 0xFB, 0x20, 0x27, 0x71, 0x51, 0x77, 0x57, 0xF7, 0x3F, 0x24, 0x20, 0x00, 0x02, 0xF1, 0x10, 0x01, 0xA3, 0xAF, 0xF8, 0x08, 0x64, 0xDF, 0x24, 0x0F, 0x38, 0x6F, 0x27, 0xCF, 0x03, 0xE5, 0x7F, 0x10, 0x23, 0x02, 0x00, 0x43, 0xF1, 0xB7, 0x16, 0x11, 0x75, 0x22, 0x00, 0x60, 0x25, 0x49, 0x07, 0xF4, 0x9F, 0x8C, 0x19, 0x4F, 0xF8, 0xD7, 0x2E, 0x4D, 0x22, 0x24, 0x91, 0x8D, 0x24, 0x47, 0x44, 0x9F, 0x44, 0x5F, 0x22, 0x7C, 0x22, 0x36, 0x0F, 0x74, 0x27, 0x28, 0xB7, 0x4A, 0x2F, 0x89, 0x4F, 0x44, 0xBB, 0x60, 0x3B, 0x32, 0xBF, 0xF8, 0xFF, 0xFE, 0x7F, 0x98, 0x19, 0x27, 0x18, 0xF8, 0xC1, 0x47, 0x80, 0x4F, 0x00, 0x6D, 0xBF, 0x88, 0x88, 0x71, 0xFE, 0x4D, 0xE7, 0x00, 0x4D, 0xFF, 0x2E, 0x17, 0x70, 0x07, 0x02, 0x40, 0x1F, 0x00, 0xBE, 0x3F, 0x7A, 0xAF, 0x88, 0x58, 0x91, 0x3A, 0xAF, 0x40, 0xFE, 0x8F, 0x7A, 0xAF, 0xCC, 0xE8, 0x88, 0x30, 0xEE, 0x21, 0x27, 0x3F, 0x73, 0x9F, 0x33, 0xAB, 0x91, 0x89, 0x5E, 0x27, 0x20, 0x2F, 0x11, 0x27, 0x28, 0x35, 0xB7, 0x73, 0x9F, 0x73, 0xC7, 0x15, 0x3F, 0x21, 0x20, 0x00, 0x5A, 0x7F, 0x27, 0x57, 0x3A, 0x47, 0x10, 0x0A, 0xF3, 0x9F, 0x10, 0x00, 0x89, 0x17, 0x68, 0xCF, 0xC1, 0x74, 0x2F, 0x01, 0xFD, 0x9F, 0xCA, 0xCC, 0xBF, 0xBB, 0x05, 0x4E, 0x17, 0xE1, 0x00, 0x5E, 0x1F, 0x9E, 0x37, 0x00, 0x5E, 0x3F, 0x15, 0x11, 0x20, 0x22, 0x3D, 0x8F, 0xF8, 0x00, 0x8D, 0xFF, 0x6E, 0x8F, 0x77, 0x4F, 0x10, 0x1C, 0x83, 0x87, 0x10, 0x2C, 0x62, 0xD7, 0x43, 0x4C, 0x49, 0x10, 0x4D, 0xFF, 0xFE, 0x2F, 0xA6, 0x00, 0x02, 0x02, 0x28, 0xC3, 0x2D, 0xFD, 0x3D, 0xEF, 0x69, 0x6D, 0x61, 0x67, 0x2F, 0xFE, 0x20, 0x0C, 0x1C, 0x40, 0x00, 0x0A, 0x3E, 0x10, 0x4E, 0x15, 0x00, 0x50, 0x03, 0x43, 0x4C, 0x24, 0x59, 0x54, 0x70, 0x3F, 0x80, 0x05, 0x47, 0xAE, 0x00, 0x6C, 0x18, 0x79, 0x74, 0x31, 0x30, 0x51, 0x3E, 0x3B, 0x00, 0x00, 0xA0, 0x00, 0x43, 0x00, 0x00, 0x70, 0x43, 0x74, 0x78, 0x6C, 0x32, 0x31, 0x40, 0x2F, 0xED, 0x2B, 0x90, 0x00, 0x0C, 0x2F, 0xF5, 0x1A, 0xC0, 0x2F, 0xF9, 0x2B, 0x3C, 0x00, 0x62, 0x75, 0x62, 0x62, 0x6C, 0x00, 0x65, 0x73, 0x2E, 0x62, 0x63, 0x6C, 0x69, 0x6D, 0x04, 0x00, 0x6D, 0x61, 0x73, 0x6B, 0x60, 0x0A, 0x77, 0x61, 0x60, 0x76, 0x80, 0x16, 0x2F, 0xFD, 0x6D, 0x61, 0x74, 0x31, 0x8C, 0xCD, 0x2E, 0x8C, 0x2B, 0xB0, 0x00, 0x1C, 0x30, 0x63, 0x2F, 0xF9, 0xD4, 0x2F, 0xFD, 0x66, 0x24, 0x2E, 0xA0, 0x30, 0x35, 0x5F, 0x30, 0x4C, 0x7A, 0xC0, 0xA8, 0xFF, 0x56, 0xFF, 0x00, 0x50, 0x01, 0x15, 0x40, 0x9B, 0x04, 0x30, 0x4A, 0x2F, 0xFF, 0x60, 0x4F, 0xC0, 0x50, 0xDD, 0x80, 0x3F, 0x50, 0x03, 0x80, 0x93, 0x00, 0x00, 0x52, 0x00, 0x60, 0x4F, 0x17, 0x80, 0x55, 0x06, 0x3F, 0x35, 0x05, 0x20, 0xFD, 0xB1, 0x27, 0x90, 0x4F, 0x81, 0x20, 0xB4, 0x22, 0x40, 0x24, 0x02, 0x10, 0x66, 0x2F, 0xFD, 0x47, 0x07, 0x61, 0x4A, 0x01, 0x04, 0x05, 0x50, 0xE2, 0x00, 0x20, 0xB8, 0x00, 0xB0, 0xB7, 0x1E, 0x02, 0x00, 0x05, 0x31, 0x02, 0x31, 0x4A, 0x31, 0x4E, 0x00, 0x50, 0xB7, 0x31, 0x82, 0x04, 0xD0, 0xB7, 0x70, 0x61, 0x6E, 0x31, 0x4C, 0x40, 0xC3, 0xFF, 0x00, 0x00, 0x52, 0x6F, 0x6F, 0x74, 0x50, 0x61, 0x6E, 0x78, 0x65, 0x00, 0x92, 0x21, 0xF2, 0x3B, 0x71, 0x67, 0x52, 0x17, 0x70, 0x61, 0x73, 0x21, 0x31, 0x08, 0x2F, 0xFD, 0x70, 0x69, 0x63, 0x31, 0x3D, 0xE5, 0xE0, 0x30, 0x53, 0x00, 0x71, 0x17, 0x2F, 0xFC, 0xC0, 0x80, 0x00, 0x80, 0xBF, 0xA4, 0x00, 0x90, 0x53, 0xF2, 0x22, 0x6B, 0x9A, 0x43, 0x71, 0xF7, 0x6F, 0xBB, 0x5F, 0xE7, 0x40, 0x03, 0x02, 0x62, 0x89, 0x62, 0xC5, 0x61, 0x37, 0x71, 0xF3, 0x31, 0x4B, 0x2E, 0x80, 0x3F, 0x30, 0x7F, 0xA0, 0x60, 0xD3, 0x00, 0x71, 0x47, 0x2F, 0xFC, 0xB8, 0x0B, 0x00, 0x00, 0x48, 0x42, 0x22, 0xCF, 0xC1, 0xE3, 0x11, 0x61, 0x8F, 0x5A, 0x80, 0x22, 0xEB, 0x80, 0x80, 0x7F, 0x72, 0x7F, 0x03, 0x21, 0xB5, 0xCD, 0x23, 0xCC, 0x6C, 0x42, 0x5B, 0x33, 0x33, 0x5B, 0x41, 0xB7, 0x50, 0x0F, 0x2F, 0x20, 0x40, 0x50, 0x0F, 0x20, 0xF0, 0x93, 0xE2, 0x43, 0x00, 0x60, 0x9F, 0x00, 0x12, 0xF2, 0xBB, 0x02, 0x40, 0x9F, 0x01, 0x22, 0x55, 0x2F, 0xFF, 0x31, 0x93, 0x3E, 0x50, 0x8B, 0x30, 0x07, 0xBE, 0x30, 0x0F, 0x14, 0x60, 0x9B, 0x30, 0x07, 0x01, 0x10, 0x9F, 0x51, 0xBF, 0x00, 0x93, 0x8F, 0x80, 0x03, 0xFD, 0x3F, 0x40, 0x00, 0xFE, 0x0F, 0xA1, 0x7F, 0xD3, 0x2B, 0x01, 0x10, 0x7E, 0x1B, 0x44, 0x98, 0x3D, 0xFC, 0x00, 0x01, 0x3F, 0xF9, 0x74, 0x49, 0x23, 0xA3, 0x41, 0xAB, 0x50, 0x07, 0xE1, 0x1F, 0x61, 0x65, 0x42, 0x47, 0x0C, 0x67, 0x72, 0x70, 0x31, 0x34, 0x1F, 0x32, 0x97, 0x47, 0x72, 0x13, 0x6F, 0x75, 0x70, 0xA4, 0xBA, 0x67, 0x72, 0x52, 0x6B, 0x30, 0x23, 0x8F, 0x32, 0xBF, 0x47, 0x5F, 0x41, 0xC4, 0x36, 0x34, 0x9B, 0xF3, 0xFB, 0xF4, 0x5B, 0xD5, 0xF3, 0x63, 0x90, 0x4B, 0x42, 0x03, 0x40, 0x4B, 0x2C, 0x40, 0x97, 0x43, 0xC4, 0xCE, 0xCD, 0x35, 0x93, 0xF4, 0xE3, 0x67, 0x72, 0x50, 0xEF, 0xB5, 0x7F, 0xE0, 0x24, 0x87, 0x5E, 0x11, 0x00, 0x05, 0x7F, 0xC8, 0x85, 0x7F, 0x33, 0xB3, 0x35, 0x3F, 0x35, 0xDB, 0x1E, 0xA8, 0x2F, 0xFF, 0x29, 0x2F, 0xFD, 0x34, 0x00, 0x05, 0x83, 0x6C, 0x6F, 0x67, 0x6F, 0x6F, 0x00, 0xD5, 0x8E, 0x35, 0x8B, 0x30, 0x22, 0x32, 0x34, 0xF0, 0x36, 0x1A, 0x31, 0x63, 0x55, 0xD8, 0x2F, 0xFF, 0x5C, 0x26, 0x3C, 0xAC, 0x02, 0x65, 0x8F, 0x02, 0x00, 0xA5, 0x8F, 0xFA, 0x30, 0x91, 0x01, 0xB5, 0xDF, 0x35, 0x8F, 0x95, 0xDF, 0x00, 0x15, 0x8F, 0x44, 0x26, 0x44, 0x40, 0x77, 0x24, 0x51, 0x2B, 0xB5, 0x8F, 0x01, 0xF5, 0xF7, 0x6A, 0x65, 0xF7, 0x90, 0xBB, 0x00, 0x25, 0xFB, 0xE1, 0x00, 0x36, 0x0F, 0x37, 0x64, 0x26, 0x13, 0x10, 0x24, 0x02, 0x11, 0x03, 0x36, 0x13, 0x7C, 0x03, 0x03, 0xA6, 0x13, 0x04, 0x30, 0xD3, 0x03, 0x56, 0x2F, 0x52, 0xC7, 0x04, 0x36, 0x2F, 0x6E, 0xAB, 0x6F, 0xE1, 0xE5, 0xAF, 0x76, 0x2F, 0x48, 0x38, 0x1B, 0x28, 0xB6, 0x40, 0x07, 0x05, 0x86, 0x2F, 0x57, 0x04, 0x06, 0x06, 0x2F, 0x20, 0x01, 0xC6, 0xCF, 0x02, 0x86, 0x2F, 0x4A, 0x1C, 0x77, 0x4F, 0xEE, 0x67, 0x4B, 0x46, 0xCB, 0x01, 0xA6, 0x2F, 0xFF, 0x00, 0x83, 0xDF, 0x39, 0x94, 0x50, 0x03, 0xF0, 0x86, 0xC6, 0x33, 0xCD, 0xCC, 0x4C, 0x3F, 0x30, 0x03, 0x57, 0x6F, 0x38, 0x7F, 0x42, 0x00, 0x16, 0xCF, 0xB7, 0xEF, 0x0B, 0x56, 0xAF, 0x3A, 0xEF, 0xF6, 0xAF, 0x3A, 0x70, 0xFA, 0x43, 0xEF, 0xFA, 0xBB, 0xF5, 0x3B, 0x01, 0x36, 0xCF, 0x5C, 0x00, 0x26, 0xCF, 0x3B, 0x77, 0xFB, 0x17, 0xFA, 0xBF, 0x80, 0x04, 0x36, 0xDF, 0x1F, 0xBC, 0xAB, 0xC4, 0xF4, 0xA7, 0x73, 0x00, 0x0A, 0x1A, 0x5D, 0x17, 0xE0, 0x62, 0x0D, 0xCB, 0x00, 0x15, 0x4B, 0xAE, 0x36, 0xE9, 0x22, 0xD6, 0xBC, 0x00, 0x84, 0x95, 0x43, 0x89, 0x8A, 0x34, 0x9F, 0xB6, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned char HomebrewLegacy_LZ[0x2000] = +{ + 0x11, 0x9C, 0x21, 0x00, 0x00, 0x64, 0x61, 0x72, 0x63, 0xFF, 0xFE, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7C, 0x21, 0x00, 0x00, 0x83, 0x30, 0x09, 0x24, 0x03, 0x00, 0x00, 0x40, 0x20, 0x03, 0x30, 0x13, 0xAB, 0x30, 0x18, 0x0F, 0x20, 0x1D, 0x02, 0xA0, 0x0B, 0x06, 0x20, 0x2B, 0x30, 0x18, 0x5A, 0x05, 0x20, 0x35, 0x10, 0x20, 0x39, 0x30, 0x2B, 0xA4, 0x20, 0x20, 0x40, 0xEA, 0x30, 0x45, 0x20, 0x1C, 0x30, 0x0B, 0x70, 0x60, 0x23, 0x08, 0x20, 0x59, 0x7A, 0x85, 0x30, 0x5D, 0x09, 0x00, 0x00, 0x28, 0x20, 0x2C, 0xA2, 0x20, 0x69, 0x21, 0x80, 0x19, 0x20, 0x0B, 0x04, 0x00, 0x00, 0xC4, 0x60, 0x47, 0xA0, 0x30, 0x5F, 0xCE, 0x20, 0x81, 0xC0, 0x1D, 0x00, 0x00, 0x9C, 0xA5, 0x20, 0x89, 0x12, 0x20, 0x75, 0x60, 0x1E, 0x50, 0x0B, 0x56, 0x30, 0x81, 0x55, 0x1F, 0x50, 0x17, 0x9A, 0x20, 0x8D, 0xA0, 0x60, 0x0B, 0xDE, 0x20, 0x99, 0x2A, 0x40, 0x20, 0x50, 0x2F, 0x22, 0x20, 0x9C, 0xE0, 0x60, 0x0B, 0x00, 0x20, 0x00, 0x2E, 0x20, 0xCB, 0x62, 0x00, 0x6C, 0x00, 0x79, 0x20, 0x00, 0x74, 0x20, 0xD5, 0x4E, 0x00, 0x69, 0x00, 0x6E, 0xA0, 0x20, 0x09, 0x65, 0x20, 0x05, 0x64, 0x00, 0x6F, 0x00, 0x4C, 0xA2, 0x20, 0x03, 0x67, 0x20, 0x07, 0x5F, 0x00, 0x55, 0x20, 0x03, 0x30, 0xAA, 0x20, 0x01, 0x2E, 0x20, 0x2D, 0x63, 0x01, 0x20, 0x2F, 0x44, 0x00, 0x40, 0x2F, 0x74, 0xA3, 0x20, 0x5F, 0x6D, 0x20, 0x51, 0x00, 0x00, 0x68, 0x40, 0x75, 0x70, 0x5D, 0x57, 0x62, 0x20, 0x6B, 0x74, 0x20, 0x81, 0x6F, 0x20, 0x1D, 0x70, 0x61, 0x30, 0x29, 0xD7, 0xF0, 0x27, 0x30, 0x21, 0x70, 0xE0, 0x21, 0x61, 0x20, 0xB1, 0x50, 0x2B, 0x01, 0x10, 0x8D, 0x18, 0x5F, 0x00, 0x53, 0x20, 0xBD, 0x30, 0xDD, 0x65, 0x00, 0x4F, 0x2F, 0x00, 0x75, 0x20, 0xF3, 0x43, 0x80, 0xD1, 0x30, 0x47, 0x01, 0x31, 0x01, 0x00, 0x10, 0x43, 0x6F, 0x42, 0x01, 0x80, 0x43, 0x00, 0x90, 0x87, 0x41, 0x03, 0x20, 0x43, 0x01, 0x90, 0x87, 0x00, 0x90, 0xCB, 0x01, 0x90, 0x87, 0xE0, 0x00, 0x91, 0x0F, 0xF1, 0x53, 0x90, 0x02, 0x43, 0x4C, 0x59, 0x54, 0xFF, 0x2C, 0xFE, 0x14, 0x33, 0x21, 0x02, 0x33, 0x03, 0x33, 0x0F, 0x6C, 0x79, 0x31, 0x74, 0x31, 0x30, 0x11, 0x43, 0x3C, 0x00, 0xC8, 0x43, 0x23, 0x0D, 0x03, 0x43, 0x74, 0x78, 0x6C, 0x31, 0x24, 0x63, 0x50, 0x22, 0xFA, 0x00, 0x00, 0x68, 0x62, 0x6C, 0x6F, 0x67, 0x6F, 0x5F, 0x00, 0x74, 0x6F, 0x70, 0x2E, 0x62, 0x63, 0x6C, 0x69, 0x81, 0x32, 0x18, 0x00, 0x6D, 0x61, 0x74, 0x31, 0x60, 0x63, 0x74, 0x86, 0x33, 0x57, 0x48, 0x62, 0x4D, 0x61, 0x32, 0xC3, 0xB0, 0x70, 0xFF, 0x35, 0xFF, 0xFF, 0x30, 0x03, 0x00, 0x40, 0x02, 0x15, 0x43, 0xB2, 0x04, 0x30, 0x5E, 0x98, 0xA0, 0xA3, 0x80, 0x3F, 0x50, 0x03, 0x23, 0x93, 0x61, 0x6E, 0x31, 0x48, 0x4C, 0x33, 0xE8, 0x04, 0xFF, 0x20, 0x5B, 0x52, 0x6F, 0x6F, 0x07, 0x74, 0x50, 0x61, 0x6E, 0x65, 0xE0, 0x60, 0x00, 0x80, 0x0E, 0x70, 0x47, 0x86, 0x50, 0xCF, 0x70, 0x61, 0x73, 0x31, 0x33, 0xDB, 0x70, 0x53, 0x03, 0xB3, 0x80, 0x53, 0x30, 0x01, 0x70, 0x50, 0xA0, 0x9B, 0x20, 0x42, 0x30, 0x03, 0x80, 0x53, 0x0B, 0x69, 0x63, 0x31, 0x80, 0x34, 0x90, 0x07, 0x30, 0xA7, 0x00, 0x11, 0x03, 0xC7, 0x01, 0x50, 0xA7, 0x23, 0x08, 0x00, 0x80, 0x41, 0xF1, 0x2B, 0x71, 0x95, 0x91, 0x1B, 0x83, 0xF1, 0x27, 0x80, 0x3F, 0x70, 0x61, 0x65, 0x60, 0xDB, 0x50, 0x07, 0x0C, 0x67, 0x72, 0x70, 0x31, 0x35, 0x21, 0x51, 0x33, 0x47, 0x72, 0xCD, 0x24, 0xDB, 0x82, 0x03, 0x67, 0x72, 0x51, 0x07, 0x30, 0x23, 0x3C, 0x25, 0x45, 0x07, 0x47, 0x5F, 0x41, 0x5F, 0x30, 0xA1, 0x02, 0x25, 0x37, 0x00, 0x01, 0x17, 0xD7, 0xF1, 0xD7, 0x30, 0x5F, 0x2C, 0x40, 0x3B, 0x42, 0xC0, 0x3B, 0x35, 0x7C, 0x00, 0x90, 0x2B, 0x67, 0x43, 0x00, 0x20, 0x2B, 0xD1, 0x7F, 0x67, 0x72, 0x50, 0xC7, 0x00, 0xB1, 0xE1, 0x01, 0x12, 0xBF, 0x40, 0xA0, 0x00, 0xB2, 0xBF, 0x62, 0x6F, 0x74, 0x74, 0x6F, 0x6D, 0xF2, 0x62, 0xC2, 0x09, 0x52, 0xBF, 0x50, 0xCF, 0x07, 0x52, 0xBF, 0xF0, 0xC2, 0x00, 0xE2, 0xBF, 0x42, 0xC3, 0x10, 0x00, 0xF2, 0xBF, 0x10, 0x76, 0x70, 0x1E, 0xF0, 0xF0, 0x0F, 0x0F, 0x30, 0x03, 0x02, 0x70, 0x1F, 0x9B, 0x4E, 0x44, 0xD7, 0x00, 0x2C, 0x8F, 0x00, 0x2D, 0x7F, 0x7D, 0x9D, 0xEE, 0x00, 0x4D, 0x9D, 0x11, 0x90, 0x00, 0xEF, 0x4E, 0x84, 0x06, 0x00, 0xA1, 0x00, 0x4D, 0xBD, 0x2B, 0xEE, 0x00, 0x00, 0xDB, 0xF6, 0xC5, 0x60, 0x77, 0x8D, 0x00, 0x30, 0x67, 0x3D, 0x23, 0xD5, 0x3D, 0x27, 0x40, 0x67, 0xFE, 0x00, 0x4E, 0x1D, 0x2A, 0x7A, 0xBE, 0x00, 0x00, 0x50, 0xFF, 0x57, 0xE9, 0x80, 0x17, 0xC5, 0x00, 0x4E, 0x5D, 0xFF, 0xA0, 0x79, 0x00, 0x50, 0x1F, 0x5F, 0x63, 0x1E, 0xD0, 0x00, 0xCF, 0x00, 0x7B, 0x38, 0x4F, 0x84, 0x00, 0xD1, 0x5D, 0x03, 0xF1, 0xEF, 0xFF, 0x1F, 0xF0, 0xF0, 0xFF, 0x81, 0xEF, 0x81, 0xFF, 0x3F, 0x8E, 0x21, 0xC6, 0x20, 0x1A, 0xE0, 0x20, 0x20, 0x92, 0x0F, 0x92, 0x1F, 0xC0, 0xF7, 0x00, 0x00, 0xFD, 0x02, 0xFF, 0xFF, 0x6E, 0xFF, 0x11, 0x06, 0x4E, 0x7A, 0xFF, 0x00, 0xFD, 0x00, 0x00, 0xF7, 0xC0, 0x01, 0x05, 0x00, 0x00, 0x00, 0x5E, 0xFF, 0x11, 0xFF, 0xFF, 0xF6, 0x0B, 0x08, 0x7F, 0x60, 0x10, 0xCF, 0x61, 0x73, 0xFF, 0xFF, 0x10, 0x01, 0x60, 0xFF, 0xCF, 0xE6, 0xFF, 0x7F, 0x0B, 0x71, 0x87, 0x04, 0xFA, 0x2C, 0xFF, 0x90, 0x03, 0x2D, 0x95, 0x4F, 0xFF, 0x04, 0xFC, 0x2C, 0xFF, 0xFF, 0x03, 0x00, 0x01, 0xA5, 0xFF, 0x90, 0x04, 0x5F, 0xCF, 0x10, 0x00, 0xEF, 0x2E, 0xD7, 0xA0, 0xF6, 0x18, 0x00, 0x00, 0xFC, 0x80, 0x47, 0x2D, 0x69, 0xFC, 0x00, 0x00, 0x01, 0xF6, 0x90, 0xFF, 0x5E, 0xFF, 0x01, 0x03, 0x3D, 0x72, 0x02, 0xC3, 0x0D, 0x8F, 0x20, 0xFF, 0xDF, 0x20, 0x42, 0xFF, 0x08, 0x00, 0x6E, 0xFF, 0x02, 0x40, 0xB3, 0x20, 0xFF, 0xC6, 0x83, 0x80, 0x77, 0xFA, 0x3E, 0xFF, 0x40, 0x04, 0x50, 0x9F, 0x41, 0xFF, 0x40, 0x01, 0x2D, 0xE4, 0x3E, 0xFA, 0x40, 0xFF, 0xEF, 0xFE, 0x04, 0x01, 0x08, 0xF5, 0xF1, 0x0D, 0x22, 0xEB, 0xAF, 0xEF, 0x00, 0xF0, 0xF0, 0x5F, 0x1F, 0xF1, 0xF5, 0x0F, 0x0D, 0x09, 0xFE, 0xEF, 0x08, 0x01, 0x72, 0xFF, 0xFF, 0x04, 0x53, 0x02, 0x07, 0xA0, 0xF6, 0xFF, 0x5E, 0xFC, 0x30, 0x5C, 0x7D, 0xE7, 0x20, 0x0C, 0x10, 0xF6, 0x90, 0x6E, 0x20, 0x48, 0xFF, 0xC3, 0x00, 0xFF, 0x01, 0x20, 0xFF, 0x0D, 0x8F, 0x00, 0x00, 0xDF, 0x32, 0x59, 0xB8, 0x22, 0xF2, 0x02, 0x20, 0x0F, 0x32, 0xF8, 0x30, 0x7D, 0xFC, 0xF8, 0x04, 0x00, 0x07, 0xF4, 0xF0, 0x0A, 0x0E, 0xF1, 0xF4, 0xFF, 0x00, 0xFD, 0xF8, 0xEC, 0xF5, 0xE0, 0xC0, 0x90, 0x2F, 0x00, 0x9F, 0x50, 0x10, 0xFF, 0xFF, 0xAF, 0x5F, 0xA0, 0x10, 0x50, 0x1F, 0x0D, 0x2E, 0x8D, 0x04, 0xF3, 0xF7, 0x08, 0x0C, 0x0C, 0xFA, 0xFD, 0x0C, 0x2F, 0x90, 0x3E, 0x77, 0x1F, 0x8F, 0x03, 0xCF, 0x8F, 0xFF, 0xFD, 0x5F, 0x1F, 0x06, 0x03, 0xEF, 0x00, 0xE2, 0x6C, 0xF4, 0x00, 0xF0, 0x1F, 0x02, 0x73, 0xEA, 0x01, 0x10, 0x1D, 0x00, 0xD0, 0x37, 0xA4, 0x63, 0xC2, 0xDF, 0x00, 0x6E, 0x28, 0x00, 0xC3, 0x6C, 0x00, 0x73, 0xEA, 0x2B, 0x01, 0x60, 0xFF, 0x30, 0x69, 0x00, 0x3F, 0x89, 0xFE, 0xFC, 0x20, 0x79, 0x3F, 0xFF, 0x00, 0x70, 0x7D, 0x23, 0x1B, 0x4F, 0xF5, 0x00, 0x7D, 0xE7, 0xF8, 0x00, 0x40, 0x0D, 0x10, 0x14, 0xCE, 0x01, 0x43, 0x4C, 0x49, 0x4D, 0xFF, 0xFE, 0x46, 0x14, 0x2F, 0xFF, 0x02, 0x02, 0x28, 0x24, 0x6E, 0x35, 0xA2, 0x69, 0x10, 0x6D, 0x61, 0x67, 0x24, 0x79, 0x00, 0x80, 0x00, 0x40, 0xFF, 0x52, 0x7D, 0x30, 0x0C, 0x07, 0x8F, 0xFF, 0x57, 0x9F, 0x38, 0x79, 0x00, 0x58, 0x7D, 0x00, 0xF0, 0x1F, 0x40, 0xD8, 0xB6, 0x53, 0x74, 0xDF, 0x00, 0x88, 0x27, 0x57, 0x5F, 0xFF, 0x28, 0x69, 0x00, 0x3F, 0xFF, 0xEF, 0xDD, 0x28, 0x79, 0x3F, 0xFF, 0xB2, 0x63, 0xDC, 0xFF, 0xFF, 0x98, 0x7D, 0xC7, 0x00, 0x67, 0xFF, 0x7F, 0x9D, 0x4F, 0xFF, 0x47, 0x0F, 0x48, 0xE5, 0x89, 0x47, 0x30, 0xD9, 0x50, 0xDD, 0x2F, 0xFF, 0x17, 0xE8, 0x00, 0xBF, 0x00, 0x4F, 0xFF, 0x02, 0x00, 0x10, 0x37, 0x00, 0x68, 0x7F, 0x01, 0x50, 0x7F, 0xB1, 0x00, 0x14, 0x92, 0x2F, 0x84, 0xBE, 0x00, 0x3F, 0xFF, 0x0E, 0x00, 0x20, 0x69, 0x9D, 0xF9, 0x05, 0x24, 0x9C, 0x61, 0xEF, 0x38, 0x67, 0x20, 0x04, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x38, 0x7F, 0x83, 0x28, 0x7D, 0x10, 0x90, 0x0F, 0x0F, 0xE0, 0x29, 0x17, 0x72, 0x1F, 0x50, 0xE0, 0x20, 0x0F, 0x10, 0x38, 0x79, 0x1B, 0xFF, 0xFF, 0x02, 0x02, 0x00, 0xFF, 0xB1, 0xDF, 0xFF, 0x20, 0x40, 0x2C, 0x02, 0x01, 0x1B, 0xFD, 0x80, 0xFF, 0xFD, 0x20, 0xB1, 0x20, 0x38, 0x00, 0xDF, 0xDF, 0x08, 0x01, 0x09, 0xF0, 0xF0, 0x0E, 0xD5, 0x2A, 0xC9, 0x28, 0x69, 0x1A, 0x30, 0x27, 0x0E, 0x20, 0x0F, 0x01, 0x2A, 0xDD, 0x90, 0x88, 0x5F, 0xF5, 0x7F, 0x38, 0xF7, 0xFF, 0xFE, 0x1A, 0xFF, 0x3A, 0xF5, 0x02, 0x2A, 0xF5, 0x30, 0x29, 0x50, 0x1D, 0xF0, 0x29, 0x83, 0xF0, 0x30, 0xF0, 0x7F, 0x25, 0xDE, 0x3A, 0x31, 0xD1, 0xF9, 0xFF, 0x7F, 0x60, 0xFE, 0x30, 0x8B, 0x6A, 0x3D, 0xFE, 0xF8, 0x2F, 0xCF, 0xC0, 0x44, 0x00, 0x28, 0x60, 0x01, 0xFF, 0xF5, 0x30, 0xA2, 0x2E, 0xAF, 0x80, 0x6A, 0x55, 0x03, 0x00, 0x30, 0xFF, 0xFE, 0xFF, 0xCF, 0x90, 0x32, 0x19, 0xFF, 0x38, 0x3A, 0x6D, 0xAF, 0xBF, 0xFF, 0xFF, 0x21, 0x2F, 0x0F, 0x29, 0x0A, 0xFB, 0x00, 0x00, 0xF2, 0x20, 0x87, 0x40, 0x2F, 0x20, 0x0F, 0xAF, 0x0F, 0x00, 0x11, 0xF2, 0xFB, 0x03, 0xFF, 0xE8, 0xFF, 0xBF, 0x1E, 0x9F, 0x22, 0x6E, 0x4A, 0x91, 0x01, 0xAF, 0xCF, 0xFF, 0xFF, 0x3F, 0x0F, 0xEF, 0x20, 0x0F, 0x67, 0x2E, 0x21, 0xFF, 0x72, 0x27, 0xFF, 0x03, 0x5B, 0x82, 0x70, 0x7F, 0x86, 0x77, 0x80, 0x01, 0x60, 0x7F, 0xF7, 0xF2, 0x7F, 0xBF, 0xD0, 0x70, 0xFF, 0x21, 0xFF, 0x20, 0x20, 0x03, 0xB1, 0xF5, 0xBF, 0x2F, 0x21, 0x23, 0x80, 0x39, 0x9F, 0xF2, 0xFD, 0xFF, 0x0B, 0x06, 0xFF, 0xDF, 0x00, 0x02, 0x00, 0x2F, 0x6F, 0x70, 0xB0, 0xBF, 0xFF, 0x40, 0xF1, 0x20, 0xC3, 0x07, 0x02, 0xDF, 0x7F, 0x00, 0x00, 0x02, 0xFB, 0xF6, 0xFD, 0xFF, 0xF2, 0xD0, 0x20, 0xB1, 0x0C, 0x1E, 0x00, 0x00, 0x07, 0x72, 0x87, 0x03, 0x74, 0x7F, 0x29, 0x73, 0xD4, 0x7F, 0x10, 0xE5, 0x56, 0xFD, 0x38, 0xF8, 0x00, 0x84, 0xBF, 0x41, 0x4E, 0x74, 0xBF, 0x9C, 0x63, 0x1A, 0x05, 0x70, 0x61, 0x74, 0x31, 0x3C, 0x63, 0x60, 0x1C, 0x67, 0x7B, 0x60, 0xF1, 0x22, 0x27, 0x3A, 0x7E, 0x53, 0x63, 0x65, 0x6E, 0x65, 0x08, 0x4F, 0x75, 0x74, 0x43, 0x2F, 0xFF, 0x47, 0x5F, 0x43, 0x10, 0x5F, 0x30, 0x30, 0xDF, 0xFF, 0x70, 0x61, 0x69, 0x31, 0x4D, 0x4C, 0x8B, 0x5A, 0x02, 0x00, 0x35, 0x19, 0x30, 0x43, 0x40, 0x2F, 0xFF, 0x04, 0x48, 0x62, 0x4D, 0x61, 0x74, 0x00, 0x2F, 0xFF, 0x48, 0x62, 0x0D, 0x52, 0x6F, 0x6F, 0x74, 0xE0, 0x48, 0x02, 0xE0, 0x9F, 0x42, 0x40, 0x9F, 0x56, 0x42, 0x08, 0x80, 0x9F, 0x41, 0x41, 0x3F, 0x41, 0x10, 0x0C, 0x81, 0x3F, 0x05, 0x33, 0x1F, 0x77, 0x00, 0xA2, 0x13, 0x49, 0x99, 0x58, 0x3D, 0x71, 0x8A, 0x00, 0x3A, 0x75, 0x0A, 0xEF, 0xE4, 0xC9, 0xFC, 0xB1, 0x00, 0x00, 0x99, 0x02, 0x63, 0xA9, 0x9B, 0x74, 0xE0, 0x00, 0x38, 0xD3, 0x33, 0xC0, 0x52, 0x6A, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; \ No newline at end of file From 9f20fed0776eb9798834f238f5e83973b543161b Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 11 May 2020 16:57:26 +0800 Subject: [PATCH 230/317] Remove references to appveyor --- .appveyor.yml | 29 ----------------------------- README.md | 7 +------ 2 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 .appveyor.yml diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 8ed1c448..00000000 --- a/.appveyor.yml +++ /dev/null @@ -1,29 +0,0 @@ -version: '{build}' - -os: -- Visual Studio 2017 -- Ubuntu1804 - -install: -- cmd: set PATH=%PATH%;C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin - -build_script: - - cmd: | - mingw32-make -j 6 -C ctrtool - mingw32-make -j 6 -C makerom - - sh: | - make -j 2 -C ctrtool - make -j 2 -C makerom - -after_build: - - cmd: | - move ctrtool\ctrtool.exe && move makerom\makerom.exe - 7z a Project_CTR.zip ctrtool.exe makerom.exe - - sh: | - cd ctrtool && 7z u ../Project_CTR.zip ctrtool - cd ../makerom && 7z u ../Project_CTR.zip makerom - -test: off - -artifacts: - - path: Project_CTR.zip diff --git a/README.md b/README.md index cb927b98..ce2a7b7b 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,4 @@ Project_CTR ctrtool - updated version of neimod's ctrtool. -makerom - creates CTR cxi/cfa/cci/cia files. - -## CI Builds -Thanks to @Margen67 we have AppVeyor make new builds for each commit to master. - -Download them here: https://ci.appveyor.com/project/jakcron/project-ctr +makerom - creates CTR cxi/cfa/cci/cia files. \ No newline at end of file From f33d66c6df9530ed2c0e01a6e55becf408b73390 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 11 May 2020 16:57:40 +0800 Subject: [PATCH 231/317] Add github actions --- .github/workflows/build_master.yml | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/build_master.yml diff --git a/.github/workflows/build_master.yml b/.github/workflows/build_master.yml new file mode 100644 index 00000000..9c4e8bcd --- /dev/null +++ b/.github/workflows/build_master.yml @@ -0,0 +1,34 @@ +name: Compile Master Branch + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + release: + types: [ created ] + +jobs: + build: + name: Compile ${{ matrix.prog }} for ${{ matrix.dist }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + dist: [ubuntu_x86_64, macos_x86_64, win_x86_64] + prog: [ctrtool, makerom] + include: + - dist: win_x86_64 + os: windows-2016 + binExt: .exe + - dist: ubuntu_x86_64 + os: ubuntu-latest + - dist: macos_x86_64 + os: macos-latest + steps: + - uses: actions/checkout@v1 + - name: Compile ${{ matrix.prog }} + run: cd ${{ matrix.prog }}; make ${{ matrix.makeArgs }}; + - uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.prog }}-${{ matrix.dist }} + path: ${{ matrix.prog }}/${{ matrix.prog }}${{ matrix.binExt }} \ No newline at end of file From 85a0ba610ece97f0947f2eabc8df0e24a554dc33 Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 11 May 2020 17:00:52 +0800 Subject: [PATCH 232/317] Update windows ctrtool makefile. --- ctrtool/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctrtool/Makefile b/ctrtool/Makefile index be6e85b7..cd515a4e 100644 --- a/ctrtool/Makefile +++ b/ctrtool/Makefile @@ -22,7 +22,7 @@ else ifneq (, $(findstring darwin, $(SYS))) else #Windows Build CFG CFLAGS += -Wno-unused-but-set-variable - LIBS += -static-libgcc -static-libstdc++ + LIBS += -static-libgcc -static-libstdc++ -static -lpthread endif main: $(OBJS) From 6ebef3c42aee8b373cbb3f837b3fa66e8ba41917 Mon Sep 17 00:00:00 2001 From: xprism1 Date: Mon, 25 Jan 2021 17:26:22 +0800 Subject: [PATCH 233/317] Add support for custom decrypted TitleKey --- makerom/cia.c | 6 ++++++ makerom/tik.c | 2 +- makerom/user_settings.c | 9 +++++++++ makerom/user_settings.h | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/makerom/cia.c b/makerom/cia.c index 492181ac..fb9f1786 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -184,6 +184,12 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) if(usrset->cia.randomTitleKey) rndset(ciaset->common.titleKey,AES_128_KEY_SIZE); + else if(usrset->cia.titleKey){ + for (size_t count = 0; count < sizeof(ciaset->common.titleKey)/sizeof(ciaset->common.titleKey[0]); count++) { + sscanf(usrset->cia.titleKey, "%2hhx", &ciaset->common.titleKey[count]); + usrset->cia.titleKey += 2; + } + } else clrmem(ciaset->common.titleKey,AES_128_KEY_SIZE); diff --git a/makerom/tik.c b/makerom/tik.c index df955992..f28f2e54 100644 --- a/makerom/tik.c +++ b/makerom/tik.c @@ -65,7 +65,7 @@ void SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset) SetLimits(hdr,ciaset); // Crypt TitleKey - if(ciaset->content.encryptCia) + if(ciaset->content.encryptCia || ciaset->common.titleKey) CryptTitleKey(ciaset->common.titleKey, hdr->encryptedTitleKey, hdr->titleId, ciaset->keys, ENC); else rndset(hdr->encryptedTitleKey,AES_128_KEY_SIZE); diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 3e498b6e..e5e07e93 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -559,6 +559,14 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) set->cia.randomTitleKey = true; return 1; } + else if (strcmp(argv[i], "-titlekey") == 0) { + if (ParamNum != 1) { + PrintArgReqParam(argv[i], 1); + return USR_ARG_REQ_PARAM; + } + set->cia.titleKey = argv[i + 1]; + return 2; + } else if (strcmp(argv[i], "-dlc") == 0) { if (ParamNum) { PrintNoNeedParam(argv[i]); @@ -990,6 +998,7 @@ void DisplayExtendedHelp(char *app_name) printf(" -dver Data-title version\n"); printf(" -deviceid 3DS unique device ID\n"); printf(" -esaccid e-Shop account ID\n"); + printf(" -titlekey Specify decrypted title key\n"); printf(" -rand Use a random title key\n"); printf(" -dlc Create DLC CIA\n"); printf(" -srl Package a TWL SRL in a CIA\n"); diff --git a/makerom/user_settings.h b/makerom/user_settings.h index ba6af79e..e09ec504 100644 --- a/makerom/user_settings.h +++ b/makerom/user_settings.h @@ -284,6 +284,7 @@ typedef struct struct{ bool randomTitleKey; + char *titleKey; bool encryptCia; bool DlcContent; bool includeUpdateNcch; From 928d9151965df067103b23507690e410d8735ae3 Mon Sep 17 00:00:00 2001 From: xprism1 Date: Wed, 27 Jan 2021 02:09:39 +0800 Subject: [PATCH 234/317] Respect content.encryptCia --- makerom/tik.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/tik.c b/makerom/tik.c index f28f2e54..df955992 100644 --- a/makerom/tik.c +++ b/makerom/tik.c @@ -65,7 +65,7 @@ void SetupTicketHeader(tik_hdr *hdr, cia_settings *ciaset) SetLimits(hdr,ciaset); // Crypt TitleKey - if(ciaset->content.encryptCia || ciaset->common.titleKey) + if(ciaset->content.encryptCia) CryptTitleKey(ciaset->common.titleKey, hdr->encryptedTitleKey, hdr->titleId, ciaset->keys, ENC); else rndset(hdr->encryptedTitleKey,AES_128_KEY_SIZE); From 24149fcf6e52fd74ead01c457e55e79109c18207 Mon Sep 17 00:00:00 2001 From: xprism1 Date: Wed, 27 Jan 2021 02:12:47 +0800 Subject: [PATCH 235/317] Override content.encryptCia when titlekey specified --- makerom/cia.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/makerom/cia.c b/makerom/cia.c index fb9f1786..00c7bddd 100644 --- a/makerom/cia.c +++ b/makerom/cia.c @@ -160,7 +160,7 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) ciaset->verbose = usrset->common.verbose; ciaset->tmd.titleType = TYPE_CTR; - ciaset->content.encryptCia = usrset->common.rsfSet.Option.EnableCrypt; + ciaset->content.encryptCia = usrset->common.rsfSet.Option.EnableCrypt || usrset->cia.titleKey != NULL; ciaset->content.IsDlc = usrset->cia.DlcContent; if(ciaset->keys->aes.commonKey[ciaset->keys->aes.currentCommonKey] == NULL && ciaset->content.encryptCia){ fprintf(stderr,"[CIA WARNING] Common Key could not be loaded, CIA will not be encrypted\n"); @@ -185,10 +185,10 @@ int GetSettingsFromUsrset(cia_settings *ciaset, user_settings *usrset) if(usrset->cia.randomTitleKey) rndset(ciaset->common.titleKey,AES_128_KEY_SIZE); else if(usrset->cia.titleKey){ - for (size_t count = 0; count < sizeof(ciaset->common.titleKey)/sizeof(ciaset->common.titleKey[0]); count++) { - sscanf(usrset->cia.titleKey, "%2hhx", &ciaset->common.titleKey[count]); - usrset->cia.titleKey += 2; - } + for (size_t count = 0; count < sizeof(ciaset->common.titleKey)/sizeof(ciaset->common.titleKey[0]); count++) { + sscanf(usrset->cia.titleKey, "%2hhx", &ciaset->common.titleKey[count]); + usrset->cia.titleKey += 2; + } } else clrmem(ciaset->common.titleKey,AES_128_KEY_SIZE); From 73aeef12b8b977bdda43b7f67b0dcca9a0cea068 Mon Sep 17 00:00:00 2001 From: xprism1 Date: Wed, 27 Jan 2021 02:19:55 +0800 Subject: [PATCH 236/317] Override content.encryptCia when titlekey specified --- makerom/user_settings.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index e5e07e93..3583e9c3 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -564,7 +564,7 @@ int SetArgument(int argc, int i, char *argv[], user_settings *set) PrintArgReqParam(argv[i], 1); return USR_ARG_REQ_PARAM; } - set->cia.titleKey = argv[i + 1]; + set->cia.titleKey = argv[i + 1]; return 2; } else if (strcmp(argv[i], "-dlc") == 0) { @@ -998,7 +998,7 @@ void DisplayExtendedHelp(char *app_name) printf(" -dver Data-title version\n"); printf(" -deviceid 3DS unique device ID\n"); printf(" -esaccid e-Shop account ID\n"); - printf(" -titlekey Specify decrypted title key\n"); + printf(" -titlekey Specify decrypted title key (Overrides \"EnableCrypt\" in RSF to true)\n"); printf(" -rand Use a random title key\n"); printf(" -dlc Create DLC CIA\n"); printf(" -srl Package a TWL SRL in a CIA\n"); From d8cede313c3c05b40ae5291fdba7261c1964af0e Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 27 Mar 2021 12:18:45 +0800 Subject: [PATCH 237/317] Update ticket definition for makerom. --- makerom/tik.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/makerom/tik.h b/makerom/tik.h index 8cc351d5..bfce48fb 100644 --- a/makerom/tik.h +++ b/makerom/tik.h @@ -64,7 +64,9 @@ typedef struct u8 padding2[8]; u8 licenceType; u8 keyId; - u8 padding3[0x2A]; + u8 propertyMask[2]; + u8 customData[0x14]; + u8 padding3[0x14]; u8 eshopAccId[4]; u8 padding4; u8 audit; From c9a198814b1c5316cd300bd919d47bf0001f93f1 Mon Sep 17 00:00:00 2001 From: Margen67 Date: Sun, 2 May 2021 23:37:54 -1000 Subject: [PATCH 238/317] build_master: Improvements Add workflow_dispatch: https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/ Upgrade windows-2016 to windows-latest. Add -j to makeArgs. Upgrade checkout to v2. Use working-directory instead of cd for compile. Add if-no-files-found: error to upload-artifact. --- .github/workflows/build_master.yml | 36 ++++++++++++++++++------------ 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build_master.yml b/.github/workflows/build_master.yml index 9c4e8bcd..437e2b64 100644 --- a/.github/workflows/build_master.yml +++ b/.github/workflows/build_master.yml @@ -7,6 +7,7 @@ on: branches: [ master ] release: types: [ created ] + workflow_dispatch: jobs: build: @@ -17,18 +18,25 @@ jobs: dist: [ubuntu_x86_64, macos_x86_64, win_x86_64] prog: [ctrtool, makerom] include: - - dist: win_x86_64 - os: windows-2016 - binExt: .exe - - dist: ubuntu_x86_64 - os: ubuntu-latest - - dist: macos_x86_64 - os: macos-latest + - dist: win_x86_64 + os: windows-latest + makeArgs: -j $env:NUMBER_OF_PROCESSORS + binExt: .exe + - dist: ubuntu_x86_64 + os: ubuntu-latest + makeArgs: -j$(nproc) + - dist: macos_x86_64 + os: macos-latest + makeArgs: -j$(sysctl -n hw.activecpu) steps: - - uses: actions/checkout@v1 - - name: Compile ${{ matrix.prog }} - run: cd ${{ matrix.prog }}; make ${{ matrix.makeArgs }}; - - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.prog }}-${{ matrix.dist }} - path: ${{ matrix.prog }}/${{ matrix.prog }}${{ matrix.binExt }} \ No newline at end of file + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Compile ${{ matrix.prog }} + working-directory: ${{ matrix.prog }} + run: make ${{ matrix.makeArgs }} + - uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.prog }}-${{ matrix.dist }} + path: ${{ matrix.prog }}/${{ matrix.prog }}${{ matrix.binExt }} + if-no-files-found: error From b7ee685b5be1b3bb886a62e0be4f489d2fafd2b6 Mon Sep 17 00:00:00 2001 From: Jack Date: Wed, 21 Jul 2021 16:50:43 +0800 Subject: [PATCH 239/317] Update README.md Looking for feedback on the future of ProjectCTR --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ce2a7b7b..6587032d 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,8 @@ Project_CTR ctrtool - updated version of neimod's ctrtool. -makerom - creates CTR cxi/cfa/cci/cia files. \ No newline at end of file +makerom - creates CTR cxi/cfa/cci/cia files. + +# Community Input Wanted +I'm looking for some feedback on where to take Project_CTR, see here: +https://github.com/3DSGuy/Project_CTR/issues/103 From 416431c24e5423b235850394e4775c696ed7d6ad Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 28 Nov 2021 12:49:45 +0800 Subject: [PATCH 240/317] Bump version number to v0.18 --- makerom/user_settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makerom/user_settings.c b/makerom/user_settings.c index 3583e9c3..469bf381 100644 --- a/makerom/user_settings.c +++ b/makerom/user_settings.c @@ -910,7 +910,7 @@ void PrintNoNeedParam(char *arg) void DisplayBanner(void) { - printf("CTR MAKEROM v0.17 (C) 3DSGuy 2020\n"); + printf("CTR MAKEROM v0.18 (C) 3DSGuy 2021\n"); printf("Built: %s %s\n\n", __TIME__, __DATE__); } From ef3a1bc2e6e7510ba781f96b480e17fc8e580738 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 12 Mar 2022 14:13:01 +0800 Subject: [PATCH 241/317] Remove old ctrtool source code. --- ctrtool/LICENSE | 22 - ctrtool/Makefile | 32 - ctrtool/aes_keygen.c | 100 -- ctrtool/aes_keygen.h | 8 - ctrtool/cia.c | 317 ----- ctrtool/cia.h | 72 - ctrtool/ctr.c | 357 ----- ctrtool/ctr.h | 149 --- ctrtool/ctrtool.sln | 20 - ctrtool/ctrtool.vcproj | 442 ------- ctrtool/ctrtool.vcxproj | 164 --- ctrtool/ctrtool.vcxproj.filters | 225 ---- ctrtool/cwav.c | 1003 -------------- ctrtool/cwav.h | 205 --- ctrtool/exefs.c | 342 ----- ctrtool/exefs.h | 61 - ctrtool/exheader.c | 704 ---------- ctrtool/exheader.h | 202 --- ctrtool/filepath.c | 90 -- ctrtool/filepath.h | 20 - ctrtool/firm.c | 257 ---- ctrtool/firm.h | 57 - ctrtool/info.h | 16 - ctrtool/ivfc.c | 247 ---- ctrtool/ivfc.h | 78 -- ctrtool/keyset.cpp | 395 ------ ctrtool/keyset.h | 96 -- ctrtool/lzss.c | 195 --- ctrtool/lzss.h | 26 - ctrtool/main.c | 488 ------- ctrtool/ncch.c | 880 ------------- ctrtool/ncch.h | 113 -- ctrtool/ncsd.c | 165 --- ctrtool/ncsd.h | 57 - ctrtool/oschar.c | 210 --- ctrtool/oschar.h | 95 -- ctrtool/polarssl/aes.c | 1164 ---------------- ctrtool/polarssl/aes.h | 139 -- ctrtool/polarssl/bignum.c | 2038 ----------------------------- ctrtool/polarssl/bignum.h | 533 -------- ctrtool/polarssl/bn_mul.h | 736 ----------- ctrtool/polarssl/config.h | 336 ----- ctrtool/polarssl/padlock.h | 98 -- ctrtool/polarssl/rsa.c | 823 ------------ ctrtool/polarssl/rsa.h | 353 ----- ctrtool/polarssl/sha2.c | 702 ---------- ctrtool/polarssl/sha2.h | 155 --- ctrtool/romfs.c | 406 ------ ctrtool/romfs.h | 99 -- ctrtool/settings.c | 314 ----- ctrtool/settings.h | 81 -- ctrtool/stream.c | 138 -- ctrtool/stream.h | 45 - ctrtool/syscalls.c | 171 --- ctrtool/syscalls.h | 19 - ctrtool/tik.c | 127 -- ctrtool/tik.h | 64 - ctrtool/tinyxml/tinystr.cpp | 111 -- ctrtool/tinyxml/tinystr.h | 305 ----- ctrtool/tinyxml/tinyxml.cpp | 1886 -------------------------- ctrtool/tinyxml/tinyxml.h | 1805 ------------------------- ctrtool/tinyxml/tinyxmlerror.cpp | 52 - ctrtool/tinyxml/tinyxmlparser.cpp | 1638 ----------------------- ctrtool/tmd.c | 180 --- ctrtool/tmd.h | 103 -- ctrtool/types.h | 44 - ctrtool/utils.c | 239 ---- ctrtool/utils.h | 59 - ctrtool/windows/getopt.c | 1047 --------------- ctrtool/windows/getopt.h | 172 --- ctrtool/windows/getopt1.c | 188 --- 71 files changed, 24280 deletions(-) delete mode 100644 ctrtool/LICENSE delete mode 100644 ctrtool/Makefile delete mode 100644 ctrtool/aes_keygen.c delete mode 100644 ctrtool/aes_keygen.h delete mode 100644 ctrtool/cia.c delete mode 100644 ctrtool/cia.h delete mode 100644 ctrtool/ctr.c delete mode 100644 ctrtool/ctr.h delete mode 100644 ctrtool/ctrtool.sln delete mode 100644 ctrtool/ctrtool.vcproj delete mode 100644 ctrtool/ctrtool.vcxproj delete mode 100644 ctrtool/ctrtool.vcxproj.filters delete mode 100644 ctrtool/cwav.c delete mode 100644 ctrtool/cwav.h delete mode 100644 ctrtool/exefs.c delete mode 100644 ctrtool/exefs.h delete mode 100644 ctrtool/exheader.c delete mode 100644 ctrtool/exheader.h delete mode 100644 ctrtool/filepath.c delete mode 100644 ctrtool/filepath.h delete mode 100644 ctrtool/firm.c delete mode 100644 ctrtool/firm.h delete mode 100644 ctrtool/info.h delete mode 100644 ctrtool/ivfc.c delete mode 100644 ctrtool/ivfc.h delete mode 100644 ctrtool/keyset.cpp delete mode 100644 ctrtool/keyset.h delete mode 100644 ctrtool/lzss.c delete mode 100644 ctrtool/lzss.h delete mode 100644 ctrtool/main.c delete mode 100644 ctrtool/ncch.c delete mode 100644 ctrtool/ncch.h delete mode 100644 ctrtool/ncsd.c delete mode 100644 ctrtool/ncsd.h delete mode 100644 ctrtool/oschar.c delete mode 100644 ctrtool/oschar.h delete mode 100644 ctrtool/polarssl/aes.c delete mode 100644 ctrtool/polarssl/aes.h delete mode 100644 ctrtool/polarssl/bignum.c delete mode 100644 ctrtool/polarssl/bignum.h delete mode 100644 ctrtool/polarssl/bn_mul.h delete mode 100644 ctrtool/polarssl/config.h delete mode 100644 ctrtool/polarssl/padlock.h delete mode 100644 ctrtool/polarssl/rsa.c delete mode 100644 ctrtool/polarssl/rsa.h delete mode 100644 ctrtool/polarssl/sha2.c delete mode 100644 ctrtool/polarssl/sha2.h delete mode 100644 ctrtool/romfs.c delete mode 100644 ctrtool/romfs.h delete mode 100644 ctrtool/settings.c delete mode 100644 ctrtool/settings.h delete mode 100644 ctrtool/stream.c delete mode 100644 ctrtool/stream.h delete mode 100644 ctrtool/syscalls.c delete mode 100644 ctrtool/syscalls.h delete mode 100644 ctrtool/tik.c delete mode 100644 ctrtool/tik.h delete mode 100644 ctrtool/tinyxml/tinystr.cpp delete mode 100644 ctrtool/tinyxml/tinystr.h delete mode 100644 ctrtool/tinyxml/tinyxml.cpp delete mode 100644 ctrtool/tinyxml/tinyxml.h delete mode 100644 ctrtool/tinyxml/tinyxmlerror.cpp delete mode 100644 ctrtool/tinyxml/tinyxmlparser.cpp delete mode 100644 ctrtool/tmd.c delete mode 100644 ctrtool/tmd.h delete mode 100644 ctrtool/types.h delete mode 100644 ctrtool/utils.c delete mode 100644 ctrtool/utils.h delete mode 100644 ctrtool/windows/getopt.c delete mode 100644 ctrtool/windows/getopt.h delete mode 100644 ctrtool/windows/getopt1.c diff --git a/ctrtool/LICENSE b/ctrtool/LICENSE deleted file mode 100644 index 37685952..00000000 --- a/ctrtool/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -MIT License - -Copyright (c) 2012 neimod -Copyright (c) 2014 3DSGuy - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/ctrtool/Makefile b/ctrtool/Makefile deleted file mode 100644 index cd515a4e..00000000 --- a/ctrtool/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# Sources -SRC_DIR = . polarssl tinyxml -OBJS = $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) - -# Compiler Settings -OUTPUT = ctrtool -CXXFLAGS = -I. -CFLAGS = -O2 -Wall -Wno-unused-variable -Wno-unused-result -I. -std=c11 -CC = gcc -CXX = g++ -SYS := $(shell gcc -dumpmachine) -ifneq (, $(findstring linux, $(SYS))) - # Linux - CFLAGS += -Wno-unused-but-set-variable -else ifneq (, $(findstring cygwin, $(SYS))) - # Cygwin - CFLAGS += -Wno-unused-but-set-variable -DUSE_FILE32API - LIBS += -liconv -static-libgcc -static-libstdc++ -else ifneq (, $(findstring darwin, $(SYS))) - # OS X - LIBS += -liconv -else - #Windows Build CFG - CFLAGS += -Wno-unused-but-set-variable - LIBS += -static-libgcc -static-libstdc++ -static -lpthread -endif - -main: $(OBJS) - $(CXX) -o $(OUTPUT) $(OBJS) $(LIBS) - -clean: - rm -rf $(OUTPUT) $(OBJS) diff --git a/ctrtool/aes_keygen.c b/ctrtool/aes_keygen.c deleted file mode 100644 index eb81bdff..00000000 --- a/ctrtool/aes_keygen.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "aes_keygen.h" - -// 128bit wrap-around math -int32_t wrap_index(int32_t i) -{ - return i < 0 ? ((i % 16) + 16) % 16 : (i > 15 ? i % 16 : i); -} - -void n128_rrot(const uint8_t *in, uint32_t rot, uint8_t *out) -{ - uint32_t bit_shift, byte_shift; - - rot = rot % 128; - byte_shift = rot / 8; - bit_shift = rot % 8; - - for (int32_t i = 0; i < 16; i++) { - out[i] = (in[wrap_index(i - byte_shift)] >> bit_shift) | (in[wrap_index(i - byte_shift - 1)] << (8 - bit_shift)); - } - -} - -void n128_lrot(const uint8_t *in, uint32_t rot, uint8_t *out) -{ - uint32_t bit_shift, byte_shift; - - rot = rot % 128; - byte_shift = rot / 8; - bit_shift = rot % 8; - - for (int32_t i = 0; i < 16; i++) { - out[i] = (in[wrap_index(i + byte_shift)] << bit_shift) | (in[wrap_index(i + byte_shift + 1)] >> (8 - bit_shift)); - } -} - -/* out = a + b -*/ -void n128_add(const uint8_t *a, const uint8_t *b, uint8_t *out) -{ - uint8_t carry = 0; - uint32_t sum = 0; - - for (int i = 15; i >= 0; i--) { - sum = a[i] + b[i] + carry; - carry = sum >> 8; - out[i] = sum & 0xff; - } -} - -/* out = a - b -*/ -void n128_sub(const uint8_t *a, const uint8_t *b, uint8_t *out) -{ - uint8_t carry = 0; - uint32_t sum = 0; - - for (int i = 15; i >= 0; i--) { - sum = a[i] - (b[i] + carry); - - // check to see if anything was borrowed from next byte - if (a[i] < (b[i] + carry)) { - sum += 0x100; - carry = 1; - } - else { - carry = 0; - } - - // set value - out[i] = sum & 0xff; - } -} - -void n128_xor(const uint8_t *a, const uint8_t *b, uint8_t *out) -{ - for (int i = 0; i < 16; i++) { - out[i] = a[i] ^ b[i]; - } -} - -// keygen algorithm -void ctr_aes_keygen(const uint8_t *x, const uint8_t *y, uint8_t *key) -{ - static const uint8_t KEYGEN_CONST[16] = { 0x1F, 0xF9, 0xE9, 0xAA, 0xC5, 0xFE, 0x04, 0x08, 0x02, 0x45, 0x91, 0xDC, 0x5D, 0x52, 0x76, 0x8A }; - - // overall algo: - // key = (((x <<< 2) ^ y) + KEYGEN_CONST) >>> 41 - uint8_t x_rot[16], key_xy[16], key_xyc[16]; - - // x_rot = x <<< 2 - n128_lrot(x, 2, x_rot); - - // key_xy = x_rot ^ y - n128_xor(x_rot, y, key_xy); - - // key_xyc = key_xy + KEYGEN_CONST - n128_add(key_xy, KEYGEN_CONST, key_xyc); - - n128_rrot(key_xyc, 41, key); -} \ No newline at end of file diff --git a/ctrtool/aes_keygen.h b/ctrtool/aes_keygen.h deleted file mode 100644 index dae87a3c..00000000 --- a/ctrtool/aes_keygen.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include - -/* - AES Key generator for the Nintendo 3DS (CTR) Consoles -*/ - -void ctr_aes_keygen(const uint8_t *x, const uint8_t *y, uint8_t *key); diff --git a/ctrtool/cia.c b/ctrtool/cia.c deleted file mode 100644 index 85b16b89..00000000 --- a/ctrtool/cia.c +++ /dev/null @@ -1,317 +0,0 @@ -#include -#include -#include -#include "types.h" -#include "utils.h" -#include "cia.h" -#include - - -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, u64 offset) -{ - ctx->offset = offset; -} - -void cia_set_size(cia_context* ctx, u64 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) -{ - u64 offset; - u64 size; - u16 contentflags; - u16 contentindex; - u8 docrypto; - filepath* path = 0; - ctr_tmd_body *body; - ctr_tmd_contentchunk *chunk; - unsigned 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++) { - contentflags = getbe16(chunk->type); - contentindex = getbe16(chunk->index); - docrypto = (contentflags & 1) && !(flags & PlainFlag); - - if(ctx->header.contentindex[contentindex >> 3] & (0x80 >> (contentindex & 7))) { - sprintf(tmpname, "%s.%04x.%08x", path->pathname, contentindex, getbe32(chunk->id)); - fprintf(stdout, "Saving content #%04x to %s\n", contentindex, tmpname); - - if(docrypto) // Decrypt if needed - { - ctx->iv[0] = (contentindex >> 8) & 0xff; - ctx->iv[1] = contentindex & 0xff; - - ctr_init_cbc_decrypt(&ctx->aes, ctx->titlekey, ctx->iv); - } - - cia_save_blob(ctx, tmpname, offset, getbe64(chunk->size) & 0xffffffff, docrypto); - - 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, u64 offset, u64 size, int do_cbc) -{ - FILE *fout = 0; - u8 buffer[16*1024]; - - fseeko64(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 = (u32) 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) -{ - fseeko64(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 = getle64(ctx->header.contentsize); - ctx->sizemeta = getle32(ctx->header.metasize); - - ctx->offsetcerts = align(ctx->sizeheader, 64); - ctx->offsettik = align((u32) (ctx->offsetcerts + ctx->sizecert), 64); - ctx->offsettmd = align((u32) (ctx->offsettik + ctx->sizetik), 64); - ctx->offsetcontent = align((u32) (ctx->offsettmd + ctx->sizetmd), 64); - ctx->offsetmeta = align64(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 (tik_get_titlekey(&ctx->tik)) - memcpy(ctx->titlekey, tik_get_titlekey(&ctx->tik), 16); - else if (settings_get_title_key(ctx->usersettings)) - memcpy(ctx->titlekey, settings_get_title_key(ctx->usersettings), 16); - - 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 & ~InfoFlag)); - - if (actions & VerifyFlag) - { - cia_verify_contents(ctx, actions); - } - - 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, u32 actions) -{ - u16 contentflags; - u16 contentindex; - ctr_tmd_body *body; - ctr_tmd_contentchunk *chunk; - u8 *verify_buf; - u32 content_size=0; - unsigned 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)); - - fseeko64(ctx->file, ctx->offset + ctx->offsetcontent, SEEK_SET); - for(i = 0; i < getbe16(body->contentcount); i++) - { - contentindex = getbe16(chunk->index); - - if(ctx->header.contentindex[contentindex >> 3] & (0x80 >> (contentindex & 7))) - { - content_size = getbe64(chunk->size) & 0xffffffff; - - contentflags = getbe16(chunk->type); - - verify_buf = malloc(content_size); - fread(verify_buf, content_size, 1, ctx->file); - - if(contentflags & 1 && !(actions & PlainFlag)) // Decrypt if needed - { - ctx->iv[0] = (contentindex >> 8) & 0xff; - ctx->iv[1] = contentindex & 0xff; - - ctr_init_cbc_decrypt(&ctx->aes, ctx->titlekey, ctx->iv); - - 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%"PRIx64"\n", ctx->offsetcerts); - fprintf(stdout, "Certificates size: 0x%x\n", ctx->sizecert); - fprintf(stdout, "Ticket offset: 0x%"PRIx64"\n", ctx->offsettik); - fprintf(stdout, "Ticket size 0x%x\n", ctx->sizetik); - fprintf(stdout, "TMD offset: 0x%"PRIx64"\n", ctx->offsettmd); - fprintf(stdout, "TMD size: 0x%x\n", ctx->sizetmd); - fprintf(stdout, "Meta offset: 0x%"PRIx64"\n", ctx->offsetmeta); - fprintf(stdout, "Meta size: 0x%x\n", ctx->sizemeta); - fprintf(stdout, "Content offset: 0x%"PRIx64"\n", ctx->offsetcontent); - fprintf(stdout, "Content size: 0x%"PRIx64"\n", ctx->sizecontent); -} diff --git a/ctrtool/cia.h b/ctrtool/cia.h deleted file mode 100644 index 52b01521..00000000 --- a/ctrtool/cia.h +++ /dev/null @@ -1,72 +0,0 @@ -#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; - u64 offset; - u64 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; - u64 sizecontent; - u32 sizemeta; - - u64 offsetcerts; - u64 offsettik; - u64 offsettmd; - u64 offsetcontent; - u64 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, u64 offset); -void cia_set_size(cia_context* ctx, u64 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, u64 offset, u64 size, int do_cbc); -void cia_verify_contents(cia_context *ctx, u32 actions); - -#endif // _CIA_H_ diff --git a/ctrtool/ctr.c b/ctrtool/ctr.c deleted file mode 100644 index 54cfc0fa..00000000 --- a/ctrtool/ctr.c +++ /dev/null @@ -1,357 +0,0 @@ -#include -#include -#include -#include - -#include "ctr.h" -#include "utils.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 block_num ) -{ - u32 ctr[4]; - ctr[3] = getbe32(&ctx->ctr[0]); - ctr[2] = getbe32(&ctx->ctr[4]); - ctr[1] = getbe32(&ctx->ctr[8]); - ctr[0] = getbe32(&ctx->ctr[12]); - - for (u32 i = 0; i < 4; i++) { - u64 total = ctr[i] + block_num; - // if there wasn't a wrap around, add the two together and exit - if (total <= 0xffffffff) { - ctr[i] += block_num; - break; - } - - // add the difference - ctr[i] = (u32)(total - 0x100000000); - // carry to next word - block_num = (u32)(total >> 32); - } - - putbe32(ctx->ctr + 0x00, ctr[3]); - putbe32(ctx->ctr + 0x04, ctr[2]); - putbe32(ctx->ctr + 0x08, ctr[1]); - putbe32(ctx->ctr + 0x0C, ctr[0]); -} - -void ctr_set_counter( ctr_aes_context* ctx, - u8 ctr[16] ) -{ - memcpy(ctx->ctr, ctr, 16); -} - - -void ctr_init_key(ctr_aes_context* ctx, - u8 key[16]) -{ - aes_setkey_enc(&ctx->aes, key, 128); -} - -void ctr_init_counter( ctr_aes_context* ctx, - u8 ctr[16] ) -{ - 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 deleted file mode 100644 index 48b61e55..00000000 --- a/ctrtool/ctr.h +++ /dev/null @@ -1,149 +0,0 @@ -#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_EXEFS, - 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 block_num ); - -void ctr_set_counter( ctr_aes_context* ctx, - u8 ctr[16] ); - -void ctr_init_key(ctr_aes_context* ctx, - u8 key[16]); - - -void ctr_init_counter( ctr_aes_context* ctx, - 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.sln b/ctrtool/ctrtool.sln deleted file mode 100644 index ab9464c1..00000000 --- a/ctrtool/ctrtool.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ctrtool", "ctrtool.vcxproj", "{96F5CA15-30DA-4DF5-9DFF-523D58D38001}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {96F5CA15-30DA-4DF5-9DFF-523D58D38001}.Debug|Win32.ActiveCfg = Debug|Win32 - {96F5CA15-30DA-4DF5-9DFF-523D58D38001}.Debug|Win32.Build.0 = Debug|Win32 - {96F5CA15-30DA-4DF5-9DFF-523D58D38001}.Release|Win32.ActiveCfg = Release|Win32 - {96F5CA15-30DA-4DF5-9DFF-523D58D38001}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/ctrtool/ctrtool.vcproj b/ctrtool/ctrtool.vcproj deleted file mode 100644 index d3e8b0d3..00000000 --- a/ctrtool/ctrtool.vcproj +++ /dev/null @@ -1,442 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ctrtool/ctrtool.vcxproj b/ctrtool/ctrtool.vcxproj deleted file mode 100644 index 21abef9d..00000000 --- a/ctrtool/ctrtool.vcxproj +++ /dev/null @@ -1,164 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {96F5CA15-30DA-4DF5-9DFF-523D58D38001} - ctrtooltje - Win32Proj - - - - Application - v140 - Unicode - true - - - Application - v140 - Unicode - - - - - - - - - - - - - <_ProjectFileVersion>11.0.50727.1 - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - true - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - false - - - - Disabled - windows;.;%(AdditionalIncludeDirectories) - WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebugDLL - - Level3 - EditAndContinue - 4996;%(DisableSpecificWarnings) - false - - - true - Console - MachineX86 - - - - - MaxSpeed - true - windows;.;%(AdditionalIncludeDirectories) - WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreaded - true - - Level3 - ProgramDatabase - - - true - Console - true - true - MachineX86 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ctrtool/ctrtool.vcxproj.filters b/ctrtool/ctrtool.vcxproj.filters deleted file mode 100644 index 8f0b3927..00000000 --- a/ctrtool/ctrtool.vcxproj.filters +++ /dev/null @@ -1,225 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {dfec7c2d-ac92-481f-a1eb-732b0669b7d1} - - - {dd509a5e-b804-4879-b0b7-876718c9621a} - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {d16b9918-7159-4a81-a0b7-521c2b2f8a30} - - - {492dc50f-e790-426b-b991-86306ad69fd4} - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\polarssl - - - Source Files\tinyxml - - - Source Files\tinyxml - - - Source Files\tinyxml - - - Source Files\tinyxml - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files\polarssl - - - Header Files\polarssl - - - Header Files\polarssl - - - Header Files\polarssl - - - Header Files\polarssl - - - Header Files\polarssl - - - Header Files\tinyxml - - - Header Files\tinyxml - - - Header Files - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/ctrtool/cwav.c b/ctrtool/cwav.c deleted file mode 100644 index cfe39d80..00000000 --- a/ctrtool/cwav.c +++ /dev/null @@ -1,1003 +0,0 @@ -#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, u64 offset) -{ - ctx->offset = offset; -} - -void cwav_set_size(cwav_context* ctx, u64 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; - - fseeko64(ctx->file, ctx->offset, SEEK_SET); - fread(&ctx->header, 1, sizeof(cwav_header), ctx->file); - - infoheaderoffset = getle32(ctx->header.infoblockref.offset); - - fseeko64(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); - - fseeko64(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); - - fseeko64(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); - - fseeko64(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 = (u32) (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 = (u32) (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 = 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; - 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 - startoffset = 0; - } - 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 = (u32) (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 = (u32) (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 = (u32) (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 deleted file mode 100644 index 180f4b00..00000000 --- a/ctrtool/cwav.h +++ /dev/null @@ -1,205 +0,0 @@ -#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; - u64 offset; - u64 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, u64 offset); -void cwav_set_size(cwav_context* ctx, u64 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 deleted file mode 100644 index 465bc8b3..00000000 --- a/ctrtool/exefs.c +++ /dev/null @@ -1,342 +0,0 @@ -#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, u64 offset) -{ - ctx->offset = offset; -} - -void exefs_set_size(exefs_context* ctx, u64 size) -{ - ctx->size = size; -} - -void exefs_set_usersettings(exefs_context* ctx, settings* usersettings) -{ - ctx->usersettings = usersettings; -} - -void exefs_set_titleid(exefs_context* ctx, u8 titleid[8]) -{ - memcpy(ctx->titleid, titleid, 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_keys(exefs_context* ctx, u8 key0[16], u8 key1[16]) -{ - memcpy(ctx->key[0], key0, 16); - memcpy(ctx->key[1], key1, 16); -} - -void exefs_set_counter(exefs_context* ctx, u8 counter[16]) -{ - memcpy(ctx->counter, counter, 16); -} - -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; - - // determine offset/size of target - 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; - } - - // create new file - 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; - } - - // seek in source file to location of target data - fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); - - // do decryption prep - if (ctx->encrypted) - { - // setup aes counter - ctr_init_counter(&ctx->aes, ctx->counter); - ctr_add_counter(&ctx->aes, offset / 0x10); - - // setup key - if (strncmp((const char*)section->name, "icon", 8) == 0 || strncmp((const char*)section->name, "banner", 8) == 0) - ctr_init_key(&ctx->aes, ctx->key[0]); - else - ctr_init_key(&ctx->aes, ctx->key[1]); - } - - // if this is file0, and compression is set or forced: decompress section - if (index == 0 && (ctx->compressedflag || (flags & DecompressCodeFlag)) && ((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; - } - - // decrypt if required - 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: - if (fout) - fclose(fout); - free(compressedbuffer); - free(decompressedbuffer); - return; -} - -void exefs_read_header(exefs_context* ctx, u32 flags) -{ - fseeko64(ctx->file, ctx->offset, SEEK_SET); - fread(&ctx->header, 1, sizeof(exefs_header), ctx->file); - - if (ctx->encrypted) { - ctr_init_key(&ctx->aes, ctx->key[0]); - ctr_init_counter(&ctx->aes, ctx->counter); - 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_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; - - fseeko64(ctx->file, ctx->offset + offset, SEEK_SET); - if (strncmp((const char*)section->name, "icon", 8) == 0 || strncmp((const char*)section->name, "banner", 8) == 0) - ctr_init_key(&ctx->aes, ctx->key[0]); - else - ctr_init_key(&ctx->aes, ctx->key[1]); - ctr_init_counter(&ctx->aes, 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; iheader.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 deleted file mode 100644 index fd6e8f09..00000000 --- a/ctrtool/exefs.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef _EXEFS_H_ -#define _EXEFS_H_ - -#include "types.h" -#include "info.h" -#include "ctr.h" -#include "filepath.h" -#include "settings.h" - -#define EXEFS_SECTION_NUM 8 - -typedef struct -{ - u8 name[8]; - u8 offset[4]; - u8 size[4]; -} exefs_sectionheader; - - -typedef struct -{ - exefs_sectionheader section[EXEFS_SECTION_NUM]; - u8 reserved[0x80]; - u8 hashes[EXEFS_SECTION_NUM][0x20]; -} exefs_header; - -typedef struct -{ - FILE* file; - settings* usersettings; - u8 titleid[8]; - u8 counter[16]; - u8 key[2][16]; - u64 offset; - u64 size; - exefs_header header; - ctr_aes_context aes; - ctr_sha256_context sha; - int hashcheck[EXEFS_SECTION_NUM]; - 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, u64 offset); -void exefs_set_size(exefs_context* ctx, u64 size); -void exefs_set_usersettings(exefs_context* ctx, settings* usersettings); -void exefs_set_titleid(exefs_context* ctx, u8 titleid[8]); -void exefs_set_counter(exefs_context* ctx, u8 counter[16]); -void exefs_set_compressedflag(exefs_context* ctx, int compressedflag); -void exefs_set_keys(exefs_context* ctx, u8 key[16], u8 special_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 deleted file mode 100644 index 86609d84..00000000 --- a/ctrtool/exheader.c +++ /dev/null @@ -1,704 +0,0 @@ -#include -#include -#include - -#include "types.h" -#include "exheader.h" -#include "utils.h" -#include "ncch.h" -#include "syscalls.h" -#include - -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, u64 offset) -{ - ctx->offset = offset; -} - -void exheader_set_size(exheader_context* ctx, u64 size) -{ - ctx->size = size; -} - -void exheader_set_usersettings(exheader_context* ctx, settings* usersettings) -{ - ctx->usersettings = usersettings; -} - -void exheader_set_titleid(exheader_context* ctx, u8 titleid[8]) -{ - memcpy(ctx->titleid, titleid, 8); -} - -void exheader_set_programid(exheader_context* ctx, u8 programid[8]) -{ - memcpy(ctx->programid, programid, 8); -} - -void exheader_set_hash(exheader_context* ctx, u8 hash[32]) -{ - memcpy(ctx->hash, hash, 32); -} - -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_read(exheader_context* ctx, u32 actions) -{ - if (ctx->haveread == 0) - { - fseeko64(ctx->file, ctx->offset, SEEK_SET); - fread(&ctx->header, 1, sizeof(exheader_header), ctx->file); - - ctr_init_key(&ctx->aes, ctx->key); - ctr_init_counter(&ctx->aes, ctx->counter); - if (ctx->encrypted) - ctr_crypt_counter(&ctx->aes, (u8*)&ctx->header, (u8*)&ctx->header, sizeof(exheader_header)); - - ctx->haveread = 1; - } -} - -int exheader_hash_valid(exheader_context* ctx) -{ - u8 hash[32]; - ctr_sha_256((u8*)&ctx->header, 0x400, hash); - - if(memcmp(ctx->hash,hash,0x20)){ - fprintf(stderr, "Error, exheader hash mismatch. Wrong key?\n"); - return 0; - } - - return 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; -} - -void exheader_deserialise_arm11localcaps_permissions(exheader_arm11systemlocalcaps_deserialised *caps, const exheader_arm11systemlocalcaps *arm11) -{ - int i; - - memset(caps, 0, sizeof(exheader_arm11systemlocalcaps_deserialised)); - - memcpy(caps->program_id, arm11->programid, 8); - caps->core_version = getle32(arm11->coreversion); - - caps->enable_l2_cache = (arm11->flag[0] >> 0) & 1; - caps->new3ds_cpu_speed = (arm11->flag[0] >> 1) & 1; - caps->new3ds_systemmode = (arm11->flag[1] >> 0) & 15; - - caps->ideal_processor = (arm11->flag[2] >> 0) & 3; - caps->affinity_mask = (arm11->flag[2] >> 2) & 3; - caps->old3ds_systemmode = (arm11->flag[2] >> 4) & 15; - - caps->priority = (s8)arm11->flag[3]; - - // storage info - if (arm11->storageinfo.otherattributes & 2) { - caps->extdata_id = 0; - for (i = 0; i < 3; i++) - caps->other_user_saveid[i] = 0; - caps->use_other_variation_savedata = 0; - - for (i = 0; i < 3; i++) - caps->accessible_saveid[i] = 0xfffff & (getle64(arm11->storageinfo.accessibleuniqueids) >> 20 * (2 - i)); - for (i = 0; i < 3; i++) - caps->accessible_saveid[i+3] = 0xfffff & (getle64(arm11->storageinfo.extsavedataid) >> 20 * (2 - i)); - } - else { - caps->extdata_id = getle64(arm11->storageinfo.extsavedataid); - for (i = 0; i < 3; i++) - caps->other_user_saveid[i] = 0xfffff & (getle64(arm11->storageinfo.accessibleuniqueids) >> 20 * (2 - i)); - caps->use_other_variation_savedata = (getle64(arm11->storageinfo.accessibleuniqueids) >> 60) & 1; - - for (i = 0; i < 6; i++) - caps->accessible_saveid[i] = 0; - } - - caps->system_saveid[0] = getle32(arm11->storageinfo.systemsavedataid); - caps->system_saveid[1] = getle32(arm11->storageinfo.systemsavedataid + 4); - caps->accessinfo = getle64(arm11->storageinfo.accessinfo) & ~((u64)0xff00000000000000); - - // Service Access Control - for (i = 0; i < 34; i++) - strncpy(caps->service_access_control[i], (char*)arm11->serviceaccesscontrol[i], 8); - - caps->resource_limit_category = arm11->resourcelimitcategory; -} - -int exheader_process(exheader_context* ctx, u32 actions) -{ - exheader_read(ctx, actions); - - if (ctx->header.codesetinfo.flags.flag & 1) - ctx->compressedflag = 1; - - exheader_deserialise_arm11localcaps_permissions(&ctx->system_local_caps, &ctx->header.arm11systemlocalcaps); - - if (actions & VerifyFlag) - exheader_verify(ctx); - - if (actions & InfoFlag) - exheader_print(ctx, actions); - - 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, u32 actions) -{ - 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"); - fprintf(stdout, " > Access Core 2: %s\n", (descriptor&(1<<13))?"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: "); - if(!(actions & ShowSyscallsFlag)) - { - for(i=0; i<8; i++) - { - for(j=0; j<24; j++) - { - svcmask = systemcallmask[i]; - - if (svcmask & (1< 0x%02X %s\n", svcid, svcname); - } - } - } - } - - fprintf(stdout, "Allowed interrupts: "); - for(i=0; i<0x7F; i++) - { - if (interrupt[i]) - { - if (interruptcount == 0) - { - fprintf(stdout, "0x%02X", i); - } - else if ( (interruptcount & 7) == 0) - { - fprintf(stdout, " "); - fprintf(stdout, "0x%02X", i); - } - else - { - fprintf(stdout, ", 0x%02X", i); - } - - interruptcount++; - if ( (interruptcount & 7) == 0) - { - fprintf(stdout, "\n"); - } - } - } - if (interruptcount & 7) - fprintf(stdout, "\n"); - if (interruptcount == 0) - fprintf(stdout, "none\n"); - - for(i=0; i<28; i++) - { - unsigned int descriptor = getle32(ctx->header.arm11kernelcaps.descriptors[i]); - - if (unknowndescriptor[i]) - fprintf(stdout, "Unknown descriptor: %08X\n", descriptor); - } -} - -char* exheader_print_accessinfobit(u32 bit, char *str) -{ - switch(bit) - { - case 0 : - sprintf(str,"Category System Application"); - break; - case 1 : - sprintf(str,"Category Hardware Check"); - break; - case 2 : - sprintf(str,"Category File System Tool"); - break; - case 3 : - sprintf(str,"Debug"); - break; - case 4 : - sprintf(str,"TWL Card Backup"); - break; - case 5 : - sprintf(str,"TWL Nand Data"); - break; - case 6 : - sprintf(str,"BOSS"); - break; - case 7 : - sprintf(str,"Direct SDMC"); - break; - case 8 : - sprintf(str,"Core"); - break; - case 9 : - sprintf(str,"CTR NAND RO"); - break; - case 10 : - sprintf(str,"CTR NAND RW"); - break; - case 11 : - sprintf(str,"CTR NAND RO (Write Access)"); - break; - case 12 : - sprintf(str,"Category System Settings"); - break; - case 13 : - sprintf(str,"CARD BOARD"); - break; - case 14 : - sprintf(str,"Export Import IVS"); - break; - case 15 : - sprintf(str,"Direct SDMC (Write Only)"); - break; - case 16 : - sprintf(str,"Switch Cleanup"); - break; - case 17 : - sprintf(str,"Save Data Move"); - break; - case 18 : - sprintf(str,"Shop"); - break; - case 19 : - sprintf(str,"Shell"); - break; - case 20 : - sprintf(str,"Category HomeMenu"); - break; - case 21 : - sprintf(str,"Seed DB"); - break; - default : - sprintf(str,"Bit %d (unknown)",bit); - break; - } - - return str; -} - -void exheader_print_arm11accessinfo(exheader_context* ctx) -{ - char str[100]; - u64 i, bit; - for(i = 0; i < 56; i++) - { - bit = ((u64)1 << i); - if((ctx->system_local_caps.accessinfo & bit) == bit) - fprintf(stdout, " > %s\n",exheader_print_accessinfobit((u32)i,str)); - } -} - -void exheader_print_arm11storageinfo(exheader_context* ctx) -{ - u32 i; - - fprintf(stdout, "Ext savedata id: 0x%"PRIx64"\n",ctx->system_local_caps.extdata_id); - for(i = 0; i < 2; i++) - fprintf(stdout, "System savedata id %d: 0x%x %s\n",i+1, ctx->system_local_caps.system_saveid[i],exheader_getvalidstring(ctx->validsystemsaveID[i])); - for(i = 0; i < 3; i++) - fprintf(stdout, "OtherUserSaveDataId%d: 0x%x\n",i+1, ctx->system_local_caps.other_user_saveid[i]); - fprintf(stdout, "Accessible Savedata Ids:\n"); - for(i = 0; i < 6; i++) - { - if(ctx->system_local_caps.accessible_saveid[i] != 0x00000) - fprintf(stdout, " > 0x%05x\n", ctx->system_local_caps.accessible_saveid[i]); - } - - fprintf(stdout, "Other Variation Saves: %s\n", ctx->system_local_caps.use_other_variation_savedata ? "Accessible" : "Inaccessible"); - fprintf(stdout, "Access info: 0x%"PRIx64" %s\n", ctx->system_local_caps.accessinfo,exheader_getvalidstring(ctx->validaccessinfo)); - exheader_print_arm11accessinfo(ctx); -} - -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, j; - exheader_arm11systemlocalcaps_deserialised accessdesc; - - exheader_deserialise_arm11localcaps_permissions(&accessdesc, &ctx->header.accessdesc.arm11systemlocalcaps); - - ctx->validsystemsaveID[0] = Good; - ctx->validsystemsaveID[1] = Good; - ctx->validaccessinfo = Good; - ctx->validcoreversion = Good; - ctx->validprogramid = Good; - ctx->validpriority = Good; - ctx->validaffinitymask = Good; - ctx->valididealprocessor = Good; - ctx->validold3dssystemmode = Good; - ctx->validnew3dssystemmode = Good; - ctx->validenablel2cache = Good; - ctx->validnew3dscpuspeed = Good; - ctx->validservicecontrol = Good; - - for(i=0; i<8; i++) - { - if (ctx->system_local_caps.program_id[i] == accessdesc.program_id[i] || accessdesc.program_id[i] == 0xFF) - continue; - ctx->validprogramid = Fail; - break; - } - - if (ctx->system_local_caps.core_version != accessdesc.core_version) - ctx->validcoreversion = Fail; - - if (ctx->system_local_caps.priority < accessdesc.priority) - ctx->validpriority = Fail; - - if((1<system_local_caps.ideal_processor & accessdesc.ideal_processor) == 0) - ctx->valididealprocessor = Fail; - - if (ctx->system_local_caps.affinity_mask & ~accessdesc.affinity_mask) - ctx->validaffinitymask = Fail; - - if (ctx->system_local_caps.old3ds_systemmode > accessdesc.old3ds_systemmode) - ctx->validold3dssystemmode = Fail; - - if (ctx->system_local_caps.new3ds_systemmode > accessdesc.new3ds_systemmode) - ctx->validnew3dssystemmode = Fail; - - if (ctx->system_local_caps.enable_l2_cache != accessdesc.enable_l2_cache) - ctx->validenablel2cache = Fail; - - if (ctx->system_local_caps.new3ds_cpu_speed != accessdesc.new3ds_cpu_speed) - ctx->validnew3dscpuspeed = Fail; - - - - - // Storage Info Verify - if(ctx->system_local_caps.system_saveid[0] & ~accessdesc.system_saveid[0]) - ctx->validsystemsaveID[0] = Fail; - if(ctx->system_local_caps.system_saveid[1] & ~accessdesc.system_saveid[1]) - ctx->validsystemsaveID[1] = Fail; - - - if (ctx->system_local_caps.accessinfo & ~accessdesc.accessinfo) - ctx->validaccessinfo = Fail; - - // Service Access Control - for (i = 0; i < 34; i++) { - if (strlen(ctx->system_local_caps.service_access_control[i]) == 0) - continue; - - for (j = 0; j < 34; j++) { - if (strcmp(ctx->system_local_caps.service_access_control[i], accessdesc.service_access_control[j]) == 0) - break; - } - - if (strcmp(ctx->system_local_caps.service_access_control[i], accessdesc.service_access_control[j]) == 0) - continue; - - ctx->validservicecontrol = 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)"; -} - -const char* exheader_getsystemmodestring(u8 systemmode) -{ - switch (systemmode) - { - case (sysmode_64MB) : - return "64MB"; - case (sysmode_96MB) : - return "96MB"; - case (sysmode_80MB) : - return "80MB"; - case (sysmode_72MB) : - return "72MB"; - case (sysmode_32MB) : - return "32MB"; - default: - return "Unknown"; - } -} - -const char* exheader_getsystemmodeextstring(u8 systemmodeext, u8 systemmode) -{ - switch (systemmodeext) - { - case (sysmode_ext_LEGACY) : - return exheader_getsystemmodestring(systemmode); - case (sysmode_ext_124MB) : - return "124MB"; - case (sysmode_ext_178MB) : - return "178MB"; - default: - return "124MB"; - } -} - - -void exheader_print(exheader_context* ctx, u32 actions) -{ - u32 i; - u64 savedatasize = getle64(ctx->header.systeminfo.savedatasize); - exheader_codesetinfo* codesetinfo = &ctx->header.codesetinfo; - - - 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); - printf("\n"); - memdump(stdout, "NCCH Hdr RSA Modulus: ", ctx->header.accessdesc.ncchpubkeymodulus, 0x100); - fprintf(stdout, "Name: %.8s\n", codesetinfo->name); - fprintf(stdout, "Flag: %02X ", codesetinfo->flags.flag); - if (codesetinfo->flags.flag & 1) - fprintf(stdout, "[compressed]"); - if (codesetinfo->flags.flag & 2) - fprintf(stdout, "[sd app]"); - 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: %016"PRIx64"\n", getle64(ctx->header.deplist.programid[i])); - } - if(savedatasize < sizeKB) - fprintf(stdout, "Savedata size: 0x%"PRIx64"\n", savedatasize); - else if(savedatasize < sizeMB) - fprintf(stdout, "Savedata size: %"PRIu64"K\n", savedatasize/sizeKB); - else - fprintf(stdout, "Savedata size: %"PRIu64"M\n", savedatasize/sizeMB); - fprintf(stdout, "Jump id: %016"PRIx64"\n", getle64(ctx->header.systeminfo.jumpid)); - - fprintf(stdout, "Program id: %016"PRIx64" %s\n", getle64(ctx->header.arm11systemlocalcaps.programid), exheader_getvalidstring(ctx->validprogramid)); - fprintf(stdout, "Core version: 0x%X\n", getle32(ctx->header.arm11systemlocalcaps.coreversion)); - fprintf(stdout, "System mode: %s %s\n", exheader_getsystemmodestring(ctx->system_local_caps.old3ds_systemmode), exheader_getvalidstring(ctx->validold3dssystemmode)); - fprintf(stdout, "System mode (New3DS): %s %s\n", exheader_getsystemmodeextstring(ctx->system_local_caps.new3ds_systemmode, ctx->system_local_caps.old3ds_systemmode), exheader_getvalidstring(ctx->validnew3dssystemmode)); - fprintf(stdout, "CPU Speed (New3DS): %s %s\n", ctx->system_local_caps.new3ds_cpu_speed? "804MHz" : "268MHz", exheader_getvalidstring(ctx->validnew3dscpuspeed)); - fprintf(stdout, "Enable L2 Cache: %s %s\n", ctx->system_local_caps.enable_l2_cache ? "YES" : "NO", exheader_getvalidstring(ctx->validnew3dscpuspeed)); - fprintf(stdout, "Ideal processor: %d %s\n", ctx->system_local_caps.ideal_processor, exheader_getvalidstring(ctx->valididealprocessor)); - fprintf(stdout, "Affinity mask: %d %s\n", ctx->system_local_caps.affinity_mask, exheader_getvalidstring(ctx->validaffinitymask)); - fprintf(stdout, "Main thread priority: %d %s\n", ctx->system_local_caps.priority, exheader_getvalidstring(ctx->validpriority)); - // print resource limit descriptor too? currently mostly zeroes... - exheader_print_arm11storageinfo(ctx); - exheader_print_arm11kernelcapabilities(ctx, actions); - exheader_print_arm9accesscontrol(ctx); - - fprintf(stdout, "Service access: %s\n", exheader_getvalidstring(ctx->validservicecontrol)); - for(i=0; i<34; i++) - { - if (strlen(ctx->system_local_caps.service_access_control[i]) > 0) - fprintf(stdout, " > %s\n", ctx->system_local_caps.service_access_control[i]); - } - fprintf(stdout, "Reslimit category: %02X\n", ctx->header.arm11systemlocalcaps.resourcelimitcategory); -} diff --git a/ctrtool/exheader.h b/ctrtool/exheader.h deleted file mode 100644 index 7f4ce496..00000000 --- a/ctrtool/exheader.h +++ /dev/null @@ -1,202 +0,0 @@ -#ifndef _EXHEADER_H_ -#define _EXHEADER_H_ - -#include -#include "types.h" -#include "ctr.h" -#include "settings.h" - -typedef enum -{ - sysmode_64MB, - sysmode_UNK, - sysmode_96MB, - sysmode_80MB, - sysmode_72MB, - sysmode_32MB, -} exheader_systemmode; - -typedef enum -{ - sysmode_ext_LEGACY, - sysmode_ext_124MB, - sysmode_ext_178MB, -} exheader_systemmodeext; - -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[8]; - u8 jumpid[8]; - u8 reserved2[0x30]; -} exheader_systeminfo; - -typedef struct -{ - u8 extsavedataid[8]; - u8 systemsavedataid[8]; - u8 accessibleuniqueids[8]; - u8 accessinfo[7]; - u8 otherattributes; -} exheader_storageinfo; - -typedef struct -{ - u8 programid[8]; - u8 coreversion[4]; - u8 flag[4]; - u8 resourcelimitdescriptor[0x10][2]; - exheader_storageinfo storageinfo; - u8 serviceaccesscontrol[34][8]; - u8 reserved[0xf]; - u8 resourcelimitcategory; -} exheader_arm11systemlocalcaps; - -typedef struct -{ - u8 program_id[8]; - u32 core_version; - - // flag - u8 enable_l2_cache; - u8 new3ds_cpu_speed; - u8 new3ds_systemmode; - u8 ideal_processor; - u8 affinity_mask; - u8 old3ds_systemmode; - s8 priority; - - // storageinfo - u64 extdata_id; - u32 other_user_saveid[3]; - u8 use_other_variation_savedata; - u32 accessible_saveid[6]; - u32 system_saveid[2]; - u64 accessinfo; - - - char service_access_control[34][10]; - u8 resource_limit_category; -} exheader_arm11systemlocalcaps_deserialised; - -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 titleid[8]; - u8 programid[8]; - u8 hash[32]; - u8 counter[16]; - u8 key[16]; - u64 offset; - u64 size; - exheader_header header; - - exheader_arm11systemlocalcaps_deserialised system_local_caps; - - ctr_aes_context aes; - ctr_rsa_context rsa; - int compressedflag; - int encrypted; - int validprogramid; - int validpriority; - int validaffinitymask; - int valididealprocessor; - int validold3dssystemmode; - int validnew3dssystemmode; - int validenablel2cache; - int validnew3dscpuspeed; - int validcoreversion; - int validsystemsaveID[2]; - int validaccessinfo; - int validservicecontrol; - 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, u64 offset); -void exheader_set_size(exheader_context* ctx, u64 size); -void exheader_set_titleid(exheader_context* ctx, u8 titleid[8]); -void exheader_set_counter(exheader_context* ctx, u8 counter[16]); -void exheader_set_programid(exheader_context* ctx, u8 programid[8]); -void exheader_set_hash(exheader_context* ctx, u8 hash[32]); -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); -const char* exheader_getvalidstring(int valid); -void exheader_print(exheader_context* ctx, u32 actions); -void exheader_verify(exheader_context* ctx); -int exheader_hash_valid(exheader_context* ctx); -int exheader_programid_valid(exheader_context* ctx); - -#endif // _EXHEADER_H_ diff --git a/ctrtool/filepath.c b/ctrtool/filepath.c deleted file mode 100644 index 366bb65b..00000000 --- a/ctrtool/filepath.c +++ /dev/null @@ -1,90 +0,0 @@ -#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++] = (char) 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 deleted file mode 100644 index 9f6d72c0..00000000 --- a/ctrtool/filepath.h +++ /dev/null @@ -1,20 +0,0 @@ -#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 deleted file mode 100644 index 491f4a7e..00000000 --- a/ctrtool/firm.c +++ /dev/null @@ -1,257 +0,0 @@ -#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, u64 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; - } - - - - fseeko64(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; - - fseeko64(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; - - fseeko64(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 copyMethod; - u32 offset; - u32 size; - u32 priority = getle32(ctx->header.priority); - 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, "Priority: %u\n", priority); - 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); - copyMethod = getle32(section->copyMethod); - - if (size) - { - fprintf(stdout, "Section %d \n", i); - fprintf(stdout, " Copy Method: %s\n", copyMethod==0 ? "NDMA" : copyMethod==1 ? "XDMA" : - copyMethod==2 ? "memcpy" : "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 deleted file mode 100644 index 5d1ebbcf..00000000 --- a/ctrtool/firm.h +++ /dev/null @@ -1,57 +0,0 @@ -#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 copyMethod[4]; - u8 hash[32]; -} firm_sectionheader; - - - - -typedef struct -{ - u8 magic[4]; - u8 priority[4]; - u8 entrypointarm11[4]; - u8 entrypointarm9[4]; - u8 reserved1[0x30]; - firm_sectionheader section[4]; - u8 signature[0x100]; -} firm_header; - -typedef struct -{ - FILE* file; - settings* usersettings; - u64 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, u64 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 deleted file mode 100644 index fac4117c..00000000 --- a/ctrtool/info.h +++ /dev/null @@ -1,16 +0,0 @@ -#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 deleted file mode 100644 index 4a77a676..00000000 --- a/ctrtool/ivfc.c +++ /dev/null @@ -1,247 +0,0 @@ -#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, u64 offset) -{ - ctx->offset = offset; -} - -void ivfc_set_size(ivfc_context* ctx, u64 size) -{ - ctx->size = size; -} - -void ivfc_set_file(ivfc_context* ctx, FILE* file) -{ - ctx->file = file; -} - -void ivfc_set_encrypted(ivfc_context* ctx, u32 encrypted) -{ - ctx->encrypted = encrypted; -} - -void ivfc_set_key(ivfc_context* ctx, u8 key[16]) -{ - ctr_init_key(&ctx->aes, key); -} - -void ivfc_set_counter(ivfc_context* ctx, u8 counter[16]) -{ - memcpy(ctx->counter, counter, 16); -} - -void ivfc_fseek(ivfc_context* ctx, u64 offset) -{ - u64 data_pos = offset - ctx->offset; - fseeko64(ctx->file, offset, SEEK_SET); - - if (ctx->encrypted) { - //printf("start fseek encrypted prep\n"); - ctr_init_counter(&ctx->aes, ctx->counter); - //printf("middle fseek encrypted prep\n"); - ctr_add_counter(&ctx->aes, (u32)(data_pos / 0x10)); - //printf("finish fseek encrypted prep\n"); - } -} - -size_t ivfc_fread(ivfc_context* ctx, void* buffer, size_t size, size_t count) -{ - size_t read; - if ((read = fread(buffer, size, count, ctx->file)) != count) { - //printf("ivfc_fread() fail\n"); - return read; - } - if (ctx->encrypted) { - ctr_crypt_counter(&ctx->aes, buffer, buffer, size*read); - } - return read; -} - - -void ivfc_process(ivfc_context* ctx, u32 actions) -{ - ivfc_fseek(ctx, ctx->offset); - ivfc_fread(ctx, &ctx->header, 1, sizeof(ivfc_header)); - - if (getle32(ctx->header.magic) != MAGIC_IVFC) - { - fprintf(stdout, "Error, IVFC segment corrupted\n"); - return; - } - - if (getle32(ctx->header.id) == 0x10000) - { - ctx->levelcount = 3; - - ctx->level[2].hashblocksize = 1 << getle32(ctx->header.level3.blocksize); - ctx->level[1].hashblocksize = 1 << getle32(ctx->header.level2.blocksize); - ctx->level[0].hashblocksize = 1 << getle32(ctx->header.level1.blocksize); - - ctx->bodyoffset = align64(IVFC_HEADER_SIZE + getle32(ctx->header.masterhashsize), ctx->level[2].hashblocksize); - ctx->bodysize = getle64(ctx->header.level3.hashdatasize); - - ctx->level[2].dataoffset = ctx->bodyoffset; - ctx->level[2].datasize = align64(ctx->bodysize, ctx->level[2].hashblocksize); - - ctx->level[0].dataoffset = ctx->level[2].dataoffset + ctx->level[2].datasize; - ctx->level[0].datasize = align64(getle64(ctx->header.level1.hashdatasize), ctx->level[0].hashblocksize); - - ctx->level[1].dataoffset = ctx->level[0].dataoffset + ctx->level[0].datasize; - ctx->level[1].datasize = align64(getle64(ctx->header.level2.hashdatasize), ctx->level[1].hashblocksize); - - ctx->level[0].hashoffset = IVFC_HEADER_SIZE; - ctx->level[1].hashoffset = ctx->level[0].dataoffset; - ctx->level[2].hashoffset = ctx->level[1].dataoffset; - } - - 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; - } - - // Import IVFC level hashes - uint8_t *levelhash[IVFC_MAX_LEVEL] = { NULL }; - - for (i=0; ilevelcount; i++) - { - blockcount = (u32)(ctx->level[i].datasize / ctx->level[i].hashblocksize); - u32 read_size = align(blockcount * 0x20, ctx->level[i].hashblocksize); - levelhash[i] = malloc(read_size); - ivfc_read(ctx, ctx->level[i].hashoffset, read_size, levelhash[i]); - } - - // Verify blocks - for (i=0; ilevelcount; i++) - { - blockcount = (u32) (ctx->level[i].datasize / ctx->level[i].hashblocksize); - if (ctx->level[i].datasize % ctx->level[i].hashblocksize != 0) - { - fprintf(stderr, "Error, IVFC block size mismatch\n"); - return; - } - - ctx->level[i].hashcheck = Good; - - for (j=0; jlevel[i].hashblocksize * j, ctx->level[i].hashblocksize, calchash); - } - // a data level - else { - ivfc_read(ctx, ctx->level[i].dataoffset + j * ctx->level[i].hashblocksize, ctx->level[i].hashblocksize, ctx->buffer); - ctr_sha_256(ctx->buffer, (u32)ctx->level[i].hashblocksize, calchash); - } - - if (memcmp(calchash, levelhash[i] + 0x20 * j, 0x20) != 0) { - ctx->level[i].hashcheck = Fail; - } - - } - } - - // Free level hashes - for (int i = 0; i < 3; i++) { - free(levelhash[i]); - } -} - -void ivfc_read(ivfc_context* ctx, u64 offset, u64 size, u8* buffer) -{ - if ( (offset > ctx->size) || (offset+size > ctx->size) ) - { - fprintf(stderr, "Error, IVFC offset out of range (offset=0x%08"PRIx64", size=0x%08"PRIx64")\n", offset, size); - return; - } - - ivfc_fseek(ctx, ctx->offset + offset); - if (size != ivfc_fread(ctx, buffer, 1, (size_t) size)) - { - fprintf(stderr, "Error, IVFC could not read file\n"); - return; - } -} - -void ivfc_hash(ivfc_context* ctx, u64 offset, u64 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, (u32) size, hash); -} - -void ivfc_print(ivfc_context* ctx) -{ - u32 i; - ivfc_header* header = &ctx->header; - - fprintf(stdout, "\nIVFC:\n"); - - fprintf(stdout, "Header: %.4s\n", header->magic); - 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%08"PRIx64"\n", ctx->offset + level->dataoffset); - fprintf(stdout, " Data size: 0x%08"PRIx64"\n", level->datasize); - fprintf(stdout, " Hash offset: 0x%08"PRIx64"\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 deleted file mode 100644 index 67b5c7e6..00000000 --- a/ctrtool/ivfc.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef __IVFC_H__ -#define __IVFC_H__ - -#include "types.h" -#include "ctr.h" -#include "settings.h" - -#define IVFC_HEADER_SIZE 0x60 -#define IVFC_MAX_LEVEL 4 -#define IVFC_MAX_BUFFERSIZE 0x4000 - -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 magic[4]; - u8 id[4]; - u8 masterhashsize[4]; - ivfc_levelheader level1; - ivfc_levelheader level2; - ivfc_levelheader level3; - u8 reserved[4]; - u8 optionalsize[4]; -} ivfc_header; - -typedef struct -{ - FILE* file; - u64 offset; - u64 size; - settings* usersettings; - u8 counter[16]; - ctr_aes_context aes; - int encrypted; - - ivfc_header header; - - 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, u64 offset); -void ivfc_set_size(ivfc_context* ctx, u64 size); -void ivfc_set_file(ivfc_context* ctx, FILE* file); -void ivfc_set_usersettings(ivfc_context* ctx, settings* usersettings); -void ivfc_set_encrypted(ivfc_context* ctx, u32 encrypted); -void ivfc_set_key(ivfc_context* ctx, u8 key[16]); -void ivfc_set_counter(ivfc_context* ctx, u8 counter[16]); -void ivfc_fseek(ivfc_context* ctx, u64 offset); -size_t ivfc_fread(ivfc_context* ctx, void* buffer, size_t size, size_t count); - -void ivfc_verify(ivfc_context* ctx, u32 flags); -void ivfc_print(ivfc_context* ctx); - -void ivfc_read(ivfc_context* ctx, u64 offset, u64 size, u8* buffer); -void ivfc_hash(ivfc_context* ctx, u64 offset, u64 size, u8* hash); - -#endif // __IVFC_H__ diff --git a/ctrtool/keyset.cpp b/ctrtool/keyset.cpp deleted file mode 100644 index b4e67633..00000000 --- a/ctrtool/keyset.cpp +++ /dev/null @@ -1,395 +0,0 @@ -#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, u32 actions) -{ - const key128 defaultkeys_retail[] = { - // fixed system key - unknown if used/correct? - {{0x52, 0x7c, 0xe6, 0x30, 0xa9, 0xca, 0x30, 0x5f, 0x36, 0x96, 0xf3, 0xcd, 0xe9, 0x54, 0x19, 0x4b}, 1}, - // NCCH 0x2c keyX - {{0xb9, 0x8e, 0x95, 0xce, 0xca, 0x3e, 0x4d, 0x17, 0x1f, 0x76, 0xa9, 0x4d, 0xe9, 0x34, 0xc0, 0x53}, 1}, - // NCCH 0x25 keyX 7.x - {{0xce, 0xe7, 0xd8, 0xab, 0x30, 0xc0, 0x0d, 0xae, 0x85, 0x0e, 0xf5, 0xe3, 0x82, 0xac, 0x5a, 0xf3}, 1}, - // NCCH 0x18 keyX N9.3 - {{0x82, 0xe9, 0xc9, 0xbe, 0xbf, 0xb8, 0xbd, 0xb8, 0x75, 0xec, 0xc0, 0xa0, 0x7d, 0x47, 0x43, 0x74}, 1}, - // NCCH 0x1B keyX N9.6 - {{0x45, 0xad, 0x04, 0x95, 0x39, 0x92, 0xc7, 0xc8, 0x93, 0x72, 0x4a, 0x9a, 0x7b, 0xce, 0x61, 0x82}, 1}, - // common key index0 (application) - {{0x64, 0xC5, 0xFD, 0x55, 0xDD, 0x3A, 0xD9, 0x88, 0x32, 0x5B, 0xAA, 0xEC, 0x52, 0x43, 0xDB, 0x98}, 1}, - // common key index1 (system) - {{0x4A, 0xAA, 0x3D, 0x0E, 0x27, 0xD4, 0xD7, 0x28, 0xD0, 0xB1, 0xB4, 0x33, 0xF0, 0xF9, 0xCB, 0xC8}, 1}, - // common key index2 - {{0xFB, 0xB0, 0xEF, 0x8C, 0xDB, 0xB0, 0xD8, 0xE4, 0x53, 0xCD, 0x99, 0x34, 0x43, 0x71, 0x69, 0x7F}, 1}, - // common key index3 - {{0x25, 0x95, 0x9B, 0x7A, 0xD0, 0x40, 0x9F, 0x72, 0x68, 0x41, 0x98, 0xBA, 0x2E, 0xCD, 0x7D, 0xC6}, 1}, - // common key index4 - {{0x7A, 0xDA, 0x22, 0xCA, 0xFF, 0xC4, 0x76, 0xCC, 0x82, 0x97, 0xA0, 0xC7, 0xCE, 0xEE, 0xEE, 0xBE}, 1}, - // common key index5 - {{0xA5, 0x05, 0x1C, 0xA1, 0xB3, 0x7D, 0xCF, 0x3A, 0xFB, 0xCF, 0x8C, 0xC1, 0xED, 0xD9, 0xCE, 0x02}, 1}, - }; - const key128 defaultkeys_dev[] = { - // fixed system key - {{0x52, 0x7c, 0xe6, 0x30, 0xa9, 0xca, 0x30, 0x5f, 0x36, 0x96, 0xf3, 0xcd, 0xe9, 0x54, 0x19, 0x4b}, 1}, - // NCCH 0x2c keyX - {{0x51, 0x02, 0x07, 0x51, 0x55, 0x07, 0xcb, 0xb1, 0x8e, 0x24, 0x3d, 0xcb, 0x85, 0xe2, 0x3a, 0x1d}, 1}, - // NCCH 0x25 keyX 7.x - {{0x81, 0x90, 0x7a, 0x4b, 0x6f, 0x1b, 0x47, 0x32, 0x3a, 0x67, 0x79, 0x74, 0xce, 0x4a, 0xd7, 0x1b}, 1}, - // NCCH 0x18 keyX N9.3 - {{0x30, 0x4b, 0xf1, 0x46, 0x83, 0x72, 0xee, 0x64, 0x11, 0x5e, 0xbd, 0x40, 0x93, 0xd8, 0x42, 0x76}, 1}, - // NCCH 0x1B keyX N9.6 - {{0x6c, 0x8b, 0x29, 0x44, 0xa0, 0x72, 0x60, 0x35, 0xf9, 0x41, 0xdf, 0xc0, 0x18, 0x52, 0x4f, 0xb6}, 1}, - // common key index0 - {{0x55, 0xA3, 0xF8, 0x72, 0xBD, 0xC8, 0x0C, 0x55, 0x5A, 0x65, 0x43, 0x81, 0x13, 0x9E, 0x15, 0x3B}, 1}, - // common key index1 - {{0x44, 0x34, 0xED, 0x14, 0x82, 0x0C, 0xA1, 0xEB, 0xAB, 0x82, 0xC1, 0x6E, 0x7B, 0xEF, 0x0C, 0x25}, 1}, - // common key index2 - {{0xF6, 0x2E, 0x3F, 0x95, 0x8E, 0x28, 0xA2, 0x1F, 0x28, 0x9E, 0xEC, 0x71, 0xA8, 0x66, 0x29, 0xDC}, 1}, - // common key index3 - {{0x2B, 0x49, 0xCB, 0x6F, 0x99, 0x98, 0xD9, 0xAD, 0x94, 0xF2, 0xED, 0xE7, 0xB5, 0xDA, 0x3E, 0x27}, 1}, - // common key index4 - {{0x75, 0x05, 0x52, 0xBF, 0xAA, 0x1C, 0x04, 0x07, 0x55, 0xC8, 0xD5, 0x9A, 0x55, 0xF9, 0xAD, 0x1F}, 1}, - // common key index5 - {{0xAA, 0xDA, 0x4C, 0xA8, 0xF6, 0xE5, 0xA9, 0x77, 0xE0, 0xA0, 0xF9, 0xE4, 0x76, 0xCF, 0x0D, 0x63}, 1}, - }; - - memset(keys, 0, sizeof(keyset)); - - if (actions & PlainFlag) - return; - - // select keyset - const key128* default_keys = (actions & DevFlag)? defaultkeys_dev : defaultkeys_retail; - - u32 key_index = 0; - // set ncch keys - memcpy(&keys->ncchfixedsystemkey, &default_keys[key_index++], sizeof(key128)); - memcpy(&keys->ncchkeyX_old, &default_keys[key_index++], sizeof(key128)); - memcpy(&keys->ncchkeyX_seven, &default_keys[key_index++], sizeof(key128)); - memcpy(&keys->ncchkeyX_ninethree, &default_keys[key_index++], sizeof(key128)); - memcpy(&keys->ncchkeyX_ninesix, &default_keys[key_index++], sizeof(key128)); - - // set common keys - for (u32 i = 0; i < COMMONKEY_NUM; i++) - { - memcpy(&keys->commonkey[i], &default_keys[key_index++], sizeof(key128)); - } - -} - -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("ncchfixedsystemkey"), &keys->ncchfixedsystemkey); - keyset_load_key128(root.FirstChild("ncchkeyxold"), &keys->ncchkeyX_old); - keyset_load_key128(root.FirstChild("ncchkeyxseven"), &keys->ncchkeyX_seven); - keyset_load_key128(root.FirstChild("ncchkeyxninethree"), &keys->ncchkeyX_ninethree); - keyset_load_key128(root.FirstChild("ncchkeyxninesix"), &keys->ncchkeyX_ninesix); - */ - - return 1; -} - - -void keyset_merge(keyset* keys, keyset* src) -{ -#define COPY_IF_VALID(v) do {\ - if (src->v.valid && !keys->v.valid)\ - keyset_set_key128(&keys->v, src->v.data);\ -} while (0) - - // todo complete key copy - /* COPY_IF_VALID(commonkey[0]); */ - //if () - for (size_t i = 0; i < COMMONKEY_NUM; i++) - { - COPY_IF_VALID(commonkey[i]); - } - COPY_IF_VALID(titlekey); - COPY_IF_VALID(seed_fallback); - COPY_IF_VALID(ncchfixedsystemkey); - COPY_IF_VALID(ncchkeyX_old); - COPY_IF_VALID(ncchkeyX_seven); - COPY_IF_VALID(ncchkeyX_ninethree); - COPY_IF_VALID(ncchkeyX_ninesix); - if (src->seed_num > 0) - { - keys->seed_num = src->seed_num; - keys->seed_db = (seeddb_entry*)calloc(keys->seed_num, sizeof(seeddb_entry)); - memcpy(keys->seed_db, src->seed_db, keys->seed_num * sizeof(seeddb_entry)); - } - -#undef COPY_IF_VALID -} - -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_parse_titlekey(keyset* keys, char* keytext, int keylen) -{ - keyset_parse_key128(&keys->titlekey, keytext, keylen); -} - -void keyset_parse_ncchkeyX_old(keyset* keys, char* keytext, int keylen) -{ - keyset_parse_key128(&keys->ncchkeyX_old, keytext, keylen); -} - -void keyset_parse_ncchfixedsystemkey(keyset* keys, char* keytext, int keylen) -{ - keyset_parse_key128(&keys->ncchfixedsystemkey, keytext, keylen); -} - -void keyset_parse_ncchkeyX_seven(keyset* keys, char* keytext, int keylen) -{ - keyset_parse_key128(&keys->ncchkeyX_seven, keytext, keylen); -} - -void keyset_parse_ncchkeyX_ninethree(keyset* keys, char* keytext, int keylen) -{ - keyset_parse_key128(&keys->ncchkeyX_ninethree, keytext, keylen); -} - -void keyset_parse_ncchkeyX_ninesix(keyset* keys, char* keytext, int keylen) -{ - keyset_parse_key128(&keys->ncchkeyX_ninesix, keytext, keylen); -} - -void keyset_parse_seeddb(keyset* keys, char* path) -{ - //keyset_parse_key128(&keys->seed, keytext, keylen); - FILE* fp = fopen(path, "rb"); - if (fp == NULL) - { - printf("[ERROR] Failed to load SeedDB (failed to open file)\n"); - return; - } - - seeddb_header hdr; - fread(&hdr, sizeof(seeddb_header), 1, fp); - - keys->seed_num = getle32(hdr.n_entries); - for (u32 i = 0; i < 0xC; i++) - { - if (hdr.padding[i] != 0x00) - { - printf("[ERROR] SeedDB is corrupt. (padding malformed)\n"); - return; - } - } - - keys->seed_db = (seeddb_entry*)calloc(keys->seed_num, sizeof(seeddb_entry)); - fread(keys->seed_db, keys->seed_num * sizeof(seeddb_entry), 1, fp); -} - -void keyset_parse_seed_fallback(keyset* keys, char* keytext, int keylen) -{ - keyset_parse_key128(&keys->seed_fallback, 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) -{ -#define DUMP_KEY(n, s) do {\ - keyset_dump_key128(&keys->n, (s));\ -} while(0) - fprintf(stdout, "Current keyset: \n"); - DUMP_KEY(ncchkeyX_old, "NCCH OLD KEYX"); - DUMP_KEY(ncchkeyX_seven, "NCCH 7.0 KEYX"); - DUMP_KEY(ncchkeyX_ninethree, "NCCH N9.3 KEYX"); - DUMP_KEY(ncchkeyX_ninesix, "NCCH N9.6 KEYX"); - DUMP_KEY(ncchfixedsystemkey, "NCCH FIXEDSYSTEMKEY"); -#undef DUMP_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 deleted file mode 100644 index 4d9235dd..00000000 --- a/ctrtool/keyset.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef _KEYSET_H_ -#define _KEYSET_H_ - -#include "types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define COMMONKEY_NUM 6 - -typedef enum -{ - KEY_ERR_LEN_MISMATCH, - KEY_ERR_INVALID_NODE, - KEY_OK -} keystatus; - -typedef enum -{ - RSAKEY_INVALID, - RSAKEY_PRIV, - RSAKEY_PUB -} rsakeytype; - -typedef struct -{ - u8 title_id[8]; - u8 seed[0x10]; - u8 padding[0x8]; -} seeddb_entry; - -typedef struct -{ - u8 n_entries[4]; - u8 padding[0xC]; -} seeddb_header; - - -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 -{ - u32 seed_num; - seeddb_entry* seed_db; - - key128 commonkey[COMMONKEY_NUM]; - key128 titlekey; - key128 seed_fallback; - key128 ncchfixedsystemkey; - key128 ncchkeyX_old; - key128 ncchkeyX_seven; - key128 ncchkeyX_ninethree; - key128 ncchkeyX_ninesix; - rsakey2048 ncsdrsakey; - rsakey2048 ncchrsakey; - rsakey2048 ncchdescrsakey; - rsakey2048 firmrsakey; -} keyset; - -void keyset_init(keyset* keys, u32 actions); -int keyset_load(keyset* keys, const char* fname, int verbose); -void keyset_merge(keyset* keys, keyset* src); -void keyset_parse_titlekey(keyset* keys, char* keytext, int keylen); -void keyset_parse_ncchkeyX_old(keyset* keys, char* keytext, int keylen); -void keyset_parse_ncchfixedsystemkey(keyset* keys, char* keytext, int keylen); -void keyset_parse_ncchkeyX_seven(keyset* keys, char* keytext, int keylen); -void keyset_parse_ncchkeyX_ninethree(keyset* keys, char* keytext, int keylen); -void keyset_parse_ncchkeyX_ninesix(keyset* keys, char* keytext, int keylen); -void keyset_parse_seeddb(keyset* keys, char* path); -void keyset_parse_seed_fallback(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 deleted file mode 100644 index 1a853a5b..00000000 --- a/ctrtool/lzss.c +++ /dev/null @@ -1,195 +0,0 @@ -#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; - - - fseeko64(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 deleted file mode 100644 index 64a5b2fc..00000000 --- a/ctrtool/lzss.h +++ /dev/null @@ -1,26 +0,0 @@ -#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 deleted file mode 100644 index 1cbc04b8..00000000 --- a/ctrtool/main.c +++ /dev/null @@ -1,488 +0,0 @@ -#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; - u64 infilesize; - settings usersettings; -} toolcontext; - -static void usage(const char *argv0) -{ - fprintf(stderr, - "CTRTOOL v0.7 (c) neimod, 3DSGuy.\n" - "Built: %s %s\n" - "\n" - "Usage: %s [options...] \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" - " -d, --dev Decrypt with development keys instead of retail.\n" - " --unitsize=size Set media unit size (default 0x200).\n" -// " --commonkey=key Set common key.\n" - " --titlekey=key Set tik title key.\n" -// " --ncchkey=key Set ncch key.\n" -// " --ncchsyskey=key Set ncch fixed system key.\n" - " --seeddb=file Set seeddb for ncch seed crypto.\n" - " --seed=key Set specific seed for ncch seed crypto.\n" - " --showkeys Show the keys being used.\n" - " --showsyscalls Show system call names instead of numbers.\n" - " -t, --intype=type Specify input file type [ncsd, ncch, exheader, cia, tmd, lzss,\n" - " firm, cwav, exefs, romfs]\n" - "LZSS options:\n" - " --lzssout=file Specify lzss output file\n" - "CXI/CCI options:\n" - " -n, --ncch=index Specify NCCH partition index.\n" - " --exheader=file Specify Extended Header file path.\n" - " --logo=file Specify Logo file path.\n" - " --plainrgn=file Specify Plain region file path\n" - " --exefs=file Specify ExeFS file path.\n" - " --exefsdir=dir Specify ExeFS directory path.\n" - " --romfs=file Specify RomFS file path.\n" - " --romfsdir=dir Specify RomFS directory path.\n" - " --listromfs List files in RomFS.\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" - "EXEFS options:\n" - " --decompresscode Decompress .code section\n" - " (only needed when using raw EXEFS file)\n" - "\n", - __TIME__, __DATE__, argv0); - exit(1); -} - - -int main(int argc, char* argv[]) -{ - toolcontext ctx; - u8 magic[4]; - char infname[512]; - int c; - u32 ncchindex = 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(&tmpkeys, 0); - - - 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}, - //{"commonkeyx", 1, NULL, 11}, - //{"ncchkeyxold", 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}, - {"logo", 1, NULL, 20}, - {"decompresscode", 0, NULL, 21}, - {"titlekey", 1, NULL, 22}, - {"plainrgn", 1, NULL, 23}, - {"showsyscalls", 0, NULL, 24}, - //{"ncchkeyxseven", 1, NULL, 25}, - //{"ncchkeyxninethree", 1, NULL, 26}, - //{"ncchkeyxninesix", 1, NULL, 27}, - {"seeddb", 1, NULL, 28}, - {"seed", 1, NULL, 29 }, - {NULL}, - }; - - c = getopt_long(argc, argv, "dryxivpk: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 'd': - ctx.actions |= DevFlag; - break; - - case 'p': - ctx.actions |= PlainFlag; - break; - - case 'r': - ctx.actions |= RawFlag; - break; - - case 'i': - ctx.actions |= InfoFlag; - break; - - case 'n': - ncchindex = 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, "exefs")) - ctx.filetype = FILETYPE_EXEFS; - 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_meta_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_commonkeyX(&tmpkeys, optarg, strlen(optarg)); break; - //case 12: keyset_parse_ncchkeyX_old(&tmpkeys, optarg, strlen(optarg)); break; - case 13: settings_set_lzss_path(&ctx.usersettings, optarg); break; - case 14: settings_set_firm_dir_path(&ctx.usersettings, optarg); break; - //case 15: keyset_parse_ncchfixedsystemkey(&tmpkeys, optarg, strlen(optarg)); break; - case 16: settings_set_wav_path(&ctx.usersettings, optarg); break; - case 17: settings_set_romfs_dir_path(&ctx.usersettings, optarg); break; - case 18: settings_set_list_romfs_files(&ctx.usersettings, 1); break; - case 19: settings_set_cwav_loopcount(&ctx.usersettings, strtoul(optarg, 0, 0)); break; - case 20: settings_set_logo_path(&ctx.usersettings, optarg); break; - case 21: ctx.actions |= DecompressCodeFlag; break; - case 22: keyset_parse_titlekey(&tmpkeys, optarg, strlen(optarg)); break; - case 23: settings_set_plainrgn_path(&ctx.usersettings, optarg); break; - case 24: ctx.actions |= ShowSyscallsFlag; break; - //case 25: keyset_parse_ncchkeyX_seven(&tmpkeys, optarg, strlen(optarg)); break; - //case 26: keyset_parse_ncchkeyX_ninethree(&tmpkeys, optarg, strlen(optarg)); break; - //case 27: keyset_parse_ncchkeyX_ninesix(&tmpkeys, optarg, strlen(optarg)); break; - case 28: keyset_parse_seeddb(&tmpkeys, optarg); break; - case 29: keyset_parse_seed_fallback(&tmpkeys, optarg, strlen(optarg)); 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_init(&ctx.usersettings.keys, ctx.actions); - 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.infilesize = _fsize(infname); - ctx.infile = fopen(infname, "rb"); - - if (ctx.infile == 0) - { - fprintf(stderr, "error: could not open input file!\n"); - return -1; - } - - if (ctx.filetype == FILETYPE_UNKNOWN) - { - fseeko64(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) - { - fseeko64(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_ncch_index(&ncsdctx, ncchindex); - 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, (u32) 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, (u32) 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, (u32) 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_EXEFS: - { - exefs_context exefsctx; - - exefs_init(&exefsctx); - exefs_set_file(&exefsctx, ctx.infile); - exefs_set_size(&exefsctx, ctx.infilesize); - exefs_set_usersettings(&exefsctx, &ctx.usersettings); - exefs_process(&exefsctx, 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_set_encrypted(&romfsctx, 0); - romfs_process(&romfsctx, ctx.actions); - - break; - } - } - - if (ctx.infile) - fclose(ctx.infile); - - return 0; -} diff --git a/ctrtool/ncch.c b/ctrtool/ncch.c deleted file mode 100644 index a5516dd8..00000000 --- a/ctrtool/ncch.c +++ /dev/null @@ -1,880 +0,0 @@ -#include -#include -#include -#include "types.h" -#include "ncch.h" -#include "utils.h" -#include "ctr.h" -#include "settings.h" -#include "aes_keygen.h" -#include - -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, u64 offset) -{ - ctx->offset = offset; -} - -void ncch_set_size(ncch_context* ctx, u64 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 = (u32) ncch_get_mediaunit_size(ctx); - u8* titleid = ctx->header.titleid; - u32 i; - u64 x = 0; - - memset(counter, 0, 16); - - if (version == 2 || version == 0) - { - for(i=0; i<8; i++) - counter[i] = titleid[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] = titleid[i]; - for(i=0; i<4; i++) - counter[12+i] = (u8) (x>>((3-i)*8)); - } -} - - -/* this is broken */ -int ncch_extract_prepare(ncch_context* ctx, u32 type, u32 flags) -{ - u64 offset = 0; - u64 size = 0; - u8 counter[16]; - - - switch(type) - { - case NCCHTYPE_EXEFS: - { - offset = ncch_get_exefs_offset(ctx); - size = ncch_get_exefs_size(ctx); - ctr_init_key(&ctx->aes, ctx->key[0]); - } - break; - - case NCCHTYPE_ROMFS: - { - offset = ncch_get_romfs_offset(ctx); - size = ncch_get_romfs_size(ctx); - ctr_init_key(&ctx->aes, ctx->key[1]); - } - break; - - case NCCHTYPE_EXHEADER: - { - offset = ncch_get_exheader_offset(ctx); - size = ncch_get_exheader_size(ctx) * 2; - ctr_init_key(&ctx->aes, ctx->key[0]); - } - break; - - case NCCHTYPE_LOGO: - { - offset = ncch_get_logo_offset(ctx); - size = ncch_get_logo_size(ctx); - } - break; - - case NCCHTYPE_PLAINRGN: - { - offset = ncch_get_plainrgn_offset(ctx); - size = ncch_get_plainrgn_size(ctx); - } - break; - - default: - { - fprintf(stderr, "Error invalid NCCH type\n"); - goto clean; - } - break; - } - - ctx->extractsize = size; - ctx->extractflags = flags; - fseeko64(ctx->file, offset, SEEK_SET); - ncch_get_counter(ctx, counter, type); - - ctr_init_counter(&ctx->aes, counter); - - return 1; - -clean: - return 0; -} - -int ncch_extract_buffer(ncch_context* ctx, u8* buffer, u32 buffersize, u32* outsize, u8 nocrypto) -{ - u32 read_len = buffersize; - - if (read_len > ctx->extractsize) - read_len = (u32) ctx->extractsize; - - *outsize = read_len; - - if (ctx->extractsize) - { - if (read_len != fread(buffer, 1, read_len, ctx->file)) - { - fprintf(stdout, "Error reading input file\n"); - goto clean; - } - - if (ctx->encrypted && !nocrypto) - ctr_crypt_counter(&ctx->aes, buffer, buffer, read_len); - - ctx->extractsize -= read_len; - } - - 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]; - exefs_header exefs_hdr; - - - 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; - case NCCHTYPE_LOGO: path = settings_get_logo_path(ctx->usersettings); break; - case NCCHTYPE_PLAINRGN: path = settings_get_plainrgn_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; - case NCCHTYPE_LOGO: fprintf(stdout, "Saving Logo...\n"); break; - case NCCHTYPE_PLAINRGN: fprintf(stdout, "Saving Plain Region...\n"); break; - } - - // special crypto considerations for exefs when two keys are used - if (type == NCCHTYPE_EXEFS && ctx->header.flags[3] > 0 && ctx->encrypted) - { - u32 read_len; - - // read header - if (0 == ncch_extract_buffer(ctx, (u8*)&exefs_hdr, sizeof(exefs_hdr), &read_len, 0)) - goto clean; - - if (read_len != fwrite(&exefs_hdr, 1, read_len, fout)) - { - fprintf(stdout, "Error writing output file\n"); - goto clean; - } - - for (int i = 0; i < 8; i++) - { - - // get section size - u32 section_size = getle32(exefs_hdr.section[i].size); - u32 section_padding = align(section_size, 0x200)-section_size; - - // skip empty sections - if (section_size == 0) - continue; - - // select correct key - if (strncmp((char*)exefs_hdr.section[i].name, "icon", 8) == 0 || strncmp((char*)exefs_hdr.section[i].name, "banner", 8) == 0) - ctr_init_key(&ctx->aes, ctx->key[0]); - else - ctr_init_key(&ctx->aes, ctx->key[1]); - - // set counter - u8 ctr[16]; - ncch_get_counter(ctx, ctr, NCCHTYPE_EXEFS); - ctr_init_counter(&ctx->aes, ctr); - ctr_add_counter(&ctx->aes, (getle32(exefs_hdr.section[i].offset) + sizeof(exefs_header)) / 0x10); - - // extract data - while (section_size > 0) - { - read_len = sizeof(buffer); - if (read_len > section_size) - read_len = section_size; - - - if (read_len != fread(buffer, 1, read_len, ctx->file)) - { - fprintf(stdout, "Error reading input file\n"); - goto clean; - } - - ctr_crypt_counter(&ctx->aes, buffer, buffer, read_len); - - if (read_len != fwrite(buffer, 1, read_len, fout)) - { - fprintf(stdout, "Error writing output file\n"); - goto clean; - } - - - section_size -= read_len; - } - - // skip the padding - if (section_padding) - { - fseeko64(ctx->file, section_padding, SEEK_CUR); - memset(buffer, 0, section_padding); - if (section_padding != fwrite(buffer, 1, section_padding, fout)) - { - fprintf(stdout, "Error writing output file\n"); - goto clean; - } - - } - - ctx->extractsize -= section_size + section_padding; - } - } - else - { - while (1) - { - u32 read_len; - - if (0 == ncch_extract_buffer(ctx, buffer, sizeof(buffer), &read_len, (type == NCCHTYPE_LOGO || type == NCCHTYPE_PLAINRGN))) - goto clean; - - if (read_len == 0) - break; - - if (read_len != fwrite(buffer, 1, read_len, 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 = (u32) 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); - u32 logohashregionsize = getle32(ctx->header.logosize) * mediaunitsize; - u8* exefshashregion = 0; - u8* romfshashregion = 0; - u8* exheaderhashregion = 0; - u8* logohashregion = 0; - u8* tmphash = 0; - rsakey2048 ncchrsakey; - - if (exefshashregionsize >= SIZE_128MB || romfshashregionsize >= SIZE_128MB || exheaderhashregionsize >= SIZE_128MB || logohashregionsize >= SIZE_128MB) - goto clean; - - exefshashregion = malloc(exefshashregionsize); - romfshashregion = malloc(romfshashregionsize); - exheaderhashregion = malloc(exheaderhashregionsize); - logohashregion = malloc(logohashregionsize); - - - 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,0)) - 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,0)) - 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,0)) - goto clean; - ctx->exheaderhashcheck = ctr_sha_256_verify(exheaderhashregion, exheaderhashregionsize, ctx->header.extendedheaderhash); - } - if (logohashregionsize) - { - if (0 == ncch_extract_prepare(ctx, NCCHTYPE_LOGO, flags)) - goto clean; - if (0 == ncch_extract_buffer(ctx, logohashregion, logohashregionsize, &logohashregionsize,1)) - goto clean; - ctx->logohashcheck = ctr_sha_256_verify(logohashregion, logohashregionsize, ctx->header.logohash); - } - - - free(exefshashregion); - free(romfshashregion); - free(exheaderhashregion); - free(logohashregion); -clean: - return; -} - - -void ncch_process(ncch_context* ctx, u32 actions) -{ - u8 exheadercounter[16]; - u8 exefscounter[16]; - u8 romfscounter[16]; - int result = 1; - - - fseeko64(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; - } - - const u32 exheaderSize = getle32(ctx->header.extendedheadersize); - if (exheaderSize != 0x400 && exheaderSize != 0) - { - fprintf(stdout, "Error, exheader is 0x%02x bytes long, expected 0x400 or 0\n", getle32(ctx->header.extendedheadersize)); - return; - } - - ncch_determine_key(ctx, actions); - - ncch_get_counter(ctx, exheadercounter, NCCHTYPE_EXHEADER); - ncch_get_counter(ctx, exefscounter, NCCHTYPE_EXEFS); - ncch_get_counter(ctx, romfscounter, NCCHTYPE_ROMFS); - - if (actions & ShowKeysFlag) - { - fprintf(stdout, "Counter(s):\n"); - memdump(stdout, " exheader: ", exheadercounter, 0x10); - memdump(stdout, " ExeFS: ", exefscounter, 0x10); - memdump(stdout, " RomFS: ", romfscounter, 0x10); - } - - - 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_titleid(&ctx->exheader, ctx->header.titleid); - exheader_set_programid(&ctx->exheader, ctx->header.programid); - exheader_set_hash(&ctx->exheader, ctx->header.extendedheaderhash); - exheader_set_counter(&ctx->exheader, exheadercounter); - exheader_set_key(&ctx->exheader, ctx->key[0]); - 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_titleid(&ctx->exefs, ctx->header.titleid); - exefs_set_usersettings(&ctx->exefs, ctx->usersettings); - exefs_set_counter(&ctx->exefs, exefscounter); - exefs_set_keys(&ctx->exefs, ctx->key[0], ctx->key[1]); - exefs_set_encrypted(&ctx->exefs, ctx->encrypted); - - romfs_set_file(&ctx->romfs, ctx->file); - romfs_set_offset(&ctx->romfs, ncch_get_romfs_offset(ctx)); - romfs_set_size(&ctx->romfs, ncch_get_romfs_size(ctx)); - romfs_set_usersettings(&ctx->romfs, ctx->usersettings); - romfs_set_counter(&ctx->romfs, romfscounter); - romfs_set_key(&ctx->romfs, ctx->key[1]); - romfs_set_encrypted(&ctx->romfs, ctx->encrypted); - - exheader_read(&ctx->exheader, actions); - - - if (actions & VerifyFlag) - ncch_verify(ctx, actions); - - if (actions & InfoFlag) - ncch_print(ctx); - - if (ctx->encrypted == NCCHCRYPTO_BROKEN) - { - fprintf(stderr, "Error, NCCH encryption broken.\n"); - return; - } - - if ((actions & ShowKeysFlag) && ctx->encrypted) - { - fprintf(stdout, "Using key(s):\n"); - if (ctx->encrypted == NCCHCRYPTO_FIXED) - { - memdump(stdout, " Key: ", ctx->key[0], 0x10); - } - else - { - memdump(stdout, " 0x2C: ", ctx->key[0], 0x10); - if (memcmp(ctx->key[0], ctx->key[1], 0x10) != 0) - { - fprintf(stdout, " special (%02x): ", ctx->header.flags[3]); - memdump(stdout, "", ctx->key[1], 0x10); - } - } - } - - if (actions & ExtractFlag) - { - ncch_save(ctx, NCCHTYPE_EXEFS, actions); - ncch_save(ctx, NCCHTYPE_ROMFS, actions); - ncch_save(ctx, NCCHTYPE_EXHEADER, actions); - ncch_save(ctx, NCCHTYPE_LOGO, actions); - ncch_save(ctx, NCCHTYPE_PLAINRGN, actions); - } - - - if (result && ncch_get_exheader_size(ctx)) - { - if (!exheader_hash_valid(&ctx->exheader)) - return; - - result = exheader_process(&ctx->exheader, actions); - } - - if (result && ncch_get_exefs_size(ctx)) - { - if(ncch_get_exheader_size(ctx)) - exefs_set_compressedflag(&ctx->exefs, exheader_get_compressedflag(&ctx->exheader)); - exefs_process(&ctx->exefs, actions); - } - - if (result && ncch_get_romfs_size(ctx)) - { - romfs_process(&ctx->romfs, 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); -} - - -u64 ncch_get_exefs_offset(ncch_context* ctx) -{ - return ctx->offset + getle32(ctx->header.exefsoffset) * ncch_get_mediaunit_size(ctx); -} - -u64 ncch_get_exefs_size(ncch_context* ctx) -{ - return getle32(ctx->header.exefssize) * ncch_get_mediaunit_size(ctx); -} - -u64 ncch_get_romfs_offset(ncch_context* ctx) -{ - return ctx->offset + getle32(ctx->header.romfsoffset) * ncch_get_mediaunit_size(ctx); -} - -u64 ncch_get_romfs_size(ncch_context* ctx) -{ - return getle32(ctx->header.romfssize) * ncch_get_mediaunit_size(ctx); -} - -u64 ncch_get_exheader_offset(ncch_context* ctx) -{ - return ctx->offset + 0x200; -} - -u64 ncch_get_exheader_size(ncch_context* ctx) -{ - return getle32(ctx->header.extendedheadersize); -} - -u64 ncch_get_logo_offset(ncch_context* ctx) -{ - return ctx->offset + getle32(ctx->header.logooffset) * ncch_get_mediaunit_size(ctx); -} - -u64 ncch_get_logo_size(ncch_context* ctx) -{ - return getle32(ctx->header.logosize) * ncch_get_mediaunit_size(ctx); -} - -u64 ncch_get_plainrgn_offset(ncch_context* ctx) -{ - return ctx->offset + getle32(ctx->header.plainregionoffset) * ncch_get_mediaunit_size(ctx); -} - -u64 ncch_get_plainrgn_size(ncch_context* ctx) -{ - return getle32(ctx->header.plainregionsize) * ncch_get_mediaunit_size(ctx); -} - - -u64 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) -{ - u8 exheader_buffer[0x400]; - u8 seedbuf[0x20]; - u8* seed; - u8* keyX = NULL; - u8 hash[0x20] = {0}; - ctr_ncchheader* header = &ctx->header; - const u32 exheaderSize = getle32(header->extendedheadersize); - struct sNcchKeyslot - { - u8 x[0x10]; - u8 y[0x10]; - u8 key[0x10]; - } key[2]; - - - // Check if the NCCH is already decrypted, by checking if the exheader hash matches - // Otherwise, use determination rules - fseeko64(ctx->file, ncch_get_exheader_offset(ctx), SEEK_SET); - memset(exheader_buffer, 0, exheaderSize); - fread(exheader_buffer, 1, exheaderSize, ctx->file); - ctr_sha_256(exheader_buffer, exheaderSize, hash); - if (!memcmp(hash, header->extendedheaderhash, 32)) - { - // exheader hash matches, so probably decrypted - ctx->encrypted = NCCHCRYPTO_NONE; - if (!(header->flags[7] & 4)) - fprintf(stderr, "Warning, exheader is decrypted but the NCCH says it isn't.\n" - "This NCCH will likely break on console.\n"); - return; - } - - // if not plain flag set and not ncch unencrypted flag set - // determine the keys - if (!(actions & PlainFlag) && !(header->flags[7] & 4)) - { - // fixed key crypto - if (header->flags[7] & 1) - { - ctx->encrypted = NCCHCRYPTO_FIXED; - if (programid_is_system(header->programid)) - { - if (settings_get_ncch_fixedsystemkey(ctx->usersettings) == NULL) - { - fprintf(stderr, "Error, could not read system fixed key.\n"); - ctx->encrypted = NCCHCRYPTO_BROKEN; - return; - } - - memcpy(key[0].key, settings_get_ncch_fixedsystemkey(ctx->usersettings), 0x10); - } - else - { - memset(key[0].key, 0x00, 0x10); - } - - memcpy(key[1].key, key[0].key, 0x10); - } - // secure crypto - else - { - ctx->encrypted = NCCHCRYPTO_SECURE; - if (settings_get_ncchkeyX_old(ctx->usersettings) == NULL) - { - fprintf(stderr, "Error, could not read NCCH base keyX.\n"); - ctx->encrypted = NCCHCRYPTO_BROKEN; - return; - } - - // setup regular keyslot seeds (exheader, exefs:icon|banner) - memcpy(key[0].x, settings_get_ncchkeyX_old(ctx->usersettings), 0x10); - memcpy(key[0].y, header->signature, 0x10); - - // setup second keyslot seed (romfs, exefs:!(icon|banner)) - // - get keyX - - switch (header->flags[3]) - { - case(0): - keyX = settings_get_ncchkeyX_old(ctx->usersettings); - break; - case(1): - keyX = settings_get_ncchkeyX_seven(ctx->usersettings); - break; - case(10): - keyX = settings_get_ncchkeyX_ninethree(ctx->usersettings); - break; - case(11): - keyX = settings_get_ncchkeyX_ninesix(ctx->usersettings); - break; - default: - fprintf(stderr, "Warning, unknown NCCH crypto method.\n"); - ctx->encrypted = NCCHCRYPTO_BROKEN; - return; - } - - if (keyX == NULL) - { - fprintf(stderr, "Error, could not read NCCH keyX.\n"); - ctx->encrypted = NCCHCRYPTO_BROKEN; - return; - } - - memcpy(key[1].x, keyX, 0x10); - - // - get keyY - if (header->flags[7] & 0x20) - { - seed = settings_get_seed(ctx->usersettings, getle64(header->programid)); - if (!seed) - { - fprintf(stderr, "This title uses seed crypto, but no seed is set, unable to decrypt.\n" - "Use -p to avoid decryption or use --seeddb=dbfile or --seed=SEEDHERE.\n"); - ctx->encrypted = NCCHCRYPTO_BROKEN; - return; - } - - memcpy(seedbuf, seed, 0x10); - // Assumes running on little endian - memcpy(seedbuf + 0x10, header->programid, sizeof(header->programid)); - ctr_sha_256(seedbuf, 0x18, hash); - if (memcmp(hash, header->seedcheck, sizeof(header->seedcheck))) { - fprintf(stderr, "Seed check mismatch. (Got: %02x%02x%02x%02x, expected: %02x%02x%02x%02x)\n", - hash[0], hash[1], hash[2], hash[3], - header->seedcheck[0], header->seedcheck[1], header->seedcheck[2], header->seedcheck[3]); - ctx->encrypted = NCCHCRYPTO_BROKEN; - return; - } - - memcpy(seedbuf, header->signature, 0x10); - memcpy(seedbuf + 0x10, seed, 0x10); - ctr_sha_256(seedbuf, 0x20, hash); - memcpy(key[1].y, hash, 0x10); - } - else - { - memcpy(key[1].y, key[0].y, 0x10); - } - - - // generate keys - ctr_aes_keygen(key[0].x, key[0].y, key[0].key); - ctr_aes_keygen(key[1].x, key[1].y, key[1].key); - } - - // save keys - memcpy(ctx->key[0], key[0].key, 0x10); - memcpy(ctx->key[1], key[1].key, 0x10); - } - else - { - ctx->encrypted = NCCHCRYPTO_NONE; - } -} - -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"; - case 5: return "Extended System Update"; - default: return "Unknown"; - } -} - -static const char* contentplatformtostring(unsigned char platform) -{ - switch (platform) - { - case 1: return "CTR"; - case 2: return "SNAKE"; - default: return "Unknown"; - } -} - - - -void ncch_print(ncch_context* ctx) -{ - ctr_ncchheader *header = &ctx->header; - u64 offset = ctx->offset; - u64 mediaunitsize = ncch_get_mediaunit_size(ctx); - - fprintf(stdout, "\nNCCH:\n"); - - fprintf(stdout, "Header: %.4s\n", header->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%08"PRIx64"\n", getle32(header->contentsize)*mediaunitsize); - fprintf(stdout, "Title id: %016"PRIx64"\n", getle64(header->titleid)); - fprintf(stdout, "Maker code: %.2s\n", header->makercode); - fprintf(stdout, "Version: %d\n", getle16(header->version)); - fprintf(stdout, "Title seed check: %08x\n", getle32(header->seedcheck)); - fprintf(stdout, "Program id: %016"PRIx64"\n", getle64(header->programid)); - if(ctx->logohashcheck == Unchecked) - memdump(stdout, "Logo hash: ", header->logohash, 0x20); - else if(ctx->logohashcheck == Good) - memdump(stdout, "Logo hash (GOOD): ", header->logohash, 0x20); - else - memdump(stdout, "Logo hash (FAIL): ", header->logohash, 0x20); - fprintf(stdout, "Product code: %.16s\n", header->productcode); - fprintf(stdout, "Exheader size: 0x%x\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: %016"PRIx64"\n", getle64(header->flags)); - fprintf(stdout, " > Mediaunit size: 0x%x\n", (u32)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 (%d)%s\n", header->flags[3], header->flags[7] & 32? " (KeyY seeded)" : ""); - fprintf(stdout, " > Form type: %s\n", formtypetostring(header->flags[5])); - fprintf(stdout, " > Content type: %s\n", contenttypetostring(header->flags[5])); - fprintf(stdout, " > Content platform: %s\n", contentplatformtostring(header->flags[4])); - if (header->flags[7] & 2) - fprintf(stdout, " > No RomFS mount\n"); - - - fprintf(stdout, "Plain region offset: 0x%08"PRIx64"\n", getle32(header->plainregionsize)? offset+getle32(header->plainregionoffset)*mediaunitsize : 0); - fprintf(stdout, "Plain region size: 0x%08"PRIx64"\n", getle32(header->plainregionsize)*mediaunitsize); - fprintf(stdout, "Logo offset: 0x%08"PRIx64"\n", getle32(header->logosize)? offset+getle32(header->logooffset)*mediaunitsize : 0); - fprintf(stdout, "Logo size: 0x%08"PRIx64"\n", getle32(header->logosize)*mediaunitsize); - fprintf(stdout, "ExeFS offset: 0x%08"PRIx64"\n", getle32(header->exefssize)? offset+getle32(header->exefsoffset)*mediaunitsize : 0); - fprintf(stdout, "ExeFS size: 0x%08"PRIx64"\n", getle32(header->exefssize)*mediaunitsize); - fprintf(stdout, "ExeFS hash region size: 0x%08"PRIx64"\n", getle32(header->exefshashregionsize)*mediaunitsize); - fprintf(stdout, "RomFS offset: 0x%08"PRIx64"\n", getle32(header->romfssize)? offset+getle32(header->romfsoffset)*mediaunitsize : 0); - fprintf(stdout, "RomFS size: 0x%08"PRIx64"\n", getle32(header->romfssize)*mediaunitsize); - fprintf(stdout, "RomFS hash region size: 0x%08"PRIx64"\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 deleted file mode 100644 index 94d29687..00000000 --- a/ctrtool/ncch.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef _NCCH_H_ -#define _NCCH_H_ - -#include -#include "types.h" -#include "keyset.h" -#include "filepath.h" -#include "ctr.h" -#include "exefs.h" -#include "romfs.h" -#include "exheader.h" -#include "settings.h" - -typedef enum -{ - NCCHTYPE_EXHEADER = 1, - NCCHTYPE_EXEFS = 2, - NCCHTYPE_ROMFS = 3, - NCCHTYPE_LOGO = 4, - NCCHTYPE_PLAINRGN = 5, -} ctr_ncchtypes; - -typedef enum -{ - NCCHCRYPTO_NONE = 0, //< already decrypted - NCCHCRYPTO_FIXED = 1, //< fixed key crypto, used for SDK-made application titles and very very old system titles - NCCHCRYPTO_SECURE = (1<<1), //< hardware generated key - NCCHCRYPTO_BROKEN = 0xFF //< Internal: failed to generate key, but encryption still used -} ctr_ncchcryptotype; - -typedef struct -{ - u8 signature[0x100]; - u8 magic[4]; - u8 contentsize[4]; - u8 titleid[8]; - u8 makercode[2]; - u8 version[2]; - u8 seedcheck[4]; - u8 programid[8]; - u8 reserved1[0x10]; - u8 logohash[0x20]; - u8 productcode[0x10]; - u8 extendedheaderhash[0x20]; - u8 extendedheadersize[4]; - u8 reserved2[4]; - u8 flags[8]; - u8 plainregionoffset[4]; - u8 plainregionsize[4]; - u8 logooffset[4]; - u8 logosize[4]; - 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[2][16]; - u8 seed[16]; - u32 encrypted; - u64 offset; - u64 size; - settings* usersettings; - ctr_ncchheader header; - ctr_aes_context aes; - exefs_context exefs; - romfs_context romfs; - exheader_context exheader; - int exefshashcheck; - int romfshashcheck; - int exheaderhashcheck; - int logohashcheck; - int headersigcheck; - u64 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, u64 offset); -void ncch_set_size(ncch_context* ctx, u64 size); -void ncch_set_file(ncch_context* ctx, FILE* file); -void ncch_set_usersettings(ncch_context* ctx, settings* usersettings); -u64 ncch_get_exefs_offset(ncch_context* ctx); -u64 ncch_get_exefs_size(ncch_context* ctx); -u64 ncch_get_romfs_offset(ncch_context* ctx); -u64 ncch_get_romfs_size(ncch_context* ctx); -u64 ncch_get_exheader_offset(ncch_context* ctx); -u64 ncch_get_exheader_size(ncch_context* ctx); -u64 ncch_get_logo_offset(ncch_context* ctx); -u64 ncch_get_logo_size(ncch_context* ctx); -u64 ncch_get_plainrgn_offset(ncch_context* ctx); -u64 ncch_get_plainrgn_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, u8 nocrypto); -u64 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 deleted file mode 100644 index ab9fae8f..00000000 --- a/ctrtool/ncsd.c +++ /dev/null @@ -1,165 +0,0 @@ -#include -#include - -#include "types.h" -#include "ncsd.h" -#include "utils.h" -#include "ctr.h" -#include - - -void ncsd_init(ncsd_context* ctx) -{ - memset(ctx, 0, sizeof(ncsd_context)); -} - -void ncsd_set_offset(ncsd_context* ctx, u64 offset) -{ - ctx->offset = offset; -} - -void ncsd_set_file(ncsd_context* ctx, FILE* file) -{ - ctx->file = file; -} - -void ncsd_set_size(ncsd_context* ctx, u64 size) -{ - ctx->size = size; -} - -void ncsd_set_ncch_index(ncsd_context* ctx, u32 ncch_index) -{ - ctx->ncch_index = ncch_index; -} - -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); -} - -u64 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_process(ncsd_context* ctx, u32 actions) -{ - fseeko64(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); - - if(ctx->ncch_index > 7 || ctx->header.partitiongeometry[ctx->ncch_index].size == 0) - { - fprintf(stderr," ERROR NCSD partition %d, does not exist\n",ctx->ncch_index); - return; - } - - ncch_set_file(&ctx->ncch, ctx->file); - ncch_set_offset(&ctx->ncch, ctx->header.partitiongeometry[ctx->ncch_index].offset * ncsd_get_mediaunit_size(ctx)); - ncch_set_size(&ctx->ncch, ctx->header.partitiongeometry[ctx->ncch_index].size * ncsd_get_mediaunit_size(ctx)); - ncch_set_usersettings(&ctx->ncch, ctx->usersettings); - ncch_process(&ctx->ncch, actions); -} - -const char* ncsd_print_mediatype(u8 type) -{ - switch(type) - { - case 0 : return "Internal Device"; - case 1 : return "Card1"; - case 2 : return "Card2"; - case 3 : return "Extended Device"; - default: return "Unknown"; - } -} - -const char* ncsd_print_carddevice(u8 type) -{ - switch(type) - { - case 1 : return "NorFlash"; - case 2 : return "None"; - case 3 : return "BT"; - default: return "Unknown"; - } -} - -void ncsd_print(ncsd_context* ctx) -{ - char magic[5]; - ctr_ncsdheader* header = &ctx->header; - unsigned int i; - unsigned int mediaunitsize = (unsigned int) 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: %016"PRIx64"\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->titleid+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); - fprintf(stdout, " > Mediatype: %s\n", ncsd_print_mediatype(header->flags[5])); - fprintf(stdout, " > Card Device: %s\n", ncsd_print_carddevice(header->flags[3] | header->flags[7])); - -} diff --git a/ctrtool/ncsd.h b/ctrtool/ncsd.h deleted file mode 100644 index 2d6ee53b..00000000 --- a/ctrtool/ncsd.h +++ /dev/null @@ -1,57 +0,0 @@ -#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 titleid[0x40]; - u8 reserved[0x30]; -} ctr_ncsdheader; - - -typedef struct -{ - FILE* file; - u64 offset; - u64 size; - u32 ncch_index; - 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, u64 offset); -void ncsd_set_size(ncsd_context* ctx, u64 size); -void ncsd_set_ncch_index(ncsd_context* ctx, u32 ncch_index); -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); -u64 ncsd_get_mediaunit_size(ncsd_context* ctx); - -#endif // _NCSD_H_ diff --git a/ctrtool/oschar.c b/ctrtool/oschar.c deleted file mode 100644 index ac8ddcc7..00000000 --- a/ctrtool/oschar.c +++ /dev/null @@ -1,210 +0,0 @@ -#include -#ifndef _WIN32 -#ifndef __CYGWIN__ -#define LIBICONV_PLUG -#endif -#include -#endif -#include "oschar.h" - -int os_fstat(const oschar_t *path) -{ - struct _osstat st; - return os_stat(path, &st); -} - -uint64_t os_fsize(const oschar_t *path) -{ - struct _osstat st; - if (os_stat(path, &st) != 0) - return 0; - else - return st.st_size; -} - -int os_makedir(const oschar_t *dir) -{ -#ifdef _WIN32 - return _wmkdir(dir); -#else - return mkdir(dir, 0777); -#endif -} - -uint32_t utf16_strlen(const utf16char_t *str) -{ - uint32_t i; - for (i = 0; str[i] != 0x0; i++); - return i; -} - -void utf16_fputs(const utf16char_t *str, FILE *out) -{ - oschar_t *_str = os_CopyConvertUTF16Str(str); - os_fputs(_str, out); - free(_str); -} - -char* strcopy_8to8(const char *src) -{ - uint32_t src_len; - char *dst; - - if (!src) - return NULL; - - src_len = strlen(src); - - // Allocate memory for expanded string - dst = calloc(src_len + 1, sizeof(char)); - if (!dst) - return NULL; - - // Copy elements from src into dst - strncpy(dst, src, src_len); - - return dst; -} - -utf16char_t* strcopy_8to16(const char *src) -{ - uint32_t src_len, i; - utf16char_t *dst; - - if (!src) - return NULL; - - src_len = strlen(src); - - // Allocate memory for expanded string - dst = calloc(src_len + 1, sizeof(utf16char_t)); - if (!dst) - return NULL; - - // Copy elements from src into dst - for (i = 0; i < src_len; i++) - dst[i] = src[i]; - - return dst; -} - - -utf16char_t* strcopy_16to16(const utf16char_t *src) -{ - uint32_t src_len, i; - utf16char_t *dst; - - if (!src) - return NULL; - - src_len = utf16_strlen(src); - - // Allocate memory for expanded string - dst = calloc(src_len + 1, sizeof(utf16char_t)); - if (!dst) - return NULL; - - // Copy elements from src into dst - for (i = 0; i < src_len; i++) - dst[i] = src[i]; - - return dst; -} - -#ifndef _WIN32 -utf16char_t* strcopy_UTF8toUTF16(const char *src) -{ - uint32_t src_len, dst_len; - size_t in_bytes, out_bytes; - utf16char_t *dst; - char *in, *out; - - if (!src) - return NULL; - - src_len = strlen(src); - dst_len = src_len + 1; - - // Allocate memory for string - dst = calloc(dst_len, sizeof(utf16char_t)); - if (!dst) - return NULL; - - in = (char*)src; - out = (char*)dst; - in_bytes = src_len*sizeof(char); - out_bytes = dst_len*sizeof(utf16char_t); - - iconv_t cd = iconv_open("UTF-16LE", "UTF-8"); - iconv(cd, &in, &in_bytes, &out, &out_bytes); - iconv_close(cd); - return dst; -} - -char* strcopy_UTF16toUTF8(const utf16char_t *src) -{ - uint32_t src_len, dst_len; - size_t in_bytes, out_bytes; - char *dst; - char *in, *out; - - if (!src) - return NULL; - - src_len = utf16_strlen(src); - // UTF-8 can use up to 3 bytes per UTF-16 code unit, or four for a surrogate pair - dst_len = src_len * 3; - - // Allocate memory for string - dst = calloc(dst_len, sizeof(char)); - if (!dst) - return NULL; - - in = (char*)src; - out = (char*)dst; - in_bytes = src_len*sizeof(uint16_t); - out_bytes = dst_len*sizeof(char); - - iconv_t cd = iconv_open("UTF-8", "UTF-16LE"); - iconv(cd, &in, &in_bytes, &out, &out_bytes); - iconv_close(cd); - return dst; -} -#endif - -oschar_t* os_AppendToPath(const oschar_t *src, const oschar_t *add) -{ - uint32_t len; - oschar_t *new_path; - - len = os_strlen(src) + os_strlen(add) + 0x10; - new_path = calloc(len, sizeof(oschar_t)); - -#ifdef _WIN32 - _snwprintf(new_path, len, L"%s%c%s", src, OS_PATH_SEPARATOR, add); -#else - snprintf(new_path, len, "%s%c%s", src, OS_PATH_SEPARATOR, add); -#endif - - return new_path; -} - -oschar_t* os_AppendUTF16StrToPath(const oschar_t *src, const utf16char_t *add) -{ - uint32_t len; - oschar_t *new_path, *_add; - - _add = os_CopyConvertUTF16Str(add); - - len = os_strlen(src) + os_strlen(_add) + 0x10; - new_path = calloc(len, sizeof(oschar_t)); - -#ifdef _WIN32 - _snwprintf(new_path, len, L"%s%c%s", src, OS_PATH_SEPARATOR, _add); -#else - snprintf(new_path, len, "%s%c%s", src, OS_PATH_SEPARATOR, _add); -#endif - - free(_add); - return new_path; -} diff --git a/ctrtool/oschar.h b/ctrtool/oschar.h deleted file mode 100644 index 9c788b07..00000000 --- a/ctrtool/oschar.h +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once -#include -#include -#include -#include -#ifdef _WIN32 -#include -#endif - -// Nintendo uses UTF16-LE chars for extended ASCII support -typedef uint16_t utf16char_t; - -// Native OS char type for unicode support -#ifdef _WIN32 -typedef wchar_t oschar_t; // UTF16-LE -#else -typedef char oschar_t; // UTF8 -#endif - -// Simple redirect macros for functions and types -#ifdef _WIN32 -#define os_strlen wcslen -#define os_strcmp wcscmp -#define os_fputs fputws - -#define os_CopyStr strcopy_16to16 -#define os_CopyConvertCharStr strcopy_8to16 -#define os_CopyConvertUTF16Str strcopy_16to16 -#define utf16_CopyStr strcopy_16to16 -#define utf16_CopyConvertOsStr strcopy_16to16 - -#define _osdirent _wdirent -#define _OSDIR _WDIR -#define os_readdir _wreaddir -#define os_opendir _wopendir -#define os_closedir _wclosedir -#define os_chdir _wchdir - -#define _osstat _stat64 -#define os_stat _wstat64 - -#define os_fopen _wfopen -#define OS_MODE_READ L"rb" -#define OS_MODE_WRITE L"wb" -#define OS_MODE_EDIT L"rb+" -#define OS_PATH_SEPARATOR '\\' -#else -#define os_strlen strlen -#define os_strcmp strcmp -#define os_fputs fputs - -#define os_CopyStr strcopy_8to8 -#define os_CopyConvertUTF16Str strcopy_UTF16toUTF8 -#define os_CopyConvertCharStr strcopy_8to8 -#define utf16_CopyStr strcopy_16to16 -#define utf16_CopyConvertOsStr strcopy_UTF8toUTF16 - -#define _osdirent dirent -#define _OSDIR DIR -#define os_readdir readdir -#define os_opendir opendir -#define os_closedir closedir -#define os_chdir chdir - -#define _osstat stat -#define os_stat stat - -#define os_fopen fopen -#define OS_MODE_READ "rb" -#define OS_MODE_WRITE "wb" -#define OS_MODE_EDIT "rb+" -#define OS_PATH_SEPARATOR '/' -#endif - -/* File related */ -int os_fstat(const oschar_t* path); -uint64_t os_fsize(const oschar_t* path); -int os_makedir(const oschar_t *dir); - -/* UTF16 String property functions */ -uint32_t utf16_strlen(const utf16char_t* str); -void utf16_fputs(const utf16char_t *str, FILE *out); - -/* String Copy and Conversion */ -char* strcopy_8to8(const char *src); -utf16char_t* strcopy_8to16(const char *src); -utf16char_t* strcopy_16to16(const utf16char_t *src); -#ifndef _WIN32 -utf16char_t* strcopy_UTF8toUTF16(const char *src); -char* strcopy_UTF16toUTF8(const utf16char_t *src); -#endif - -/* String Append and Create */ -oschar_t* os_AppendToPath(const oschar_t *src, const oschar_t *add); -oschar_t* os_AppendUTF16StrToPath(const oschar_t *src, const utf16char_t *add); \ No newline at end of file diff --git a/ctrtool/polarssl/aes.c b/ctrtool/polarssl/aes.c deleted file mode 100644 index 45f74a61..00000000 --- a/ctrtool/polarssl/aes.c +++ /dev/null @@ -1,1164 +0,0 @@ -/* - * 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 deleted file mode 100644 index 5576680e..00000000 --- a/ctrtool/polarssl/aes.h +++ /dev/null @@ -1,139 +0,0 @@ -/** - * \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 deleted file mode 100644 index 78e9384d..00000000 --- a/ctrtool/polarssl/bignum.c +++ /dev/null @@ -1,2038 +0,0 @@ -/* - * 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 deleted file mode 100644 index 80399a22..00000000 --- a/ctrtool/polarssl/bignum.h +++ /dev/null @@ -1,533 +0,0 @@ -/** - * \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 deleted file mode 100644 index a73d5fb0..00000000 --- a/ctrtool/polarssl/bn_mul.h +++ /dev/null @@ -1,736 +0,0 @@ -/** - * \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 deleted file mode 100644 index 267e2dde..00000000 --- a/ctrtool/polarssl/config.h +++ /dev/null @@ -1,336 +0,0 @@ -/** - * \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 deleted file mode 100644 index 12fa7d3c..00000000 --- a/ctrtool/polarssl/padlock.h +++ /dev/null @@ -1,98 +0,0 @@ -/** - * \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 deleted file mode 100644 index 77404fcc..00000000 --- a/ctrtool/polarssl/rsa.c +++ /dev/null @@ -1,823 +0,0 @@ -/* - * 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 deleted file mode 100644 index 5fae7947..00000000 --- a/ctrtool/polarssl/rsa.h +++ /dev/null @@ -1,353 +0,0 @@ -/** - * \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 deleted file mode 100644 index dd5ae98a..00000000 --- a/ctrtool/polarssl/sha2.c +++ /dev/null @@ -1,702 +0,0 @@ -/* - * 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 deleted file mode 100644 index be4ae56a..00000000 --- a/ctrtool/polarssl/sha2.h +++ /dev/null @@ -1,155 +0,0 @@ -/** - * \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 deleted file mode 100644 index 6f004674..00000000 --- a/ctrtool/romfs.c +++ /dev/null @@ -1,406 +0,0 @@ -#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, u64 offset) -{ - ctx->offset = offset; -} - -void romfs_set_size(romfs_context* ctx, u64 size) -{ - ctx->size = size; -} - -void romfs_set_usersettings(romfs_context* ctx, settings* usersettings) -{ - ctx->usersettings = usersettings; -} - -void romfs_set_encrypted(romfs_context* ctx, u32 encrypted) -{ - ctx->encrypted = encrypted; -} - -void romfs_set_key(romfs_context* ctx, u8 key[16]) -{ - memcpy(ctx->key, key, 16); - ctr_init_key(&ctx->aes, key); -} - -void romfs_set_counter(romfs_context* ctx, u8 counter[16]) -{ - memcpy(ctx->counter, counter, 16); -} - -void romfs_fseek(romfs_context* ctx, u64 offset) -{ - u64 data_pos = offset - ctx->offset; - fseeko64(ctx->file, offset, SEEK_SET); - - if (ctx->encrypted) { - ctr_init_counter(&ctx->aes, ctx->counter); - ctr_add_counter(&ctx->aes, (u32)(data_pos / 0x10)); - } -} - -size_t romfs_fread(romfs_context* ctx, void* buffer, size_t size, size_t count) -{ - size_t read; - if ((read = fread(buffer, size, count, ctx->file)) != count) { - //printf("romfs_fread() fail\n"); - return read; - } - if (ctx->encrypted) { - ctr_crypt_counter(&ctx->aes, buffer, buffer, size*read); - } - return read; -} - -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_set_counter(&ctx->ivfc, ctx->counter); - ivfc_set_key(&ctx->ivfc, ctx->key); - ivfc_set_encrypted(&ctx->ivfc, ctx->encrypted); - ivfc_process(&ctx->ivfc, actions); - - romfs_fseek(ctx, ctx->offset); - romfs_fread(ctx, &ctx->header, 1, sizeof(romfs_header)); - - if (getle32(ctx->header.magic) != MAGIC_IVFC) - { - fprintf(stdout, "Error, RomFS corrupted\n"); - return; - } - - ctx->infoblockoffset = (u32) (ctx->offset + 0x1000); - - romfs_fseek(ctx, ctx->infoblockoffset); - romfs_fread(ctx, &ctx->infoheader, 1, sizeof(romfs_infoheader)); - - 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); - - u32 hdrsize = getle32(ctx->infoheader.dataoffset); - u8 *block = malloc(hdrsize); - romfs_fseek(ctx, ctx->infoblockoffset); - romfs_fread(ctx, block, hdrsize, 1); - - ctx->dirblock = malloc(dirblocksize); - ctx->dirblocksize = dirblocksize; - if(ctx->dirblock) - memcpy(ctx->dirblock, block + getle32(ctx->infoheader.section[1].offset), dirblocksize); - - ctx->fileblock = malloc(fileblocksize); - ctx->fileblocksize = fileblocksize; - if (ctx->fileblock) - memcpy(ctx->fileblock, block + getle32(ctx->infoheader.section[3].offset), fileblocksize); - - free(block); - - ctx->datablockoffset = ctx->infoblockoffset + getle32(ctx->infoheader.dataoffset); - - if (actions & InfoFlag) - romfs_print(ctx); - - if (settings_get_romfs_dir_path(ctx->usersettings)->valid) - ctx->extractdir = os_CopyConvertCharStr(settings_get_romfs_dir_path(ctx->usersettings)->pathname); - else - ctx->extractdir = NULL; - - romfs_visit_dir(ctx, 0, 0, actions, ctx->extractdir); - free(ctx->extractdir); -} - -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, const oschar_t* rootpath) -{ - u32 siblingoffset; - u32 childoffset; - u32 fileoffset; - oschar_t* 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 && os_strlen(rootpath)) - { - if (utf16_strlen((const utf16char_t*)entry->name) > 0) - currentpath = os_AppendUTF16StrToPath(rootpath, (const utf16char_t*)entry->name); - else // root dir, use the provided extract path instead of the empty root name. - currentpath = os_CopyStr(rootpath); - - if (currentpath) - { - os_makedir(currentpath); - } - else - { - fputs("Error creating directory in root ", stderr); - os_fputs(rootpath, stderr); - fputs("\n", stderr); - return; - } - } - else - { - currentpath = os_CopyConvertUTF16Str((const utf16char_t*)entry->name); - if (settings_get_list_romfs_files(ctx->usersettings)) - { - u32 i; - - for(i=0; isiblingoffset); - childoffset = getle32(entry->childoffset); - fileoffset = getle32(entry->fileoffset); - - if (fileoffset != (~0)) - romfs_visit_file(ctx, fileoffset, depth+1, actions, currentpath); - - if (childoffset != (~0)) - romfs_visit_dir(ctx, childoffset, depth+1, actions, currentpath); - - if (siblingoffset != (~0)) - romfs_visit_dir(ctx, siblingoffset, depth, actions, rootpath); - - free(currentpath); -} - - -void romfs_visit_file(romfs_context* ctx, u32 fileoffset, u32 depth, u32 actions, const oschar_t* rootpath) -{ - u32 siblingoffset = 0; - oschar_t* currentpath = NULL; - 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 && os_strlen(rootpath)) - { - currentpath = os_AppendUTF16StrToPath(rootpath, (const utf16char_t*)entry->name); - if (currentpath) - { - fputs("Saving ", stdout); - os_fputs(currentpath, stdout); - fputs("...\n", stdout); - romfs_extract_datafile(ctx, getle64(entry->dataoffset), getle64(entry->datasize), currentpath); - } - else - { - fputs("Error creating file in root ", stderr); - os_fputs(rootpath, stderr); - fputs("\n", stderr); - return; - } - } - else - { - currentpath = os_CopyConvertUTF16Str((const utf16char_t*)entry->name); - if (settings_get_list_romfs_files(ctx->usersettings)) - { - u32 i; - - for(i=0; isiblingoffset); - - if (siblingoffset != (~0)) - romfs_visit_file(ctx, siblingoffset, depth, actions, rootpath); - - free(currentpath); -} - -void romfs_extract_datafile(romfs_context* ctx, u64 offset, u64 size, const oschar_t* path) -{ - FILE* outfile = 0; - u32 max; - u8 buffer[4096]; - - - if (path == NULL || os_strlen(path) == 0) - goto clean; - - offset += ctx->datablockoffset; - - romfs_fseek(ctx, offset); - outfile = os_fopen(path, OS_MODE_WRITE); - if (outfile == NULL) - { - fprintf(stderr, "Error opening file for writing\n"); - goto clean; - } - - while(size) - { - max = sizeof(buffer); - if (max > size) - max = (u32) size; - - if (max != romfs_fread(ctx, buffer, 1, max)) - { - 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%08"PRIX64"\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%08"PRIX64"\n", ctx->offset + 0x1000 + getle32(ctx->infoheader.dataoffset)); -} diff --git a/ctrtool/romfs.h b/ctrtool/romfs.h deleted file mode 100644 index 7706be59..00000000 --- a/ctrtool/romfs.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef __ROMFS_H__ -#define __ROMFS_H__ - -#include "types.h" -#include "info.h" -#include "ctr.h" -#include "oschar.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; - oschar_t* extractdir; - settings* usersettings; - u8 counter[16]; - u8 key[16]; - u64 offset; - u64 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; - ctr_aes_context aes; - int encrypted; -} romfs_context; - -void romfs_init(romfs_context* ctx); -void romfs_set_file(romfs_context* ctx, FILE* file); -void romfs_set_offset(romfs_context* ctx, u64 offset); -void romfs_set_size(romfs_context* ctx, u64 size); -void romfs_set_usersettings(romfs_context* ctx, settings* usersettings); -void romfs_set_encrypted(romfs_context* ctx, u32 encrypted); -void romfs_set_key(romfs_context* ctx, u8 key[16]); -void romfs_set_counter(romfs_context* ctx, u8 counter[16]); -void romfs_fseek(romfs_context* ctx, u64 offset); -size_t romfs_fread(romfs_context* ctx, void* buffer, size_t size, size_t count); -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, const oschar_t* rootpath); -void romfs_visit_file(romfs_context* ctx, u32 fileoffset, u32 depth, u32 actions, const oschar_t* rootpath); -void romfs_extract_datafile(romfs_context* ctx, u64 offset, u64 size, const oschar_t* 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 deleted file mode 100644 index 9fbf0567..00000000 --- a/ctrtool/settings.c +++ /dev/null @@ -1,314 +0,0 @@ -#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_logo_path(settings* usersettings) -{ - if (usersettings) - return &usersettings->logopath; - 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_content_path(settings* usersettings) -{ - if (usersettings) - return &usersettings->contentpath; - else - return 0; -} - -filepath* settings_get_meta_path(settings* usersettings) -{ - if (usersettings) - return &usersettings->metapath; - else - return 0; -} - -filepath* settings_get_plainrgn_path(settings* usersettings) -{ - if (usersettings) - return &usersettings->plainrgnpath; - else - return 0; -} - -unsigned int settings_get_mediaunit_size(settings* usersettings) -{ - if (usersettings) - return usersettings->mediaunitsize; - else - return 0; -} - -#define GETKEY(s, k) do {\ - if ((s) && (s)->keys.k.valid)\ - return (s)->keys.k.data;\ - else\ - return NULL;\ -} while (0) - -unsigned char* settings_get_ncch_fixedsystemkey(settings* usersettings) -{ - GETKEY(usersettings, ncchfixedsystemkey); -} - -unsigned char* settings_get_ncchkeyX_old(settings* usersettings) -{ - GETKEY(usersettings, ncchkeyX_old); -} - -unsigned char* settings_get_ncchkeyX_seven(settings* usersettings) -{ - GETKEY(usersettings, ncchkeyX_seven); -} - -unsigned char* settings_get_ncchkeyX_ninethree(settings* usersettings) -{ - GETKEY(usersettings, ncchkeyX_ninethree); -} - -unsigned char* settings_get_ncchkeyX_ninesix(settings* usersettings) -{ - GETKEY(usersettings, ncchkeyX_ninesix); -} - -unsigned char* settings_get_common_key(settings* usersettings, u8 index) -{ - if (index > (COMMONKEY_NUM - 1)) return NULL; - GETKEY(usersettings, commonkey[index]); -} - -unsigned char* settings_get_seed(settings* usersettings, u64 title_id) -{ - for (u32 i = 0; i < usersettings->keys.seed_num; i++) - { - if (title_id == getle64(usersettings->keys.seed_db[i].title_id)) - { - return usersettings->keys.seed_db[i].seed; - } - } - GETKEY(usersettings, seed_fallback); -} - -unsigned char* settings_get_title_key(settings* usersettings) -{ - GETKEY(usersettings, titlekey); -} - -#undef GETKEY - -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_logo_path(settings* usersettings, const char* path) -{ - filepath_set(&usersettings->logopath, 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_content_path(settings* usersettings, const char* path) -{ - filepath_set(&usersettings->contentpath, path); -} - -void settings_set_meta_path(settings* usersettings, const char* path) -{ - filepath_set(&usersettings->metapath, 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_plainrgn_path(settings* usersettings, const char* path) -{ - filepath_set(&usersettings->plainrgnpath, 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 deleted file mode 100644 index e634eaa3..00000000 --- a/ctrtool/settings.h +++ /dev/null @@ -1,81 +0,0 @@ -#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 logopath; - filepath plainrgnpath; - 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_logo_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_content_path(settings* usersettings); -filepath* settings_get_meta_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); -filepath* settings_get_plainrgn_path(settings* usersettings); -unsigned int settings_get_mediaunit_size(settings* usersettings); -unsigned char* settings_get_ncch_fixedsystemkey(settings* usersettings); -unsigned char* settings_get_ncchkeyX_old(settings* usersettings); -unsigned char* settings_get_ncchkeyX_seven(settings* usersettings); -unsigned char* settings_get_ncchkeyX_ninethree(settings* usersettings); -unsigned char* settings_get_ncchkeyX_ninesix(settings* usersettings); -unsigned char* settings_get_common_key(settings* usersettings, u8 index); -unsigned char* settings_get_seed(settings* usersettings, u64 title_id); -unsigned char* settings_get_title_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_logo_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_content_path(settings* usersettings, const char* path); -void settings_set_meta_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_plainrgn_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 deleted file mode 100644 index dfb1ac6f..00000000 --- a/ctrtool/stream.c +++ /dev/null @@ -1,138 +0,0 @@ -#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) // will be zero if nothing is written - 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 deleted file mode 100644 index df5bcbc9..00000000 --- a/ctrtool/stream.h +++ /dev/null @@ -1,45 +0,0 @@ -#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/syscalls.c b/ctrtool/syscalls.c deleted file mode 100644 index f4505357..00000000 --- a/ctrtool/syscalls.c +++ /dev/null @@ -1,171 +0,0 @@ -#include -#include -#include -#include "syscalls.h" - -// List of 3DS system calls. NULL indicates unknown. -static const char *const syscall_list[NUM_SYSCALLS] = -{ - NULL, // 00 - "ControlMemory", // 01 - "QueryMemory", // 02 - "ExitProcess", // 03 - "GetProcessAffinityMask", // 04 - "SetProcessAffinityMask", // 05 - "GetProcessIdealProcessor", // 06 - "SetProcessIdealProcessor", // 07 - "CreateThread", // 08 - "ExitThread", // 09 - "SleepThread", // 0A - "GetThreadPriority", // 0B - "SetThreadPriority", // 0C - "GetThreadAffinityMask", // 0D - "SetThreadAffinityMask", // 0E - "GetThreadIdealProcessor", // 0F - "SetThreadIdealProcessor", // 10 - "GetCurrentProcessorNumber", // 11 - "Run", // 12 - "CreateMutex", // 13 - "ReleaseMutex", // 14 - "CreateSemaphore", // 15 - "ReleaseSemaphore", // 16 - "CreateEvent", // 17 - "SignalEvent", // 18 - "ClearEvent", // 19 - "CreateTimer", // 1A - "SetTimer", // 1B - "CancelTimer", // 1C - "ClearTimer", // 1D - "CreateMemoryBlock", // 1E - "MapMemoryBlock", // 1F - "UnmapMemoryBlock", // 20 - "CreateAddressArbiter", // 21 - "ArbitrateAddress", // 22 - "CloseHandle", // 23 - "WaitSynchronization1", // 24 - "WaitSynchronizationN", // 25 - "SignalAndWait", // 26 - "DuplicateHandle", // 27 - "GetSystemTick", // 28 - "GetHandleInfo", // 29 - "GetSystemInfo", // 2A - "GetProcessInfo", // 2B - "GetThreadInfo", // 2C - "ConnectToPort", // 2D - "SendSyncRequest1", // 2E - "SendSyncRequest2", // 2F - "SendSyncRequest3", // 30 - "SendSyncRequest4", // 31 - "SendSyncRequest", // 32 - "OpenProcess", // 33 - "OpenThread", // 34 - "GetProcessId", // 35 - "GetProcessIdOfThread", // 36 - "GetThreadId", // 37 - "GetResourceLimit", // 38 - "GetResourceLimitLimitValues", // 39 - "GetResourceLimitCurrentValues", // 3A - "GetThreadContext", // 3B - "Break", // 3C - "OutputDebugString", // 3D - "ControlPerformanceCounter", // 3E - NULL, // 3F - NULL, // 40 - NULL, // 41 - NULL, // 42 - NULL, // 43 - NULL, // 44 - NULL, // 45 - NULL, // 46 - "CreatePort", // 47 - "CreateSessionToPort", // 48 - "CreateSession", // 49 - "AcceptSession", // 4A - "ReplyAndReceive1", // 4B - "ReplyAndReceive2", // 4C - "ReplyAndReceive3", // 4D - "ReplyAndReceive4", // 4E - "ReplyAndReceive", // 4F - "BindInterrupt", // 50 - "UnbindInterrupt", // 51 - "InvalidateProcessDataCache", // 52 - "StoreProcessDataCache", // 53 - "FlushProcessDataCache", // 54 - "StartInterProcessDma", // 55 - "StopDma", // 56 - "GetDmaState", // 57 - "RestartDma", // 58 - "SetGpuProt", // 59 - "SetWifiEnabled", // 5A - NULL, // 5B - NULL, // 5C - NULL, // 5D - NULL, // 5E - NULL, // 5F - "DebugActiveProcess", // 60 - "BreakDebugProcess", // 61 - "TerminateDebugProcess", // 62 - "GetProcessDebugEvent", // 63 - "ContinueDebugEvent", // 64 - "GetProcessList", // 65 - "GetThreadList", // 66 - "GetDebugThreadContext", // 67 - "SetDebugThreadContext", // 68 - "QueryDebugProcessMemory", // 69 - "ReadProcessMemory", // 6A - "WriteProcessMemory", // 6B - "SetHardwareBreakPoint", // 6C - "GetDebugThreadParam", // 6D - NULL, // 6E - NULL, // 6F - "ControlProcessMemory", // 70 - "MapProcessMemory", // 71 - "UnmapProcessMemory", // 72 - "CreateCodeSet", // 73 - NULL, // 74 - "CreateProcess", // 75 - "TerminateProcess", // 76 - "SetProcessResourceLimits", // 77 - "CreateResourceLimit", // 78 - "SetResourceLimitValues", // 79 - "AddCodeSegment", // 7A - "Backdoor", // 7B - "KernelSetState", // 7C - "QueryProcessMemory", // 7D - NULL, // 7E - NULL, // 7F -}; - - -void syscall_get_name(char *output, size_t size, unsigned int call_num) -{ -#ifdef _MSC_VER - typedef char StaticAssert[sizeof(syscall_list) / sizeof(syscall_list[0]) == NUM_SYSCALLS ? 1 : -1]; -#else - _Static_assert(sizeof(syscall_list) / sizeof(syscall_list[0]) == NUM_SYSCALLS, - "syscall table length mismatch"); -#endif - - - if (size == 0) - { - return; - } - - const char *name = NULL; - if (call_num < (unsigned int) NUM_SYSCALLS) - { - name = syscall_list[call_num]; - } - - char name_buf[] = "UnknownXX"; - sprintf(&name_buf[sizeof(name_buf) - 3], "%02X", call_num & 0xFFu); - - name = name ? name : name_buf; - - size_t length = strlen(name); - length = (length > (size - 1)) ? (size - 1) : length; - - memcpy(output, name, length); - output[length] = '\0'; -} diff --git a/ctrtool/syscalls.h b/ctrtool/syscalls.h deleted file mode 100644 index 5afca898..00000000 --- a/ctrtool/syscalls.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _SYSCALLS_H_ -#define _SYSCALLS_H_ - -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - -enum { NUM_SYSCALLS = 0x80 }; - -void syscall_get_name(char *output, size_t size, unsigned int call_num); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif diff --git a/ctrtool/tik.c b/ctrtool/tik.c deleted file mode 100644 index 285f403c..00000000 --- a/ctrtool/tik.c +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include -#include - -#include "aes_keygen.h" -#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, u64 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; -} - -const unsigned char* tik_get_titlekey(tik_context* ctx) -{ - return ctx->titlekey.valid ? ctx->titlekey.data : NULL; -} - -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); -} - -int tik_decrypt_titlekey(tik_context* ctx, u8 decryptedkey[0x10]) -{ - u8 iv[16]; - u8* commonkey = settings_get_common_key(ctx->usersettings, ctx->tik.commonkey_idx); - - memset(decryptedkey, 0, 0x10); - if (!commonkey) - { - fprintf(stdout, "Error, could not read common key.\n"); - return 1; - } - - memset(iv, 0, 0x10); - memcpy(iv, ctx->tik.title_id, 8); - - ctr_init_cbc_decrypt(&ctx->aes, commonkey, iv); - ctr_decrypt_cbc(&ctx->aes, ctx->tik.encrypted_title_key, decryptedkey, 0x10); - return 0; -} - -void tik_process(tik_context* ctx, u32 actions) -{ - if (ctx->size < sizeof(eticket)) - { - fprintf(stderr, "Error, ticket size too small\n"); - goto clean; - } - - fseeko64(ctx->file, ctx->offset, SEEK_SET); - fread((u8*)&ctx->tik, 1, sizeof(eticket), ctx->file); - - ctx->titlekey.valid = tik_decrypt_titlekey(ctx, ctx->titlekey.data) == 0 ? 1 : 0; - - 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 (ctx->titlekey.valid) - memdump(stdout, "Decrypted Titlekey: ", ctx->titlekey.data, 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 deleted file mode 100644 index 4d85aaa2..00000000 --- a/ctrtool/tik.h +++ /dev/null @@ -1,64 +0,0 @@ -#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; - u64 offset; - u32 size; - key128 titlekey; - 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, u64 offset); -void tik_set_size(tik_context* ctx, u32 size); -void tik_set_usersettings(tik_context* ctx, settings* usersettings); -const unsigned char* tik_get_titlekey(tik_context* ctx); -void tik_get_titleid(tik_context* ctx, u8 titleid[8]); -void tik_get_iv(tik_context* ctx, u8 iv[0x10]); -int 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 deleted file mode 100644 index 6ca4dc82..00000000 --- a/ctrtool/tinyxml/tinystr.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* -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 deleted file mode 100644 index 89cca334..00000000 --- a/ctrtool/tinyxml/tinystr.h +++ /dev/null @@ -1,305 +0,0 @@ -/* -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 deleted file mode 100644 index cec7b8cb..00000000 --- a/ctrtool/tinyxml/tinyxml.cpp +++ /dev/null @@ -1,1886 +0,0 @@ -/* -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 deleted file mode 100644 index eea0f069..00000000 --- a/ctrtool/tinyxml/tinyxml.h +++ /dev/null @@ -1,1805 +0,0 @@ -/* -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" -#include - - -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, u64 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) - { - fseeko64(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 savesize = 0; - unsigned int titlever = 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); - savesize = getle32(body->savedatasize); - titlever = getbe16(body->titleversion); - - 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)); - if(savesize < sizeKB) - fprintf(stdout, "Save Size: %08x\n", savesize); - else if(savesize < sizeMB) - fprintf(stdout, "Save Size: %dKB (%08x)\n", savesize/sizeKB, savesize); - else - fprintf(stdout, "Save Size: %dMB (%08x)\n", savesize/sizeMB, savesize); - fprintf(stdout, "Access rights: %08x\n", getbe32(body->accessrights)); - fprintf(stdout, "Title version: %d.%d.%d (v%d)\n", (titlever >> 10) & 0x3F, (titlever >> 4) & 0x3F, titlever & 0xF, titlever); - 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: %016"PRIx64"\n", getbe64(chunk->size)); - - switch(ctx->content_hash_stat[i]) { - 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 deleted file mode 100644 index d9701a7d..00000000 --- a/ctrtool/tmd.h +++ /dev/null @@ -1,103 +0,0 @@ -#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 savedatasize[4]; - unsigned char privsavedatasize[4]; - unsigned char padding3[4]; - unsigned char twlflag; - unsigned char padding4[0x31]; - unsigned char accessrights[4]; - unsigned char titleversion[2]; - unsigned char contentcount[2]; - unsigned char bootcontent[2]; - unsigned char padding5[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; - u64 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, u64 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 deleted file mode 100644 index c35e2ba8..00000000 --- a/ctrtool/types.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef __TYPES_H__ -#define __TYPES_H__ - -#include -#include - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -typedef int8_t s8; -typedef int16_t s16; -typedef int32_t s32; -typedef int64_t s64; - -enum flags -{ - ExtractFlag = (1<<0), - InfoFlag = (1<<1), - PlainFlag = (1<<2), - VerboseFlag = (1<<3), - VerifyFlag = (1<<4), - RawFlag = (1<<5), - ShowKeysFlag = (1<<6), - DecompressCodeFlag = (1<<7), - ShowSyscallsFlag = (1<<8), - DevFlag = (1<<9), -}; - -enum validstate -{ - Unchecked = 0, - Good = 1, - Fail = 2, -}; - -enum sizeunits -{ - sizeKB = 0x400, - sizeMB = 0x100000, -}; - -#endif diff --git a/ctrtool/utils.c b/ctrtool/utils.c deleted file mode 100644 index 92125e5b..00000000 --- a/ctrtool/utils.c +++ /dev/null @@ -1,239 +0,0 @@ -#include -#include -#include -#ifdef _WIN32 -#include -#endif -#include "utils.h" - - - -u32 align(u32 offset, u32 alignment) -{ - u32 mask = ~(alignment-1); - - return (offset + (alignment-1)) & mask; -} - -u64 align64(u64 offset, u32 alignment) -{ - u64 mask = ~(u64)(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] = (u8) n; - p[1] = (u8) (n>>8); -} - -void putle32(u8* p, u32 n) -{ - p[0] = (u8) n; - p[1] = (u8) (n>>8); - p[2] = (u8) (n>>16); - p[3] = (u8) (n>>24); -} - -void putle64(u8* p, u64 n) -{ - p[0] = (u8) n; - p[1] = (u8) (n >> 8); - p[2] = (u8) (n >> 16); - p[3] = (u8) (n >> 24); - p[4] = (u8) (n >> 32); - p[5] = (u8) (n >> 40); - p[6] = (u8) (n >> 48); - p[7] = (u8) (n >> 56); -} - -void putbe16(u8* p, u16 n) -{ - p[1] = (u8) n; - p[0] = (u8) (n >> 8); -} - -void putbe32(u8* p, u32 n) -{ - p[3] = (u8) n; - p[2] = (u8) (n >> 8); - p[1] = (u8) (n >> 16); - p[0] = (u8) (n >> 24); -} - -void putbe64(u8* p, u64 n) -{ - p[7] = (u8) n; - p[6] = (u8) (n >> 8); - p[5] = (u8) (n >> 16); - p[4] = (u8) (n >> 24); - p[3] = (u8) (n >> 32); - p[2] = (u8) (n >> 40); - p[1] = (u8) (n >> 48); - p[0] = (u8) (n >> 56); -} - -void readkeyfile(u8* key, const char* keyfname) -{ - u64 keysize = _fsize(keyfname); - FILE* f = fopen(keyfname, "rb"); - - if (0 == f) - { - fprintf(stdout, "Error opening key file\n"); - goto clean; - } - - if (keysize != 16) - { - fprintf(stdout, "Error key size mismatch, got %"PRIu64", 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 deleted file mode 100644 index b65740db..00000000 --- a/ctrtool/windows/getopt.h +++ /dev/null @@ -1,172 +0,0 @@ -/* 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 deleted file mode 100644 index 00c3071c..00000000 --- a/ctrtool/windows/getopt1.c +++ /dev/null @@ -1,188 +0,0 @@ -/* 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 */ From cf1ec3dda32194a3b886bc141686bd6d9a5667ca Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 12 Mar 2022 14:14:13 +0800 Subject: [PATCH 242/317] Separate build scripts for MakeROM and CTRTool --- .github/workflows/{build_master.yml => Build_MakeROM.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{build_master.yml => Build_MakeROM.yml} (97%) diff --git a/.github/workflows/build_master.yml b/.github/workflows/Build_MakeROM.yml similarity index 97% rename from .github/workflows/build_master.yml rename to .github/workflows/Build_MakeROM.yml index 437e2b64..8e3fd3f4 100644 --- a/.github/workflows/build_master.yml +++ b/.github/workflows/Build_MakeROM.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: dist: [ubuntu_x86_64, macos_x86_64, win_x86_64] - prog: [ctrtool, makerom] + prog: [makerom] include: - dist: win_x86_64 os: windows-latest From 74db0204f3ad47ffde37c1cd90293869f6a52511 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 12 Mar 2022 15:58:46 +0800 Subject: [PATCH 243/317] Update gitignore --- .gitignore | 317 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 212 insertions(+), 105 deletions(-) diff --git a/.gitignore b/.gitignore index 7872f095..fbf0828c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,68 +1,81 @@ -################# -## 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 -################# +bin/* +data/* +*.o +*.a +*.so.* +.DS_Store +.vscode/* ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. - -*.VC.opendb +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.suo *.user +*.userosscache *.sln.docstates -# Build results +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs +# Build results [Dd]ebug/ +[Dd]ebugPublic/ [Rr]elease/ +[Rr]eleases/ x64/ -build/ +x86/ +bld/ [Bb]in/ [Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio *_i.c *_p.c +*_i.h *.ilk *.meta *.obj +*.iobj *.pch *.pdb +*.ipdb *.pgc *.pgd *.rsp @@ -77,21 +90,34 @@ build/ *.vssscc .builds *.pidb -*.log +*.svclog *.scc +# Chutzpah Test files +_Chutzpah* + # Visual C++ cache files ipch/ *.aps *.ncb +*.opendb *.opensdf *.sdf *.cachefile +*.VC.db +*.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ # Guidance Automation Toolkit *.gpState @@ -99,6 +125,10 @@ ipch/ # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode # TeamCity is a build add-in _TeamCity* @@ -106,9 +136,25 @@ _TeamCity* # DotCover is a Code Coverage Tool *.dotCover +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + # NCrunch -*.ncrunch* +_NCrunch_* .*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ # Installshield output folder [Ee]xpress/ @@ -127,105 +173,166 @@ DocProject/Help/html publish/ # Publish Web Output -*.Publish.xml +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted *.pubxml - -# NuGet Packages Directory -## TODO: If you have NuGet Package Restore enabled, uncomment the next line -#packages/ - -# Windows Azure Build Output -csx +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ *.build.csdef -# Windows Store app package directory +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ # Others -sql/ -*.Cache ClientBin/ -[Ss]tyle[Cc]op.* ~$* *~ *.dbmdl -*.[Pp]ublish.xml +*.dbproj.schemaview +*.jfm *.pfx *.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ # 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 ;-) +# 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 +ServiceFabricBackup/ +*.rptproj.bak # SQL Server files -App_Data/*.mdf -App_Data/*.ldf +*.mdf +*.ldf +*.ndf -############# -## Windows detritus -############# +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser -# Windows image file caches -Thumbs.db -ehthumbs.db +# Microsoft Fakes +FakesAssemblies/ -# Folder config file -Desktop.ini +# GhostDoc plugin setting file +*.GhostDoc.xml -# Recycle Bin used on file shares -$RECYCLE.BIN/ +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ -# Mac crap -.DS_Store +# Visual Studio 6 build log +*.plg +# Visual Studio 6 workspace options file +*.opt -############# -## Python -############# +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw -*.py[co] +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions -# Packages -*.egg -*.egg-info -dist/ -build/ -eggs/ -parts/ -var/ -sdist/ -develop-eggs/ -.installed.cfg +# Paket dependency manager +.paket/paket.exe +paket-files/ -# Installer logs -pip-log.txt +# FAKE - F# Make +.fake/ -# Unit test / coverage reports -.coverage -.tox +# JetBrains Rider +.idea/ +*.sln.iml -#Translations -*.mo +# CodeRush +.cr/ -#Mr Developer -.mr.developer.cfg +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc -*.o +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser -#CTR -*.cxi -*.cfa -*.cci -*.3ds -*.cia - -#exec -*.exe -*.db -/.vs/slnx.sqlite +# MFractors (Xamarin productivity tool) working folder +.mfractor/ \ No newline at end of file From 6ad2f13c50819a34d4fe106fcd371ca3a17b499d Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 12 Mar 2022 16:00:10 +0800 Subject: [PATCH 244/317] Change title for MakeROM workflow --- .github/workflows/Build_MakeROM.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Build_MakeROM.yml b/.github/workflows/Build_MakeROM.yml index 8e3fd3f4..f6db2083 100644 --- a/.github/workflows/Build_MakeROM.yml +++ b/.github/workflows/Build_MakeROM.yml @@ -1,4 +1,4 @@ -name: Compile Master Branch +name: Compile MakeROM (on master branch) on: push: From 800f5776bca9c509d7db691b39b471e57c1b0b9c Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 12 Mar 2022 16:00:33 +0800 Subject: [PATCH 245/317] Add source code for ctrtool --- ctrtool/build/visualstudio/CTRTool.sln | 90 + .../visualstudio/CTRTool/CTRTool.vcxproj | 192 + .../CTRTool/CTRTool.vcxproj.filters | 111 + ctrtool/deps/libbroadon-es/LICENSE | 21 + ctrtool/deps/libbroadon-es/README.md | 2 + .../build/visualstudio/libbroadon-es.sln | 62 + .../libbroadon-es/libbroadon-es.vcxproj | 151 + .../libbroadon-es.vcxproj.filters | 48 + ctrtool/deps/libbroadon-es/include/brd/es.h | 7 + .../libbroadon-es/include/brd/es/es_cert.h | 101 + .../libbroadon-es/include/brd/es/es_sign.h | 73 + .../libbroadon-es/include/brd/es/es_ticket.h | 232 + .../libbroadon-es/include/brd/es/es_tmd.h | 129 + .../deps/libbroadon-es/include/brd/es/types.h | 73 + ctrtool/deps/libbroadon-es/makefile | 193 + ctrtool/deps/libbroadon-es/src/Dummy.cpp | 138 + ctrtool/deps/libfmt/LICENSE.rst | 27 + ctrtool/deps/libfmt/README.md | 2 + .../deps/libfmt/build/visualstudio/libfmt.sln | 31 + .../build/visualstudio/libfmt/libfmt.vcxproj | 171 + .../libfmt/libfmt.vcxproj.filters | 66 + ctrtool/deps/libfmt/include/fmt/args.h | 234 + ctrtool/deps/libfmt/include/fmt/chrono.h | 2067 ++++ ctrtool/deps/libfmt/include/fmt/color.h | 638 ++ ctrtool/deps/libfmt/include/fmt/compile.h | 642 ++ ctrtool/deps/libfmt/include/fmt/core.h | 3236 ++++++ ctrtool/deps/libfmt/include/fmt/format-inl.h | 2643 +++++ ctrtool/deps/libfmt/include/fmt/format.h | 3104 ++++++ ctrtool/deps/libfmt/include/fmt/locale.h | 2 + ctrtool/deps/libfmt/include/fmt/os.h | 527 + ctrtool/deps/libfmt/include/fmt/ostream.h | 135 + ctrtool/deps/libfmt/include/fmt/printf.h | 657 ++ ctrtool/deps/libfmt/include/fmt/ranges.h | 793 ++ ctrtool/deps/libfmt/include/fmt/xchar.h | 236 + ctrtool/deps/libfmt/makefile | 197 + ctrtool/deps/libfmt/src/format.cc | 124 + ctrtool/deps/libfmt/src/os.cc | 361 + ctrtool/deps/libmbedtls/BUILDING.md | 31 + ctrtool/deps/libmbedtls/LICENSE | 2 + ctrtool/deps/libmbedtls/README.md | 2 + ctrtool/deps/libmbedtls/apache-2.0.txt | 202 + .../build/visualstudio/libmbedtls.sln | 31 + .../libmbedtls/libmbedtls.vcxproj | 289 + .../libmbedtls/libmbedtls.vcxproj.filters | 492 + ctrtool/deps/libmbedtls/include/mbedtls/aes.h | 674 ++ .../deps/libmbedtls/include/mbedtls/aesni.h | 138 + .../deps/libmbedtls/include/mbedtls/arc4.h | 146 + .../deps/libmbedtls/include/mbedtls/aria.h | 370 + .../deps/libmbedtls/include/mbedtls/asn1.h | 358 + .../libmbedtls/include/mbedtls/asn1write.h | 329 + .../deps/libmbedtls/include/mbedtls/base64.h | 98 + .../deps/libmbedtls/include/mbedtls/bignum.h | 984 ++ .../libmbedtls/include/mbedtls/blowfish.h | 287 + .../deps/libmbedtls/include/mbedtls/bn_mul.h | 916 ++ .../libmbedtls/include/mbedtls/camellia.h | 326 + ctrtool/deps/libmbedtls/include/mbedtls/ccm.h | 310 + .../deps/libmbedtls/include/mbedtls/certs.h | 252 + .../libmbedtls/include/mbedtls/chacha20.h | 226 + .../libmbedtls/include/mbedtls/chachapoly.h | 358 + .../libmbedtls/include/mbedtls/check_config.h | 708 ++ .../deps/libmbedtls/include/mbedtls/cipher.h | 872 ++ .../include/mbedtls/cipher_internal.h | 125 + .../deps/libmbedtls/include/mbedtls/cmac.h | 213 + .../libmbedtls/include/mbedtls/compat-1.3.h | 2531 +++++ .../deps/libmbedtls/include/mbedtls/config.h | 3344 ++++++ .../libmbedtls/include/mbedtls/ctr_drbg.h | 512 + .../deps/libmbedtls/include/mbedtls/debug.h | 265 + ctrtool/deps/libmbedtls/include/mbedtls/des.h | 356 + ctrtool/deps/libmbedtls/include/mbedtls/dhm.h | 1096 ++ .../deps/libmbedtls/include/mbedtls/ecdh.h | 440 + .../deps/libmbedtls/include/mbedtls/ecdsa.h | 604 + .../deps/libmbedtls/include/mbedtls/ecjpake.h | 277 + ctrtool/deps/libmbedtls/include/mbedtls/ecp.h | 1132 ++ .../libmbedtls/include/mbedtls/ecp_internal.h | 299 + .../deps/libmbedtls/include/mbedtls/entropy.h | 289 + .../libmbedtls/include/mbedtls/entropy_poll.h | 110 + .../deps/libmbedtls/include/mbedtls/error.h | 129 + ctrtool/deps/libmbedtls/include/mbedtls/gcm.h | 326 + .../deps/libmbedtls/include/mbedtls/havege.h | 81 + .../deps/libmbedtls/include/mbedtls/hkdf.h | 141 + .../libmbedtls/include/mbedtls/hmac_drbg.h | 415 + ctrtool/deps/libmbedtls/include/mbedtls/md.h | 468 + ctrtool/deps/libmbedtls/include/mbedtls/md2.h | 306 + ctrtool/deps/libmbedtls/include/mbedtls/md4.h | 311 + ctrtool/deps/libmbedtls/include/mbedtls/md5.h | 311 + .../libmbedtls/include/mbedtls/md_internal.h | 115 + .../include/mbedtls/memory_buffer_alloc.h | 151 + ctrtool/deps/libmbedtls/include/mbedtls/net.h | 37 + .../libmbedtls/include/mbedtls/net_sockets.h | 271 + .../deps/libmbedtls/include/mbedtls/nist_kw.h | 184 + ctrtool/deps/libmbedtls/include/mbedtls/oid.h | 605 + .../deps/libmbedtls/include/mbedtls/padlock.h | 126 + ctrtool/deps/libmbedtls/include/mbedtls/pem.h | 136 + ctrtool/deps/libmbedtls/include/mbedtls/pk.h | 755 ++ .../libmbedtls/include/mbedtls/pk_internal.h | 138 + .../deps/libmbedtls/include/mbedtls/pkcs11.h | 175 + .../deps/libmbedtls/include/mbedtls/pkcs12.h | 130 + .../deps/libmbedtls/include/mbedtls/pkcs5.h | 109 + .../libmbedtls/include/mbedtls/platform.h | 367 + .../include/mbedtls/platform_time.h | 82 + .../include/mbedtls/platform_util.h | 196 + .../libmbedtls/include/mbedtls/poly1305.h | 192 + .../libmbedtls/include/mbedtls/ripemd160.h | 237 + ctrtool/deps/libmbedtls/include/mbedtls/rsa.h | 1274 +++ .../libmbedtls/include/mbedtls/rsa_internal.h | 226 + .../deps/libmbedtls/include/mbedtls/sha1.h | 352 + .../deps/libmbedtls/include/mbedtls/sha256.h | 297 + .../deps/libmbedtls/include/mbedtls/sha512.h | 300 + ctrtool/deps/libmbedtls/include/mbedtls/ssl.h | 3262 ++++++ .../libmbedtls/include/mbedtls/ssl_cache.h | 150 + .../include/mbedtls/ssl_ciphersuites.h | 540 + .../libmbedtls/include/mbedtls/ssl_cookie.h | 115 + .../libmbedtls/include/mbedtls/ssl_internal.h | 782 ++ .../libmbedtls/include/mbedtls/ssl_ticket.h | 142 + .../libmbedtls/include/mbedtls/threading.h | 122 + .../deps/libmbedtls/include/mbedtls/timing.h | 153 + .../deps/libmbedtls/include/mbedtls/version.h | 112 + .../deps/libmbedtls/include/mbedtls/x509.h | 337 + .../libmbedtls/include/mbedtls/x509_crl.h | 174 + .../libmbedtls/include/mbedtls/x509_crt.h | 785 ++ .../libmbedtls/include/mbedtls/x509_csr.h | 307 + .../deps/libmbedtls/include/mbedtls/xtea.h | 139 + ctrtool/deps/libmbedtls/makefile | 197 + ctrtool/deps/libmbedtls/src/aes.c | 2233 ++++ ctrtool/deps/libmbedtls/src/aesni.c | 470 + ctrtool/deps/libmbedtls/src/arc4.c | 201 + ctrtool/deps/libmbedtls/src/aria.c | 1079 ++ ctrtool/deps/libmbedtls/src/asn1parse.c | 389 + ctrtool/deps/libmbedtls/src/asn1write.c | 421 + ctrtool/deps/libmbedtls/src/base64.c | 293 + ctrtool/deps/libmbedtls/src/bignum.c | 2866 +++++ ctrtool/deps/libmbedtls/src/blowfish.c | 696 ++ ctrtool/deps/libmbedtls/src/camellia.c | 1114 ++ ctrtool/deps/libmbedtls/src/ccm.c | 545 + ctrtool/deps/libmbedtls/src/certs.c | 1753 +++ ctrtool/deps/libmbedtls/src/chacha20.c | 570 + ctrtool/deps/libmbedtls/src/chachapoly.c | 540 + ctrtool/deps/libmbedtls/src/cipher.c | 1158 ++ ctrtool/deps/libmbedtls/src/cipher_wrap.c | 2272 ++++ ctrtool/deps/libmbedtls/src/cmac.c | 1078 ++ ctrtool/deps/libmbedtls/src/ctr_drbg.c | 722 ++ ctrtool/deps/libmbedtls/src/debug.c | 450 + ctrtool/deps/libmbedtls/src/des.c | 1064 ++ ctrtool/deps/libmbedtls/src/dhm.c | 712 ++ ctrtool/deps/libmbedtls/src/ecdh.c | 676 ++ ctrtool/deps/libmbedtls/src/ecdsa.c | 991 ++ ctrtool/deps/libmbedtls/src/ecjpake.c | 1140 ++ ctrtool/deps/libmbedtls/src/ecp.c | 2999 +++++ ctrtool/deps/libmbedtls/src/ecp_curves.c | 1470 +++ ctrtool/deps/libmbedtls/src/entropy.c | 721 ++ ctrtool/deps/libmbedtls/src/entropy_poll.c | 236 + ctrtool/deps/libmbedtls/src/error.c | 916 ++ ctrtool/deps/libmbedtls/src/gcm.c | 994 ++ ctrtool/deps/libmbedtls/src/havege.c | 253 + ctrtool/deps/libmbedtls/src/hkdf.c | 192 + ctrtool/deps/libmbedtls/src/hmac_drbg.c | 625 ++ ctrtool/deps/libmbedtls/src/md.c | 475 + ctrtool/deps/libmbedtls/src/md2.c | 363 + ctrtool/deps/libmbedtls/src/md4.c | 484 + ctrtool/deps/libmbedtls/src/md5.c | 498 + ctrtool/deps/libmbedtls/src/md_wrap.c | 586 + .../deps/libmbedtls/src/memory_buffer_alloc.c | 750 ++ ctrtool/deps/libmbedtls/src/net_sockets.c | 668 ++ ctrtool/deps/libmbedtls/src/nist_kw.c | 755 ++ ctrtool/deps/libmbedtls/src/oid.c | 758 ++ ctrtool/deps/libmbedtls/src/padlock.c | 170 + ctrtool/deps/libmbedtls/src/pem.c | 490 + ctrtool/deps/libmbedtls/src/pk.c | 546 + ctrtool/deps/libmbedtls/src/pk_wrap.c | 719 ++ ctrtool/deps/libmbedtls/src/pkcs11.c | 240 + ctrtool/deps/libmbedtls/src/pkcs12.c | 365 + ctrtool/deps/libmbedtls/src/pkcs5.c | 411 + ctrtool/deps/libmbedtls/src/pkparse.c | 1538 +++ ctrtool/deps/libmbedtls/src/pkwrite.c | 566 + ctrtool/deps/libmbedtls/src/platform.c | 348 + ctrtool/deps/libmbedtls/src/platform_util.c | 139 + ctrtool/deps/libmbedtls/src/poly1305.c | 559 + ctrtool/deps/libmbedtls/src/ripemd160.c | 559 + ctrtool/deps/libmbedtls/src/rsa.c | 2729 +++++ ctrtool/deps/libmbedtls/src/rsa_internal.c | 492 + ctrtool/deps/libmbedtls/src/sha1.c | 573 + ctrtool/deps/libmbedtls/src/sha256.c | 586 + ctrtool/deps/libmbedtls/src/sha512.c | 636 ++ ctrtool/deps/libmbedtls/src/ssl_cache.c | 327 + .../deps/libmbedtls/src/ssl_ciphersuites.c | 2373 ++++ ctrtool/deps/libmbedtls/src/ssl_cli.c | 3636 ++++++ ctrtool/deps/libmbedtls/src/ssl_cookie.c | 256 + ctrtool/deps/libmbedtls/src/ssl_srv.c | 4379 ++++++++ ctrtool/deps/libmbedtls/src/ssl_ticket.c | 485 + ctrtool/deps/libmbedtls/src/ssl_tls.c | 9791 +++++++++++++++++ ctrtool/deps/libmbedtls/src/threading.c | 187 + ctrtool/deps/libmbedtls/src/timing.c | 536 + ctrtool/deps/libmbedtls/src/version.c | 50 + .../deps/libmbedtls/src/version_features.c | 785 ++ ctrtool/deps/libmbedtls/src/x509.c | 1072 ++ ctrtool/deps/libmbedtls/src/x509_create.c | 379 + ctrtool/deps/libmbedtls/src/x509_crl.c | 773 ++ ctrtool/deps/libmbedtls/src/x509_crt.c | 2730 +++++ ctrtool/deps/libmbedtls/src/x509_csr.c | 419 + ctrtool/deps/libmbedtls/src/x509write_crt.c | 522 + ctrtool/deps/libmbedtls/src/x509write_csr.c | 302 + ctrtool/deps/libmbedtls/src/xtea.c | 277 + ctrtool/deps/libnintendo-n3ds/LICENSE | 21 + ctrtool/deps/libnintendo-n3ds/README.md | 2 + .../build/visualstudio/libnintendo-n3ds.sln | 71 + .../libnintendo-n3ds/libnintendo-n3ds.vcxproj | 185 + .../libnintendo-n3ds.vcxproj.filters | 147 + .../deps/libnintendo-n3ds/include/ntd/n3ds.h | 26 + .../include/ntd/n3ds/CciFsSnapshotGenerator.h | 19 + .../include/ntd/n3ds/CiaFsSnapshotGenerator.h | 19 + .../include/ntd/n3ds/CtrKeyGenerator.h | 19 + .../include/ntd/n3ds/ExeFsSnapshotGenerator.h | 14 + .../include/ntd/n3ds/IvfcStream.h | 141 + .../include/ntd/n3ds/RomFsSnapshotGenerator.h | 30 + .../libnintendo-n3ds/include/ntd/n3ds/bcwav.h | 212 + .../libnintendo-n3ds/include/ntd/n3ds/cci.h | 258 + .../libnintendo-n3ds/include/ntd/n3ds/cia.h | 61 + .../libnintendo-n3ds/include/ntd/n3ds/cro.h | 160 + .../libnintendo-n3ds/include/ntd/n3ds/crr.h | 48 + .../libnintendo-n3ds/include/ntd/n3ds/es.h | 5 + .../include/ntd/n3ds/es/Certificate.h | 117 + .../include/ntd/n3ds/es/ISigner.h | 18 + .../include/ntd/n3ds/es/RsaSigner.h | 30 + .../include/ntd/n3ds/es/Signature.h | 50 + .../include/ntd/n3ds/es/Ticket.h | 72 + .../include/ntd/n3ds/es/TitleMetaData.h | 106 + .../libnintendo-n3ds/include/ntd/n3ds/exefs.h | 33 + .../include/ntd/n3ds/exheader.h | 317 + .../libnintendo-n3ds/include/ntd/n3ds/firm.h | 40 + .../libnintendo-n3ds/include/ntd/n3ds/ivfc.h | 70 + .../libnintendo-n3ds/include/ntd/n3ds/ncch.h | 158 + .../libnintendo-n3ds/include/ntd/n3ds/romfs.h | 62 + .../libnintendo-n3ds/include/ntd/n3ds/smdh.h | 128 + .../include/ntd/n3ds/systemupdater.h | 129 + ctrtool/deps/libnintendo-n3ds/makefile | 193 + .../src/CciFsSnapshotGenerator.cpp | 131 + .../src/CiaFsSnapshotGenerator.cpp | 241 + .../libnintendo-n3ds/src/CtrKeyGenerator.cpp | 99 + .../src/ExeFsSnapshotGenerator.cpp | 122 + .../deps/libnintendo-n3ds/src/IvfcStream.cpp | 467 + .../src/RomFsSnapshotGenerator.cpp | 360 + .../libnintendo-n3ds/src/es/Certificate.cpp | 200 + .../libnintendo-n3ds/src/es/RsaSigner.cpp | 84 + .../libnintendo-n3ds/src/es/Signature.cpp | 138 + .../deps/libnintendo-n3ds/src/es/Ticket.cpp | 182 + .../libnintendo-n3ds/src/es/TitleMetaData.cpp | 135 + ctrtool/deps/libtoolchain/BUILDING.md | 44 + ctrtool/deps/libtoolchain/Doxyfile | 2565 +++++ ctrtool/deps/libtoolchain/DoxygenMainpage.md | 3 + ctrtool/deps/libtoolchain/LICENSE | 20 + ctrtool/deps/libtoolchain/README.md | 46 + .../libtoolchain-test.vcxproj | 325 + .../libtoolchain-test.vcxproj.filters | 567 + .../build/visualstudio/libtoolchain.sln | 64 + .../libtoolchain/libtoolchain.vcxproj | 350 + .../libtoolchain/libtoolchain.vcxproj.filters | 690 ++ ctrtool/deps/libtoolchain/include/tc.h | 37 + .../include/tc/AccessViolationException.h | 57 + .../include/tc/ArgumentException.h | 57 + .../include/tc/ArgumentNullException.h | 57 + .../include/tc/ArgumentOutOfRangeException.h | 57 + .../include/tc/ArithmeticException.h | 57 + .../deps/libtoolchain/include/tc/ByteData.h | 102 + .../deps/libtoolchain/include/tc/Exception.h | 71 + .../include/tc/InvalidOperationException.h | 57 + .../include/tc/NotImplementedException.h | 57 + .../include/tc/NotSupportedException.h | 57 + .../include/tc/ObjectDisposedException.h | 57 + .../deps/libtoolchain/include/tc/Optional.h | 151 + .../include/tc/OutOfMemoryException.h | 57 + .../include/tc/OverflowException.h | 57 + .../include/tc/PlatformErrorHandlingUtil.h | 46 + .../libtoolchain/include/tc/ResourceStatus.h | 29 + .../include/tc/SecurityException.h | 57 + .../include/tc/UnauthorisedAccessException.h | 57 + ctrtool/deps/libtoolchain/include/tc/bn.h | 17 + .../libtoolchain/include/tc/bn/binary_utils.h | 39 + .../libtoolchain/include/tc/bn/bitarray.h | 69 + .../libtoolchain/include/tc/bn/endian_types.h | 233 + ctrtool/deps/libtoolchain/include/tc/bn/pad.h | 29 + .../deps/libtoolchain/include/tc/bn/string.h | 141 + ctrtool/deps/libtoolchain/include/tc/cli.h | 14 + .../libtoolchain/include/tc/cli/FormatUtil.h | 105 + .../include/tc/cli/OptionParser.h | 285 + ctrtool/deps/libtoolchain/include/tc/crypto.h | 85 + .../tc/crypto/Aes128CbcEncryptedStream.h | 168 + .../include/tc/crypto/Aes128CbcEncryptor.h | 94 + .../tc/crypto/Aes128CtrEncryptedStream.h | 166 + .../include/tc/crypto/Aes128CtrEncryptor.h | 115 + .../include/tc/crypto/Aes128EcbEncryptor.h | 84 + .../include/tc/crypto/Aes128XtsEncryptor.h | 104 + .../include/tc/crypto/Aes192CbcEncryptor.h | 94 + .../include/tc/crypto/Aes192CtrEncryptor.h | 115 + .../include/tc/crypto/Aes192EcbEncryptor.h | 84 + .../include/tc/crypto/Aes256CbcEncryptor.h | 94 + .../include/tc/crypto/Aes256CtrEncryptor.h | 115 + .../include/tc/crypto/Aes256EcbEncryptor.h | 84 + .../include/tc/crypto/Aes256XtsEncryptor.h | 104 + .../include/tc/crypto/AesEncryptor.h | 140 + .../include/tc/crypto/CbcEncryptor.h | 177 + .../include/tc/crypto/CryptoException.h | 57 + .../include/tc/crypto/CtrEncryptor.h | 154 + .../include/tc/crypto/EcbEncryptor.h | 146 + .../include/tc/crypto/HmacGenerator.h | 219 + .../include/tc/crypto/HmacMd5Generator.h | 45 + .../include/tc/crypto/HmacSha1Generator.h | 45 + .../include/tc/crypto/HmacSha256Generator.h | 45 + .../include/tc/crypto/HmacSha512Generator.h | 45 + .../include/tc/crypto/Md5Generator.h | 221 + .../include/tc/crypto/Pbkdf1KeyDeriver.h | 116 + .../include/tc/crypto/Pbkdf1Md5KeyDeriver.h | 47 + .../include/tc/crypto/Pbkdf1Sha1KeyDeriver.h | 47 + .../include/tc/crypto/Pbkdf2KeyDeriver.h | 118 + .../include/tc/crypto/Pbkdf2Sha1KeyDeriver.h | 47 + .../tc/crypto/Pbkdf2Sha256KeyDeriver.h | 47 + .../tc/crypto/Pbkdf2Sha512KeyDeriver.h | 47 + .../tc/crypto/PseudoRandomByteGenerator.h | 65 + .../libtoolchain/include/tc/crypto/RsaKey.h | 75 + .../include/tc/crypto/RsaKeyGenerator.h | 71 + .../include/tc/crypto/RsaOaepEncryptor.h | 246 + .../tc/crypto/RsaOaepSha256Encryptor.h | 201 + .../tc/crypto/RsaOaepSha512Encryptor.h | 139 + .../include/tc/crypto/RsaPkcs1Md5Signer.h | 144 + .../include/tc/crypto/RsaPkcs1Sha1Signer.h | 144 + .../include/tc/crypto/RsaPkcs1Sha256Signer.h | 144 + .../include/tc/crypto/RsaPkcs1Sha512Signer.h | 144 + .../include/tc/crypto/RsaPkcs1Signer.h | 172 + .../include/tc/crypto/RsaPssSha256Signer.h | 144 + .../include/tc/crypto/RsaPssSha512Signer.h | 144 + .../include/tc/crypto/RsaPssSigner.h | 208 + .../include/tc/crypto/Sha1Generator.h | 221 + .../include/tc/crypto/Sha256Generator.h | 216 + .../include/tc/crypto/Sha512Generator.h | 217 + .../include/tc/crypto/XtsEncryptor.h | 167 + .../include/tc/crypto/detail/AesImpl.h | 102 + .../include/tc/crypto/detail/BlockUtilImpl.h | 69 + .../include/tc/crypto/detail/CbcModeImpl.h | 130 + .../include/tc/crypto/detail/CtrModeImpl.h | 113 + .../include/tc/crypto/detail/EcbModeImpl.h | 147 + .../include/tc/crypto/detail/HmacImpl.h | 107 + .../include/tc/crypto/detail/Md5Impl.h | 44 + .../include/tc/crypto/detail/Pbkdf1Impl.h | 159 + .../include/tc/crypto/detail/Pbkdf2Impl.h | 179 + .../include/tc/crypto/detail/PrbgImpl.h | 61 + .../include/tc/crypto/detail/RsaImpl.h | 119 + .../tc/crypto/detail/RsaKeyGeneratorImpl.h | 79 + .../include/tc/crypto/detail/RsaOaepPadding.h | 169 + .../tc/crypto/detail/RsaPkcs1Padding.h | 117 + .../include/tc/crypto/detail/RsaPssPadding.h | 260 + .../include/tc/crypto/detail/Sha1Impl.h | 44 + .../include/tc/crypto/detail/Sha2Impl.h | 57 + .../include/tc/crypto/detail/XtsModeImpl.h | 330 + ctrtool/deps/libtoolchain/include/tc/io.h | 48 + .../include/tc/io/BasicPathResolver.h | 109 + .../include/tc/io/ConcatenatedStream.h | 205 + .../tc/io/DirectoryNotEmptyException.h | 57 + .../tc/io/DirectoryNotFoundException.h | 57 + .../libtoolchain/include/tc/io/FileAccess.h | 23 + .../include/tc/io/FileExistsException.h | 57 + .../libtoolchain/include/tc/io/FileMode.h | 26 + .../include/tc/io/FileNotFoundException.h | 57 + .../libtoolchain/include/tc/io/FileStream.h | 240 + .../libtoolchain/include/tc/io/IFileSystem.h | 124 + .../libtoolchain/include/tc/io/IOException.h | 57 + .../deps/libtoolchain/include/tc/io/IOUtil.h | 63 + .../include/tc/io/IPathResolver.h | 40 + .../include/tc/io/IPortablePathResolver.h | 37 + .../include/tc/io/IReadableSink.h | 28 + .../deps/libtoolchain/include/tc/io/ISink.h | 46 + .../deps/libtoolchain/include/tc/io/ISource.h | 39 + .../deps/libtoolchain/include/tc/io/IStream.h | 117 + .../include/tc/io/LocalFileSystem.h | 143 + .../libtoolchain/include/tc/io/MemorySource.h | 68 + .../libtoolchain/include/tc/io/MemoryStream.h | 155 + .../include/tc/io/OverlayedSource.h | 96 + .../include/tc/io/PaddingSource.h | 59 + .../deps/libtoolchain/include/tc/io/Path.h | 253 + .../include/tc/io/PathTooLongException.h | 57 + .../libtoolchain/include/tc/io/PathUtil.h | 39 + .../libtoolchain/include/tc/io/SeekOrigin.h | 23 + .../libtoolchain/include/tc/io/StreamSink.h | 76 + .../libtoolchain/include/tc/io/StreamSource.h | 66 + .../libtoolchain/include/tc/io/StreamUtil.h | 37 + .../include/tc/io/SubFileSystem.h | 136 + .../deps/libtoolchain/include/tc/io/SubSink.h | 77 + .../libtoolchain/include/tc/io/SubSource.h | 64 + .../libtoolchain/include/tc/io/SubStream.h | 155 + .../include/tc/io/VirtualFileSystem.h | 212 + ctrtool/deps/libtoolchain/include/tc/os.h | 13 + .../libtoolchain/include/tc/os/Environment.h | 28 + .../libtoolchain/include/tc/os/UnicodeMain.h | 71 + ctrtool/deps/libtoolchain/include/tc/string.h | 12 + .../include/tc/string/TranscodeUtil.h | 77 + .../include/tc/string/detail/utf16.h | 22 + .../include/tc/string/detail/utf8.h | 39 + ctrtool/deps/libtoolchain/include/tc/types.h | 65 + ctrtool/deps/libtoolchain/makefile | 197 + ctrtool/deps/libtoolchain/src/ByteData.cpp | 110 + ctrtool/deps/libtoolchain/src/Exception.cpp | 46 + .../src/PlatformErrorHandlingUtil.cpp | 41 + .../deps/libtoolchain/src/cli/FormatUtil.cpp | 222 + .../libtoolchain/src/cli/OptionParser.cpp | 185 + .../src/crypto/Aes128CbcEncryptedStream.cpp | 328 + .../src/crypto/Aes128CbcEncryptor.cpp | 15 + .../src/crypto/Aes128CtrEncryptedStream.cpp | 284 + .../src/crypto/Aes128CtrEncryptor.cpp | 24 + .../src/crypto/Aes128EcbEncryptor.cpp | 15 + .../src/crypto/Aes128XtsEncryptor.cpp | 15 + .../src/crypto/Aes192CbcEncryptor.cpp | 15 + .../src/crypto/Aes192CtrEncryptor.cpp | 24 + .../src/crypto/Aes192EcbEncryptor.cpp | 15 + .../src/crypto/Aes256CbcEncryptor.cpp | 15 + .../src/crypto/Aes256CtrEncryptor.cpp | 24 + .../src/crypto/Aes256EcbEncryptor.cpp | 15 + .../src/crypto/Aes256XtsEncryptor.cpp | 15 + .../src/crypto/HmacMd5Generator.cpp | 9 + .../src/crypto/HmacSha1Generator.cpp | 9 + .../src/crypto/HmacSha256Generator.cpp | 9 + .../src/crypto/HmacSha512Generator.cpp | 9 + .../libtoolchain/src/crypto/Md5Generator.cpp | 11 + .../src/crypto/Pbkdf1Md5KeyDeriver.cpp | 8 + .../src/crypto/Pbkdf1Sha1KeyDeriver.cpp | 8 + .../src/crypto/Pbkdf2Sha1KeyDeriver.cpp | 8 + .../src/crypto/Pbkdf2Sha256KeyDeriver.cpp | 8 + .../src/crypto/Pbkdf2Sha512KeyDeriver.cpp | 8 + .../src/crypto/PseudoRandomByteGenerator.cpp | 7 + .../deps/libtoolchain/src/crypto/RsaKey.cpp | 29 + .../src/crypto/RsaKeyGenerator.cpp | 7 + .../src/crypto/RsaOaepSha256Encryptor.cpp | 43 + .../src/crypto/RsaOaepSha512Encryptor.cpp | 29 + .../src/crypto/RsaPkcs1Md5Signer.cpp | 43 + .../src/crypto/RsaPkcs1Sha1Signer.cpp | 43 + .../src/crypto/RsaPkcs1Sha256Signer.cpp | 43 + .../src/crypto/RsaPkcs1Sha512Signer.cpp | 43 + .../src/crypto/RsaPssSha256Signer.cpp | 43 + .../src/crypto/RsaPssSha512Signer.cpp | 43 + .../libtoolchain/src/crypto/Sha1Generator.cpp | 12 + .../src/crypto/Sha256Generator.cpp | 11 + .../src/crypto/Sha512Generator.cpp | 11 + .../src/crypto/detail/AesImpl.cpp | 51 + .../src/crypto/detail/Md5Impl.cpp | 44 + .../src/crypto/detail/PrbgImpl.cpp | 50 + .../src/crypto/detail/RsaImpl.cpp | 140 + .../src/crypto/detail/RsaKeyGeneratorImpl.cpp | 85 + .../src/crypto/detail/Sha1Impl.cpp | 44 + .../src/crypto/detail/Sha2Impl.cpp | 58 + .../libtoolchain/src/io/BasicPathResolver.cpp | 97 + .../src/io/ConcatenatedStream.cpp | 370 + .../deps/libtoolchain/src/io/FileStream.cpp | 752 ++ ctrtool/deps/libtoolchain/src/io/IOUtil.cpp | 40 + .../libtoolchain/src/io/LocalFileSystem.cpp | 419 + .../deps/libtoolchain/src/io/MemorySource.cpp | 49 + .../deps/libtoolchain/src/io/MemoryStream.cpp | 155 + .../libtoolchain/src/io/OverlayedSource.cpp | 143 + .../libtoolchain/src/io/PaddingSource.cpp | 34 + ctrtool/deps/libtoolchain/src/io/Path.cpp | 331 + ctrtool/deps/libtoolchain/src/io/PathUtil.cpp | 12 + .../deps/libtoolchain/src/io/StreamSink.cpp | 54 + .../deps/libtoolchain/src/io/StreamSource.cpp | 55 + .../deps/libtoolchain/src/io/StreamUtil.cpp | 22 + .../libtoolchain/src/io/SubFileSystem.cpp | 216 + ctrtool/deps/libtoolchain/src/io/SubSink.cpp | 73 + .../deps/libtoolchain/src/io/SubSource.cpp | 66 + .../deps/libtoolchain/src/io/SubStream.cpp | 183 + .../libtoolchain/src/io/VirtualFileSystem.cpp | 192 + .../deps/libtoolchain/src/os/Environment.cpp | 40 + .../libtoolchain/src/string/TranscodeUtil.cpp | 165 + ctrtool/deps/libtoolchain/src/types.cpp | 26 + .../libtoolchain/test/ByteData_TestClass.cpp | 671 ++ .../libtoolchain/test/ByteData_TestClass.h | 23 + .../libtoolchain/test/FileSystemTestUtil.cpp | 3 + .../libtoolchain/test/FileSystemTestUtil.h | 79 + ctrtool/deps/libtoolchain/test/ITestClass.h | 8 + .../libtoolchain/test/Optional_TestClass.cpp | 300 + .../libtoolchain/test/Optional_TestClass.h | 20 + ctrtool/deps/libtoolchain/test/PbkdfUtil.cpp | 245 + ctrtool/deps/libtoolchain/test/PbkdfUtil.h | 34 + .../deps/libtoolchain/test/RsaOaepUtil.cpp | 172 + ctrtool/deps/libtoolchain/test/RsaOaepUtil.h | 34 + .../deps/libtoolchain/test/RsaPkcs1Util.cpp | 146 + ctrtool/deps/libtoolchain/test/RsaPkcs1Util.h | 31 + ctrtool/deps/libtoolchain/test/RsaPssUtil.cpp | 143 + ctrtool/deps/libtoolchain/test/RsaPssUtil.h | 32 + .../deps/libtoolchain/test/SinkTestUtil.cpp | 90 + ctrtool/deps/libtoolchain/test/SinkTestUtil.h | 42 + .../deps/libtoolchain/test/SourceTestUtil.cpp | 32 + .../deps/libtoolchain/test/SourceTestUtil.h | 9 + .../deps/libtoolchain/test/StreamTestUtil.cpp | 125 + .../deps/libtoolchain/test/StreamTestUtil.h | 154 + .../test/bn_binaryutils_TestClass.cpp | 173 + .../test/bn_binaryutils_TestClass.h | 25 + .../test/bn_bitarrayByteBEBitBE_TestClass.cpp | 238 + .../test/bn_bitarrayByteBEBitBE_TestClass.h | 19 + .../test/bn_bitarrayByteBEBitLE_TestClass.cpp | 238 + .../test/bn_bitarrayByteBEBitLE_TestClass.h | 19 + .../test/bn_bitarrayByteLEBitBE_TestClass.cpp | 238 + .../test/bn_bitarrayByteLEBitBE_TestClass.h | 19 + .../test/bn_bitarrayByteLEBitLE_TestClass.cpp | 238 + .../test/bn_bitarrayByteLEBitLE_TestClass.h | 19 + .../libtoolchain/test/bn_endian_TestClass.cpp | 778 ++ .../libtoolchain/test/bn_endian_TestClass.h | 32 + .../libtoolchain/test/bn_pad_TestClass.cpp | 55 + .../deps/libtoolchain/test/bn_pad_TestClass.h | 14 + .../libtoolchain/test/bn_string_TestClass.cpp | 219 + .../libtoolchain/test/bn_string_TestClass.h | 16 + .../test/cli_FormatUtil_TestClass.cpp | 366 + .../test/cli_FormatUtil_TestClass.h | 15 + .../test/cli_OptionParser_TestClass.cpp | 770 ++ .../test/cli_OptionParser_TestClass.h | 20 + ...pto_Aes128CbcEncryptedStream_TestClass.cpp | 265 + ...rypto_Aes128CbcEncryptedStream_TestClass.h | 29 + .../crypto_Aes128CbcEncryptor_TestClass.cpp | 570 + .../crypto_Aes128CbcEncryptor_TestClass.h | 33 + ...pto_Aes128CtrEncryptedStream_TestClass.cpp | 265 + ...rypto_Aes128CtrEncryptedStream_TestClass.h | 29 + .../crypto_Aes128CtrEncryptor_TestClass.cpp | 543 + .../crypto_Aes128CtrEncryptor_TestClass.h | 34 + .../crypto_Aes128EcbEncryptor_TestClass.cpp | 523 + .../crypto_Aes128EcbEncryptor_TestClass.h | 32 + .../test/crypto_Aes128Encryptor_TestClass.cpp | 1016 ++ .../test/crypto_Aes128Encryptor_TestClass.h | 30 + .../crypto_Aes128XtsEncryptor_TestClass.cpp | 691 ++ .../crypto_Aes128XtsEncryptor_TestClass.h | 35 + .../crypto_Aes192CbcEncryptor_TestClass.cpp | 570 + .../crypto_Aes192CbcEncryptor_TestClass.h | 33 + .../crypto_Aes192CtrEncryptor_TestClass.cpp | 543 + .../crypto_Aes192CtrEncryptor_TestClass.h | 34 + .../crypto_Aes192EcbEncryptor_TestClass.cpp | 523 + .../crypto_Aes192EcbEncryptor_TestClass.h | 32 + .../test/crypto_Aes192Encryptor_TestClass.cpp | 1336 +++ .../test/crypto_Aes192Encryptor_TestClass.h | 30 + .../crypto_Aes256CbcEncryptor_TestClass.cpp | 570 + .../crypto_Aes256CbcEncryptor_TestClass.h | 33 + .../crypto_Aes256CtrEncryptor_TestClass.cpp | 543 + .../crypto_Aes256CtrEncryptor_TestClass.h | 34 + .../crypto_Aes256EcbEncryptor_TestClass.cpp | 523 + .../crypto_Aes256EcbEncryptor_TestClass.h | 32 + .../test/crypto_Aes256Encryptor_TestClass.cpp | 1656 +++ .../test/crypto_Aes256Encryptor_TestClass.h | 30 + .../crypto_Aes256XtsEncryptor_TestClass.cpp | 575 + .../crypto_Aes256XtsEncryptor_TestClass.h | 35 + .../crypto_HmacMd5Generator_TestClass.cpp | 547 + .../test/crypto_HmacMd5Generator_TestClass.h | 34 + .../crypto_HmacSha1Generator_TestClass.cpp | 547 + .../test/crypto_HmacSha1Generator_TestClass.h | 34 + .../crypto_HmacSha256Generator_TestClass.cpp | 547 + .../crypto_HmacSha256Generator_TestClass.h | 34 + .../crypto_HmacSha512Generator_TestClass.cpp | 547 + .../crypto_HmacSha512Generator_TestClass.h | 34 + .../test/crypto_Md5Generator_TestClass.cpp | 490 + .../test/crypto_Md5Generator_TestClass.h | 18 + .../crypto_Pbkdf1Md5KeyDeriver_TestClass.cpp | 285 + .../crypto_Pbkdf1Md5KeyDeriver_TestClass.h | 15 + .../crypto_Pbkdf1Sha1KeyDeriver_TestClass.cpp | 285 + .../crypto_Pbkdf1Sha1KeyDeriver_TestClass.h | 15 + .../crypto_Pbkdf2Sha1KeyDeriver_TestClass.cpp | 286 + .../crypto_Pbkdf2Sha1KeyDeriver_TestClass.h | 15 + ...rypto_Pbkdf2Sha256KeyDeriver_TestClass.cpp | 287 + .../crypto_Pbkdf2Sha256KeyDeriver_TestClass.h | 15 + ...rypto_Pbkdf2Sha512KeyDeriver_TestClass.cpp | 286 + .../crypto_Pbkdf2Sha512KeyDeriver_TestClass.h | 15 + ...to_PseudoRandomByteGenerator_TestClass.cpp | 226 + ...ypto_PseudoRandomByteGenerator_TestClass.h | 14 + ...o_Rsa1024OaepSha256Encryptor_TestClass.cpp | 673 ++ ...pto_Rsa1024OaepSha256Encryptor_TestClass.h | 23 + ...crypto_Rsa1024Pkcs1Md5Signer_TestClass.cpp | 498 + .../crypto_Rsa1024Pkcs1Md5Signer_TestClass.h | 22 + ...rypto_Rsa1024Pkcs1Sha1Signer_TestClass.cpp | 498 + .../crypto_Rsa1024Pkcs1Sha1Signer_TestClass.h | 22 + ...pto_Rsa1024Pkcs1Sha256Signer_TestClass.cpp | 498 + ...rypto_Rsa1024Pkcs1Sha256Signer_TestClass.h | 22 + ...pto_Rsa1024Pkcs1Sha512Signer_TestClass.cpp | 498 + ...rypto_Rsa1024Pkcs1Sha512Signer_TestClass.h | 22 + ...rypto_Rsa1024PssSha256Signer_TestClass.cpp | 502 + .../crypto_Rsa1024PssSha256Signer_TestClass.h | 22 + ...rypto_Rsa1024PssSha512Signer_TestClass.cpp | 503 + .../crypto_Rsa1024PssSha512Signer_TestClass.h | 22 + ...o_Rsa2048OaepSha256Encryptor_TestClass.cpp | 673 ++ ...pto_Rsa2048OaepSha256Encryptor_TestClass.h | 23 + ...o_Rsa2048OaepSha512Encryptor_TestClass.cpp | 673 ++ ...pto_Rsa2048OaepSha512Encryptor_TestClass.h | 23 + ...crypto_Rsa2048Pkcs1Md5Signer_TestClass.cpp | 498 + .../crypto_Rsa2048Pkcs1Md5Signer_TestClass.h | 22 + ...rypto_Rsa2048Pkcs1Sha1Signer_TestClass.cpp | 498 + .../crypto_Rsa2048Pkcs1Sha1Signer_TestClass.h | 22 + ...pto_Rsa2048Pkcs1Sha256Signer_TestClass.cpp | 498 + ...rypto_Rsa2048Pkcs1Sha256Signer_TestClass.h | 22 + ...pto_Rsa2048Pkcs1Sha512Signer_TestClass.cpp | 498 + ...rypto_Rsa2048Pkcs1Sha512Signer_TestClass.h | 22 + ...rypto_Rsa2048PssSha256Signer_TestClass.cpp | 502 + .../crypto_Rsa2048PssSha256Signer_TestClass.h | 22 + ...rypto_Rsa2048PssSha512Signer_TestClass.cpp | 502 + .../crypto_Rsa2048PssSha512Signer_TestClass.h | 22 + ...o_Rsa4096OaepSha256Encryptor_TestClass.cpp | 673 ++ ...pto_Rsa4096OaepSha256Encryptor_TestClass.h | 23 + ...o_Rsa4096OaepSha512Encryptor_TestClass.cpp | 673 ++ ...pto_Rsa4096OaepSha512Encryptor_TestClass.h | 23 + ...crypto_Rsa4096Pkcs1Md5Signer_TestClass.cpp | 498 + .../crypto_Rsa4096Pkcs1Md5Signer_TestClass.h | 22 + ...rypto_Rsa4096Pkcs1Sha1Signer_TestClass.cpp | 498 + .../crypto_Rsa4096Pkcs1Sha1Signer_TestClass.h | 22 + ...pto_Rsa4096Pkcs1Sha256Signer_TestClass.cpp | 498 + ...rypto_Rsa4096Pkcs1Sha256Signer_TestClass.h | 22 + ...pto_Rsa4096Pkcs1Sha512Signer_TestClass.cpp | 498 + ...rypto_Rsa4096Pkcs1Sha512Signer_TestClass.h | 22 + ...rypto_Rsa4096PssSha256Signer_TestClass.cpp | 502 + .../crypto_Rsa4096PssSha256Signer_TestClass.h | 22 + ...rypto_Rsa4096PssSha512Signer_TestClass.cpp | 502 + .../crypto_Rsa4096PssSha512Signer_TestClass.h | 22 + .../test/crypto_Sha1Generator_TestClass.cpp | 462 + .../test/crypto_Sha1Generator_TestClass.h | 18 + .../test/crypto_Sha256Generator_TestClass.cpp | 462 + .../test/crypto_Sha256Generator_TestClass.h | 18 + .../test/crypto_Sha512Generator_TestClass.cpp | 462 + .../test/crypto_Sha512Generator_TestClass.h | 18 + .../test/io_BasicPathResolver_TestClass.cpp | 292 + .../test/io_BasicPathResolver_TestClass.h | 20 + .../test/io_ConcatenatedStream_TestClass.cpp | 2645 +++++ .../test/io_ConcatenatedStream_TestClass.h | 50 + .../test/io_FileStream_TestClass.cpp | 2070 ++++ .../test/io_FileStream_TestClass.h | 94 + .../test/io_LocalFileSystem_TestClass.cpp | 535 + .../test/io_LocalFileSystem_TestClass.h | 35 + .../test/io_MemorySource_TestClass.cpp | 200 + .../test/io_MemorySource_TestClass.h | 17 + .../test/io_MemoryStream_TestClass.cpp | 1028 ++ .../test/io_MemoryStream_TestClass.h | 43 + .../test/io_OverlayedSource_TestClass.cpp | 377 + .../test/io_OverlayedSource_TestClass.h | 21 + .../test/io_PaddingSource_TestClass.cpp | 134 + .../test/io_PaddingSource_TestClass.h | 13 + .../libtoolchain/test/io_Path_TestClass.cpp | 1197 ++ .../libtoolchain/test/io_Path_TestClass.h | 35 + .../test/io_StreamSink_TestClass.cpp | 307 + .../test/io_StreamSink_TestClass.h | 23 + .../test/io_StreamSource_TestClass.cpp | 206 + .../test/io_StreamSource_TestClass.h | 16 + .../test/io_SubFileSystem_TestClass.cpp | 736 ++ .../test/io_SubFileSystem_TestClass.h | 19 + .../test/io_SubSink_TestClass.cpp | 352 + .../libtoolchain/test/io_SubSink_TestClass.h | 24 + .../test/io_SubSource_TestClass.cpp | 141 + .../test/io_SubSource_TestClass.h | 13 + .../test/io_SubStream_TestClass.cpp | 344 + .../test/io_SubStream_TestClass.h | 18 + .../test/io_VirtualFileSystem_TestClass.cpp | 2543 +++++ .../test/io_VirtualFileSystem_TestClass.h | 21 + ctrtool/deps/libtoolchain/test/main.cpp | 195 + .../test/string_TranscodeUtil_TestClass.cpp | 69 + .../test/string_TranscodeUtil_TestClass.h | 13 + ctrtool/makefile | 177 + ctrtool/src/CciProcess.cpp | 567 + ctrtool/src/CciProcess.h | 70 + ctrtool/src/CiaProcess.cpp | 913 ++ ctrtool/src/CiaProcess.h | 128 + ctrtool/src/CrrProcess.cpp | 192 + ctrtool/src/CrrProcess.h | 46 + ctrtool/src/ExHeaderProcess.cpp | 993 ++ ctrtool/src/ExHeaderProcess.h | 92 + ctrtool/src/ExeFsProcess.cpp | 275 + ctrtool/src/ExeFsProcess.h | 49 + ctrtool/src/FirmProcess.cpp | 396 + ctrtool/src/FirmProcess.h | 71 + ctrtool/src/IvfcProcess.cpp | 199 + ctrtool/src/IvfcProcess.h | 50 + ctrtool/src/KeyBag.cpp | 945 ++ ctrtool/src/KeyBag.h | 128 + ctrtool/src/LzssProcess.cpp | 54 + ctrtool/src/LzssProcess.h | 25 + ctrtool/src/NcchProcess.cpp | 968 ++ ctrtool/src/NcchProcess.h | 92 + ctrtool/src/RomFsProcess.cpp | 231 + ctrtool/src/RomFsProcess.h | 48 + ctrtool/src/Settings.cpp | 704 ++ ctrtool/src/Settings.h | 175 + ctrtool/src/cwav.h | 152 + ctrtool/src/lzss.c | 107 + ctrtool/src/lzss.h | 13 + ctrtool/src/main.cpp | 245 + ctrtool/src/types.h | 15 + ctrtool/src/version.h | 7 + 681 files changed, 219734 insertions(+) create mode 100644 ctrtool/build/visualstudio/CTRTool.sln create mode 100644 ctrtool/build/visualstudio/CTRTool/CTRTool.vcxproj create mode 100644 ctrtool/build/visualstudio/CTRTool/CTRTool.vcxproj.filters create mode 100644 ctrtool/deps/libbroadon-es/LICENSE create mode 100644 ctrtool/deps/libbroadon-es/README.md create mode 100644 ctrtool/deps/libbroadon-es/build/visualstudio/libbroadon-es.sln create mode 100644 ctrtool/deps/libbroadon-es/build/visualstudio/libbroadon-es/libbroadon-es.vcxproj create mode 100644 ctrtool/deps/libbroadon-es/build/visualstudio/libbroadon-es/libbroadon-es.vcxproj.filters create mode 100644 ctrtool/deps/libbroadon-es/include/brd/es.h create mode 100644 ctrtool/deps/libbroadon-es/include/brd/es/es_cert.h create mode 100644 ctrtool/deps/libbroadon-es/include/brd/es/es_sign.h create mode 100644 ctrtool/deps/libbroadon-es/include/brd/es/es_ticket.h create mode 100644 ctrtool/deps/libbroadon-es/include/brd/es/es_tmd.h create mode 100644 ctrtool/deps/libbroadon-es/include/brd/es/types.h create mode 100644 ctrtool/deps/libbroadon-es/makefile create mode 100644 ctrtool/deps/libbroadon-es/src/Dummy.cpp create mode 100644 ctrtool/deps/libfmt/LICENSE.rst create mode 100644 ctrtool/deps/libfmt/README.md create mode 100644 ctrtool/deps/libfmt/build/visualstudio/libfmt.sln create mode 100644 ctrtool/deps/libfmt/build/visualstudio/libfmt/libfmt.vcxproj create mode 100644 ctrtool/deps/libfmt/build/visualstudio/libfmt/libfmt.vcxproj.filters create mode 100644 ctrtool/deps/libfmt/include/fmt/args.h create mode 100644 ctrtool/deps/libfmt/include/fmt/chrono.h create mode 100644 ctrtool/deps/libfmt/include/fmt/color.h create mode 100644 ctrtool/deps/libfmt/include/fmt/compile.h create mode 100644 ctrtool/deps/libfmt/include/fmt/core.h create mode 100644 ctrtool/deps/libfmt/include/fmt/format-inl.h create mode 100644 ctrtool/deps/libfmt/include/fmt/format.h create mode 100644 ctrtool/deps/libfmt/include/fmt/locale.h create mode 100644 ctrtool/deps/libfmt/include/fmt/os.h create mode 100644 ctrtool/deps/libfmt/include/fmt/ostream.h create mode 100644 ctrtool/deps/libfmt/include/fmt/printf.h create mode 100644 ctrtool/deps/libfmt/include/fmt/ranges.h create mode 100644 ctrtool/deps/libfmt/include/fmt/xchar.h create mode 100644 ctrtool/deps/libfmt/makefile create mode 100644 ctrtool/deps/libfmt/src/format.cc create mode 100644 ctrtool/deps/libfmt/src/os.cc create mode 100644 ctrtool/deps/libmbedtls/BUILDING.md create mode 100644 ctrtool/deps/libmbedtls/LICENSE create mode 100644 ctrtool/deps/libmbedtls/README.md create mode 100644 ctrtool/deps/libmbedtls/apache-2.0.txt create mode 100644 ctrtool/deps/libmbedtls/build/visualstudio/libmbedtls.sln create mode 100644 ctrtool/deps/libmbedtls/build/visualstudio/libmbedtls/libmbedtls.vcxproj create mode 100644 ctrtool/deps/libmbedtls/build/visualstudio/libmbedtls/libmbedtls.vcxproj.filters create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/aes.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/aesni.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/arc4.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/aria.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/asn1.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/asn1write.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/base64.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/bignum.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/blowfish.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/bn_mul.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/camellia.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ccm.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/certs.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/chacha20.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/chachapoly.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/check_config.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/cipher.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/cipher_internal.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/cmac.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/compat-1.3.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/config.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ctr_drbg.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/debug.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/des.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/dhm.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ecdh.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ecdsa.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ecjpake.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ecp.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ecp_internal.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/entropy.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/entropy_poll.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/error.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/gcm.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/havege.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/hkdf.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/hmac_drbg.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/md.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/md2.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/md4.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/md5.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/md_internal.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/memory_buffer_alloc.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/net.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/net_sockets.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/nist_kw.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/oid.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/padlock.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/pem.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/pk.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/pk_internal.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/pkcs11.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/pkcs12.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/pkcs5.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/platform.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/platform_time.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/platform_util.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/poly1305.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ripemd160.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/rsa.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/rsa_internal.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/sha1.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/sha256.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/sha512.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ssl.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ssl_cache.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ssl_ciphersuites.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ssl_cookie.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ssl_internal.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/ssl_ticket.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/threading.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/timing.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/version.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/x509.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/x509_crl.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/x509_crt.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/x509_csr.h create mode 100644 ctrtool/deps/libmbedtls/include/mbedtls/xtea.h create mode 100644 ctrtool/deps/libmbedtls/makefile create mode 100644 ctrtool/deps/libmbedtls/src/aes.c create mode 100644 ctrtool/deps/libmbedtls/src/aesni.c create mode 100644 ctrtool/deps/libmbedtls/src/arc4.c create mode 100644 ctrtool/deps/libmbedtls/src/aria.c create mode 100644 ctrtool/deps/libmbedtls/src/asn1parse.c create mode 100644 ctrtool/deps/libmbedtls/src/asn1write.c create mode 100644 ctrtool/deps/libmbedtls/src/base64.c create mode 100644 ctrtool/deps/libmbedtls/src/bignum.c create mode 100644 ctrtool/deps/libmbedtls/src/blowfish.c create mode 100644 ctrtool/deps/libmbedtls/src/camellia.c create mode 100644 ctrtool/deps/libmbedtls/src/ccm.c create mode 100644 ctrtool/deps/libmbedtls/src/certs.c create mode 100644 ctrtool/deps/libmbedtls/src/chacha20.c create mode 100644 ctrtool/deps/libmbedtls/src/chachapoly.c create mode 100644 ctrtool/deps/libmbedtls/src/cipher.c create mode 100644 ctrtool/deps/libmbedtls/src/cipher_wrap.c create mode 100644 ctrtool/deps/libmbedtls/src/cmac.c create mode 100644 ctrtool/deps/libmbedtls/src/ctr_drbg.c create mode 100644 ctrtool/deps/libmbedtls/src/debug.c create mode 100644 ctrtool/deps/libmbedtls/src/des.c create mode 100644 ctrtool/deps/libmbedtls/src/dhm.c create mode 100644 ctrtool/deps/libmbedtls/src/ecdh.c create mode 100644 ctrtool/deps/libmbedtls/src/ecdsa.c create mode 100644 ctrtool/deps/libmbedtls/src/ecjpake.c create mode 100644 ctrtool/deps/libmbedtls/src/ecp.c create mode 100644 ctrtool/deps/libmbedtls/src/ecp_curves.c create mode 100644 ctrtool/deps/libmbedtls/src/entropy.c create mode 100644 ctrtool/deps/libmbedtls/src/entropy_poll.c create mode 100644 ctrtool/deps/libmbedtls/src/error.c create mode 100644 ctrtool/deps/libmbedtls/src/gcm.c create mode 100644 ctrtool/deps/libmbedtls/src/havege.c create mode 100644 ctrtool/deps/libmbedtls/src/hkdf.c create mode 100644 ctrtool/deps/libmbedtls/src/hmac_drbg.c create mode 100644 ctrtool/deps/libmbedtls/src/md.c create mode 100644 ctrtool/deps/libmbedtls/src/md2.c create mode 100644 ctrtool/deps/libmbedtls/src/md4.c create mode 100644 ctrtool/deps/libmbedtls/src/md5.c create mode 100644 ctrtool/deps/libmbedtls/src/md_wrap.c create mode 100644 ctrtool/deps/libmbedtls/src/memory_buffer_alloc.c create mode 100644 ctrtool/deps/libmbedtls/src/net_sockets.c create mode 100644 ctrtool/deps/libmbedtls/src/nist_kw.c create mode 100644 ctrtool/deps/libmbedtls/src/oid.c create mode 100644 ctrtool/deps/libmbedtls/src/padlock.c create mode 100644 ctrtool/deps/libmbedtls/src/pem.c create mode 100644 ctrtool/deps/libmbedtls/src/pk.c create mode 100644 ctrtool/deps/libmbedtls/src/pk_wrap.c create mode 100644 ctrtool/deps/libmbedtls/src/pkcs11.c create mode 100644 ctrtool/deps/libmbedtls/src/pkcs12.c create mode 100644 ctrtool/deps/libmbedtls/src/pkcs5.c create mode 100644 ctrtool/deps/libmbedtls/src/pkparse.c create mode 100644 ctrtool/deps/libmbedtls/src/pkwrite.c create mode 100644 ctrtool/deps/libmbedtls/src/platform.c create mode 100644 ctrtool/deps/libmbedtls/src/platform_util.c create mode 100644 ctrtool/deps/libmbedtls/src/poly1305.c create mode 100644 ctrtool/deps/libmbedtls/src/ripemd160.c create mode 100644 ctrtool/deps/libmbedtls/src/rsa.c create mode 100644 ctrtool/deps/libmbedtls/src/rsa_internal.c create mode 100644 ctrtool/deps/libmbedtls/src/sha1.c create mode 100644 ctrtool/deps/libmbedtls/src/sha256.c create mode 100644 ctrtool/deps/libmbedtls/src/sha512.c create mode 100644 ctrtool/deps/libmbedtls/src/ssl_cache.c create mode 100644 ctrtool/deps/libmbedtls/src/ssl_ciphersuites.c create mode 100644 ctrtool/deps/libmbedtls/src/ssl_cli.c create mode 100644 ctrtool/deps/libmbedtls/src/ssl_cookie.c create mode 100644 ctrtool/deps/libmbedtls/src/ssl_srv.c create mode 100644 ctrtool/deps/libmbedtls/src/ssl_ticket.c create mode 100644 ctrtool/deps/libmbedtls/src/ssl_tls.c create mode 100644 ctrtool/deps/libmbedtls/src/threading.c create mode 100644 ctrtool/deps/libmbedtls/src/timing.c create mode 100644 ctrtool/deps/libmbedtls/src/version.c create mode 100644 ctrtool/deps/libmbedtls/src/version_features.c create mode 100644 ctrtool/deps/libmbedtls/src/x509.c create mode 100644 ctrtool/deps/libmbedtls/src/x509_create.c create mode 100644 ctrtool/deps/libmbedtls/src/x509_crl.c create mode 100644 ctrtool/deps/libmbedtls/src/x509_crt.c create mode 100644 ctrtool/deps/libmbedtls/src/x509_csr.c create mode 100644 ctrtool/deps/libmbedtls/src/x509write_crt.c create mode 100644 ctrtool/deps/libmbedtls/src/x509write_csr.c create mode 100644 ctrtool/deps/libmbedtls/src/xtea.c create mode 100644 ctrtool/deps/libnintendo-n3ds/LICENSE create mode 100644 ctrtool/deps/libnintendo-n3ds/README.md create mode 100644 ctrtool/deps/libnintendo-n3ds/build/visualstudio/libnintendo-n3ds.sln create mode 100644 ctrtool/deps/libnintendo-n3ds/build/visualstudio/libnintendo-n3ds/libnintendo-n3ds.vcxproj create mode 100644 ctrtool/deps/libnintendo-n3ds/build/visualstudio/libnintendo-n3ds/libnintendo-n3ds.vcxproj.filters create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/CciFsSnapshotGenerator.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/CiaFsSnapshotGenerator.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/CtrKeyGenerator.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/ExeFsSnapshotGenerator.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/IvfcStream.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/RomFsSnapshotGenerator.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/bcwav.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/cci.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/cia.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/cro.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/crr.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/es.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/es/Certificate.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/es/ISigner.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/es/RsaSigner.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/es/Signature.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/es/Ticket.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/es/TitleMetaData.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/exefs.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/exheader.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/firm.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/ivfc.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/ncch.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/romfs.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/smdh.h create mode 100644 ctrtool/deps/libnintendo-n3ds/include/ntd/n3ds/systemupdater.h create mode 100644 ctrtool/deps/libnintendo-n3ds/makefile create mode 100644 ctrtool/deps/libnintendo-n3ds/src/CciFsSnapshotGenerator.cpp create mode 100644 ctrtool/deps/libnintendo-n3ds/src/CiaFsSnapshotGenerator.cpp create mode 100644 ctrtool/deps/libnintendo-n3ds/src/CtrKeyGenerator.cpp create mode 100644 ctrtool/deps/libnintendo-n3ds/src/ExeFsSnapshotGenerator.cpp create mode 100644 ctrtool/deps/libnintendo-n3ds/src/IvfcStream.cpp create mode 100644 ctrtool/deps/libnintendo-n3ds/src/RomFsSnapshotGenerator.cpp create mode 100644 ctrtool/deps/libnintendo-n3ds/src/es/Certificate.cpp create mode 100644 ctrtool/deps/libnintendo-n3ds/src/es/RsaSigner.cpp create mode 100644 ctrtool/deps/libnintendo-n3ds/src/es/Signature.cpp create mode 100644 ctrtool/deps/libnintendo-n3ds/src/es/Ticket.cpp create mode 100644 ctrtool/deps/libnintendo-n3ds/src/es/TitleMetaData.cpp create mode 100644 ctrtool/deps/libtoolchain/BUILDING.md create mode 100644 ctrtool/deps/libtoolchain/Doxyfile create mode 100644 ctrtool/deps/libtoolchain/DoxygenMainpage.md create mode 100644 ctrtool/deps/libtoolchain/LICENSE create mode 100644 ctrtool/deps/libtoolchain/README.md create mode 100644 ctrtool/deps/libtoolchain/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj create mode 100644 ctrtool/deps/libtoolchain/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj.filters create mode 100644 ctrtool/deps/libtoolchain/build/visualstudio/libtoolchain.sln create mode 100644 ctrtool/deps/libtoolchain/build/visualstudio/libtoolchain/libtoolchain.vcxproj create mode 100644 ctrtool/deps/libtoolchain/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters create mode 100644 ctrtool/deps/libtoolchain/include/tc.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/AccessViolationException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/ArgumentException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/ArgumentNullException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/ArgumentOutOfRangeException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/ArithmeticException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/ByteData.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/Exception.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/InvalidOperationException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/NotImplementedException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/NotSupportedException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/ObjectDisposedException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/Optional.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/OutOfMemoryException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/OverflowException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/PlatformErrorHandlingUtil.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/ResourceStatus.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/SecurityException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/UnauthorisedAccessException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/bn.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/bn/binary_utils.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/bn/bitarray.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/bn/endian_types.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/bn/pad.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/bn/string.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/cli.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/cli/FormatUtil.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/cli/OptionParser.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes128CbcEncryptedStream.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes128CbcEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes128CtrEncryptedStream.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes128CtrEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes128EcbEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes128XtsEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes192CbcEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes192CtrEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes192EcbEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes256CbcEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes256CtrEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes256EcbEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Aes256XtsEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/AesEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/CbcEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/CryptoException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/CtrEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/EcbEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/HmacGenerator.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/HmacMd5Generator.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/HmacSha1Generator.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/HmacSha256Generator.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/HmacSha512Generator.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Md5Generator.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Pbkdf1KeyDeriver.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Pbkdf1Md5KeyDeriver.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Pbkdf1Sha1KeyDeriver.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Pbkdf2KeyDeriver.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Pbkdf2Sha1KeyDeriver.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Pbkdf2Sha256KeyDeriver.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Pbkdf2Sha512KeyDeriver.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/PseudoRandomByteGenerator.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaKey.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaKeyGenerator.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaOaepEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaOaepSha256Encryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaOaepSha512Encryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaPkcs1Md5Signer.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaPkcs1Sha1Signer.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaPkcs1Sha256Signer.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaPkcs1Sha512Signer.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaPkcs1Signer.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaPssSha256Signer.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaPssSha512Signer.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/RsaPssSigner.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Sha1Generator.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Sha256Generator.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/Sha512Generator.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/XtsEncryptor.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/AesImpl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/BlockUtilImpl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/CbcModeImpl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/CtrModeImpl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/EcbModeImpl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/HmacImpl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/Md5Impl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/Pbkdf1Impl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/Pbkdf2Impl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/PrbgImpl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/RsaImpl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/RsaKeyGeneratorImpl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/RsaOaepPadding.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/RsaPkcs1Padding.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/RsaPssPadding.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/Sha1Impl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/Sha2Impl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/crypto/detail/XtsModeImpl.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/BasicPathResolver.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/ConcatenatedStream.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/DirectoryNotEmptyException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/DirectoryNotFoundException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/FileAccess.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/FileExistsException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/FileMode.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/FileNotFoundException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/FileStream.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/IFileSystem.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/IOException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/IOUtil.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/IPathResolver.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/IPortablePathResolver.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/IReadableSink.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/ISink.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/ISource.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/IStream.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/LocalFileSystem.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/MemorySource.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/MemoryStream.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/OverlayedSource.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/PaddingSource.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/Path.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/PathTooLongException.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/PathUtil.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/SeekOrigin.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/StreamSink.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/StreamSource.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/StreamUtil.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/SubFileSystem.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/SubSink.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/SubSource.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/SubStream.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/io/VirtualFileSystem.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/os.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/os/Environment.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/os/UnicodeMain.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/string.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/string/TranscodeUtil.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/string/detail/utf16.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/string/detail/utf8.h create mode 100644 ctrtool/deps/libtoolchain/include/tc/types.h create mode 100644 ctrtool/deps/libtoolchain/makefile create mode 100644 ctrtool/deps/libtoolchain/src/ByteData.cpp create mode 100644 ctrtool/deps/libtoolchain/src/Exception.cpp create mode 100644 ctrtool/deps/libtoolchain/src/PlatformErrorHandlingUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/src/cli/FormatUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/src/cli/OptionParser.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes128CbcEncryptedStream.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes128CbcEncryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes128CtrEncryptedStream.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes128CtrEncryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes128EcbEncryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes128XtsEncryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes192CbcEncryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes192CtrEncryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes192EcbEncryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes256CbcEncryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes256CtrEncryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes256EcbEncryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Aes256XtsEncryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/HmacMd5Generator.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/HmacSha1Generator.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/HmacSha256Generator.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/HmacSha512Generator.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Md5Generator.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Pbkdf1Md5KeyDeriver.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Pbkdf1Sha1KeyDeriver.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Pbkdf2Sha1KeyDeriver.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Pbkdf2Sha256KeyDeriver.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Pbkdf2Sha512KeyDeriver.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/PseudoRandomByteGenerator.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/RsaKey.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/RsaKeyGenerator.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/RsaOaepSha256Encryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/RsaOaepSha512Encryptor.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/RsaPkcs1Md5Signer.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/RsaPkcs1Sha1Signer.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/RsaPkcs1Sha256Signer.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/RsaPkcs1Sha512Signer.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/RsaPssSha256Signer.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/RsaPssSha512Signer.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Sha1Generator.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Sha256Generator.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/Sha512Generator.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/detail/AesImpl.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/detail/Md5Impl.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/detail/PrbgImpl.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/detail/RsaImpl.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/detail/RsaKeyGeneratorImpl.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/detail/Sha1Impl.cpp create mode 100644 ctrtool/deps/libtoolchain/src/crypto/detail/Sha2Impl.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/BasicPathResolver.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/ConcatenatedStream.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/FileStream.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/IOUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/LocalFileSystem.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/MemorySource.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/MemoryStream.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/OverlayedSource.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/PaddingSource.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/Path.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/PathUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/StreamSink.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/StreamSource.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/StreamUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/SubFileSystem.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/SubSink.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/SubSource.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/SubStream.cpp create mode 100644 ctrtool/deps/libtoolchain/src/io/VirtualFileSystem.cpp create mode 100644 ctrtool/deps/libtoolchain/src/os/Environment.cpp create mode 100644 ctrtool/deps/libtoolchain/src/string/TranscodeUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/src/types.cpp create mode 100644 ctrtool/deps/libtoolchain/test/ByteData_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/ByteData_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/FileSystemTestUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/test/FileSystemTestUtil.h create mode 100644 ctrtool/deps/libtoolchain/test/ITestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/Optional_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/Optional_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/PbkdfUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/test/PbkdfUtil.h create mode 100644 ctrtool/deps/libtoolchain/test/RsaOaepUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/test/RsaOaepUtil.h create mode 100644 ctrtool/deps/libtoolchain/test/RsaPkcs1Util.cpp create mode 100644 ctrtool/deps/libtoolchain/test/RsaPkcs1Util.h create mode 100644 ctrtool/deps/libtoolchain/test/RsaPssUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/test/RsaPssUtil.h create mode 100644 ctrtool/deps/libtoolchain/test/SinkTestUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/test/SinkTestUtil.h create mode 100644 ctrtool/deps/libtoolchain/test/SourceTestUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/test/SourceTestUtil.h create mode 100644 ctrtool/deps/libtoolchain/test/StreamTestUtil.cpp create mode 100644 ctrtool/deps/libtoolchain/test/StreamTestUtil.h create mode 100644 ctrtool/deps/libtoolchain/test/bn_binaryutils_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/bn_binaryutils_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/bn_bitarrayByteBEBitBE_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/bn_bitarrayByteBEBitBE_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/bn_bitarrayByteBEBitLE_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/bn_bitarrayByteBEBitLE_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/bn_bitarrayByteLEBitBE_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/bn_bitarrayByteLEBitBE_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/bn_bitarrayByteLEBitLE_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/bn_bitarrayByteLEBitLE_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/bn_endian_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/bn_endian_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/bn_pad_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/bn_pad_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/bn_string_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/bn_string_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/cli_FormatUtil_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/cli_FormatUtil_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/cli_OptionParser_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/cli_OptionParser_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128CbcEncryptedStream_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128CbcEncryptedStream_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128CbcEncryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128CbcEncryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128CtrEncryptedStream_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128CtrEncryptedStream_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128CtrEncryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128CtrEncryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128EcbEncryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128EcbEncryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128Encryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128Encryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128XtsEncryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes128XtsEncryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes192CbcEncryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes192CbcEncryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes192CtrEncryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes192CtrEncryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes192EcbEncryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes192EcbEncryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes192Encryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes192Encryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes256CbcEncryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes256CbcEncryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes256CtrEncryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes256CtrEncryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes256EcbEncryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes256EcbEncryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes256Encryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes256Encryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes256XtsEncryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Aes256XtsEncryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_HmacMd5Generator_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_HmacMd5Generator_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_HmacSha1Generator_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_HmacSha1Generator_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_HmacSha256Generator_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_HmacSha256Generator_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_HmacSha512Generator_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_HmacSha512Generator_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Md5Generator_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Md5Generator_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Pbkdf1Md5KeyDeriver_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Pbkdf1Md5KeyDeriver_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Pbkdf1Sha1KeyDeriver_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Pbkdf1Sha1KeyDeriver_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Pbkdf2Sha1KeyDeriver_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Pbkdf2Sha1KeyDeriver_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Pbkdf2Sha256KeyDeriver_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Pbkdf2Sha256KeyDeriver_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Pbkdf2Sha512KeyDeriver_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Pbkdf2Sha512KeyDeriver_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_PseudoRandomByteGenerator_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_PseudoRandomByteGenerator_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024OaepSha256Encryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024OaepSha256Encryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024Pkcs1Md5Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024Pkcs1Md5Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024Pkcs1Sha1Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024Pkcs1Sha1Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024Pkcs1Sha256Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024Pkcs1Sha256Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024Pkcs1Sha512Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024Pkcs1Sha512Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024PssSha256Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024PssSha256Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024PssSha512Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa1024PssSha512Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048OaepSha256Encryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048OaepSha256Encryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048OaepSha512Encryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048OaepSha512Encryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048Pkcs1Md5Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048Pkcs1Md5Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048Pkcs1Sha1Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048Pkcs1Sha1Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048Pkcs1Sha256Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048Pkcs1Sha256Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048Pkcs1Sha512Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048Pkcs1Sha512Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048PssSha256Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048PssSha256Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048PssSha512Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa2048PssSha512Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096OaepSha256Encryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096OaepSha256Encryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096OaepSha512Encryptor_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096OaepSha512Encryptor_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096Pkcs1Md5Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096Pkcs1Md5Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096Pkcs1Sha1Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096Pkcs1Sha1Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096Pkcs1Sha256Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096Pkcs1Sha256Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096Pkcs1Sha512Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096Pkcs1Sha512Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096PssSha256Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096PssSha256Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096PssSha512Signer_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Rsa4096PssSha512Signer_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Sha1Generator_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Sha1Generator_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Sha256Generator_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Sha256Generator_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Sha512Generator_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/crypto_Sha512Generator_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_BasicPathResolver_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_BasicPathResolver_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_ConcatenatedStream_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_ConcatenatedStream_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_FileStream_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_FileStream_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_LocalFileSystem_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_LocalFileSystem_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_MemorySource_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_MemorySource_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_MemoryStream_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_MemoryStream_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_OverlayedSource_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_OverlayedSource_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_PaddingSource_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_PaddingSource_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_Path_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_Path_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_StreamSink_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_StreamSink_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_StreamSource_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_StreamSource_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_SubFileSystem_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_SubFileSystem_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_SubSink_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_SubSink_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_SubSource_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_SubSource_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_SubStream_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_SubStream_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/io_VirtualFileSystem_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/io_VirtualFileSystem_TestClass.h create mode 100644 ctrtool/deps/libtoolchain/test/main.cpp create mode 100644 ctrtool/deps/libtoolchain/test/string_TranscodeUtil_TestClass.cpp create mode 100644 ctrtool/deps/libtoolchain/test/string_TranscodeUtil_TestClass.h create mode 100644 ctrtool/makefile create mode 100644 ctrtool/src/CciProcess.cpp create mode 100644 ctrtool/src/CciProcess.h create mode 100644 ctrtool/src/CiaProcess.cpp create mode 100644 ctrtool/src/CiaProcess.h create mode 100644 ctrtool/src/CrrProcess.cpp create mode 100644 ctrtool/src/CrrProcess.h create mode 100644 ctrtool/src/ExHeaderProcess.cpp create mode 100644 ctrtool/src/ExHeaderProcess.h create mode 100644 ctrtool/src/ExeFsProcess.cpp create mode 100644 ctrtool/src/ExeFsProcess.h create mode 100644 ctrtool/src/FirmProcess.cpp create mode 100644 ctrtool/src/FirmProcess.h create mode 100644 ctrtool/src/IvfcProcess.cpp create mode 100644 ctrtool/src/IvfcProcess.h create mode 100644 ctrtool/src/KeyBag.cpp create mode 100644 ctrtool/src/KeyBag.h create mode 100644 ctrtool/src/LzssProcess.cpp create mode 100644 ctrtool/src/LzssProcess.h create mode 100644 ctrtool/src/NcchProcess.cpp create mode 100644 ctrtool/src/NcchProcess.h create mode 100644 ctrtool/src/RomFsProcess.cpp create mode 100644 ctrtool/src/RomFsProcess.h create mode 100644 ctrtool/src/Settings.cpp create mode 100644 ctrtool/src/Settings.h create mode 100644 ctrtool/src/cwav.h create mode 100644 ctrtool/src/lzss.c create mode 100644 ctrtool/src/lzss.h create mode 100644 ctrtool/src/main.cpp create mode 100644 ctrtool/src/types.h create mode 100644 ctrtool/src/version.h diff --git a/ctrtool/build/visualstudio/CTRTool.sln b/ctrtool/build/visualstudio/CTRTool.sln new file mode 100644 index 00000000..0612a167 --- /dev/null +++ b/ctrtool/build/visualstudio/CTRTool.sln @@ -0,0 +1,90 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31229.75 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CTRTool", "CTRTool\CTRTool.vcxproj", "{CADDA22C-5FED-4DAB-A87E-AFBD279DB367}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfmt", "..\..\deps\libfmt\build\visualstudio\libfmt\libfmt.vcxproj", "{F4B0540E-0AAE-4006-944B-356944EF61FA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmbedtls", "..\..\deps\libmbedtls\build\visualstudio\libmbedtls\libmbedtls.vcxproj", "{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtoolchain", "..\..\deps\libtoolchain\build\visualstudio\libtoolchain\libtoolchain.vcxproj", "{E194E4B8-1482-40A2-901B-75D4387822E9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libbroadon-es", "..\..\deps\libbroadon-es\build\visualstudio\libbroadon-es\libbroadon-es.vcxproj", "{8A35D7DC-293A-4669-9C1E-4F45ABFE8838}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-n3ds", "..\..\deps\libnintendo-n3ds\build\visualstudio\libnintendo-n3ds\libnintendo-n3ds.vcxproj", "{7789491C-5403-4613-B06B-DB0C58E19A01}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deps", "deps", "{1AF77D36-45A2-412E-A540-F559B78A6471}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CADDA22C-5FED-4DAB-A87E-AFBD279DB367}.Debug|x64.ActiveCfg = Debug|x64 + {CADDA22C-5FED-4DAB-A87E-AFBD279DB367}.Debug|x64.Build.0 = Debug|x64 + {CADDA22C-5FED-4DAB-A87E-AFBD279DB367}.Debug|x86.ActiveCfg = Debug|Win32 + {CADDA22C-5FED-4DAB-A87E-AFBD279DB367}.Debug|x86.Build.0 = Debug|Win32 + {CADDA22C-5FED-4DAB-A87E-AFBD279DB367}.Release|x64.ActiveCfg = Release|x64 + {CADDA22C-5FED-4DAB-A87E-AFBD279DB367}.Release|x64.Build.0 = Release|x64 + {CADDA22C-5FED-4DAB-A87E-AFBD279DB367}.Release|x86.ActiveCfg = Release|Win32 + {CADDA22C-5FED-4DAB-A87E-AFBD279DB367}.Release|x86.Build.0 = Release|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x64.ActiveCfg = Debug|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x64.Build.0 = Debug|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x86.ActiveCfg = Debug|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x86.Build.0 = Debug|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x64.ActiveCfg = Release|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x64.Build.0 = Release|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x86.ActiveCfg = Release|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x86.Build.0 = Release|Win32 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.ActiveCfg = Debug|x64 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.Build.0 = Debug|x64 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.ActiveCfg = Debug|Win32 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.Build.0 = Debug|Win32 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.ActiveCfg = Release|x64 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.Build.0 = Release|x64 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.ActiveCfg = Release|Win32 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.Build.0 = Release|Win32 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x64.ActiveCfg = Debug|x64 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x64.Build.0 = Debug|x64 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x86.ActiveCfg = Debug|Win32 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x86.Build.0 = Debug|Win32 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x64.ActiveCfg = Release|x64 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x64.Build.0 = Release|x64 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x86.ActiveCfg = Release|Win32 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x86.Build.0 = Release|Win32 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Debug|x64.ActiveCfg = Debug|x64 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Debug|x64.Build.0 = Debug|x64 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Debug|x86.ActiveCfg = Debug|Win32 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Debug|x86.Build.0 = Debug|Win32 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Release|x64.ActiveCfg = Release|x64 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Release|x64.Build.0 = Release|x64 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Release|x86.ActiveCfg = Release|Win32 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Release|x86.Build.0 = Release|Win32 + {7789491C-5403-4613-B06B-DB0C58E19A01}.Debug|x64.ActiveCfg = Debug|x64 + {7789491C-5403-4613-B06B-DB0C58E19A01}.Debug|x64.Build.0 = Debug|x64 + {7789491C-5403-4613-B06B-DB0C58E19A01}.Debug|x86.ActiveCfg = Debug|Win32 + {7789491C-5403-4613-B06B-DB0C58E19A01}.Debug|x86.Build.0 = Debug|Win32 + {7789491C-5403-4613-B06B-DB0C58E19A01}.Release|x64.ActiveCfg = Release|x64 + {7789491C-5403-4613-B06B-DB0C58E19A01}.Release|x64.Build.0 = Release|x64 + {7789491C-5403-4613-B06B-DB0C58E19A01}.Release|x86.ActiveCfg = Release|Win32 + {7789491C-5403-4613-B06B-DB0C58E19A01}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {F4B0540E-0AAE-4006-944B-356944EF61FA} = {1AF77D36-45A2-412E-A540-F559B78A6471} + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {1AF77D36-45A2-412E-A540-F559B78A6471} + {E194E4B8-1482-40A2-901B-75D4387822E9} = {1AF77D36-45A2-412E-A540-F559B78A6471} + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838} = {1AF77D36-45A2-412E-A540-F559B78A6471} + {7789491C-5403-4613-B06B-DB0C58E19A01} = {1AF77D36-45A2-412E-A540-F559B78A6471} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {10D725B5-08F6-43F3-A28F-7B034F1ADEFC} + EndGlobalSection +EndGlobal diff --git a/ctrtool/build/visualstudio/CTRTool/CTRTool.vcxproj b/ctrtool/build/visualstudio/CTRTool/CTRTool.vcxproj new file mode 100644 index 00000000..69ff2fe8 --- /dev/null +++ b/ctrtool/build/visualstudio/CTRTool/CTRTool.vcxproj @@ -0,0 +1,192 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {cadda22c-5fed-4dab-a87e-afbd279db367} + CTRTool + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)..\..\deps\libmbedtls\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libbroadon-es\include;$(SolutionDir)..\..\deps\libnintendo-n3ds\include;$(ProjectDir)..\..\..\include + MultiThreadedDebug + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)..\..\deps\libmbedtls\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libbroadon-es\include;$(SolutionDir)..\..\deps\libnintendo-n3ds\include;$(ProjectDir)..\..\..\include + MultiThreaded + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)..\..\deps\libmbedtls\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libbroadon-es\include;$(SolutionDir)..\..\deps\libnintendo-n3ds\include;$(ProjectDir)..\..\..\include + MultiThreadedDebug + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)..\..\deps\libmbedtls\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libbroadon-es\include;$(SolutionDir)..\..\deps\libnintendo-n3ds\include;$(ProjectDir)..\..\..\include + MultiThreaded + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {f4b0540e-0aae-4006-944b-356944ef61fa} + + + {7a7c66f3-2b5b-4e23-85d8-2a74fedad92c} + + + {8a35d7dc-293a-4669-9c1e-4f45abfe8838} + + + {7789491c-5403-4613-b06b-db0c58e19a01} + + + {e194e4b8-1482-40a2-901b-75d4387822e9} + + + + + + \ No newline at end of file diff --git a/ctrtool/build/visualstudio/CTRTool/CTRTool.vcxproj.filters b/ctrtool/build/visualstudio/CTRTool/CTRTool.vcxproj.filters new file mode 100644 index 00000000..947c47c1 --- /dev/null +++ b/ctrtool/build/visualstudio/CTRTool/CTRTool.vcxproj.filters @@ -0,0 +1,111 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/ctrtool/deps/libbroadon-es/LICENSE b/ctrtool/deps/libbroadon-es/LICENSE new file mode 100644 index 00000000..38e64e9d --- /dev/null +++ b/ctrtool/deps/libbroadon-es/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Jack + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ctrtool/deps/libbroadon-es/README.md b/ctrtool/deps/libbroadon-es/README.md new file mode 100644 index 00000000..80668971 --- /dev/null +++ b/ctrtool/deps/libbroadon-es/README.md @@ -0,0 +1,2 @@ +# libbroadon-es +C++ Library for processing Nintendo's ES DRM formats. diff --git a/ctrtool/deps/libbroadon-es/build/visualstudio/libbroadon-es.sln b/ctrtool/deps/libbroadon-es/build/visualstudio/libbroadon-es.sln new file mode 100644 index 00000000..d1cc8fce --- /dev/null +++ b/ctrtool/deps/libbroadon-es/build/visualstudio/libbroadon-es.sln @@ -0,0 +1,62 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31229.75 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libbroadon-es", "libbroadon-es\libbroadon-es.vcxproj", "{8A35D7DC-293A-4669-9C1E-4F45ABFE8838}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfmt", "..\..\deps\libfmt\build\visualstudio\libfmt\libfmt.vcxproj", "{F4B0540E-0AAE-4006-944B-356944EF61FA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmbedtls", "..\..\deps\libmbedtls\build\visualstudio\libmbedtls\libmbedtls.vcxproj", "{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtoolchain", "..\..\deps\libtoolchain\build\visualstudio\libtoolchain\libtoolchain.vcxproj", "{E194E4B8-1482-40A2-901B-75D4387822E9}" +EndProject + +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Debug|x64.ActiveCfg = Debug|x64 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Debug|x64.Build.0 = Debug|x64 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Debug|x86.ActiveCfg = Debug|Win32 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Debug|x86.Build.0 = Debug|Win32 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Release|x64.ActiveCfg = Release|x64 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Release|x64.Build.0 = Release|x64 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Release|x86.ActiveCfg = Release|Win32 + {8A35D7DC-293A-4669-9C1E-4F45ABFE8838}.Release|x86.Build.0 = Release|Win32 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.ActiveCfg = Debug|x64 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.Build.0 = Debug|x64 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.ActiveCfg = Debug|Win32 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.Build.0 = Debug|Win32 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.ActiveCfg = Release|x64 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.Build.0 = Release|x64 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.ActiveCfg = Release|Win32 + {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.Build.0 = Release|Win32 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x64.ActiveCfg = Debug|x64 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x64.Build.0 = Debug|x64 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x86.ActiveCfg = Debug|Win32 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x86.Build.0 = Debug|Win32 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x64.ActiveCfg = Release|x64 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x64.Build.0 = Release|x64 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x86.ActiveCfg = Release|Win32 + {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x86.Build.0 = Release|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x64.ActiveCfg = Debug|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x64.Build.0 = Debug|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x86.ActiveCfg = Debug|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x86.Build.0 = Debug|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x64.ActiveCfg = Release|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x64.Build.0 = Release|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x86.ActiveCfg = Release|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {76A6C1B6-F1C0-467F-B3A1-D7BDCBAA4FC0} + EndGlobalSection +EndGlobal diff --git a/ctrtool/deps/libbroadon-es/build/visualstudio/libbroadon-es/libbroadon-es.vcxproj b/ctrtool/deps/libbroadon-es/build/visualstudio/libbroadon-es/libbroadon-es.vcxproj new file mode 100644 index 00000000..a83e05a9 --- /dev/null +++ b/ctrtool/deps/libbroadon-es/build/visualstudio/libbroadon-es/libbroadon-es.vcxproj @@ -0,0 +1,151 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {8a35d7dc-293a-4669-9c1e-4f45abfe8838} + libnintendoesdrm + 10.0 + + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + false + v142 + true + Unicode + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + $(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libmbedtls\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(ProjectDir)..\..\..\include + MultiThreadedDebug + + + + + Level3 + Disabled + true + true + $(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libmbedtls\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(ProjectDir)..\..\..\include + MultiThreadedDebug + + + + + Level3 + MaxSpeed + true + true + true + true + $(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libmbedtls\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(ProjectDir)..\..\..\include + MultiThreaded + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + $(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libmbedtls\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(ProjectDir)..\..\..\include + MultiThreaded + + + true + true + + + + + + + + + + + + + + + + {7a7c66f3-2b5b-4e23-85d8-2a74fedad92c} + + + {e194e4b8-1482-40a2-901b-75d4387822e9} + + + {f4b0540e-0aae-4006-944b-356944ef61fa} + + + + + + \ No newline at end of file diff --git a/ctrtool/deps/libbroadon-es/build/visualstudio/libbroadon-es/libbroadon-es.vcxproj.filters b/ctrtool/deps/libbroadon-es/build/visualstudio/libbroadon-es/libbroadon-es.vcxproj.filters new file mode 100644 index 00000000..ec5de4d7 --- /dev/null +++ b/ctrtool/deps/libbroadon-es/build/visualstudio/libbroadon-es/libbroadon-es.vcxproj.filters @@ -0,0 +1,48 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {ba10896d-aa46-41de-9331-2c89a9ddf64a} + + + {c67a3de1-eb64-4733-a6e6-cdcb7be43b60} + + + + + Source Files + + + + + Header Files\brd\es + + + Header Files\brd\es + + + Header Files\brd\es + + + Header Files\brd\es + + + Header Files\brd\es + + + Header Files\brd + + + \ No newline at end of file diff --git a/ctrtool/deps/libbroadon-es/include/brd/es.h b/ctrtool/deps/libbroadon-es/include/brd/es.h new file mode 100644 index 00000000..1ccf8f82 --- /dev/null +++ b/ctrtool/deps/libbroadon-es/include/brd/es.h @@ -0,0 +1,7 @@ +#pragma once + +// definitions +#include +#include +#include +#include \ No newline at end of file diff --git a/ctrtool/deps/libbroadon-es/include/brd/es/es_cert.h b/ctrtool/deps/libbroadon-es/include/brd/es/es_cert.h new file mode 100644 index 00000000..f5a7a059 --- /dev/null +++ b/ctrtool/deps/libbroadon-es/include/brd/es/es_cert.h @@ -0,0 +1,101 @@ +#pragma once +#include + +namespace brd { namespace es { + /** + @enum ESCertPubKeyType + @brief ES Certificate public key type definition + */ +enum class ESCertPubKeyType : uint32_t +{ + RSA4096 = 0, /* RSA 4096 bit key */ + RSA2048 = 1, /* RSA 2048 bit key */ + ECC = 2, /* ECC pub key 512 bits */ +}; + +static const size_t ES_CERT_NAME_SIZE = 64; +using ESCertName = tc::bn::string; +using ESServerId = ESCertName; +using ESDeviceId = ESCertName; + +template +using ESPubKeyPad = std::array; + +/* pack to 4 byte boundaries */ +#pragma pack(push,4) + +struct ESCertHeader +{ + tc::bn::be32 pubKeyType; // ESCertPubKeyType + union { + ESServerId serverId; + ESDeviceId deviceId; + } name; + tc::bn::be32 date; // unix time-stamp +}; +static_assert(sizeof(ESCertHeader) == 72, "ESCertHeader size"); + +struct ESCertRsa2048PublicKey +{ + Rsa2048PublicKey pubKey; + ESPubKeyPad<52> pad; +}; +static_assert(sizeof(ESCertRsa2048PublicKey) == 312, "ESCertRsa2048PublicKey size"); + +struct ESCertRsa4096PublicKey +{ + Rsa4096PublicKey pubKey; + ESPubKeyPad<52> pad; +}; +static_assert(sizeof(ESCertRsa4096PublicKey) == 568, "ESCertRsa4096PublicKey size"); + +struct ESCertEcc233PublicKey +{ + Ecc233PublicKey pubKey; + ESPubKeyPad<60> pad; +}; +static_assert(sizeof(ESCertEcc233PublicKey) == 120, "ESCertEcc233PublicKey size"); + +struct ESRootCert +{ + ESSigRsa4096 sig; + ESCertHeader head; + ESCertRsa4096PublicKey body; +}; +static_assert(sizeof(ESRootCert) == 1280, "ESRootCert size"); + +struct ESCACert +{ + ESSigRsa4096 sig; + ESCertHeader head; + ESCertRsa2048PublicKey body; +}; +static_assert(sizeof(ESCACert) == 1024, "ESCACert size"); + +struct ESCASignedCert +{ + ESSigRsa2048 sig; + ESCertHeader head; + ESCertRsa2048PublicKey body; +}; +static_assert(sizeof(ESCASignedCert) == 768, "ESCASignedCert size"); + +struct ESDeviceCert +{ + ESSigRsa2048 sig; + ESCertHeader head; + ESCertEcc233PublicKey body; +}; +static_assert(sizeof(ESDeviceCert) == 576, "ESDeviceCert size"); + +struct ESDeviceSignedCert +{ + ESSigEcc233 sig; + ESCertHeader head; + ESCertEcc233PublicKey body; +}; +static_assert(sizeof(ESDeviceSignedCert) == 384, "ESDeviceSignedCert size"); + +#pragma pack(pop) + +}} // namespace brd::es diff --git a/ctrtool/deps/libbroadon-es/include/brd/es/es_sign.h b/ctrtool/deps/libbroadon-es/include/brd/es/es_sign.h new file mode 100644 index 00000000..4f313ea8 --- /dev/null +++ b/ctrtool/deps/libbroadon-es/include/brd/es/es_sign.h @@ -0,0 +1,73 @@ +#pragma once +#include + +namespace brd { namespace es { + /** + @enum ESSigType + @brief ES Signature type definition + */ +enum class ESSigType : uint32_t +{ + RSA4096_SHA1 = 0x00010000, /* RSA 4096 bit signature */ + RSA2048_SHA1 = 0x00010001, /* RSA 2048 bit signature */ + ECC_SHA1 = 0x00010002, /* ECC signature 512 bits */ + RSA4096_SHA256 = 0x00010003, /* RSA 4096 bit sig using SHA-256 */ + RSA2048_SHA256 = 0x00010004, /* RSA 2048 bit sig using SHA-256 */ // note that Switch Ticket has this word swapped + ECC_SHA256 = 0x00010005, /* ECC sig 512 bits using SHA-256 */ + HMAC_SHA1 = 0x00010006, /* HMAC-SHA1 160 bit signature */ +}; + +static const size_t ES_ISSUER_SIZE = 64; + + /** + * @class ESIssuer + * @brief The signature issuer ASCII encoded certificate hierarchy. Padded with nulls. + * + * Examples: + * Root (issued by Root) + * Root-CAxxxxxxxx (issued by Certifcate Authority server xxxxxxxx) + * Root-CAxxxxxxxx-XSxxxxxxxx (issued by Ticket/Transaction server xxxxxxxx) + * Root-CAxxxxxxxx-CPxxxxxxxx (issued by Content Publishing server xxxxxxxx) + * Root-CAxxxxxxxx-MSxxxxxxxx (issued by Manufacturing server xxxxxxxx) + * Root-CAxxxxxxxx-MSxxxxxxxx-YYxxxxxxxx (issued by Device with of type YY and serial number xxxxxxxx) + * + * xxxxxxxx represents the server/device serial number encoded in hex. (e.g. XS0000000f is ticket server 15). + */ +using ESIssuer = tc::bn::string; + +template +using ESSigPad = tc::bn::pad<_size>; + +/* pack to 4 byte boundaries */ +#pragma pack(push,4) + +struct ESSigRsa2048 +{ + tc::bn::be32 sigType; + Rsa2048Sig sig; + ESSigPad<60> pad; + ESIssuer issuer; +}; +static_assert(sizeof(ESSigRsa2048) == 384, "ESSigRsa2048 size"); + +struct ESSigRsa4096 +{ + tc::bn::be32 sigType; + Rsa4096Sig sig; + ESSigPad<60> pad; + ESIssuer issuer; +}; +static_assert(sizeof(ESSigRsa4096) == 640, "ESSigRsa4096 size"); + +struct ESSigEcc233 +{ + tc::bn::be32 sigType; + Ecc233Sig sig; + ESSigPad<64> pad; + ESIssuer issuer; +}; +static_assert(sizeof(ESSigEcc233) == 192, "ESSigEcc233 size"); + +#pragma pack(pop) + +}} // namespace brd::es \ No newline at end of file diff --git a/ctrtool/deps/libbroadon-es/include/brd/es/es_ticket.h b/ctrtool/deps/libbroadon-es/include/brd/es/es_ticket.h new file mode 100644 index 00000000..e9a38fe1 --- /dev/null +++ b/ctrtool/deps/libbroadon-es/include/brd/es/es_ticket.h @@ -0,0 +1,232 @@ +#pragma once +#include + +namespace brd { namespace es { + +// ES license types +enum class ESLicenseType : uint8_t +{ + PERMANENT = 0, + DEMO = 1, + TRIAL = 2, + RENTAL = 3, + SUBSCRIPTION = 4, + SERVICE = 5, +}; +static const uint8_t ES_LICENSE_MASK = 0xf; + +// ES title-level limit codes +enum class ESLimitCode : uint32_t +{ + DURATION_TIME = 1, + ABSOLUTE_TIME = 2, + NUM_TITLES = 3, + NUM_LAUNCH = 4, + ELAPSED_TIME = 5, +}; +static const uint32_t ES_MAX_LIMIT_TYPE = 8; + +// ES item-level rights +enum class ESItemType : uint32_t +{ + PERMANENT = 1, + SUBSCRIPTION = 2, + CONTENT = 3, + CONTENT_CONSUMPTION = 4, + ACCESS_TITLE = 5, + LIMITED_RESOURCE = 6, +}; + +enum class ESPropertyMaskFlag : uint16_t +{ + PRE_INSTALL = 0x1, // bit0 + SHARED_TITLE = 0x2, // bit1 + ALLOW_ALL_CONTENT = 0x4, // bit2 + DEVICE_LINK_INDEPENDENT = 0x8, // bit3 + VOLATILE = 0x10, // bit4 + ELICENSE_REQUIRED = 0x20, // bit5 +}; + +enum class ESV1SectionHeaderFlag : uint16_t +{ + COMPRESSED = 0x1 // ironically this is defined but not supported, probably for future use +}; + +enum class ESV2TitleKekType : byte_t +{ + AES128_CBC, + RSA2048 +}; + +#pragma pack(push, 4) + +#ifdef _WIN32 +#pragma warning(disable : 4200) // silence warnings for usage of empty arrays in stucts +#endif + +struct ESLimitedPlayEntry +{ + tc::bn::be32 code; //ESLimitCode + tc::bn::be32 limit; +}; +static_assert(sizeof(ESLimitedPlayEntry) == 8, "ESLimitedPlayEntry size"); + +using ESSysAccessMask = std::array; +using ESTicketCustomData = std::array; +using ESTicketReserved = std::array; +using ESCidxMask = std::array; +using ESLimitedPlayArray = std::array; + +using ESReferenceId = std::array; + +using ESV1CidxMask = std::array; + +using ESV2TitleKey = std::array; +using ESRightsId = std::array; +using ESV2TicketReserved = std::array; + +struct ESTicket +{ + ESSigRsa2048 sig; // RSA 2048-bit sign of the ticket + Ecc233PublicKey serverPubKey; // Ticketing server public key + uint8_t version; // Ticket data structure version number + uint8_t caCrlVersion; // CA CRL version number + uint8_t signerCrlVersion; // Signer CRL version number + Aes128Key titleKey; // Published title key + /* 1 byte alignment padding */ + tc::bn::be64 ticketId; // Unique 64bit ticket ID + tc::bn::be32 deviceId; // Unique 32bit device ID + tc::bn::be64 titleId; // Unique 64bit title ID + ESSysAccessMask sysAccessMask; // 16-bit cidx mask to indicate which + // of the first 16 pieces of contents + // can be accessed by the system app + tc::bn::be16 ticketVersion; // 16-bit ticket version + tc::bn::be32 accessTitleId; // 32-bit title ID for access control + tc::bn::be32 accessTitleMask; // 32-bit title ID mask + uint8_t licenseType; // + uint8_t keyId; // Common key ID + tc::bn::be16 propertyMask; // 16-bit property mask + ESTicketCustomData customData; // 20-byte custom data + ESTicketReserved reserved; // 25-byte reserved info + uint8_t audit; // + /* 2 bytes alignment padding */ + ESCidxMask cidxMask; // Bit-mask of the content indices + ESLimitedPlayArray limits; // Limited play entries +}; +static_assert(sizeof(ESTicket) == 676, "ESTicket size"); + +struct ESV1TicketHeader +{ + tc::bn::be16 hdrVersion; // Version of the ticket header + tc::bn::be16 hdrSize; // Size of ticket header + tc::bn::be32 ticketSize; // Size of the v1 portion of the ticket + tc::bn::be32 sectHdrOfst; // Offset of the section header table + tc::bn::be16 nSectHdrs; // Number of section headers + tc::bn::be16 sectHdrEntrySize; // Size of each section header + tc::bn::be32 flags; // Miscellaneous attributes +}; +static_assert(sizeof(ESV1TicketHeader) == 20, "ESV1TicketHeader size"); + +struct ESV1SectionHeader +{ + tc::bn::be32 sectOfst; // Offset of this section + tc::bn::be32 nRecords; // Number of records in this section + tc::bn::be32 recordSize; // Size of each record + tc::bn::be32 sectionSize; // Total size of this section + tc::bn::be16 sectionType; // Type code of this section + tc::bn::be16 flags; // Miscellaneous attributes +}; +static_assert(sizeof(ESV1SectionHeader) == 20, "ESV1SectionHeader size"); + +struct ESV1Ticket +{ + ESTicket head; + ESV1TicketHeader v1Head; + ESV1SectionHeader sectHdrs[]; +}; +static_assert(sizeof(ESV1Ticket) == 696, "ESV1Ticket size"); + +struct ESV1PermanentRecord +{ + ESReferenceId referenceId; // Reference ID + tc::bn::be32 referenceIdAttr; // Reference ID attributes +}; +static_assert(sizeof(ESV1PermanentRecord) == 20, "ESV1PermanentRecord size"); + +struct ESV1SubscriptionRecord +{ + tc::bn::be32 limit; // Expiration time + ESReferenceId referenceId; // Reference ID + tc::bn::be32 referenceIdAttr; // Reference ID attributes +}; +static_assert(sizeof(ESV1SubscriptionRecord) == 24, "ESV1SubscriptionRecord size"); + +struct ESV1ContentRecord +{ + tc::bn::be32 offset; // Offset content index + ESV1CidxMask accessMask; // Access mask +}; +static_assert(sizeof(ESV1ContentRecord) == 132, "ESV1ContentRecord size"); + +struct ESV1ContentConsumptionRecord +{ + tc::bn::be16 index; // Content index + tc::bn::be16 code; // Limit code + tc::bn::be32 limit; // Limit value +}; +static_assert(sizeof(ESV1ContentConsumptionRecord) == 8, "ESV1ContentConsumptionRecord size"); + +struct ESV1AccessTitleRecord +{ + tc::bn::be64 accessTitleId; // Access title ID + tc::bn::be64 accessTitleMask; // Access title mask +}; +static_assert(sizeof(ESV1AccessTitleRecord) == 16, "ESV1AccessTitleRecord size"); + +struct ESV1LimitedResourceRecord +{ + tc::bn::be32 limit; // Expiration time + ESReferenceId referenceId; // Reference ID + tc::bn::be32 referenceIdAttr; // Reference ID attributes +}; +static_assert(sizeof(ESV1LimitedResourceRecord) == 24, "ESV1LimitedResourceRecord size"); + +struct ESV2Ticket +{ + ESSigRsa2048 sig; // RSA 2048-bit sign of the ticket + ESV2TitleKey titleKey; // Published title key + uint8_t version; // Ticket data structure version number + uint8_t keyType; // Title key encryption key type + tc::bn::le16 ticketVersion; // 16-bit ticket version + uint8_t licenseType; + uint8_t keyId; // Common key ID + tc::bn::le16 propertyMask; // 16-bit property mask + ESV2TicketReserved reservedRegion; // probably the accessTitleId & mask + tc::bn::le64 ticketId; // Unique 64bit ticket ID + tc::bn::le64 deviceId; // Unique 64bit device ID + ESRightsId rightsId; // Unique 128bit rights ID + tc::bn::le32 accountId; // Unique 32bit account ID + tc::bn::le32 sectTotalSize; // Total size of sections + tc::bn::le32 sectHdrOffset; // Offset of the section header table + tc::bn::le16 nSectHdrs; // Number of section headers + tc::bn::le16 nSectHdrEntrySize; // Size of each section header +}; +static_assert(sizeof(ESV2Ticket) == 704, "ESV2Ticket size"); + +struct ESV2SectionHeader +{ + tc::bn::le32 sectOfst; // Offset of this section + tc::bn::le32 recordSize; // Size of each record + tc::bn::le32 sectionSize; // Total size of this section + tc::bn::le16 nRecords; // Number of records in this section + tc::bn::le16 sectionType; // Type code of this section +}; +static_assert(sizeof(ESV2SectionHeader) == 16, "ESV2SectionHeader size"); + +#ifdef _WIN32 +#pragma warning(default : 4200) +#endif + +#pragma pack(pop) + +}} // namespace brd::es \ No newline at end of file diff --git a/ctrtool/deps/libbroadon-es/include/brd/es/es_tmd.h b/ctrtool/deps/libbroadon-es/include/brd/es/es_tmd.h new file mode 100644 index 00000000..8bab8438 --- /dev/null +++ b/ctrtool/deps/libbroadon-es/include/brd/es/es_tmd.h @@ -0,0 +1,129 @@ +#pragma once +#include + +namespace brd { namespace es { + +// ES title type +enum class ESTitleType : uint32_t +{ + NC_TITLE = 0x1, // bit0 NetCard - End-of-life + NG_TITLE = 0x2, // bit1 Wii/NDEV + DS_TITLE = 0x4, // bit2 TWL/DSi + DATA = 0x8, // bit3 boring data title + CT_TITLE = 0x40, // bit6 CTR/3DS + GVM_TITLE = 0x80, // bit7 GVM = ? (from BroadOn libraries) + CAFE_TITLE = 0x100, // bit8 WiiU (from WiiU sdk) +}; + +// ES content type +enum ESContentType : uint16_t +{ + ESContentType_ENCRYPTED = 0x1, // bit0 (from broadOn & b4) + ESContentType_DISC = 0x2, // bit1 (from broadOn & b4) + ESContentType_HASHED = 0x2, // bit1 (from b4) + ESContentType_CFM = 0x4, // bit3 (from broadOn & b4) + ESContentType_SHA1_HASH = 0x2000, // bit13 from b4 (wiiu sdk) + ESContentType_OPTIONAL = 0x4000, // bit14 (from broadOn & b4) + ESContentType_SHARED = 0x8000, // bit15 (from broadOn & b4) +}; + +// +// Maximum possible content index value is 64K - 2, since +// the maximum number of contents per title is 64K - 1 +// +static const size_t ES_CONTENT_INDEX_MAX = 65534; + + +// There are a maximum of 64 CMD groups, each with a maximum of 1K CMDs +static const size_t ES_MAX_CMDS_IN_GROUP = 1024; +static const size_t ES_MAX_CMD_GROUPS = 64; + +#pragma pack(push, 4) + +#ifdef _WIN32 +#pragma warning(disable : 4200) // silence warnings for usage of empty arrays in stucts +#endif + +struct ESContentMeta +{ + tc::bn::be32 cid; // 32-bit content ID + tc::bn::be16 index; // Content index, unique per title + tc::bn::be16 type; // Content type + tc::bn::be64 size; // Unencrypted content size in bytes + Sha1Hash hash; // Hash of the content +}; +static_assert(sizeof(ESContentMeta) == 36, "ESContentMeta size"); + +struct ESV1ContentMeta +{ + tc::bn::be32 cid; // 32-bit content ID + tc::bn::be16 index; // Content index, unique per title + tc::bn::be16 type; // Content type + tc::bn::be64 size; // Unencrypted content size in bytes + Sha256Hash hash; // Hash of the content +}; +static_assert(sizeof(ESV1ContentMeta) == 48, "ESV1ContentMeta size"); + +struct ESTitleMetaHeader +{ + using ESTmdCustomData = std::array; + using ESTmdReserved = std::array; + + uint8_t version; // TMD version number + uint8_t caCrlVersion; // CA CRL version number + uint8_t signerCrlVersion; // Signer CRL version number + tc::bn::be64 sysVersion; // System software version number + tc::bn::be64 titleId; // 64-bit title id + tc::bn::be32 type; // 32-bit title type + tc::bn::be16 groupId; + ESTmdCustomData customData; // 32-byte custom data + ESTmdReserved reserved; // 30-byte reserved info + tc::bn::be32 accessRights; // Rights to system resources + tc::bn::be16 titleVersion; // 16-bit title version + tc::bn::be16 numContents; // Number of contents + tc::bn::be16 bootIndex; // Boot content index + tc::bn::be16 minorTitleVersion; // 16-bit minor title version +}; +static_assert(sizeof(ESTitleMetaHeader) == 100, "ESTitleMetaHeader size"); + +struct ESV1ContentMetaGroup +{ + tc::bn::be16 offset; // Offset content index + tc::bn::be16 nCmds; // Number of CMDs in this group + Sha256Hash groupHash; // Hash for this group of CMDs +}; +static_assert(sizeof(ESV1ContentMetaGroup) == 36, "ESV1ContentMetaGroup size"); + +struct ESV1TitleMetaHeader +{ + using ESV1ContentMetaGroupArray = std::array; + + Sha256Hash hash; // Hash for the CMD groups + ESV1ContentMetaGroupArray cmdGroups; +}; +static_assert(sizeof(ESV1TitleMetaHeader) == 2336, "ESV1TitleMetaHeader size"); + +struct ESTitleMeta +{ + ESSigRsa2048 sig; // RSA 2048-bit sign of the TMD header + ESTitleMetaHeader head; + ESContentMeta contents[]; // CMD array sorted by content index +}; +static_assert(sizeof(ESTitleMeta) == 484, "ESTitleMeta size"); + +struct ESV1TitleMeta +{ + ESSigRsa2048 sig; // RSA 2048-bit sign of the TMD header + ESTitleMetaHeader head; + ESV1TitleMetaHeader v1Head; // Extension to the v0 TMD header + ESV1ContentMeta contents[]; // CMD array sorted by content index +}; +static_assert(sizeof(ESV1TitleMeta) == 2820, "ESV1TitleMeta size"); + +#ifdef _WIN32 +#pragma warning(default : 4200) +#endif + +#pragma pack(pop) + +}} // namespace brd::es \ No newline at end of file diff --git a/ctrtool/deps/libbroadon-es/include/brd/es/types.h b/ctrtool/deps/libbroadon-es/include/brd/es/types.h new file mode 100644 index 00000000..2a944074 --- /dev/null +++ b/ctrtool/deps/libbroadon-es/include/brd/es/types.h @@ -0,0 +1,73 @@ +#pragma once +#include + +namespace brd { namespace es { + +#pragma pack(push,4) + +static const size_t kAes128KeySize = 16; +using Aes128Key = std::array; + +static const size_t kAes192KeySize = 24; +using Aes192Key = std::array; + +static const size_t kAes256KeySize = 32; +using Aes256Key = std::array; + +static const size_t kSha1Size = 20; +using Sha1Hash = std::array; +using Sha1Hmac = std::array; + +static const size_t kSha256Size = 32; +using Sha256Hash = std::array; +using Sha256Hmac = std::array; + +static const size_t kRsaPublicExponentSize = 4; +using RsaPublicExponent = std::array; + +static const size_t kRsa2048Size = 0x100; +using Rsa2048Integer = std::array; +struct Rsa2048PublicKey +{ + Rsa2048Integer m; // modulus + RsaPublicExponent e; // public_exponent +}; +struct Rsa2048PrivateKey +{ + Rsa2048Integer m; // modulus + Rsa2048Integer d; // private_exponent +}; +using Rsa2048Sig = Rsa2048Integer; + +static const size_t kRsa4096Size = 0x200; +using Rsa4096Integer = std::array; +struct Rsa4096PublicKey +{ + Rsa4096Integer m; // modulus + RsaPublicExponent e; // public_exponent +}; +struct Rsa4096PrivateKey +{ + Rsa4096Integer m; // modulus + Rsa4096Integer d; // private_exponent +}; +using Rsa4096Sig = Rsa4096Integer; + +static const size_t kEcc233Size = 60; +using Ecc233Integer = std::array; +struct Ecc233Point +{ + Ecc233Integer x; + Ecc233Integer y; +}; +using Ecc233PrivateKey = Ecc233Integer; +using Ecc233PublicKey = Ecc233Point; +struct Ecc233Sig +{ + Ecc233Integer r; + Ecc233Integer s; +}; + +#pragma pack(pop) + +}} // namespace brd::es \ No newline at end of file diff --git a/ctrtool/deps/libbroadon-es/makefile b/ctrtool/deps/libbroadon-es/makefile new file mode 100644 index 00000000..87521a45 --- /dev/null +++ b/ctrtool/deps/libbroadon-es/makefile @@ -0,0 +1,193 @@ +# C++/C Recursive Project Makefile +# (c) Jack +# Version 5 + +# Project Name +PROJECT_NAME = libbroadon-es + +# Project Relative Paths +PROJECT_PATH = $(CURDIR) +PROJECT_SRC_PATH = src +PROJECT_SRC_SUBDIRS = $(PROJECT_SRC_PATH) +PROJECT_INCLUDE_PATH = include +#PROJECT_TESTSRC_PATH = test +#PROJECT_TESTSRC_SUBDIRS = $(PROJECT_TESTSRC_PATH) +PROJECT_BIN_PATH = bin +#PROJECT_DOCS_PATH = docs +#PROJECT_DOXYFILE_PATH = Doxyfile + +# Determine if the root makefile has been established, and if not establish this makefile as the root makefile +ifeq ($(ROOT_PROJECT_NAME),) + export ROOT_PROJECT_NAME = $(PROJECT_NAME) + export ROOT_PROJECT_PATH = $(PROJECT_PATH) + export ROOT_PROJECT_DEPENDENCY_PATH = $(ROOT_PROJECT_PATH)/deps +endif + +# Shared Library Definitions +PROJECT_SO_VER_MAJOR = 0 +PROJECT_SO_VER_MINOR = 1 +PROJECT_SO_VER_PATCH = 0 +PROJECT_SONAME = $(PROJECT_NAME).so.$(PROJECT_SO_VER_MAJOR) +PROJECT_SO_FILENAME = $(PROJECT_SONAME).$(PROJECT_SO_VER_MINOR).$(PROJECT_SO_VER_PATCH) + +# Project Dependencies +PROJECT_DEPEND = mbedtls toolchain +PROJECT_DEPEND_LOCAL_DIR = libmbedtls libtoolchain + +# Generate compiler flags for including project include path +ifneq ($(PROJECT_INCLUDE_PATH),) + INC += -I"$(PROJECT_INCLUDE_PATH)" +endif + +# Generate compiler flags for local included dependencies +ifneq ($(PROJECT_DEPEND_LOCAL_DIR),) + LIB += $(foreach dep,$(PROJECT_DEPEND_LOCAL_DIR), -L"$(ROOT_PROJECT_DEPENDENCY_PATH)/$(dep)/bin") + INC += $(foreach dep,$(PROJECT_DEPEND_LOCAL_DIR), -I"$(ROOT_PROJECT_DEPENDENCY_PATH)/$(dep)/include") +endif + +# Generate compiler flags for external dependencies +ifneq ($(PROJECT_DEPEND),) + LIB += $(foreach dep,$(PROJECT_DEPEND), -l$(dep)) +endif + +# Detect Platform +ifeq ($(PROJECT_PLATFORM),) + ifeq ($(OS), Windows_NT) + export PROJECT_PLATFORM = WIN32 + else + UNAME = $(shell uname -s) + ifeq ($(UNAME), Darwin) + export PROJECT_PLATFORM = MACOS + else + export PROJECT_PLATFORM = GNU + endif + endif +endif + +# Detect Architecture +ifeq ($(PROJECT_PLATFORM_ARCH),) + ifeq ($(PROJECT_PLATFORM), WIN32) + export PROJECT_PLATFORM_ARCH = x86_64 + else ifeq ($(PROJECT_PLATFORM), GNU) + export PROJECT_PLATFORM_ARCH = $(shell uname -m) + else ifeq ($(PROJECT_PLATFORM), MACOS) + export PROJECT_PLATFORM_ARCH = $(shell uname -m) + else + export PROJECT_PLATFORM_ARCH = x86_64 + endif +endif + +# Generate platform specific compiler flags +ifeq ($(PROJECT_PLATFORM), WIN32) + # Windows Flags/Libs + CC = x86_64-w64-mingw32-gcc + CXX = x86_64-w64-mingw32-g++ + WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-but-set-variable + ARCHFLAGS = + INC += + LIB += -static + ARFLAGS = cr -o +else ifeq ($(PROJECT_PLATFORM), GNU) + # GNU/Linux Flags/Libs + #CC = + #CXX = + WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-but-set-variable + ARCHFLAGS = + INC += + LIB += + ARFLAGS = cr -o +else ifeq ($(PROJECT_PLATFORM), MACOS) + # MacOS Flags/Libs + #CC = + #CXX = + WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-private-field + ARCHFLAGS = -arch $(PROJECT_PLATFORM_ARCH) + INC += + LIB += + ARFLAGS = rc +endif + +# Compiler Flags +CXXFLAGS = -std=c++11 $(INC) $(WARNFLAGS) $(ARCHFLAGS) -fPIC +CFLAGS = -std=c11 $(INC) $(WARNFLAGS) $(ARCHFLAGS) -fPIC + +# Object Files +SRC_OBJ = $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c))) +TESTSRC_OBJ = $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c))) + +# all is the default, user should specify what the default should do +# - 'static_lib' for building static library +# - 'shared_lib' for building shared library +# - 'program' for building the program +# - 'test_program' for building the test program +# These can typically be used together however *_lib and program should not be used together +all: static_lib + +clean: clean_object_files remove_binary_dir + +# Object Compile Rules +%.o: %.c + @echo CC $< + @$(CC) $(CFLAGS) -c $< -o $@ + +%.o: %.cpp + @echo CXX $< + @$(CXX) $(CXXFLAGS) -c $< -o $@ + +# Binary Directory +.PHONY: create_binary_dir +create_binary_dir: + @mkdir -p "$(PROJECT_BIN_PATH)" + +.PHONY: remove_binary_dir +remove_binary_dir: +ifneq ($(PROJECT_BIN_PATH),) + @rm -rf "$(PROJECT_BIN_PATH)" +endif + +.PHONY: clean_object_files +clean_object_files: + @rm -f $(SRC_OBJ) $(TESTSRC_OBJ) + +# Build Library +static_lib: $(SRC_OBJ) create_binary_dir + @echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME).a + @ar $(ARFLAGS) "$(PROJECT_BIN_PATH)/$(PROJECT_NAME).a" $(SRC_OBJ) + +shared_lib: $(SRC_OBJ) create_binary_dir + @echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_SO_FILENAME) + @gcc -shared -Wl,-soname,$(PROJECT_SONAME) -o "$(PROJECT_BIN_PATH)/$(PROJECT_SO_FILENAME)" $(SRC_OBJ) + +# Build Program +program: $(SRC_OBJ) create_binary_dir + @echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME) + @$(CXX) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)" + +# Build Test Program +test_program: $(TESTSRC_OBJ) $(SRC_OBJ) create_binary_dir +ifneq ($(PROJECT_TESTSRC_PATH),) + @echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME)_test + @$(CXX) $(TESTSRC_OBJ) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)_test" +endif + +# Documentation +.PHONY: docs +docs: +ifneq ($(PROJECT_DOCS_PATH),) + doxygen "$(PROJECT_DOXYFILE_PATH)" +endif + +.PHONY: clean_docs +clean_docs: +ifneq ($(PROJECT_DOCS_PATH),) + @rm -rf "$(PROJECT_DOCS_PATH)" +endif + +# Dependencies +.PHONY: deps +deps: + @$(foreach lib,$(PROJECT_DEPEND_LOCAL_DIR), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/$(lib)" && $(MAKE) static_lib && cd "$(PROJECT_PATH)";) + +.PHONY: clean_deps +clean_deps: + @$(foreach lib,$(PROJECT_DEPEND_LOCAL_DIR), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/$(lib)" && $(MAKE) clean && cd "$(PROJECT_PATH)";) \ No newline at end of file diff --git a/ctrtool/deps/libbroadon-es/src/Dummy.cpp b/ctrtool/deps/libbroadon-es/src/Dummy.cpp new file mode 100644 index 00000000..0e29ff25 --- /dev/null +++ b/ctrtool/deps/libbroadon-es/src/Dummy.cpp @@ -0,0 +1,138 @@ +#include +#include + +#ifdef _WIN32 +#pragma warning(disable : 4101) // silence warnings for unused local variables +#endif + +namespace brd { + namespace es { + + int dummy_method1a() + { + // types + brd::es::Aes128Key Aes128Key_var; + brd::es::Aes192Key Aes192Key_var; + brd::es::Aes256Key Aes256Key_var; + brd::es::Sha1Hash Sha1Hash_var; + brd::es::Sha1Hmac Sha1Hmac_var; + brd::es::Sha256Hash Sha256Hash_var; + brd::es::Sha256Hmac Sha256Hmac_var; + brd::es::RsaPublicExponent RsaPublicExponent_var; + brd::es::Rsa2048Integer Rsa2048Integer_var; + brd::es::Rsa2048PublicKey Rsa2048PublicKey_var; + brd::es::Rsa2048PrivateKey Rsa2048PrivateKey_var; + brd::es::Rsa2048Sig Rsa2048Sig_var; + return 11; + } + + int dummy_method1b() + { + // types + brd::es::Rsa4096Integer Rsa4096Integer_var; + brd::es::Rsa4096PublicKey Rsa4096PublicKey_var; + brd::es::Rsa4096PrivateKey Rsa4096PrivateKey_var; + brd::es::Rsa4096Sig Rsa4096Sig_var; + brd::es::Ecc233Integer Ecc233Integer_var; + brd::es::Ecc233Point Ecc233Point_var; + brd::es::Ecc233PrivateKey Ecc233PrivateKey_var; + brd::es::Ecc233PublicKey Ecc233PublicKey_var; + brd::es::Ecc233Sig Ecc233Sig_var; + + return 01323; + } + + int dummy_method2() + { + // sign + brd::es::ESSigType ESSigType_var; + size_t ES_ISSUER_SIZE_var = brd::es::ES_ISSUER_SIZE; + brd::es::ESIssuer ESIssuer_var; + brd::es::ESSigRsa2048 ESSigRsa2048_struct; + brd::es::ESSigRsa4096 ESSigRsa4096_struct; + brd::es::ESSigEcc233 ESSigEcc233_struct; + + return 1232; + } + + int dummy_method3() + { + // cert + brd::es::ESCertPubKeyType ESCertPubKeyType_var; + size_t ES_CERT_NAME_SIZE_var = brd::es::ES_CERT_NAME_SIZE; + brd::es::ESCertName ESCertName_var; + brd::es::ESServerId ESServerId_var; + brd::es::ESDeviceId ESDeviceId_var; + brd::es::ESCertHeader ESCertHeader_struct; + brd::es::ESCertRsa2048PublicKey ESCertRsa2048PublicKey_struct; + brd::es::ESCertRsa4096PublicKey ESCertRsa4096PublicKey_struct; + brd::es::ESCertEcc233PublicKey ESCertEcc233PublicKey_struct; + brd::es::ESRootCert ESRootCert_struct; + brd::es::ESCACert ESCACert_struct; + brd::es::ESCASignedCert ESCASignedCert_struct; + brd::es::ESDeviceCert ESDeviceCert_struct; + brd::es::ESDeviceSignedCert ESDeviceSignedCert_struct; + + return 55; + } + + int dummy_method4() + { + // tik + brd::es::ESLicenseType ESLicenseType_var; + uint8_t ES_LICENSE_MASK_var = brd::es::ES_LICENSE_MASK; + brd::es::ESLimitCode ESLimitCode_var; + uint32_t ES_MAX_LIMIT_TYPE_var = brd::es::ES_MAX_LIMIT_TYPE; + brd::es::ESItemType ESItemType_var; + brd::es::ESPropertyMaskFlag ESPropertyMaskFlag_var; + brd::es::ESV1SectionHeaderFlag ESV1SectionHeaderFlag_var; + brd::es::ESV2TitleKekType ESV2TitleKekType_var; + brd::es::ESLimitedPlayEntry ESLimitedPlayEntry_struct; + brd::es::ESSysAccessMask ESSysAccessMask_var; + brd::es::ESTicketCustomData ESTicketCustomData_var; + brd::es::ESTicketReserved ESTicketReserved_var; + brd::es::ESCidxMask ESCidxMask_var; + brd::es::ESLimitedPlayArray ESLimitedPlayArray_var; + brd::es::ESReferenceId ESReferenceId_var; + brd::es::ESV1CidxMask ESV1CidxMask_var; + brd::es::ESV2TitleKey ESV2TitleKey_var; + brd::es::ESRightsId ESRightsId_var; + brd::es::ESV2TicketReserved ESV2TicketReserved_var; + brd::es::ESTicket ESTicket_struct; + brd::es::ESV1TicketHeader ESV1TicketHeader_struct; + brd::es::ESV1SectionHeader ESV1SectionHeader_struct; + brd::es::ESV1Ticket ESV1Ticket_struct; + brd::es::ESV1PermanentRecord ESV1PermanentRecord_struct; + brd::es::ESV1SubscriptionRecord ESV1SubscriptionRecord_struct; + brd::es::ESV1ContentRecord ESV1ContentRecord_struct; + brd::es::ESV1ContentConsumptionRecord ESV1ContentConsumptionRecord_struct; + brd::es::ESV1AccessTitleRecord ESV1AccessTitleRecord_struct; + brd::es::ESV1LimitedResourceRecord ESV1LimitedResourceRecord_struct; + brd::es::ESV2Ticket ESV2Ticket_struct; + brd::es::ESV2SectionHeader ESV2SectionHeader_struct; + + return 0; + } + + int dummy_method5() + { + // tmd + brd::es::ESTitleType ESTitleType_var; + brd::es::ESContentType ESContentType_var; + size_t ES_CONTENT_INDEX_MAX_var = brd::es::ES_CONTENT_INDEX_MAX; + size_t ES_MAX_CMDS_IN_GROUP_var = brd::es::ES_MAX_CMDS_IN_GROUP; + brd::es::ESContentMeta ESContentMeta_struct; + brd::es::ESV1ContentMeta ESV1ContentMeta_struct; + brd::es::ESTitleMetaHeader ESTitleMetaHeader_struct; + brd::es::ESV1ContentMetaGroup ESV1ContentMetaGroup_struct; + brd::es::ESV1TitleMetaHeader ESV1TitleMetaHeader_struct; + brd::es::ESTitleMeta ESTitleMeta_struct; + brd::es::ESV1TitleMeta ESV1TitleMeta_struct; + return 54; + } + + } +} +#ifdef _WIN32 +#pragma warning(default : 4101) +#endif \ No newline at end of file diff --git a/ctrtool/deps/libfmt/LICENSE.rst b/ctrtool/deps/libfmt/LICENSE.rst new file mode 100644 index 00000000..8f921680 --- /dev/null +++ b/ctrtool/deps/libfmt/LICENSE.rst @@ -0,0 +1,27 @@ +Copyright (c) 2012 - present, Victor Zverovich + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--- Optional exception to the license --- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into a machine-executable object form of such +source code, you may redistribute such embedded portions in such object form +without including the above copyright and permission notices. \ No newline at end of file diff --git a/ctrtool/deps/libfmt/README.md b/ctrtool/deps/libfmt/README.md new file mode 100644 index 00000000..ce8069f7 --- /dev/null +++ b/ctrtool/deps/libfmt/README.md @@ -0,0 +1,2 @@ +# libfmt +Clone of {fmt} (https://github.com/fmtlib/fmt) diff --git a/ctrtool/deps/libfmt/build/visualstudio/libfmt.sln b/ctrtool/deps/libfmt/build/visualstudio/libfmt.sln new file mode 100644 index 00000000..368aee4c --- /dev/null +++ b/ctrtool/deps/libfmt/build/visualstudio/libfmt.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31229.75 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfmt", "libfmt\libfmt.vcxproj", "{F4B0540E-0AAE-4006-944B-356944EF61FA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x64.ActiveCfg = Debug|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x64.Build.0 = Debug|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x86.ActiveCfg = Debug|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x86.Build.0 = Debug|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x64.ActiveCfg = Release|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x64.Build.0 = Release|x64 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x86.ActiveCfg = Release|Win32 + {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {EA4E4F87-6367-4723-8641-6767757CD6DD} + EndGlobalSection +EndGlobal diff --git a/ctrtool/deps/libfmt/build/visualstudio/libfmt/libfmt.vcxproj b/ctrtool/deps/libfmt/build/visualstudio/libfmt/libfmt.vcxproj new file mode 100644 index 00000000..61a5ca35 --- /dev/null +++ b/ctrtool/deps/libfmt/build/visualstudio/libfmt/libfmt.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {f4b0540e-0aae-4006-944b-356944ef61fa} + libfmt + 10.0 + + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + false + v142 + true + Unicode + + + StaticLibrary + true + v142 + Unicode + + + StaticLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + $(ProjectDir)..\..\..\include + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + $(ProjectDir)..\..\..\include + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + $(ProjectDir)..\..\..\include + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + $(ProjectDir)..\..\..\include + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ctrtool/deps/libfmt/build/visualstudio/libfmt/libfmt.vcxproj.filters b/ctrtool/deps/libfmt/build/visualstudio/libfmt/libfmt.vcxproj.filters new file mode 100644 index 00000000..61b8ad12 --- /dev/null +++ b/ctrtool/deps/libfmt/build/visualstudio/libfmt/libfmt.vcxproj.filters @@ -0,0 +1,66 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/ctrtool/deps/libfmt/include/fmt/args.h b/ctrtool/deps/libfmt/include/fmt/args.h new file mode 100644 index 00000000..9a8e4ed2 --- /dev/null +++ b/ctrtool/deps/libfmt/include/fmt/args.h @@ -0,0 +1,234 @@ +// Formatting library for C++ - dynamic format arguments +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_ARGS_H_ +#define FMT_ARGS_H_ + +#include // std::reference_wrapper +#include // std::unique_ptr +#include + +#include "core.h" + +FMT_BEGIN_NAMESPACE + +namespace detail { + +template struct is_reference_wrapper : std::false_type {}; +template +struct is_reference_wrapper> : std::true_type {}; + +template const T& unwrap(const T& v) { return v; } +template const T& unwrap(const std::reference_wrapper& v) { + return static_cast(v); +} + +class dynamic_arg_list { + // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for + // templates it doesn't complain about inability to deduce single translation + // unit for placing vtable. So storage_node_base is made a fake template. + template struct node { + virtual ~node() = default; + std::unique_ptr> next; + }; + + template struct typed_node : node<> { + T value; + + template + FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} + + template + FMT_CONSTEXPR typed_node(const basic_string_view& arg) + : value(arg.data(), arg.size()) {} + }; + + std::unique_ptr> head_; + + public: + template const T& push(const Arg& arg) { + auto new_node = std::unique_ptr>(new typed_node(arg)); + auto& value = new_node->value; + new_node->next = std::move(head_); + head_ = std::move(new_node); + return value; + } +}; +} // namespace detail + +/** + \rst + A dynamic version of `fmt::format_arg_store`. + It's equipped with a storage to potentially temporary objects which lifetimes + could be shorter than the format arguments object. + + It can be implicitly converted into `~fmt::basic_format_args` for passing + into type-erased formatting functions such as `~fmt::vformat`. + \endrst + */ +template +class dynamic_format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + using char_type = typename Context::char_type; + + template struct need_copy { + static constexpr detail::type mapped_type = + detail::mapped_type_constant::value; + + enum { + value = !(detail::is_reference_wrapper::value || + std::is_same>::value || + std::is_same>::value || + (mapped_type != detail::type::cstring_type && + mapped_type != detail::type::string_type && + mapped_type != detail::type::custom_type)) + }; + }; + + template + using stored_type = conditional_t::value && + !has_formatter::value && + !detail::is_reference_wrapper::value, + std::basic_string, T>; + + // Storage of basic_format_arg must be contiguous. + std::vector> data_; + std::vector> named_info_; + + // Storage of arguments not fitting into basic_format_arg must grow + // without relocation because items in data_ refer to it. + detail::dynamic_arg_list dynamic_args_; + + friend class basic_format_args; + + unsigned long long get_types() const { + return detail::is_unpacked_bit | data_.size() | + (named_info_.empty() + ? 0ULL + : static_cast(detail::has_named_args_bit)); + } + + const basic_format_arg* data() const { + return named_info_.empty() ? data_.data() : data_.data() + 1; + } + + template void emplace_arg(const T& arg) { + data_.emplace_back(detail::make_arg(arg)); + } + + template + void emplace_arg(const detail::named_arg& arg) { + if (named_info_.empty()) { + constexpr const detail::named_arg_info* zero_ptr{nullptr}; + data_.insert(data_.begin(), {zero_ptr, 0}); + } + data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); + auto pop_one = [](std::vector>* data) { + data->pop_back(); + }; + std::unique_ptr>, decltype(pop_one)> + guard{&data_, pop_one}; + named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); + data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; + guard.release(); + } + + public: + constexpr dynamic_format_arg_store() = default; + + /** + \rst + Adds an argument into the dynamic store for later passing to a formatting + function. + + Note that custom types and string types (but not string views) are copied + into the store dynamically allocating memory if necessary. + + **Example**:: + + fmt::dynamic_format_arg_store store; + store.push_back(42); + store.push_back("abc"); + store.push_back(1.5f); + std::string result = fmt::vformat("{} and {} and {}", store); + \endrst + */ + template void push_back(const T& arg) { + if (detail::const_check(need_copy::value)) + emplace_arg(dynamic_args_.push>(arg)); + else + emplace_arg(detail::unwrap(arg)); + } + + /** + \rst + Adds a reference to the argument into the dynamic store for later passing to + a formatting function. + + **Example**:: + + fmt::dynamic_format_arg_store store; + char band[] = "Rolling Stones"; + store.push_back(std::cref(band)); + band[9] = 'c'; // Changing str affects the output. + std::string result = fmt::vformat("{}", store); + // result == "Rolling Scones" + \endrst + */ + template void push_back(std::reference_wrapper arg) { + static_assert( + need_copy::value, + "objects of built-in types and string views are always copied"); + emplace_arg(arg.get()); + } + + /** + Adds named argument into the dynamic store for later passing to a formatting + function. ``std::reference_wrapper`` is supported to avoid copying of the + argument. The name is always copied into the store. + */ + template + void push_back(const detail::named_arg& arg) { + const char_type* arg_name = + dynamic_args_.push>(arg.name).c_str(); + if (detail::const_check(need_copy::value)) { + emplace_arg( + fmt::arg(arg_name, dynamic_args_.push>(arg.value))); + } else { + emplace_arg(fmt::arg(arg_name, arg.value)); + } + } + + /** Erase all elements from the store */ + void clear() { + data_.clear(); + named_info_.clear(); + dynamic_args_ = detail::dynamic_arg_list(); + } + + /** + \rst + Reserves space to store at least *new_cap* arguments including + *new_cap_named* named arguments. + \endrst + */ + void reserve(size_t new_cap, size_t new_cap_named) { + FMT_ASSERT(new_cap >= new_cap_named, + "Set of arguments includes set of named arguments"); + data_.reserve(new_cap); + named_info_.reserve(new_cap_named); + } +}; + +FMT_END_NAMESPACE + +#endif // FMT_ARGS_H_ diff --git a/ctrtool/deps/libfmt/include/fmt/chrono.h b/ctrtool/deps/libfmt/include/fmt/chrono.h new file mode 100644 index 00000000..682efd8d --- /dev/null +++ b/ctrtool/deps/libfmt/include/fmt/chrono.h @@ -0,0 +1,2067 @@ +// Formatting library for C++ - chrono support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CHRONO_H_ +#define FMT_CHRONO_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +// Enable tzset. +#ifndef FMT_USE_TZSET +// UWP doesn't provide _tzset. +# if FMT_HAS_INCLUDE("winapifamily.h") +# include +# endif +# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \ + (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) +# define FMT_USE_TZSET 1 +# else +# define FMT_USE_TZSET 0 +# endif +#endif + +// Enable safe chrono durations, unless explicitly disabled. +#ifndef FMT_SAFE_DURATION_CAST +# define FMT_SAFE_DURATION_CAST 1 +#endif +#if FMT_SAFE_DURATION_CAST + +// For conversion between std::chrono::durations without undefined +// behaviour or erroneous results. +// This is a stripped down version of duration_cast, for inclusion in fmt. +// See https://github.com/pauldreik/safe_duration_cast +// +// Copyright Paul Dreik 2019 +namespace safe_duration_cast { + +template ::value && + std::numeric_limits::is_signed == + std::numeric_limits::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits; + using T = std::numeric_limits; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + // A and B are both signed, or both unsigned. + if (detail::const_check(F::digits <= T::digits)) { + // From fits in To without any problem. + } else { + // From does not always fit in To, resort to a dynamic check. + if (from < (T::min)() || from > (T::max)()) { + // outside range. + ec = 1; + return {}; + } + } + return static_cast(from); +} + +/** + * converts From to To, without loss. If the dynamic value of from + * can't be converted to To without loss, ec is set. + */ +template ::value && + std::numeric_limits::is_signed != + std::numeric_limits::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits; + using T = std::numeric_limits; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + if (detail::const_check(F::is_signed && !T::is_signed)) { + // From may be negative, not allowed! + if (fmt::detail::is_negative(from)) { + ec = 1; + return {}; + } + // From is positive. Can it always fit in To? + if (detail::const_check(F::digits > T::digits) && + from > static_cast(detail::max_value())) { + ec = 1; + return {}; + } + } + + if (detail::const_check(!F::is_signed && T::is_signed && + F::digits >= T::digits) && + from > static_cast(detail::max_value())) { + ec = 1; + return {}; + } + return static_cast(from); // Lossless conversion. +} + +template ::value)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + return from; +} // function + +// clang-format off +/** + * converts From to To if possible, otherwise ec is set. + * + * input | output + * ---------------------------------|--------------- + * NaN | NaN + * Inf | Inf + * normal, fits in output | converted (possibly lossy) + * normal, does not fit in output | ec is set + * subnormal | best effort + * -Inf | -Inf + */ +// clang-format on +template ::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + using T = std::numeric_limits; + static_assert(std::is_floating_point::value, "From must be floating"); + static_assert(std::is_floating_point::value, "To must be floating"); + + // catch the only happy case + if (std::isfinite(from)) { + if (from >= T::lowest() && from <= (T::max)()) { + return static_cast(from); + } + // not within range. + ec = 1; + return {}; + } + + // nan and inf will be preserved + return static_cast(from); +} // function + +template ::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + static_assert(std::is_floating_point::value, "From must be floating"); + return from; +} + +/** + * safe duration cast between integral durations + */ +template ::value), + FMT_ENABLE_IF(std::is_integral::value)> +To safe_duration_cast(std::chrono::duration from, + int& ec) { + using From = std::chrono::duration; + ec = 0; + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type::type; + + // safe conversion to IntermediateRep + IntermediateRep count = + lossless_integral_conversion(from.count(), ec); + if (ec) return {}; + // multiply with Factor::num without overflow or underflow + if (detail::const_check(Factor::num != 1)) { + const auto max1 = detail::max_value() / Factor::num; + if (count > max1) { + ec = 1; + return {}; + } + const auto min1 = + (std::numeric_limits::min)() / Factor::num; + if (count < min1) { + ec = 1; + return {}; + } + count *= Factor::num; + } + + if (detail::const_check(Factor::den != 1)) count /= Factor::den; + auto tocount = lossless_integral_conversion(count, ec); + return ec ? To() : To(tocount); +} + +/** + * safe duration_cast between floating point durations + */ +template ::value), + FMT_ENABLE_IF(std::is_floating_point::value)> +To safe_duration_cast(std::chrono::duration from, + int& ec) { + using From = std::chrono::duration; + ec = 0; + if (std::isnan(from.count())) { + // nan in, gives nan out. easy. + return To{std::numeric_limits::quiet_NaN()}; + } + // maybe we should also check if from is denormal, and decide what to do about + // it. + + // +-inf should be preserved. + if (std::isinf(from.count())) { + return To{from.count()}; + } + + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type::type; + + // force conversion of From::rep -> IntermediateRep to be safe, + // even if it will never happen be narrowing in this context. + IntermediateRep count = + safe_float_conversion(from.count(), ec); + if (ec) { + return {}; + } + + // multiply with Factor::num without overflow or underflow + if (detail::const_check(Factor::num != 1)) { + constexpr auto max1 = detail::max_value() / + static_cast(Factor::num); + if (count > max1) { + ec = 1; + return {}; + } + constexpr auto min1 = std::numeric_limits::lowest() / + static_cast(Factor::num); + if (count < min1) { + ec = 1; + return {}; + } + count *= static_cast(Factor::num); + } + + // this can't go wrong, right? den>0 is checked earlier. + if (detail::const_check(Factor::den != 1)) { + using common_t = typename std::common_type::type; + count /= static_cast(Factor::den); + } + + // convert to the to type, safely + using ToRep = typename To::rep; + + const ToRep tocount = safe_float_conversion(count, ec); + if (ec) { + return {}; + } + return To{tocount}; +} +} // namespace safe_duration_cast +#endif + +// Prevents expansion of a preceding token as a function-style macro. +// Usage: f FMT_NOMACRO() +#define FMT_NOMACRO + +namespace detail { +template struct null {}; +inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } +inline null<> localtime_s(...) { return null<>(); } +inline null<> gmtime_r(...) { return null<>(); } +inline null<> gmtime_s(...) { return null<>(); } + +inline const std::locale& get_classic_locale() { + static const auto& locale = std::locale::classic(); + return locale; +} + +template struct codecvt_result { + static constexpr const size_t max_size = 32; + CodeUnit buf[max_size]; + CodeUnit* end; +}; +template +constexpr const size_t codecvt_result::max_size; + +template +void write_codecvt(codecvt_result& out, string_view in_buf, + const std::locale& loc) { + using codecvt = std::codecvt; +#if FMT_CLANG_VERSION +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated" + auto& f = std::use_facet(loc); +# pragma clang diagnostic pop +#else + auto& f = std::use_facet(loc); +#endif + auto mb = std::mbstate_t(); + const char* from_next = nullptr; + auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, + std::begin(out.buf), std::end(out.buf), out.end); + if (result != std::codecvt_base::ok) + FMT_THROW(format_error("failed to format time")); +} + +template +auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) + -> OutputIt { + if (detail::is_utf8() && loc != get_classic_locale()) { + // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and + // gcc-4. +#if FMT_MSC_VER != 0 || \ + (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) + // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 + // and newer. + using code_unit = wchar_t; +#else + using code_unit = char32_t; +#endif + + using unit_t = codecvt_result; + unit_t unit; + write_codecvt(unit, in, loc); + // In UTF-8 is used one to four one-byte code units. + auto&& buf = basic_memory_buffer(); + for (code_unit* p = unit.buf; p != unit.end; ++p) { + uint32_t c = static_cast(*p); + if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) { + // surrogate pair + ++p; + if (p == unit.end || (c & 0xfc00) != 0xd800 || + (*p & 0xfc00) != 0xdc00) { + FMT_THROW(format_error("failed to format time")); + } + c = (c << 10) + static_cast(*p) - 0x35fdc00; + } + if (c < 0x80) { + buf.push_back(static_cast(c)); + } else if (c < 0x800) { + buf.push_back(static_cast(0xc0 | (c >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { + buf.push_back(static_cast(0xe0 | (c >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if (c >= 0x10000 && c <= 0x10ffff) { + buf.push_back(static_cast(0xf0 | (c >> 18))); + buf.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else { + FMT_THROW(format_error("failed to format time")); + } + } + return copy_str(buf.data(), buf.data() + buf.size(), out); + } + return copy_str(in.data(), in.data() + in.size(), out); +} + +template ::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + codecvt_result unit; + write_codecvt(unit, sv, loc); + return copy_str(unit.buf, unit.end, out); +} + +template ::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + return write_encoded_tm_str(out, sv, loc); +} + +template +inline void do_write(buffer& buf, const std::tm& time, + const std::locale& loc, char format, char modifier) { + auto&& format_buf = formatbuf>(buf); + auto&& os = std::basic_ostream(&format_buf); + os.imbue(loc); + using iterator = std::ostreambuf_iterator; + const auto& facet = std::use_facet>(loc); + auto end = facet.put(os, os, Char(' '), &time, format, modifier); + if (end.failed()) FMT_THROW(format_error("failed to format time")); +} + +template ::value)> +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto&& buf = get_buffer(out); + do_write(buf, time, loc, format, modifier); + return buf.out(); +} + +template ::value)> +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto&& buf = basic_memory_buffer(); + do_write(buf, time, loc, format, modifier); + return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); +} + +} // namespace detail + +FMT_MODULE_EXPORT_BEGIN + +/** + Converts given time since epoch as ``std::time_t`` value into calendar time, + expressed in local time. Unlike ``std::localtime``, this function is + thread-safe on most platforms. + */ +inline std::tm localtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::detail; + return handle(localtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(detail::null<>) { + using namespace fmt::detail; + return fallback(localtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VER + bool fallback(detail::null<>) { + using namespace fmt::detail; + std::tm* tm = std::localtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher lt(time); + // Too big time values may be unsupported. + if (!lt.run()) FMT_THROW(format_error("time_t value out of range")); + return lt.tm_; +} + +inline std::tm localtime( + std::chrono::time_point time_point) { + return localtime(std::chrono::system_clock::to_time_t(time_point)); +} + +/** + Converts given time since epoch as ``std::time_t`` value into calendar time, + expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this + function is thread-safe on most platforms. + */ +inline std::tm gmtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::detail; + return handle(gmtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(detail::null<>) { + using namespace fmt::detail; + return fallback(gmtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VER + bool fallback(detail::null<>) { + std::tm* tm = std::gmtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher gt(time); + // Too big time values may be unsupported. + if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); + return gt.tm_; +} + +inline std::tm gmtime( + std::chrono::time_point time_point) { + return gmtime(std::chrono::system_clock::to_time_t(time_point)); +} + +FMT_BEGIN_DETAIL_NAMESPACE + +// Writes two-digit numbers a, b and c separated by sep to buf. +// The method by Pavel Novikov based on +// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/. +inline void write_digit2_separated(char* buf, unsigned a, unsigned b, + unsigned c, char sep) { + unsigned long long digits = + a | (b << 24) | (static_cast(c) << 48); + // Convert each value to BCD. + // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b. + // The difference is + // y - x = a * 6 + // a can be found from x: + // a = floor(x / 10) + // then + // y = x + a * 6 = x + floor(x / 10) * 6 + // floor(x / 10) is (x * 205) >> 11 (needs 16 bits). + digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6; + // Put low nibbles to high bytes and high nibbles to low bytes. + digits = ((digits & 0x00f00000f00000f0) >> 4) | + ((digits & 0x000f00000f00000f) << 8); + auto usep = static_cast(sep); + // Add ASCII '0' to each digit byte and insert separators. + digits |= 0x3030003030003030 | (usep << 16) | (usep << 40); + + constexpr const size_t len = 8; + if (const_check(is_big_endian())) { + char tmp[len]; + memcpy(tmp, &digits, len); + std::reverse_copy(tmp, tmp + len, buf); + } else { + memcpy(buf, &digits, len); + } +} + +template FMT_CONSTEXPR inline const char* get_units() { + if (std::is_same::value) return "as"; + if (std::is_same::value) return "fs"; + if (std::is_same::value) return "ps"; + if (std::is_same::value) return "ns"; + if (std::is_same::value) return "µs"; + if (std::is_same::value) return "ms"; + if (std::is_same::value) return "cs"; + if (std::is_same::value) return "ds"; + if (std::is_same>::value) return "s"; + if (std::is_same::value) return "das"; + if (std::is_same::value) return "hs"; + if (std::is_same::value) return "ks"; + if (std::is_same::value) return "Ms"; + if (std::is_same::value) return "Gs"; + if (std::is_same::value) return "Ts"; + if (std::is_same::value) return "Ps"; + if (std::is_same::value) return "Es"; + if (std::is_same>::value) return "m"; + if (std::is_same>::value) return "h"; + return nullptr; +} + +enum class numeric_system { + standard, + // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. + alternative +}; + +// Parses a put_time-like format string and invokes handler actions. +template +FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, + const Char* end, + Handler&& handler) { + auto ptr = begin; + while (ptr != end) { + auto c = *ptr; + if (c == '}') break; + if (c != '%') { + ++ptr; + continue; + } + if (begin != ptr) handler.on_text(begin, ptr); + ++ptr; // consume '%' + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case '%': + handler.on_text(ptr - 1, ptr); + break; + case 'n': { + const Char newline[] = {'\n'}; + handler.on_text(newline, newline + 1); + break; + } + case 't': { + const Char tab[] = {'\t'}; + handler.on_text(tab, tab + 1); + break; + } + // Year: + case 'Y': + handler.on_year(numeric_system::standard); + break; + case 'y': + handler.on_short_year(numeric_system::standard); + break; + case 'C': + handler.on_century(numeric_system::standard); + break; + case 'G': + handler.on_iso_week_based_year(); + break; + case 'g': + handler.on_iso_week_based_short_year(); + break; + // Day of the week: + case 'a': + handler.on_abbr_weekday(); + break; + case 'A': + handler.on_full_weekday(); + break; + case 'w': + handler.on_dec0_weekday(numeric_system::standard); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::standard); + break; + // Month: + case 'b': + case 'h': + handler.on_abbr_month(); + break; + case 'B': + handler.on_full_month(); + break; + case 'm': + handler.on_dec_month(numeric_system::standard); + break; + // Day of the year/month: + case 'U': + handler.on_dec0_week_of_year(numeric_system::standard); + break; + case 'W': + handler.on_dec1_week_of_year(numeric_system::standard); + break; + case 'V': + handler.on_iso_week_of_year(numeric_system::standard); + break; + case 'j': + handler.on_day_of_year(); + break; + case 'd': + handler.on_day_of_month(numeric_system::standard); + break; + case 'e': + handler.on_day_of_month_space(numeric_system::standard); + break; + // Hour, minute, second: + case 'H': + handler.on_24_hour(numeric_system::standard); + break; + case 'I': + handler.on_12_hour(numeric_system::standard); + break; + case 'M': + handler.on_minute(numeric_system::standard); + break; + case 'S': + handler.on_second(numeric_system::standard); + break; + // Other: + case 'c': + handler.on_datetime(numeric_system::standard); + break; + case 'x': + handler.on_loc_date(numeric_system::standard); + break; + case 'X': + handler.on_loc_time(numeric_system::standard); + break; + case 'D': + handler.on_us_date(); + break; + case 'F': + handler.on_iso_date(); + break; + case 'r': + handler.on_12_hour_time(); + break; + case 'R': + handler.on_24_hour_time(); + break; + case 'T': + handler.on_iso_time(); + break; + case 'p': + handler.on_am_pm(); + break; + case 'Q': + handler.on_duration_value(); + break; + case 'q': + handler.on_duration_unit(); + break; + case 'z': + handler.on_utc_offset(); + break; + case 'Z': + handler.on_tz_name(); + break; + // Alternative representation: + case 'E': { + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'Y': + handler.on_year(numeric_system::alternative); + break; + case 'y': + handler.on_offset_year(); + break; + case 'C': + handler.on_century(numeric_system::alternative); + break; + case 'c': + handler.on_datetime(numeric_system::alternative); + break; + case 'x': + handler.on_loc_date(numeric_system::alternative); + break; + case 'X': + handler.on_loc_time(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + } + case 'O': + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'y': + handler.on_short_year(numeric_system::alternative); + break; + case 'm': + handler.on_dec_month(numeric_system::alternative); + break; + case 'U': + handler.on_dec0_week_of_year(numeric_system::alternative); + break; + case 'W': + handler.on_dec1_week_of_year(numeric_system::alternative); + break; + case 'V': + handler.on_iso_week_of_year(numeric_system::alternative); + break; + case 'd': + handler.on_day_of_month(numeric_system::alternative); + break; + case 'e': + handler.on_day_of_month_space(numeric_system::alternative); + break; + case 'w': + handler.on_dec0_weekday(numeric_system::alternative); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::alternative); + break; + case 'H': + handler.on_24_hour(numeric_system::alternative); + break; + case 'I': + handler.on_12_hour(numeric_system::alternative); + break; + case 'M': + handler.on_minute(numeric_system::alternative); + break; + case 'S': + handler.on_second(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + default: + FMT_THROW(format_error("invalid format")); + } + begin = ptr; + } + if (begin != ptr) handler.on_text(begin, ptr); + return ptr; +} + +template struct null_chrono_spec_handler { + FMT_CONSTEXPR void unsupported() { + static_cast(this)->unsupported(); + } + FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_offset_year() { unsupported(); } + FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); } + FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); } + FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); } + FMT_CONSTEXPR void on_full_weekday() { unsupported(); } + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_abbr_month() { unsupported(); } + FMT_CONSTEXPR void on_full_month() { unsupported(); } + FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_day_of_year() { unsupported(); } + FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_us_date() { unsupported(); } + FMT_CONSTEXPR void on_iso_date() { unsupported(); } + FMT_CONSTEXPR void on_12_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_24_hour_time() { unsupported(); } + FMT_CONSTEXPR void on_iso_time() { unsupported(); } + FMT_CONSTEXPR void on_am_pm() { unsupported(); } + FMT_CONSTEXPR void on_duration_value() { unsupported(); } + FMT_CONSTEXPR void on_duration_unit() { unsupported(); } + FMT_CONSTEXPR void on_utc_offset() { unsupported(); } + FMT_CONSTEXPR void on_tz_name() { unsupported(); } +}; + +struct tm_format_checker : null_chrono_spec_handler { + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); } + + template + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_year(numeric_system) {} + FMT_CONSTEXPR void on_short_year(numeric_system) {} + FMT_CONSTEXPR void on_offset_year() {} + FMT_CONSTEXPR void on_century(numeric_system) {} + FMT_CONSTEXPR void on_iso_week_based_year() {} + FMT_CONSTEXPR void on_iso_week_based_short_year() {} + FMT_CONSTEXPR void on_abbr_weekday() {} + FMT_CONSTEXPR void on_full_weekday() {} + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {} + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} + FMT_CONSTEXPR void on_abbr_month() {} + FMT_CONSTEXPR void on_full_month() {} + FMT_CONSTEXPR void on_dec_month(numeric_system) {} + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_day_of_year() {} + FMT_CONSTEXPR void on_day_of_month(numeric_system) {} + FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {} + FMT_CONSTEXPR void on_24_hour(numeric_system) {} + FMT_CONSTEXPR void on_12_hour(numeric_system) {} + FMT_CONSTEXPR void on_minute(numeric_system) {} + FMT_CONSTEXPR void on_second(numeric_system) {} + FMT_CONSTEXPR void on_datetime(numeric_system) {} + FMT_CONSTEXPR void on_loc_date(numeric_system) {} + FMT_CONSTEXPR void on_loc_time(numeric_system) {} + FMT_CONSTEXPR void on_us_date() {} + FMT_CONSTEXPR void on_iso_date() {} + FMT_CONSTEXPR void on_12_hour_time() {} + FMT_CONSTEXPR void on_24_hour_time() {} + FMT_CONSTEXPR void on_iso_time() {} + FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_utc_offset() {} + FMT_CONSTEXPR void on_tz_name() {} +}; + +inline const char* tm_wday_full_name(int wday) { + static constexpr const char* full_name_list[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"}; + return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; +} +inline const char* tm_wday_short_name(int wday) { + static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; + return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; +} + +inline const char* tm_mon_full_name(int mon) { + static constexpr const char* full_name_list[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"}; + return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; +} +inline const char* tm_mon_short_name(int mon) { + static constexpr const char* short_name_list[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???"; +} + +template +struct has_member_data_tm_gmtoff : std::false_type {}; +template +struct has_member_data_tm_gmtoff> + : std::true_type {}; + +template +struct has_member_data_tm_zone : std::false_type {}; +template +struct has_member_data_tm_zone> + : std::true_type {}; + +#if FMT_USE_TZSET +inline void tzset_once() { + static bool init = []() -> bool { + _tzset(); + return true; + }(); + ignore_unused(init); +} +#endif + +template class tm_writer { + private: + static constexpr int days_per_week = 7; + + const std::locale& loc_; + const bool is_classic_; + OutputIt out_; + const std::tm& tm_; + + auto tm_sec() const noexcept -> int { + FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, ""); + return tm_.tm_sec; + } + auto tm_min() const noexcept -> int { + FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, ""); + return tm_.tm_min; + } + auto tm_hour() const noexcept -> int { + FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, ""); + return tm_.tm_hour; + } + auto tm_mday() const noexcept -> int { + FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, ""); + return tm_.tm_mday; + } + auto tm_mon() const noexcept -> int { + FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, ""); + return tm_.tm_mon; + } + auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; } + auto tm_wday() const noexcept -> int { + FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, ""); + return tm_.tm_wday; + } + auto tm_yday() const noexcept -> int { + FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, ""); + return tm_.tm_yday; + } + + auto tm_hour12() const noexcept -> int { + const auto h = tm_hour(); + const auto z = h < 12 ? h : h - 12; + return z == 0 ? 12 : z; + } + + // POSIX and the C Standard are unclear or inconsistent about what %C and %y + // do if the year is negative or exceeds 9999. Use the convention that %C + // concatenated with %y yields the same output as %Y, and that %Y contains at + // least 4 characters, with more only if necessary. + auto split_year_lower(long long year) const noexcept -> int { + auto l = year % 100; + if (l < 0) l = -l; // l in [0, 99] + return static_cast(l); + } + + // Algorithm: + // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date + auto iso_year_weeks(long long curr_year) const noexcept -> int { + const auto prev_year = curr_year - 1; + const auto curr_p = + (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % + days_per_week; + const auto prev_p = + (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % + days_per_week; + return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); + } + auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int { + return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) / + days_per_week; + } + auto tm_iso_week_year() const noexcept -> long long { + const auto year = tm_year(); + const auto w = iso_week_num(tm_yday(), tm_wday()); + if (w < 1) return year - 1; + if (w > iso_year_weeks(year)) return year + 1; + return year; + } + auto tm_iso_week_of_year() const noexcept -> int { + const auto year = tm_year(); + const auto w = iso_week_num(tm_yday(), tm_wday()); + if (w < 1) return iso_year_weeks(year - 1); + if (w > iso_year_weeks(year)) return 1; + return w; + } + + void write1(int value) { + *out_++ = static_cast('0' + to_unsigned(value) % 10); + } + void write2(int value) { + const char* d = digits2(to_unsigned(value) % 100); + *out_++ = *d++; + *out_++ = *d; + } + + void write_year_extended(long long year) { + // At least 4 characters. + int width = 4; + if (year < 0) { + *out_++ = '-'; + year = 0 - year; + --width; + } + uint32_or_64_or_128_t n = to_unsigned(year); + const int num_digits = count_digits(n); + if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0'); + out_ = format_decimal(out_, n, num_digits).end; + } + void write_year(long long year) { + if (year >= 0 && year < 10000) { + write2(static_cast(year / 100)); + write2(static_cast(year % 100)); + } else { + write_year_extended(year); + } + } + + void write_utc_offset(long offset) { + if (offset < 0) { + *out_++ = '-'; + offset = -offset; + } else { + *out_++ = '+'; + } + offset /= 60; + write2(static_cast(offset / 60)); + write2(static_cast(offset % 60)); + } + template ::value)> + void format_utc_offset_impl(const T& tm) { + write_utc_offset(tm.tm_gmtoff); + } + template ::value)> + void format_utc_offset_impl(const T& tm) { +#if defined(_WIN32) && defined(_UCRT) +# if FMT_USE_TZSET + tzset_once(); +# endif + long offset = 0; + _get_timezone(&offset); + if (tm.tm_isdst) { + long dstbias = 0; + _get_dstbias(&dstbias); + offset += dstbias; + } + write_utc_offset(-offset); +#else + ignore_unused(tm); + format_localized('z'); +#endif + } + + template ::value)> + void format_tz_name_impl(const T& tm) { + if (is_classic_) + out_ = write_tm_str(out_, tm.tm_zone, loc_); + else + format_localized('Z'); + } + template ::value)> + void format_tz_name_impl(const T&) { + format_localized('Z'); + } + + void format_localized(char format, char modifier = 0) { + out_ = write(out_, tm_, loc_, format, modifier); + } + + public: + tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm) + : loc_(loc), + is_classic_(loc_ == get_classic_locale()), + out_(out), + tm_(tm) {} + + OutputIt out() const { return out_; } + + FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { + out_ = copy_str(begin, end, out_); + } + + void on_abbr_weekday() { + if (is_classic_) + out_ = write(out_, tm_wday_short_name(tm_wday())); + else + format_localized('a'); + } + void on_full_weekday() { + if (is_classic_) + out_ = write(out_, tm_wday_full_name(tm_wday())); + else + format_localized('A'); + } + void on_dec0_weekday(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday()); + format_localized('w', 'O'); + } + void on_dec1_weekday(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write1(wday == 0 ? days_per_week : wday); + } else { + format_localized('u', 'O'); + } + } + + void on_abbr_month() { + if (is_classic_) + out_ = write(out_, tm_mon_short_name(tm_mon())); + else + format_localized('b'); + } + void on_full_month() { + if (is_classic_) + out_ = write(out_, tm_mon_full_name(tm_mon())); + else + format_localized('B'); + } + + void on_datetime(numeric_system ns) { + if (is_classic_) { + on_abbr_weekday(); + *out_++ = ' '; + on_abbr_month(); + *out_++ = ' '; + on_day_of_month_space(numeric_system::standard); + *out_++ = ' '; + on_iso_time(); + *out_++ = ' '; + on_year(numeric_system::standard); + } else { + format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); + } + } + void on_loc_date(numeric_system ns) { + if (is_classic_) + on_us_date(); + else + format_localized('x', ns == numeric_system::standard ? '\0' : 'E'); + } + void on_loc_time(numeric_system ns) { + if (is_classic_) + on_iso_time(); + else + format_localized('X', ns == numeric_system::standard ? '\0' : 'E'); + } + void on_us_date() { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_mon() + 1), + to_unsigned(tm_mday()), + to_unsigned(split_year_lower(tm_year())), '/'); + out_ = copy_str(std::begin(buf), std::end(buf), out_); + } + void on_iso_date() { + auto year = tm_year(); + char buf[10]; + size_t offset = 0; + if (year >= 0 && year < 10000) { + copy2(buf, digits2(to_unsigned(year / 100))); + } else { + offset = 4; + write_year_extended(year); + year = 0; + } + write_digit2_separated(buf + 2, static_cast(year % 100), + to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), + '-'); + out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); + } + + void on_utc_offset() { format_utc_offset_impl(tm_); } + void on_tz_name() { format_tz_name_impl(tm_); } + + void on_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write_year(tm_year()); + format_localized('Y', 'E'); + } + void on_short_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(split_year_lower(tm_year())); + format_localized('y', 'O'); + } + void on_offset_year() { + if (is_classic_) return write2(split_year_lower(tm_year())); + format_localized('y', 'E'); + } + + void on_century(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto year = tm_year(); + auto upper = year / 100; + if (year >= -99 && year < 0) { + // Zero upper on negative year. + *out_++ = '-'; + *out_++ = '0'; + } else if (upper >= 0 && upper < 100) { + write2(static_cast(upper)); + } else { + out_ = write(out_, upper); + } + } else { + format_localized('C', 'E'); + } + } + + void on_dec_month(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_mon() + 1); + format_localized('m', 'O'); + } + + void on_dec0_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); + format_localized('U', 'O'); + } + void on_dec1_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write2((tm_yday() + days_per_week - + (wday == 0 ? (days_per_week - 1) : (wday - 1))) / + days_per_week); + } else { + format_localized('W', 'O'); + } + } + void on_iso_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_iso_week_of_year()); + format_localized('V', 'O'); + } + + void on_iso_week_based_year() { write_year(tm_iso_week_year()); } + void on_iso_week_based_short_year() { + write2(split_year_lower(tm_iso_week_year())); + } + + void on_day_of_year() { + auto yday = tm_yday() + 1; + write1(yday / 100); + write2(yday % 100); + } + void on_day_of_month(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); + format_localized('d', 'O'); + } + void on_day_of_month_space(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto mday = to_unsigned(tm_mday()) % 100; + const char* d2 = digits2(mday); + *out_++ = mday < 10 ? ' ' : d2[0]; + *out_++ = d2[1]; + } else { + format_localized('e', 'O'); + } + } + + void on_24_hour(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour()); + format_localized('H', 'O'); + } + void on_12_hour(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_hour12()); + format_localized('I', 'O'); + } + void on_minute(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_min()); + format_localized('M', 'O'); + } + void on_second(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec()); + format_localized('S', 'O'); + } + + void on_12_hour_time() { + if (is_classic_) { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_hour12()), + to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); + out_ = copy_str(std::begin(buf), std::end(buf), out_); + *out_++ = ' '; + on_am_pm(); + } else { + format_localized('r'); + } + } + void on_24_hour_time() { + write2(tm_hour()); + *out_++ = ':'; + write2(tm_min()); + } + void on_iso_time() { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()), + to_unsigned(tm_sec()), ':'); + out_ = copy_str(std::begin(buf), std::end(buf), out_); + } + + void on_am_pm() { + if (is_classic_) { + *out_++ = tm_hour() < 12 ? 'A' : 'P'; + *out_++ = 'M'; + } else { + format_localized('p'); + } + } + + // These apply to chrono durations but not tm. + void on_duration_value() {} + void on_duration_unit() {} +}; + +struct chrono_format_checker : null_chrono_spec_handler { + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } + + template + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_24_hour(numeric_system) {} + FMT_CONSTEXPR void on_12_hour(numeric_system) {} + FMT_CONSTEXPR void on_minute(numeric_system) {} + FMT_CONSTEXPR void on_second(numeric_system) {} + FMT_CONSTEXPR void on_12_hour_time() {} + FMT_CONSTEXPR void on_24_hour_time() {} + FMT_CONSTEXPR void on_iso_time() {} + FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_duration_value() {} + FMT_CONSTEXPR void on_duration_unit() {} +}; + +template ::value)> +inline bool isnan(T) { + return false; +} +template ::value)> +inline bool isnan(T value) { + return std::isnan(value); +} + +template ::value)> +inline bool isfinite(T) { + return true; +} + +// Converts value to Int and checks that it's in the range [0, upper). +template ::value)> +inline Int to_nonnegative_int(T value, Int upper) { + FMT_ASSERT(value >= 0 && to_unsigned(value) <= to_unsigned(upper), + "invalid value"); + (void)upper; + return static_cast(value); +} +template ::value)> +inline Int to_nonnegative_int(T value, Int upper) { + if (value < 0 || value > static_cast(upper)) + FMT_THROW(format_error("invalid value")); + return static_cast(value); +} + +template ::value)> +inline T mod(T x, int y) { + return x % static_cast(y); +} +template ::value)> +inline T mod(T x, int y) { + return std::fmod(x, static_cast(y)); +} + +// If T is an integral type, maps T to its unsigned counterpart, otherwise +// leaves it unchanged (unlike std::make_unsigned). +template ::value> +struct make_unsigned_or_unchanged { + using type = T; +}; + +template struct make_unsigned_or_unchanged { + using type = typename std::make_unsigned::type; +}; + +#if FMT_SAFE_DURATION_CAST +// throwing version of safe_duration_cast +template +To fmt_safe_duration_cast(std::chrono::duration from) { + int ec; + To to = safe_duration_cast::safe_duration_cast(from, ec); + if (ec) FMT_THROW(format_error("cannot format duration")); + return to; +} +#endif + +template ::value)> +inline std::chrono::duration get_milliseconds( + std::chrono::duration d) { + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + using CommonSecondsType = + typename std::common_type::type; + const auto d_as_common = fmt_safe_duration_cast(d); + const auto d_as_whole_seconds = + fmt_safe_duration_cast(d_as_common); + // this conversion should be nonproblematic + const auto diff = d_as_common - d_as_whole_seconds; + const auto ms = + fmt_safe_duration_cast>(diff); + return ms; +#else + auto s = std::chrono::duration_cast(d); + return std::chrono::duration_cast(d - s); +#endif +} + +// Returns the number of fractional digits in the range [0, 18] according to the +// C++20 spec. If more than 18 fractional digits are required then returns 6 for +// microseconds precision. +constexpr int count_fractional_digits(long long num, long long den, int n = 0) { + return num % den == 0 + ? n + : (n > 18 ? 6 : count_fractional_digits(num * 10, den, n + 1)); +} + +constexpr long long pow10(std::uint32_t n) { + return n == 0 ? 1 : 10 * pow10(n - 1); +} + +template ::is_signed)> +constexpr std::chrono::duration abs( + std::chrono::duration d) { + // We need to compare the duration using the count() method directly + // due to a compiler bug in clang-11 regarding the spaceship operator, + // when -Wzero-as-null-pointer-constant is enabled. + // In clang-12 the bug has been fixed. See + // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example: + // https://www.godbolt.org/z/Knbb5joYx. + return d.count() >= d.zero().count() ? d : -d; +} + +template ::is_signed)> +constexpr std::chrono::duration abs( + std::chrono::duration d) { + return d; +} + +template ::value)> +OutputIt format_duration_value(OutputIt out, Rep val, int) { + return write(out, val); +} + +template ::value)> +OutputIt format_duration_value(OutputIt out, Rep val, int precision) { + auto specs = basic_format_specs(); + specs.precision = precision; + specs.type = precision >= 0 ? presentation_type::fixed_lower + : presentation_type::general_lower; + return write(out, val, specs); +} + +template +OutputIt copy_unit(string_view unit, OutputIt out, Char) { + return std::copy(unit.begin(), unit.end(), out); +} + +template +OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { + // This works when wchar_t is UTF-32 because units only contain characters + // that have the same representation in UTF-16 and UTF-32. + utf8_to_utf16 u(unit); + return std::copy(u.c_str(), u.c_str() + u.size(), out); +} + +template +OutputIt format_duration_unit(OutputIt out) { + if (const char* unit = get_units()) + return copy_unit(string_view(unit), out, Char()); + *out++ = '['; + out = write(out, Period::num); + if (const_check(Period::den != 1)) { + *out++ = '/'; + out = write(out, Period::den); + } + *out++ = ']'; + *out++ = 's'; + return out; +} + +class get_locale { + private: + union { + std::locale locale_; + }; + bool has_locale_ = false; + + public: + get_locale(bool localized, locale_ref loc) : has_locale_(localized) { + if (localized) + ::new (&locale_) std::locale(loc.template get()); + } + ~get_locale() { + if (has_locale_) locale_.~locale(); + } + operator const std::locale&() const { + return has_locale_ ? locale_ : get_classic_locale(); + } +}; + +template +struct chrono_formatter { + FormatContext& context; + OutputIt out; + int precision; + bool localized = false; + // rep is unsigned to avoid overflow. + using rep = + conditional_t::value && sizeof(Rep) < sizeof(int), + unsigned, typename make_unsigned_or_unchanged::type>; + rep val; + using seconds = std::chrono::duration; + seconds s; + using milliseconds = std::chrono::duration; + bool negative; + + using char_type = typename FormatContext::char_type; + using tm_writer_type = tm_writer; + + chrono_formatter(FormatContext& ctx, OutputIt o, + std::chrono::duration d) + : context(ctx), + out(o), + val(static_cast(d.count())), + negative(false) { + if (d.count() < 0) { + val = 0 - val; + negative = true; + } + + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + // might need checked conversion (rep!=Rep) + auto tmpval = std::chrono::duration(val); + s = fmt_safe_duration_cast(tmpval); +#else + s = std::chrono::duration_cast( + std::chrono::duration(val)); +#endif + } + + // returns true if nan or inf, writes to out. + bool handle_nan_inf() { + if (isfinite(val)) { + return false; + } + if (isnan(val)) { + write_nan(); + return true; + } + // must be +-inf + if (val > 0) { + write_pinf(); + } else { + write_ninf(); + } + return true; + } + + Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } + + Rep hour12() const { + Rep hour = static_cast(mod((s.count() / 3600), 12)); + return hour <= 0 ? 12 : hour; + } + + Rep minute() const { return static_cast(mod((s.count() / 60), 60)); } + Rep second() const { return static_cast(mod(s.count(), 60)); } + + std::tm time() const { + auto time = std::tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + time.tm_min = to_nonnegative_int(minute(), 60); + time.tm_sec = to_nonnegative_int(second(), 60); + return time; + } + + void write_sign() { + if (negative) { + *out++ = '-'; + negative = false; + } + } + + void write(Rep value, int width) { + write_sign(); + if (isnan(value)) return write_nan(); + uint32_or_64_or_128_t n = + to_unsigned(to_nonnegative_int(value, max_value())); + int num_digits = detail::count_digits(n); + if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); + out = format_decimal(out, n, num_digits).end; + } + + template void write_fractional_seconds(Duration d) { + constexpr auto num_fractional_digits = + count_fractional_digits(Duration::period::num, Duration::period::den); + + using subsecond_precision = std::chrono::duration< + typename std::common_type::type, + std::ratio<1, detail::pow10(num_fractional_digits)>>; + if (std::ratio_less::value) { + *out++ = '.'; + // Don't convert long double to integer seconds to avoid overflow. + using sec = conditional_t< + std::is_same::value, + std::chrono::duration, std::chrono::seconds>; + auto fractional = detail::abs(d) - std::chrono::duration_cast(d); + const auto subseconds = + std::chrono::treat_as_floating_point< + typename subsecond_precision::rep>::value + ? fractional.count() + : std::chrono::duration_cast(fractional) + .count(); + uint32_or_64_or_128_t n = + to_unsigned(to_nonnegative_int(subseconds, max_value())); + int num_digits = detail::count_digits(n); + if (num_fractional_digits > num_digits) + out = std::fill_n(out, num_fractional_digits - num_digits, '0'); + out = format_decimal(out, n, num_digits).end; + } + } + + void write_nan() { std::copy_n("nan", 3, out); } + void write_pinf() { std::copy_n("inf", 3, out); } + void write_ninf() { std::copy_n("-inf", 4, out); } + + template + void format_tm(const tm& time, Callback cb, Args... args) { + if (isnan(val)) return write_nan(); + get_locale loc(localized, context.locale()); + auto w = tm_writer_type(loc, out, time); + (w.*cb)(args...); + out = w.out(); + } + + void on_text(const char_type* begin, const char_type* end) { + std::copy(begin, end, out); + } + + // These are not implemented because durations don't have date information. + void on_abbr_weekday() {} + void on_full_weekday() {} + void on_dec0_weekday(numeric_system) {} + void on_dec1_weekday(numeric_system) {} + void on_abbr_month() {} + void on_full_month() {} + void on_datetime(numeric_system) {} + void on_loc_date(numeric_system) {} + void on_loc_time(numeric_system) {} + void on_us_date() {} + void on_iso_date() {} + void on_utc_offset() {} + void on_tz_name() {} + void on_year(numeric_system) {} + void on_short_year(numeric_system) {} + void on_offset_year() {} + void on_century(numeric_system) {} + void on_iso_week_based_year() {} + void on_iso_week_based_short_year() {} + void on_dec_month(numeric_system) {} + void on_dec0_week_of_year(numeric_system) {} + void on_dec1_week_of_year(numeric_system) {} + void on_iso_week_of_year(numeric_system) {} + void on_day_of_year() {} + void on_day_of_month(numeric_system) {} + void on_day_of_month_space(numeric_system) {} + + void on_24_hour(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour(), 2); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + format_tm(time, &tm_writer_type::on_24_hour, ns); + } + + void on_12_hour(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour12(), 2); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour12(), 12); + format_tm(time, &tm_writer_type::on_12_hour, ns); + } + + void on_minute(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(minute(), 2); + auto time = tm(); + time.tm_min = to_nonnegative_int(minute(), 60); + format_tm(time, &tm_writer_type::on_minute, ns); + } + + void on_second(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) { + write(second(), 2); + write_fractional_seconds(std::chrono::duration{val}); + return; + } + auto time = tm(); + time.tm_sec = to_nonnegative_int(second(), 60); + format_tm(time, &tm_writer_type::on_second, ns); + } + + void on_12_hour_time() { + if (handle_nan_inf()) return; + format_tm(time(), &tm_writer_type::on_12_hour_time); + } + + void on_24_hour_time() { + if (handle_nan_inf()) { + *out++ = ':'; + handle_nan_inf(); + return; + } + + write(hour(), 2); + *out++ = ':'; + write(minute(), 2); + } + + void on_iso_time() { + on_24_hour_time(); + *out++ = ':'; + if (handle_nan_inf()) return; + on_second(numeric_system::standard); + } + + void on_am_pm() { + if (handle_nan_inf()) return; + format_tm(time(), &tm_writer_type::on_am_pm); + } + + void on_duration_value() { + if (handle_nan_inf()) return; + write_sign(); + out = format_duration_value(out, val, precision); + } + + void on_duration_unit() { + out = format_duration_unit(out); + } +}; + +FMT_END_DETAIL_NAMESPACE + +#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 +using weekday = std::chrono::weekday; +#else +// A fallback version of weekday. +class weekday { + private: + unsigned char value; + + public: + weekday() = default; + explicit constexpr weekday(unsigned wd) noexcept + : value(static_cast(wd != 7 ? wd : 0)) {} + constexpr unsigned c_encoding() const noexcept { return value; } +}; + +class year_month_day {}; +#endif + +// A rudimentary weekday formatter. +template struct formatter { + private: + bool localized = false; + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin != end && *begin == 'L') { + ++begin; + localized = true; + } + return begin; + } + + template + auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_wday = static_cast(wd.c_encoding()); + detail::get_locale loc(localized, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_abbr_weekday(); + return w.out(); + } +}; + +template +struct formatter, Char> { + private: + basic_format_specs specs; + int precision = -1; + using arg_ref_type = detail::arg_ref; + arg_ref_type width_ref; + arg_ref_type precision_ref; + bool localized = false; + basic_string_view format_str; + using duration = std::chrono::duration; + + struct spec_handler { + formatter& f; + basic_format_parse_context& context; + basic_string_view format_str; + + template FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) { + context.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view arg_id) { + context.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) { + return arg_ref_type(context.next_arg_id()); + } + + void on_error(const char* msg) { FMT_THROW(format_error(msg)); } + FMT_CONSTEXPR void on_fill(basic_string_view fill) { + f.specs.fill = fill; + } + FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; } + FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; } + FMT_CONSTEXPR void on_precision(int _precision) { + f.precision = _precision; + } + FMT_CONSTEXPR void end_precision() {} + + template FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { + f.width_ref = make_arg_ref(arg_id); + } + + template FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { + f.precision_ref = make_arg_ref(arg_id); + } + }; + + using iterator = typename basic_format_parse_context::iterator; + struct parse_range { + iterator begin; + iterator end; + }; + + FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context& ctx) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end || *begin == '}') return {begin, begin}; + spec_handler handler{*this, ctx, format_str}; + begin = detail::parse_align(begin, end, handler); + if (begin == end) return {begin, begin}; + begin = detail::parse_width(begin, end, handler); + if (begin == end) return {begin, begin}; + if (*begin == '.') { + if (std::is_floating_point::value) + begin = detail::parse_precision(begin, end, handler); + else + handler.on_error("precision not allowed for this argument type"); + } + if (begin != end && *begin == 'L') { + ++begin; + localized = true; + } + end = detail::parse_chrono_format(begin, end, + detail::chrono_format_checker()); + return {begin, end}; + } + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto range = do_parse(ctx); + format_str = basic_string_view( + &*range.begin, detail::to_unsigned(range.end - range.begin)); + return range.end; + } + + template + auto format(const duration& d, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto specs_copy = specs; + auto precision_copy = precision; + auto begin = format_str.begin(), end = format_str.end(); + // As a possible future optimization, we could avoid extra copying if width + // is not specified. + basic_memory_buffer buf; + auto out = std::back_inserter(buf); + detail::handle_dynamic_spec(specs_copy.width, + width_ref, ctx); + detail::handle_dynamic_spec(precision_copy, + precision_ref, ctx); + if (begin == end || *begin == '}') { + out = detail::format_duration_value(out, d.count(), precision_copy); + detail::format_duration_unit(out); + } else { + detail::chrono_formatter f( + ctx, out, d); + f.precision = precision_copy; + f.localized = localized; + detail::parse_chrono_format(begin, end, f); + } + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); + } +}; + +template +struct formatter, + Char> : formatter { + FMT_CONSTEXPR formatter() { + this->do_parse(default_specs, + default_specs + sizeof(default_specs) / sizeof(Char)); + } + + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return this->do_parse(ctx.begin(), ctx.end(), true); + } + + template + auto format(std::chrono::time_point val, + FormatContext& ctx) const -> decltype(ctx.out()) { + return formatter::format(localtime(val), ctx); + } + + static constexpr const Char default_specs[] = {'%', 'F', ' ', '%', 'T'}; +}; + +template +constexpr const Char + formatter, + Char>::default_specs[]; + +template struct formatter { + private: + enum class spec { + unknown, + year_month_day, + hh_mm_ss, + }; + spec spec_ = spec::unknown; + basic_string_view specs; + + protected: + template + FMT_CONSTEXPR auto do_parse(It begin, It end, bool with_default = false) + -> It { + if (begin != end && *begin == ':') ++begin; + end = detail::parse_chrono_format(begin, end, detail::tm_format_checker()); + if (!with_default || end != begin) + specs = {begin, detail::to_unsigned(end - begin)}; + // basic_string_view<>::compare isn't constexpr before C++17. + if (specs.size() == 2 && specs[0] == Char('%')) { + if (specs[1] == Char('F')) + spec_ = spec::year_month_day; + else if (specs[1] == Char('T')) + spec_ = spec::hh_mm_ss; + } + return end; + } + + public: + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return this->do_parse(ctx.begin(), ctx.end()); + } + + template + auto format(const std::tm& tm, FormatContext& ctx) const + -> decltype(ctx.out()) { + const auto loc_ref = ctx.locale(); + detail::get_locale loc(static_cast(loc_ref), loc_ref); + auto w = detail::tm_writer(loc, ctx.out(), tm); + if (spec_ == spec::year_month_day) + w.on_iso_date(); + else if (spec_ == spec::hh_mm_ss) + w.on_iso_time(); + else + detail::parse_chrono_format(specs.begin(), specs.end(), w); + return w.out(); + } +}; + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_CHRONO_H_ diff --git a/ctrtool/deps/libfmt/include/fmt/color.h b/ctrtool/deps/libfmt/include/fmt/color.h new file mode 100644 index 00000000..dfbe4829 --- /dev/null +++ b/ctrtool/deps/libfmt/include/fmt/color.h @@ -0,0 +1,638 @@ +// Formatting library for C++ - color support +// +// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COLOR_H_ +#define FMT_COLOR_H_ + +#include "format.h" + +// __declspec(deprecated) is broken in some MSVC versions. +#if FMT_MSC_VER +# define FMT_DEPRECATED_NONMSVC +#else +# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED +#endif + +FMT_BEGIN_NAMESPACE +FMT_MODULE_EXPORT_BEGIN + +enum class color : uint32_t { + alice_blue = 0xF0F8FF, // rgb(240,248,255) + antique_white = 0xFAEBD7, // rgb(250,235,215) + aqua = 0x00FFFF, // rgb(0,255,255) + aquamarine = 0x7FFFD4, // rgb(127,255,212) + azure = 0xF0FFFF, // rgb(240,255,255) + beige = 0xF5F5DC, // rgb(245,245,220) + bisque = 0xFFE4C4, // rgb(255,228,196) + black = 0x000000, // rgb(0,0,0) + blanched_almond = 0xFFEBCD, // rgb(255,235,205) + blue = 0x0000FF, // rgb(0,0,255) + blue_violet = 0x8A2BE2, // rgb(138,43,226) + brown = 0xA52A2A, // rgb(165,42,42) + burly_wood = 0xDEB887, // rgb(222,184,135) + cadet_blue = 0x5F9EA0, // rgb(95,158,160) + chartreuse = 0x7FFF00, // rgb(127,255,0) + chocolate = 0xD2691E, // rgb(210,105,30) + coral = 0xFF7F50, // rgb(255,127,80) + cornflower_blue = 0x6495ED, // rgb(100,149,237) + cornsilk = 0xFFF8DC, // rgb(255,248,220) + crimson = 0xDC143C, // rgb(220,20,60) + cyan = 0x00FFFF, // rgb(0,255,255) + dark_blue = 0x00008B, // rgb(0,0,139) + dark_cyan = 0x008B8B, // rgb(0,139,139) + dark_golden_rod = 0xB8860B, // rgb(184,134,11) + dark_gray = 0xA9A9A9, // rgb(169,169,169) + dark_green = 0x006400, // rgb(0,100,0) + dark_khaki = 0xBDB76B, // rgb(189,183,107) + dark_magenta = 0x8B008B, // rgb(139,0,139) + dark_olive_green = 0x556B2F, // rgb(85,107,47) + dark_orange = 0xFF8C00, // rgb(255,140,0) + dark_orchid = 0x9932CC, // rgb(153,50,204) + dark_red = 0x8B0000, // rgb(139,0,0) + dark_salmon = 0xE9967A, // rgb(233,150,122) + dark_sea_green = 0x8FBC8F, // rgb(143,188,143) + dark_slate_blue = 0x483D8B, // rgb(72,61,139) + dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) + dark_turquoise = 0x00CED1, // rgb(0,206,209) + dark_violet = 0x9400D3, // rgb(148,0,211) + deep_pink = 0xFF1493, // rgb(255,20,147) + deep_sky_blue = 0x00BFFF, // rgb(0,191,255) + dim_gray = 0x696969, // rgb(105,105,105) + dodger_blue = 0x1E90FF, // rgb(30,144,255) + fire_brick = 0xB22222, // rgb(178,34,34) + floral_white = 0xFFFAF0, // rgb(255,250,240) + forest_green = 0x228B22, // rgb(34,139,34) + fuchsia = 0xFF00FF, // rgb(255,0,255) + gainsboro = 0xDCDCDC, // rgb(220,220,220) + ghost_white = 0xF8F8FF, // rgb(248,248,255) + gold = 0xFFD700, // rgb(255,215,0) + golden_rod = 0xDAA520, // rgb(218,165,32) + gray = 0x808080, // rgb(128,128,128) + green = 0x008000, // rgb(0,128,0) + green_yellow = 0xADFF2F, // rgb(173,255,47) + honey_dew = 0xF0FFF0, // rgb(240,255,240) + hot_pink = 0xFF69B4, // rgb(255,105,180) + indian_red = 0xCD5C5C, // rgb(205,92,92) + indigo = 0x4B0082, // rgb(75,0,130) + ivory = 0xFFFFF0, // rgb(255,255,240) + khaki = 0xF0E68C, // rgb(240,230,140) + lavender = 0xE6E6FA, // rgb(230,230,250) + lavender_blush = 0xFFF0F5, // rgb(255,240,245) + lawn_green = 0x7CFC00, // rgb(124,252,0) + lemon_chiffon = 0xFFFACD, // rgb(255,250,205) + light_blue = 0xADD8E6, // rgb(173,216,230) + light_coral = 0xF08080, // rgb(240,128,128) + light_cyan = 0xE0FFFF, // rgb(224,255,255) + light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) + light_gray = 0xD3D3D3, // rgb(211,211,211) + light_green = 0x90EE90, // rgb(144,238,144) + light_pink = 0xFFB6C1, // rgb(255,182,193) + light_salmon = 0xFFA07A, // rgb(255,160,122) + light_sea_green = 0x20B2AA, // rgb(32,178,170) + light_sky_blue = 0x87CEFA, // rgb(135,206,250) + light_slate_gray = 0x778899, // rgb(119,136,153) + light_steel_blue = 0xB0C4DE, // rgb(176,196,222) + light_yellow = 0xFFFFE0, // rgb(255,255,224) + lime = 0x00FF00, // rgb(0,255,0) + lime_green = 0x32CD32, // rgb(50,205,50) + linen = 0xFAF0E6, // rgb(250,240,230) + magenta = 0xFF00FF, // rgb(255,0,255) + maroon = 0x800000, // rgb(128,0,0) + medium_aquamarine = 0x66CDAA, // rgb(102,205,170) + medium_blue = 0x0000CD, // rgb(0,0,205) + medium_orchid = 0xBA55D3, // rgb(186,85,211) + medium_purple = 0x9370DB, // rgb(147,112,219) + medium_sea_green = 0x3CB371, // rgb(60,179,113) + medium_slate_blue = 0x7B68EE, // rgb(123,104,238) + medium_spring_green = 0x00FA9A, // rgb(0,250,154) + medium_turquoise = 0x48D1CC, // rgb(72,209,204) + medium_violet_red = 0xC71585, // rgb(199,21,133) + midnight_blue = 0x191970, // rgb(25,25,112) + mint_cream = 0xF5FFFA, // rgb(245,255,250) + misty_rose = 0xFFE4E1, // rgb(255,228,225) + moccasin = 0xFFE4B5, // rgb(255,228,181) + navajo_white = 0xFFDEAD, // rgb(255,222,173) + navy = 0x000080, // rgb(0,0,128) + old_lace = 0xFDF5E6, // rgb(253,245,230) + olive = 0x808000, // rgb(128,128,0) + olive_drab = 0x6B8E23, // rgb(107,142,35) + orange = 0xFFA500, // rgb(255,165,0) + orange_red = 0xFF4500, // rgb(255,69,0) + orchid = 0xDA70D6, // rgb(218,112,214) + pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) + pale_green = 0x98FB98, // rgb(152,251,152) + pale_turquoise = 0xAFEEEE, // rgb(175,238,238) + pale_violet_red = 0xDB7093, // rgb(219,112,147) + papaya_whip = 0xFFEFD5, // rgb(255,239,213) + peach_puff = 0xFFDAB9, // rgb(255,218,185) + peru = 0xCD853F, // rgb(205,133,63) + pink = 0xFFC0CB, // rgb(255,192,203) + plum = 0xDDA0DD, // rgb(221,160,221) + powder_blue = 0xB0E0E6, // rgb(176,224,230) + purple = 0x800080, // rgb(128,0,128) + rebecca_purple = 0x663399, // rgb(102,51,153) + red = 0xFF0000, // rgb(255,0,0) + rosy_brown = 0xBC8F8F, // rgb(188,143,143) + royal_blue = 0x4169E1, // rgb(65,105,225) + saddle_brown = 0x8B4513, // rgb(139,69,19) + salmon = 0xFA8072, // rgb(250,128,114) + sandy_brown = 0xF4A460, // rgb(244,164,96) + sea_green = 0x2E8B57, // rgb(46,139,87) + sea_shell = 0xFFF5EE, // rgb(255,245,238) + sienna = 0xA0522D, // rgb(160,82,45) + silver = 0xC0C0C0, // rgb(192,192,192) + sky_blue = 0x87CEEB, // rgb(135,206,235) + slate_blue = 0x6A5ACD, // rgb(106,90,205) + slate_gray = 0x708090, // rgb(112,128,144) + snow = 0xFFFAFA, // rgb(255,250,250) + spring_green = 0x00FF7F, // rgb(0,255,127) + steel_blue = 0x4682B4, // rgb(70,130,180) + tan = 0xD2B48C, // rgb(210,180,140) + teal = 0x008080, // rgb(0,128,128) + thistle = 0xD8BFD8, // rgb(216,191,216) + tomato = 0xFF6347, // rgb(255,99,71) + turquoise = 0x40E0D0, // rgb(64,224,208) + violet = 0xEE82EE, // rgb(238,130,238) + wheat = 0xF5DEB3, // rgb(245,222,179) + white = 0xFFFFFF, // rgb(255,255,255) + white_smoke = 0xF5F5F5, // rgb(245,245,245) + yellow = 0xFFFF00, // rgb(255,255,0) + yellow_green = 0x9ACD32 // rgb(154,205,50) +}; // enum class color + +enum class terminal_color : uint8_t { + black = 30, + red, + green, + yellow, + blue, + magenta, + cyan, + white, + bright_black = 90, + bright_red, + bright_green, + bright_yellow, + bright_blue, + bright_magenta, + bright_cyan, + bright_white +}; + +enum class emphasis : uint8_t { + bold = 1, + faint = 1 << 1, + italic = 1 << 2, + underline = 1 << 3, + blink = 1 << 4, + reverse = 1 << 5, + conceal = 1 << 6, + strikethrough = 1 << 7, +}; + +// rgb is a struct for red, green and blue colors. +// Using the name "rgb" makes some editors show the color in a tooltip. +struct rgb { + FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} + FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} + FMT_CONSTEXPR rgb(uint32_t hex) + : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} + FMT_CONSTEXPR rgb(color hex) + : r((uint32_t(hex) >> 16) & 0xFF), + g((uint32_t(hex) >> 8) & 0xFF), + b(uint32_t(hex) & 0xFF) {} + uint8_t r; + uint8_t g; + uint8_t b; +}; + +FMT_BEGIN_DETAIL_NAMESPACE + +// color is a struct of either a rgb color or a terminal color. +struct color_type { + FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} + FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), + value{} { + value.rgb_color = static_cast(rgb_color); + } + FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { + value.rgb_color = (static_cast(rgb_color.r) << 16) | + (static_cast(rgb_color.g) << 8) | rgb_color.b; + } + FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), + value{} { + value.term_color = static_cast(term_color); + } + bool is_rgb; + union color_union { + uint8_t term_color; + uint32_t rgb_color; + } value; +}; + +FMT_END_DETAIL_NAMESPACE + +/** A text style consisting of foreground and background colors and emphasis. */ +class text_style { + public: + FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT + : set_foreground_color(), + set_background_color(), + ems(em) {} + + FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) + FMT_THROW(format_error("can't OR a terminal color")); + foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + if (!background_color.is_rgb || !rhs.background_color.is_rgb) + FMT_THROW(format_error("can't OR a terminal color")); + background_color.value.rgb_color |= rhs.background_color.value.rgb_color; + } + + ems = static_cast(static_cast(ems) | + static_cast(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR text_style operator|(text_style lhs, + const text_style& rhs) { + return lhs |= rhs; + } + + FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=( + const text_style& rhs) { + return and_assign(rhs); + } + + FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style + operator&(text_style lhs, const text_style& rhs) { + return lhs.and_assign(rhs); + } + + FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { + return set_foreground_color; + } + FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { + return set_background_color; + } + FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { + return static_cast(ems) != 0; + } + FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT { + FMT_ASSERT(has_foreground(), "no foreground specified for this style"); + return foreground_color; + } + FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT { + FMT_ASSERT(has_background(), "no background specified for this style"); + return background_color; + } + FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { + FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); + return ems; + } + + private: + FMT_CONSTEXPR text_style(bool is_foreground, + detail::color_type text_color) FMT_NOEXCEPT + : set_foreground_color(), + set_background_color(), + ems() { + if (is_foreground) { + foreground_color = text_color; + set_foreground_color = true; + } else { + background_color = text_color; + set_background_color = true; + } + } + + // DEPRECATED! + FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) + FMT_THROW(format_error("can't AND a terminal color")); + foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + if (!background_color.is_rgb || !rhs.background_color.is_rgb) + FMT_THROW(format_error("can't AND a terminal color")); + background_color.value.rgb_color &= rhs.background_color.value.rgb_color; + } + + ems = static_cast(static_cast(ems) & + static_cast(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) + FMT_NOEXCEPT; + + friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) + FMT_NOEXCEPT; + + detail::color_type foreground_color; + detail::color_type background_color; + bool set_foreground_color; + bool set_background_color; + emphasis ems; +}; + +/** Creates a text style from the foreground (text) color. */ +FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT { + return text_style(true, foreground); +} + +/** Creates a text style from the background color. */ +FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT { + return text_style(false, background); +} + +FMT_CONSTEXPR inline text_style operator|(emphasis lhs, + emphasis rhs) FMT_NOEXCEPT { + return text_style(lhs) | rhs; +} + +FMT_BEGIN_DETAIL_NAMESPACE + +template struct ansi_color_escape { + FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, + const char* esc) FMT_NOEXCEPT { + // If we have a terminal color, we need to output another escape code + // sequence. + if (!text_color.is_rgb) { + bool is_background = esc == string_view("\x1b[48;2;"); + uint32_t value = text_color.value.term_color; + // Background ASCII codes are the same as the foreground ones but with + // 10 more. + if (is_background) value += 10u; + + size_t index = 0; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + + if (value >= 100u) { + buffer[index++] = static_cast('1'); + value %= 100u; + } + buffer[index++] = static_cast('0' + value / 10u); + buffer[index++] = static_cast('0' + value % 10u); + + buffer[index++] = static_cast('m'); + buffer[index++] = static_cast('\0'); + return; + } + + for (int i = 0; i < 7; i++) { + buffer[i] = static_cast(esc[i]); + } + rgb color(text_color.value.rgb_color); + to_esc(color.r, buffer + 7, ';'); + to_esc(color.g, buffer + 11, ';'); + to_esc(color.b, buffer + 15, 'm'); + buffer[19] = static_cast(0); + } + FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { + uint8_t em_codes[num_emphases] = {}; + if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; + if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; + if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3; + if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4; + if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5; + if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7; + if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; + if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; + + size_t index = 0; + for (size_t i = 0; i < num_emphases; ++i) { + if (!em_codes[i]) continue; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + buffer[index++] = static_cast('0' + em_codes[i]); + buffer[index++] = static_cast('m'); + } + buffer[index++] = static_cast(0); + } + FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } + + FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } + FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT { + return buffer + std::char_traits::length(buffer); + } + + private: + static constexpr size_t num_emphases = 8; + Char buffer[7u + 3u * num_emphases + 1u]; + + static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, + char delimiter) FMT_NOEXCEPT { + out[0] = static_cast('0' + c / 100); + out[1] = static_cast('0' + c / 10 % 10); + out[2] = static_cast('0' + c % 10); + out[3] = static_cast(delimiter); + } + static FMT_CONSTEXPR bool has_emphasis(emphasis em, + emphasis mask) FMT_NOEXCEPT { + return static_cast(em) & static_cast(mask); + } +}; + +template +FMT_CONSTEXPR ansi_color_escape make_foreground_color( + detail::color_type foreground) FMT_NOEXCEPT { + return ansi_color_escape(foreground, "\x1b[38;2;"); +} + +template +FMT_CONSTEXPR ansi_color_escape make_background_color( + detail::color_type background) FMT_NOEXCEPT { + return ansi_color_escape(background, "\x1b[48;2;"); +} + +template +FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) FMT_NOEXCEPT { + return ansi_color_escape(em); +} + +template +inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { + std::fputs(chars, stream); +} + +template <> +inline void fputs(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { + std::fputws(chars, stream); +} + +template inline void reset_color(FILE* stream) FMT_NOEXCEPT { + fputs("\x1b[0m", stream); +} + +template <> inline void reset_color(FILE* stream) FMT_NOEXCEPT { + fputs(L"\x1b[0m", stream); +} + +template +inline void reset_color(buffer& buffer) FMT_NOEXCEPT { + auto reset_color = string_view("\x1b[0m"); + buffer.append(reset_color.begin(), reset_color.end()); +} + +template +void vformat_to(buffer& buf, const text_style& ts, + basic_string_view format_str, + basic_format_args>> args) { + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = detail::make_emphasis(ts.get_emphasis()); + buf.append(emphasis.begin(), emphasis.end()); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = detail::make_foreground_color(ts.get_foreground()); + buf.append(foreground.begin(), foreground.end()); + } + if (ts.has_background()) { + has_style = true; + auto background = detail::make_background_color(ts.get_background()); + buf.append(background.begin(), background.end()); + } + detail::vformat_to(buf, format_str, args, {}); + if (has_style) detail::reset_color(buf); +} + +FMT_END_DETAIL_NAMESPACE + +template > +void vprint(std::FILE* f, const text_style& ts, const S& format, + basic_format_args>> args) { + basic_memory_buffer buf; + detail::vformat_to(buf, ts, to_string_view(format), args); + buf.push_back(Char(0)); + detail::fputs(buf.data(), f); +} + +/** + \rst + Formats a string and prints it to the specified file stream using ANSI + escape sequences to specify text formatting. + + **Example**:: + + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + "Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template ::value)> +void print(std::FILE* f, const text_style& ts, const S& format_str, + const Args&... args) { + vprint(f, ts, format_str, + fmt::make_args_checked(format_str, args...)); +} + +/** + \rst + Formats a string and prints it to stdout using ANSI escape sequences to + specify text formatting. + + **Example**:: + + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + "Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template ::value)> +void print(const text_style& ts, const S& format_str, const Args&... args) { + return print(stdout, ts, format_str, args...); +} + +template > +inline std::basic_string vformat( + const text_style& ts, const S& format_str, + basic_format_args>> args) { + basic_memory_buffer buf; + detail::vformat_to(buf, ts, to_string_view(format_str), args); + return fmt::to_string(buf); +} + +/** + \rst + Formats arguments and returns the result as a string using ANSI + escape sequences to specify text formatting. + + **Example**:: + + #include + std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), + "The answer is {}", 42); + \endrst +*/ +template > +inline std::basic_string format(const text_style& ts, const S& format_str, + const Args&... args) { + return fmt::vformat(ts, to_string_view(format_str), + fmt::make_args_checked(format_str, args...)); +} + +/** + Formats a string with the given text_style and writes the output to ``out``. + */ +template ::value)> +OutputIt vformat_to( + OutputIt out, const text_style& ts, basic_string_view format_str, + basic_format_args>> args) { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, ts, format_str, args); + return detail::get_iterator(buf); +} + +/** + \rst + Formats arguments with the given text_style, writes the result to the output + iterator ``out`` and returns the iterator past the end of the output range. + + **Example**:: + + std::vector out; + fmt::format_to(std::back_inserter(out), + fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); + \endrst +*/ +template >::value&& + detail::is_string::value> +inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, + Args&&... args) -> + typename std::enable_if::type { + return vformat_to(out, ts, to_string_view(format_str), + fmt::make_args_checked(format_str, args...)); +} + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_COLOR_H_ diff --git a/ctrtool/deps/libfmt/include/fmt/compile.h b/ctrtool/deps/libfmt/include/fmt/compile.h new file mode 100644 index 00000000..1dba3ddb --- /dev/null +++ b/ctrtool/deps/libfmt/include/fmt/compile.h @@ -0,0 +1,642 @@ +// Formatting library for C++ - experimental format string compilation +// +// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COMPILE_H_ +#define FMT_COMPILE_H_ + +#include "format.h" + +FMT_BEGIN_NAMESPACE +namespace detail { + +// An output iterator that counts the number of objects written to it and +// discards them. +class counting_iterator { + private: + size_t count_; + + public: + using iterator_category = std::output_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + using _Unchecked_type = counting_iterator; // Mark iterator as checked. + + struct value_type { + template void operator=(const T&) {} + }; + + counting_iterator() : count_(0) {} + + size_t count() const { return count_; } + + counting_iterator& operator++() { + ++count_; + return *this; + } + counting_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + friend counting_iterator operator+(counting_iterator it, difference_type n) { + it.count_ += static_cast(n); + return it; + } + + value_type operator*() const { return {}; } +}; + +template +inline counting_iterator copy_str(InputIt begin, InputIt end, + counting_iterator it) { + return it + (end - begin); +} + +template class truncating_iterator_base { + protected: + OutputIt out_; + size_t limit_; + size_t count_ = 0; + + truncating_iterator_base() : out_(), limit_(0) {} + + truncating_iterator_base(OutputIt out, size_t limit) + : out_(out), limit_(limit) {} + + public: + using iterator_category = std::output_iterator_tag; + using value_type = typename std::iterator_traits::value_type; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + using _Unchecked_type = + truncating_iterator_base; // Mark iterator as checked. + + OutputIt base() const { return out_; } + size_t count() const { return count_; } +}; + +// An output iterator that truncates the output and counts the number of objects +// written to it. +template ::value_type>::type> +class truncating_iterator; + +template +class truncating_iterator + : public truncating_iterator_base { + mutable typename truncating_iterator_base::value_type blackhole_; + + public: + using value_type = typename truncating_iterator_base::value_type; + + truncating_iterator() = default; + + truncating_iterator(OutputIt out, size_t limit) + : truncating_iterator_base(out, limit) {} + + truncating_iterator& operator++() { + if (this->count_++ < this->limit_) ++this->out_; + return *this; + } + + truncating_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + value_type& operator*() const { + return this->count_ < this->limit_ ? *this->out_ : blackhole_; + } +}; + +template +class truncating_iterator + : public truncating_iterator_base { + public: + truncating_iterator() = default; + + truncating_iterator(OutputIt out, size_t limit) + : truncating_iterator_base(out, limit) {} + + template truncating_iterator& operator=(T val) { + if (this->count_++ < this->limit_) *this->out_++ = val; + return *this; + } + + truncating_iterator& operator++() { return *this; } + truncating_iterator& operator++(int) { return *this; } + truncating_iterator& operator*() { return *this; } +}; + +// A compile-time string which is compiled into fast formatting code. +class compiled_string {}; + +template +struct is_compiled_string : std::is_base_of {}; + +/** + \rst + Converts a string literal *s* into a format string that will be parsed at + compile time and converted into efficient formatting code. Requires C++17 + ``constexpr if`` compiler support. + + **Example**:: + + // Converts 42 into std::string using the most efficient method and no + // runtime format string processing. + std::string s = fmt::format(FMT_COMPILE("{}"), 42); + \endrst + */ +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +# define FMT_COMPILE(s) \ + FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) +#else +# define FMT_COMPILE(s) FMT_STRING(s) +#endif + +#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +template Str> +struct udl_compiled_string : compiled_string { + using char_type = Char; + constexpr operator basic_string_view() const { + return {Str.data, N - 1}; + } +}; +#endif + +template +const T& first(const T& value, const Tail&...) { + return value; +} + +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +template struct type_list {}; + +// Returns a reference to the argument at index N from [first, rest...]. +template +constexpr const auto& get([[maybe_unused]] const T& first, + [[maybe_unused]] const Args&... rest) { + static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); + if constexpr (N == 0) + return first; + else + return detail::get(rest...); +} + +template +constexpr int get_arg_index_by_name(basic_string_view name, + type_list) { + return get_arg_index_by_name(name); +} + +template struct get_type_impl; + +template struct get_type_impl> { + using type = + remove_cvref_t(std::declval()...))>; +}; + +template +using get_type = typename get_type_impl::type; + +template struct is_compiled_format : std::false_type {}; + +template struct text { + basic_string_view data; + using char_type = Char; + + template + constexpr OutputIt format(OutputIt out, const Args&...) const { + return write(out, data); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr text make_text(basic_string_view s, size_t pos, + size_t size) { + return {{&s[pos], size}}; +} + +template struct code_unit { + Char value; + using char_type = Char; + + template + constexpr OutputIt format(OutputIt out, const Args&...) const { + return write(out, value); + } +}; + +// This ensures that the argument type is convertible to `const T&`. +template +constexpr const T& get_arg_checked(const Args&... args) { + const auto& arg = detail::get(args...); + if constexpr (detail::is_named_arg>()) { + return arg.value; + } else { + return arg; + } +} + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument N. +template struct field { + using char_type = Char; + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + return write(out, get_arg_checked(args...)); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument with name. +template struct runtime_named_field { + using char_type = Char; + basic_string_view name; + + template + constexpr static bool try_format_argument( + OutputIt& out, + // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 + [[maybe_unused]] basic_string_view arg_name, const T& arg) { + if constexpr (is_named_arg::type>::value) { + if (arg_name == arg.name) { + out = write(out, arg.value); + return true; + } + } + return false; + } + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + bool found = (try_format_argument(out, name, args) || ...); + if (!found) { + FMT_THROW(format_error("argument with specified name is not found")); + } + return out; + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument N and has format specifiers. +template struct spec_field { + using char_type = Char; + formatter fmt; + + template + constexpr FMT_INLINE OutputIt format(OutputIt out, + const Args&... args) const { + const auto& vargs = + fmt::make_format_args>(args...); + basic_format_context ctx(out, vargs); + return fmt.format(get_arg_checked(args...), ctx); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template struct concat { + L lhs; + R rhs; + using char_type = typename L::char_type; + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + out = lhs.format(out, args...); + return rhs.format(out, args...); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr concat make_concat(L lhs, R rhs) { + return {lhs, rhs}; +} + +struct unknown_format {}; + +template +constexpr size_t parse_text(basic_string_view str, size_t pos) { + for (size_t size = str.size(); pos != size; ++pos) { + if (str[pos] == '{' || str[pos] == '}') break; + } + return pos; +} + +template +constexpr auto compile_format_string(S format_str); + +template +constexpr auto parse_tail(T head, S format_str) { + if constexpr (POS != + basic_string_view(format_str).size()) { + constexpr auto tail = compile_format_string(format_str); + if constexpr (std::is_same, + unknown_format>()) + return tail; + else + return make_concat(head, tail); + } else { + return head; + } +} + +template struct parse_specs_result { + formatter fmt; + size_t end; + int next_arg_id; +}; + +constexpr int manual_indexing_id = -1; + +template +constexpr parse_specs_result parse_specs(basic_string_view str, + size_t pos, int next_arg_id) { + str.remove_prefix(pos); + auto ctx = basic_format_parse_context(str, {}, next_arg_id); + auto f = formatter(); + auto end = f.parse(ctx); + return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1, + next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; +} + +template struct arg_id_handler { + arg_ref arg_id; + + constexpr int operator()() { + FMT_ASSERT(false, "handler cannot be used with automatic indexing"); + return 0; + } + constexpr int operator()(int id) { + arg_id = arg_ref(id); + return 0; + } + constexpr int operator()(basic_string_view id) { + arg_id = arg_ref(id); + return 0; + } + + constexpr void on_error(const char* message) { + FMT_THROW(format_error(message)); + } +}; + +template struct parse_arg_id_result { + arg_ref arg_id; + const Char* arg_id_end; +}; + +template +constexpr auto parse_arg_id(const Char* begin, const Char* end) { + auto handler = arg_id_handler{arg_ref{}}; + auto arg_id_end = parse_arg_id(begin, end, handler); + return parse_arg_id_result{handler.arg_id, arg_id_end}; +} + +template struct field_type { + using type = remove_cvref_t; +}; + +template +struct field_type::value>> { + using type = remove_cvref_t; +}; + +template +constexpr auto parse_replacement_field_then_tail(S format_str) { + using char_type = typename S::char_type; + constexpr auto str = basic_string_view(format_str); + constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); + if constexpr (c == '}') { + return parse_tail( + field::type, ARG_INDEX>(), + format_str); + } else if constexpr (c == ':') { + constexpr auto result = parse_specs::type>( + str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); + return parse_tail( + spec_field::type, ARG_INDEX>{ + result.fmt}, + format_str); + } +} + +// Compiles a non-empty format string and returns the compiled representation +// or unknown_format() on unrecognized input. +template +constexpr auto compile_format_string(S format_str) { + using char_type = typename S::char_type; + constexpr auto str = basic_string_view(format_str); + if constexpr (str[POS] == '{') { + if constexpr (POS + 1 == str.size()) + FMT_THROW(format_error("unmatched '{' in format string")); + if constexpr (str[POS + 1] == '{') { + return parse_tail(make_text(str, POS, 1), format_str); + } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { + static_assert(ID != manual_indexing_id, + "cannot switch from manual to automatic argument indexing"); + constexpr auto next_id = + ID != manual_indexing_id ? ID + 1 : manual_indexing_id; + return parse_replacement_field_then_tail, Args, + POS + 1, ID, next_id>( + format_str); + } else { + constexpr auto arg_id_result = + parse_arg_id(str.data() + POS + 1, str.data() + str.size()); + constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data(); + constexpr char_type c = + arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); + static_assert(c == '}' || c == ':', "missing '}' in format string"); + if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) { + static_assert( + ID == manual_indexing_id || ID == 0, + "cannot switch from automatic to manual argument indexing"); + constexpr auto arg_index = arg_id_result.arg_id.val.index; + return parse_replacement_field_then_tail, + Args, arg_id_end_pos, + arg_index, manual_indexing_id>( + format_str); + } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { + constexpr auto arg_index = + get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); + if constexpr (arg_index != invalid_arg_index) { + constexpr auto next_id = + ID != manual_indexing_id ? ID + 1 : manual_indexing_id; + return parse_replacement_field_then_tail< + decltype(get_type::value), Args, arg_id_end_pos, + arg_index, next_id>(format_str); + } else { + if constexpr (c == '}') { + return parse_tail( + runtime_named_field{arg_id_result.arg_id.val.name}, + format_str); + } else if constexpr (c == ':') { + return unknown_format(); // no type info for specs parsing + } + } + } + } + } else if constexpr (str[POS] == '}') { + if constexpr (POS + 1 == str.size()) + FMT_THROW(format_error("unmatched '}' in format string")); + return parse_tail(make_text(str, POS, 1), format_str); + } else { + constexpr auto end = parse_text(str, POS + 1); + if constexpr (end - POS > 1) { + return parse_tail(make_text(str, POS, end - POS), + format_str); + } else { + return parse_tail(code_unit{str[POS]}, + format_str); + } + } +} + +template ::value)> +constexpr auto compile(S format_str) { + constexpr auto str = basic_string_view(format_str); + if constexpr (str.size() == 0) { + return detail::make_text(str, 0, 0); + } else { + constexpr auto result = + detail::compile_format_string, 0, 0>( + format_str); + return result; + } +} +#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +} // namespace detail + +FMT_MODULE_EXPORT_BEGIN + +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) + +template ::value)> +FMT_INLINE std::basic_string format(const CompiledFormat& cf, + const Args&... args) { + auto s = std::basic_string(); + cf.format(std::back_inserter(s), args...); + return s; +} + +template ::value)> +constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { + return cf.format(out, args...); +} + +template ::value)> +FMT_INLINE std::basic_string format(const S&, + Args&&... args) { + if constexpr (std::is_same::value) { + constexpr auto str = basic_string_view(S()); + if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { + const auto& first = detail::first(args...); + if constexpr (detail::is_named_arg< + remove_cvref_t>::value) { + return fmt::to_string(first.value); + } else { + return fmt::to_string(first); + } + } + } + constexpr auto compiled = detail::compile(S()); + if constexpr (std::is_same, + detail::unknown_format>()) { + return format(static_cast>(S()), + std::forward(args)...); + } else { + return format(compiled, std::forward(args)...); + } +} + +template ::value)> +FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { + constexpr auto compiled = detail::compile(S()); + if constexpr (std::is_same, + detail::unknown_format>()) { + return format_to(out, + static_cast>(S()), + std::forward(args)...); + } else { + return format_to(out, compiled, std::forward(args)...); + } +} +#endif + +template ::value)> +format_to_n_result format_to_n(OutputIt out, size_t n, + const S& format_str, Args&&... args) { + auto it = format_to(detail::truncating_iterator(out, n), format_str, + std::forward(args)...); + return {it.base(), it.count()}; +} + +template ::value)> +size_t formatted_size(const S& format_str, const Args&... args) { + return format_to(detail::counting_iterator(), format_str, args...).count(); +} + +template ::value)> +void print(std::FILE* f, const S& format_str, const Args&... args) { + memory_buffer buffer; + format_to(std::back_inserter(buffer), format_str, args...); + detail::print(f, {buffer.data(), buffer.size()}); +} + +template ::value)> +void print(const S& format_str, const Args&... args) { + print(stdout, format_str, args...); +} + +#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +inline namespace literals { +template +constexpr detail::udl_compiled_string< + remove_cvref_t, + sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> +operator""_cf() { + return {}; +} +} // namespace literals +#endif + +FMT_MODULE_EXPORT_END +FMT_END_NAMESPACE + +#endif // FMT_COMPILE_H_ diff --git a/ctrtool/deps/libfmt/include/fmt/core.h b/ctrtool/deps/libfmt/include/fmt/core.h new file mode 100644 index 00000000..92a7aa1d --- /dev/null +++ b/ctrtool/deps/libfmt/include/fmt/core.h @@ -0,0 +1,3236 @@ +// Formatting library for C++ - the core API for char/UTF-8 +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CORE_H_ +#define FMT_CORE_H_ + +#include // std::byte +#include // std::FILE +#include +#include +#include +#include +#include + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 80101 + +#if defined(__clang__) && !defined(__ibmxl__) +# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#else +# define FMT_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \ + !defined(__NVCOMPILER) +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define FMT_GCC_VERSION 0 +#endif + +#ifndef FMT_GCC_PRAGMA +// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884. +# if FMT_GCC_VERSION >= 504 +# define FMT_GCC_PRAGMA(arg) _Pragma(arg) +# else +# define FMT_GCC_PRAGMA(arg) +# endif +#endif + +#ifdef __ICL +# define FMT_ICC_VERSION __ICL +#elif defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#else +# define FMT_ICC_VERSION 0 +#endif + +#ifdef __NVCC__ +# define FMT_NVCC __NVCC__ +#else +# define FMT_NVCC 0 +#endif + +#ifdef _MSC_VER +# define FMT_MSC_VER _MSC_VER +# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) +#else +# define FMT_MSC_VER 0 +# define FMT_MSC_WARNING(...) +#endif + +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif + +#if defined(__has_include) && \ + (!defined(__INTELLISENSE__) || FMT_MSC_VER > 1900) && \ + (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600) +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif + +#ifdef __has_cpp_attribute +# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#ifdef _MSVC_LANG +# define FMT_CPLUSPLUS _MSVC_LANG +#else +# define FMT_CPLUSPLUS __cplusplus +#endif + +#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ + (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ + (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +// Check if relaxed C++14 constexpr is supported. +// GCC doesn't allow throw in constexpr until version 6 (bug 67371). +#ifndef FMT_USE_CONSTEXPR +# define FMT_USE_CONSTEXPR \ + (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1912 || \ + (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ + !FMT_NVCC && !FMT_ICC_VERSION +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +# define FMT_CONSTEXPR_DECL constexpr +#else +# define FMT_CONSTEXPR +# define FMT_CONSTEXPR_DECL +#endif + +#if ((__cplusplus >= 202002L) && \ + (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ + (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) +# define FMT_CONSTEXPR20 constexpr +#else +# define FMT_CONSTEXPR20 +#endif + +// Check if constexpr std::char_traits<>::compare,length is supported. +#if defined(__GLIBCXX__) +# if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \ + _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. +# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +# endif +#elif defined(_LIBCPP_VERSION) && __cplusplus >= 201703L && \ + _LIBCPP_VERSION >= 4000 +# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +#elif FMT_MSC_VER >= 1914 && _MSVC_LANG >= 201703L +# define FMT_CONSTEXPR_CHAR_TRAITS constexpr +#endif +#ifndef FMT_CONSTEXPR_CHAR_TRAITS +# define FMT_CONSTEXPR_CHAR_TRAITS +#endif + +// Check if exceptions are disabled. +#ifndef FMT_EXCEPTIONS +# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ + FMT_MSC_VER && !_HAS_EXCEPTIONS +# define FMT_EXCEPTIONS 0 +# else +# define FMT_EXCEPTIONS 1 +# endif +#endif + +// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). +#ifndef FMT_USE_NOEXCEPT +# define FMT_USE_NOEXCEPT 0 +#endif + +#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ + FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900 +# define FMT_DETECTED_NOEXCEPT noexcept +# define FMT_HAS_CXX11_NOEXCEPT 1 +#else +# define FMT_DETECTED_NOEXCEPT throw() +# define FMT_HAS_CXX11_NOEXCEPT 0 +#endif + +#ifndef FMT_NOEXCEPT +# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT +# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT +# else +# define FMT_NOEXCEPT +# endif +#endif + +// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code +// warnings. +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ + !FMT_NVCC +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + +#if __cplusplus == 201103L || __cplusplus == 201402L +# if defined(__INTEL_COMPILER) || defined(__PGI) +# define FMT_FALLTHROUGH +# elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +# elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +# else +# define FMT_FALLTHROUGH +# endif +#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) +# define FMT_FALLTHROUGH [[fallthrough]] +#else +# define FMT_FALLTHROUGH +#endif + +#ifndef FMT_NODISCARD +# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard) +# define FMT_NODISCARD [[nodiscard]] +# else +# define FMT_NODISCARD +# endif +#endif + +#ifndef FMT_USE_FLOAT +# define FMT_USE_FLOAT 1 +#endif +#ifndef FMT_USE_DOUBLE +# define FMT_USE_DOUBLE 1 +#endif +#ifndef FMT_USE_LONG_DOUBLE +# define FMT_USE_LONG_DOUBLE 1 +#endif + +#ifndef FMT_INLINE +# if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_INLINE inline __attribute__((always_inline)) +# else +# define FMT_INLINE inline +# endif +#endif + +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 +# define FMT_DEPRECATED [[deprecated]] +# else +# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VER +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif +# endif +#endif + +#ifndef FMT_BEGIN_NAMESPACE +# define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + inline namespace v8 { +# define FMT_END_NAMESPACE \ + } \ + } +#endif + +#ifndef FMT_MODULE_EXPORT +# define FMT_MODULE_EXPORT +# define FMT_MODULE_EXPORT_BEGIN +# define FMT_MODULE_EXPORT_END +# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { +# define FMT_END_DETAIL_NAMESPACE } +#endif + +#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) +# define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275) +# ifdef FMT_EXPORT +# define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# endif +#else +# define FMT_CLASS_API +# if defined(FMT_EXPORT) || defined(FMT_SHARED) +# if defined(__GNUC__) || defined(__clang__) +# define FMT_API __attribute__((visibility("default"))) +# endif +# endif +#endif +#ifndef FMT_API +# define FMT_API +#endif + +// libc++ supports string_view in pre-c++17. +#if (FMT_HAS_INCLUDE() && \ + (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ + (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +# include +# define FMT_USE_STRING_VIEW +#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L +# include +# define FMT_USE_EXPERIMENTAL_STRING_VIEW +#endif + +#ifndef FMT_UNICODE +# define FMT_UNICODE !FMT_MSC_VER +#endif + +#ifndef FMT_CONSTEVAL +# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ + __cplusplus > 201703L && !defined(__apple_build_version__)) || \ + (defined(__cpp_consteval) && \ + (!FMT_MSC_VER || _MSC_FULL_VER >= 193030704)) +// consteval is broken in MSVC before VS2022 and Apple clang 13. +# define FMT_CONSTEVAL consteval +# define FMT_HAS_CONSTEVAL +# else +# define FMT_CONSTEVAL +# endif +#endif + +#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +# if defined(__cpp_nontype_template_args) && \ + ((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \ + __cpp_nontype_template_args >= 201911L) +# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1 +# else +# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0 +# endif +#endif + +// Enable minimal optimizations for more compact code in debug mode. +FMT_GCC_PRAGMA("GCC push_options") +#ifndef __OPTIMIZE__ +FMT_GCC_PRAGMA("GCC optimize(\"Og\")") +#endif + +FMT_BEGIN_NAMESPACE +FMT_MODULE_EXPORT_BEGIN + +// Implementations of enable_if_t and other metafunctions for older systems. +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; +template using bool_constant = std::integral_constant; +template +using remove_reference_t = typename std::remove_reference::type; +template +using remove_const_t = typename std::remove_const::type; +template +using remove_cvref_t = typename std::remove_cv>::type; +template struct type_identity { using type = T; }; +template using type_identity_t = typename type_identity::type; + +struct monostate { + constexpr monostate() {} +}; + +// An enable_if helper to be used in template parameters which results in much +// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed +// to workaround a bug in MSVC 2019 (see #1140 and #1186). +#ifdef FMT_DOC +# define FMT_ENABLE_IF(...) +#else +# define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 +#endif + +FMT_BEGIN_DETAIL_NAMESPACE + +// Suppress "unused variable" warnings with the method described in +// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. +// (void)var does not work on many Intel compilers. +template FMT_CONSTEXPR void ignore_unused(const T&...) {} + +constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false) + FMT_NOEXCEPT -> bool { +#ifdef __cpp_lib_is_constant_evaluated + ignore_unused(default_value); + return std::is_constant_evaluated(); +#else + return default_value; +#endif +} + +// A function to suppress "conditional expression is constant" warnings. +template constexpr FMT_INLINE auto const_check(T value) -> T { + return value; +} + +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + +#ifndef FMT_ASSERT +# ifdef NDEBUG +// FMT_ASSERT is not empty to avoid -Werror=empty-body. +# define FMT_ASSERT(condition, message) \ + ::fmt::detail::ignore_unused((condition), (message)) +# else +# define FMT_ASSERT(condition, message) \ + ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ + ? (void)0 \ + : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message))) +# endif +#endif + +#ifdef __cpp_lib_byte +using byte = std::byte; +#else +enum class byte : unsigned char {}; +#endif + +#if defined(FMT_USE_STRING_VIEW) +template using std_string_view = std::basic_string_view; +#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) +template +using std_string_view = std::experimental::basic_string_view; +#else +template struct std_string_view {}; +#endif + +#ifdef FMT_USE_INT128 +// Do nothing. +#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \ + !(FMT_CLANG_VERSION && FMT_MSC_VER) +# define FMT_USE_INT128 1 +using int128_t = __int128_t; +using uint128_t = __uint128_t; +template inline auto convert_for_visit(T value) -> T { + return value; +} +#else +# define FMT_USE_INT128 0 +#endif +#if !FMT_USE_INT128 +enum class int128_t {}; +enum class uint128_t {}; +// Reduce template instantiations. +template inline auto convert_for_visit(T) -> monostate { + return {}; +} +#endif + +// Casts a nonnegative integer to unsigned. +template +FMT_CONSTEXPR auto to_unsigned(Int value) -> + typename std::make_unsigned::type { + FMT_ASSERT(value >= 0, "negative value"); + return static_cast::type>(value); +} + +FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5"; + +constexpr auto is_utf8() -> bool { + // Avoid buggy sign extensions in MSVC's constant evaluation mode. + // https://developercommunity.visualstudio.com/t/C-difference-in-behavior-for-unsigned/1233612 + using uchar = unsigned char; + return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 && + uchar(micro[1]) == 0xB5); +} +FMT_END_DETAIL_NAMESPACE + +/** + An implementation of ``std::basic_string_view`` for pre-C++17. It provides a + subset of the API. ``fmt::basic_string_view`` is used for format strings even + if ``std::string_view`` is available to prevent issues when a library is + compiled with a different ``-std`` option than the client code (which is not + recommended). + */ +template class basic_string_view { + private: + const Char* data_; + size_t size_; + + public: + using value_type = Char; + using iterator = const Char*; + + constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} + + /** Constructs a string reference object from a C string and a size. */ + constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT + : data_(s), + size_(count) {} + + /** + \rst + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + \endrst + */ + FMT_CONSTEXPR_CHAR_TRAITS + FMT_INLINE + basic_string_view(const Char* s) + : data_(s), + size_(detail::const_check(std::is_same::value && + !detail::is_constant_evaluated(true)) + ? std::strlen(reinterpret_cast(s)) + : std::char_traits::length(s)) {} + + /** Constructs a string reference from a ``std::basic_string`` object. */ + template + FMT_CONSTEXPR basic_string_view( + const std::basic_string& s) FMT_NOEXCEPT + : data_(s.data()), + size_(s.size()) {} + + template >::value)> + FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), + size_(s.size()) {} + + /** Returns a pointer to the string data. */ + constexpr auto data() const FMT_NOEXCEPT -> const Char* { return data_; } + + /** Returns the string size. */ + constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } + + constexpr auto begin() const FMT_NOEXCEPT -> iterator { return data_; } + constexpr auto end() const FMT_NOEXCEPT -> iterator { return data_ + size_; } + + constexpr auto operator[](size_t pos) const FMT_NOEXCEPT -> const Char& { + return data_[pos]; + } + + FMT_CONSTEXPR void remove_prefix(size_t n) FMT_NOEXCEPT { + data_ += n; + size_ -= n; + } + + // Lexicographically compare this string reference to other. + FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { + size_t str_size = size_ < other.size_ ? size_ : other.size_; + int result = std::char_traits::compare(data_, other.data_, str_size); + if (result == 0) + result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + return result; + } + + FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, + basic_string_view rhs) + -> bool { + return lhs.compare(rhs) == 0; + } + friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) != 0; + } + friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) < 0; + } + friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) <= 0; + } + friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) > 0; + } + friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) >= 0; + } +}; + +using string_view = basic_string_view; + +/** Specifies if ``T`` is a character type. Can be specialized by users. */ +template struct is_char : std::false_type {}; +template <> struct is_char : std::true_type {}; + +// Returns a string view of `s`. +template ::value)> +FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { + return s; +} +template +inline auto to_string_view(const std::basic_string& s) + -> basic_string_view { + return s; +} +template +constexpr auto to_string_view(basic_string_view s) + -> basic_string_view { + return s; +} +template >::value)> +inline auto to_string_view(detail::std_string_view s) + -> basic_string_view { + return s; +} + +// A base class for compile-time strings. It is defined in the fmt namespace to +// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42). +struct compile_string {}; + +template +struct is_compile_string : std::is_base_of {}; + +template ::value)> +constexpr auto to_string_view(const S& s) + -> basic_string_view { + return basic_string_view(s); +} + +FMT_BEGIN_DETAIL_NAMESPACE + +void to_string_view(...); +using fmt::to_string_view; + +// Specifies whether S is a string type convertible to fmt::basic_string_view. +// It should be a constexpr function but MSVC 2017 fails to compile it in +// enable_if and MSVC 2015 fails to compile it as an alias template. +template +struct is_string : std::is_class()))> { +}; + +template struct char_t_impl {}; +template struct char_t_impl::value>> { + using result = decltype(to_string_view(std::declval())); + using type = typename result::value_type; +}; + +// Reports a compile-time error if S is not a valid format string. +template ::value)> +FMT_INLINE void check_format_string(const S&) { +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert(is_compile_string::value, + "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " + "FMT_STRING."); +#endif +} +template ::value)> +void check_format_string(S); + +FMT_NORETURN FMT_API void throw_format_error(const char* message); + +struct error_handler { + constexpr error_handler() = default; + constexpr error_handler(const error_handler&) = default; + + // This function is intentionally not constexpr to give a compile-time error. + FMT_NORETURN FMT_API void on_error(const char* message); +}; +FMT_END_DETAIL_NAMESPACE + +/** String's character type. */ +template using char_t = typename detail::char_t_impl::type; + +/** + \rst + Parsing context consisting of a format string range being parsed and an + argument counter for automatic indexing. + You can use the ``format_parse_context`` type alias for ``char`` instead. + \endrst + */ +template +class basic_format_parse_context : private ErrorHandler { + private: + basic_string_view format_str_; + int next_arg_id_; + + public: + using char_type = Char; + using iterator = typename basic_string_view::iterator; + + explicit constexpr basic_format_parse_context( + basic_string_view format_str, ErrorHandler eh = {}, + int next_arg_id = 0) + : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {} + + /** + Returns an iterator to the beginning of the format string range being + parsed. + */ + constexpr auto begin() const FMT_NOEXCEPT -> iterator { + return format_str_.begin(); + } + + /** + Returns an iterator past the end of the format string range being parsed. + */ + constexpr auto end() const FMT_NOEXCEPT -> iterator { + return format_str_.end(); + } + + /** Advances the begin iterator to ``it``. */ + FMT_CONSTEXPR void advance_to(iterator it) { + format_str_.remove_prefix(detail::to_unsigned(it - begin())); + } + + /** + Reports an error if using the manual argument indexing; otherwise returns + the next argument index and switches to the automatic indexing. + */ + FMT_CONSTEXPR auto next_arg_id() -> int { + // Don't check if the argument id is valid to avoid overhead and because it + // will be checked during formatting anyway. + if (next_arg_id_ >= 0) return next_arg_id_++; + on_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + + /** + Reports an error if using the automatic argument indexing; otherwise + switches to the manual indexing. + */ + FMT_CONSTEXPR void check_arg_id(int) { + if (next_arg_id_ > 0) + on_error("cannot switch from automatic to manual argument indexing"); + else + next_arg_id_ = -1; + } + + FMT_CONSTEXPR void check_arg_id(basic_string_view) {} + + FMT_CONSTEXPR void on_error(const char* message) { + ErrorHandler::on_error(message); + } + + constexpr auto error_handler() const -> ErrorHandler { return *this; } +}; + +using format_parse_context = basic_format_parse_context; + +template class basic_format_arg; +template class basic_format_args; +template class dynamic_format_arg_store; + +// A formatter for objects of type T. +template +struct formatter { + // A deleted default constructor indicates a disabled formatter. + formatter() = delete; +}; + +// Specifies if T has an enabled formatter specialization. A type can be +// formattable even if it doesn't have a formatter e.g. via a conversion. +template +using has_formatter = + std::is_constructible>; + +// Checks whether T is a container with contiguous storage. +template struct is_contiguous : std::false_type {}; +template +struct is_contiguous> : std::true_type {}; + +class appender; + +FMT_BEGIN_DETAIL_NAMESPACE + +template +constexpr auto has_const_formatter_impl(T*) + -> decltype(typename Context::template formatter_type().format( + std::declval(), std::declval()), + true) { + return true; +} +template +constexpr auto has_const_formatter_impl(...) -> bool { + return false; +} +template +constexpr auto has_const_formatter() -> bool { + return has_const_formatter_impl(static_cast(nullptr)); +} + +// Extracts a reference to the container from back_insert_iterator. +template +inline auto get_container(std::back_insert_iterator it) + -> Container& { + using bi_iterator = std::back_insert_iterator; + struct accessor : bi_iterator { + accessor(bi_iterator iter) : bi_iterator(iter) {} + using bi_iterator::container; + }; + return *accessor(it).container; +} + +template +FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + while (begin != end) *out++ = static_cast(*begin++); + return out; +} + +template , U>::value&& is_char::value)> +FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* { + if (is_constant_evaluated()) return copy_str(begin, end, out); + auto size = to_unsigned(end - begin); + memcpy(out, begin, size * sizeof(U)); + return out + size; +} + +/** + \rst + A contiguous memory buffer with an optional growing ability. It is an internal + class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`. + \endrst + */ +template class buffer { + private: + T* ptr_; + size_t size_; + size_t capacity_; + + protected: + // Don't initialize ptr_ since it is not accessed to save a few cycles. + FMT_MSC_WARNING(suppress : 26495) + buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} + + FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, + size_t cap = 0) FMT_NOEXCEPT : ptr_(p), + size_(sz), + capacity_(cap) {} + + FMT_CONSTEXPR20 ~buffer() = default; + buffer(buffer&&) = default; + + /** Sets the buffer data and capacity. */ + FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { + ptr_ = buf_data; + capacity_ = buf_capacity; + } + + /** Increases the buffer capacity to hold at least *capacity* elements. */ + virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0; + + public: + using value_type = T; + using const_reference = const T&; + + buffer(const buffer&) = delete; + void operator=(const buffer&) = delete; + + auto begin() FMT_NOEXCEPT -> T* { return ptr_; } + auto end() FMT_NOEXCEPT -> T* { return ptr_ + size_; } + + auto begin() const FMT_NOEXCEPT -> const T* { return ptr_; } + auto end() const FMT_NOEXCEPT -> const T* { return ptr_ + size_; } + + /** Returns the size of this buffer. */ + constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } + + /** Returns the capacity of this buffer. */ + constexpr auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; } + + /** Returns a pointer to the buffer data. */ + FMT_CONSTEXPR auto data() FMT_NOEXCEPT -> T* { return ptr_; } + + /** Returns a pointer to the buffer data. */ + FMT_CONSTEXPR auto data() const FMT_NOEXCEPT -> const T* { return ptr_; } + + /** Clears this buffer. */ + void clear() { size_ = 0; } + + // Tries resizing the buffer to contain *count* elements. If T is a POD type + // the new elements may not be initialized. + FMT_CONSTEXPR20 void try_resize(size_t count) { + try_reserve(count); + size_ = count <= capacity_ ? count : capacity_; + } + + // Tries increasing the buffer capacity to *new_capacity*. It can increase the + // capacity by a smaller amount than requested but guarantees there is space + // for at least one additional element either by increasing the capacity or by + // flushing the buffer if it is full. + FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) { + if (new_capacity > capacity_) grow(new_capacity); + } + + FMT_CONSTEXPR20 void push_back(const T& value) { + try_reserve(size_ + 1); + ptr_[size_++] = value; + } + + /** Appends data to the end of the buffer. */ + template void append(const U* begin, const U* end); + + template FMT_CONSTEXPR auto operator[](I index) -> T& { + return ptr_[index]; + } + template + FMT_CONSTEXPR auto operator[](I index) const -> const T& { + return ptr_[index]; + } +}; + +struct buffer_traits { + explicit buffer_traits(size_t) {} + auto count() const -> size_t { return 0; } + auto limit(size_t size) -> size_t { return size; } +}; + +class fixed_buffer_traits { + private: + size_t count_ = 0; + size_t limit_; + + public: + explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} + auto count() const -> size_t { return count_; } + auto limit(size_t size) -> size_t { + size_t n = limit_ > count_ ? limit_ - count_ : 0; + count_ += size; + return size < n ? size : n; + } +}; + +// A buffer that writes to an output iterator when flushed. +template +class iterator_buffer final : public Traits, public buffer { + private: + OutputIt out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + protected: + FMT_CONSTEXPR20 void grow(size_t) override { + if (this->size() == buffer_size) flush(); + } + + void flush() { + auto size = this->size(); + this->clear(); + out_ = copy_str(data_, data_ + this->limit(size), out_); + } + + public: + explicit iterator_buffer(OutputIt out, size_t n = buffer_size) + : Traits(n), buffer(data_, 0, buffer_size), out_(out) {} + iterator_buffer(iterator_buffer&& other) + : Traits(other), buffer(data_, 0, buffer_size), out_(other.out_) {} + ~iterator_buffer() { flush(); } + + auto out() -> OutputIt { + flush(); + return out_; + } + auto count() const -> size_t { return Traits::count() + this->size(); } +}; + +template +class iterator_buffer final + : public fixed_buffer_traits, + public buffer { + private: + T* out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + protected: + FMT_CONSTEXPR20 void grow(size_t) override { + if (this->size() == this->capacity()) flush(); + } + + void flush() { + size_t n = this->limit(this->size()); + if (this->data() == out_) { + out_ += n; + this->set(data_, buffer_size); + } + this->clear(); + } + + public: + explicit iterator_buffer(T* out, size_t n = buffer_size) + : fixed_buffer_traits(n), buffer(out, 0, n), out_(out) {} + iterator_buffer(iterator_buffer&& other) + : fixed_buffer_traits(other), + buffer(std::move(other)), + out_(other.out_) { + if (this->data() != out_) { + this->set(data_, buffer_size); + this->clear(); + } + } + ~iterator_buffer() { flush(); } + + auto out() -> T* { + flush(); + return out_; + } + auto count() const -> size_t { + return fixed_buffer_traits::count() + this->size(); + } +}; + +template class iterator_buffer final : public buffer { + protected: + FMT_CONSTEXPR20 void grow(size_t) override {} + + public: + explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} + + auto out() -> T* { return &*this->end(); } +}; + +// A buffer that writes to a container with the contiguous storage. +template +class iterator_buffer, + enable_if_t::value, + typename Container::value_type>> + final : public buffer { + private: + Container& container_; + + protected: + FMT_CONSTEXPR20 void grow(size_t capacity) override { + container_.resize(capacity); + this->set(&container_[0], capacity); + } + + public: + explicit iterator_buffer(Container& c) + : buffer(c.size()), container_(c) {} + explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) + : iterator_buffer(get_container(out)) {} + auto out() -> std::back_insert_iterator { + return std::back_inserter(container_); + } +}; + +// A buffer that counts the number of code units written discarding the output. +template class counting_buffer final : public buffer { + private: + enum { buffer_size = 256 }; + T data_[buffer_size]; + size_t count_ = 0; + + protected: + FMT_CONSTEXPR20 void grow(size_t) override { + if (this->size() != buffer_size) return; + count_ += this->size(); + this->clear(); + } + + public: + counting_buffer() : buffer(data_, 0, buffer_size) {} + + auto count() -> size_t { return count_ + this->size(); } +}; + +template +using buffer_appender = conditional_t::value, appender, + std::back_insert_iterator>>; + +// Maps an output iterator to a buffer. +template +auto get_buffer(OutputIt out) -> iterator_buffer { + return iterator_buffer(out); +} + +template +auto get_iterator(Buffer& buf) -> decltype(buf.out()) { + return buf.out(); +} +template auto get_iterator(buffer& buf) -> buffer_appender { + return buffer_appender(buf); +} + +template +struct fallback_formatter { + fallback_formatter() = delete; +}; + +// Specifies if T has an enabled fallback_formatter specialization. +template +using has_fallback_formatter = + std::is_constructible>; + +struct view {}; + +template struct named_arg : view { + const Char* name; + const T& value; + named_arg(const Char* n, const T& v) : name(n), value(v) {} +}; + +template struct named_arg_info { + const Char* name; + int id; +}; + +template +struct arg_data { + // args_[0].named_args points to named_args_ to avoid bloating format_args. + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)]; + named_arg_info named_args_[NUM_NAMED_ARGS]; + + template + arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {} + arg_data(const arg_data& other) = delete; + auto args() const -> const T* { return args_ + 1; } + auto named_args() -> named_arg_info* { return named_args_; } +}; + +template +struct arg_data { + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + T args_[NUM_ARGS != 0 ? NUM_ARGS : +1]; + + template + FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {} + FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; } + FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { + return nullptr; + } +}; + +template +inline void init_named_args(named_arg_info*, int, int) {} + +template struct is_named_arg : std::false_type {}; +template struct is_statically_named_arg : std::false_type {}; + +template +struct is_named_arg> : std::true_type {}; + +template ::value)> +void init_named_args(named_arg_info* named_args, int arg_count, + int named_arg_count, const T&, const Tail&... args) { + init_named_args(named_args, arg_count + 1, named_arg_count, args...); +} + +template ::value)> +void init_named_args(named_arg_info* named_args, int arg_count, + int named_arg_count, const T& arg, const Tail&... args) { + named_args[named_arg_count++] = {arg.name, arg_count}; + init_named_args(named_args, arg_count + 1, named_arg_count, args...); +} + +template +FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, + const Args&...) {} + +template constexpr auto count() -> size_t { return B ? 1 : 0; } +template constexpr auto count() -> size_t { + return (B1 ? 1 : 0) + count(); +} + +template constexpr auto count_named_args() -> size_t { + return count::value...>(); +} + +template +constexpr auto count_statically_named_args() -> size_t { + return count::value...>(); +} + +enum class type { + none_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_t, int128_type); +FMT_TYPE_CONSTANT(uint128_t, uint128_type); +FMT_TYPE_CONSTANT(bool, bool_type); +FMT_TYPE_CONSTANT(Char, char_type); +FMT_TYPE_CONSTANT(float, float_type); +FMT_TYPE_CONSTANT(double, double_type); +FMT_TYPE_CONSTANT(long double, long_double_type); +FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_string_view, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +constexpr bool is_integral_type(type t) { + return t > type::none_type && t <= type::last_integer_type; +} + +constexpr bool is_arithmetic_type(type t) { + return t > type::none_type && t <= type::last_numeric_type; +} + +struct unformattable {}; +struct unformattable_char : unformattable {}; +struct unformattable_const : unformattable {}; +struct unformattable_pointer : unformattable {}; + +template struct string_value { + const Char* data; + size_t size; +}; + +template struct named_arg_value { + const named_arg_info* data; + size_t size; +}; + +template struct custom_value { + using parse_context = typename Context::parse_context_type; + void* value; + void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); +}; + +// A formatting argument value. +template class value { + public: + using char_type = typename Context::char_type; + + union { + monostate no_value; + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + int128_t int128_value; + uint128_t uint128_value; + bool bool_value; + char_type char_value; + float float_value; + double double_value; + long double long_double_value; + const void* pointer; + string_value string; + custom_value custom; + named_arg_value named_args; + }; + + constexpr FMT_INLINE value() : no_value() {} + constexpr FMT_INLINE value(int val) : int_value(val) {} + constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} + constexpr FMT_INLINE value(long long val) : long_long_value(val) {} + constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} + FMT_INLINE value(int128_t val) : int128_value(val) {} + FMT_INLINE value(uint128_t val) : uint128_value(val) {} + constexpr FMT_INLINE value(float val) : float_value(val) {} + constexpr FMT_INLINE value(double val) : double_value(val) {} + FMT_INLINE value(long double val) : long_double_value(val) {} + constexpr FMT_INLINE value(bool val) : bool_value(val) {} + constexpr FMT_INLINE value(char_type val) : char_value(val) {} + FMT_CONSTEXPR FMT_INLINE value(const char_type* val) { + string.data = val; + if (is_constant_evaluated()) string.size = {}; + } + FMT_CONSTEXPR FMT_INLINE value(basic_string_view val) { + string.data = val.data(); + string.size = val.size(); + } + FMT_INLINE value(const void* val) : pointer(val) {} + FMT_INLINE value(const named_arg_info* args, size_t size) + : named_args{args, size} {} + + template FMT_CONSTEXPR FMT_INLINE value(T& val) { + using value_type = remove_cvref_t; + custom.value = const_cast(&val); + // Get the formatter type through the context to allow different contexts + // have different extension points, e.g. `formatter` for `format` and + // `printf_formatter` for `printf`. + custom.format = format_custom_arg< + value_type, + conditional_t::value, + typename Context::template formatter_type, + fallback_formatter>>; + } + value(unformattable); + value(unformattable_char); + value(unformattable_const); + value(unformattable_pointer); + + private: + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom_arg(void* arg, + typename Context::parse_context_type& parse_ctx, + Context& ctx) { + auto f = Formatter(); + parse_ctx.advance_to(f.parse(parse_ctx)); + using qualified_type = + conditional_t(), const T, T>; + ctx.advance_to(f.format(*static_cast(arg), ctx)); + } +}; + +template +FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg; + +// To minimize the number of types we need to deal with, long is translated +// either to int or to long long depending on its size. +enum { long_short = sizeof(long) == sizeof(int) }; +using long_type = conditional_t; +using ulong_type = conditional_t; + +// Maps formatting arguments to core types. +// arg_mapper reports errors by returning unformattable instead of using +// static_assert because it's used in the is_formattable trait. +template struct arg_mapper { + using char_type = typename Context::char_type; + + FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) + -> unsigned long long { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(int128_t val) -> int128_t { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } + + template ::value || + std::is_same::value)> + FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type { + return val; + } + template ::value || +#ifdef __cpp_char8_t + std::is_same::value || +#endif + std::is_same::value || + std::is_same::value) && + !std::is_same::value, + int> = 0> + FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char { + return {}; + } + + FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double { + return val; + } + + FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* { + return val; + } + template ::value && !std::is_pointer::value && + std::is_same>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view { + return to_string_view(val); + } + template ::value && !std::is_pointer::value && + !std::is_same>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char { + return {}; + } + template , T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view { + return basic_string_view(val); + } + template < + typename T, + FMT_ENABLE_IF( + std::is_constructible, T>::value && + !std::is_constructible, T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> basic_string_view { + return std_string_view(val); + } + + using cstring_result = conditional_t::value, + const char*, unformattable_pointer>; + + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val) + -> cstring_result { + return map(reinterpret_cast(val)); + } + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val) + -> cstring_result { + return map(reinterpret_cast(val)); + } + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(signed char* val) + -> cstring_result { + return map(reinterpret_cast(val)); + } + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val) + -> cstring_result { + return map(reinterpret_cast(val)); + } + + FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } + FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { + return val; + } + FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* { + return val; + } + + // We use SFINAE instead of a const T* parameter to avoid conflicting with + // the C array overload. + template < + typename T, + FMT_ENABLE_IF( + std::is_member_pointer::value || + std::is_function::type>::value || + (std::is_convertible::value && + !std::is_convertible::value))> + FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { + return {}; + } + + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] { + return values; + } + + template ::value&& std::is_convertible::value && + !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> decltype(std::declval().map( + static_cast::type>(val))) { + return map(static_cast::type>(val)); + } + + FMT_CONSTEXPR FMT_INLINE auto map(detail::byte val) -> unsigned { + return map(static_cast(val)); + } + + template > + struct formattable + : bool_constant() || + !std::is_const>::value || + has_fallback_formatter::value> {}; + +#if FMT_MSC_VER != 0 && FMT_MSC_VER < 1910 + // Workaround a bug in MSVC. + template FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { + return val; + } +#else + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { + return val; + } + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const { + return {}; + } +#endif + + template , + FMT_ENABLE_IF(!is_string::value && !is_char::value && + !std::is_array::value && + (has_formatter::value || + has_fallback_formatter::value))> + FMT_CONSTEXPR FMT_INLINE auto map(T&& val) + -> decltype(this->do_map(std::forward(val))) { + return do_map(std::forward(val)); + } + + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) + -> decltype(std::declval().map(named_arg.value)) { + return map(named_arg.value); + } + + auto map(...) -> unformattable { return {}; } +}; + +// A type constant after applying arg_mapper. +template +using mapped_type_constant = + type_constant().map(std::declval())), + typename Context::char_type>; + +enum { packed_arg_bits = 4 }; +// Maximum number of arguments with packed types. +enum { max_packed_args = 62 / packed_arg_bits }; +enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; +enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; + +FMT_END_DETAIL_NAMESPACE + +// An output iterator that appends to a buffer. +// It is used to reduce symbol sizes for the common case. +class appender : public std::back_insert_iterator> { + using base = std::back_insert_iterator>; + + template + friend auto get_buffer(appender out) -> detail::buffer& { + return detail::get_container(out); + } + + public: + using std::back_insert_iterator>::back_insert_iterator; + appender(base it) FMT_NOEXCEPT : base(it) {} + using _Unchecked_type = appender; // Mark iterator as checked. + + auto operator++() FMT_NOEXCEPT -> appender& { return *this; } + + auto operator++(int) FMT_NOEXCEPT -> appender { return *this; } +}; + +// A formatting argument. It is a trivially copyable/constructible type to +// allow storage in basic_memory_buffer. +template class basic_format_arg { + private: + detail::value value_; + detail::type type_; + + template + friend FMT_CONSTEXPR auto detail::make_arg(const T& value) + -> basic_format_arg; + + template + friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, + const basic_format_arg& arg) + -> decltype(vis(0)); + + friend class basic_format_args; + friend class dynamic_format_arg_store; + + using char_type = typename Context::char_type; + + template + friend struct detail::arg_data; + + basic_format_arg(const detail::named_arg_info* args, size_t size) + : value_(args, size) {} + + public: + class handle { + public: + explicit handle(detail::custom_value custom) : custom_(custom) {} + + void format(typename Context::parse_context_type& parse_ctx, + Context& ctx) const { + custom_.format(custom_.value, parse_ctx, ctx); + } + + private: + detail::custom_value custom_; + }; + + constexpr basic_format_arg() : type_(detail::type::none_type) {} + + constexpr explicit operator bool() const FMT_NOEXCEPT { + return type_ != detail::type::none_type; + } + + auto type() const -> detail::type { return type_; } + + auto is_integral() const -> bool { return detail::is_integral_type(type_); } + auto is_arithmetic() const -> bool { + return detail::is_arithmetic_type(type_); + } +}; + +/** + \rst + Visits an argument dispatching to the appropriate visit method based on + the argument type. For example, if the argument type is ``double`` then + ``vis(value)`` will be called with the value of type ``double``. + \endrst + */ +template +FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( + Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { + switch (arg.type_) { + case detail::type::none_type: + break; + case detail::type::int_type: + return vis(arg.value_.int_value); + case detail::type::uint_type: + return vis(arg.value_.uint_value); + case detail::type::long_long_type: + return vis(arg.value_.long_long_value); + case detail::type::ulong_long_type: + return vis(arg.value_.ulong_long_value); + case detail::type::int128_type: + return vis(detail::convert_for_visit(arg.value_.int128_value)); + case detail::type::uint128_type: + return vis(detail::convert_for_visit(arg.value_.uint128_value)); + case detail::type::bool_type: + return vis(arg.value_.bool_value); + case detail::type::char_type: + return vis(arg.value_.char_value); + case detail::type::float_type: + return vis(arg.value_.float_value); + case detail::type::double_type: + return vis(arg.value_.double_value); + case detail::type::long_double_type: + return vis(arg.value_.long_double_value); + case detail::type::cstring_type: + return vis(arg.value_.string.data); + case detail::type::string_type: + using sv = basic_string_view; + return vis(sv(arg.value_.string.data, arg.value_.string.size)); + case detail::type::pointer_type: + return vis(arg.value_.pointer); + case detail::type::custom_type: + return vis(typename basic_format_arg::handle(arg.value_.custom)); + } + return vis(monostate()); +} + +FMT_BEGIN_DETAIL_NAMESPACE + +template +auto copy_str(InputIt begin, InputIt end, appender out) -> appender { + get_container(out).append(begin, end); + return out; +} + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 +// A workaround for gcc 4.8 to make void_t work in a SFINAE context. +template struct void_t_impl { using type = void; }; +template +using void_t = typename detail::void_t_impl::type; +#else +template using void_t = void; +#endif + +template +struct is_output_iterator : std::false_type {}; + +template +struct is_output_iterator< + It, T, + void_t::iterator_category, + decltype(*std::declval() = std::declval())>> + : std::true_type {}; + +template +struct is_back_insert_iterator : std::false_type {}; +template +struct is_back_insert_iterator> + : std::true_type {}; + +template +struct is_contiguous_back_insert_iterator : std::false_type {}; +template +struct is_contiguous_back_insert_iterator> + : is_contiguous {}; +template <> +struct is_contiguous_back_insert_iterator : std::true_type {}; + +// A type-erased reference to an std::locale to avoid heavy include. +class locale_ref { + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + constexpr locale_ref() : locale_(nullptr) {} + template explicit locale_ref(const Locale& loc); + + explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } + + template auto get() const -> Locale; +}; + +template constexpr auto encode_types() -> unsigned long long { + return 0; +} + +template +constexpr auto encode_types() -> unsigned long long { + return static_cast(mapped_type_constant::value) | + (encode_types() << packed_arg_bits); +} + +template +FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg { + basic_format_arg arg; + arg.type_ = mapped_type_constant::value; + arg.value_ = arg_mapper().map(value); + return arg; +} + +// The type template parameter is there to avoid an ODR violation when using +// a fallback formatter in one translation unit and an implicit conversion in +// another (not recommended). +template +FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { + const auto& arg = arg_mapper().map(std::forward(val)); + + constexpr bool formattable_char = + !std::is_same::value; + static_assert(formattable_char, "Mixing character types is disallowed."); + + constexpr bool formattable_const = + !std::is_same::value; + static_assert(formattable_const, "Cannot format a const argument."); + + // Formatting of arbitrary pointers is disallowed. If you want to output + // a pointer cast it to "void *" or "const void *". In particular, this + // forbids formatting of "[const] volatile char *" which is printed as bool + // by iostreams. + constexpr bool formattable_pointer = + !std::is_same::value; + static_assert(formattable_pointer, + "Formatting of non-void pointers is disallowed."); + + constexpr bool formattable = + !std::is_same::value; + static_assert( + formattable, + "Cannot format an argument. To make type T formattable provide a " + "formatter specialization: https://fmt.dev/latest/api.html#udt"); + return {arg}; +} + +template +inline auto make_arg(const T& value) -> basic_format_arg { + return make_arg(value); +} +FMT_END_DETAIL_NAMESPACE + +// Formatting context. +template class basic_format_context { + public: + /** The character type for the output. */ + using char_type = Char; + + private: + OutputIt out_; + basic_format_args args_; + detail::locale_ref loc_; + + public: + using iterator = OutputIt; + using format_arg = basic_format_arg; + using parse_context_type = basic_format_parse_context; + template using formatter_type = formatter; + + basic_format_context(basic_format_context&&) = default; + basic_format_context(const basic_format_context&) = delete; + void operator=(const basic_format_context&) = delete; + /** + Constructs a ``basic_format_context`` object. References to the arguments are + stored in the object so make sure they have appropriate lifetimes. + */ + constexpr basic_format_context( + OutputIt out, basic_format_args ctx_args, + detail::locale_ref loc = detail::locale_ref()) + : out_(out), args_(ctx_args), loc_(loc) {} + + constexpr auto arg(int id) const -> format_arg { return args_.get(id); } + FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { + return args_.get(name); + } + FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { + return args_.get_id(name); + } + auto args() const -> const basic_format_args& { + return args_; + } + + FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } + void on_error(const char* message) { error_handler().on_error(message); } + + // Returns an iterator to the beginning of the output range. + FMT_CONSTEXPR auto out() -> iterator { return out_; } + + // Advances the begin iterator to ``it``. + void advance_to(iterator it) { + if (!detail::is_back_insert_iterator()) out_ = it; + } + + FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } +}; + +template +using buffer_context = + basic_format_context, Char>; +using format_context = buffer_context; + +// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164. +#define FMT_BUFFER_CONTEXT(Char) \ + basic_format_context, Char> + +template +using is_formattable = bool_constant< + !std::is_base_of>().map( + std::declval()))>::value && + !detail::has_fallback_formatter::value>; + +/** + \rst + An array of references to arguments. It can be implicitly converted into + `~fmt::basic_format_args` for passing into type-erased formatting functions + such as `~fmt::vformat`. + \endrst + */ +template +class format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + static const size_t num_args = sizeof...(Args); + static const size_t num_named_args = detail::count_named_args(); + static const bool is_packed = num_args <= detail::max_packed_args; + + using value_type = conditional_t, + basic_format_arg>; + + detail::arg_data + data_; + + friend class basic_format_args; + + static constexpr unsigned long long desc = + (is_packed ? detail::encode_types() + : detail::is_unpacked_bit | num_args) | + (num_named_args != 0 + ? static_cast(detail::has_named_args_bit) + : 0); + + public: + template + FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args) + : +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + basic_format_args(*this), +#endif + data_{detail::make_arg< + is_packed, Context, + detail::mapped_type_constant, Context>::value>( + std::forward(args))...} { + detail::init_named_args(data_.named_args(), 0, 0, args...); + } +}; + +/** + \rst + Constructs a `~fmt::format_arg_store` object that contains references to + arguments and can be implicitly converted to `~fmt::format_args`. `Context` + can be omitted in which case it defaults to `~fmt::context`. + See `~fmt::arg` for lifetime considerations. + \endrst + */ +template +constexpr auto make_format_args(Args&&... args) + -> format_arg_store...> { + return {std::forward(args)...}; +} + +/** + \rst + Returns a named argument to be used in a formatting function. + It should only be used in a call to a formatting function or + `dynamic_format_arg_store::push_back`. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); + \endrst + */ +template +inline auto arg(const Char* name, const T& arg) -> detail::named_arg { + static_assert(!detail::is_named_arg(), "nested named arguments"); + return {name, arg}; +} + +/** + \rst + A view of a collection of formatting arguments. To avoid lifetime issues it + should only be used as a parameter type in type-erased functions such as + ``vformat``:: + + void vlog(string_view format_str, format_args args); // OK + format_args args = make_format_args(42); // Error: dangling reference + \endrst + */ +template class basic_format_args { + public: + using size_type = int; + using format_arg = basic_format_arg; + + private: + // A descriptor that contains information about formatting arguments. + // If the number of arguments is less or equal to max_packed_args then + // argument types are passed in the descriptor. This reduces binary code size + // per formatting function call. + unsigned long long desc_; + union { + // If is_packed() returns true then argument values are stored in values_; + // otherwise they are stored in args_. This is done to improve cache + // locality and reduce compiled code size since storing larger objects + // may require more code (at least on x86-64) even if the same amount of + // data is actually copied to stack. It saves ~10% on the bloat test. + const detail::value* values_; + const format_arg* args_; + }; + + constexpr auto is_packed() const -> bool { + return (desc_ & detail::is_unpacked_bit) == 0; + } + auto has_named_args() const -> bool { + return (desc_ & detail::has_named_args_bit) != 0; + } + + FMT_CONSTEXPR auto type(int index) const -> detail::type { + int shift = index * detail::packed_arg_bits; + unsigned int mask = (1 << detail::packed_arg_bits) - 1; + return static_cast((desc_ >> shift) & mask); + } + + constexpr FMT_INLINE basic_format_args(unsigned long long desc, + const detail::value* values) + : desc_(desc), values_(values) {} + constexpr basic_format_args(unsigned long long desc, const format_arg* args) + : desc_(desc), args_(args) {} + + public: + constexpr basic_format_args() : desc_(0), args_(nullptr) {} + + /** + \rst + Constructs a `basic_format_args` object from `~fmt::format_arg_store`. + \endrst + */ + template + constexpr FMT_INLINE basic_format_args( + const format_arg_store& store) + : basic_format_args(format_arg_store::desc, + store.data_.args()) {} + + /** + \rst + Constructs a `basic_format_args` object from + `~fmt::dynamic_format_arg_store`. + \endrst + */ + constexpr FMT_INLINE basic_format_args( + const dynamic_format_arg_store& store) + : basic_format_args(store.get_types(), store.data()) {} + + /** + \rst + Constructs a `basic_format_args` object from a dynamic set of arguments. + \endrst + */ + constexpr basic_format_args(const format_arg* args, int count) + : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), + args) {} + + /** Returns the argument with the specified id. */ + FMT_CONSTEXPR auto get(int id) const -> format_arg { + format_arg arg; + if (!is_packed()) { + if (id < max_size()) arg = args_[id]; + return arg; + } + if (id >= detail::max_packed_args) return arg; + arg.type_ = type(id); + if (arg.type_ == detail::type::none_type) return arg; + arg.value_ = values_[id]; + return arg; + } + + template + auto get(basic_string_view name) const -> format_arg { + int id = get_id(name); + return id >= 0 ? get(id) : format_arg(); + } + + template + auto get_id(basic_string_view name) const -> int { + if (!has_named_args()) return -1; + const auto& named_args = + (is_packed() ? values_[-1] : args_[-1].value_).named_args; + for (size_t i = 0; i < named_args.size; ++i) { + if (named_args.data[i].name == name) return named_args.data[i].id; + } + return -1; + } + + auto max_size() const -> int { + unsigned long long max_packed = detail::max_packed_args; + return static_cast(is_packed() ? max_packed + : desc_ & ~detail::is_unpacked_bit); + } +}; + +/** An alias to ``basic_format_args``. */ +// A separate type would result in shorter symbols but break ABI compatibility +// between clang and gcc on ARM (#1919). +using format_args = basic_format_args; + +// We cannot use enum classes as bit fields because of a gcc bug +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. +namespace align { +enum type { none, left, right, center, numeric }; +} +using align_t = align::type; +namespace sign { +enum type { none, minus, plus, space }; +} +using sign_t = sign::type; + +FMT_BEGIN_DETAIL_NAMESPACE + +// Workaround an array initialization issue in gcc 4.8. +template struct fill_t { + private: + enum { max_size = 4 }; + Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; + unsigned char size_ = 1; + + public: + FMT_CONSTEXPR void operator=(basic_string_view s) { + auto size = s.size(); + if (size > max_size) return throw_format_error("invalid fill"); + for (size_t i = 0; i < size; ++i) data_[i] = s[i]; + size_ = static_cast(size); + } + + constexpr auto size() const -> size_t { return size_; } + constexpr auto data() const -> const Char* { return data_; } + + FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; } + FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& { + return data_[index]; + } +}; +FMT_END_DETAIL_NAMESPACE + +enum class presentation_type : unsigned char { + none, + // Integer types should go first, + dec, // 'd' + oct, // 'o' + hex_lower, // 'x' + hex_upper, // 'X' + bin_lower, // 'b' + bin_upper, // 'B' + hexfloat_lower, // 'a' + hexfloat_upper, // 'A' + exp_lower, // 'e' + exp_upper, // 'E' + fixed_lower, // 'f' + fixed_upper, // 'F' + general_lower, // 'g' + general_upper, // 'G' + chr, // 'c' + string, // 's' + pointer // 'p' +}; + +// Format specifiers for built-in and string types. +template struct basic_format_specs { + int width; + int precision; + presentation_type type; + align_t align : 4; + sign_t sign : 3; + bool alt : 1; // Alternate form ('#'). + bool localized : 1; + detail::fill_t fill; + + constexpr basic_format_specs() + : width(0), + precision(-1), + type(presentation_type::none), + align(align::none), + sign(sign::none), + alt(false), + localized(false) {} +}; + +using format_specs = basic_format_specs; + +FMT_BEGIN_DETAIL_NAMESPACE + +enum class arg_id_kind { none, index, name }; + +// An argument reference. +template struct arg_ref { + FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {} + + FMT_CONSTEXPR explicit arg_ref(int index) + : kind(arg_id_kind::index), val(index) {} + FMT_CONSTEXPR explicit arg_ref(basic_string_view name) + : kind(arg_id_kind::name), val(name) {} + + FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& { + kind = arg_id_kind::index; + val.index = idx; + return *this; + } + + arg_id_kind kind; + union value { + FMT_CONSTEXPR value(int id = 0) : index{id} {} + FMT_CONSTEXPR value(basic_string_view n) : name(n) {} + + int index; + basic_string_view name; + } val; +}; + +// Format specifiers with width and precision resolved at formatting rather +// than parsing time to allow re-using the same parsed specifiers with +// different sets of arguments (precompilation of format strings). +template +struct dynamic_format_specs : basic_format_specs { + arg_ref width_ref; + arg_ref precision_ref; +}; + +struct auto_id {}; + +// A format specifier handler that sets fields in basic_format_specs. +template class specs_setter { + protected: + basic_format_specs& specs_; + + public: + explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) + : specs_(specs) {} + + FMT_CONSTEXPR specs_setter(const specs_setter& other) + : specs_(other.specs_) {} + + FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } + FMT_CONSTEXPR void on_fill(basic_string_view fill) { + specs_.fill = fill; + } + FMT_CONSTEXPR void on_sign(sign_t s) { specs_.sign = s; } + FMT_CONSTEXPR void on_hash() { specs_.alt = true; } + FMT_CONSTEXPR void on_localized() { specs_.localized = true; } + + FMT_CONSTEXPR void on_zero() { + if (specs_.align == align::none) specs_.align = align::numeric; + specs_.fill[0] = Char('0'); + } + + FMT_CONSTEXPR void on_width(int width) { specs_.width = width; } + FMT_CONSTEXPR void on_precision(int precision) { + specs_.precision = precision; + } + FMT_CONSTEXPR void end_precision() {} + + FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; } +}; + +// Format spec handler that saves references to arguments representing dynamic +// width and precision to be resolved at formatting time. +template +class dynamic_specs_handler + : public specs_setter { + public: + using char_type = typename ParseContext::char_type; + + FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs& specs, + ParseContext& ctx) + : specs_setter(specs), specs_(specs), context_(ctx) {} + + FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other) + : specs_setter(other), + specs_(other.specs_), + context_(other.context_) {} + + template FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { + specs_.width_ref = make_arg_ref(arg_id); + } + + template FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { + specs_.precision_ref = make_arg_ref(arg_id); + } + + FMT_CONSTEXPR void on_error(const char* message) { + context_.on_error(message); + } + + private: + dynamic_format_specs& specs_; + ParseContext& context_; + + using arg_ref_type = arg_ref; + + FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type { + context_.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type { + return arg_ref_type(context_.next_arg_id()); + } + + FMT_CONSTEXPR auto make_arg_ref(basic_string_view arg_id) + -> arg_ref_type { + context_.check_arg_id(arg_id); + basic_string_view format_str( + context_.begin(), to_unsigned(context_.end() - context_.begin())); + return arg_ref_type(arg_id); + } +}; + +template constexpr bool is_ascii_letter(Char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +// Converts a character to ASCII. Returns a number > 127 on conversion failure. +template ::value)> +constexpr auto to_ascii(Char value) -> Char { + return value; +} +template ::value)> +constexpr auto to_ascii(Char value) -> + typename std::underlying_type::type { + return value; +} + +template +FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { + if (const_check(sizeof(Char) != 1)) return 1; + auto lengths = + "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"; + int len = lengths[static_cast(*begin) >> 3]; + + // Compute the pointer to the next character early so that the next + // iteration can start working on the next character. Neither Clang + // nor GCC figure out this reordering on their own. + return len + !len; +} + +// Return the result via the out param to workaround gcc bug 77539. +template +FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { + for (out = first; out != last; ++out) { + if (*out == value) return true; + } + return false; +} + +template <> +inline auto find(const char* first, const char* last, char value, + const char*& out) -> bool { + out = static_cast( + std::memchr(first, value, to_unsigned(last - first))); + return out != nullptr; +} + +// Parses the range [begin, end) as an unsigned integer. This function assumes +// that the range is non-empty and the first character is a digit. +template +FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, + int error_value) noexcept -> int { + FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); + unsigned value = 0, prev = 0; + auto p = begin; + do { + prev = value; + value = value * 10 + unsigned(*p - '0'); + ++p; + } while (p != end && '0' <= *p && *p <= '9'); + auto num_digits = p - begin; + begin = p; + if (num_digits <= std::numeric_limits::digits10) + return static_cast(value); + // Check for overflow. + const unsigned max = to_unsigned((std::numeric_limits::max)()); + return num_digits == std::numeric_limits::digits10 + 1 && + prev * 10ull + unsigned(p[-1] - '0') <= max + ? static_cast(value) + : error_value; +} + +// Parses fill and alignment. +template +FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + FMT_ASSERT(begin != end, ""); + auto align = align::none; + auto p = begin + code_point_length(begin); + if (p >= end) p = begin; + for (;;) { + switch (to_ascii(*p)) { + case '<': + align = align::left; + break; + case '>': + align = align::right; + break; + case '^': + align = align::center; + break; + default: + break; + } + if (align != align::none) { + if (p != begin) { + auto c = *begin; + if (c == '{') + return handler.on_error("invalid fill character '{'"), begin; + handler.on_fill(basic_string_view(begin, to_unsigned(p - begin))); + begin = p + 1; + } else + ++begin; + handler.on_align(align); + break; + } else if (p == begin) { + break; + } + p = begin; + } + return begin; +} + +template FMT_CONSTEXPR bool is_name_start(Char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; +} + +template +FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, + IDHandler&& handler) -> const Char* { + FMT_ASSERT(begin != end, ""); + Char c = *begin; + if (c >= '0' && c <= '9') { + int index = 0; + if (c != '0') + index = + parse_nonnegative_int(begin, end, (std::numeric_limits::max)()); + else + ++begin; + if (begin == end || (*begin != '}' && *begin != ':')) + handler.on_error("invalid format string"); + else + handler(index); + return begin; + } + if (!is_name_start(c)) { + handler.on_error("invalid format string"); + return begin; + } + auto it = begin; + do { + ++it; + } while (it != end && (is_name_start(c = *it) || ('0' <= c && c <= '9'))); + handler(basic_string_view(begin, to_unsigned(it - begin))); + return it; +} + +template +FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, + IDHandler&& handler) -> const Char* { + Char c = *begin; + if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); + handler(); + return begin; +} + +template +FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + using detail::auto_id; + struct width_adapter { + Handler& handler; + + FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); } + FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); } + FMT_CONSTEXPR void operator()(basic_string_view id) { + handler.on_dynamic_width(id); + } + FMT_CONSTEXPR void on_error(const char* message) { + if (message) handler.on_error(message); + } + }; + + FMT_ASSERT(begin != end, ""); + if ('0' <= *begin && *begin <= '9') { + int width = parse_nonnegative_int(begin, end, -1); + if (width != -1) + handler.on_width(width); + else + handler.on_error("number is too big"); + } else if (*begin == '{') { + ++begin; + if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler}); + if (begin == end || *begin != '}') + return handler.on_error("invalid format string"), begin; + ++begin; + } + return begin; +} + +template +FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + using detail::auto_id; + struct precision_adapter { + Handler& handler; + + FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); } + FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); } + FMT_CONSTEXPR void operator()(basic_string_view id) { + handler.on_dynamic_precision(id); + } + FMT_CONSTEXPR void on_error(const char* message) { + if (message) handler.on_error(message); + } + }; + + ++begin; + auto c = begin != end ? *begin : Char(); + if ('0' <= c && c <= '9') { + auto precision = parse_nonnegative_int(begin, end, -1); + if (precision != -1) + handler.on_precision(precision); + else + handler.on_error("number is too big"); + } else if (c == '{') { + ++begin; + if (begin != end) + begin = parse_arg_id(begin, end, precision_adapter{handler}); + if (begin == end || *begin++ != '}') + return handler.on_error("invalid format string"), begin; + } else { + return handler.on_error("missing precision specifier"), begin; + } + handler.end_precision(); + return begin; +} + +template +FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type { + switch (to_ascii(type)) { + case 'd': + return presentation_type::dec; + case 'o': + return presentation_type::oct; + case 'x': + return presentation_type::hex_lower; + case 'X': + return presentation_type::hex_upper; + case 'b': + return presentation_type::bin_lower; + case 'B': + return presentation_type::bin_upper; + case 'a': + return presentation_type::hexfloat_lower; + case 'A': + return presentation_type::hexfloat_upper; + case 'e': + return presentation_type::exp_lower; + case 'E': + return presentation_type::exp_upper; + case 'f': + return presentation_type::fixed_lower; + case 'F': + return presentation_type::fixed_upper; + case 'g': + return presentation_type::general_lower; + case 'G': + return presentation_type::general_upper; + case 'c': + return presentation_type::chr; + case 's': + return presentation_type::string; + case 'p': + return presentation_type::pointer; + default: + return presentation_type::none; + } +} + +// Parses standard format specifiers and sends notifications about parsed +// components to handler. +template +FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin, + const Char* end, + SpecHandler&& handler) + -> const Char* { + if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) && + *begin != 'L') { + presentation_type type = parse_presentation_type(*begin++); + if (type == presentation_type::none) + handler.on_error("invalid type specifier"); + handler.on_type(type); + return begin; + } + + if (begin == end) return begin; + + begin = parse_align(begin, end, handler); + if (begin == end) return begin; + + // Parse sign. + switch (to_ascii(*begin)) { + case '+': + handler.on_sign(sign::plus); + ++begin; + break; + case '-': + handler.on_sign(sign::minus); + ++begin; + break; + case ' ': + handler.on_sign(sign::space); + ++begin; + break; + default: + break; + } + if (begin == end) return begin; + + if (*begin == '#') { + handler.on_hash(); + if (++begin == end) return begin; + } + + // Parse zero flag. + if (*begin == '0') { + handler.on_zero(); + if (++begin == end) return begin; + } + + begin = parse_width(begin, end, handler); + if (begin == end) return begin; + + // Parse precision. + if (*begin == '.') { + begin = parse_precision(begin, end, handler); + if (begin == end) return begin; + } + + if (*begin == 'L') { + handler.on_localized(); + ++begin; + } + + // Parse type. + if (begin != end && *begin != '}') { + presentation_type type = parse_presentation_type(*begin++); + if (type == presentation_type::none) + handler.on_error("invalid type specifier"); + handler.on_type(type); + } + return begin; +} + +template +FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + struct id_adapter { + Handler& handler; + int arg_id; + + FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); } + FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); } + FMT_CONSTEXPR void operator()(basic_string_view id) { + arg_id = handler.on_arg_id(id); + } + FMT_CONSTEXPR void on_error(const char* message) { + if (message) handler.on_error(message); + } + }; + + ++begin; + if (begin == end) return handler.on_error("invalid format string"), end; + if (*begin == '}') { + handler.on_replacement_field(handler.on_arg_id(), begin); + } else if (*begin == '{') { + handler.on_text(begin, begin + 1); + } else { + auto adapter = id_adapter{handler, 0}; + begin = parse_arg_id(begin, end, adapter); + Char c = begin != end ? *begin : Char(); + if (c == '}') { + handler.on_replacement_field(adapter.arg_id, begin); + } else if (c == ':') { + begin = handler.on_format_specs(adapter.arg_id, begin + 1, end); + if (begin == end || *begin != '}') + return handler.on_error("unknown format specifier"), end; + } else { + return handler.on_error("missing '}' in format string"), end; + } + } + return begin + 1; +} + +template +FMT_CONSTEXPR FMT_INLINE void parse_format_string( + basic_string_view format_str, Handler&& handler) { + // Workaround a name-lookup bug in MSVC's modules implementation. + using detail::find; + + auto begin = format_str.data(); + auto end = begin + format_str.size(); + if (end - begin < 32) { + // Use a simple loop instead of memchr for small strings. + const Char* p = begin; + while (p != end) { + auto c = *p++; + if (c == '{') { + handler.on_text(begin, p - 1); + begin = p = parse_replacement_field(p - 1, end, handler); + } else if (c == '}') { + if (p == end || *p != '}') + return handler.on_error("unmatched '}' in format string"); + handler.on_text(begin, p); + begin = ++p; + } + } + handler.on_text(begin, end); + return; + } + struct writer { + FMT_CONSTEXPR void operator()(const Char* pbegin, const Char* pend) { + if (pbegin == pend) return; + for (;;) { + const Char* p = nullptr; + if (!find(pbegin, pend, Char('}'), p)) + return handler_.on_text(pbegin, pend); + ++p; + if (p == pend || *p != '}') + return handler_.on_error("unmatched '}' in format string"); + handler_.on_text(pbegin, p); + pbegin = p + 1; + } + } + Handler& handler_; + } write{handler}; + while (begin != end) { + // Doing two passes with memchr (one for '{' and another for '}') is up to + // 2.5x faster than the naive one-pass implementation on big format strings. + const Char* p = begin; + if (*begin != '{' && !find(begin + 1, end, Char('{'), p)) + return write(begin, end); + write(begin, p); + begin = parse_replacement_field(p, end, handler); + } +} + +template +FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) + -> decltype(ctx.begin()) { + using char_type = typename ParseContext::char_type; + using context = buffer_context; + using mapped_type = conditional_t< + mapped_type_constant::value != type::custom_type, + decltype(arg_mapper().map(std::declval())), T>; + auto f = conditional_t::value, + formatter, + fallback_formatter>(); + return f.parse(ctx); +} + +// A parse context with extra argument id checks. It is only used at compile +// time because adding checks at runtime would introduce substantial overhead +// and would be redundant since argument ids are checked when arguments are +// retrieved anyway. +template +class compile_parse_context + : public basic_format_parse_context { + private: + int num_args_; + using base = basic_format_parse_context; + + public: + explicit FMT_CONSTEXPR compile_parse_context( + basic_string_view format_str, + int num_args = (std::numeric_limits::max)(), ErrorHandler eh = {}) + : base(format_str, eh), num_args_(num_args) {} + + FMT_CONSTEXPR auto next_arg_id() -> int { + int id = base::next_arg_id(); + if (id >= num_args_) this->on_error("argument not found"); + return id; + } + + FMT_CONSTEXPR void check_arg_id(int id) { + base::check_arg_id(id); + if (id >= num_args_) this->on_error("argument not found"); + } + using base::check_arg_id; +}; + +template +FMT_CONSTEXPR void check_int_type_spec(presentation_type type, + ErrorHandler&& eh) { + if (type > presentation_type::bin_upper && type != presentation_type::chr) + eh.on_error("invalid type specifier"); +} + +// Checks char specs and returns true if the type spec is char (and not int). +template +FMT_CONSTEXPR auto check_char_specs(const basic_format_specs& specs, + ErrorHandler&& eh = {}) -> bool { + if (specs.type != presentation_type::none && + specs.type != presentation_type::chr) { + check_int_type_spec(specs.type, eh); + return false; + } + if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) + eh.on_error("invalid format specifier for char"); + return true; +} + +// A floating-point presentation format. +enum class float_format : unsigned char { + general, // General: exponent notation or fixed point based on magnitude. + exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. + fixed, // Fixed point with the default precision of 6, e.g. 0.0012. + hex +}; + +struct float_specs { + int precision; + float_format format : 8; + sign_t sign : 8; + bool upper : 1; + bool locale : 1; + bool binary32 : 1; + bool fallback : 1; + bool showpoint : 1; +}; + +template +FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs& specs, + ErrorHandler&& eh = {}) + -> float_specs { + auto result = float_specs(); + result.showpoint = specs.alt; + result.locale = specs.localized; + switch (specs.type) { + case presentation_type::none: + result.format = float_format::general; + break; + case presentation_type::general_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::general_lower: + result.format = float_format::general; + break; + case presentation_type::exp_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::exp_lower: + result.format = float_format::exp; + result.showpoint |= specs.precision != 0; + break; + case presentation_type::fixed_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::fixed_lower: + result.format = float_format::fixed; + result.showpoint |= specs.precision != 0; + break; + case presentation_type::hexfloat_upper: + result.upper = true; + FMT_FALLTHROUGH; + case presentation_type::hexfloat_lower: + result.format = float_format::hex; + break; + default: + eh.on_error("invalid type specifier"); + break; + } + return result; +} + +template +FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, + ErrorHandler&& eh = {}) -> bool { + if (type == presentation_type::none || type == presentation_type::string) + return true; + if (type != presentation_type::pointer) eh.on_error("invalid type specifier"); + return false; +} + +template +FMT_CONSTEXPR void check_string_type_spec(presentation_type type, + ErrorHandler&& eh = {}) { + if (type != presentation_type::none && type != presentation_type::string) + eh.on_error("invalid type specifier"); +} + +template +FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type, + ErrorHandler&& eh) { + if (type != presentation_type::none && type != presentation_type::pointer) + eh.on_error("invalid type specifier"); +} + +// A parse_format_specs handler that checks if specifiers are consistent with +// the argument type. +template class specs_checker : public Handler { + private: + detail::type arg_type_; + + FMT_CONSTEXPR void require_numeric_argument() { + if (!is_arithmetic_type(arg_type_)) + this->on_error("format specifier requires numeric argument"); + } + + public: + FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type) + : Handler(handler), arg_type_(arg_type) {} + + FMT_CONSTEXPR void on_align(align_t align) { + if (align == align::numeric) require_numeric_argument(); + Handler::on_align(align); + } + + FMT_CONSTEXPR void on_sign(sign_t s) { + require_numeric_argument(); + if (is_integral_type(arg_type_) && arg_type_ != type::int_type && + arg_type_ != type::long_long_type && arg_type_ != type::char_type) { + this->on_error("format specifier requires signed argument"); + } + Handler::on_sign(s); + } + + FMT_CONSTEXPR void on_hash() { + require_numeric_argument(); + Handler::on_hash(); + } + + FMT_CONSTEXPR void on_localized() { + require_numeric_argument(); + Handler::on_localized(); + } + + FMT_CONSTEXPR void on_zero() { + require_numeric_argument(); + Handler::on_zero(); + } + + FMT_CONSTEXPR void end_precision() { + if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) + this->on_error("precision not allowed for this argument type"); + } +}; + +constexpr int invalid_arg_index = -1; + +#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +template +constexpr auto get_arg_index_by_name(basic_string_view name) -> int { + if constexpr (detail::is_statically_named_arg()) { + if (name == T::name) return N; + } + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name(name); + (void)name; // Workaround an MSVC bug about "unused" parameter. + return invalid_arg_index; +} +#endif + +template +FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { +#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name<0, Args...>(name); +#endif + (void)name; + return invalid_arg_index; +} + +template +class format_string_checker { + private: + using parse_context_type = compile_parse_context; + enum { num_args = sizeof...(Args) }; + + // Format specifier parsing function. + using parse_func = const Char* (*)(parse_context_type&); + + parse_context_type context_; + parse_func parse_funcs_[num_args > 0 ? num_args : 1]; + + public: + explicit FMT_CONSTEXPR format_string_checker( + basic_string_view format_str, ErrorHandler eh) + : context_(format_str, num_args, eh), + parse_funcs_{&parse_format_specs...} {} + + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + + FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + return context_.check_arg_id(id), id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { +#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS + auto index = get_arg_index_by_name(id); + if (index == invalid_arg_index) on_error("named argument is not found"); + return context_.check_arg_id(index), index; +#else + (void)id; + on_error("compile-time checks for named arguments require C++20 support"); + return 0; +#endif + } + + FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} + + FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) + -> const Char* { + context_.advance_to(context_.begin() + (begin - &*context_.begin())); + // id >= 0 check is a workaround for gcc 10 bug (#2065). + return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin; + } + + FMT_CONSTEXPR void on_error(const char* message) { + context_.on_error(message); + } +}; + +template ::value), int>> +void check_format_string(S format_str) { + FMT_CONSTEXPR auto s = to_string_view(format_str); + using checker = format_string_checker...>; + FMT_CONSTEXPR bool invalid_format = + (parse_format_string(s, checker(s, {})), true); + ignore_unused(invalid_format); +} + +template +void vformat_to( + buffer& buf, basic_string_view fmt, + basic_format_args)> args, + locale_ref loc = {}); + +FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); +#ifndef _WIN32 +inline void vprint_mojibake(std::FILE*, string_view, format_args) {} +#endif +FMT_END_DETAIL_NAMESPACE + +// A formatter specialization for the core types corresponding to detail::type +// constants. +template +struct formatter::value != + detail::type::custom_type>> { + private: + detail::dynamic_format_specs specs_; + + public: + // Parses format specifiers stopping either at the end of the range or at the + // terminating '}'. + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end) return begin; + using handler_type = detail::dynamic_specs_handler; + auto type = detail::type_constant::value; + auto checker = + detail::specs_checker(handler_type(specs_, ctx), type); + auto it = detail::parse_format_specs(begin, end, checker); + auto eh = ctx.error_handler(); + switch (type) { + case detail::type::none_type: + FMT_ASSERT(false, "invalid argument type"); + break; + case detail::type::bool_type: + if (specs_.type == presentation_type::none || + specs_.type == presentation_type::string) { + break; + } + FMT_FALLTHROUGH; + case detail::type::int_type: + case detail::type::uint_type: + case detail::type::long_long_type: + case detail::type::ulong_long_type: + case detail::type::int128_type: + case detail::type::uint128_type: + detail::check_int_type_spec(specs_.type, eh); + break; + case detail::type::char_type: + detail::check_char_specs(specs_, eh); + break; + case detail::type::float_type: + if (detail::const_check(FMT_USE_FLOAT)) + detail::parse_float_type_spec(specs_, eh); + else + FMT_ASSERT(false, "float support disabled"); + break; + case detail::type::double_type: + if (detail::const_check(FMT_USE_DOUBLE)) + detail::parse_float_type_spec(specs_, eh); + else + FMT_ASSERT(false, "double support disabled"); + break; + case detail::type::long_double_type: + if (detail::const_check(FMT_USE_LONG_DOUBLE)) + detail::parse_float_type_spec(specs_, eh); + else + FMT_ASSERT(false, "long double support disabled"); + break; + case detail::type::cstring_type: + detail::check_cstring_type_spec(specs_.type, eh); + break; + case detail::type::string_type: + detail::check_string_type_spec(specs_.type, eh); + break; + case detail::type::pointer_type: + detail::check_pointer_type_spec(specs_.type, eh); + break; + case detail::type::custom_type: + // Custom format specifiers are checked in parse functions of + // formatter specializations. + break; + } + return it; + } + + template + FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const + -> decltype(ctx.out()); +}; + +template struct basic_runtime { basic_string_view str; }; + +/** A compile-time format string. */ +template class basic_format_string { + private: + basic_string_view str_; + + public: + template >::value)> + FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) { + static_assert( + detail::count< + (std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); +#ifdef FMT_HAS_CONSTEVAL + if constexpr (detail::count_named_args() == + detail::count_statically_named_args()) { + using checker = detail::format_string_checker...>; + detail::parse_format_string(str_, checker(s, {})); + } +#else + detail::check_format_string(s); +#endif + } + basic_format_string(basic_runtime r) : str_(r.str) {} + + FMT_INLINE operator basic_string_view() const { return str_; } +}; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +// Workaround broken conversion on older gcc. +template using format_string = string_view; +template auto runtime(const S& s) -> basic_string_view> { + return s; +} +#else +template +using format_string = basic_format_string...>; +/** + \rst + Creates a runtime format string. + + **Example**:: + + // Check format string at runtime instead of compile-time. + fmt::print(fmt::runtime("{:d}"), "I am not a number"); + \endrst + */ +template auto runtime(const S& s) -> basic_runtime> { + return {{s}}; +} +#endif + +FMT_API auto vformat(string_view fmt, format_args args) -> std::string; + +/** + \rst + Formats ``args`` according to specifications in ``fmt`` and returns the result + as a string. + + **Example**:: + + #include + std::string message = fmt::format("The answer is {}.", 42); + \endrst +*/ +template +FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) + -> std::string { + return vformat(fmt, fmt::make_format_args(args...)); +} + +/** Formats a string and writes the output to ``out``. */ +template ::value)> +auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { + using detail::get_buffer; + auto&& buf = get_buffer(out); + detail::vformat_to(buf, fmt, args, {}); + return detail::get_iterator(buf); +} + +/** + \rst + Formats ``args`` according to specifications in ``fmt``, writes the result to + the output iterator ``out`` and returns the iterator past the end of the output + range. `format_to` does not append a terminating null character. + + **Example**:: + + auto out = std::vector(); + fmt::format_to(std::back_inserter(out), "{}", 42); + \endrst + */ +template ::value)> +FMT_INLINE auto format_to(OutputIt out, format_string fmt, T&&... args) + -> OutputIt { + return vformat_to(out, fmt, fmt::make_format_args(args...)); +} + +template struct format_to_n_result { + /** Iterator past the end of the output range. */ + OutputIt out; + /** Total (not truncated) output size. */ + size_t size; +}; + +template ::value)> +auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) + -> format_to_n_result { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, n); + detail::vformat_to(buf, fmt, args, {}); + return {buf.out(), buf.count()}; +} + +/** + \rst + Formats ``args`` according to specifications in ``fmt``, writes up to ``n`` + characters of the result to the output iterator ``out`` and returns the total + (not truncated) output size and the iterator past the end of the output range. + `format_to_n` does not append a terminating null character. + \endrst + */ +template ::value)> +FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, + T&&... args) -> format_to_n_result { + return vformat_to_n(out, n, fmt, fmt::make_format_args(args...)); +} + +/** Returns the number of chars in the output of ``format(fmt, args...)``. */ +template +FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, + T&&... args) -> size_t { + auto buf = detail::counting_buffer<>(); + detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {}); + return buf.count(); +} + +FMT_API void vprint(string_view fmt, format_args args); +FMT_API void vprint(std::FILE* f, string_view fmt, format_args args); + +/** + \rst + Formats ``args`` according to specifications in ``fmt`` and writes the output + to ``stdout``. + + **Example**:: + + fmt::print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template +FMT_INLINE void print(format_string fmt, T&&... args) { + const auto& vargs = fmt::make_format_args(args...); + return detail::is_utf8() ? vprint(fmt, vargs) + : detail::vprint_mojibake(stdout, fmt, vargs); +} + +/** + \rst + Formats ``args`` according to specifications in ``fmt`` and writes the + output to the file ``f``. + + **Example**:: + + fmt::print(stderr, "Don't {}!", "panic"); + \endrst + */ +template +FMT_INLINE void print(std::FILE* f, format_string fmt, T&&... args) { + const auto& vargs = fmt::make_format_args(args...); + return detail::is_utf8() ? vprint(f, fmt, vargs) + : detail::vprint_mojibake(f, fmt, vargs); +} + +FMT_MODULE_EXPORT_END +FMT_GCC_PRAGMA("GCC pop_options") +FMT_END_NAMESPACE + +#ifdef FMT_HEADER_ONLY +# include "format.h" +#endif +#endif // FMT_CORE_H_ diff --git a/ctrtool/deps/libfmt/include/fmt/format-inl.h b/ctrtool/deps/libfmt/include/fmt/format-inl.h new file mode 100644 index 00000000..2c51c50a --- /dev/null +++ b/ctrtool/deps/libfmt/include/fmt/format-inl.h @@ -0,0 +1,2643 @@ +// Formatting library for C++ - implementation +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_FORMAT_INL_H_ +#define FMT_FORMAT_INL_H_ + +#include +#include +#include // errno +#include +#include +#include +#include // std::memmove +#include +#include + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +# include +#endif + +#ifdef _WIN32 +# include // _isatty +#endif + +#include "format.h" + +FMT_BEGIN_NAMESPACE +namespace detail { + +FMT_FUNC void assert_fail(const char* file, int line, const char* message) { + // Use unchecked std::fprintf to avoid triggering another assertion when + // writing to stderr fails + std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); + // Chosen instead of std::abort to satisfy Clang in CUDA mode during device + // code pass. + std::terminate(); +} + +FMT_FUNC void throw_format_error(const char* message) { + FMT_THROW(format_error(message)); +} + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else // _MSC_VER +inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); + va_end(args); + return result; +} +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + +FMT_FUNC void format_error_code(detail::buffer& out, int error_code, + string_view message) FMT_NOEXCEPT { + // Report error code making sure that the output fits into + // inline_buffer_size to avoid dynamic memory allocation and potential + // bad_alloc. + out.try_resize(0); + static const char SEP[] = ": "; + static const char ERROR_STR[] = "error "; + // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. + size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; + auto abs_value = static_cast>(error_code); + if (detail::is_negative(error_code)) { + abs_value = 0 - abs_value; + ++error_code_size; + } + error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); + auto it = buffer_appender(out); + if (message.size() <= inline_buffer_size - error_code_size) + format_to(it, FMT_STRING("{}{}"), message, SEP); + format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); + FMT_ASSERT(out.size() <= inline_buffer_size, ""); +} + +FMT_FUNC void report_error(format_func func, int error_code, + const char* message) FMT_NOEXCEPT { + memory_buffer full_message; + func(full_message, error_code, message); + // Don't use fwrite_fully because the latter may throw. + if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) + std::fputc('\n', stderr); +} + +// A wrapper around fwrite that throws on error. +inline void fwrite_fully(const void* ptr, size_t size, size_t count, + FILE* stream) { + size_t written = std::fwrite(ptr, size, count, stream); + if (written < count) FMT_THROW(system_error(errno, "cannot write to file")); +} + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template +locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { + static_assert(std::is_same::value, ""); +} + +template Locale locale_ref::get() const { + static_assert(std::is_same::value, ""); + return locale_ ? *static_cast(locale_) : std::locale(); +} + +template +FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { + auto& facet = std::use_facet>(loc.get()); + auto grouping = facet.grouping(); + auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); + return {std::move(grouping), thousands_sep}; +} +template FMT_FUNC Char decimal_point_impl(locale_ref loc) { + return std::use_facet>(loc.get()) + .decimal_point(); +} +#else +template +FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result { + return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; +} +template FMT_FUNC Char decimal_point_impl(locale_ref) { + return '.'; +} +#endif +} // namespace detail + +#if !FMT_MSC_VER +FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default; +#endif + +FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, + format_args args) { + auto ec = std::error_code(error_code, std::generic_category()); + return std::system_error(ec, vformat(format_str, args)); +} + +namespace detail { + +template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { + // fallback_uintptr is always stored in little endian. + int i = static_cast(sizeof(void*)) - 1; + while (i > 0 && n.value[i] == 0) --i; + auto char_digits = std::numeric_limits::digits / 4; + return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; +} + +// log10(2) = 0x0.4d104d427de7fbcc... +static constexpr uint64_t log10_2_significand = 0x4d104d427de7fbcc; + +template struct basic_impl_data { + // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. + // These are generated by support/compute-powers.py. + static constexpr uint64_t pow10_significands[87] = { + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, + 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, + 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, + 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, + 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, + 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, + 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, + 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, + 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, + 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, + 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, + 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, + 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, + 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, + 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, + 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, + 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, + 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, + 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, + 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, + 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, + 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, + }; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnarrowing" +#endif + // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding + // to significands above. + static constexpr int16_t pow10_exponents[87] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic pop +#endif + + static constexpr uint64_t power_of_10_64[20] = { + 1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; +}; + +// This is a struct rather than an alias to avoid shadowing warnings in gcc. +struct impl_data : basic_impl_data<> {}; + +#if __cplusplus < 201703L +template +constexpr uint64_t basic_impl_data::pow10_significands[]; +template constexpr int16_t basic_impl_data::pow10_exponents[]; +template constexpr uint64_t basic_impl_data::power_of_10_64[]; +#endif + +template struct bits { + static FMT_CONSTEXPR_DECL const int value = + static_cast(sizeof(T) * std::numeric_limits::digits); +}; + +// Returns the number of significand bits in Float excluding the implicit bit. +template constexpr int num_significand_bits() { + // Subtract 1 to account for an implicit most significant bit in the + // normalized form. + return std::numeric_limits::digits - 1; +} + +// A floating-point number f * pow(2, e). +struct fp { + uint64_t f; + int e; + + static constexpr const int num_significand_bits = bits::value; + + constexpr fp() : f(0), e(0) {} + constexpr fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} + + // Constructs fp from an IEEE754 floating-point number. It is a template to + // prevent compile errors on systems where n is not IEEE754. + template explicit FMT_CONSTEXPR fp(Float n) { assign(n); } + + template + using is_supported = bool_constant; + + // Assigns d to this and return true iff predecessor is closer than successor. + template ::value)> + FMT_CONSTEXPR bool assign(Float n) { + // Assume float is in the format [sign][exponent][significand]. + const int num_float_significand_bits = + detail::num_significand_bits(); + const uint64_t implicit_bit = 1ULL << num_float_significand_bits; + const uint64_t significand_mask = implicit_bit - 1; + constexpr bool is_double = sizeof(Float) == sizeof(uint64_t); + auto u = bit_cast>(n); + f = u & significand_mask; + const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; + int biased_e = + static_cast((u & exponent_mask) >> num_float_significand_bits); + // The predecessor is closer if n is a normalized power of 2 (f == 0) other + // than the smallest normalized number (biased_e > 1). + bool is_predecessor_closer = f == 0 && biased_e > 1; + if (biased_e != 0) + f += implicit_bit; + else + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + const int exponent_bias = std::numeric_limits::max_exponent - 1; + e = biased_e - exponent_bias - num_float_significand_bits; + return is_predecessor_closer; + } + + template ::value)> + bool assign(Float) { + FMT_ASSERT(false, ""); + return false; + } +}; + +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template FMT_CONSTEXPR fp normalize(fp value) { + // Handle subnormals. + const uint64_t implicit_bit = 1ULL << num_significand_bits(); + const auto shifted_implicit_bit = implicit_bit << SHIFT; + while ((value.f & shifted_implicit_bit) == 0) { + value.f <<= 1; + --value.e; + } + // Subtract 1 to account for hidden bit. + const auto offset = + fp::num_significand_bits - num_significand_bits() - SHIFT - 1; + value.f <<= offset; + value.e -= offset; + return value; +} + +inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; } + +// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. +FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +#if FMT_USE_INT128 + auto product = static_cast<__uint128_t>(lhs) * rhs; + auto f = static_cast(product >> 64); + return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; +#else + // Multiply 32-bit parts of significands. + uint64_t mask = (1ULL << 32) - 1; + uint64_t a = lhs >> 32, b = lhs & mask; + uint64_t c = rhs >> 32, d = rhs & mask; + uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; + // Compute mid 64-bit of result and round. + uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); + return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); +#endif +} + +FMT_CONSTEXPR inline fp operator*(fp x, fp y) { + return {multiply(x.f, y.f), x.e + y.e + 64}; +} + +// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its +// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. +FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, + int& pow10_exponent) { + const int shift = 32; + const auto significand = static_cast(log10_2_significand); + int index = static_cast( + ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + + ((int64_t(1) << shift) - 1)) // ceil + >> 32 // arithmetic shift + ); + // Decimal exponent of the first (smallest) cached power of 10. + const int first_dec_exp = -348; + // Difference between 2 consecutive decimal exponents in cached powers of 10. + const int dec_exp_step = 8; + index = (index - first_dec_exp - 1) / dec_exp_step + 1; + pow10_exponent = first_dec_exp + index * dec_exp_step; + return {impl_data::pow10_significands[index], + impl_data::pow10_exponents[index]}; +} + +// A simple accumulator to hold the sums of terms in bigint::square if uint128_t +// is not available. +struct accumulator { + uint64_t lower; + uint64_t upper; + + constexpr accumulator() : lower(0), upper(0) {} + constexpr explicit operator uint32_t() const { + return static_cast(lower); + } + + FMT_CONSTEXPR void operator+=(uint64_t n) { + lower += n; + if (lower < n) ++upper; + } + FMT_CONSTEXPR void operator>>=(int shift) { + FMT_ASSERT(shift == 32, ""); + (void)shift; + lower = (upper << 32) | (lower >> 32); + upper >>= 32; + } +}; + +class bigint { + private: + // A bigint is stored as an array of bigits (big digits), with bigit at index + // 0 being the least significant one. + using bigit = uint32_t; + using double_bigit = uint64_t; + enum { bigits_capacity = 32 }; + basic_memory_buffer bigits_; + int exp_; + + FMT_CONSTEXPR20 bigit operator[](int index) const { + return bigits_[to_unsigned(index)]; + } + FMT_CONSTEXPR20 bigit& operator[](int index) { + return bigits_[to_unsigned(index)]; + } + + static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; + + friend struct formatter; + + FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = static_cast((*this)[index]) - other - borrow; + (*this)[index] = static_cast(result); + borrow = static_cast(result >> (bigit_bits * 2 - 1)); + } + + FMT_CONSTEXPR20 void remove_leading_zeros() { + int num_bigits = static_cast(bigits_.size()) - 1; + while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + bigits_.resize(to_unsigned(num_bigits + 1)); + } + + // Computes *this -= other assuming aligned bigints and *this >= other. + FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) { + FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); + FMT_ASSERT(compare(*this, other) >= 0, ""); + bigit borrow = 0; + int i = other.exp_ - exp_; + for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) + subtract_bigits(i, other.bigits_[j], borrow); + while (borrow > 0) subtract_bigits(i, 0, borrow); + remove_leading_zeros(); + } + + FMT_CONSTEXPR20 void multiply(uint32_t value) { + const double_bigit wide_value = value; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * wide_value + carry; + bigits_[i] = static_cast(result); + carry = static_cast(result >> bigit_bits); + } + if (carry != 0) bigits_.push_back(carry); + } + + FMT_CONSTEXPR20 void multiply(uint64_t value) { + const bigit mask = ~bigit(0); + const double_bigit lower = value & mask; + const double_bigit upper = value >> bigit_bits; + double_bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * lower + (carry & mask); + carry = + bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits); + bigits_[i] = static_cast(result); + } + while (carry != 0) { + bigits_.push_back(carry & mask); + carry >>= bigit_bits; + } + } + + public: + FMT_CONSTEXPR20 bigint() : exp_(0) {} + explicit bigint(uint64_t n) { assign(n); } + FMT_CONSTEXPR20 ~bigint() { + FMT_ASSERT(bigits_.capacity() <= bigits_capacity, ""); + } + + bigint(const bigint&) = delete; + void operator=(const bigint&) = delete; + + FMT_CONSTEXPR20 void assign(const bigint& other) { + auto size = other.bigits_.size(); + bigits_.resize(size); + auto data = other.bigits_.data(); + std::copy(data, data + size, make_checked(bigits_.data(), size)); + exp_ = other.exp_; + } + + FMT_CONSTEXPR20 void assign(uint64_t n) { + size_t num_bigits = 0; + do { + bigits_[num_bigits++] = n & ~bigit(0); + n >>= bigit_bits; + } while (n != 0); + bigits_.resize(num_bigits); + exp_ = 0; + } + + FMT_CONSTEXPR20 int num_bigits() const { + return static_cast(bigits_.size()) + exp_; + } + + FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { + FMT_ASSERT(shift >= 0, ""); + exp_ += shift / bigit_bits; + shift %= bigit_bits; + if (shift == 0) return *this; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + bigit c = bigits_[i] >> (bigit_bits - shift); + bigits_[i] = (bigits_[i] << shift) + carry; + carry = c; + } + if (carry != 0) bigits_.push_back(carry); + return *this; + } + + template FMT_CONSTEXPR20 bigint& operator*=(Int value) { + FMT_ASSERT(value > 0, ""); + multiply(uint32_or_64_or_128_t(value)); + return *this; + } + + friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) { + int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); + if (num_lhs_bigits != num_rhs_bigits) + return num_lhs_bigits > num_rhs_bigits ? 1 : -1; + int i = static_cast(lhs.bigits_.size()) - 1; + int j = static_cast(rhs.bigits_.size()) - 1; + int end = i - j; + if (end < 0) end = 0; + for (; i >= end; --i, --j) { + bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; + if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; + } + if (i != j) return i > j ? 1 : -1; + return 0; + } + + // Returns compare(lhs1 + lhs2, rhs). + friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) { + int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); + int num_rhs_bigits = rhs.num_bigits(); + if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; + if (max_lhs_bigits > num_rhs_bigits) return 1; + auto get_bigit = [](const bigint& n, int i) -> bigit { + return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; + }; + double_bigit borrow = 0; + int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_); + for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { + double_bigit sum = + static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); + bigit rhs_bigit = get_bigit(rhs, i); + if (sum > rhs_bigit + borrow) return 1; + borrow = rhs_bigit + borrow - sum; + if (borrow > 1) return -1; + borrow <<= bigit_bits; + } + return borrow != 0 ? -1 : 0; + } + + // Assigns pow(10, exp) to this bigint. + FMT_CONSTEXPR20 void assign_pow10(int exp) { + FMT_ASSERT(exp >= 0, ""); + if (exp == 0) return assign(1); + // Find the top bit. + int bitmask = 1; + while (exp >= bitmask) bitmask <<= 1; + bitmask >>= 1; + // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by + // repeated squaring and multiplication. + assign(5); + bitmask >>= 1; + while (bitmask != 0) { + square(); + if ((exp & bitmask) != 0) *this *= 5; + bitmask >>= 1; + } + *this <<= exp; // Multiply by pow(2, exp) by shifting. + } + + FMT_CONSTEXPR20 void square() { + int num_bigits = static_cast(bigits_.size()); + int num_result_bigits = 2 * num_bigits; + basic_memory_buffer n(std::move(bigits_)); + bigits_.resize(to_unsigned(num_result_bigits)); + using accumulator_t = conditional_t; + auto sum = accumulator_t(); + for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { + // Compute bigit at position bigit_index of the result by adding + // cross-product terms n[i] * n[j] such that i + j == bigit_index. + for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { + // Most terms are multiplied twice which can be optimized in the future. + sum += static_cast(n[i]) * n[j]; + } + (*this)[bigit_index] = static_cast(sum); + sum >>= bits::value; // Compute the carry. + } + // Do the same for the top half. + for (int bigit_index = num_bigits; bigit_index < num_result_bigits; + ++bigit_index) { + for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) + sum += static_cast(n[i++]) * n[j--]; + (*this)[bigit_index] = static_cast(sum); + sum >>= bits::value; + } + remove_leading_zeros(); + exp_ *= 2; + } + + // If this bigint has a bigger exponent than other, adds trailing zero to make + // exponents equal. This simplifies some operations such as subtraction. + FMT_CONSTEXPR20 void align(const bigint& other) { + int exp_difference = exp_ - other.exp_; + if (exp_difference <= 0) return; + int num_bigits = static_cast(bigits_.size()); + bigits_.resize(to_unsigned(num_bigits + exp_difference)); + for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) + bigits_[j] = bigits_[i]; + std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); + exp_ -= exp_difference; + } + + // Divides this bignum by divisor, assigning the remainder to this and + // returning the quotient. + FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { + FMT_ASSERT(this != &divisor, ""); + if (compare(*this, divisor) < 0) return 0; + FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); + align(divisor); + int quotient = 0; + do { + subtract_aligned(divisor); + ++quotient; + } while (compare(*this, divisor) >= 0); + return quotient; + } +}; + +enum class round_direction { unknown, up, down }; + +// Given the divisor (normally a power of 10), the remainder = v % divisor for +// some number v and the error, returns whether v should be rounded up, down, or +// whether the rounding direction can't be determined due to error. +// error should be less than divisor / 2. +FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor, + uint64_t remainder, + uint64_t error) { + FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. + FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. + FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. + // Round down if (remainder + error) * 2 <= divisor. + if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) + return round_direction::down; + // Round up if (remainder - error) * 2 >= divisor. + if (remainder >= error && + remainder - error >= divisor - (remainder - error)) { + return round_direction::up; + } + return round_direction::unknown; +} + +namespace digits { +enum result { + more, // Generate more digits. + done, // Done generating digits. + error // Digit generation cancelled due to an error. +}; +} + +struct gen_digits_handler { + char* buf; + int size; + int precision; + int exp10; + bool fixed; + + FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor, + uint64_t remainder, uint64_t error, + bool integral) { + FMT_ASSERT(remainder < divisor, ""); + buf[size++] = digit; + if (!integral && error >= remainder) return digits::error; + if (size < precision) return digits::more; + if (!integral) { + // Check if error * 2 < divisor with overflow prevention. + // The check is not needed for the integral part because error = 1 + // and divisor > (1 << 32) there. + if (error >= divisor || error >= divisor - error) return digits::error; + } else { + FMT_ASSERT(error == 1 && divisor > 2, ""); + } + auto dir = get_round_direction(divisor, remainder, error); + if (dir != round_direction::up) + return dir == round_direction::down ? digits::done : digits::error; + ++buf[size - 1]; + for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + if (fixed) + buf[size++] = '0'; + else + ++exp10; + } + return digits::done; + } +}; + +// Generates output using the Grisu digit-gen algorithm. +// error: the size of the region (lower, upper) outside of which numbers +// definitely do not round to value (Delta in Grisu3). +FMT_INLINE FMT_CONSTEXPR20 digits::result grisu_gen_digits( + fp value, uint64_t error, int& exp, gen_digits_handler& handler) { + const fp one(1ULL << -value.e, value.e); + // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be + // zero because it contains a product of two 64-bit numbers with MSB set (due + // to normalization) - 1, shifted right by at most 60 bits. + auto integral = static_cast(value.f >> -one.e); + FMT_ASSERT(integral != 0, ""); + FMT_ASSERT(integral == value.f >> -one.e, ""); + // The fractional part of scaled value (p2 in Grisu) c = value % one. + uint64_t fractional = value.f & (one.f - 1); + exp = count_digits(integral); // kappa in Grisu. + // Non-fixed formats require at least one digit and no precision adjustment. + if (handler.fixed) { + // Adjust fixed precision by exponent because it is relative to decimal + // point. + int precision_offset = exp + handler.exp10; + if (precision_offset > 0 && + handler.precision > max_value() - precision_offset) { + FMT_THROW(format_error("number is too big")); + } + handler.precision += precision_offset; + // Check if precision is satisfied just by leading zeros, e.g. + // format("{:.2f}", 0.001) gives "0.00" without generating any digits. + if (handler.precision <= 0) { + if (handler.precision < 0) return digits::done; + // Divide by 10 to prevent overflow. + uint64_t divisor = impl_data::power_of_10_64[exp - 1] << -one.e; + auto dir = get_round_direction(divisor, value.f / 10, error * 10); + if (dir == round_direction::unknown) return digits::error; + handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0'; + return digits::done; + } + } + // Generate digits for the integral part. This can produce up to 10 digits. + do { + uint32_t digit = 0; + auto divmod_integral = [&](uint32_t divisor) { + digit = integral / divisor; + integral %= divisor; + }; + // This optimization by Milo Yip reduces the number of integer divisions by + // one per iteration. + switch (exp) { + case 10: + divmod_integral(1000000000); + break; + case 9: + divmod_integral(100000000); + break; + case 8: + divmod_integral(10000000); + break; + case 7: + divmod_integral(1000000); + break; + case 6: + divmod_integral(100000); + break; + case 5: + divmod_integral(10000); + break; + case 4: + divmod_integral(1000); + break; + case 3: + divmod_integral(100); + break; + case 2: + divmod_integral(10); + break; + case 1: + digit = integral; + integral = 0; + break; + default: + FMT_ASSERT(false, "invalid number of digits"); + } + --exp; + auto remainder = (static_cast(integral) << -one.e) + fractional; + auto result = handler.on_digit(static_cast('0' + digit), + impl_data::power_of_10_64[exp] << -one.e, + remainder, error, true); + if (result != digits::more) return result; + } while (exp > 0); + // Generate digits for the fractional part. + for (;;) { + fractional *= 10; + error *= 10; + char digit = static_cast('0' + (fractional >> -one.e)); + fractional &= one.f - 1; + --exp; + auto result = handler.on_digit(digit, one.f, fractional, error, false); + if (result != digits::more) return result; + } +} + +// A 128-bit integer type used internally, +struct uint128_wrapper { + uint128_wrapper() = default; + +#if FMT_USE_INT128 + uint128_t internal_; + + constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT + : internal_{static_cast(low) | + (static_cast(high) << 64)} {} + + constexpr uint128_wrapper(uint128_t u) : internal_{u} {} + + constexpr uint64_t high() const FMT_NOEXCEPT { + return uint64_t(internal_ >> 64); + } + constexpr uint64_t low() const FMT_NOEXCEPT { return uint64_t(internal_); } + + uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { + internal_ += n; + return *this; + } +#else + uint64_t high_; + uint64_t low_; + + constexpr uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT + : high_{high}, + low_{low} {} + + constexpr uint64_t high() const FMT_NOEXCEPT { return high_; } + constexpr uint64_t low() const FMT_NOEXCEPT { return low_; } + + uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { +# if defined(_MSC_VER) && defined(_M_X64) + unsigned char carry = _addcarry_u64(0, low_, n, &low_); + _addcarry_u64(carry, high_, 0, &high_); + return *this; +# else + uint64_t sum = low_ + n; + high_ += (sum < low_ ? 1 : 0); + low_ = sum; + return *this; +# endif + } +#endif +}; + +// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. +namespace dragonbox { +// Computes 128-bit result of multiplication of two 64-bit unsigned integers. +inline uint128_wrapper umul128(uint64_t x, uint64_t y) FMT_NOEXCEPT { +#if FMT_USE_INT128 + return static_cast(x) * static_cast(y); +#elif defined(_MSC_VER) && defined(_M_X64) + uint128_wrapper result; + result.low_ = _umul128(x, y, &result.high_); + return result; +#else + const uint64_t mask = (uint64_t(1) << 32) - uint64_t(1); + + uint64_t a = x >> 32; + uint64_t b = x & mask; + uint64_t c = y >> 32; + uint64_t d = y & mask; + + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + + uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask); + + return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), + (intermediate << 32) + (bd & mask)}; +#endif +} + +// Computes upper 64 bits of multiplication of two 64-bit unsigned integers. +inline uint64_t umul128_upper64(uint64_t x, uint64_t y) FMT_NOEXCEPT { +#if FMT_USE_INT128 + auto p = static_cast(x) * static_cast(y); + return static_cast(p >> 64); +#elif defined(_MSC_VER) && defined(_M_X64) + return __umulh(x, y); +#else + return umul128(x, y).high(); +#endif +} + +// Computes upper 64 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +inline uint64_t umul192_upper64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT { + uint128_wrapper g0 = umul128(x, y.high()); + g0 += umul128_upper64(x, y.low()); + return g0.high(); +} + +// Computes upper 32 bits of multiplication of a 32-bit unsigned integer and a +// 64-bit unsigned integer. +inline uint32_t umul96_upper32(uint32_t x, uint64_t y) FMT_NOEXCEPT { + return static_cast(umul128_upper64(x, y)); +} + +// Computes middle 64 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +inline uint64_t umul192_middle64(uint64_t x, uint128_wrapper y) FMT_NOEXCEPT { + uint64_t g01 = x * y.high(); + uint64_t g10 = umul128_upper64(x, y.low()); + return g01 + g10; +} + +// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a +// 64-bit unsigned integer. +inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT { + return x * y; +} + +// Computes floor(log10(pow(2, e))) for e in [-1700, 1700] using the method from +// https://fmt.dev/papers/Grisu-Exact.pdf#page=5, section 3.4. +inline int floor_log10_pow2(int e) FMT_NOEXCEPT { + FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); + const int shift = 22; + return (e * static_cast(log10_2_significand >> (64 - shift))) >> shift; +} + +// Various fast log computations. +inline int floor_log2_pow10(int e) FMT_NOEXCEPT { + FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); + const uint64_t log2_10_integer_part = 3; + const uint64_t log2_10_fractional_digits = 0x5269e12f346e2bf9; + const int shift_amount = 19; + return (e * static_cast( + (log2_10_integer_part << shift_amount) | + (log2_10_fractional_digits >> (64 - shift_amount)))) >> + shift_amount; +} +inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT { + FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); + const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375; + const int shift_amount = 22; + return (e * static_cast(log10_2_significand >> (64 - shift_amount)) - + static_cast(log10_4_over_3_fractional_digits >> + (64 - shift_amount))) >> + shift_amount; +} + +// Returns true iff x is divisible by pow(2, exp). +inline bool divisible_by_power_of_2(uint32_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp >= 1, ""); + FMT_ASSERT(x != 0, ""); +#ifdef FMT_BUILTIN_CTZ + return FMT_BUILTIN_CTZ(x) >= exp; +#else + return exp < num_bits() && x == ((x >> exp) << exp); +#endif +} +inline bool divisible_by_power_of_2(uint64_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp >= 1, ""); + FMT_ASSERT(x != 0, ""); +#ifdef FMT_BUILTIN_CTZLL + return FMT_BUILTIN_CTZLL(x) >= exp; +#else + return exp < num_bits() && x == ((x >> exp) << exp); +#endif +} + +// Table entry type for divisibility test. +template struct divtest_table_entry { + T mod_inv; + T max_quotient; +}; + +// Returns true iff x is divisible by pow(5, exp). +inline bool divisible_by_power_of_5(uint32_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp <= 10, "too large exponent"); + static constexpr const divtest_table_entry divtest_table[] = { + {0x00000001, 0xffffffff}, {0xcccccccd, 0x33333333}, + {0xc28f5c29, 0x0a3d70a3}, {0x26e978d5, 0x020c49ba}, + {0x3afb7e91, 0x0068db8b}, {0x0bcbe61d, 0x0014f8b5}, + {0x68c26139, 0x000431bd}, {0xae8d46a5, 0x0000d6bf}, + {0x22e90e21, 0x00002af3}, {0x3a2e9c6d, 0x00000897}, + {0x3ed61f49, 0x000001b7}}; + return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient; +} +inline bool divisible_by_power_of_5(uint64_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp <= 23, "too large exponent"); + static constexpr const divtest_table_entry divtest_table[] = { + {0x0000000000000001, 0xffffffffffffffff}, + {0xcccccccccccccccd, 0x3333333333333333}, + {0x8f5c28f5c28f5c29, 0x0a3d70a3d70a3d70}, + {0x1cac083126e978d5, 0x020c49ba5e353f7c}, + {0xd288ce703afb7e91, 0x0068db8bac710cb2}, + {0x5d4e8fb00bcbe61d, 0x0014f8b588e368f0}, + {0x790fb65668c26139, 0x000431bde82d7b63}, + {0xe5032477ae8d46a5, 0x0000d6bf94d5e57a}, + {0xc767074b22e90e21, 0x00002af31dc46118}, + {0x8e47ce423a2e9c6d, 0x0000089705f4136b}, + {0x4fa7f60d3ed61f49, 0x000001b7cdfd9d7b}, + {0x0fee64690c913975, 0x00000057f5ff85e5}, + {0x3662e0e1cf503eb1, 0x000000119799812d}, + {0xa47a2cf9f6433fbd, 0x0000000384b84d09}, + {0x54186f653140a659, 0x00000000b424dc35}, + {0x7738164770402145, 0x0000000024075f3d}, + {0xe4a4d1417cd9a041, 0x000000000734aca5}, + {0xc75429d9e5c5200d, 0x000000000170ef54}, + {0xc1773b91fac10669, 0x000000000049c977}, + {0x26b172506559ce15, 0x00000000000ec1e4}, + {0xd489e3a9addec2d1, 0x000000000002f394}, + {0x90e860bb892c8d5d, 0x000000000000971d}, + {0x502e79bf1b6f4f79, 0x0000000000001e39}, + {0xdcd618596be30fe5, 0x000000000000060b}}; + return x * divtest_table[exp].mod_inv <= divtest_table[exp].max_quotient; +} + +// Replaces n by floor(n / pow(5, N)) returning true if and only if n is +// divisible by pow(5, N). +// Precondition: n <= 2 * pow(5, N + 1). +template +bool check_divisibility_and_divide_by_pow5(uint32_t& n) FMT_NOEXCEPT { + static constexpr struct { + uint32_t magic_number; + int bits_for_comparison; + uint32_t threshold; + int shift_amount; + } infos[] = {{0xcccd, 16, 0x3333, 18}, {0xa429, 8, 0x0a, 20}}; + constexpr auto info = infos[N - 1]; + n *= info.magic_number; + const uint32_t comparison_mask = (1u << info.bits_for_comparison) - 1; + bool result = (n & comparison_mask) <= info.threshold; + n >>= info.shift_amount; + return result; +} + +// Computes floor(n / pow(10, N)) for small n and N. +// Precondition: n <= pow(10, N + 1). +template uint32_t small_division_by_pow10(uint32_t n) FMT_NOEXCEPT { + static constexpr struct { + uint32_t magic_number; + int shift_amount; + uint32_t divisor_times_10; + } infos[] = {{0xcccd, 19, 100}, {0xa3d8, 22, 1000}}; + constexpr auto info = infos[N - 1]; + FMT_ASSERT(n <= info.divisor_times_10, "n is too large"); + return n * info.magic_number >> info.shift_amount; +} + +// Computes floor(n / 10^(kappa + 1)) (float) +inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) FMT_NOEXCEPT { + return n / float_info::big_divisor; +} +// Computes floor(n / 10^(kappa + 1)) (double) +inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) FMT_NOEXCEPT { + return umul128_upper64(n, 0x83126e978d4fdf3c) >> 9; +} + +// Various subroutines using pow10 cache +template struct cache_accessor; + +template <> struct cache_accessor { + using carrier_uint = float_info::carrier_uint; + using cache_entry_type = uint64_t; + + static uint64_t get_cached_power(int k) FMT_NOEXCEPT { + FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, + "k is out of range"); + static constexpr const uint64_t pow10_significands[] = { + 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, + 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, + 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, + 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb, + 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a, + 0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, + 0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, + 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, + 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424, + 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b, + 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, + 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, + 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, + 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, + 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, + 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, + 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, + 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, + 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, + 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984, + 0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 0xfc6f7c4045812296, + 0x9dc5ada82b70b59d, 0xc5371912364ce305, 0xf684df56c3e01bc6, + 0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20, + 0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd, + 0x92efd1b8d0cf37be, 0xb7abc627050305ad, 0xe596b7b0c643c719, + 0x8f7e32ce7bea5c6f, 0xb35dbf821ae4f38b, 0xe0352f62a19e306e}; + return pow10_significands[k - float_info::min_k]; + } + + static carrier_uint compute_mul(carrier_uint u, + const cache_entry_type& cache) FMT_NOEXCEPT { + return umul96_upper32(u, cache); + } + + static uint32_t compute_delta(const cache_entry_type& cache, + int beta_minus_1) FMT_NOEXCEPT { + return static_cast(cache >> (64 - 1 - beta_minus_1)); + } + + static bool compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta_minus_1) FMT_NOEXCEPT { + FMT_ASSERT(beta_minus_1 >= 1, ""); + FMT_ASSERT(beta_minus_1 < 64, ""); + + return ((umul96_lower64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; + } + + static carrier_uint compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return static_cast( + (cache - (cache >> (float_info::significand_bits + 2))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1)); + } + + static carrier_uint compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return static_cast( + (cache + (cache >> (float_info::significand_bits + 1))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1)); + } + + static carrier_uint compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return (static_cast( + cache >> + (64 - float_info::significand_bits - 2 - beta_minus_1)) + + 1) / + 2; + } +}; + +template <> struct cache_accessor { + using carrier_uint = float_info::carrier_uint; + using cache_entry_type = uint128_wrapper; + + static uint128_wrapper get_cached_power(int k) FMT_NOEXCEPT { + FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, + "k is out of range"); + + static constexpr const uint128_wrapper pow10_significands[] = { +#if FMT_USE_FULL_CACHE_DRAGONBOX + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0x9faacf3df73609b1, 0x77b191618c54e9ad}, + {0xc795830d75038c1d, 0xd59df5b9ef6a2418}, + {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e}, + {0x9becce62836ac577, 0x4ee367f9430aec33}, + {0xc2e801fb244576d5, 0x229c41f793cda740}, + {0xf3a20279ed56d48a, 0x6b43527578c11110}, + {0x9845418c345644d6, 0x830a13896b78aaaa}, + {0xbe5691ef416bd60c, 0x23cc986bc656d554}, + {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9}, + {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa}, + {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54}, + {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69}, + {0x91376c36d99995be, 0x23100809b9c21fa2}, + {0xb58547448ffffb2d, 0xabd40a0c2832a78b}, + {0xe2e69915b3fff9f9, 0x16c90c8f323f516d}, + {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4}, + {0xb1442798f49ffb4a, 0x99cd11cfdf41779d}, + {0xdd95317f31c7fa1d, 0x40405643d711d584}, + {0x8a7d3eef7f1cfc52, 0x482835ea666b2573}, + {0xad1c8eab5ee43b66, 0xda3243650005eed0}, + {0xd863b256369d4a40, 0x90bed43e40076a83}, + {0x873e4f75e2224e68, 0x5a7744a6e804a292}, + {0xa90de3535aaae202, 0x711515d0a205cb37}, + {0xd3515c2831559a83, 0x0d5a5b44ca873e04}, + {0x8412d9991ed58091, 0xe858790afe9486c3}, + {0xa5178fff668ae0b6, 0x626e974dbe39a873}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a}, + {0xa139029f6a239f72, 0x1c1fffc1ebc44e81}, + {0xc987434744ac874e, 0xa327ffb266b56221}, + {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9}, + {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa}, + {0xc4ce17b399107c22, 0xcb550fb4384d21d4}, + {0xf6019da07f549b2b, 0x7e2a53a146606a49}, + {0x99c102844f94e0fb, 0x2eda7444cbfc426e}, + {0xc0314325637a1939, 0xfa911155fefb5309}, + {0xf03d93eebc589f88, 0x793555ab7eba27cb}, + {0x96267c7535b763b5, 0x4bc1558b2f3458df}, + {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17}, + {0xea9c227723ee8bcb, 0x465e15a979c1cadd}, + {0x92a1958a7675175f, 0x0bfacd89ec191eca}, + {0xb749faed14125d36, 0xcef980ec671f667c}, + {0xe51c79a85916f484, 0x82b7e12780e7401b}, + {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811}, + {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16}, + {0xdfbdcece67006ac9, 0x67a791e093e1d49b}, + {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1}, + {0xaecc49914078536d, 0x58fae9f773886e19}, + {0xda7f5bf590966848, 0xaf39a475506a899f}, + {0x888f99797a5e012d, 0x6d8406c952429604}, + {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84}, + {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65}, + {0x855c3be0a17fcd26, 0x5cf2eea09a550680}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0xd0601d8efc57b08b, 0xf13b94daf124da27}, + {0x823c12795db6ce57, 0x76c53d08d6b70859}, + {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f}, + {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a}, + {0xfe5d54150b090b02, 0xd3f93b35435d7c4d}, + {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0}, + {0xc6b8e9b0709f109a, 0x359ab6419ca1091c}, + {0xf867241c8cc6d4c0, 0xc30163d203c94b63}, + {0x9b407691d7fc44f8, 0x79e0de63425dcf1e}, + {0xc21094364dfb5636, 0x985915fc12f542e5}, + {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e}, + {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43}, + {0xbd8430bd08277231, 0x50c6ff782a838354}, + {0xece53cec4a314ebd, 0xa4f8bf5635246429}, + {0x940f4613ae5ed136, 0x871b7795e136be9a}, + {0xb913179899f68584, 0x28e2557b59846e40}, + {0xe757dd7ec07426e5, 0x331aeada2fe589d0}, + {0x9096ea6f3848984f, 0x3ff0d2c85def7622}, + {0xb4bca50b065abe63, 0x0fed077a756b53aa}, + {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895}, + {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d}, + {0xb080392cc4349dec, 0xbd8d794d96aacfb4}, + {0xdca04777f541c567, 0xecf0d7a0fc5583a1}, + {0x89e42caaf9491b60, 0xf41686c49db57245}, + {0xac5d37d5b79b6239, 0x311c2875c522ced6}, + {0xd77485cb25823ac7, 0x7d633293366b828c}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xa8530886b54dbdeb, 0xd9f57f830283fdfd}, + {0xd267caa862a12d66, 0xd072df63c324fd7c}, + {0x8380dea93da4bc60, 0x4247cb9e59f71e6e}, + {0xa46116538d0deb78, 0x52d9be85f074e609}, + {0xcd795be870516656, 0x67902e276c921f8c}, + {0x806bd9714632dff6, 0x00ba1cd8a3db53b7}, + {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5}, + {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce}, + {0xfad2a4b13d1b5d6c, 0x796b805720085f82}, + {0x9cc3a6eec6311a63, 0xcbe3303674053bb1}, + {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d}, + {0xf4f1b4d515acb93b, 0xee92fb5515482d45}, + {0x991711052d8bf3c5, 0x751bdd152d4d1c4b}, + {0xbf5cd54678eef0b6, 0xd262d45a78a0635e}, + {0xef340a98172aace4, 0x86fb897116c87c35}, + {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1}, + {0xbae0a846d2195712, 0x8974836059cca10a}, + {0xe998d258869facd7, 0x2bd1a438703fc94c}, + {0x91ff83775423cc06, 0x7b6306a34627ddd0}, + {0xb67f6455292cbf08, 0x1a3bc84c17b1d543}, + {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94}, + {0x8e938662882af53e, 0x547eb47b7282ee9d}, + {0xb23867fb2a35b28d, 0xe99e619a4f23aa44}, + {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5}, + {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05}, + {0xae0b158b4738705e, 0x9624ab50b148d446}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7}, + {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d}, + {0xd47487cc8470652b, 0x7647c32000696720}, + {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074}, + {0xa5fb0a17c777cf09, 0xf468107100525891}, + {0xcf79cc9db955c2cc, 0x7182148d4066eeb5}, + {0x81ac1fe293d599bf, 0xc6f14cd848405531}, + {0xa21727db38cb002f, 0xb8ada00e5a506a7d}, + {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d}, + {0xfd442e4688bd304a, 0x908f4a166d1da664}, + {0x9e4a9cec15763e2e, 0x9a598e4e043287ff}, + {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe}, + {0xf7549530e188c128, 0xd12bee59e68ef47d}, + {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf}, + {0xc13a148e3032d6e7, 0xe36a52363c1faf02}, + {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2}, + {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba}, + {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8}, + {0xebdf661791d60f56, 0x111b495b3464ad22}, + {0x936b9fcebb25c995, 0xcab10dd900beec35}, + {0xb84687c269ef3bfb, 0x3d5d514f40eea743}, + {0xe65829b3046b0afa, 0x0cb4a5a3112a5113}, + {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac}, + {0xb3f4e093db73a093, 0x59ed216765690f57}, + {0xe0f218b8d25088b8, 0x306869c13ec3532d}, + {0x8c974f7383725573, 0x1e414218c73a13fc}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0xdbac6c247d62a583, 0xdf45f746b74abf3a}, + {0x894bc396ce5da772, 0x6b8bba8c328eb784}, + {0xab9eb47c81f5114f, 0x066ea92f3f326565}, + {0xd686619ba27255a2, 0xc80a537b0efefebe}, + {0x8613fd0145877585, 0xbd06742ce95f5f37}, + {0xa798fc4196e952e7, 0x2c48113823b73705}, + {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6}, + {0x82ef85133de648c4, 0x9a984d73dbe722fc}, + {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb}, + {0xcc963fee10b7d1b3, 0x318df905079926a9}, + {0xffbbcfe994e5c61f, 0xfdf17746497f7053}, + {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634}, + {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1}, + {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1}, + {0x9c1661a651213e2d, 0x06bea10ca65c084f}, + {0xc31bfa0fe5698db8, 0x486e494fcff30a63}, + {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb}, + {0x986ddb5c6b3a76b7, 0xf89629465a75e01d}, + {0xbe89523386091465, 0xf6bbb397f1135824}, + {0xee2ba6c0678b597f, 0x746aa07ded582e2d}, + {0x94db483840b717ef, 0xa8c2a44eb4571cdd}, + {0xba121a4650e4ddeb, 0x92f34d62616ce414}, + {0xe896a0d7e51e1566, 0x77b020baf9c81d18}, + {0x915e2486ef32cd60, 0x0ace1474dc1d122f}, + {0xb5b5ada8aaff80b8, 0x0d819992132456bb}, + {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3}, + {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf}, + {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c}, + {0xad4ab7112eb3929d, 0x86c16c98d2c953c7}, + {0xd89d64d57a607744, 0xe871c7bf077ba8b8}, + {0x87625f056c7c4a8b, 0x11471cd764ad4973}, + {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0}, + {0xd389b47879823479, 0x4aff1d108d4ec2c4}, + {0x843610cb4bf160cb, 0xcedf722a585139bb}, + {0xa54394fe1eedb8fe, 0xc2974eb4ee658829}, + {0xce947a3da6a9273e, 0x733d226229feea33}, + {0x811ccc668829b887, 0x0806357d5a3f5260}, + {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8}, + {0xc9bcff6034c13052, 0xfc89b393dd02f0b6}, + {0xfc2c3f3841f17c67, 0xbbac2078d443ace3}, + {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e}, + {0xc5029163f384a931, 0x0a9e795e65d4df12}, + {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6}, + {0x99ea0196163fa42e, 0x504bced1bf8e4e46}, + {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7}, + {0xf07da27a82c37088, 0x5d767327bb4e5a4d}, + {0x964e858c91ba2655, 0x3a6a07f8d510f870}, + {0xbbe226efb628afea, 0x890489f70a55368c}, + {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f}, + {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e}, + {0xb77ada0617e3bbcb, 0x09ce6ebb40173745}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0x8f57fa54c2a9eab6, 0x9fa946824a12232e}, + {0xb32df8e9f3546564, 0x47939822dc96abfa}, + {0xdff9772470297ebd, 0x59787e2b93bc56f8}, + {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b}, + {0xaefae51477a06b03, 0xede622920b6b23f2}, + {0xdab99e59958885c4, 0xe95fab368e45ecee}, + {0x88b402f7fd75539b, 0x11dbcb0218ebb415}, + {0xaae103b5fcd2a881, 0xd652bdc29f26a11a}, + {0xd59944a37c0752a2, 0x4be76d3346f04960}, + {0x857fcae62d8493a5, 0x6f70a4400c562ddc}, + {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953}, + {0xd097ad07a71f26b2, 0x7e2000a41346a7a8}, + {0x825ecc24c873782f, 0x8ed400668c0c28c9}, + {0xa2f67f2dfa90563b, 0x728900802f0f32fb}, + {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba}, + {0xfea126b7d78186bc, 0xe2f610c84987bfa9}, + {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca}, + {0xc6ede63fa05d3143, 0x91503d1c79720dbc}, + {0xf8a95fcf88747d94, 0x75a44c6397ce912b}, + {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb}, + {0xc24452da229b021b, 0xfbe85badce996169}, + {0xf2d56790ab41c2a2, 0xfae27299423fb9c4}, + {0x97c560ba6b0919a5, 0xdccd879fc967d41b}, + {0xbdb6b8e905cb600f, 0x5400e987bbc1c921}, + {0xed246723473e3813, 0x290123e9aab23b69}, + {0x9436c0760c86e30b, 0xf9a0b6720aaf6522}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0xe7958cb87392c2c2, 0xb60b1d1230b20e05}, + {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3}, + {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4}, + {0xe2280b6c20dd5232, 0x25c6da63c38de1b1}, + {0x8d590723948a535f, 0x579c487e5a38ad0f}, + {0xb0af48ec79ace837, 0x2d835a9df0c6d852}, + {0xdcdb1b2798182244, 0xf8e431456cf88e66}, + {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900}, + {0xac8b2d36eed2dac5, 0xe272467e3d222f40}, + {0xd7adf884aa879177, 0x5b0ed81dcc6abb10}, + {0x86ccbb52ea94baea, 0x98e947129fc2b4ea}, + {0xa87fea27a539e9a5, 0x3f2398d747b36225}, + {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae}, + {0x83a3eeeef9153e89, 0x1953cf68300424ad}, + {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8}, + {0xcdb02555653131b6, 0x3792f412cb06794e}, + {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1}, + {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5}, + {0xc8de047564d20a8b, 0xf245825a5a445276}, + {0xfb158592be068d2e, 0xeed6e2f0f0d56713}, + {0x9ced737bb6c4183d, 0x55464dd69685606c}, + {0xc428d05aa4751e4c, 0xaa97e14c3c26b887}, + {0xf53304714d9265df, 0xd53dd99f4b3066a9}, + {0x993fe2c6d07b7fab, 0xe546a8038efe402a}, + {0xbf8fdb78849a5f96, 0xde98520472bdd034}, + {0xef73d256a5c0f77c, 0x963e66858f6d4441}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xbb127c53b17ec159, 0x5560c018580d5d53}, + {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7}, + {0x9226712162ab070d, 0xcab3961304ca70e9}, + {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23}, + {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b}, + {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243}, + {0xb267ed1940f1c61c, 0x55f038b237591ed4}, + {0xdf01e85f912e37a3, 0x6b6c46dec52f6689}, + {0x8b61313bbabce2c6, 0x2323ac4b3b3da016}, + {0xae397d8aa96c1b77, 0xabec975e0a0d081b}, + {0xd9c7dced53c72255, 0x96e7bd358c904a22}, + {0x881cea14545c7575, 0x7e50d64177da2e55}, + {0xaa242499697392d2, 0xdde50bd1d5d0b9ea}, + {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865}, + {0x84ec3c97da624ab4, 0xbd5af13bef0b113f}, + {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f}, + {0xcfb11ead453994ba, 0x67de18eda5814af3}, + {0x81ceb32c4b43fcf4, 0x80eacf948770ced8}, + {0xa2425ff75e14fc31, 0xa1258379a94d028e}, + {0xcad2f7f5359a3b3e, 0x096ee45813a04331}, + {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd}, + {0x9e74d1b791e07e48, 0x775ea264cf55347e}, + {0xc612062576589dda, 0x95364afe032a819e}, + {0xf79687aed3eec551, 0x3a83ddbd83f52205}, + {0x9abe14cd44753b52, 0xc4926a9672793543}, + {0xc16d9a0095928a27, 0x75b7053c0f178294}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0x971da05074da7bee, 0xd3f6fc16ebca5e04}, + {0xbce5086492111aea, 0x88f4bb1ca6bcf585}, + {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6}, + {0x9392ee8e921d5d07, 0x3aff322e62439fd0}, + {0xb877aa3236a4b449, 0x09befeb9fad487c3}, + {0xe69594bec44de15b, 0x4c2ebe687989a9b4}, + {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11}, + {0xb424dc35095cd80f, 0x538484c19ef38c95}, + {0xe12e13424bb40e13, 0x2865a5f206b06fba}, + {0x8cbccc096f5088cb, 0xf93f87b7442e45d4}, + {0xafebff0bcb24aafe, 0xf78f69a51539d749}, + {0xdbe6fecebdedd5be, 0xb573440e5a884d1c}, + {0x89705f4136b4a597, 0x31680a88f8953031}, + {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e}, + {0xd6bf94d5e57a42bc, 0x3d32907604691b4d}, + {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110}, + {0xa7c5ac471b478423, 0x0fcf80dc33721d54}, + {0xd1b71758e219652b, 0xd3c36113404ea4a9}, + {0x83126e978d4fdf3b, 0x645a1cac083126ea}, + {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4}, + {0xcccccccccccccccc, 0xcccccccccccccccd}, + {0x8000000000000000, 0x0000000000000000}, + {0xa000000000000000, 0x0000000000000000}, + {0xc800000000000000, 0x0000000000000000}, + {0xfa00000000000000, 0x0000000000000000}, + {0x9c40000000000000, 0x0000000000000000}, + {0xc350000000000000, 0x0000000000000000}, + {0xf424000000000000, 0x0000000000000000}, + {0x9896800000000000, 0x0000000000000000}, + {0xbebc200000000000, 0x0000000000000000}, + {0xee6b280000000000, 0x0000000000000000}, + {0x9502f90000000000, 0x0000000000000000}, + {0xba43b74000000000, 0x0000000000000000}, + {0xe8d4a51000000000, 0x0000000000000000}, + {0x9184e72a00000000, 0x0000000000000000}, + {0xb5e620f480000000, 0x0000000000000000}, + {0xe35fa931a0000000, 0x0000000000000000}, + {0x8e1bc9bf04000000, 0x0000000000000000}, + {0xb1a2bc2ec5000000, 0x0000000000000000}, + {0xde0b6b3a76400000, 0x0000000000000000}, + {0x8ac7230489e80000, 0x0000000000000000}, + {0xad78ebc5ac620000, 0x0000000000000000}, + {0xd8d726b7177a8000, 0x0000000000000000}, + {0x878678326eac9000, 0x0000000000000000}, + {0xa968163f0a57b400, 0x0000000000000000}, + {0xd3c21bcecceda100, 0x0000000000000000}, + {0x84595161401484a0, 0x0000000000000000}, + {0xa56fa5b99019a5c8, 0x0000000000000000}, + {0xcecb8f27f4200f3a, 0x0000000000000000}, + {0x813f3978f8940984, 0x4000000000000000}, + {0xa18f07d736b90be5, 0x5000000000000000}, + {0xc9f2c9cd04674ede, 0xa400000000000000}, + {0xfc6f7c4045812296, 0x4d00000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xc5371912364ce305, 0x6c28000000000000}, + {0xf684df56c3e01bc6, 0xc732000000000000}, + {0x9a130b963a6c115c, 0x3c7f400000000000}, + {0xc097ce7bc90715b3, 0x4b9f100000000000}, + {0xf0bdc21abb48db20, 0x1e86d40000000000}, + {0x96769950b50d88f4, 0x1314448000000000}, + {0xbc143fa4e250eb31, 0x17d955a000000000}, + {0xeb194f8e1ae525fd, 0x5dcfab0800000000}, + {0x92efd1b8d0cf37be, 0x5aa1cae500000000}, + {0xb7abc627050305ad, 0xf14a3d9e40000000}, + {0xe596b7b0c643c719, 0x6d9ccd05d0000000}, + {0x8f7e32ce7bea5c6f, 0xe4820023a2000000}, + {0xb35dbf821ae4f38b, 0xdda2802c8a800000}, + {0xe0352f62a19e306e, 0xd50b2037ad200000}, + {0x8c213d9da502de45, 0x4526f422cc340000}, + {0xaf298d050e4395d6, 0x9670b12b7f410000}, + {0xdaf3f04651d47b4c, 0x3c0cdd765f114000}, + {0x88d8762bf324cd0f, 0xa5880a69fb6ac800}, + {0xab0e93b6efee0053, 0x8eea0d047a457a00}, + {0xd5d238a4abe98068, 0x72a4904598d6d880}, + {0x85a36366eb71f041, 0x47a6da2b7f864750}, + {0xa70c3c40a64e6c51, 0x999090b65f67d924}, + {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d}, + {0x82818f1281ed449f, 0xbff8f10e7a8921a4}, + {0xa321f2d7226895c7, 0xaff72d52192b6a0d}, + {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490}, + {0xfee50b7025c36a08, 0x02f236d04753d5b4}, + {0x9f4f2726179a2245, 0x01d762422c946590}, + {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5}, + {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2}, + {0x9b934c3b330c8577, 0x63cc55f49f88eb2f}, + {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb}, + {0xf316271c7fc3908a, 0x8bef464e3945ef7a}, + {0x97edd871cfda3a56, 0x97758bf0e3cbb5ac}, + {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317}, + {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd}, + {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a}, + {0xb975d6b6ee39e436, 0xb3e2fd538e122b44}, + {0xe7d34c64a9c85d44, 0x60dbbca87196b616}, + {0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd}, + {0xb51d13aea4a488dd, 0x6babab6398bdbe41}, + {0xe264589a4dcdab14, 0xc696963c7eed2dd1}, + {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2}, + {0xb0de65388cc8ada8, 0x3b25a55f43294bcb}, + {0xdd15fe86affad912, 0x49ef0eb713f39ebe}, + {0x8a2dbf142dfcc7ab, 0x6e3569326c784337}, + {0xacb92ed9397bf996, 0x49c2c37f07965404}, + {0xd7e77a8f87daf7fb, 0xdc33745ec97be906}, + {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3}, + {0xa8acd7c0222311bc, 0xc40832ea0d68ce0c}, + {0xd2d80db02aabd62b, 0xf50a3fa490c30190}, + {0x83c7088e1aab65db, 0x792667c6da79e0fa}, + {0xa4b8cab1a1563f52, 0x577001b891185938}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, + {0x80b05e5ac60b6178, 0x544f8158315b05b4}, + {0xa0dc75f1778e39d6, 0x696361ae3db1c721}, + {0xc913936dd571c84c, 0x03bc3a19cd1e38e9}, + {0xfb5878494ace3a5f, 0x04ab48a04065c723}, + {0x9d174b2dcec0e47b, 0x62eb0d64283f9c76}, + {0xc45d1df942711d9a, 0x3ba5d0bd324f8394}, + {0xf5746577930d6500, 0xca8f44ec7ee36479}, + {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb}, + {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e}, + {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e}, + {0x95d04aee3b80ece5, 0xbba1f1d158724a12}, + {0xbb445da9ca61281f, 0x2a8a6e45ae8edc97}, + {0xea1575143cf97226, 0xf52d09d71a3293bd}, + {0x924d692ca61be758, 0x593c2626705f9c56}, + {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c}, + {0xe498f455c38b997a, 0x0b6dfb9c0f956447}, + {0x8edf98b59a373fec, 0x4724bd4189bd5eac}, + {0xb2977ee300c50fe7, 0x58edec91ec2cb657}, + {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed}, + {0x8b865b215899f46c, 0xbd79e0d20082ee74}, + {0xae67f1e9aec07187, 0xecd8590680a3aa11}, + {0xda01ee641a708de9, 0xe80e6f4820cc9495}, + {0x884134fe908658b2, 0x3109058d147fdcdd}, + {0xaa51823e34a7eede, 0xbd4b46f0599fd415}, + {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a}, + {0x850fadc09923329e, 0x03e2cf6bc604ddb0}, + {0xa6539930bf6bff45, 0x84db8346b786151c}, + {0xcfe87f7cef46ff16, 0xe612641865679a63}, + {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e}, + {0xa26da3999aef7749, 0xe3be5e330f38f09d}, + {0xcb090c8001ab551c, 0x5cadf5bfd3072cc5}, + {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6}, + {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa}, + {0xc646d63501a1511d, 0xb281e1fd541501b8}, + {0xf7d88bc24209a565, 0x1f225a7ca91a4226}, + {0x9ae757596946075f, 0x3375788de9b06958}, + {0xc1a12d2fc3978937, 0x0052d6b1641c83ae}, + {0xf209787bb47d6b84, 0xc0678c5dbd23a49a}, + {0x9745eb4d50ce6332, 0xf840b7ba963646e0}, + {0xbd176620a501fbff, 0xb650e5a93bc3d898}, + {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe}, + {0x93ba47c980e98cdf, 0xc66f336c36b10137}, + {0xb8a8d9bbe123f017, 0xb80b0047445d4184}, + {0xe6d3102ad96cec1d, 0xa60dc059157491e5}, + {0x9043ea1ac7e41392, 0x87c89837ad68db2f}, + {0xb454e4a179dd1877, 0x29babe4598c311fb}, + {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a}, + {0x8ce2529e2734bb1d, 0x1899e4a65f58660c}, + {0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f}, + {0xdc21a1171d42645d, 0x76707543f4fa1f73}, + {0x899504ae72497eba, 0x6a06494a791c53a8}, + {0xabfa45da0edbde69, 0x0487db9d17636892}, + {0xd6f8d7509292d603, 0x45a9d2845d3c42b6}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, + {0xa7f26836f282b732, 0x8e6cac7768d7141e}, + {0xd1ef0244af2364ff, 0x3207d795430cd926}, + {0x8335616aed761f1f, 0x7f44e6bd49e807b8}, + {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6}, + {0xcd036837130890a1, 0x36dba887c37a8c0f}, + {0x802221226be55a64, 0xc2494954da2c9789}, + {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c}, + {0xc83553c5c8965d3d, 0x6f92829494e5acc7}, + {0xfa42a8b73abbf48c, 0xcb772339ba1f17f9}, + {0x9c69a97284b578d7, 0xff2a760414536efb}, + {0xc38413cf25e2d70d, 0xfef5138519684aba}, + {0xf46518c2ef5b8cd1, 0x7eb258665fc25d69}, + {0x98bf2f79d5993802, 0xef2f773ffbd97a61}, + {0xbeeefb584aff8603, 0xaafb550ffacfd8fa}, + {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38}, + {0x952ab45cfa97a0b2, 0xdd945a747bf26183}, + {0xba756174393d88df, 0x94f971119aeef9e4}, + {0xe912b9d1478ceb17, 0x7a37cd5601aab85d}, + {0x91abb422ccb812ee, 0xac62e055c10ab33a}, + {0xb616a12b7fe617aa, 0x577b986b314d6009}, + {0xe39c49765fdf9d94, 0xed5a7e85fda0b80b}, + {0x8e41ade9fbebc27d, 0x14588f13be847307}, + {0xb1d219647ae6b31c, 0x596eb2d8ae258fc8}, + {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb}, + {0x8aec23d680043bee, 0x25de7bb9480d5854}, + {0xada72ccc20054ae9, 0xaf561aa79a10ae6a}, + {0xd910f7ff28069da4, 0x1b2ba1518094da04}, + {0x87aa9aff79042286, 0x90fb44d2f05d0842}, + {0xa99541bf57452b28, 0x353a1607ac744a53}, + {0xd3fa922f2d1675f2, 0x42889b8997915ce8}, + {0x847c9b5d7c2e09b7, 0x69956135febada11}, + {0xa59bc234db398c25, 0x43fab9837e699095}, + {0xcf02b2c21207ef2e, 0x94f967e45e03f4bb}, + {0x8161afb94b44f57d, 0x1d1be0eebac278f5}, + {0xa1ba1ba79e1632dc, 0x6462d92a69731732}, + {0xca28a291859bbf93, 0x7d7b8f7503cfdcfe}, + {0xfcb2cb35e702af78, 0x5cda735244c3d43e}, + {0x9defbf01b061adab, 0x3a0888136afa64a7}, + {0xc56baec21c7a1916, 0x088aaa1845b8fdd0}, + {0xf6c69a72a3989f5b, 0x8aad549e57273d45}, + {0x9a3c2087a63f6399, 0x36ac54e2f678864b}, + {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd}, + {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5}, + {0x969eb7c47859e743, 0x9f644ae5a4b1b325}, + {0xbc4665b596706114, 0x873d5d9f0dde1fee}, + {0xeb57ff22fc0c7959, 0xa90cb506d155a7ea}, + {0x9316ff75dd87cbd8, 0x09a7f12442d588f2}, + {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb2f}, + {0xe5d3ef282a242e81, 0x8f1668c8a86da5fa}, + {0x8fa475791a569d10, 0xf96e017d694487bc}, + {0xb38d92d760ec4455, 0x37c981dcc395a9ac}, + {0xe070f78d3927556a, 0x85bbe253f47b1417}, + {0x8c469ab843b89562, 0x93956d7478ccec8e}, + {0xaf58416654a6babb, 0x387ac8d1970027b2}, + {0xdb2e51bfe9d0696a, 0x06997b05fcc0319e}, + {0x88fcf317f22241e2, 0x441fece3bdf81f03}, + {0xab3c2fddeeaad25a, 0xd527e81cad7626c3}, + {0xd60b3bd56a5586f1, 0x8a71e223d8d3b074}, + {0x85c7056562757456, 0xf6872d5667844e49}, + {0xa738c6bebb12d16c, 0xb428f8ac016561db}, + {0xd106f86e69d785c7, 0xe13336d701beba52}, + {0x82a45b450226b39c, 0xecc0024661173473}, + {0xa34d721642b06084, 0x27f002d7f95d0190}, + {0xcc20ce9bd35c78a5, 0x31ec038df7b441f4}, + {0xff290242c83396ce, 0x7e67047175a15271}, + {0x9f79a169bd203e41, 0x0f0062c6e984d386}, + {0xc75809c42c684dd1, 0x52c07b78a3e60868}, + {0xf92e0c3537826145, 0xa7709a56ccdf8a82}, + {0x9bbcc7a142b17ccb, 0x88a66076400bb691}, + {0xc2abf989935ddbfe, 0x6acff893d00ea435}, + {0xf356f7ebf83552fe, 0x0583f6b8c4124d43}, + {0x98165af37b2153de, 0xc3727a337a8b704a}, + {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c}, + {0xeda2ee1c7064130c, 0x1162def06f79df73}, + {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8}, + {0xb9a74a0637ce2ee1, 0x6d953e2bd7173692}, + {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437}, + {0x910ab1d4db9914a0, 0x1d9c9892400a22a2}, + {0xb54d5e4a127f59c8, 0x2503beb6d00cab4b}, + {0xe2a0b5dc971f303a, 0x2e44ae64840fd61d}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, + {0xb10d8e1456105dad, 0x7425a83e872c5f47}, + {0xdd50f1996b947518, 0xd12f124e28f77719}, + {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f}, + {0xace73cbfdc0bfb7b, 0x636cc64d1001550b}, + {0xd8210befd30efa5a, 0x3c47f7e05401aa4e}, + {0x8714a775e3e95c78, 0x65acfaec34810a71}, + {0xa8d9d1535ce3b396, 0x7f1839a741a14d0d}, + {0xd31045a8341ca07c, 0x1ede48111209a050}, + {0x83ea2b892091e44d, 0x934aed0aab460432}, + {0xa4e4b66b68b65d60, 0xf81da84d5617853f}, + {0xce1de40642e3f4b9, 0x36251260ab9d668e}, + {0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019}, + {0xa1075a24e4421730, 0xb24cf65b8612f81f}, + {0xc94930ae1d529cfc, 0xdee033f26797b627}, + {0xfb9b7cd9a4a7443c, 0x169840ef017da3b1}, + {0x9d412e0806e88aa5, 0x8e1f289560ee864e}, + {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2}, + {0xf5b5d7ec8acb58a2, 0xae10af696774b1db}, + {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29}, + {0xbff610b0cc6edd3f, 0x17fd090a58d32af3}, + {0xeff394dcff8a948e, 0xddfc4b4cef07f5b0}, + {0x95f83d0a1fb69cd9, 0x4abdaf101564f98e}, + {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1}, + {0xea53df5fd18d5513, 0x84c86189216dc5ed}, + {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4}, + {0xb7118682dbb66a77, 0x3fbc8c33221dc2a1}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, + {0x8f05b1163ba6832d, 0x29cb4d87f2a7400e}, + {0xb2c71d5bca9023f8, 0x743e20e9ef511012}, + {0xdf78e4b2bd342cf6, 0x914da9246b255416}, + {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e}, + {0xae9672aba3d0c320, 0xa184ac2473b529b1}, + {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e}, + {0x8865899617fb1871, 0x7e2fa67c7a658892}, + {0xaa7eebfb9df9de8d, 0xddbb901b98feeab7}, + {0xd51ea6fa85785631, 0x552a74227f3ea565}, + {0x8533285c936b35de, 0xd53a88958f87275f}, + {0xa67ff273b8460356, 0x8a892abaf368f137}, + {0xd01fef10a657842c, 0x2d2b7569b0432d85}, + {0x8213f56a67f6b29b, 0x9c3b29620e29fc73}, + {0xa298f2c501f45f42, 0x8349f3ba91b47b8f}, + {0xcb3f2f7642717713, 0x241c70a936219a73}, + {0xfe0efb53d30dd4d7, 0xed238cd383aa0110}, + {0x9ec95d1463e8a506, 0xf4363804324a40aa}, + {0xc67bb4597ce2ce48, 0xb143c6053edcd0d5}, + {0xf81aa16fdc1b81da, 0xdd94b7868e94050a}, + {0x9b10a4e5e9913128, 0xca7cf2b4191c8326}, + {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0}, + {0xf24a01a73cf2dccf, 0xbc633b39673c8cec}, + {0x976e41088617ca01, 0xd5be0503e085d813}, + {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18}, + {0xec9c459d51852ba2, 0xddf8e7d60ed1219e}, + {0x93e1ab8252f33b45, 0xcabb90e5c942b503}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, + {0xe7109bfba19c0c9d, 0x0cc512670a783ad4}, + {0x906a617d450187e2, 0x27fb2b80668b24c5}, + {0xb484f9dc9641e9da, 0xb1f9f660802dedf6}, + {0xe1a63853bbd26451, 0x5e7873f8a0396973}, + {0x8d07e33455637eb2, 0xdb0b487b6423e1e8}, + {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62}, + {0xdc5c5301c56b75f7, 0x7641a140cc7810fb}, + {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d}, + {0xac2820d9623bf429, 0x546345fa9fbdcd44}, + {0xd732290fbacaf133, 0xa97c177947ad4095}, + {0x867f59a9d4bed6c0, 0x49ed8eabcccc485d}, + {0xa81f301449ee8c70, 0x5c68f256bfff5a74}, + {0xd226fc195c6a2f8c, 0x73832eec6fff3111}, + {0x83585d8fd9c25db7, 0xc831fd53c5ff7eab}, + {0xa42e74f3d032f525, 0xba3e7ca8b77f5e55}, + {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb}, + {0x80444b5e7aa7cf85, 0x7980d163cf5b81b3}, + {0xa0555e361951c366, 0xd7e105bcc332621f}, + {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7}, + {0xfa856334878fc150, 0xb14f98f6f0feb951}, + {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3}, + {0xc3b8358109e84f07, 0x0a862f80ec4700c8}, + {0xf4a642e14c6262c8, 0xcd27bb612758c0fa}, + {0x98e7e9cccfbd7dbd, 0x8038d51cb897789c}, + {0xbf21e44003acdd2c, 0xe0470a63e6bd56c3}, + {0xeeea5d5004981478, 0x1858ccfce06cac74}, + {0x95527a5202df0ccb, 0x0f37801e0c43ebc8}, + {0xbaa718e68396cffd, 0xd30560258f54e6ba}, + {0xe950df20247c83fd, 0x47c6b82ef32a2069}, + {0x91d28b7416cdd27e, 0x4cdc331d57fa5441}, + {0xb6472e511c81471d, 0xe0133fe4adf8e952}, + {0xe3d8f9e563a198e5, 0x58180fddd97723a6}, + {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648}, + {0xb201833b35d63f73, 0x2cd2cc6551e513da}, + {0xde81e40a034bcf4f, 0xf8077f7ea65e58d1}, + {0x8b112e86420f6191, 0xfb04afaf27faf782}, + {0xadd57a27d29339f6, 0x79c5db9af1f9b563}, + {0xd94ad8b1c7380874, 0x18375281ae7822bc}, + {0x87cec76f1c830548, 0x8f2293910d0b15b5}, + {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb22}, + {0xd433179d9c8cb841, 0x5fa60692a46151eb}, + {0x849feec281d7f328, 0xdbc7c41ba6bcd333}, + {0xa5c7ea73224deff3, 0x12b9b522906c0800}, + {0xcf39e50feae16bef, 0xd768226b34870a00}, + {0x81842f29f2cce375, 0xe6a1158300d46640}, + {0xa1e53af46f801c53, 0x60495ae3c1097fd0}, + {0xca5e89b18b602368, 0x385bb19cb14bdfc4}, + {0xfcf62c1dee382c42, 0x46729e03dd9ed7b5}, + {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d1}, + {0xc5a05277621be293, 0xc7098b7305241885}, + { 0xf70867153aa2db38, + 0xb8cbee4fc66d1ea7 } +#else + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0xc350000000000000, 0x0000000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xfee50b7025c36a08, 0x02f236d04753d5b4}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, + {0xa6539930bf6bff45, 0x84db8346b786151c}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, + {0xd910f7ff28069da4, 0x1b2ba1518094da04}, + {0xaf58416654a6babb, 0x387ac8d1970027b2}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, + { 0x95527a5202df0ccb, + 0x0f37801e0c43ebc8 } +#endif + }; + +#if FMT_USE_FULL_CACHE_DRAGONBOX + return pow10_significands[k - float_info::min_k]; +#else + static constexpr const uint64_t powers_of_5_64[] = { + 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, + 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, + 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, + 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd, + 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9, + 0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, + 0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631, + 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, + 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; + + static constexpr const uint32_t pow10_recovery_errors[] = { + 0x50001400, 0x54044100, 0x54014555, 0x55954415, 0x54115555, 0x00000001, + 0x50000000, 0x00104000, 0x54010004, 0x05004001, 0x55555544, 0x41545555, + 0x54040551, 0x15445545, 0x51555514, 0x10000015, 0x00101100, 0x01100015, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04450514, 0x45414110, + 0x55555145, 0x50544050, 0x15040155, 0x11054140, 0x50111514, 0x11451454, + 0x00400541, 0x00000000, 0x55555450, 0x10056551, 0x10054011, 0x55551014, + 0x69514555, 0x05151109, 0x00155555}; + + static const int compression_ratio = 27; + + // Compute base index. + int cache_index = (k - float_info::min_k) / compression_ratio; + int kb = cache_index * compression_ratio + float_info::min_k; + int offset = k - kb; + + // Get base cache. + uint128_wrapper base_cache = pow10_significands[cache_index]; + if (offset == 0) return base_cache; + + // Compute the required amount of bit-shift. + int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset; + FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected"); + + // Try to recover the real cache. + uint64_t pow5 = powers_of_5_64[offset]; + uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5); + uint128_wrapper middle_low = + umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5); + + recovered_cache += middle_low.high(); + + uint64_t high_to_middle = recovered_cache.high() << (64 - alpha); + uint64_t middle_to_low = recovered_cache.low() << (64 - alpha); + + recovered_cache = + uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle, + ((middle_low.low() >> alpha) | middle_to_low)}; + + if (kb < 0) recovered_cache += 1; + + // Get error. + int error_idx = (k - float_info::min_k) / 16; + uint32_t error = (pow10_recovery_errors[error_idx] >> + ((k - float_info::min_k) % 16) * 2) & + 0x3; + + // Add the error back. + FMT_ASSERT(recovered_cache.low() + error >= recovered_cache.low(), ""); + return {recovered_cache.high(), recovered_cache.low() + error}; +#endif + } + + static carrier_uint compute_mul(carrier_uint u, + const cache_entry_type& cache) FMT_NOEXCEPT { + return umul192_upper64(u, cache); + } + + static uint32_t compute_delta(cache_entry_type const& cache, + int beta_minus_1) FMT_NOEXCEPT { + return static_cast(cache.high() >> (64 - 1 - beta_minus_1)); + } + + static bool compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta_minus_1) FMT_NOEXCEPT { + FMT_ASSERT(beta_minus_1 >= 1, ""); + FMT_ASSERT(beta_minus_1 < 64, ""); + + return ((umul192_middle64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; + } + + static carrier_uint compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return (cache.high() - + (cache.high() >> (float_info::significand_bits + 2))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1); + } + + static carrier_uint compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return (cache.high() + + (cache.high() >> (float_info::significand_bits + 1))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1); + } + + static carrier_uint compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return ((cache.high() >> + (64 - float_info::significand_bits - 2 - beta_minus_1)) + + 1) / + 2; + } +}; + +// Various integer checks +template +bool is_left_endpoint_integer_shorter_interval(int exponent) FMT_NOEXCEPT { + return exponent >= + float_info< + T>::case_shorter_interval_left_endpoint_lower_threshold && + exponent <= + float_info::case_shorter_interval_left_endpoint_upper_threshold; +} +template +bool is_endpoint_integer(typename float_info::carrier_uint two_f, + int exponent, int minus_k) FMT_NOEXCEPT { + if (exponent < float_info::case_fc_pm_half_lower_threshold) return false; + // For k >= 0. + if (exponent <= float_info::case_fc_pm_half_upper_threshold) return true; + // For k < 0. + if (exponent > float_info::divisibility_check_by_5_threshold) return false; + return divisible_by_power_of_5(two_f, minus_k); +} + +template +bool is_center_integer(typename float_info::carrier_uint two_f, int exponent, + int minus_k) FMT_NOEXCEPT { + // Exponent for 5 is negative. + if (exponent > float_info::divisibility_check_by_5_threshold) return false; + if (exponent > float_info::case_fc_upper_threshold) + return divisible_by_power_of_5(two_f, minus_k); + // Both exponents are nonnegative. + if (exponent >= float_info::case_fc_lower_threshold) return true; + // Exponent for 2 is negative. + return divisible_by_power_of_2(two_f, minus_k - exponent + 1); +} + +// Remove trailing zeros from n and return the number of zeros removed (float) +FMT_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT { +#ifdef FMT_BUILTIN_CTZ + int t = FMT_BUILTIN_CTZ(n); +#else + int t = ctz(n); +#endif + if (t > float_info::max_trailing_zeros) + t = float_info::max_trailing_zeros; + + const uint32_t mod_inv1 = 0xcccccccd; + const uint32_t max_quotient1 = 0x33333333; + const uint32_t mod_inv2 = 0xc28f5c29; + const uint32_t max_quotient2 = 0x0a3d70a3; + + int s = 0; + for (; s < t - 1; s += 2) { + if (n * mod_inv2 > max_quotient2) break; + n *= mod_inv2; + } + if (s < t && n * mod_inv1 <= max_quotient1) { + n *= mod_inv1; + ++s; + } + n >>= s; + return s; +} + +// Removes trailing zeros and returns the number of zeros removed (double) +FMT_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { +#ifdef FMT_BUILTIN_CTZLL + int t = FMT_BUILTIN_CTZLL(n); +#else + int t = ctzll(n); +#endif + if (t > float_info::max_trailing_zeros) + t = float_info::max_trailing_zeros; + // Divide by 10^8 and reduce to 32-bits + // Since ret_value.significand <= (2^64 - 1) / 1000 < 10^17, + // both of the quotient and the r should fit in 32-bits + + const uint32_t mod_inv1 = 0xcccccccd; + const uint32_t max_quotient1 = 0x33333333; + const uint64_t mod_inv8 = 0xc767074b22e90e21; + const uint64_t max_quotient8 = 0x00002af31dc46118; + + // If the number is divisible by 1'0000'0000, work with the quotient + if (t >= 8) { + auto quotient_candidate = n * mod_inv8; + + if (quotient_candidate <= max_quotient8) { + auto quotient = static_cast(quotient_candidate >> 8); + + int s = 8; + for (; s < t; ++s) { + if (quotient * mod_inv1 > max_quotient1) break; + quotient *= mod_inv1; + } + quotient >>= (s - 8); + n = quotient; + return s; + } + } + + // Otherwise, work with the remainder + auto quotient = static_cast(n / 100000000); + auto remainder = static_cast(n - 100000000 * quotient); + + if (t == 0 || remainder * mod_inv1 > max_quotient1) { + return 0; + } + remainder *= mod_inv1; + + if (t == 1 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 1) + quotient * 10000000ull; + return 1; + } + remainder *= mod_inv1; + + if (t == 2 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 2) + quotient * 1000000ull; + return 2; + } + remainder *= mod_inv1; + + if (t == 3 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 3) + quotient * 100000ull; + return 3; + } + remainder *= mod_inv1; + + if (t == 4 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 4) + quotient * 10000ull; + return 4; + } + remainder *= mod_inv1; + + if (t == 5 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 5) + quotient * 1000ull; + return 5; + } + remainder *= mod_inv1; + + if (t == 6 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 6) + quotient * 100ull; + return 6; + } + remainder *= mod_inv1; + + n = (remainder >> 7) + quotient * 10ull; + return 7; +} + +// The main algorithm for shorter interval case +template +FMT_INLINE decimal_fp shorter_interval_case(int exponent) FMT_NOEXCEPT { + decimal_fp ret_value; + // Compute k and beta + const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); + const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); + + // Compute xi and zi + using cache_entry_type = typename cache_accessor::cache_entry_type; + const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); + + auto xi = cache_accessor::compute_left_endpoint_for_shorter_interval_case( + cache, beta_minus_1); + auto zi = cache_accessor::compute_right_endpoint_for_shorter_interval_case( + cache, beta_minus_1); + + // If the left endpoint is not an integer, increase it + if (!is_left_endpoint_integer_shorter_interval(exponent)) ++xi; + + // Try bigger divisor + ret_value.significand = zi / 10; + + // If succeed, remove trailing zeros if necessary and return + if (ret_value.significand * 10 >= xi) { + ret_value.exponent = minus_k + 1; + ret_value.exponent += remove_trailing_zeros(ret_value.significand); + return ret_value; + } + + // Otherwise, compute the round-up of y + ret_value.significand = + cache_accessor::compute_round_up_for_shorter_interval_case( + cache, beta_minus_1); + ret_value.exponent = minus_k; + + // When tie occurs, choose one of them according to the rule + if (exponent >= float_info::shorter_interval_tie_lower_threshold && + exponent <= float_info::shorter_interval_tie_upper_threshold) { + ret_value.significand = ret_value.significand % 2 == 0 + ? ret_value.significand + : ret_value.significand - 1; + } else if (ret_value.significand < xi) { + ++ret_value.significand; + } + return ret_value; +} + +template decimal_fp to_decimal(T x) FMT_NOEXCEPT { + // Step 1: integer promotion & Schubfach multiplier calculation. + + using carrier_uint = typename float_info::carrier_uint; + using cache_entry_type = typename cache_accessor::cache_entry_type; + auto br = bit_cast(x); + + // Extract significand bits and exponent bits. + const carrier_uint significand_mask = + (static_cast(1) << float_info::significand_bits) - 1; + carrier_uint significand = (br & significand_mask); + int exponent = static_cast((br & exponent_mask()) >> + float_info::significand_bits); + + if (exponent != 0) { // Check if normal. + exponent += float_info::exponent_bias - float_info::significand_bits; + + // Shorter interval case; proceed like Schubfach. + if (significand == 0) return shorter_interval_case(exponent); + + significand |= + (static_cast(1) << float_info::significand_bits); + } else { + // Subnormal case; the interval is always regular. + if (significand == 0) return {0, 0}; + exponent = float_info::min_exponent - float_info::significand_bits; + } + + const bool include_left_endpoint = (significand % 2 == 0); + const bool include_right_endpoint = include_left_endpoint; + + // Compute k and beta. + const int minus_k = floor_log10_pow2(exponent) - float_info::kappa; + const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); + const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); + + // Compute zi and deltai + // 10^kappa <= deltai < 10^(kappa + 1) + const uint32_t deltai = cache_accessor::compute_delta(cache, beta_minus_1); + const carrier_uint two_fc = significand << 1; + const carrier_uint two_fr = two_fc | 1; + const carrier_uint zi = + cache_accessor::compute_mul(two_fr << beta_minus_1, cache); + + // Step 2: Try larger divisor; remove trailing zeros if necessary + + // Using an upper bound on zi, we might be able to optimize the division + // better than the compiler; we are computing zi / big_divisor here + decimal_fp ret_value; + ret_value.significand = divide_by_10_to_kappa_plus_1(zi); + uint32_t r = static_cast(zi - float_info::big_divisor * + ret_value.significand); + + if (r > deltai) { + goto small_divisor_case_label; + } else if (r < deltai) { + // Exclude the right endpoint if necessary + if (r == 0 && !include_right_endpoint && + is_endpoint_integer(two_fr, exponent, minus_k)) { + --ret_value.significand; + r = float_info::big_divisor; + goto small_divisor_case_label; + } + } else { + // r == deltai; compare fractional parts + // Check conditions in the order different from the paper + // to take advantage of short-circuiting + const carrier_uint two_fl = two_fc - 1; + if ((!include_left_endpoint || + !is_endpoint_integer(two_fl, exponent, minus_k)) && + !cache_accessor::compute_mul_parity(two_fl, cache, beta_minus_1)) { + goto small_divisor_case_label; + } + } + ret_value.exponent = minus_k + float_info::kappa + 1; + + // We may need to remove trailing zeros + ret_value.exponent += remove_trailing_zeros(ret_value.significand); + return ret_value; + + // Step 3: Find the significand with the smaller divisor + +small_divisor_case_label: + ret_value.significand *= 10; + ret_value.exponent = minus_k + float_info::kappa; + + const uint32_t mask = (1u << float_info::kappa) - 1; + auto dist = r - (deltai / 2) + (float_info::small_divisor / 2); + + // Is dist divisible by 2^kappa? + if ((dist & mask) == 0) { + const bool approx_y_parity = + ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; + dist >>= float_info::kappa; + + // Is dist divisible by 5^kappa? + if (check_divisibility_and_divide_by_pow5::kappa>(dist)) { + ret_value.significand += dist; + + // Check z^(f) >= epsilon^(f) + // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, + // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f) + // Since there are only 2 possibilities, we only need to care about the + // parity. Also, zi and r should have the same parity since the divisor + // is an even number + if (cache_accessor::compute_mul_parity(two_fc, cache, beta_minus_1) != + approx_y_parity) { + --ret_value.significand; + } else { + // If z^(f) >= epsilon^(f), we might have a tie + // when z^(f) == epsilon^(f), or equivalently, when y is an integer + if (is_center_integer(two_fc, exponent, minus_k)) { + ret_value.significand = ret_value.significand % 2 == 0 + ? ret_value.significand + : ret_value.significand - 1; + } + } + } + // Is dist not divisible by 5^kappa? + else { + ret_value.significand += dist; + } + } + // Is dist not divisible by 2^kappa? + else { + // Since we know dist is small, we might be able to optimize the division + // better than the compiler; we are computing dist / small_divisor here + ret_value.significand += + small_division_by_pow10::kappa>(dist); + } + return ret_value; +} +} // namespace dragonbox + +// Formats a floating-point number using a variation of the Fixed-Precision +// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White: +// https://fmt.dev/papers/p372-steele.pdf. +FMT_CONSTEXPR20 inline void format_dragon(fp value, bool is_predecessor_closer, + int num_digits, buffer& buf, + int& exp10) { + bigint numerator; // 2 * R in (FPP)^2. + bigint denominator; // 2 * S in (FPP)^2. + // lower and upper are differences between value and corresponding boundaries. + bigint lower; // (M^- in (FPP)^2). + bigint upper_store; // upper's value if different from lower. + bigint* upper = nullptr; // (M^+ in (FPP)^2). + // Shift numerator and denominator by an extra bit or two (if lower boundary + // is closer) to make lower and upper integers. This eliminates multiplication + // by 2 during later computations. + int shift = is_predecessor_closer ? 2 : 1; + uint64_t significand = value.f << shift; + if (value.e >= 0) { + numerator.assign(significand); + numerator <<= value.e; + lower.assign(1); + lower <<= value.e; + if (shift != 1) { + upper_store.assign(1); + upper_store <<= value.e + 1; + upper = &upper_store; + } + denominator.assign_pow10(exp10); + denominator <<= shift; + } else if (exp10 < 0) { + numerator.assign_pow10(-exp10); + lower.assign(numerator); + if (shift != 1) { + upper_store.assign(numerator); + upper_store <<= 1; + upper = &upper_store; + } + numerator *= significand; + denominator.assign(1); + denominator <<= shift - value.e; + } else { + numerator.assign(significand); + denominator.assign_pow10(exp10); + denominator <<= shift - value.e; + lower.assign(1); + if (shift != 1) { + upper_store.assign(1ULL << 1); + upper = &upper_store; + } + } + // Invariant: value == (numerator / denominator) * pow(10, exp10). + if (num_digits < 0) { + // Generate the shortest representation. + if (!upper) upper = &lower; + bool even = (value.f & 1) == 0; + num_digits = 0; + char* data = buf.data(); + for (;;) { + int digit = numerator.divmod_assign(denominator); + bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. + // numerator + upper >[=] pow10: + bool high = add_compare(numerator, *upper, denominator) + even > 0; + data[num_digits++] = static_cast('0' + digit); + if (low || high) { + if (!low) { + ++data[num_digits - 1]; + } else if (high) { + int result = add_compare(numerator, numerator, denominator); + // Round half to even. + if (result > 0 || (result == 0 && (digit % 2) != 0)) + ++data[num_digits - 1]; + } + buf.try_resize(to_unsigned(num_digits)); + exp10 -= num_digits - 1; + return; + } + numerator *= 10; + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + // Generate the given number of digits. + exp10 -= num_digits - 1; + if (num_digits == 0) { + denominator *= 10; + auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + buf.push_back(digit); + return; + } + buf.try_resize(to_unsigned(num_digits)); + for (int i = 0; i < num_digits - 1; ++i) { + int digit = numerator.divmod_assign(denominator); + buf[i] = static_cast('0' + digit); + numerator *= 10; + } + int digit = numerator.divmod_assign(denominator); + auto result = add_compare(numerator, numerator, denominator); + if (result > 0 || (result == 0 && (digit % 2) != 0)) { + if (digit == 9) { + const auto overflow = '0' + 10; + buf[num_digits - 1] = overflow; + // Propagate the carry. + for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] == overflow) { + buf[0] = '1'; + ++exp10; + } + return; + } + ++digit; + } + buf[num_digits - 1] = static_cast('0' + digit); +} + +template +FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, + float_specs specs, + buffer& buf) { + // float is passed as double to reduce the number of instantiations. + static_assert(!std::is_same::value, ""); + FMT_ASSERT(value >= 0, "value is negative"); + + const bool fixed = specs.format == float_format::fixed; + if (value <= 0) { // <= instead of == to silence a warning. + if (precision <= 0 || !fixed) { + buf.push_back('0'); + return 0; + } + buf.try_resize(to_unsigned(precision)); + fill_n(buf.data(), precision, '0'); + return -precision; + } + + if (specs.fallback) return snprintf_float(value, precision, specs, buf); + + if (!is_constant_evaluated() && precision < 0) { + // Use Dragonbox for the shortest format. + if (specs.binary32) { + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } + + int exp = 0; + bool use_dragon = true; + if (is_fast_float()) { + // Use Grisu + Dragon4 for the given precision: + // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. + const int min_exp = -60; // alpha in Grisu. + int cached_exp10 = 0; // K in Grisu. + fp normalized = normalize(fp(value)); + const auto cached_pow = get_cached_power( + min_exp - (normalized.e + fp::num_significand_bits), cached_exp10); + normalized = normalized * cached_pow; + gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; + if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error && + !is_constant_evaluated()) { + exp += handler.exp10; + buf.try_resize(to_unsigned(handler.size)); + use_dragon = false; + } else { + exp += handler.size - cached_exp10 - 1; + precision = handler.precision; + } + } + if (use_dragon) { + auto f = fp(); + bool is_predecessor_closer = + specs.binary32 ? f.assign(static_cast(value)) : f.assign(value); + // Limit precision to the maximum possible number of significant digits in + // an IEEE754 double because we don't need to generate zeros. + const int max_double_digits = 767; + if (precision > max_double_digits) precision = max_double_digits; + format_dragon(f, is_predecessor_closer, precision, buf, exp); + } + if (!fixed && !specs.showpoint) { + // Remove trailing zeros. + auto num_digits = buf.size(); + while (num_digits > 0 && buf[num_digits - 1] == '0') { + --num_digits; + ++exp; + } + buf.try_resize(num_digits); + } + return exp; +} + +template +int snprintf_float(T value, int precision, float_specs specs, + buffer& buf) { + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); + static_assert(!std::is_same::value, ""); + + // Subtract 1 to account for the difference in precision since we use %e for + // both general and exponent format. + if (specs.format == float_format::general || + specs.format == float_format::exp) + precision = (precision >= 0 ? precision : 6) - 1; + + // Build the format string. + enum { max_format_size = 7 }; // The longest format is "%#.*Le". + char format[max_format_size]; + char* format_ptr = format; + *format_ptr++ = '%'; + if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#'; + if (precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (std::is_same()) *format_ptr++ = 'L'; + *format_ptr++ = specs.format != float_format::hex + ? (specs.format == float_format::fixed ? 'f' : 'e') + : (specs.upper ? 'A' : 'a'); + *format_ptr = '\0'; + + // Format using snprintf. + auto offset = buf.size(); + for (;;) { + auto begin = buf.data() + offset; + auto capacity = buf.capacity() - offset; +#ifdef FMT_FUZZ + if (precision > 100000) + throw std::runtime_error( + "fuzz mode - avoid large allocation inside snprintf"); +#endif + // Suppress the warning about a nonliteral format string. + // Cannot use auto because of a bug in MinGW (#1532). + int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; + int result = precision >= 0 + ? snprintf_ptr(begin, capacity, format, precision, value) + : snprintf_ptr(begin, capacity, format, value); + if (result < 0) { + // The buffer will grow exponentially. + buf.try_reserve(buf.capacity() + 1); + continue; + } + auto size = to_unsigned(result); + // Size equal to capacity means that the last character was truncated. + if (size >= capacity) { + buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'. + continue; + } + auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; + if (specs.format == float_format::fixed) { + if (precision == 0) { + buf.try_resize(size); + return 0; + } + // Find and remove the decimal point. + auto end = begin + size, p = end; + do { + --p; + } while (is_digit(*p)); + int fraction_size = static_cast(end - p - 1); + std::memmove(p, p + 1, to_unsigned(fraction_size)); + buf.try_resize(size - 1); + return -fraction_size; + } + if (specs.format == float_format::hex) { + buf.try_resize(size + offset); + return 0; + } + // Find and parse the exponent. + auto end = begin + size, exp_pos = end; + do { + --exp_pos; + } while (*exp_pos != 'e'); + char sign = exp_pos[1]; + FMT_ASSERT(sign == '+' || sign == '-', ""); + int exp = 0; + auto p = exp_pos + 2; // Skip 'e' and sign. + do { + FMT_ASSERT(is_digit(*p), ""); + exp = exp * 10 + (*p++ - '0'); + } while (p != end); + if (sign == '-') exp = -exp; + int fraction_size = 0; + if (exp_pos != begin + 1) { + // Remove trailing zeros. + auto fraction_end = exp_pos - 1; + while (*fraction_end == '0') --fraction_end; + // Move the fractional part left to get rid of the decimal point. + fraction_size = static_cast(fraction_end - begin - 1); + std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size)); + } + buf.try_resize(to_unsigned(fraction_size) + offset + 1); + return exp - fraction_size; + } +} +} // namespace detail + +template <> struct formatter { + FMT_CONSTEXPR format_parse_context::iterator parse( + format_parse_context& ctx) { + return ctx.begin(); + } + + format_context::iterator format(const detail::bigint& n, + format_context& ctx) { + auto out = ctx.out(); + bool first = true; + for (auto i = n.bigits_.size(); i > 0; --i) { + auto value = n.bigits_[i - 1u]; + if (first) { + out = format_to(out, FMT_STRING("{:x}"), value); + first = false; + continue; + } + out = format_to(out, FMT_STRING("{:08x}"), value); + } + if (n.exp_ > 0) + out = format_to(out, FMT_STRING("p{}"), + n.exp_ * detail::bigint::bigit_bits); + return out; + } +}; + +FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { + for_each_codepoint(s, [this](uint32_t cp, string_view) { + if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8")); + if (cp <= 0xFFFF) { + buffer_.push_back(static_cast(cp)); + } else { + cp -= 0x10000; + buffer_.push_back(static_cast(0xD800 + (cp >> 10))); + buffer_.push_back(static_cast(0xDC00 + (cp & 0x3FF))); + } + return true; + }); + buffer_.push_back(0); +} + +FMT_FUNC void format_system_error(detail::buffer& out, int error_code, + const char* message) FMT_NOEXCEPT { + FMT_TRY { + auto ec = std::error_code(error_code, std::generic_category()); + write(std::back_inserter(out), std::system_error(ec, message).what()); + return; + } + FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +FMT_FUNC void report_system_error(int error_code, + const char* message) FMT_NOEXCEPT { + report_error(format_system_error, error_code, message); +} + +// DEPRECATED! +// This function is defined here and not inline for ABI compatiblity. +FMT_FUNC void detail::error_handler::on_error(const char* message) { + throw_format_error(message); +} + +FMT_FUNC std::string vformat(string_view fmt, format_args args) { + // Don't optimize the "{}" case to keep the binary size small and because it + // can be better optimized in fmt::format anyway. + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + return to_string(buffer); +} + +#ifdef _WIN32 +namespace detail { +using dword = conditional_t; +extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // + void*, const void*, dword, dword*, void*); +} // namespace detail +#endif + +namespace detail { +FMT_FUNC void print(std::FILE* f, string_view text) { +#ifdef _WIN32 + auto fd = _fileno(f); + if (_isatty(fd)) { + detail::utf8_to_utf16 u16(string_view(text.data(), text.size())); + auto written = detail::dword(); + if (detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), + u16.c_str(), static_cast(u16.size()), + &written, nullptr)) { + return; + } + // Fallback to fwrite on failure. It can happen if the output has been + // redirected to NUL. + } +#endif + detail::fwrite_fully(text.data(), 1, text.size(), f); +} +} // namespace detail + +FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, args); + detail::print(f, {buffer.data(), buffer.size()}); +} + +#ifdef _WIN32 +// Print assuming legacy (non-Unicode) encoding. +FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str, + format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, + basic_format_args>(args)); + fwrite_fully(buffer.data(), 1, buffer.size(), f); +} +#endif + +FMT_FUNC void vprint(string_view format_str, format_args args) { + vprint(stdout, format_str, args); +} + +FMT_END_NAMESPACE + +#endif // FMT_FORMAT_INL_H_ diff --git a/ctrtool/deps/libfmt/include/fmt/format.h b/ctrtool/deps/libfmt/include/fmt/format.h new file mode 100644 index 00000000..ee69651c --- /dev/null +++ b/ctrtool/deps/libfmt/include/fmt/format.h @@ -0,0 +1,3104 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - present, Victor Zverovich + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + --- Optional exception to the license --- + + As an exception, if, as a result of your compiling your source code, portions + of this Software are embedded into a machine-executable object form of such + source code, you may redistribute such embedded portions in such object form + without including the above copyright and permission notices. + */ + +#ifndef FMT_FORMAT_H_ +#define FMT_FORMAT_H_ + +#include // std::signbit +#include // uint32_t +#include // std::numeric_limits +#include // std::uninitialized_copy +#include // std::runtime_error +#include // std::system_error +#include // std::swap + +#ifdef __cpp_lib_bit_cast +# include // std::bitcast +#endif + +#include "core.h" + +#if FMT_GCC_VERSION +# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) +#else +# define FMT_GCC_VISIBILITY_HIDDEN +#endif + +#ifdef __NVCC__ +# define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__) +#else +# define FMT_CUDA_VERSION 0 +#endif + +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(x) 0 +#endif + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_NOINLINE __attribute__((noinline)) +#else +# define FMT_NOINLINE +#endif + +#if FMT_MSC_VER +# define FMT_MSC_DEFAULT = default +#else +# define FMT_MSC_DEFAULT +#endif + +#ifndef FMT_THROW +# if FMT_EXCEPTIONS +# if FMT_MSC_VER || FMT_NVCC +FMT_BEGIN_NAMESPACE +namespace detail { +template inline void do_throw(const Exception& x) { + // Silence unreachable code warnings in MSVC and NVCC because these + // are nearly impossible to fix in a generic code. + volatile bool b = true; + if (b) throw x; +} +} // namespace detail +FMT_END_NAMESPACE +# define FMT_THROW(x) detail::do_throw(x) +# else +# define FMT_THROW(x) throw x +# endif +# else +# define FMT_THROW(x) \ + do { \ + FMT_ASSERT(false, (x).what()); \ + } while (false) +# endif +#endif + +#if FMT_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) +#else +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) +#endif + +#ifndef FMT_MAYBE_UNUSED +# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) +# define FMT_MAYBE_UNUSED [[maybe_unused]] +# else +# define FMT_MAYBE_UNUSED +# endif +#endif + +// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. +#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC +# define FMT_DEPRECATED_ALIAS +#else +# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED +#endif + +#ifndef FMT_USE_USER_DEFINED_LITERALS +// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. +# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ + FMT_MSC_VER >= 1900) && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) +# define FMT_USE_USER_DEFINED_LITERALS 1 +# else +# define FMT_USE_USER_DEFINED_LITERALS 0 +# endif +#endif + +// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of +// integer formatter template instantiations to just one by only using the +// largest integer type. This results in a reduction in binary size but will +// cause a decrease in integer formatting performance. +#if !defined(FMT_REDUCE_INT_INSTANTIATIONS) +# define FMT_REDUCE_INT_INSTANTIATIONS 0 +#endif + +// __builtin_clz is broken in clang with Microsoft CodeGen: +// https://github.com/fmtlib/fmt/issues/519. +#if !FMT_MSC_VER +# if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +# endif +# if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +# endif +#endif + +// __builtin_ctz is broken in Intel Compiler Classic on Windows: +// https://github.com/fmtlib/fmt/issues/2510. +#ifndef __ICL +# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) +# endif +# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) +# endif +#endif + +#if FMT_MSC_VER +# include // _BitScanReverse[64], _BitScanForward[64], _umul128 +#endif + +// Some compilers masquerade as both MSVC and GCC-likes or otherwise support +// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the +// MSVC intrinsics if the clz and clzll builtins are not available. +#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(FMT_BUILTIN_CTZLL) +FMT_BEGIN_NAMESPACE +namespace detail { +// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. +# if !defined(__clang__) +# pragma intrinsic(_BitScanForward) +# pragma intrinsic(_BitScanReverse) +# if defined(_WIN64) +# pragma intrinsic(_BitScanForward64) +# pragma intrinsic(_BitScanReverse64) +# endif +# endif + +inline auto clz(uint32_t x) -> int { + unsigned long r = 0; + _BitScanReverse(&r, x); + FMT_ASSERT(x != 0, ""); + // Static analysis complains about using uninitialized data + // "r", but the only way that can happen is if "x" is 0, + // which the callers guarantee to not happen. + FMT_MSC_WARNING(suppress : 6102) + return 31 ^ static_cast(r); +} +# define FMT_BUILTIN_CLZ(n) detail::clz(n) + +inline auto clzll(uint64_t x) -> int { + unsigned long r = 0; +# ifdef _WIN64 + _BitScanReverse64(&r, x); +# else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 ^ (r + 32); + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x)); +# endif + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. + return 63 ^ static_cast(r); +} +# define FMT_BUILTIN_CLZLL(n) detail::clzll(n) + +inline auto ctz(uint32_t x) -> int { + unsigned long r = 0; + _BitScanForward(&r, x); + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. + return static_cast(r); +} +# define FMT_BUILTIN_CTZ(n) detail::ctz(n) + +inline auto ctzll(uint64_t x) -> int { + unsigned long r = 0; + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. +# ifdef _WIN64 + _BitScanForward64(&r, x); +# else + // Scan the low 32 bits. + if (_BitScanForward(&r, static_cast(x))) return static_cast(r); + // Scan the high 32 bits. + _BitScanForward(&r, static_cast(x >> 32)); + r += 32; +# endif + return static_cast(r); +} +# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) +} // namespace detail +FMT_END_NAMESPACE +#endif + +#ifdef FMT_HEADER_ONLY +# define FMT_HEADER_ONLY_CONSTEXPR20 FMT_CONSTEXPR20 +#else +# define FMT_HEADER_ONLY_CONSTEXPR20 +#endif + +FMT_BEGIN_NAMESPACE +namespace detail { + +template class formatbuf : public Streambuf { + private: + using char_type = typename Streambuf::char_type; + using streamsize = decltype(std::declval().sputn(nullptr, 0)); + using int_type = typename Streambuf::int_type; + using traits_type = typename Streambuf::traits_type; + + buffer& buffer_; + + public: + explicit formatbuf(buffer& buf) : buffer_(buf) {} + + protected: + // The put area is always empty. This makes the implementation simpler and has + // the advantage that the streambuf and the buffer are always in sync and + // sputc never writes into uninitialized memory. A disadvantage is that each + // call to sputc always results in a (virtual) call to overflow. There is no + // disadvantage here for sputn since this always results in a call to xsputn. + + auto overflow(int_type ch) -> int_type override { + if (!traits_type::eq_int_type(ch, traits_type::eof())) + buffer_.push_back(static_cast(ch)); + return ch; + } + + auto xsputn(const char_type* s, streamsize count) -> streamsize override { + buffer_.append(s, s + count); + return count; + } +}; + +// Implementation of std::bit_cast for pre-C++20. +template +FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { + static_assert(sizeof(To) == sizeof(From), "size mismatch"); +#ifdef __cpp_lib_bit_cast + if (is_constant_evaluated()) return std::bit_cast(from); +#endif + auto to = To(); + std::memcpy(&to, &from, sizeof(to)); + return to; +} + +inline auto is_big_endian() -> bool { +#ifdef _WIN32 + return false; +#elif defined(__BIG_ENDIAN__) + return true; +#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) + return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; +#else + struct bytes { + char data[sizeof(int)]; + }; + return bit_cast(1).data[0] == 0; +#endif +} + +// A fallback implementation of uintptr_t for systems that lack it. +struct fallback_uintptr { + unsigned char value[sizeof(void*)]; + + fallback_uintptr() = default; + explicit fallback_uintptr(const void* p) { + *this = bit_cast(p); + if (const_check(is_big_endian())) { + for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j) + std::swap(value[i], value[j]); + } + } +}; +#ifdef UINTPTR_MAX +using uintptr_t = ::uintptr_t; +inline auto to_uintptr(const void* p) -> uintptr_t { + return bit_cast(p); +} +#else +using uintptr_t = fallback_uintptr; +inline auto to_uintptr(const void* p) -> fallback_uintptr { + return fallback_uintptr(p); +} +#endif + +// Returns the largest possible value for type T. Same as +// std::numeric_limits::max() but shorter and not affected by the max macro. +template constexpr auto max_value() -> T { + return (std::numeric_limits::max)(); +} +template constexpr auto num_bits() -> int { + return std::numeric_limits::digits; +} +// std::numeric_limits::digits may return 0 for 128-bit ints. +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { + return static_cast(sizeof(void*) * + std::numeric_limits::digits); +} + +FMT_INLINE void assume(bool condition) { + (void)condition; +#if FMT_HAS_BUILTIN(__builtin_assume) + __builtin_assume(condition); +#endif +} + +// An approximation of iterator_t for pre-C++20 systems. +template +using iterator_t = decltype(std::begin(std::declval())); +template using sentinel_t = decltype(std::end(std::declval())); + +// A workaround for std::string not having mutable data() until C++17. +template +inline auto get_data(std::basic_string& s) -> Char* { + return &s[0]; +} +template +inline auto get_data(Container& c) -> typename Container::value_type* { + return c.data(); +} + +#if defined(_SECURE_SCL) && _SECURE_SCL +// Make a checked iterator to avoid MSVC warnings. +template using checked_ptr = stdext::checked_array_iterator; +template +constexpr auto make_checked(T* p, size_t size) -> checked_ptr { + return {p, size}; +} +#else +template using checked_ptr = T*; +template constexpr auto make_checked(T* p, size_t) -> T* { + return p; +} +#endif + +// Attempts to reserve space for n extra characters in the output range. +// Returns a pointer to the reserved range or a reference to it. +template ::value)> +#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION +__attribute__((no_sanitize("undefined"))) +#endif +inline auto +reserve(std::back_insert_iterator it, size_t n) + -> checked_ptr { + Container& c = get_container(it); + size_t size = c.size(); + c.resize(size + n); + return make_checked(get_data(c) + size, n); +} + +template +inline auto reserve(buffer_appender it, size_t n) -> buffer_appender { + buffer& buf = get_container(it); + buf.try_reserve(buf.size() + n); + return it; +} + +template +constexpr auto reserve(Iterator& it, size_t) -> Iterator& { + return it; +} + +template +using reserve_iterator = + remove_reference_t(), 0))>; + +template +constexpr auto to_pointer(OutputIt, size_t) -> T* { + return nullptr; +} +template auto to_pointer(buffer_appender it, size_t n) -> T* { + buffer& buf = get_container(it); + auto size = buf.size(); + if (buf.capacity() < size + n) return nullptr; + buf.try_resize(size + n); + return buf.data() + size; +} + +template ::value)> +inline auto base_iterator(std::back_insert_iterator& it, + checked_ptr) + -> std::back_insert_iterator { + return it; +} + +template +constexpr auto base_iterator(Iterator, Iterator it) -> Iterator { + return it; +} + +// is spectacularly slow to compile in C++20 so use a simple fill_n +// instead (#1998). +template +FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) + -> OutputIt { + for (Size i = 0; i < count; ++i) *out++ = value; + return out; +} +template +FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { + if (is_constant_evaluated()) { + return fill_n(out, count, value); + } + std::memset(out, value, to_unsigned(count)); + return out + count; +} + +#ifdef __cpp_char8_t +using char8_type = char8_t; +#else +enum char8_type : unsigned char {}; +#endif + +template +FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, + OutputIt out) -> OutputIt { + return copy_str(begin, end, out); +} + +// A public domain branchless UTF-8 decoder by Christopher Wellons: +// https://github.com/skeeto/branchless-utf8 +/* Decode the next character, c, from s, reporting errors in e. + * + * Since this is a branchless decoder, four bytes will be read from the + * buffer regardless of the actual length of the next character. This + * means the buffer _must_ have at least three bytes of zero padding + * following the end of the data stream. + * + * Errors are reported in e, which will be non-zero if the parsed + * character was somehow invalid: invalid byte sequence, non-canonical + * encoding, or a surrogate half. + * + * The function returns a pointer to the next character. When an error + * occurs, this pointer will be a guess that depends on the particular + * error, but it will always advance at least one byte. + */ +FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) + -> const char* { + constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + constexpr const int shiftc[] = {0, 18, 12, 6, 0}; + constexpr const int shifte[] = {0, 6, 4, 2, 0}; + + int len = code_point_length(s); + const char* next = s + len; + + // Assume a four-byte character and load four bytes. Unused bits are + // shifted out. + *c = uint32_t(s[0] & masks[len]) << 18; + *c |= uint32_t(s[1] & 0x3f) << 12; + *c |= uint32_t(s[2] & 0x3f) << 6; + *c |= uint32_t(s[3] & 0x3f) << 0; + *c >>= shiftc[len]; + + // Accumulate the various error conditions. + using uchar = unsigned char; + *e = (*c < mins[len]) << 6; // non-canonical encoding + *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? + *e |= (*c > 0x10FFFF) << 8; // out of range? + *e |= (uchar(s[1]) & 0xc0) >> 2; + *e |= (uchar(s[2]) & 0xc0) >> 4; + *e |= uchar(s[3]) >> 6; + *e ^= 0x2a; // top two bits of each tail byte correct? + *e >>= shifte[len]; + + return next; +} + +constexpr uint32_t invalid_code_point = ~uint32_t(); + +// Invokes f(cp, sv) for every code point cp in s with sv being the string view +// corresponding to the code point. cp is invalid_code_point on error. +template +FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { + auto decode = [f](const char* buf_ptr, const char* ptr) { + auto cp = uint32_t(); + auto error = 0; + auto end = utf8_decode(buf_ptr, &cp, &error); + bool result = f(error ? invalid_code_point : cp, + string_view(ptr, to_unsigned(end - buf_ptr))); + return result ? end : nullptr; + }; + auto p = s.data(); + const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. + if (s.size() >= block_size) { + for (auto end = p + s.size() - block_size + 1; p < end;) { + p = decode(p, p); + if (!p) return; + } + } + if (auto num_chars_left = s.data() + s.size() - p) { + char buf[2 * block_size - 1] = {}; + copy_str(p, p + num_chars_left, buf); + const char* buf_ptr = buf; + do { + auto end = decode(buf_ptr, p); + if (!end) return; + p += end - buf_ptr; + buf_ptr = end; + } while (buf_ptr - buf < num_chars_left); + } +} + +template +inline auto compute_width(basic_string_view s) -> size_t { + return s.size(); +} + +// Computes approximate display width of a UTF-8 string. +FMT_CONSTEXPR inline size_t compute_width(string_view s) { + size_t num_code_points = 0; + // It is not a lambda for compatibility with C++14. + struct count_code_points { + size_t* count; + FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool { + *count += detail::to_unsigned( + 1 + + (cp >= 0x1100 && + (cp <= 0x115f || // Hangul Jamo init. consonants + cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET + cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET + // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE: + (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) || + (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables + (cp >= 0xf900 && cp <= 0xfaff) || // CJK Compatibility Ideographs + (cp >= 0xfe10 && cp <= 0xfe19) || // Vertical Forms + (cp >= 0xfe30 && cp <= 0xfe6f) || // CJK Compatibility Forms + (cp >= 0xff00 && cp <= 0xff60) || // Fullwidth Forms + (cp >= 0xffe0 && cp <= 0xffe6) || // Fullwidth Forms + (cp >= 0x20000 && cp <= 0x2fffd) || // CJK + (cp >= 0x30000 && cp <= 0x3fffd) || + // Miscellaneous Symbols and Pictographs + Emoticons: + (cp >= 0x1f300 && cp <= 0x1f64f) || + // Supplemental Symbols and Pictographs: + (cp >= 0x1f900 && cp <= 0x1f9ff)))); + return true; + } + }; + for_each_codepoint(s, count_code_points{&num_code_points}); + return num_code_points; +} + +inline auto compute_width(basic_string_view s) -> size_t { + return compute_width(basic_string_view( + reinterpret_cast(s.data()), s.size())); +} + +template +inline auto code_point_index(basic_string_view s, size_t n) -> size_t { + size_t size = s.size(); + return n < size ? n : size; +} + +// Calculates the index of the nth code point in a UTF-8 string. +inline auto code_point_index(basic_string_view s, size_t n) + -> size_t { + const char8_type* data = s.data(); + size_t num_code_points = 0; + for (size_t i = 0, size = s.size(); i != size; ++i) { + if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i; + } + return s.size(); +} + +template ::value> +struct is_fast_float : bool_constant::is_iec559 && + sizeof(T) <= sizeof(double)> {}; +template struct is_fast_float : std::false_type {}; + +#ifndef FMT_USE_FULL_CACHE_DRAGONBOX +# define FMT_USE_FULL_CACHE_DRAGONBOX 0 +#endif + +template +template +void buffer::append(const U* begin, const U* end) { + while (begin != end) { + auto count = to_unsigned(end - begin); + try_reserve(size_ + count); + auto free_cap = capacity_ - size_; + if (free_cap < count) count = free_cap; + std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count)); + size_ += count; + begin += count; + } +} + +template +struct is_locale : std::false_type {}; +template +struct is_locale> : std::true_type {}; +} // namespace detail + +FMT_MODULE_EXPORT_BEGIN + +// The number of characters to store in the basic_memory_buffer object itself +// to avoid dynamic memory allocation. +enum { inline_buffer_size = 500 }; + +/** + \rst + A dynamically growing memory buffer for trivially copyable/constructible types + with the first ``SIZE`` elements stored in the object itself. + + You can use the ``memory_buffer`` type alias for ``char`` instead. + + **Example**:: + + auto out = fmt::memory_buffer(); + format_to(std::back_inserter(out), "The answer is {}.", 42); + + This will append the following output to the ``out`` object: + + .. code-block:: none + + The answer is 42. + + The output can be converted to an ``std::string`` with ``to_string(out)``. + \endrst + */ +template > +class basic_memory_buffer final : public detail::buffer { + private: + T store_[SIZE]; + + // Don't inherit from Allocator avoid generating type_info for it. + Allocator alloc_; + + // Deallocate memory allocated by the buffer. + FMT_CONSTEXPR20 void deallocate() { + T* data = this->data(); + if (data != store_) alloc_.deallocate(data, this->capacity()); + } + + protected: + FMT_CONSTEXPR20 void grow(size_t size) override; + + public: + using value_type = T; + using const_reference = const T&; + + FMT_CONSTEXPR20 explicit basic_memory_buffer( + const Allocator& alloc = Allocator()) + : alloc_(alloc) { + this->set(store_, SIZE); + if (detail::is_constant_evaluated()) { + detail::fill_n(store_, SIZE, T{}); + } + } + FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } + + private: + // Move data from other to this buffer. + FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { + alloc_ = std::move(other.alloc_); + T* data = other.data(); + size_t size = other.size(), capacity = other.capacity(); + if (data == other.store_) { + this->set(store_, capacity); + if (detail::is_constant_evaluated()) { + detail::copy_str(other.store_, other.store_ + size, + detail::make_checked(store_, capacity)); + } else { + std::uninitialized_copy(other.store_, other.store_ + size, + detail::make_checked(store_, capacity)); + } + } else { + this->set(data, capacity); + // Set pointer to the inline array so that delete is not called + // when deallocating. + other.set(other.store_, 0); + } + this->resize(size); + } + + public: + /** + \rst + Constructs a :class:`fmt::basic_memory_buffer` object moving the content + of the other object to it. + \endrst + */ + FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) + FMT_NOEXCEPT { + move(other); + } + + /** + \rst + Moves the content of the other ``basic_memory_buffer`` object to this one. + \endrst + */ + auto operator=(basic_memory_buffer&& other) FMT_NOEXCEPT + -> basic_memory_buffer& { + FMT_ASSERT(this != &other, ""); + deallocate(); + move(other); + return *this; + } + + // Returns a copy of the allocator associated with this buffer. + auto get_allocator() const -> Allocator { return alloc_; } + + /** + Resizes the buffer to contain *count* elements. If T is a POD type new + elements may not be initialized. + */ + FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); } + + /** Increases the buffer capacity to *new_capacity*. */ + void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } + + // Directly append data into the buffer + using detail::buffer::append; + template + void append(const ContiguousRange& range) { + append(range.data(), range.data() + range.size()); + } +}; + +template +FMT_CONSTEXPR20 void basic_memory_buffer::grow( + size_t size) { +#ifdef FMT_FUZZ + if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much"); +#endif + const size_t max_size = std::allocator_traits::max_size(alloc_); + size_t old_capacity = this->capacity(); + size_t new_capacity = old_capacity + old_capacity / 2; + if (size > new_capacity) + new_capacity = size; + else if (new_capacity > max_size) + new_capacity = size > max_size ? size : max_size; + T* old_data = this->data(); + T* new_data = + std::allocator_traits::allocate(alloc_, new_capacity); + // The following code doesn't throw, so the raw pointer above doesn't leak. + std::uninitialized_copy(old_data, old_data + this->size(), + detail::make_checked(new_data, new_capacity)); + this->set(new_data, new_capacity); + // deallocate must not throw according to the standard, but even if it does, + // the buffer already uses the new storage and will deallocate it in + // destructor. + if (old_data != store_) alloc_.deallocate(old_data, old_capacity); +} + +using memory_buffer = basic_memory_buffer; + +template +struct is_contiguous> : std::true_type { +}; + +namespace detail { +FMT_API void print(std::FILE*, string_view); +} + +/** A formatting error such as invalid format string. */ +FMT_CLASS_API +class FMT_API format_error : public std::runtime_error { + public: + explicit format_error(const char* message) : std::runtime_error(message) {} + explicit format_error(const std::string& message) + : std::runtime_error(message) {} + format_error(const format_error&) = default; + format_error& operator=(const format_error&) = default; + format_error(format_error&&) = default; + format_error& operator=(format_error&&) = default; + ~format_error() FMT_NOEXCEPT override FMT_MSC_DEFAULT; +}; + +/** + \rst + Constructs a `~fmt::format_arg_store` object that contains references + to arguments and can be implicitly converted to `~fmt::format_args`. + If ``fmt`` is a compile-time string then `make_args_checked` checks + its validity at compile time. + \endrst + */ +template > +FMT_INLINE auto make_args_checked(const S& fmt, + const remove_reference_t&... args) + -> format_arg_store, remove_reference_t...> { + static_assert( + detail::count<( + std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); + detail::check_format_string(fmt); + return {args...}; +} + +// compile-time support +namespace detail_exported { +#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS +template struct fixed_string { + constexpr fixed_string(const Char (&str)[N]) { + detail::copy_str(static_cast(str), + str + N, data); + } + Char data[N]{}; +}; +#endif + +// Converts a compile-time string to basic_string_view. +template +constexpr auto compile_string_to_view(const Char (&s)[N]) + -> basic_string_view { + // Remove trailing NUL character if needed. Won't be present if this is used + // with a raw character array (i.e. not defined as a string). + return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; +} +template +constexpr auto compile_string_to_view(detail::std_string_view s) + -> basic_string_view { + return {s.data(), s.size()}; +} +} // namespace detail_exported + +FMT_BEGIN_DETAIL_NAMESPACE + +template struct is_integral : std::is_integral {}; +template <> struct is_integral : std::true_type {}; +template <> struct is_integral : std::true_type {}; + +template +using is_signed = + std::integral_constant::is_signed || + std::is_same::value>; + +// Returns true if value is negative, false otherwise. +// Same as `value < 0` but doesn't produce warnings if T is an unsigned type. +template ::value)> +FMT_CONSTEXPR auto is_negative(T value) -> bool { + return value < 0; +} +template ::value)> +FMT_CONSTEXPR auto is_negative(T) -> bool { + return false; +} + +template ::value)> +FMT_CONSTEXPR auto is_supported_floating_point(T) -> uint16_t { + return (std::is_same::value && FMT_USE_FLOAT) || + (std::is_same::value && FMT_USE_DOUBLE) || + (std::is_same::value && FMT_USE_LONG_DOUBLE); +} + +// Smallest of uint32_t, uint64_t, uint128_t that is large enough to +// represent all values of an integral type T. +template +using uint32_or_64_or_128_t = + conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, + uint32_t, + conditional_t() <= 64, uint64_t, uint128_t>>; +template +using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ + (factor)*1000000, (factor)*10000000, (factor)*100000000, \ + (factor)*1000000000 + +// Converts value in the range [0, 100) to a string. +constexpr const char* digits2(size_t value) { + // GCC generates slightly better code when value is pointer-size. + return &"0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"[value * 2]; +} + +// Sign is a template parameter to workaround a bug in gcc 4.8. +template constexpr Char sign(Sign s) { +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 + static_assert(std::is_same::value, ""); +#endif + return static_cast("\0-+ "[s]); +} + +template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { + int count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000u; + count += 4; + } +} +#if FMT_USE_INT128 +FMT_CONSTEXPR inline auto count_digits(uint128_t n) -> int { + return count_digits_fallback(n); +} +#endif + +#ifdef FMT_BUILTIN_CLZLL +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +inline auto do_count_digits(uint64_t n) -> int { + // This has comparable performance to the version by Kendall Willets + // (https://github.com/fmtlib/format-benchmark/blob/master/digits10) + // but uses smaller tables. + // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). + static constexpr uint8_t bsr2log10[] = { + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; + auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; + static constexpr const uint64_t zero_or_powers_of_10[] = { + 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + return t - (n < zero_or_powers_of_10[t]); +} +#endif + +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { +#ifdef FMT_BUILTIN_CLZLL + if (!is_constant_evaluated()) { + return do_count_digits(n); + } +#endif + return count_digits_fallback(n); +} + +// Counts the number of digits in n. BITS = log2(radix). +template +FMT_CONSTEXPR auto count_digits(UInt n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (num_bits() == 32) + return (FMT_BUILTIN_CLZ(static_cast(n) | 1) ^ 31) / BITS + 1; +#endif + // Lambda avoids unreachable code warnings from NVHPC. + return [](UInt m) { + int num_digits = 0; + do { + ++num_digits; + } while ((m >>= BITS) != 0); + return num_digits; + }(n); +} + +template <> auto count_digits<4>(detail::fallback_uintptr n) -> int; + +#ifdef FMT_BUILTIN_CLZ +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +FMT_INLINE auto do_count_digits(uint32_t n) -> int { +// An optimization by Kendall Willets from https://bit.ly/3uOIQrB. +// This increments the upper 32 bits (log10(T) - 1) when >= T is added. +# define FMT_INC(T) (((sizeof(# T) - 1ull) << 32) - T) + static constexpr uint64_t table[] = { + FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 + FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 + FMT_INC(100), FMT_INC(100), FMT_INC(100), // 512 + FMT_INC(1000), FMT_INC(1000), FMT_INC(1000), // 4096 + FMT_INC(10000), FMT_INC(10000), FMT_INC(10000), // 32k + FMT_INC(100000), FMT_INC(100000), FMT_INC(100000), // 256k + FMT_INC(1000000), FMT_INC(1000000), FMT_INC(1000000), // 2048k + FMT_INC(10000000), FMT_INC(10000000), FMT_INC(10000000), // 16M + FMT_INC(100000000), FMT_INC(100000000), FMT_INC(100000000), // 128M + FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M + FMT_INC(1000000000), FMT_INC(1000000000) // 4B + }; + auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31]; + return static_cast((n + inc) >> 32); +} +#endif + +// Optional version of count_digits for better performance on 32-bit platforms. +FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated()) { + return do_count_digits(n); + } +#endif + return count_digits_fallback(n); +} + +template constexpr auto digits10() FMT_NOEXCEPT -> int { + return std::numeric_limits::digits10; +} +template <> constexpr auto digits10() FMT_NOEXCEPT -> int { + return 38; +} +template <> constexpr auto digits10() FMT_NOEXCEPT -> int { + return 38; +} + +template struct thousands_sep_result { + std::string grouping; + Char thousands_sep; +}; + +template +FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result; +template +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { + auto result = thousands_sep_impl(loc); + return {result.grouping, Char(result.thousands_sep)}; +} +template <> +inline auto thousands_sep(locale_ref loc) -> thousands_sep_result { + return thousands_sep_impl(loc); +} + +template +FMT_API auto decimal_point_impl(locale_ref loc) -> Char; +template inline auto decimal_point(locale_ref loc) -> Char { + return Char(decimal_point_impl(loc)); +} +template <> inline auto decimal_point(locale_ref loc) -> wchar_t { + return decimal_point_impl(loc); +} + +// Compares two characters for equality. +template auto equal2(const Char* lhs, const char* rhs) -> bool { + return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); +} +inline auto equal2(const char* lhs, const char* rhs) -> bool { + return memcmp(lhs, rhs, 2) == 0; +} + +// Copies two characters from src to dst. +template +FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) { + if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) { + memcpy(dst, src, 2); + return; + } + *dst++ = static_cast(*src++); + *dst = static_cast(*src); +} + +template struct format_decimal_result { + Iterator begin; + Iterator end; +}; + +// Formats a decimal unsigned integer value writing into out pointing to a +// buffer of specified size. The caller must ensure that the buffer is large +// enough. +template +FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) + -> format_decimal_result { + FMT_ASSERT(size >= count_digits(value), "invalid digit count"); + out += size; + Char* end = out; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + out -= 2; + copy2(out, digits2(static_cast(value % 100))); + value /= 100; + } + if (value < 10) { + *--out = static_cast('0' + value); + return {out, end}; + } + out -= 2; + copy2(out, digits2(static_cast(value))); + return {out, end}; +} + +template >::value)> +inline auto format_decimal(Iterator out, UInt value, int size) + -> format_decimal_result { + // Buffer is large enough to hold all digits (digits10 + 1). + Char buffer[digits10() + 1]; + auto end = format_decimal(buffer, value, size).end; + return {out, detail::copy_str_noinline(buffer, end, out)}; +} + +template +FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) -> Char* { + buffer += num_digits; + Char* end = buffer; + do { + const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) + : digits[digit]); + } while ((value >>= BASE_BITS) != 0); + return end; +} + +template +auto format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, + bool = false) -> Char* { + auto char_digits = std::numeric_limits::digits / 4; + int start = (num_digits + char_digits - 1) / char_digits - 1; + if (int start_digits = num_digits % char_digits) { + unsigned value = n.value[start--]; + buffer = format_uint(buffer, value, start_digits); + } + for (; start >= 0; --start) { + unsigned value = n.value[start]; + buffer += char_digits; + auto p = buffer; + for (int i = 0; i < char_digits; ++i) { + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--p = static_cast("0123456789abcdef"[digit]); + value >>= BASE_BITS; + } + } + return buffer; +} + +template +inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) + -> It { + if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { + format_uint(ptr, value, num_digits, upper); + return out; + } + // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). + char buffer[num_bits() / BASE_BITS + 1]; + format_uint(buffer, value, num_digits, upper); + return detail::copy_str_noinline(buffer, buffer + num_digits, out); +} + +// A converter from UTF-8 to UTF-16. +class utf8_to_utf16 { + private: + basic_memory_buffer buffer_; + + public: + FMT_API explicit utf8_to_utf16(string_view s); + operator basic_string_view() const { return {&buffer_[0], size()}; } + auto size() const -> size_t { return buffer_.size() - 1; } + auto c_str() const -> const wchar_t* { return &buffer_[0]; } + auto str() const -> std::wstring { return {&buffer_[0], size()}; } +}; + +namespace dragonbox { + +// Type-specific information that Dragonbox uses. +template struct float_info; + +template <> struct float_info { + using carrier_uint = uint32_t; + static const int significand_bits = 23; + static const int exponent_bits = 8; + static const int min_exponent = -126; + static const int max_exponent = 127; + static const int exponent_bias = -127; + static const int decimal_digits = 9; + static const int kappa = 1; + static const int big_divisor = 100; + static const int small_divisor = 10; + static const int min_k = -31; + static const int max_k = 46; + static const int cache_bits = 64; + static const int divisibility_check_by_5_threshold = 39; + static const int case_fc_pm_half_lower_threshold = -1; + static const int case_fc_pm_half_upper_threshold = 6; + static const int case_fc_lower_threshold = -2; + static const int case_fc_upper_threshold = 6; + static const int case_shorter_interval_left_endpoint_lower_threshold = 2; + static const int case_shorter_interval_left_endpoint_upper_threshold = 3; + static const int shorter_interval_tie_lower_threshold = -35; + static const int shorter_interval_tie_upper_threshold = -35; + static const int max_trailing_zeros = 7; +}; + +template <> struct float_info { + using carrier_uint = uint64_t; + static const int significand_bits = 52; + static const int exponent_bits = 11; + static const int min_exponent = -1022; + static const int max_exponent = 1023; + static const int exponent_bias = -1023; + static const int decimal_digits = 17; + static const int kappa = 2; + static const int big_divisor = 1000; + static const int small_divisor = 100; + static const int min_k = -292; + static const int max_k = 326; + static const int cache_bits = 128; + static const int divisibility_check_by_5_threshold = 86; + static const int case_fc_pm_half_lower_threshold = -2; + static const int case_fc_pm_half_upper_threshold = 9; + static const int case_fc_lower_threshold = -4; + static const int case_fc_upper_threshold = 9; + static const int case_shorter_interval_left_endpoint_lower_threshold = 2; + static const int case_shorter_interval_left_endpoint_upper_threshold = 3; + static const int shorter_interval_tie_lower_threshold = -77; + static const int shorter_interval_tie_upper_threshold = -77; + static const int max_trailing_zeros = 16; +}; + +template struct decimal_fp { + using significand_type = typename float_info::carrier_uint; + significand_type significand; + int exponent; +}; + +template +FMT_API auto to_decimal(T x) FMT_NOEXCEPT -> decimal_fp; +} // namespace dragonbox + +template +constexpr auto exponent_mask() -> + typename dragonbox::float_info::carrier_uint { + using uint = typename dragonbox::float_info::carrier_uint; + return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) + << dragonbox::float_info::significand_bits; +} + +// Writes the exponent exp in the form "[+-]d{2,3}" to buffer. +template +FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { + FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); + if (exp < 0) { + *it++ = static_cast('-'); + exp = -exp; + } else { + *it++ = static_cast('+'); + } + if (exp >= 100) { + const char* top = digits2(to_unsigned(exp / 100)); + if (exp >= 1000) *it++ = static_cast(top[0]); + *it++ = static_cast(top[1]); + exp %= 100; + } + const char* d = digits2(to_unsigned(exp)); + *it++ = static_cast(d[0]); + *it++ = static_cast(d[1]); + return it; +} + +template +FMT_HEADER_ONLY_CONSTEXPR20 auto format_float(T value, int precision, + float_specs specs, + buffer& buf) -> int; + +// Formats a floating-point number with snprintf. +template +auto snprintf_float(T value, int precision, float_specs specs, + buffer& buf) -> int; + +template constexpr auto promote_float(T value) -> T { + return value; +} +constexpr auto promote_float(float value) -> double { + return static_cast(value); +} + +template +FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, + const fill_t& fill) -> OutputIt { + auto fill_size = fill.size(); + if (fill_size == 1) return detail::fill_n(it, n, fill[0]); + auto data = fill.data(); + for (size_t i = 0; i < n; ++i) + it = copy_str(data, data + fill_size, it); + return it; +} + +// Writes the output of f, padded according to format specifications in specs. +// size: output size in code units. +// width: output display width in (terminal) column positions. +template +FMT_CONSTEXPR auto write_padded(OutputIt out, + const basic_format_specs& specs, + size_t size, size_t width, F&& f) -> OutputIt { + static_assert(align == align::left || align == align::right, ""); + unsigned spec_width = to_unsigned(specs.width); + size_t padding = spec_width > width ? spec_width - width : 0; + // Shifts are encoded as string literals because static constexpr is not + // supported in constexpr functions. + auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; + size_t left_padding = padding >> shifts[specs.align]; + size_t right_padding = padding - left_padding; + auto it = reserve(out, size + padding * specs.fill.size()); + if (left_padding != 0) it = fill(it, left_padding, specs.fill); + it = f(it); + if (right_padding != 0) it = fill(it, right_padding, specs.fill); + return base_iterator(out, it); +} + +template +constexpr auto write_padded(OutputIt out, const basic_format_specs& specs, + size_t size, F&& f) -> OutputIt { + return write_padded(out, specs, size, size, f); +} + +template +FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, + const basic_format_specs& specs) + -> OutputIt { + return write_padded( + out, specs, bytes.size(), [bytes](reserve_iterator it) { + const char* data = bytes.data(); + return copy_str(data, data + bytes.size(), it); + }); +} + +template +auto write_ptr(OutputIt out, UIntPtr value, + const basic_format_specs* specs) -> OutputIt { + int num_digits = count_digits<4>(value); + auto size = to_unsigned(num_digits) + size_t(2); + auto write = [=](reserve_iterator it) { + *it++ = static_cast('0'); + *it++ = static_cast('x'); + return format_uint<4, Char>(it, value, num_digits); + }; + return specs ? write_padded(out, *specs, size, write) + : base_iterator(out, write(reserve(out, size))); +} + +template +FMT_CONSTEXPR auto write_char(OutputIt out, Char value, + const basic_format_specs& specs) + -> OutputIt { + return write_padded(out, specs, 1, [=](reserve_iterator it) { + *it++ = value; + return it; + }); +} +template +FMT_CONSTEXPR auto write(OutputIt out, Char value, + const basic_format_specs& specs, + locale_ref loc = {}) -> OutputIt { + return check_char_specs(specs) + ? write_char(out, value, specs) + : write(out, static_cast(value), specs, loc); +} + +// Data for write_int that doesn't depend on output iterator type. It is used to +// avoid template code bloat. +template struct write_int_data { + size_t size; + size_t padding; + + FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, + const basic_format_specs& specs) + : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { + if (specs.align == align::numeric) { + auto width = to_unsigned(specs.width); + if (width > size) { + padding = width - size; + size = width; + } + } else if (specs.precision > num_digits) { + size = (prefix >> 24) + to_unsigned(specs.precision); + padding = to_unsigned(specs.precision - num_digits); + } + } +}; + +// Writes an integer in the format +// +// where are written by write_digits(it). +// prefix contains chars in three lower bytes and the size in the fourth byte. +template +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, + unsigned prefix, + const basic_format_specs& specs, + W write_digits) -> OutputIt { + // Slightly faster check for specs.width == 0 && specs.precision == -1. + if ((specs.width | (specs.precision + 1)) == 0) { + auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); + if (prefix != 0) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + } + return base_iterator(out, write_digits(it)); + } + auto data = write_int_data(num_digits, prefix, specs); + return write_padded( + out, specs, data.size, [=](reserve_iterator it) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + it = detail::fill_n(it, data.padding, static_cast('0')); + return write_digits(it); + }); +} + +template class digit_grouping { + private: + thousands_sep_result sep_; + + struct next_state { + std::string::const_iterator group; + int pos; + }; + next_state initial_state() const { return {sep_.grouping.begin(), 0}; } + + // Returns the next digit group separator position. + int next(next_state& state) const { + if (!sep_.thousands_sep) return max_value(); + if (state.group == sep_.grouping.end()) + return state.pos += sep_.grouping.back(); + if (*state.group <= 0 || *state.group == max_value()) + return max_value(); + state.pos += *state.group++; + return state.pos; + } + + public: + explicit digit_grouping(locale_ref loc, bool localized = true) { + if (localized) + sep_ = thousands_sep(loc); + else + sep_.thousands_sep = Char(); + } + explicit digit_grouping(thousands_sep_result sep) : sep_(sep) {} + + Char separator() const { return sep_.thousands_sep; } + + int count_separators(int num_digits) const { + int count = 0; + auto state = initial_state(); + while (num_digits > next(state)) ++count; + return count; + } + + // Applies grouping to digits and write the output to out. + template + Out apply(Out out, basic_string_view digits) const { + auto num_digits = static_cast(digits.size()); + auto separators = basic_memory_buffer(); + separators.push_back(0); + auto state = initial_state(); + while (int i = next(state)) { + if (i >= num_digits) break; + separators.push_back(i); + } + for (int i = 0, sep_index = static_cast(separators.size() - 1); + i < num_digits; ++i) { + if (num_digits - i == separators[sep_index]) { + *out++ = separator(); + --sep_index; + } + *out++ = static_cast(digits[to_unsigned(i)]); + } + return out; + } +}; + +template +auto write_int_localized(OutputIt out, UInt value, unsigned prefix, + const basic_format_specs& specs, + const digit_grouping& grouping) -> OutputIt { + static_assert(std::is_same, UInt>::value, ""); + int num_digits = count_digits(value); + char digits[40]; + format_decimal(digits, value, num_digits); + unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + + grouping.count_separators(num_digits)); + return write_padded( + out, specs, size, size, [&](reserve_iterator it) { + if (prefix != 0) *it++ = static_cast(prefix); + return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); + }); +} + +template +auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, + const basic_format_specs& specs, locale_ref loc) + -> bool { + auto grouping = digit_grouping(loc); + out = write_int_localized(out, value, prefix, specs, grouping); + return true; +} + +FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { + prefix |= prefix != 0 ? value << 8 : value; + prefix += (1u + (value > 0xff ? 1 : 0)) << 24; +} + +template struct write_int_arg { + UInt abs_value; + unsigned prefix; +}; + +template +FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) + -> write_int_arg> { + auto prefix = 0u; + auto abs_value = static_cast>(value); + if (is_negative(value)) { + prefix = 0x01000000 | '-'; + abs_value = 0 - abs_value; + } else { + constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', + 0x1000000u | ' '}; + prefix = prefixes[sign]; + } + return {abs_value, prefix}; +} + +template +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, + const basic_format_specs& specs, + locale_ref loc) -> OutputIt { + static_assert(std::is_same>::value, ""); + auto abs_value = arg.abs_value; + auto prefix = arg.prefix; + switch (specs.type) { + case presentation_type::none: + case presentation_type::dec: { + if (specs.localized && + write_int_localized(out, static_cast>(abs_value), + prefix, specs, loc)) { + return out; + } + auto num_digits = count_digits(abs_value); + return write_int( + out, num_digits, prefix, specs, [=](reserve_iterator it) { + return format_decimal(it, abs_value, num_digits).end; + }); + } + case presentation_type::hex_lower: + case presentation_type::hex_upper: { + bool upper = specs.type == presentation_type::hex_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); + int num_digits = count_digits<4>(abs_value); + return write_int( + out, num_digits, prefix, specs, [=](reserve_iterator it) { + return format_uint<4, Char>(it, abs_value, num_digits, upper); + }); + } + case presentation_type::bin_lower: + case presentation_type::bin_upper: { + bool upper = specs.type == presentation_type::bin_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); + int num_digits = count_digits<1>(abs_value); + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator it) { + return format_uint<1, Char>(it, abs_value, num_digits); + }); + } + case presentation_type::oct: { + int num_digits = count_digits<3>(abs_value); + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + if (specs.alt && specs.precision <= num_digits && abs_value != 0) + prefix_append(prefix, '0'); + return write_int(out, num_digits, prefix, specs, + [=](reserve_iterator it) { + return format_uint<3, Char>(it, abs_value, num_digits); + }); + } + case presentation_type::chr: + return write_char(out, static_cast(abs_value), specs); + default: + throw_format_error("invalid type specifier"); + } + return out; +} +template +FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( + OutputIt out, write_int_arg arg, const basic_format_specs& specs, + locale_ref loc) -> OutputIt { + return write_int(out, arg, specs, loc); +} +template ::value && + !std::is_same::value && + std::is_same>::value)> +FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, + const basic_format_specs& specs, + locale_ref loc) -> OutputIt { + return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, + loc); +} +// An inlined version of write used in format string compilation. +template ::value && + !std::is_same::value && + !std::is_same>::value)> +FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, + const basic_format_specs& specs, + locale_ref loc) -> OutputIt { + return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); +} + +template +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const basic_format_specs& specs) -> OutputIt { + auto data = s.data(); + auto size = s.size(); + if (specs.precision >= 0 && to_unsigned(specs.precision) < size) + size = code_point_index(s, to_unsigned(specs.precision)); + auto width = + specs.width != 0 ? compute_width(basic_string_view(data, size)) : 0; + return write_padded(out, specs, size, width, + [=](reserve_iterator it) { + return copy_str(data, data + size, it); + }); +} +template +FMT_CONSTEXPR auto write(OutputIt out, + basic_string_view> s, + const basic_format_specs& specs, locale_ref) + -> OutputIt { + check_string_type_spec(specs.type); + return write(out, s, specs); +} +template +FMT_CONSTEXPR auto write(OutputIt out, const Char* s, + const basic_format_specs& specs, locale_ref) + -> OutputIt { + return check_cstring_type_spec(specs.type) + ? write(out, basic_string_view(s), specs, {}) + : write_ptr(out, to_uintptr(s), &specs); +} + +template +FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isinf, + basic_format_specs specs, + const float_specs& fspecs) -> OutputIt { + auto str = + isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); + constexpr size_t str_size = 3; + auto sign = fspecs.sign; + auto size = str_size + (sign ? 1 : 0); + // Replace '0'-padding with space for non-finite values. + const bool is_zero_fill = + specs.fill.size() == 1 && *specs.fill.data() == static_cast('0'); + if (is_zero_fill) specs.fill[0] = static_cast(' '); + return write_padded(out, specs, size, [=](reserve_iterator it) { + if (sign) *it++ = detail::sign(sign); + return copy_str(str, str + str_size, it); + }); +} + +// A decimal floating-point number significand * pow(10, exp). +struct big_decimal_fp { + const char* significand; + int significand_size; + int exponent; +}; + +constexpr auto get_significand_size(const big_decimal_fp& fp) -> int { + return fp.significand_size; +} +template +inline auto get_significand_size(const dragonbox::decimal_fp& fp) -> int { + return count_digits(fp.significand); +} + +template +constexpr auto write_significand(OutputIt out, const char* significand, + int significand_size) -> OutputIt { + return copy_str(significand, significand + significand_size, out); +} +template +inline auto write_significand(OutputIt out, UInt significand, + int significand_size) -> OutputIt { + return format_decimal(out, significand, significand_size).end; +} +template +FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, + int significand_size, int exponent, + const Grouping& grouping) -> OutputIt { + if (!grouping.separator()) { + out = write_significand(out, significand, significand_size); + return detail::fill_n(out, exponent, static_cast('0')); + } + auto buffer = memory_buffer(); + write_significand(appender(buffer), significand, significand_size); + detail::fill_n(appender(buffer), exponent, '0'); + return grouping.apply(out, string_view(buffer.data(), buffer.size())); +} + +template ::value)> +inline auto write_significand(Char* out, UInt significand, int significand_size, + int integral_size, Char decimal_point) -> Char* { + if (!decimal_point) + return format_decimal(out, significand, significand_size).end; + out += significand_size + 1; + Char* end = out; + int floating_size = significand_size - integral_size; + for (int i = floating_size / 2; i > 0; --i) { + out -= 2; + copy2(out, digits2(significand % 100)); + significand /= 100; + } + if (floating_size % 2 != 0) { + *--out = static_cast('0' + significand % 10); + significand /= 10; + } + *--out = decimal_point; + format_decimal(out - integral_size, significand, integral_size); + return end; +} + +template >::value)> +inline auto write_significand(OutputIt out, UInt significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { + // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. + Char buffer[digits10() + 2]; + auto end = write_significand(buffer, significand, significand_size, + integral_size, decimal_point); + return detail::copy_str_noinline(buffer, end, out); +} + +template +FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { + out = detail::copy_str_noinline(significand, + significand + integral_size, out); + if (!decimal_point) return out; + *out++ = decimal_point; + return detail::copy_str_noinline(significand + integral_size, + significand + significand_size, out); +} + +template +FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, + int significand_size, int integral_size, + Char decimal_point, + const Grouping& grouping) -> OutputIt { + if (!grouping.separator()) { + return write_significand(out, significand, significand_size, integral_size, + decimal_point); + } + auto buffer = basic_memory_buffer(); + write_significand(buffer_appender(buffer), significand, + significand_size, integral_size, decimal_point); + grouping.apply( + out, basic_string_view(buffer.data(), to_unsigned(integral_size))); + return detail::copy_str_noinline(buffer.data() + integral_size, + buffer.end(), out); +} + +template > +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, + const basic_format_specs& specs, + float_specs fspecs, locale_ref loc) + -> OutputIt { + auto significand = fp.significand; + int significand_size = get_significand_size(fp); + constexpr Char zero = static_cast('0'); + auto sign = fspecs.sign; + size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); + using iterator = reserve_iterator; + + Char decimal_point = + fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); + + int output_exp = fp.exponent + significand_size - 1; + auto use_exp_format = [=]() { + if (fspecs.format == float_format::exp) return true; + if (fspecs.format != float_format::general) return false; + // Use the fixed notation if the exponent is in [exp_lower, exp_upper), + // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. + const int exp_lower = -4, exp_upper = 16; + return output_exp < exp_lower || + output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper); + }; + if (use_exp_format()) { + int num_zeros = 0; + if (fspecs.showpoint) { + num_zeros = fspecs.precision - significand_size; + if (num_zeros < 0) num_zeros = 0; + size += to_unsigned(num_zeros); + } else if (significand_size == 1) { + decimal_point = Char(); + } + auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp; + int exp_digits = 2; + if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; + + size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); + char exp_char = fspecs.upper ? 'E' : 'e'; + auto write = [=](iterator it) { + if (sign) *it++ = detail::sign(sign); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, significand, significand_size, 1, + decimal_point); + if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero); + *it++ = static_cast(exp_char); + return write_exponent(output_exp, it); + }; + return specs.width > 0 ? write_padded(out, specs, size, write) + : base_iterator(out, write(reserve(out, size))); + } + + int exp = fp.exponent + significand_size; + if (fp.exponent >= 0) { + // 1234e5 -> 123400000[.0+] + size += to_unsigned(fp.exponent); + int num_zeros = fspecs.precision - exp; +#ifdef FMT_FUZZ + if (num_zeros > 5000) + throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); +#endif + if (fspecs.showpoint) { + if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1; + if (num_zeros > 0) size += to_unsigned(num_zeros) + 1; + } + auto grouping = Grouping(loc, fspecs.locale); + size += to_unsigned(grouping.count_separators(significand_size)); + return write_padded(out, specs, size, [&](iterator it) { + if (sign) *it++ = detail::sign(sign); + it = write_significand(it, significand, significand_size, + fp.exponent, grouping); + if (!fspecs.showpoint) return it; + *it++ = decimal_point; + return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; + }); + } else if (exp > 0) { + // 1234e-2 -> 12.34[0+] + int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; + size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); + auto grouping = Grouping(loc, fspecs.locale); + size += to_unsigned(grouping.count_separators(significand_size)); + return write_padded(out, specs, size, [&](iterator it) { + if (sign) *it++ = detail::sign(sign); + it = write_significand(it, significand, significand_size, exp, + decimal_point, grouping); + return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; + }); + } + // 1234e-6 -> 0.001234 + int num_zeros = -exp; + if (significand_size == 0 && fspecs.precision >= 0 && + fspecs.precision < num_zeros) { + num_zeros = fspecs.precision; + } + bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint; + size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); + return write_padded(out, specs, size, [&](iterator it) { + if (sign) *it++ = detail::sign(sign); + *it++ = zero; + if (!pointy) return it; + *it++ = decimal_point; + it = detail::fill_n(it, num_zeros, zero); + return write_significand(it, significand, significand_size); + }); +} + +template class fallback_digit_grouping { + public: + constexpr fallback_digit_grouping(locale_ref, bool) {} + + constexpr Char separator() const { return Char(); } + + constexpr int count_separators(int) const { return 0; } + + template + constexpr Out apply(Out out, basic_string_view) const { + return out; + } +}; + +template +FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& fp, + const basic_format_specs& specs, + float_specs fspecs, locale_ref loc) + -> OutputIt { + if (is_constant_evaluated()) { + return do_write_float>(out, fp, specs, fspecs, + loc); + } else { + return do_write_float(out, fp, specs, fspecs, loc); + } +} + +template ::value)> +FMT_CONSTEXPR20 bool isinf(T value) { + if (is_constant_evaluated()) { +#if defined(__cpp_if_constexpr) + if constexpr (std::numeric_limits::is_iec559) { + auto bits = detail::bit_cast(static_cast(value)); + constexpr auto significand_bits = + dragonbox::float_info::significand_bits; + return (bits & exponent_mask()) && + !(bits & ((uint64_t(1) << significand_bits) - 1)); + } +#endif + } + return std::isinf(value); +} + +template ::value)> +FMT_CONSTEXPR20 bool isfinite(T value) { + if (is_constant_evaluated()) { +#if defined(__cpp_if_constexpr) + if constexpr (std::numeric_limits::is_iec559) { + auto bits = detail::bit_cast(static_cast(value)); + return (bits & exponent_mask()) != exponent_mask(); + } +#endif + } + return std::isfinite(value); +} + +template ::value)> +FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { + if (is_constant_evaluated()) { +#ifdef __cpp_if_constexpr + if constexpr (std::numeric_limits::is_iec559) { + auto bits = detail::bit_cast(static_cast(value)); + return (bits & (uint64_t(1) << (num_bits() - 1))) != 0; + } +#endif + } + return std::signbit(value); +} + +template ::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value, + basic_format_specs specs, locale_ref loc = {}) + -> OutputIt { + if (const_check(!is_supported_floating_point(value))) return out; + float_specs fspecs = parse_float_type_spec(specs); + fspecs.sign = specs.sign; + if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit. + fspecs.sign = sign::minus; + value = -value; + } else if (fspecs.sign == sign::minus) { + fspecs.sign = sign::none; + } + + if (!detail::isfinite(value)) + return write_nonfinite(out, detail::isinf(value), specs, fspecs); + + if (specs.align == align::numeric && fspecs.sign) { + auto it = reserve(out, 1); + *it++ = detail::sign(fspecs.sign); + out = base_iterator(out, it); + fspecs.sign = sign::none; + if (specs.width != 0) --specs.width; + } + + memory_buffer buffer; + if (fspecs.format == float_format::hex) { + if (fspecs.sign) buffer.push_back(detail::sign(fspecs.sign)); + snprintf_float(promote_float(value), specs.precision, fspecs, buffer); + return write_bytes(out, {buffer.data(), buffer.size()}, + specs); + } + int precision = specs.precision >= 0 || specs.type == presentation_type::none + ? specs.precision + : 6; + if (fspecs.format == float_format::exp) { + if (precision == max_value()) + throw_format_error("number is too big"); + else + ++precision; + } + if (const_check(std::is_same())) fspecs.binary32 = true; + if (!is_fast_float()) fspecs.fallback = true; + int exp = format_float(promote_float(value), precision, fspecs, buffer); + fspecs.precision = precision; + auto fp = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; + return write_float(out, fp, specs, fspecs, loc); +} + +template ::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { + if (is_constant_evaluated()) { + return write(out, value, basic_format_specs()); + } + + if (const_check(!is_supported_floating_point(value))) return out; + + using floaty = conditional_t::value, double, T>; + using uint = typename dragonbox::float_info::carrier_uint; + auto bits = bit_cast(value); + + auto fspecs = float_specs(); + if (detail::signbit(value)) { + fspecs.sign = sign::minus; + value = -value; + } + + constexpr auto specs = basic_format_specs(); + uint mask = exponent_mask(); + if ((bits & mask) == mask) + return write_nonfinite(out, std::isinf(value), specs, fspecs); + + auto dec = dragonbox::to_decimal(static_cast(value)); + return write_float(out, dec, specs, fspecs, {}); +} + +template ::value && + !is_fast_float::value)> +inline auto write(OutputIt out, T value) -> OutputIt { + return write(out, value, basic_format_specs()); +} + +template +auto write(OutputIt out, monostate, basic_format_specs = {}, + locale_ref = {}) -> OutputIt { + FMT_ASSERT(false, ""); + return out; +} + +template +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) + -> OutputIt { + auto it = reserve(out, value.size()); + it = copy_str_noinline(value.begin(), value.end(), it); + return base_iterator(out, it); +} + +template ::value)> +constexpr auto write(OutputIt out, const T& value) -> OutputIt { + return write(out, to_string_view(value)); +} + +template ::value && + !std::is_same::value && + !std::is_same::value)> +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { + auto abs_value = static_cast>(value); + bool negative = is_negative(value); + // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. + if (negative) abs_value = ~abs_value + 1; + int num_digits = count_digits(abs_value); + auto size = (negative ? 1 : 0) + static_cast(num_digits); + auto it = reserve(out, size); + if (auto ptr = to_pointer(it, size)) { + if (negative) *ptr++ = static_cast('-'); + format_decimal(ptr, abs_value, num_digits); + return out; + } + if (negative) *it++ = static_cast('-'); + it = format_decimal(it, abs_value, num_digits).end; + return base_iterator(out, it); +} + +// FMT_ENABLE_IF() condition separated to workaround an MSVC bug. +template < + typename Char, typename OutputIt, typename T, + bool check = + std::is_enum::value && !std::is_same::value && + mapped_type_constant>::value != + type::custom_type, + FMT_ENABLE_IF(check)> +FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { + return write( + out, static_cast::type>(value)); +} + +template ::value)> +FMT_CONSTEXPR auto write(OutputIt out, T value, + const basic_format_specs& specs = {}, + locale_ref = {}) -> OutputIt { + return specs.type != presentation_type::none && + specs.type != presentation_type::string + ? write(out, value ? 1 : 0, specs, {}) + : write_bytes(out, value ? "true" : "false", specs); +} + +template +FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { + auto it = reserve(out, 1); + *it++ = value; + return base_iterator(out, it); +} + +template +FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) + -> OutputIt { + if (!value) { + throw_format_error("string pointer is null"); + } else { + out = write(out, basic_string_view(value)); + } + return out; +} + +template ::value)> +auto write(OutputIt out, const T* value, + const basic_format_specs& specs = {}, locale_ref = {}) + -> OutputIt { + check_pointer_type_spec(specs.type, error_handler()); + return write_ptr(out, to_uintptr(value), &specs); +} + +// A write overload that handles implicit conversions. +template > +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< + std::is_class::value && !is_string::value && + !std::is_same::value && + !std::is_same().map(value))>::value, + OutputIt> { + return write(out, arg_mapper().map(value)); +} + +template > +FMT_CONSTEXPR auto write(OutputIt out, const T& value) + -> enable_if_t::value == type::custom_type, + OutputIt> { + using formatter_type = + conditional_t::value, + typename Context::template formatter_type, + fallback_formatter>; + auto ctx = Context(out, {}, {}); + return formatter_type().format(value, ctx); +} + +// An argument visitor that formats the argument and writes it via the output +// iterator. It's a class and not a generic lambda for compatibility with C++11. +template struct default_arg_formatter { + using iterator = buffer_appender; + using context = buffer_context; + + iterator out; + basic_format_args args; + locale_ref loc; + + template auto operator()(T value) -> iterator { + return write(out, value); + } + auto operator()(typename basic_format_arg::handle h) -> iterator { + basic_format_parse_context parse_ctx({}); + context format_ctx(out, args, loc); + h.format(parse_ctx, format_ctx); + return format_ctx.out(); + } +}; + +template struct arg_formatter { + using iterator = buffer_appender; + using context = buffer_context; + + iterator out; + const basic_format_specs& specs; + locale_ref locale; + + template + FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator { + return detail::write(out, value, specs, locale); + } + auto operator()(typename basic_format_arg::handle) -> iterator { + // User-defined types are handled separately because they require access + // to the parse context. + return out; + } +}; + +template struct custom_formatter { + basic_format_parse_context& parse_ctx; + buffer_context& ctx; + + void operator()( + typename basic_format_arg>::handle h) const { + h.format(parse_ctx, ctx); + } + template void operator()(T) const {} +}; + +template +using is_integer = + bool_constant::value && !std::is_same::value && + !std::is_same::value && + !std::is_same::value>; + +template class width_checker { + public: + explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} + + template ::value)> + FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { + if (is_negative(value)) handler_.on_error("negative width"); + return static_cast(value); + } + + template ::value)> + FMT_CONSTEXPR auto operator()(T) -> unsigned long long { + handler_.on_error("width is not integer"); + return 0; + } + + private: + ErrorHandler& handler_; +}; + +template class precision_checker { + public: + explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} + + template ::value)> + FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { + if (is_negative(value)) handler_.on_error("negative precision"); + return static_cast(value); + } + + template ::value)> + FMT_CONSTEXPR auto operator()(T) -> unsigned long long { + handler_.on_error("precision is not integer"); + return 0; + } + + private: + ErrorHandler& handler_; +}; + +template

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