Merge pull request #48 from jakcron/nstool-1.0

Update master to NSTool v1.0.4
This commit is contained in:
Jack 2018-09-23 13:34:31 +08:00 committed by GitHub
commit f09c17ed51
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
85 changed files with 6484 additions and 2468 deletions

View file

@ -20,7 +20,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<ProjectGuid>{7BE99936-0D40-410D-944B-4513C2EFF8DC}</ProjectGuid> <ProjectGuid>{7BE99936-0D40-410D-944B-4513C2EFF8DC}</ProjectGuid>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">

View file

@ -1,8 +1,6 @@
/* /*
BitMath.h BitMath.h
(c) 2018 Jakcron (c) 2018 Jakcron
This is a 0x40 byte header to prepend to raw EXEFS .code binaries that provide enough data to be equivalent to an ELF.
*/ */
#pragma once #pragma once
@ -12,3 +10,5 @@ This is a 0x40 byte header to prepend to raw EXEFS .code binaries that provide e
// Bit math macros // Bit math macros
#define _BIT(n) BIT(n) #define _BIT(n) BIT(n)
#define _HAS_BIT(val, bit) (((val) & _BIT(bit)) != 0) #define _HAS_BIT(val, bit) (((val) & _BIT(bit)) != 0)
#define _SET_BIT(val, bit) ((val) |= _BIT(bit))
#define _BITMASK(width) (_BIT(width)-1)

View file

@ -0,0 +1,141 @@
#pragma once
#include <fnd/types.h>
#include <cstdio>
namespace fnd
{
template <class T>
class SharedPtr
{
public:
SharedPtr();
// constructor for creating owner object
SharedPtr(T* ptr);
// copy constructor
SharedPtr(const SharedPtr<T>& other);
// destructor
~SharedPtr();
// own operator
void operator=(T* ptr);
// copy operator
void operator=(const SharedPtr<T>& other);
// access ptr
const T* operator*() const;
T* operator*();
private:
T* mPtr;
size_t* mRefCnt;
void deletePtr();
};
template <class T>
inline SharedPtr<T>::SharedPtr() :
mPtr(nullptr),
mRefCnt(new size_t)
{
*mRefCnt = 0;
}
template <class T>
inline SharedPtr<T>::SharedPtr(T* ptr) :
SharedPtr()
{
*this = ptr;
}
template <class T>
inline SharedPtr<T>::SharedPtr(const SharedPtr<T>& other) :
SharedPtr()
{
*this = other;
}
template <class T>
inline SharedPtr<T>::~SharedPtr()
{
deletePtr();
}
template <class T>
inline void SharedPtr<T>::operator=(T* ptr)
{
deletePtr();
if (ptr != nullptr)
{
mPtr = ptr;
mRefCnt = new size_t;
*mRefCnt = 1;
}
else
{
mPtr = nullptr;
mRefCnt = new size_t;
*mRefCnt = 0;
}
}
template <class T>
inline void SharedPtr<T>::operator=(const SharedPtr<T>& other)
{
deletePtr();
mPtr = other.mPtr;
mRefCnt = other.mRefCnt;
*mRefCnt += 1;
}
template <class T>
inline const T* SharedPtr<T>::operator*() const
{
return mPtr;
}
template <class T>
inline T* SharedPtr<T>::operator*()
{
return mPtr;
}
template <class T>
inline void SharedPtr<T>::deletePtr()
{
// if this is not the last reference
if (*mRefCnt > 1)
{
// decrement reference count
*mRefCnt -= 1;
// make ptrs null
mPtr = nullptr;
mRefCnt = nullptr;
}
// if this is the last refeference
else if (*mRefCnt == 1)
{
// delete memory
delete mPtr;
delete mRefCnt;
// make ptrs null
mPtr = nullptr;
mRefCnt = nullptr;
}
// else if this is an empty refernce
else if (*mRefCnt == 0)
{
delete mRefCnt;
mPtr = nullptr;
mRefCnt = nullptr;
}
}
}

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/Vec.h>
namespace fnd namespace fnd
{ {
@ -10,6 +12,9 @@ namespace fnd
static void hxdStyleDump(const byte_t* data, size_t len); static void hxdStyleDump(const byte_t* data, size_t len);
static void hexDump(const byte_t* data, size_t len, size_t row_len, size_t indent_len); static void hexDump(const byte_t* data, size_t len, size_t row_len, size_t indent_len);
static void hexDump(const byte_t* data, size_t len); static void hexDump(const byte_t* data, size_t len);
static std::string arrayToString(const byte_t* data, size_t len, bool upper_case, const std::string& separator);
static void stringToArray(const std::string& str, fnd::Vec<byte_t>& array);
private: private:
static const size_t kDefaultRowLen = 0x10; static const size_t kDefaultRowLen = 0x10;
static const size_t kDefaultByteGroupingSize = 1; static const size_t kDefaultByteGroupingSize = 1;

View file

@ -21,8 +21,8 @@ namespace fnd
void operator=(const sEcdsa240Point& other) void operator=(const sEcdsa240Point& other)
{ {
memcpy(this->r, r, kEcdsa240Size); memcpy(this->r, other.r, kEcdsa240Size);
memcpy(this->s, s, kEcdsa240Size); memcpy(this->s, other.s, kEcdsa240Size);
} }
bool operator==(const sEcdsa240Point& other) const bool operator==(const sEcdsa240Point& other) const
@ -43,7 +43,7 @@ namespace fnd
void operator=(const sEcdsa240PrivateKey& other) void operator=(const sEcdsa240PrivateKey& other)
{ {
memcpy(this->k, k, kEcdsa240Size); memcpy(this->k, other.k, kEcdsa240Size);
} }
bool operator==(const sEcdsa240PrivateKey& other) const bool operator==(const sEcdsa240PrivateKey& other) const
@ -56,6 +56,28 @@ namespace fnd
return !operator==(other); return !operator==(other);
} }
}; };
struct sEcdsa240Key
{
sEcdsa240Point pub;
sEcdsa240PrivateKey pvt;
void operator=(const sEcdsa240Key& other)
{
this->pub = other.pub;
this->pvt = other.pvt;
}
bool operator==(const sEcdsa240Key& other) const
{
return this->pub == other.pub && this->pvt == other.pvt;
}
bool operator!=(const sEcdsa240Key& other) const
{
return !operator==(other);
}
};
#pragma pack (pop) #pragma pack (pop)
} }
} }

View file

@ -1,216 +1,458 @@
#pragma once #pragma once
#include "types.h" #include "types.h"
typedef byte_t Elf_Byte; namespace fnd
typedef word_t Elf32_Addr;
typedef word_t Elf32_Off;
typedef long_t Elf32_Sword; // lol "sword"
typedef word_t Elf32_Word;
typedef hword_t Elf32_Half;
enum
{ {
EI_MAG0 = 0, // 0x7F namespace elf
EI_MAG1 = 1, // 'E' {
EI_MAG2 = 2, // 'L' /* These constants are for the segment types stored in the image headers */
EI_MAG3 = 3, // 'F' enum SegmentType
EI_CLASS = 4, // File class {
EI_DATA = 5, // Data encoding PT_NULL = 0,
EI_VERSION = 6, // File version PT_LOAD = 1,
EI_PAD = 7, // Start of padding bytes PT_DYNAMIC = 2,
EI_NIDENT = 16 // Size of e_ident[] PT_INTERP = 3,
}; PT_NOTE = 4,
PT_SHLIB = 5,
PT_PHDR = 6,
PT_TLS = 7, /* Thread local storage segment */
PT_LOOS = 0x60000000, /* OS-specific */
PT_HIOS = 0x6fffffff, /* OS-specific */
PT_LOPROC = 0x70000000,
PT_HIPROC = 0x7fffffff
};
typedef struct /* These constants define the different elf file types */
{ enum ElfType
unsigned char e_ident[EI_NIDENT]; // Identification bytes {
Elf32_Half e_type; // Object file type ET_NONE = 0,
Elf32_Half e_machine; // Object architecture ET_REL = 1,
Elf32_Word e_version; // Object file version ET_EXEC = 2,
Elf32_Addr e_entry; // Object entry point ET_DYN = 3,
Elf32_Off e_phoff; // Program header file offset ET_CORE = 4,
Elf32_Off e_shoff; // Section header file offset ET_LOPROC = 0xff00,
Elf32_Word e_flags; // Processor-specific flags ET_HIPROC = 0xffff
Elf32_Half e_ehsize; // ELF header size };
Elf32_Half e_phentsize; // Program header entry size
Elf32_Half e_phnum; // Program header entries
Elf32_Half e_shentsize; // Section header entry size
Elf32_Half e_shnum; // Section header entries
Elf32_Half e_shstrndx; // String table index
} Elf32_Ehdr;
typedef struct /* This is the info that is needed to parse the dynamic section of the file */
{ enum DynamicSectionType
Elf32_Word p_type; // Segment type {
Elf32_Off p_offset; // File offset DT_NULL = 0,
Elf32_Addr p_vaddr; // Virtual address DT_NEEDED = 1,
Elf32_Addr p_paddr; // Physical address DT_PLTRELSZ = 2,
Elf32_Word p_filesz; // File image size DT_PLTGOT = 3,
Elf32_Word p_memsz; // Memory image size DT_HASH = 4,
Elf32_Word p_flags; // Segment flags DT_STRTAB = 5,
Elf32_Word p_align; // Alignment value DT_SYMTAB = 6,
} Elf32_Phdr; DT_RELA = 7,
DT_RELASZ = 8,
DT_RELAENT = 9,
DT_STRSZ = 10,
DT_SYMENT = 11,
DT_INIT = 12,
DT_FINI = 13,
DT_SONAME = 14,
DT_RPATH = 15,
DT_SYMBOLIC = 16,
DT_REL = 17,
DT_RELSZ = 18,
DT_RELENT = 19,
DT_PLTREL = 20,
DT_DEBUG = 21,
DT_TEXTREL = 22,
DT_JMPREL = 23,
DT_ENCODING = 32,
OLD_DT_LOOS = 0x60000000,
DT_LOOS = 0x6000000d,
DT_HIOS = 0x6ffff000,
DT_VALRNGLO = 0x6ffffd00,
DT_VALRNGHI = 0x6ffffdff,
DT_ADDRRNGLO = 0x6ffffe00,
DT_ADDRRNGHI = 0x6ffffeff,
DT_VERSYM = 0x6ffffff0,
DT_RELACOUNT = 0x6ffffff9,
DT_RELCOUNT = 0x6ffffffa,
DT_FLAGS_1 = 0x6ffffffb,
DT_VERDEF = 0x6ffffffc,
DT_VERDEFNUM = 0x6ffffffd,
DT_VERNEED = 0x6ffffffe,
DT_VERNEEDNUM = 0x6fffffff,
OLD_DT_HIOS = 0x6fffffff,
DT_LOPROC = 0x70000000,
DT_HIPROC = 0x7fffffff
};
typedef struct /* This info is needed when parsing the symbol table */
{ enum SymbolBinding
Elf32_Word sh_name; // Name (index into section header string table section) {
Elf32_Word sh_type; // Type STB_LOCAL = 0,
Elf32_Word sh_flags; // Flags STB_GLOBAL = 1,
Elf32_Addr sh_addr; // Address STB_WEAK = 2,
Elf32_Off sh_offset; // File offset STB_LOOS = 10,
Elf32_Word sh_size; // Section size STB_HIOS = 12,
Elf32_Word sh_link; // Section header table index link STB_LOPROC,
Elf32_Word sh_info; // Extra information STB_HIPROC = 0xf
Elf32_Word sh_addralign; // Address alignment };
Elf32_Word sh_entsize; // Section entry size
} Elf32_Shdr;
typedef struct enum SymbolType
{ {
Elf32_Addr r_offset; // Offset of relocation STT_NOTYPE = 0,
Elf32_Word r_info; // Symbol table index and type STT_OBJECT = 1,
} Elf32_Rel; STT_FUNC = 2,
STT_SECTION = 3,
STT_FILE = 4,
STT_COMMON = 5,
STT_TLS = 6,
STT_LOOS = 10,
STT_HIOS = 12,
STT_LOPROC,
STT_HIPROC = 0xf
};
typedef struct /* These constants define the permissions on sections in the program
{ header, p_flags. */
Elf32_Word st_name; // Name - index into string table enum PermissionFlag
Elf32_Addr st_value; // Symbol value {
Elf32_Word st_size; // Symbol size PF_R = 0x4,
unsigned char st_info; // Type and binding PF_W = 0x2,
unsigned char st_other; // Visibility PF_X = 0x1
Elf32_Half st_shndx; // Section header index };
} Elf32_Sym;
enum /* sh_type */
{ enum SectionHeaderType
ET_NONE = 0, // No file type {
ET_REL = 1, // Relocatable file SHT_NULL = 0,
ET_EXEC = 2, // Executable file SHT_PROGBITS = 1,
ET_DYN = 3, // Shared object file SHT_SYMTAB = 2,
ET_CORE = 4, // Core file SHT_STRTAB = 3,
}; SHT_RELA = 4,
SHT_HASH = 5,
SHT_DYNAMIC = 6,
SHT_NOTE = 7,
SHT_NOBITS = 8,
SHT_REL = 9,
SHT_SHLIB = 10,
SHT_DYNSYM = 11,
SHT_NUM = 12,
SHT_LOPROC = 0x70000000,
SHT_HIPROC = 0x7fffffff,
SHT_LOUSER = 0x80000000,
SHT_HIUSER = 0xffffffff
};
enum /* sh_flags */
{ enum SectionHeaderFlag
ET_ARM = 40 // ARM architecture {
}; SHF_WRITE = 0x1,
SHF_ALLOC = 0x2,
SHF_EXECINSTR = 0x4,
SHF_RELA_LIVEPATCH = 0x00100000,
SHF_RO_AFTER_INIT = 0x00200000,
SHF_MASKPROC = 0xf0000000
};
enum /* special section indexes */
{ enum SpecialSectionIndex
EV_NONE = 0, // Invalid version {
EV_CURRENT = 1 // Current version SHN_UNDEF = 0,
}; SHN_LORESERVE = 0xff00,
SHN_LOPROC = 0xff00,
SHN_HIPROC = 0xff1f,
SHN_LOOS = 0xff20,
SHN_HIOS = 0xff3f,
SHN_ABS = 0xfff1,
SHN_COMMON = 0xfff2,
SHN_HIRESERVE = 0xffff
};
#define ELF_MAGIC "\177ELF" enum ElfIdentIndex
{
EI_MAG0 = 0, /* e_ident[] indexes */
EI_MAG1 = 1,
EI_MAG2 = 2,
EI_MAG3 = 3,
EI_CLASS = 4,
EI_DATA = 5,
EI_VERSION = 6,
EI_OSABI = 7,
EI_PAD = 8
};
enum enum ElfClass
{ {
ELFDATANONE = 0, // Invalid data encoding ELFCLASSNONE = 0, /* EI_CLASS */
ELFDATA2LSB = 1, // Little endian ELFCLASS32 = 1,
ELFDATA2MSB = 2, // Big endian ELFCLASS64 = 2,
}; ELFCLASSNUM = 3
};
enum enum ElfData
{ {
PT_NULL = 0, // Unused ELFDATANONE = 0, /* e_ident[EI_DATA] */
PT_LOAD = 1, // Loadable segment ELFDATA2LSB = 1,
PT_DYNAMIC = 2, // Dynamic linking information ELFDATA2MSB = 2
PT_INTERP = 3, // Interpreter };
PT_NOTE = 4, // Auxiliary information
PT_SHLIB = 5, // Reserved
PT_PHDR = 6 // Program header table
};
enum enum ElfVersion
{ {
PF_R = 4, // Read flag EV_NONE = 0, /* e_version, EI_VERSION */
PF_W = 2, // Write flag EV_CURRENT = 1,
PF_X = 1, // Execute flag EV_NUM = 2,
PF_OS_SHARED = 0x100000, // OS-specific };
PF_CTRSDK = 0x80000000, // Set in CTRSDK ELF Text segments
};
enum enum ElfOsAbi
{ {
SHN_LORESERVE = 0xFF00, ELFOSABI_NONE = 0,
SHN_HIRESERVE = 0xFFFF ELFOSABI_LINUX =3
}; };
enum
{
SHT_NULL = 0, // Inactive
SHT_PROGBITS = 1, // Program defined information
SHT_SYMTAB = 2, // Symbol table section
SHT_STRTAB = 3, // String table section
SHT_RELA = 4, // Relocation section with addends
SHT_HASH = 5, // Symbol hash table section
SHT_DYNAMIC = 6, // Dynamic section
SHT_NOTE = 7, // Note section
SHT_NOBITS = 8, // No space section
SHT_REL = 9, // Relation section without addends
SHT_SHLIB = 10, // Reserved
SHT_DYNSYM = 11, // Dynamic symbol table section
SHT_NUM = 12, // Number of section types
SHT_LOPROC = 0x70000000, // Reserved range for processor
SHT_ARM_EXIDX = 0x70000001, // ARM exception index table
SHT_HIPROC = 0x7fffffff, // Specific section header types
SHT_LOUSER = 0x80000000, // Reserved range for application
SHT_HIUSER = 0xffffffff // Specific indexes
};
enum /*
{ * Notes used in ET_CORE. Architectures export some of the arch register sets
SHF_WRITE = 1, // Writable section * using the corresponding note types via the PTRACE_GETREGSET and
SHF_ALLOC = 2, // Loadable section * PTRACE_SETREGSET requests.
SHF_EXECINSTR = 4, // Executable section */
SHF_MASKPROC = 0xf0000000, // Processor-specific enum NoteType
}; {
NT_PRSTATUS = 1,
NT_PRFPREG = 2,
NT_PRPSINFO = 3,
NT_TASKSTRUCT = 4,
NT_AUXV = 6,
/*
* Note to userspace developers: size of NT_SIGINFO note may increase
* in the future to accomodate more fields, don't assume it is fixed!
*/
NT_SIGINFO = 0x53494749,
NT_FILE = 0x46494c45,
NT_PRXFPREG = 0x46e62b7f, /* copied from gdb5.1/include/elf/common.h */
NT_PPC_VMX = 0x100, /* PowerPC Altivec/VMX registers */
NT_PPC_SPE = 0x101, /* PowerPC SPE/EVR registers */
NT_PPC_VSX = 0x102, /* PowerPC VSX registers */
NT_PPC_TAR = 0x103, /* Target Address Register */
NT_PPC_PPR = 0x104, /* Program Priority Register */
NT_PPC_DSCR = 0x105, /* Data Stream Control Register */
NT_PPC_EBB = 0x106, /* Event Based Branch Registers */
NT_PPC_PMU = 0x107, /* Performance Monitor Registers */
NT_PPC_TM_CGPR = 0x108, /* TM checkpointed GPR Registers */
NT_PPC_TM_CFPR = 0x109, /* TM checkpointed FPR Registers */
NT_PPC_TM_CVMX = 0x10a, /* TM checkpointed VMX Registers */
NT_PPC_TM_CVSX = 0x10b, /* TM checkpointed VSX Registers */
NT_PPC_TM_SPR = 0x10c, /* TM Special Purpose Registers */
NT_PPC_TM_CTAR = 0x10d, /* TM checkpointed Target Address Register */
NT_PPC_TM_CPPR = 0x10e, /* TM checkpointed Program Priority Register */
NT_PPC_TM_CDSCR = 0x10f, /* TM checkpointed Data Stream Control Register */
NT_PPC_PKEY = 0x110, /* Memory Protection Keys registers */
NT_386_TLS = 0x200, /* i386 TLS slots (struct user_desc) */
NT_386_IOPERM = 0x201, /* x86 io permission bitmap (1=deny) */
NT_X86_XSTATE = 0x202, /* x86 extended state using xsave */
NT_S390_HIGH_GPRS = 0x300, /* s390 upper register halves */
NT_S390_TIMER = 0x301, /* s390 timer register */
NT_S390_TODCMP = 0x302, /* s390 TOD clock comparator register */
NT_S390_TODPREG = 0x303, /* s390 TOD programmable register */
NT_S390_CTRS = 0x304, /* s390 control registers */
NT_S390_PREFIX = 0x305, /* s390 prefix register */
NT_S390_LAST_BREAK = 0x306, /* s390 breaking event address */
NT_S390_SYSTEM_CALL = 0x307, /* s390 system call restart data */
NT_S390_TDB = 0x308, /* s390 transaction diagnostic block */
NT_S390_VXRS_LOW = 0x309, /* s390 vector registers 0-15 upper half */
NT_S390_VXRS_HIGH = 0x30a, /* s390 vector registers 16-31 */
NT_S390_GS_CB = 0x30b, /* s390 guarded storage registers */
NT_S390_GS_BC = 0x30c, /* s390 guarded storage broadcast control block */
NT_S390_RI_CB = 0x30d, /* s390 runtime instrumentation */
NT_ARM_VFP = 0x400, /* ARM VFP/NEON registers */
NT_ARM_TLS = 0x401, /* ARM TLS register */
NT_ARM_HW_BREAK = 0x402, /* ARM hardware breakpoint registers */
NT_ARM_HW_WATCH = 0x403, /* ARM hardware watchpoint registers */
NT_ARM_SYSTEM_CALL = 0x404, /* ARM system call number */
NT_ARM_SVE = 0x405, /* ARM Scalable Vector Extension registers */
NT_ARC_V2 = 0x600, /* ARCv2 accumulator/extra registers */
NT_VMCOREDD = 0x700, /* Vmcore Device Dump Note */
NT_MIPS_DSP = 0x800, /* MIPS DSP ASE registers */
NT_MIPS_FP_MODE = 0x801, /* MIPS floating-point mode */
};
#define ELF32_R_SYM(i) ((i) >> 8) static const size_t kEIdentSize = 0x10;
#define ELF32_R_TYPE(i) ((unsigned char)(i)) static const byte_t kElfMagic[sizeof(uint32_t)] = {0x7f, 'E', 'L', 'F'};
#define ELF32_R_INFO(s,t) (((s) << 8) + (unsigned char)(t))
enum
{
R_ARM_NONE = 0,
R_ARM_PC24 = 1,
R_ARM_ABS32 = 2,
R_ARM_REL32 = 3,
R_ARM_THM_CALL = 10,
R_ARM_PLT32 = 27,
R_ARM_CALL = 28,
R_ARM_JUMP24 = 29,
R_ARM_TARGET1 = 38,
R_ARM_TARGET2 = 41,
R_ARM_PREL31 = 42,
R_ARM_THM_JUMP11 = 102,
R_ARM_THM_JUMP8 = 103
};
// Symbol scope inline byte_t get_elf_st_bind(byte_t st_info) { return st_info >> 4; }
enum inline byte_t get_elf_st_type(byte_t st_info) { return st_info & 0xf; }
{ inline byte_t get_elf_st_info(byte_t st_bind, byte_t st_type) { return (st_type & 0xf) | ((st_bind & 0xf) << 4);}
STB_LOCAL = 0,
STB_GLOBAL = 1,
STB_WEAK = 2
};
#define ELF32_ST_BIND(i) (((unsigned char)(i)) >> 4) /* The following are used with relocations */
#define ELF32_ST_TYPE(val) ((val) & 0xf) #define ELF32_R_SYM(x) ((x) >> 8)
#define ELF32_R_TYPE(x) ((x) & 0xff)
// Symbol type #define ELF64_R_SYM(i) ((i) >> 32)
enum #define ELF64_R_TYPE(i) ((i) & 0xffffffff)
{ }
STT_NOTYPE = 0,
STT_OBJECT = 1,
STT_FUNC = 2
};
// Symbol visibility struct Elf32_Dyn
enum {
{ int32_t d_tag;
STV_DEFAULT = 0, union{
STV_INTERNAL = 1, int32_t d_val;
STV_HIDDEN = 2, uint32_t d_ptr;
STV_PROTECTED = 3 } d_un;
}; };
struct Elf64_Dyn
{
int64_t d_tag; /* entry tag value */
union {
uint64_t d_val;
uint64_t d_ptr;
} d_un;
};
struct Elf32_Rel
{
uint32_t r_offset;
uint32_t r_info;
};
struct Elf64_Rel
{
uint64_t r_offset; /* Location at which to apply the action */
uint64_t r_info; /* index and type of relocation */
};
struct Elf32_Rela
{
uint32_t r_offset;
uint32_t r_info;
int32_t r_addend;
};
struct Elf64_Rela
{
uint64_t r_offset; /* Location at which to apply the action */
uint64_t r_info; /* index and type of relocation */
int64_t r_addend; /* Constant addend used to compute value */
};
struct Elf32_Sym
{
uint32_t st_name;
uint32_t st_value;
uint32_t st_size;
byte_t st_info;
byte_t st_other;
uint16_t st_shndx;
};
struct Elf64_Sym
{
uint32_t st_name; /* Symbol name, index in string tbl */
byte_t st_info; /* Type and binding attributes */
byte_t st_other; /* No defined meaning, 0 */
uint16_t st_shndx; /* Associated section index */
uint64_t st_value; /* Value of the symbol */
uint64_t st_size; /* Associated symbol size */
};
struct Elf32_Ehdr
{
byte_t e_ident[elf::kEIdentSize];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint32_t e_entry; /* Entry point */
uint32_t e_phoff;
uint32_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct Elf64_Ehdr
{
byte_t e_ident[elf::kEIdentSize]; /* ELF "magic number" */
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint64_t e_entry; /* Entry point virtual address */
uint64_t e_phoff; /* Program header table file offset */
uint64_t e_shoff; /* Section header table file offset */
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct Elf32_Phdr
{
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
};
struct Elf64_Phdr
{
uint32_t p_type;
uint32_t p_flags;
uint64_t p_offset; /* Segment file offset */
uint64_t p_vaddr; /* Segment virtual address */
uint64_t p_paddr; /* Segment physical address */
uint64_t p_filesz; /* Segment size in file */
uint64_t p_memsz; /* Segment size in memory */
uint64_t p_align; /* Segment alignment, file & memory */
};
struct Elf32_Shdr
{
uint32_t sh_name;
uint32_t sh_type;
uint32_t sh_flags;
uint32_t sh_addr;
uint32_t sh_offset;
uint32_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint32_t sh_addralign;
uint32_t sh_entsize;
};
struct Elf64_Shdr
{
uint32_t sh_name; /* Section name, index in string tbl */
uint32_t sh_type; /* Type of section */
uint64_t sh_flags; /* Miscellaneous section attributes */
uint64_t sh_addr; /* Section virtual addr at execution */
uint64_t sh_offset; /* Section file offset */
uint64_t sh_size; /* Size of section in bytes */
uint32_t sh_link; /* Index of another section */
uint32_t sh_info; /* Additional section information */
uint64_t sh_addralign; /* Section alignment */
uint64_t sh_entsize; /* Entry size if section holds table */
};
/* Note header in a PT_NOTE section */
struct Elf32_Nhdr
{
uint32_t n_namesz; /* Name size */
uint32_t n_descsz; /* Content size */
uint32_t n_type; /* Content type */
};
/* Note header in a PT_NOTE section */
struct Elf64_Nhdr
{
uint32_t n_namesz; /* Name size */
uint32_t n_descsz; /* Content size */
uint32_t n_type; /* Content type */
};
}

View file

@ -114,7 +114,7 @@ namespace fnd
//int rsaSign(const sRsa1024Key& key, sha::HashType hash_type, const uint8_t* hash, uint8_t signature[kRsa1024Size]); //int rsaSign(const sRsa1024Key& key, sha::HashType hash_type, const uint8_t* hash, uint8_t signature[kRsa1024Size]);
//int rsaVerify(const sRsa1024Key& key, sha::HashType hash_type, const uint8_t* hash, const uint8_t signature[kRsa1024Size]); //int rsaVerify(const sRsa1024Key& key, sha::HashType hash_type, const uint8_t* hash, const uint8_t signature[kRsa1024Size]);
// rsa2048 // rsa2048
//int rsaSign(const sRsa2048Key& key, sha::HashType hash_type, const uint8_t* hash, uint8_t signature[kRsa2048Size]); int rsaSign(const sRsa2048Key& key, sha::HashType hash_type, const uint8_t* hash, uint8_t signature[kRsa2048Size]);
int rsaVerify(const sRsa2048Key& key, sha::HashType hash_type, const uint8_t* hash, const uint8_t signature[kRsa2048Size]); int rsaVerify(const sRsa2048Key& key, sha::HashType hash_type, const uint8_t* hash, const uint8_t signature[kRsa2048Size]);
// rsa4096 // rsa4096
//int rsaSign(const sRsa4096Key& key, sha::HashType hash_type, const uint8_t* hash, uint8_t signature[kRsa4096Size]); //int rsaSign(const sRsa4096Key& key, sha::HashType hash_type, const uint8_t* hash, uint8_t signature[kRsa4096Size]);

View file

@ -21,7 +21,7 @@
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion> <VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{4D27EDB9-5110-44FE-8CE2-D46C5AD3C55B}</ProjectGuid> <ProjectGuid>{4D27EDB9-5110-44FE-8CE2-D46C5AD3C55B}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
@ -136,6 +136,7 @@
<ClInclude Include="include\fnd\ResourceFileReader.h" /> <ClInclude Include="include\fnd\ResourceFileReader.h" />
<ClInclude Include="include\fnd\rsa.h" /> <ClInclude Include="include\fnd\rsa.h" />
<ClInclude Include="include\fnd\sha.h" /> <ClInclude Include="include\fnd\sha.h" />
<ClInclude Include="include\fnd\SharedPtr.h" />
<ClInclude Include="include\fnd\SimpleFile.h" /> <ClInclude Include="include\fnd\SimpleFile.h" />
<ClInclude Include="include\fnd\SimpleTextOutput.h" /> <ClInclude Include="include\fnd\SimpleTextOutput.h" />
<ClInclude Include="include\fnd\StringConv.h" /> <ClInclude Include="include\fnd\StringConv.h" />

View file

@ -63,6 +63,9 @@
<ClInclude Include="include\fnd\sha.h"> <ClInclude Include="include\fnd\sha.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\fnd\SharedPtr.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\fnd\SimpleFile.h"> <ClInclude Include="include\fnd\SimpleFile.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>

View file

@ -1,5 +1,8 @@
#include <fnd/SimpleTextOutput.h> #include <iostream>
#include <iomanip>
#include <sstream>
#include <cstdio> #include <cstdio>
#include <fnd/SimpleTextOutput.h>
void fnd::SimpleTextOutput::hxdStyleDump(const byte_t* data, size_t len, size_t row_len, size_t byte_grouping_size) void fnd::SimpleTextOutput::hxdStyleDump(const byte_t* data, size_t len, size_t row_len, size_t byte_grouping_size)
{ {
@ -58,31 +61,57 @@ void fnd::SimpleTextOutput::hxdStyleDump(const byte_t* data, size_t len)
void fnd::SimpleTextOutput::hexDump(const byte_t* data, size_t len, size_t row_len, size_t indent_len) void fnd::SimpleTextOutput::hexDump(const byte_t* data, size_t len, size_t row_len, size_t indent_len)
{ {
for (size_t i = 0; i < len; i++) for (size_t i = 0; i < len; i += row_len)
{ {
if ((i % row_len) == 0) for (size_t j = 0; j < indent_len; j++)
{ std::cout << " ";
if (i > 0) std::cout << arrayToString(data+i, _MIN(len-i, row_len), true, "") << std::endl;
putchar('\n');
for (size_t j = 0; j < indent_len; j++)
{
putchar(' ');
}
}
printf("%02X", data[i]);
if ((i+1) >= len)
{
putchar('\n');
}
} }
} }
void fnd::SimpleTextOutput::hexDump(const byte_t* data, size_t len) void fnd::SimpleTextOutput::hexDump(const byte_t* data, size_t len)
{ {
std::cout << arrayToString(data, len, true, "") << std::endl;
}
std::string fnd::SimpleTextOutput::arrayToString(const byte_t* data, size_t len, bool upper_case, const std::string& separator)
{
std::stringstream ss;
if (upper_case)
ss << std::uppercase;
for (size_t i = 0; i < len; i++) for (size_t i = 0; i < len; i++)
{ {
printf("%02X", data[i]); ss << std::hex << std::setw(2) << std::setfill('0') << (uint32_t)data[i];
if (i+1 < len)
ss << separator;
}
return ss.str();
}
inline byte_t charToByte(char chr)
{
if (chr >= 'a' && chr <= 'f')
return (chr - 'a') + 0xa;
else if (chr >= 'A' && chr <= 'F')
return (chr - 'A') + 0xa;
else if (chr >= '0' && chr <= '9')
return chr - '0';
return 0;
}
void fnd::SimpleTextOutput::stringToArray(const std::string& str, fnd::Vec<byte_t>& array)
{
size_t size = str.size();
if ((size % 2))
{
return;
}
array.alloc(size/2);
for (size_t i = 0; i < array.size(); i++)
{
array[i] = (charToByte(str[i * 2]) << 4) | charToByte(str[(i * 2) + 1]);
} }
putchar('\n');
} }

View file

@ -1,6 +1,8 @@
#include <fnd/rsa.h> #include <fnd/rsa.h>
#include <polarssl/rsa.h> #include <polarssl/rsa.h>
#include <polarssl/md.h> #include <polarssl/md.h>
#include <polarssl/entropy.h>
#include <polarssl/ctr_drbg.h>
using namespace fnd::rsa; using namespace fnd::rsa;
using namespace fnd::sha; using namespace fnd::sha;
@ -165,6 +167,33 @@ int fnd::rsa::pkcs::rsaVerify(const sRsa4096Key & key, HashType hash_type, const
return ret; return ret;
} }
int fnd::rsa::pss::rsaSign(const sRsa2048Key & key, HashType hash_type, const uint8_t * hash, uint8_t signature[kRsa2048Size])
{
int ret;
const char* pers = "fnd::rsa::pss::rsaSign";
// rsa
rsa_context rsa;
rsa_init(&rsa, RSA_PKCS_V21, getMdWrappedHashType(hash_type));
rsa.len = kRsa2048Size;
mpi_read_binary(&rsa.D, key.priv_exponent, rsa.len);
mpi_read_binary(&rsa.N, key.modulus, rsa.len);
entropy_context entropy;
entropy_init(&entropy);
ctr_drbg_context ctr_drbg;
ctr_drbg_init(&ctr_drbg, entropy_func, &entropy, (const uint8_t*)pers, strlen(pers));
ret = rsa_rsassa_pss_sign(&rsa, ctr_drbg_random, &ctr_drbg, RSA_PRIVATE, getWrappedHashType(hash_type), getWrappedHashSize(hash_type), hash, signature);
rsa_free(&rsa);
return ret;
}
int fnd::rsa::pss::rsaVerify(const sRsa2048Key & key, HashType hash_type, const uint8_t * hash, const uint8_t signature[kRsa2048Size]) int fnd::rsa::pss::rsaVerify(const sRsa2048Key & key, HashType hash_type, const uint8_t * hash, const uint8_t signature[kRsa2048Size])
{ {
static const uint8_t public_exponent[3] = { 0x01, 0x00, 0x01 }; static const uint8_t public_exponent[3] = { 0x01, 0x00, 0x01 };

View file

@ -120,18 +120,18 @@ namespace hac
struct AddOnContentMetaExtendedHeader struct AddOnContentMetaExtendedHeader
{ {
uint64_t application_id; uint64_t application_id;
uint32_t required_system_version; uint32_t required_application_version;
void operator=(const AddOnContentMetaExtendedHeader& other) void operator=(const AddOnContentMetaExtendedHeader& other)
{ {
application_id = other.application_id; application_id = other.application_id;
required_system_version = other.required_system_version; required_application_version = other.required_application_version;
} }
bool operator==(const AddOnContentMetaExtendedHeader& other) const bool operator==(const AddOnContentMetaExtendedHeader& other) const
{ {
return (application_id == other.application_id) \ return (application_id == other.application_id) \
&& (required_system_version == other.required_system_version); && (required_application_version == other.required_application_version);
} }
bool operator!=(const AddOnContentMetaExtendedHeader& other) const bool operator!=(const AddOnContentMetaExtendedHeader& other) const

View file

@ -0,0 +1,21 @@
#pragma once
#include <fnd/types.h>
namespace nn
{
namespace hac
{
class IdConverter
{
public:
static uint64_t convertToAocBaseId(uint64_t application_id);
static uint64_t convertToDeltaId(uint64_t application_id);
static uint64_t convertToPatchId(uint64_t application_id);
private:
static const uint64_t kAocBaseId = 0x1000;
static const uint64_t kDeltaId = 0xc00;
static const uint64_t kPatchId = 0x800;
};
}
}

View file

@ -0,0 +1,39 @@
#pragma once
#include <fnd/types.h>
namespace nn
{
namespace hac
{
class Result
{
public:
Result();
Result(uint32_t result);
Result(uint32_t module_num, uint32_t desc, uint32_t sub_desc);
void operator=(const Result& other);
bool operator==(const Result& other) const;
bool operator!=(const Result& other) const;
bool isSuccess() const;
bool isFailure() const;
uint32_t getInnerValue() const;
uint32_t getModuleNum() const;
uint32_t getDescription() const;
uint32_t getSubDescription() const;
private:
static const uint32_t kModuleNumBitWidth = 9;
static const uint32_t kModuleNumBitPos = 0;
static const uint32_t kDescriptionBitWidth = 13;
static const uint32_t kDescriptionBitPos = 9;
static const uint32_t kSubDescriptionBitWidth = 10;
static const uint32_t kSubDescriptionBitPos = 22;
inline uint32_t bitWidthToMask(uint32_t bit_width) const { return _BIT(bit_width) - 1; }
uint32_t mResult;
};
}
}

View file

@ -118,7 +118,7 @@ namespace hac
struct sAddOnContentMetaExtendedHeader struct sAddOnContentMetaExtendedHeader
{ {
le_uint64_t application_id; le_uint64_t application_id;
le_uint32_t required_system_version; le_uint32_t required_application_version;
byte_t reserved[4]; byte_t reserved[4];
}; };

View file

@ -1,70 +0,0 @@
#pragma once
#include <fnd/types.h>
namespace nn
{
namespace hac
{
namespace elf
{
enum SpecialSectionIndex
{
SHN_UNDEF,
SHN_LORESERVE = 0xFF00,
SHN_LOPROC = 0xFF00,
SHN_HIPROC = 0xFF1F,
SHN_LOOS,
SHN_HIOS = 0xFF3F,
SHN_ABS = 0xFFF1,
SHN_COMMON,
SHN_HIRESERVE = 0xFFFF
};
enum SymbolType
{
STT_NOTYPE,
STT_OBJECT,
STT_FUNC,
STT_SECTION,
STT_FILE,
STT_LOOS = 10,
STT_HIOS = 12,
STT_LOPROC,
STT_HIPROC = 0xF
};
enum SymbolBinding
{
STB_LOCAL,
STB_GLOBAL,
STB_WEAK,
STB_LOOS = 10,
STB_HIOS = 12,
STB_LOPROC,
STB_HIPROC = 0xF
};
}
#pragma pack(push,1)
struct sElfSymbol32Bit
{
le_uint32_t name;
le_uint32_t value;
le_uint32_t size;
le_uint32_t info;
le_uint32_t other;
le_uint32_t special_section_index;
};
struct sElfSymbol64Bit
{
le_uint32_t name;
byte_t info;
byte_t other;
le_uint16_t special_section_index;
le_uint64_t value;
le_uint64_t size;
};
#pragma pack(pop)
}
}

View file

@ -39,6 +39,7 @@
<ClInclude Include="include\nn\hac\HierarchicalIntegrityHeader.h" /> <ClInclude Include="include\nn\hac\HierarchicalIntegrityHeader.h" />
<ClInclude Include="include\nn\hac\hierarchicalsha256.h" /> <ClInclude Include="include\nn\hac\hierarchicalsha256.h" />
<ClInclude Include="include\nn\hac\HierarchicalSha256Header.h" /> <ClInclude Include="include\nn\hac\HierarchicalSha256Header.h" />
<ClInclude Include="include\nn\hac\IdConverter.h" />
<ClInclude Include="include\nn\hac\IKernelCapabilityHandler.h" /> <ClInclude Include="include\nn\hac\IKernelCapabilityHandler.h" />
<ClInclude Include="include\nn\hac\InteruptEntry.h" /> <ClInclude Include="include\nn\hac\InteruptEntry.h" />
<ClInclude Include="include\nn\hac\InteruptHandler.h" /> <ClInclude Include="include\nn\hac\InteruptHandler.h" />
@ -67,6 +68,7 @@
<ClInclude Include="include\nn\hac\NsoHeader.h" /> <ClInclude Include="include\nn\hac\NsoHeader.h" />
<ClInclude Include="include\nn\hac\pfs.h" /> <ClInclude Include="include\nn\hac\pfs.h" />
<ClInclude Include="include\nn\hac\PfsHeader.h" /> <ClInclude Include="include\nn\hac\PfsHeader.h" />
<ClInclude Include="include\nn\hac\Result.h" />
<ClInclude Include="include\nn\hac\romfs.h" /> <ClInclude Include="include\nn\hac\romfs.h" />
<ClInclude Include="include\nn\hac\ServiceAccessControlBinary.h" /> <ClInclude Include="include\nn\hac\ServiceAccessControlBinary.h" />
<ClInclude Include="include\nn\hac\ServiceAccessControlEntry.h" /> <ClInclude Include="include\nn\hac\ServiceAccessControlEntry.h" />
@ -90,6 +92,7 @@
<ClCompile Include="source\HandleTableSizeHandler.cpp" /> <ClCompile Include="source\HandleTableSizeHandler.cpp" />
<ClCompile Include="source\HierarchicalIntegrityHeader.cpp" /> <ClCompile Include="source\HierarchicalIntegrityHeader.cpp" />
<ClCompile Include="source\HierarchicalSha256Header.cpp" /> <ClCompile Include="source\HierarchicalSha256Header.cpp" />
<ClCompile Include="source\IdConverter.cpp" />
<ClCompile Include="source\InteruptEntry.cpp" /> <ClCompile Include="source\InteruptEntry.cpp" />
<ClCompile Include="source\InteruptHandler.cpp" /> <ClCompile Include="source\InteruptHandler.cpp" />
<ClCompile Include="source\KernelCapabilityBinary.cpp" /> <ClCompile Include="source\KernelCapabilityBinary.cpp" />
@ -108,6 +111,7 @@
<ClCompile Include="source\NroHeader.cpp" /> <ClCompile Include="source\NroHeader.cpp" />
<ClCompile Include="source\NsoHeader.cpp" /> <ClCompile Include="source\NsoHeader.cpp" />
<ClCompile Include="source\PfsHeader.cpp" /> <ClCompile Include="source\PfsHeader.cpp" />
<ClCompile Include="source\Result.cpp" />
<ClCompile Include="source\ServiceAccessControlBinary.cpp" /> <ClCompile Include="source\ServiceAccessControlBinary.cpp" />
<ClCompile Include="source\ServiceAccessControlEntry.cpp" /> <ClCompile Include="source\ServiceAccessControlEntry.cpp" />
<ClCompile Include="source\SystemCallEntry.cpp" /> <ClCompile Include="source\SystemCallEntry.cpp" />
@ -121,7 +125,7 @@
<VCProjectVersion>15.0</VCProjectVersion> <VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{91BA9E79-8242-4F7D-B997-0DFEC95EA22B}</ProjectGuid> <ProjectGuid>{91BA9E79-8242-4F7D-B997-0DFEC95EA22B}</ProjectGuid>
<RootNamespace>hac</RootNamespace> <RootNamespace>hac</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
<ProjectName>libhac</ProjectName> <ProjectName>libhac</ProjectName>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

View file

@ -183,6 +183,12 @@
<ClInclude Include="include\nn\hac\XciUtils.h"> <ClInclude Include="include\nn\hac\XciUtils.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\nn\hac\IdConverter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\nn\hac\Result.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="source\AccessControlInfoBinary.cpp"> <ClCompile Include="source\AccessControlInfoBinary.cpp">
@ -296,5 +302,11 @@
<ClCompile Include="source\XciUtils.cpp"> <ClCompile Include="source\XciUtils.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\IdConverter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\Result.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -34,14 +34,10 @@ bool nn::hac::AccessControlInfoBinary::operator!=(const AccessControlInfoBinary
void nn::hac::AccessControlInfoBinary::toBytes() void nn::hac::AccessControlInfoBinary::toBytes()
{ {
if (mFileSystemAccessControl.getBytes().size() == 0) // serialise the sections
mFileSystemAccessControl.toBytes(); mFileSystemAccessControl.toBytes();
mServiceAccessControl.toBytes();
if (mServiceAccessControl.getBytes().size() == 0) mKernelCapabilities.toBytes();
mServiceAccessControl.toBytes();
if (mKernelCapabilities.getBytes().size() == 0)
mKernelCapabilities.toBytes();
// determine section layout // determine section layout
struct sLayout { struct sLayout {
@ -74,6 +70,11 @@ void nn::hac::AccessControlInfoBinary::toBytes()
hdr->sac.size = sac.size; hdr->sac.size = sac.size;
hdr->kc.offset = kc.offset; hdr->kc.offset = kc.offset;
hdr->kc.size = kc.size; hdr->kc.size = kc.size;
// write data
memcpy(mRawBinary.data() + fac.offset, mFileSystemAccessControl.getBytes().data(), fac.size);
memcpy(mRawBinary.data() + sac.offset, mServiceAccessControl.getBytes().data(), sac.size);
memcpy(mRawBinary.data() + kc.offset, mKernelCapabilities.getBytes().data(), kc.size);
} }
void nn::hac::AccessControlInfoBinary::fromBytes(const byte_t* data, size_t len) void nn::hac::AccessControlInfoBinary::fromBytes(const byte_t* data, size_t len)

View file

@ -38,14 +38,10 @@ bool nn::hac::AccessControlInfoDescBinary::operator!=(const AccessControlInfoDes
void nn::hac::AccessControlInfoDescBinary::toBytes() void nn::hac::AccessControlInfoDescBinary::toBytes()
{ {
if (mFileSystemAccessControl.getBytes().size() == 0) // serialise the sections
mFileSystemAccessControl.toBytes(); mFileSystemAccessControl.toBytes();
mServiceAccessControl.toBytes();
if (mServiceAccessControl.getBytes().size() == 0) mKernelCapabilities.toBytes();
mServiceAccessControl.toBytes();
if (mKernelCapabilities.getBytes().size() == 0)
mKernelCapabilities.toBytes();
// determine section layout // determine section layout
struct sLayout { struct sLayout {
@ -91,6 +87,11 @@ void nn::hac::AccessControlInfoDescBinary::toBytes()
hdr->sac.size = sac.size; hdr->sac.size = sac.size;
hdr->kc.offset = kc.offset; hdr->kc.offset = kc.offset;
hdr->kc.size = kc.size; hdr->kc.size = kc.size;
// write data
memcpy(mRawBinary.data() + fac.offset, mFileSystemAccessControl.getBytes().data(), fac.size);
memcpy(mRawBinary.data() + sac.offset, mServiceAccessControl.getBytes().data(), sac.size);
memcpy(mRawBinary.data() + kc.offset, mKernelCapabilities.getBytes().data(), kc.size);
} }
void nn::hac::AccessControlInfoDescBinary::fromBytes(const byte_t* data, size_t len) void nn::hac::AccessControlInfoDescBinary::fromBytes(const byte_t* data, size_t len)
@ -157,7 +158,7 @@ void nn::hac::AccessControlInfoDescBinary::generateSignature(const fnd::rsa::sRs
byte_t hash[fnd::sha::kSha256HashLen]; byte_t hash[fnd::sha::kSha256HashLen];
fnd::sha::Sha256(mRawBinary.data() + fnd::rsa::kRsa2048Size, mRawBinary.size() - fnd::rsa::kRsa2048Size, hash); fnd::sha::Sha256(mRawBinary.data() + fnd::rsa::kRsa2048Size, mRawBinary.size() - fnd::rsa::kRsa2048Size, hash);
if (fnd::rsa::pkcs::rsaSign(key, fnd::sha::HASH_SHA256, hash, mRawBinary.data()) != 0) if (fnd::rsa::pss::rsaSign(key, fnd::sha::HASH_SHA256, hash, mRawBinary.data()) != 0)
{ {
throw fnd::Exception(kModuleName, "Failed to sign Access Control Info Desc"); throw fnd::Exception(kModuleName, "Failed to sign Access Control Info Desc");
} }

View file

@ -100,7 +100,7 @@ void nn::hac::ContentMetaBinary::fromBytes(const byte_t* data, size_t len)
break; break;
case (cnmt::METATYPE_ADD_ON_CONTENT): case (cnmt::METATYPE_ADD_ON_CONTENT):
mAddOnContentMetaExtendedHeader.application_id = ((sAddOnContentMetaExtendedHeader*)mExtendedHeader.data())->application_id.get(); mAddOnContentMetaExtendedHeader.application_id = ((sAddOnContentMetaExtendedHeader*)mExtendedHeader.data())->application_id.get();
mAddOnContentMetaExtendedHeader.required_system_version = ((sAddOnContentMetaExtendedHeader*)mExtendedHeader.data())->required_system_version.get(); mAddOnContentMetaExtendedHeader.required_application_version = ((sAddOnContentMetaExtendedHeader*)mExtendedHeader.data())->required_application_version.get();
break; break;
case (cnmt::METATYPE_DELTA): case (cnmt::METATYPE_DELTA):
mDeltaMetaExtendedHeader.application_id = ((sDeltaMetaExtendedHeader*)mExtendedHeader.data())->application_id.get(); mDeltaMetaExtendedHeader.application_id = ((sDeltaMetaExtendedHeader*)mExtendedHeader.data())->application_id.get();

View file

@ -41,9 +41,17 @@ void nn::hac::FileSystemAccessControlBinary::toBytes()
} content, savedata; } content, savedata;
content.offset = (uint32_t)align(sizeof(sFacHeader), fac::kSectionAlignSize); content.offset = (uint32_t)align(sizeof(sFacHeader), fac::kSectionAlignSize);
content.size = (uint32_t)(sizeof(uint32_t) + mContentOwnerIdList.size() * sizeof(uint64_t)); if (mContentOwnerIdList.size() > 0)
content.size = (uint32_t)(sizeof(uint32_t) + mContentOwnerIdList.size() * sizeof(uint64_t));
else
content.size = 0;
savedata.offset = (uint32_t)(content.offset + (content.size > 0 ? align(content.size, fac::kSectionAlignSize) : 0)); savedata.offset = (uint32_t)(content.offset + (content.size > 0 ? align(content.size, fac::kSectionAlignSize) : 0));
savedata.size = (uint32_t)(sizeof(uint32_t) + align(mSaveDataOwnerIdList.size(), fac::kSectionAlignSize) + mSaveDataOwnerIdList.size() * sizeof(uint64_t)); if (mSaveDataOwnerIdList.size() > 0)
savedata.size = (uint32_t)(sizeof(uint32_t) + align(mSaveDataOwnerIdList.size(), fac::kSectionAlignSize) + mSaveDataOwnerIdList.size() * sizeof(uint64_t));
else
savedata.size = 0;
// get total size // get total size
size_t total_size = _MAX(_MAX(content.offset + content.size, savedata.offset + savedata.size), align(sizeof(sFacHeader), fac::kSectionAlignSize)); size_t total_size = _MAX(_MAX(content.offset + content.size, savedata.offset + savedata.size), align(sizeof(sFacHeader), fac::kSectionAlignSize));

View file

@ -0,0 +1,16 @@
#include <nn/hac/IdConverter.h>
uint64_t nn::hac::IdConverter::convertToAocBaseId(uint64_t application_id)
{
return application_id + kAocBaseId;
}
uint64_t nn::hac::IdConverter::convertToDeltaId(uint64_t application_id)
{
return application_id + kDeltaId;
}
uint64_t nn::hac::IdConverter::convertToPatchId(uint64_t application_id)
{
return application_id + kPatchId;
}

View file

@ -11,6 +11,7 @@ nn::hac::KernelCapabilityBinary::KernelCapabilityBinary(const KernelCapabilityBi
void nn::hac::KernelCapabilityBinary::operator=(const KernelCapabilityBinary & other) void nn::hac::KernelCapabilityBinary::operator=(const KernelCapabilityBinary & other)
{ {
clear(); clear();
mRawBinary = other.mRawBinary;
mThreadInfo = other.mThreadInfo; mThreadInfo = other.mThreadInfo;
mSystemCalls = other.mSystemCalls; mSystemCalls = other.mSystemCalls;
mMemoryMap = other.mMemoryMap; mMemoryMap = other.mMemoryMap;

View file

@ -132,16 +132,8 @@ void nn::hac::NpdmBinary::fromBytes(const byte_t* data, size_t len)
mMainThreadCpuId = hdr.main_thread_cpu_id; mMainThreadCpuId = hdr.main_thread_cpu_id;
mVersion = hdr.version.get(); mVersion = hdr.version.get();
mMainThreadStackSize = hdr.main_thread_stack_size.get(); mMainThreadStackSize = hdr.main_thread_stack_size.get();
mName = std::string(hdr.name, npdm::kNameMaxLen); mName = std::string(hdr.name, _MIN(strlen(hdr.name), npdm::kNameMaxLen));
if (mName[0] == '\0') mProductCode = std::string(hdr.product_code, _MIN(strlen(hdr.product_code), npdm::kProductCodeMaxLen));
{
mName.clear();
}
mProductCode = std::string(hdr.product_code, npdm::kProductCodeMaxLen);
if (mProductCode[0] == '\0')
{
mProductCode.clear();
}
// total size // total size
size_t total_size = _MAX(_MAX(hdr.acid.offset.get() + hdr.acid.size.get(), hdr.aci.offset.get() + hdr.aci.size.get()), sizeof(sNpdmHeader)); size_t total_size = _MAX(_MAX(hdr.acid.offset.get() + hdr.acid.size.get(), hdr.aci.offset.get() + hdr.aci.size.get()), sizeof(sNpdmHeader));

View file

@ -0,0 +1,62 @@
#include <nn/hac/Result.h>
nn::hac::Result::Result() :
mResult(0)
{}
nn::hac::Result::Result(uint32_t result) :
mResult(result)
{}
nn::hac::Result::Result(uint32_t module_num, uint32_t desc, uint32_t sub_desc) :
mResult(0)
{
mResult |= (module_num & bitWidthToMask(kModuleNumBitWidth)) << kModuleNumBitPos;
mResult |= (desc & bitWidthToMask(kDescriptionBitWidth)) << kDescriptionBitPos;
mResult |= (sub_desc & bitWidthToMask(kSubDescriptionBitWidth)) << kSubDescriptionBitPos;
}
void nn::hac::Result::operator=(const Result & other)
{
mResult = other.mResult;
}
bool nn::hac::Result::operator==(const Result & other) const
{
return mResult == other.mResult;
}
bool nn::hac::Result::operator!=(const Result & other) const
{
return !(*this == other);
}
bool nn::hac::Result::isSuccess() const
{
return mResult == 0;
}
bool nn::hac::Result::isFailure() const
{
return !isSuccess();
}
uint32_t nn::hac::Result::getInnerValue() const
{
return mResult;
}
uint32_t nn::hac::Result::getModuleNum() const
{
return (mResult >> kModuleNumBitPos) & bitWidthToMask(kModuleNumBitWidth);
}
uint32_t nn::hac::Result::getDescription() const
{
return (mResult >> kDescriptionBitPos) & bitWidthToMask(kDescriptionBitWidth);
}
uint32_t nn::hac::Result::getSubDescription() const
{
return (mResult >> kSubDescriptionBitPos) & bitWidthToMask(kSubDescriptionBitWidth);
}

View file

@ -54,7 +54,7 @@ void nn::hac::SystemCallHandler::exportKernelCapabilityList(fnd::List<KernelCapa
if (isSet() == false) if (isSet() == false)
return; return;
fnd::List<SystemCallEntry> entries; SystemCallEntry entries[kSyscallTotalEntryNum];
for (size_t i = 0; i < kSyscallTotalEntryNum; i++) for (size_t i = 0; i < kSyscallTotalEntryNum; i++)
{ {
entries[i].setSystemCallUpperBits((uint32_t)i); entries[i].setSystemCallUpperBits((uint32_t)i);
@ -71,7 +71,7 @@ void nn::hac::SystemCallHandler::exportKernelCapabilityList(fnd::List<KernelCapa
entries[mSystemCalls[i] / 24].setSystemCallLowerBits(entries[mSystemCalls[i] / 24].getSystemCallLowerBits() | BIT(mSystemCalls[i] % 24)); entries[mSystemCalls[i] / 24].setSystemCallLowerBits(entries[mSystemCalls[i] / 24].getSystemCallLowerBits() | BIT(mSystemCalls[i] % 24));
} }
for (size_t i = 0; i < entries.size(); i++) for (size_t i = 0; i < kSyscallTotalEntryNum; i++)
{ {
if (entries[i].getSystemCallLowerBits() != 0) if (entries[i].getSystemCallLowerBits() != 0)
{ {

View file

@ -15,8 +15,8 @@
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="source\lz4.h"> <ClInclude Include="include\lz4.h">
<Filter>Source Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -506,7 +506,7 @@
* *
* This module provides the CTR_DRBG AES-256 random number generator. * 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 * \def POLARSSL_DEBUG_C
@ -578,7 +578,7 @@
* *
* This module provides a generic entropy pool * This module provides a generic entropy pool
*/ */
//#define POLARSSL_ENTROPY_C #define POLARSSL_ENTROPY_C
/** /**
* \def POLARSSL_ERROR_C * \def POLARSSL_ERROR_C
@ -838,7 +838,7 @@
* *
* This module adds support for SHA-384 and SHA-512. * This module adds support for SHA-384 and SHA-512.
*/ */
//#define POLARSSL_SHA4_C #define POLARSSL_SHA4_C
/** /**
* \def POLARSSL_SSL_CACHE_C * \def POLARSSL_SSL_CACHE_C

View file

@ -0,0 +1,234 @@
/**
* \file ctr_drbg.h
*
* \brief CTR_DRBG based on AES-256 (NIST SP 800-90)
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
*
* This file is part of mbed TLS (https://polarssl.org)
*
* 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 <string.h>
#include "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
*
* \note If add_len is greater than CTR_DRBG_MAX_SEED_INPUT,
* only the first CTR_DRBG_MAX_SEED_INPUT bytes are used,
* the remaining ones are silently discarded.
*/
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,
* POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR 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,
* POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR 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 */

View file

@ -0,0 +1,180 @@
/**
* \file entropy.h
*
* \brief Entropy accumulator implementation
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
*
* This file is part of mbed TLS (https://polarssl.org)
*
* 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 <string.h>
#include "config.h"
#include "sha4.h"
#if defined(POLARSSL_HAVEGE_C)
#include "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. */
#define POLARSSL_ERR_ENTROPY_FILE_IO_ERROR -0x0058 /**< Read/write error in file. */
#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_MAX_SEED_SIZE 1024 /**< Maximum size of seed we read from seed file */
#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 *data, unsigned char *output, size_t len, size_t *olen);
/**
* \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 );
#if defined(POLARSSL_FS_IO)
/**
* \brief Write a seed file
*
* \param ctx Entropy context
* \param path Name of the file
*
* \return 0 if successful,
* POLARSSL_ERR_ENTROPY_FILE_IO_ERROR on file error, or
* POLARSSL_ERR_ENTROPY_SOURCE_FAILED
*/
int entropy_write_seed_file( entropy_context *ctx, const char *path );
/**
* \brief Read and update a seed file. Seed is added to this
* instance. No more than ENTROPY_MAX_SEED_SIZE bytes are
* read from the seed file. The rest is ignored.
*
* \param ctx Entropy context
* \param path Name of the file
*
* \return 0 if successful,
* POLARSSL_ERR_ENTROPY_FILE_IO_ERROR on file error,
* POLARSSL_ERR_ENTROPY_SOURCE_FAILED
*/
int entropy_update_seed_file( entropy_context *ctx, const char *path );
#endif
#ifdef __cplusplus
}
#endif
#endif /* entropy.h */

View file

@ -0,0 +1,72 @@
/**
* \file entropy_poll.h
*
* \brief Platform-specific and custom entropy polling functions
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
*
* This file is part of mbed TLS (https://polarssl.org)
*
* 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 <string.h>
#include "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 */

View file

@ -0,0 +1,183 @@
/**
* \file sha4.h
*
* \brief SHA-384 and SHA-512 cryptographic hash function
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
*
* This file is part of mbed TLS (https://polarssl.org)
*
* 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 "config.h"
#include <string.h>
#if defined(_MSC_VER) || defined(__WATCOMC__)
#define UL64(x) x##ui64
typedef unsigned __int64 uint64_t;
#else
#include <inttypes.h>
#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 "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 */

View file

@ -21,7 +21,7 @@
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion> <VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{394EFC16-BD3A-4538-B33D-7BA1EDB8DAC1}</ProjectGuid> <ProjectGuid>{394EFC16-BD3A-4538-B33D-7BA1EDB8DAC1}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
@ -117,30 +117,38 @@
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup>
<None Include="makefile" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="include\polarssl\aes.h" /> <ClInclude Include="include\polarssl\aes.h" />
<ClInclude Include="include\polarssl\base64.h" /> <ClInclude Include="include\polarssl\base64.h" />
<ClInclude Include="include\polarssl\bignum.h" /> <ClInclude Include="include\polarssl\bignum.h" />
<ClInclude Include="include\polarssl\bn_mul.h" /> <ClInclude Include="include\polarssl\bn_mul.h" />
<ClInclude Include="include\polarssl\config.h" /> <ClInclude Include="include\polarssl\config.h" />
<ClInclude Include="include\polarssl\ctr_drbg.h" />
<ClInclude Include="include\polarssl\entropy.h" />
<ClInclude Include="include\polarssl\entropy_poll.h" />
<ClInclude Include="include\polarssl\md.h" /> <ClInclude Include="include\polarssl\md.h" />
<ClInclude Include="include\polarssl\md_wrap.h" /> <ClInclude Include="include\polarssl\md_wrap.h" />
<ClInclude Include="include\polarssl\rsa.h" /> <ClInclude Include="include\polarssl\rsa.h" />
<ClInclude Include="include\polarssl\sha1.h" /> <ClInclude Include="include\polarssl\sha1.h" />
<ClInclude Include="include\polarssl\sha2.h" /> <ClInclude Include="include\polarssl\sha2.h" />
<ClInclude Include="include\polarssl\sha4.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="source\aes.c" /> <ClCompile Include="source\aes.c" />
<ClCompile Include="source\base64.c" /> <ClCompile Include="source\base64.c" />
<ClCompile Include="source\bignum.c" /> <ClCompile Include="source\bignum.c" />
<ClCompile Include="source\ctr_drbg.c" />
<ClCompile Include="source\entropy.c" />
<ClCompile Include="source\entropy_poll.c" />
<ClCompile Include="source\md.c" /> <ClCompile Include="source\md.c" />
<ClCompile Include="source\md_wrap.c" /> <ClCompile Include="source\md_wrap.c" />
<ClCompile Include="source\rsa.c" /> <ClCompile Include="source\rsa.c" />
<ClCompile Include="source\sha1.c" /> <ClCompile Include="source\sha1.c" />
<ClCompile Include="source\sha2.c" /> <ClCompile Include="source\sha2.c" />
</ItemGroup> <ClCompile Include="source\sha4.c" />
<ItemGroup>
<None Include="makefile" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View file

@ -14,6 +14,9 @@
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="makefile" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="include\polarssl\aes.h"> <ClInclude Include="include\polarssl\aes.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
@ -30,6 +33,21 @@
<ClInclude Include="include\polarssl\config.h"> <ClInclude Include="include\polarssl\config.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\polarssl\ctr_drbg.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\polarssl\entropy.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\polarssl\entropy_poll.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\polarssl\md.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\polarssl\md_wrap.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\polarssl\rsa.h"> <ClInclude Include="include\polarssl\rsa.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -39,10 +57,7 @@
<ClInclude Include="include\polarssl\sha2.h"> <ClInclude Include="include\polarssl\sha2.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\polarssl\md.h"> <ClInclude Include="include\polarssl\sha4.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\polarssl\md_wrap.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
@ -56,6 +71,15 @@
<ClCompile Include="source\bignum.c"> <ClCompile Include="source\bignum.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\ctr_drbg.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\entropy.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\entropy_poll.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\md.c"> <ClCompile Include="source\md.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -71,8 +95,8 @@
<ClCompile Include="source\sha2.c"> <ClCompile Include="source\sha2.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
</ItemGroup> <ClCompile Include="source\sha4.c">
<ItemGroup> <Filter>Source Files</Filter>
<None Include="makefile" /> </ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -0,0 +1,582 @@
/*
* CTR_DRBG implementation based on AES-256 (NIST SP 800-90)
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
*
* This file is part of mbed TLS (https://polarssl.org)
*
* 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 <stdio.h>
#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 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;
}
static 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, *iv;
aes_context aes_ctx;
int i, j, buf_len, use_len;
if( data_len > CTR_DRBG_MAX_SEED_INPUT )
return( POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG );
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 );
}
static 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 )
{
/* MAX_INPUT would be more logical here, but we have to match
* block_cipher_df()'s limits since we can't propagate errors */
if( add_len > CTR_DRBG_MAX_SEED_INPUT )
add_len = CTR_DRBG_MAX_SEED_INPUT;
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 )
{
fclose( f );
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 )
{
fclose( f );
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 <stdio.h>
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 };
static size_t test_offset;
static 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

View file

@ -0,0 +1,261 @@
/*
* Entropy accumulator implementation
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
*
* This file is part of mbed TLS (https://polarssl.org)
*
* 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_FS_IO)
#include <stdio.h>
#endif
#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
*/
static 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 );
/*
* 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 );
/*
* Perform second SHA-512 on entropy
*/
sha4( buf, ENTROPY_BLOCK_SIZE, buf, 0 );
for( i = 0; i < ctx->source_count; i++ )
ctx->source[i].size = 0;
memcpy( output, buf, len );
return( 0 );
}
#if defined(POLARSSL_FS_IO)
int entropy_write_seed_file( entropy_context *ctx, const char *path )
{
int ret = POLARSSL_ERR_ENTROPY_FILE_IO_ERROR;
FILE *f;
unsigned char buf[ENTROPY_BLOCK_SIZE];
if( ( f = fopen( path, "wb" ) ) == NULL )
return( POLARSSL_ERR_ENTROPY_FILE_IO_ERROR );
if( ( ret = entropy_func( ctx, buf, ENTROPY_BLOCK_SIZE ) ) != 0 )
goto exit;
if( fwrite( buf, 1, ENTROPY_BLOCK_SIZE, f ) != ENTROPY_BLOCK_SIZE )
{
ret = POLARSSL_ERR_ENTROPY_FILE_IO_ERROR;
goto exit;
}
ret = 0;
exit:
fclose( f );
return( ret );
}
int entropy_update_seed_file( entropy_context *ctx, const char *path )
{
FILE *f;
size_t n;
unsigned char buf[ ENTROPY_MAX_SEED_SIZE ];
if( ( f = fopen( path, "rb" ) ) == NULL )
return( POLARSSL_ERR_ENTROPY_FILE_IO_ERROR );
fseek( f, 0, SEEK_END );
n = (size_t) ftell( f );
fseek( f, 0, SEEK_SET );
if( n > ENTROPY_MAX_SEED_SIZE )
n = ENTROPY_MAX_SEED_SIZE;
if( fread( buf, 1, n, f ) != n )
{
fclose( f );
return( POLARSSL_ERR_ENTROPY_FILE_IO_ERROR );
}
fclose( f );
entropy_update_manual( ctx, buf, n );
return( entropy_write_seed_file( ctx, path ) );
}
#endif /* POLARSSL_FS_IO */
#endif

View file

@ -0,0 +1,133 @@
/*
* Platform-specific and custom entropy polling functions
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
*
* This file is part of mbed TLS (https://polarssl.org)
*
* 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 <windows.h>
#include <wincrypt.h>
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 <stdio.h>
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 */

View file

@ -0,0 +1,762 @@
/*
* FIPS-180-2 compliant SHA-384/512 implementation
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
*
* This file is part of mbed TLS (https://polarssl.org)
*
* 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 <stdio.h>
#endif
#if !defined(POLARSSL_SHA4_ALT)
/* Implementation that should never be optimized out by the compiler */
static void polarssl_zeroize( void *v, size_t n ) {
volatile unsigned char *p = v; while( n-- ) *p++ = 0;
}
/*
* 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 );
polarssl_zeroize( &ctx, 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 );
polarssl_zeroize( &ctx, 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 );
polarssl_zeroize( sum, 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 );
polarssl_zeroize( tmpbuf, 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 );
polarssl_zeroize( &ctx, 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

View file

@ -181,16 +181,17 @@
<ClInclude Include="source\AesCtrWrappedIFile.h" /> <ClInclude Include="source\AesCtrWrappedIFile.h" />
<ClInclude Include="source\AssetProcess.h" /> <ClInclude Include="source\AssetProcess.h" />
<ClInclude Include="source\CnmtProcess.h" /> <ClInclude Include="source\CnmtProcess.h" />
<ClInclude Include="source\common.h" />
<ClInclude Include="source\ElfSymbolParser.h" /> <ClInclude Include="source\ElfSymbolParser.h" />
<ClInclude Include="source\EsTikProcess.h" /> <ClInclude Include="source\EsTikProcess.h" />
<ClInclude Include="source\HashTreeMeta.h" /> <ClInclude Include="source\HashTreeMeta.h" />
<ClInclude Include="source\HashTreeWrappedIFile.h" /> <ClInclude Include="source\HashTreeWrappedIFile.h" />
<ClInclude Include="source\KeyConfiguration.h" />
<ClInclude Include="source\NacpProcess.h" /> <ClInclude Include="source\NacpProcess.h" />
<ClInclude Include="source\NcaProcess.h" /> <ClInclude Include="source\NcaProcess.h" />
<ClInclude Include="source\NpdmProcess.h" /> <ClInclude Include="source\NpdmProcess.h" />
<ClInclude Include="source\NroProcess.h" /> <ClInclude Include="source\NroProcess.h" />
<ClInclude Include="source\NsoProcess.h" /> <ClInclude Include="source\NsoProcess.h" />
<ClInclude Include="source\nstool.h" />
<ClInclude Include="source\OffsetAdjustedIFile.h" /> <ClInclude Include="source\OffsetAdjustedIFile.h" />
<ClInclude Include="source\PfsProcess.h" /> <ClInclude Include="source\PfsProcess.h" />
<ClInclude Include="source\PkiCertProcess.h" /> <ClInclude Include="source\PkiCertProcess.h" />
@ -210,6 +211,7 @@
<ClCompile Include="source\EsTikProcess.cpp" /> <ClCompile Include="source\EsTikProcess.cpp" />
<ClCompile Include="source\HashTreeMeta.cpp" /> <ClCompile Include="source\HashTreeMeta.cpp" />
<ClCompile Include="source\HashTreeWrappedIFile.cpp" /> <ClCompile Include="source\HashTreeWrappedIFile.cpp" />
<ClCompile Include="source\KeyConfiguration.cpp" />
<ClCompile Include="source\main.cpp" /> <ClCompile Include="source\main.cpp" />
<ClCompile Include="source\NacpProcess.cpp" /> <ClCompile Include="source\NacpProcess.cpp" />
<ClCompile Include="source\NcaProcess.cpp" /> <ClCompile Include="source\NcaProcess.cpp" />

View file

@ -40,6 +40,9 @@
<ClInclude Include="source\HashTreeWrappedIFile.h"> <ClInclude Include="source\HashTreeWrappedIFile.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="source\KeyConfiguration.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\NacpProcess.h"> <ClInclude Include="source\NacpProcess.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -55,9 +58,6 @@
<ClInclude Include="source\NsoProcess.h"> <ClInclude Include="source\NsoProcess.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="source\nstool.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\OffsetAdjustedIFile.h"> <ClInclude Include="source\OffsetAdjustedIFile.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -88,6 +88,9 @@
<ClInclude Include="source\XciProcess.h"> <ClInclude Include="source\XciProcess.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="source\common.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="source\AesCtrWrappedIFile.cpp"> <ClCompile Include="source\AesCtrWrappedIFile.cpp">
@ -111,6 +114,9 @@
<ClCompile Include="source\HashTreeWrappedIFile.cpp"> <ClCompile Include="source\HashTreeWrappedIFile.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\KeyConfiguration.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\main.cpp"> <ClCompile Include="source\main.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>

View file

@ -1,7 +1,6 @@
#include "AesCtrWrappedIFile.h" #include "AesCtrWrappedIFile.h"
AesCtrWrappedIFile::AesCtrWrappedIFile(fnd::IFile* file, bool ownIfile, const fnd::aes::sAes128Key& key, const fnd::aes::sAesIvCtr& ctr) : AesCtrWrappedIFile::AesCtrWrappedIFile(const fnd::SharedPtr<fnd::IFile>& file, const fnd::aes::sAes128Key& key, const fnd::aes::sAesIvCtr& ctr) :
mOwnIFile(ownIfile),
mFile(file), mFile(file),
mKey(key), mKey(key),
mBaseCtr(ctr), mBaseCtr(ctr),
@ -10,17 +9,9 @@ AesCtrWrappedIFile::AesCtrWrappedIFile(fnd::IFile* file, bool ownIfile, const fn
mCache.alloc(kCacheSizeAllocSize); mCache.alloc(kCacheSizeAllocSize);
} }
AesCtrWrappedIFile::~AesCtrWrappedIFile()
{
if (mOwnIFile)
{
delete mFile;
}
}
size_t AesCtrWrappedIFile::size() size_t AesCtrWrappedIFile::size()
{ {
return mFile->size(); return (*mFile)->size();
} }
void AesCtrWrappedIFile::seek(size_t offset) void AesCtrWrappedIFile::seek(size_t offset)
@ -44,8 +35,8 @@ void AesCtrWrappedIFile::read(byte_t* out, size_t len)
//printf("[%x] AesCtrWrappedIFile::read() CACHE READ: readlen=%" PRIx64 "\n", this, read_len); //printf("[%x] AesCtrWrappedIFile::read() CACHE READ: readlen=%" PRIx64 "\n", this, read_len);
mFile->seek(read_pos); (*mFile)->seek(read_pos);
mFile->read(mCache.data(), kCacheSizeAllocSize); (*mFile)->read(mCache.data(), kCacheSizeAllocSize);
fnd::aes::AesIncrementCounter(mBaseCtr.iv, read_pos>>4, mCurrentCtr.iv); fnd::aes::AesIncrementCounter(mBaseCtr.iv, read_pos>>4, mCurrentCtr.iv);
fnd::aes::AesCtr(mCache.data(), kCacheSizeAllocSize, mKey.key, mCurrentCtr.iv, mCache.data()); fnd::aes::AesCtr(mCache.data(), kCacheSizeAllocSize, mKey.key, mCurrentCtr.iv, mCache.data());
@ -81,8 +72,8 @@ void AesCtrWrappedIFile::write(const byte_t* in, size_t len)
fnd::aes::AesIncrementCounter(mBaseCtr.iv, write_pos>>4, mCurrentCtr.iv); fnd::aes::AesIncrementCounter(mBaseCtr.iv, write_pos>>4, mCurrentCtr.iv);
fnd::aes::AesCtr(mCache.data(), kCacheSizeAllocSize, mKey.key, mCurrentCtr.iv, mCache.data()); fnd::aes::AesCtr(mCache.data(), kCacheSizeAllocSize, mKey.key, mCurrentCtr.iv, mCache.data());
mFile->seek(write_pos); (*mFile)->seek(write_pos);
mFile->write(mCache.data(), kCacheSizeAllocSize); (*mFile)->write(mCache.data(), kCacheSizeAllocSize);
} }
seek(mFileOffset + len); seek(mFileOffset + len);
@ -92,7 +83,7 @@ void AesCtrWrappedIFile::write(const byte_t* in, size_t len)
{ {
memcpy(mScratch.data() + mBlockOffset, out + (i * kAesCtrScratchSize), kAesCtrScratchSize); memcpy(mScratch.data() + mBlockOffset, out + (i * kAesCtrScratchSize), kAesCtrScratchSize);
fnd::aes::AesCtr(mScratch.data(), kAesCtrScratchAllocSize, mKey.key, mCurrentCtr.iv, mScratch.data()); fnd::aes::AesCtr(mScratch.data(), kAesCtrScratchAllocSize, mKey.key, mCurrentCtr.iv, mScratch.data());
mFile->write(mScratch.data() + mBlockOffset, kAesCtrScratchSize); (*mFile)->write(mScratch.data() + mBlockOffset, kAesCtrScratchSize);
} }
if (len % kAesCtrScratchSize) if (len % kAesCtrScratchSize)
@ -101,7 +92,7 @@ void AesCtrWrappedIFile::write(const byte_t* in, size_t len)
size_t write_pos = ((len / kAesCtrScratchSize) * kAesCtrScratchSize); size_t write_pos = ((len / kAesCtrScratchSize) * kAesCtrScratchSize);
memcpy(mScratch.data() + mBlockOffset, out + write_pos, write_len); memcpy(mScratch.data() + mBlockOffset, out + write_pos, write_len);
fnd::aes::AesCtr(mScratch.data(), kAesCtrScratchAllocSize, mKey.key, mCurrentCtr.iv, mScratch.data()); fnd::aes::AesCtr(mScratch.data(), kAesCtrScratchAllocSize, mKey.key, mCurrentCtr.iv, mScratch.data());
mFile->write(mScratch.data() + mBlockOffset, write_len); (*mFile)->write(mScratch.data() + mBlockOffset, write_len);
} }
*/ */
seek(mFileOffset + len); seek(mFileOffset + len);

View file

@ -1,12 +1,12 @@
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/Vec.h> #include <fnd/Vec.h>
#include <fnd/aes.h> #include <fnd/aes.h>
class AesCtrWrappedIFile : public fnd::IFile class AesCtrWrappedIFile : public fnd::IFile
{ {
public: public:
AesCtrWrappedIFile(fnd::IFile* file, bool ownIfile, const fnd::aes::sAes128Key& key, const fnd::aes::sAesIvCtr& ctr); AesCtrWrappedIFile(const fnd::SharedPtr<fnd::IFile>& file, const fnd::aes::sAes128Key& key, const fnd::aes::sAesIvCtr& ctr);
~AesCtrWrappedIFile();
size_t size(); size_t size();
void seek(size_t offset); void seek(size_t offset);
@ -19,8 +19,7 @@ private:
static const size_t kCacheSize = 0x10000; static const size_t kCacheSize = 0x10000;
static const size_t kCacheSizeAllocSize = kCacheSize + fnd::aes::kAesBlockSize; static const size_t kCacheSizeAllocSize = kCacheSize + fnd::aes::kAesBlockSize;
bool mOwnIFile; fnd::SharedPtr<fnd::IFile> mFile;
fnd::IFile* mFile;
fnd::aes::sAes128Key mKey; fnd::aes::sAes128Key mKey;
fnd::aes::sAesIvCtr mBaseCtr, mCurrentCtr; fnd::aes::sAesIvCtr mBaseCtr, mCurrentCtr;
size_t mFileOffset; size_t mFileOffset;

View file

@ -1,3 +1,5 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleFile.h> #include <fnd/SimpleFile.h>
#include <fnd/Vec.h> #include <fnd/Vec.h>
#include "AssetProcess.h" #include "AssetProcess.h"
@ -5,39 +7,23 @@
AssetProcess::AssetProcess() : AssetProcess::AssetProcess() :
mFile(nullptr), mFile(),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)), mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false) mVerify(false)
{ {
}
AssetProcess::~AssetProcess()
{
if (mOwnIFile)
{
delete mFile;
}
} }
void AssetProcess::process() void AssetProcess::process()
{ {
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
importHeader(); importHeader();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
displayHeader(); displayHeader();
processSections(); processSections();
} }
void AssetProcess::setInputFile(fnd::IFile* file, bool ownIFile) void AssetProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
{ {
mFile = file; mFile = file;
mOwnIFile = ownIFile;
} }
void AssetProcess::setCliOutputMode(CliOutputMode type) void AssetProcess::setCliOutputMode(CliOutputMode type)
@ -74,13 +60,19 @@ void AssetProcess::setRomfsExtractPath(const std::string& path)
void AssetProcess::importHeader() void AssetProcess::importHeader()
{ {
fnd::Vec<byte_t> scratch; fnd::Vec<byte_t> scratch;
if (mFile->size() < sizeof(nn::hac::sAssetHeader))
if (*mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
if ((*mFile)->size() < sizeof(nn::hac::sAssetHeader))
{ {
throw fnd::Exception(kModuleName, "Corrupt ASET: file too small"); throw fnd::Exception(kModuleName, "Corrupt ASET: file too small");
} }
scratch.alloc(sizeof(nn::hac::sAssetHeader)); scratch.alloc(sizeof(nn::hac::sAssetHeader));
mFile->read(scratch.data(), 0, scratch.size()); (*mFile)->read(scratch.data(), 0, scratch.size());
mHdr.fromBytes(scratch.data(), scratch.size()); mHdr.fromBytes(scratch.data(), scratch.size());
} }
@ -89,21 +81,21 @@ void AssetProcess::processSections()
{ {
if (mHdr.getIconInfo().size > 0 && mIconExtractPath.isSet) if (mHdr.getIconInfo().size > 0 && mIconExtractPath.isSet)
{ {
if ((mHdr.getIconInfo().size + mHdr.getIconInfo().offset) > mFile->size()) if ((mHdr.getIconInfo().size + mHdr.getIconInfo().offset) > (*mFile)->size())
throw fnd::Exception(kModuleName, "ASET geometry for icon beyond file size"); throw fnd::Exception(kModuleName, "ASET geometry for icon beyond file size");
fnd::SimpleFile outfile(mIconExtractPath.var, fnd::SimpleFile::Create); fnd::SimpleFile outfile(mIconExtractPath.var, fnd::SimpleFile::Create);
fnd::Vec<byte_t> cache; fnd::Vec<byte_t> cache;
cache.alloc(mHdr.getIconInfo().size); cache.alloc(mHdr.getIconInfo().size);
mFile->read(cache.data(), mHdr.getIconInfo().offset, cache.size()); (*mFile)->read(cache.data(), mHdr.getIconInfo().offset, cache.size());
outfile.write(cache.data(), cache.size()); outfile.write(cache.data(), cache.size());
outfile.close(); outfile.close();
} }
if (mHdr.getNacpInfo().size > 0) if (mHdr.getNacpInfo().size > 0)
{ {
if ((mHdr.getNacpInfo().size + mHdr.getNacpInfo().offset) > mFile->size()) if ((mHdr.getNacpInfo().size + mHdr.getNacpInfo().offset) > (*mFile)->size())
throw fnd::Exception(kModuleName, "ASET geometry for nacp beyond file size"); throw fnd::Exception(kModuleName, "ASET geometry for nacp beyond file size");
if (mNacpExtractPath.isSet) if (mNacpExtractPath.isSet)
@ -112,12 +104,12 @@ void AssetProcess::processSections()
fnd::Vec<byte_t> cache; fnd::Vec<byte_t> cache;
cache.alloc(mHdr.getNacpInfo().size); cache.alloc(mHdr.getNacpInfo().size);
mFile->read(cache.data(), mHdr.getNacpInfo().offset, cache.size()); (*mFile)->read(cache.data(), mHdr.getNacpInfo().offset, cache.size());
outfile.write(cache.data(), cache.size()); outfile.write(cache.data(), cache.size());
outfile.close(); outfile.close();
} }
mNacp.setInputFile(new OffsetAdjustedIFile(mFile, false, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size), true); mNacp.setInputFile(new OffsetAdjustedIFile(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size));
mNacp.setCliOutputMode(mCliOutputMode); mNacp.setCliOutputMode(mCliOutputMode);
mNacp.setVerifyMode(mVerify); mNacp.setVerifyMode(mVerify);
@ -126,10 +118,10 @@ void AssetProcess::processSections()
if (mHdr.getRomfsInfo().size > 0) if (mHdr.getRomfsInfo().size > 0)
{ {
if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > mFile->size()) if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > (*mFile)->size())
throw fnd::Exception(kModuleName, "ASET geometry for romfs beyond file size"); throw fnd::Exception(kModuleName, "ASET geometry for romfs beyond file size");
mRomfs.setInputFile(new OffsetAdjustedIFile(mFile, false, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size), true); mRomfs.setInputFile(new OffsetAdjustedIFile(mFile, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size));
mRomfs.setCliOutputMode(mCliOutputMode); mRomfs.setCliOutputMode(mCliOutputMode);
mRomfs.setVerifyMode(mVerify); mRomfs.setVerifyMode(mVerify);
@ -141,16 +133,16 @@ void AssetProcess::displayHeader()
{ {
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
{ {
printf("[ASET Header]\n"); std::cout << "[ASET Header]" << std::endl;
printf(" Icon:\n"); std::cout << " Icon:" << std::endl;
printf(" Offset: 0x%" PRIx64 "\n", mHdr.getIconInfo().offset); std::cout << " Offset: 0x" << std::hex << mHdr.getIconInfo().offset << std::endl;
printf(" Size: 0x%" PRIx64 "\n", mHdr.getIconInfo().size); std::cout << " Size: 0x" << std::hex << mHdr.getIconInfo().size << std::endl;
printf(" NACP:\n"); std::cout << " NACP:" << std::endl;
printf(" Offset: 0x%" PRIx64 "\n", mHdr.getNacpInfo().offset); std::cout << " Offset: 0x" << std::hex << mHdr.getNacpInfo().offset << std::endl;
printf(" Size: 0x%" PRIx64 "\n", mHdr.getNacpInfo().size); std::cout << " Size: 0x" << std::hex << mHdr.getNacpInfo().size << std::endl;
printf(" RomFS:\n"); std::cout << " RomFS:" << std::endl;
printf(" Offset: 0x%" PRIx64 "\n", mHdr.getRomfsInfo().offset); std::cout << " Offset: 0x" << std::hex << mHdr.getRomfsInfo().offset << std::endl;
printf(" Size: 0x%" PRIx64 "\n", mHdr.getRomfsInfo().size); std::cout << " Size: 0x" << std::hex << mHdr.getRomfsInfo().size << std::endl;
} }
} }

View file

@ -2,21 +2,21 @@
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/AssetHeader.h> #include <nn/hac/AssetHeader.h>
#include "NacpProcess.h" #include "NacpProcess.h"
#include "RomfsProcess.h" #include "RomfsProcess.h"
#include "nstool.h" #include "common.h"
class AssetProcess class AssetProcess
{ {
public: public:
AssetProcess(); AssetProcess();
~AssetProcess();
void process(); void process();
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setCliOutputMode(CliOutputMode type); void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -30,8 +30,7 @@ public:
private: private:
const std::string kModuleName = "AssetProcess"; const std::string kModuleName = "AssetProcess";
fnd::IFile* mFile; fnd::SharedPtr<fnd::IFile> mFile;
bool mOwnIFile;
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;

View file

@ -1,184 +1,27 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h> #include <fnd/SimpleTextOutput.h>
#include "OffsetAdjustedIFile.h" #include "OffsetAdjustedIFile.h"
#include "CnmtProcess.h" #include "CnmtProcess.h"
const std::string kContentTypeStr[7] =
{
"Meta",
"Program",
"Data",
"Control",
"HtmlDocument",
"LegalInformation",
"DeltaFragment"
};
const std::string kContentMetaTypeStr[2][0x80] =
{
{
"",
"SystemProgram",
"SystemData",
"SystemUpdate",
"BootImagePackage",
"BootImagePackageSafe"
},
{
"Application",
"Patch",
"AddOnContent",
"Delta"
}
};
const std::string kUpdateTypeStr[3] =
{
"ApplyAsDelta",
"Overwrite",
"Create"
};
const std::string kContentMetaAttrStr[3] =
{
"IncludesExFatDriver",
"Rebootless"
};
std::string kUnknownStr = "Unknown";
inline const char* getBoolStr(bool isTrue)
{
return isTrue? "TRUE" : "FALSE";
}
inline const char* getContentTypeStr(byte_t i)
{
return i < 7 ? kContentTypeStr[i].c_str() : kUnknownStr.c_str();
}
inline const char* getContentMetaTypeStr(byte_t i)
{
return (i < 0x80) ? kContentMetaTypeStr[0][i].c_str() : kContentMetaTypeStr[1][i - 0x80].c_str();
}
void CnmtProcess::displayCmnt()
{
#define _SPLIT_VER(ver) ( (ver>>26) & 0x3f), ( (ver>>20) & 0x3f), ( (ver>>16) & 0xf), (ver & 0xffff)
#define _HEXDUMP_U(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02X", var[a__a__A]); } while(0)
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0)
printf("[ContentMeta]\n");
printf(" TitleId: 0x%016" PRIx64 "\n", (uint64_t)mCnmt.getTitleId());
printf(" Version: v%" PRId32 " (%d.%d.%d.%d)\n", (uint32_t)mCnmt.getTitleVersion(), _SPLIT_VER(mCnmt.getTitleVersion()));
printf(" Type: %s (%d)\n", getContentMetaTypeStr(mCnmt.getType()), mCnmt.getType());
printf(" Attributes: %x\n", mCnmt.getAttributes());
printf(" IncludesExFatDriver: %s\n", getBoolStr(_HAS_BIT(mCnmt.getAttributes(), nn::hac::cnmt::ATTRIBUTE_INCLUDES_EX_FAT_DRIVER)));
printf(" Rebootless: %s\n", getBoolStr(_HAS_BIT(mCnmt.getAttributes(), nn::hac::cnmt::ATTRIBUTE_REBOOTLESS)));
printf(" RequiredDownloadSystemVersion: v%" PRId32 " (%d.%d.%d.%d)\n", (uint32_t)mCnmt.getRequiredDownloadSystemVersion(), _SPLIT_VER(mCnmt.getRequiredDownloadSystemVersion()));
switch(mCnmt.getType())
{
case (nn::hac::cnmt::METATYPE_APPLICATION):
printf(" ApplicationExtendedHeader:\n");
printf(" RequiredSystemVersion: v%" PRId32 " (%d.%d.%d.%d)\n", (uint32_t)mCnmt.getApplicationMetaExtendedHeader().required_system_version, _SPLIT_VER(mCnmt.getApplicationMetaExtendedHeader().required_system_version));
printf(" PatchId: 0x%016" PRIx64 "\n", (uint64_t)mCnmt.getApplicationMetaExtendedHeader().patch_id);
break;
case (nn::hac::cnmt::METATYPE_PATCH):
printf(" PatchMetaExtendedHeader:\n");
printf(" RequiredSystemVersion: v%" PRId32 " (%d.%d.%d.%d))\n", (uint32_t)mCnmt.getPatchMetaExtendedHeader().required_system_version, _SPLIT_VER(mCnmt.getPatchMetaExtendedHeader().required_system_version));
printf(" ApplicationId: 0x%016" PRIx64 "\n", (uint64_t)mCnmt.getPatchMetaExtendedHeader().application_id);
break;
case (nn::hac::cnmt::METATYPE_ADD_ON_CONTENT):
printf(" AddOnContentMetaExtendedHeader:\n");
printf(" RequiredSystemVersion: v%" PRId32 " (%d.%d.%d.%d)\n", (uint32_t)mCnmt.getAddOnContentMetaExtendedHeader().required_system_version, _SPLIT_VER(mCnmt.getAddOnContentMetaExtendedHeader().required_system_version));
printf(" ApplicationId: 0x%016" PRIx64 "\n", (uint64_t)mCnmt.getAddOnContentMetaExtendedHeader().application_id);
break;
case (nn::hac::cnmt::METATYPE_DELTA):
printf(" DeltaMetaExtendedHeader:\n");
printf(" ApplicationId: 0x%016" PRIx64 "\n", (uint64_t)mCnmt.getDeltaMetaExtendedHeader().application_id);
break;
default:
break;
}
if (mCnmt.getContentInfo().size() > 0)
{
printf(" ContentInfo:\n");
for (size_t i = 0; i < mCnmt.getContentInfo().size(); i++)
{
const nn::hac::ContentMetaBinary::ContentInfo& info = mCnmt.getContentInfo()[i];
printf(" %d\n", (int)i);
printf(" Type: %s (%d)\n", getContentTypeStr(info.type), info.type);
printf(" Id: ");
_HEXDUMP_L(info.nca_id, nn::hac::cnmt::kContentIdLen);
printf("\n");
printf(" Size: 0x%" PRIx64 "\n", (uint64_t)info.size);
printf(" Hash: ");
_HEXDUMP_L(info.hash.bytes, sizeof(info.hash));
printf("\n");
}
}
if (mCnmt.getContentMetaInfo().size() > 0)
{
printf(" ContentMetaInfo:\n");
for (size_t i = 0; i < mCnmt.getContentMetaInfo().size(); i++)
{
const nn::hac::ContentMetaBinary::ContentMetaInfo& info = mCnmt.getContentMetaInfo()[i];
printf(" %d\n", (int)i);
printf(" Id: 0x%016" PRIx64 "\n", (uint64_t)info.id);
printf(" Version: v%" PRId32 " (%d.%d.%d.%d)\n", (uint32_t)info.version, _SPLIT_VER(info.version));
printf(" Type: %s (%d)\n", getContentMetaTypeStr(info.type), info.type);
printf(" Attributes: %x\n", mCnmt.getAttributes());
printf(" IncludesExFatDriver: %s\n", getBoolStr(_HAS_BIT(mCnmt.getAttributes(), nn::hac::cnmt::ATTRIBUTE_INCLUDES_EX_FAT_DRIVER)));
printf(" Rebootless: %s\n", getBoolStr(_HAS_BIT(mCnmt.getAttributes(), nn::hac::cnmt::ATTRIBUTE_REBOOTLESS)));
}
}
printf(" Digest: ");
_HEXDUMP_L(mCnmt.getDigest().data, nn::hac::cnmt::kDigestLen);
printf("\n");
#undef _HEXDUMP_L
#undef _HEXDUMP_U
#undef _SPLIT_VER
}
CnmtProcess::CnmtProcess() : CnmtProcess::CnmtProcess() :
mFile(nullptr), mFile(),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)), mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false) mVerify(false)
{ {
} }
CnmtProcess::~CnmtProcess()
{
if (mOwnIFile)
{
delete mFile;
}
}
void CnmtProcess::process() void CnmtProcess::process()
{ {
fnd::Vec<byte_t> scratch; importCnmt();
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
scratch.alloc(mFile->size());
mFile->read(scratch.data(), 0, scratch.size());
mCnmt.fromBytes(scratch.data(), scratch.size());
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
displayCmnt(); displayCnmt();
} }
void CnmtProcess::setInputFile(fnd::IFile* file, bool ownIFile) void CnmtProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
{ {
mFile = file; mFile = file;
mOwnIFile = ownIFile;
} }
void CnmtProcess::setCliOutputMode(CliOutputMode type) void CnmtProcess::setCliOutputMode(CliOutputMode type)
@ -195,3 +38,212 @@ const nn::hac::ContentMetaBinary& CnmtProcess::getContentMetaBinary() const
{ {
return mCnmt; return mCnmt;
} }
void CnmtProcess::importCnmt()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
scratch.alloc((*mFile)->size());
(*mFile)->read(scratch.data(), 0, scratch.size());
mCnmt.fromBytes(scratch.data(), scratch.size());
}
void CnmtProcess::displayCnmt()
{
#define _SPLIT_VER(ver) (uint32_t)((ver>>26) & 0x3f) << "." << (uint32_t)((ver>>20) & 0x3f) << "." << (uint32_t)((ver>>16) & 0xf) << "." << (uint32_t)(ver & 0xffff)
std::cout << "[ContentMeta]" << std::endl;
std::cout << " TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getTitleId() << std::endl;
std::cout << " Version: v" << std::dec << mCnmt.getTitleVersion() << " (" << _SPLIT_VER(mCnmt.getTitleVersion()) << ")"<< std::endl;
std::cout << " Type: " << getContentMetaTypeStr(mCnmt.getType()) << " (" << std::dec << mCnmt.getType() << ")" << std::endl;
std::cout << " Attributes: 0x" << std::hex << (uint32_t)mCnmt.getAttributes() << std::endl;
std::cout << " IncludesExFatDriver: " << getBoolStr(_HAS_BIT(mCnmt.getAttributes(), nn::hac::cnmt::ATTRIBUTE_INCLUDES_EX_FAT_DRIVER)) << std::endl;
std::cout << " Rebootless: " << getBoolStr(_HAS_BIT(mCnmt.getAttributes(), nn::hac::cnmt::ATTRIBUTE_REBOOTLESS)) << std::endl;
std::cout << " RequiredDownloadSystemVersion: v" << mCnmt.getRequiredDownloadSystemVersion() << " (" << _SPLIT_VER(mCnmt.getRequiredDownloadSystemVersion()) << ")"<< std::endl;
switch(mCnmt.getType())
{
case (nn::hac::cnmt::METATYPE_APPLICATION):
std::cout << " ApplicationExtendedHeader:" << std::endl;
std::cout << " RequiredSystemVersion: v" << std::dec << mCnmt.getApplicationMetaExtendedHeader().required_system_version << " (" << _SPLIT_VER(mCnmt.getApplicationMetaExtendedHeader().required_system_version) << ")"<< std::endl;
std::cout << " PatchId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getApplicationMetaExtendedHeader().patch_id << std::endl;
break;
case (nn::hac::cnmt::METATYPE_PATCH):
std::cout << " PatchMetaExtendedHeader:" << std::endl;
std::cout << " RequiredSystemVersion: v" << std::dec << mCnmt.getPatchMetaExtendedHeader().required_system_version << " (" << _SPLIT_VER(mCnmt.getPatchMetaExtendedHeader().required_system_version) << ")"<< std::endl;
std::cout << " ApplicationId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getPatchMetaExtendedHeader().application_id << std::endl;
break;
case (nn::hac::cnmt::METATYPE_ADD_ON_CONTENT):
std::cout << " AddOnContentMetaExtendedHeader:" << std::endl;
std::cout << " RequiredApplicationVersion: v" << std::dec << mCnmt.getAddOnContentMetaExtendedHeader().required_application_version << " (" << _SPLIT_VER(mCnmt.getAddOnContentMetaExtendedHeader().required_application_version) << ")" << std::endl;
std::cout << " ApplicationId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getAddOnContentMetaExtendedHeader().application_id << std::endl;
break;
case (nn::hac::cnmt::METATYPE_DELTA):
std::cout << " DeltaMetaExtendedHeader:" << std::endl;
std::cout << " ApplicationId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getDeltaMetaExtendedHeader().application_id << std::endl;
break;
default:
break;
}
if (mCnmt.getContentInfo().size() > 0)
{
printf(" ContentInfo:\n");
for (size_t i = 0; i < mCnmt.getContentInfo().size(); i++)
{
const nn::hac::ContentMetaBinary::ContentInfo& info = mCnmt.getContentInfo()[i];
std::cout << " " << std::dec << i << std::endl;
std::cout << " Type: " << getContentTypeStr(info.type) << " (" << std::dec << info.type << ")" << std::endl;
std::cout << " Id: " << fnd::SimpleTextOutput::arrayToString(info.nca_id, nn::hac::cnmt::kContentIdLen, false, "") << std::endl;
std::cout << " Size: 0x" << std::hex << info.size << std::endl;
std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(info.hash.bytes, sizeof(info.hash), false, "") << std::endl;
}
}
if (mCnmt.getContentMetaInfo().size() > 0)
{
std::cout << " ContentMetaInfo:" << std::endl;
for (size_t i = 0; i < mCnmt.getContentMetaInfo().size(); i++)
{
const nn::hac::ContentMetaBinary::ContentMetaInfo& info = mCnmt.getContentMetaInfo()[i];
std::cout << " " << std::dec << i << std::endl;
std::cout << " Id: 0x" << std::hex << std::setw(16) << std::setfill('0') << info.id << std::endl;
std::cout << " Version: v" << std::dec << info.version << " (" << _SPLIT_VER(info.version) << ")"<< std::endl;
std::cout << " Type: " << getContentMetaTypeStr(info.type) << " (" << std::dec << info.type << ")" << std::endl;
std::cout << " Attributes: 0x" << std::hex << (uint32_t)info.attributes << std::endl;
std::cout << " IncludesExFatDriver: " << getBoolStr(_HAS_BIT(info.attributes, nn::hac::cnmt::ATTRIBUTE_INCLUDES_EX_FAT_DRIVER)) << std::endl;
std::cout << " Rebootless: " << getBoolStr(_HAS_BIT(info.attributes, nn::hac::cnmt::ATTRIBUTE_REBOOTLESS)) << std::endl;
}
}
std::cout << " Digest: " << fnd::SimpleTextOutput::arrayToString(mCnmt.getDigest().data, nn::hac::cnmt::kDigestLen, false, "") << std::endl;
#undef _SPLIT_VER
}
const char* CnmtProcess::getBoolStr(bool state) const
{
return state? "TRUE" : "FALSE";
}
const char* CnmtProcess::getContentTypeStr(nn::hac::cnmt::ContentType type) const
{
const char* str = nullptr;
switch (type)
{
case (nn::hac::cnmt::TYPE_META):
str = "Meta";
break;
case (nn::hac::cnmt::TYPE_PROGRAM):
str = "Program";
break;
case (nn::hac::cnmt::TYPE_DATA):
str = "Data";
break;
case (nn::hac::cnmt::TYPE_CONTROL):
str = "Control";
break;
case (nn::hac::cnmt::TYPE_HTML_DOCUMENT):
str = "HtmlDocument";
break;
case (nn::hac::cnmt::TYPE_LEGAL_INFORMATION):
str = "LegalInformation";
break;
case (nn::hac::cnmt::TYPE_DELTA_FRAGMENT):
str = "DeltaFragment";
break;
default:
str = "Unknown";
break;
}
return str;
}
const char* CnmtProcess::getContentMetaTypeStr(nn::hac::cnmt::ContentMetaType type) const
{
const char* str = nullptr;
switch (type)
{
case (nn::hac::cnmt::METATYPE_SYSTEM_PROGRAM):
str = "SystemProgram";
break;
case (nn::hac::cnmt::METATYPE_SYSTEM_DATA):
str = "SystemData";
break;
case (nn::hac::cnmt::METATYPE_SYSTEM_UPDATE):
str = "SystemUpdate";
break;
case (nn::hac::cnmt::METATYPE_BOOT_IMAGE_PACKAGE):
str = "BootImagePackage";
break;
case (nn::hac::cnmt::METATYPE_BOOT_IMAGE_PACKAGE_SAFE):
str = "BootImagePackageSafe";
break;
case (nn::hac::cnmt::METATYPE_APPLICATION):
str = "Application";
break;
case (nn::hac::cnmt::METATYPE_PATCH):
str = "Patch";
break;
case (nn::hac::cnmt::METATYPE_ADD_ON_CONTENT):
str = "AddOnContent";
break;
case (nn::hac::cnmt::METATYPE_DELTA):
str = "Delta";
break;
default:
str = "Unknown";
break;
}
return str;
}
const char* CnmtProcess::getUpdateTypeStr(nn::hac::cnmt::UpdateType type) const
{
const char* str = nullptr;
switch (type)
{
case (nn::hac::cnmt::UPDATETYPE_APPLY_AS_DELTA):
str = "ApplyAsDelta";
break;
case (nn::hac::cnmt::UPDATETYPE_OVERWRITE):
str = "Overwrite";
break;
case (nn::hac::cnmt::UPDATETYPE_CREATE):
str = "Create";
break;
default:
str = "Unknown";
break;
}
return str;
}
const char* CnmtProcess::getContentMetaAttrStr(nn::hac::cnmt::ContentMetaAttribute attr) const
{
const char* str = nullptr;
switch (attr)
{
case (nn::hac::cnmt::ATTRIBUTE_INCLUDES_EX_FAT_DRIVER):
str = "IncludesExFatDriver";
break;
case (nn::hac::cnmt::ATTRIBUTE_REBOOTLESS):
str = "Rebootless";
break;
default:
str = "Unknown";
break;
}
return str;
}

View file

@ -2,19 +2,19 @@
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/ContentMetaBinary.h> #include <nn/hac/ContentMetaBinary.h>
#include "nstool.h" #include "common.h"
class CnmtProcess class CnmtProcess
{ {
public: public:
CnmtProcess(); CnmtProcess();
~CnmtProcess();
void process(); void process();
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setCliOutputMode(CliOutputMode type); void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -23,12 +23,18 @@ public:
private: private:
const std::string kModuleName = "CnmtProcess"; const std::string kModuleName = "CnmtProcess";
fnd::IFile* mFile; fnd::SharedPtr<fnd::IFile> mFile;
bool mOwnIFile;
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;
nn::hac::ContentMetaBinary mCnmt; nn::hac::ContentMetaBinary mCnmt;
void displayCmnt(); void importCnmt();
void displayCnmt();
const char* getBoolStr(bool state) const;
const char* getContentTypeStr(nn::hac::cnmt::ContentType type) const;
const char* getContentMetaTypeStr(nn::hac::cnmt::ContentMetaType type) const;
const char* getUpdateTypeStr(nn::hac::cnmt::UpdateType type) const;
const char* getContentMetaAttrStr(nn::hac::cnmt::ContentMetaAttribute attr) const;
}; };

View file

@ -22,38 +22,38 @@ bool ElfSymbolParser::operator!=(const ElfSymbolParser& other) const
void ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit) void ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit)
{ {
//printf("ElfSymbolParser::parseData()"); size_t dynSymSize = is64Bit ? sizeof(fnd::Elf64_Sym) : sizeof(fnd::Elf32_Sym);
size_t dynSymSize = is64Bit ? sizeof(nn::hac::sElfSymbol64Bit) : sizeof(nn::hac::sElfSymbol32Bit);
sElfSymbol symbol; sElfSymbol symbol;
for (size_t i = 0; i < dyn_sym_size; i += dynSymSize) for (size_t i = 0; i < dyn_sym_size; i += dynSymSize)
{ {
//printf("pos %x\n", i);
uint32_t name_pos; uint32_t name_pos;
if (is64Bit) if (is64Bit)
{ {
name_pos = ((nn::hac::sElfSymbol64Bit*)(dyn_sym + i))->name.get(); name_pos = le_word(((fnd::Elf64_Sym*)(dyn_sym + i))->st_name);
symbol.shn_index = (nn::hac::elf::SpecialSectionIndex)((nn::hac::sElfSymbol64Bit*)(dyn_sym + i))->special_section_index.get(); symbol.shn_index = le_hword(((fnd::Elf64_Sym*)(dyn_sym + i))->st_shndx);
symbol.symbol_type = (nn::hac::elf::SymbolType)((((nn::hac::sElfSymbol64Bit*)(dyn_sym + i))->info) & nn::hac::elf::STT_HIPROC); symbol.symbol_type = fnd::elf::get_elf_st_type(((fnd::Elf64_Sym*)(dyn_sym + i))->st_info);
symbol.symbol_binding = (nn::hac::elf::SymbolBinding)(((((nn::hac::sElfSymbol64Bit*)(dyn_sym + i))->info) >> 4) & nn::hac::elf::STB_HIPROC); symbol.symbol_binding = fnd::elf::get_elf_st_bind(((fnd::Elf64_Sym*)(dyn_sym + i))->st_info);
} }
else else
{ {
name_pos = ((nn::hac::sElfSymbol64Bit*)(dyn_sym + i))->name.get(); name_pos = le_word(((fnd::Elf32_Sym*)(dyn_sym + i))->st_name);
symbol.shn_index = (nn::hac::elf::SpecialSectionIndex)((nn::hac::sElfSymbol32Bit*)(dyn_sym + i))->special_section_index.get(); symbol.shn_index = le_hword(((fnd::Elf32_Sym*)(dyn_sym + i))->st_shndx);
symbol.symbol_type = (nn::hac::elf::SymbolType)((((nn::hac::sElfSymbol32Bit*)(dyn_sym + i))->info.get()) & nn::hac::elf::STT_HIPROC); symbol.symbol_type = fnd::elf::get_elf_st_type(((fnd::Elf32_Sym*)(dyn_sym + i))->st_info);
symbol.symbol_binding = (nn::hac::elf::SymbolBinding)(((((nn::hac::sElfSymbol32Bit*)(dyn_sym + i))->info.get()) >> 4) & nn::hac::elf::STB_HIPROC); symbol.symbol_binding = fnd::elf::get_elf_st_bind(((fnd::Elf32_Sym*)(dyn_sym + i))->st_info);
} }
for (; dyn_str[name_pos] == 0x00 && name_pos < dyn_str_size; name_pos++); if (name_pos >= dyn_str_size)
{
throw fnd::Exception(kModuleName, "Out of bounds symbol name offset");
}
//for (; dyn_str[name_pos] == 0x00 && name_pos < dyn_str_size; name_pos++);
//printf("name_pos = 0x%x\n", name_pos);
symbol.name = std::string((char*)&dyn_str[name_pos]); symbol.name = std::string((char*)&dyn_str[name_pos]);
mSymbolList.addElement(symbol); mSymbolList.addElement(symbol);
} }
//printf("ElfSymbolParser::parseData() end\n");
} }
const fnd::List<ElfSymbolParser::sElfSymbol>& ElfSymbolParser::getSymbolList() const const fnd::List<ElfSymbolParser::sElfSymbol>& ElfSymbolParser::getSymbolList() const

View file

@ -1,16 +1,16 @@
#pragma once #pragma once
#include <string> #include <string>
#include <fnd/List.h> #include <fnd/List.h>
#include <nn/hac/elf.h> #include <fnd/elf.h>
class ElfSymbolParser class ElfSymbolParser
{ {
public: public:
struct sElfSymbol struct sElfSymbol
{ {
nn::hac::elf::SpecialSectionIndex shn_index; uint16_t shn_index;
nn::hac::elf::SymbolType symbol_type; byte_t symbol_type;
nn::hac::elf::SymbolBinding symbol_binding; byte_t symbol_binding;
std::string name; std::string name;
void operator=(const sElfSymbol& other) void operator=(const sElfSymbol& other)
@ -42,6 +42,7 @@ public:
const fnd::List<sElfSymbol>& getSymbolList() const; const fnd::List<sElfSymbol>& getSymbolList() const;
private: private:
const std::string kModuleName = "ElfSymbolParser";
// data // data
fnd::List<sElfSymbol> mSymbolList; fnd::List<sElfSymbol> mSymbolList;

View file

@ -1,6 +1,5 @@
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <fnd/SimpleTextOutput.h> #include <fnd/SimpleTextOutput.h>
#include <nn/pki/SignUtils.h> #include <nn/pki/SignUtils.h>
#include "OffsetAdjustedIFile.h" #include "OffsetAdjustedIFile.h"
@ -10,21 +9,12 @@
EsTikProcess::EsTikProcess() : EsTikProcess::EsTikProcess() :
mFile(nullptr), mFile(),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)), mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false) mVerify(false)
{ {
} }
EsTikProcess::~EsTikProcess()
{
if (mOwnIFile)
{
delete mFile;
}
}
void EsTikProcess::process() void EsTikProcess::process()
{ {
importTicket(); importTicket();
@ -36,15 +26,14 @@ void EsTikProcess::process()
displayTicket(); displayTicket();
} }
void EsTikProcess::setInputFile(fnd::IFile* file, bool ownIFile) void EsTikProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
{ {
mFile = file; mFile = file;
mOwnIFile = ownIFile;
} }
void EsTikProcess::setKeyset(const sKeyset* keyset) void EsTikProcess::setKeyCfg(const KeyConfiguration& keycfg)
{ {
mKeyset = keyset; mKeyCfg = keycfg;
} }
void EsTikProcess::setCertificateChain(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs) void EsTikProcess::setCertificateChain(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs)
@ -64,14 +53,16 @@ void EsTikProcess::setVerifyMode(bool verify)
void EsTikProcess::importTicket() void EsTikProcess::importTicket()
{ {
if (mFile == nullptr) fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
{ {
throw fnd::Exception(kModuleName, "No file reader set."); throw fnd::Exception(kModuleName, "No file reader set.");
} }
fnd::Vec<byte_t> scratch; scratch.alloc((*mFile)->size());
scratch.alloc(mFile->size()); (*mFile)->read(scratch.data(), 0, scratch.size());
mFile->read(scratch.data(), 0, scratch.size());
mTik.fromBytes(scratch.data(), scratch.size()); mTik.fromBytes(scratch.data(), scratch.size());
} }
@ -94,7 +85,7 @@ void EsTikProcess::verifyTicket()
try try
{ {
pki_validator.setRootKey(mKeyset->pki.root_sign_key); pki_validator.setKeyCfg(mKeyCfg);
pki_validator.addCertificates(mCerts); pki_validator.addCertificates(mCerts);
pki_validator.validateSignature(mTik.getBody().getIssuer(), mTik.getSignature().getSignType(), mTik.getSignature().getSignature(), tik_hash); pki_validator.validateSignature(mTik.getBody().getIssuer(), mTik.getSignature().getSignType(), mTik.getSignature().getSignature(), tik_hash);
} }
@ -106,9 +97,7 @@ void EsTikProcess::verifyTicket()
void EsTikProcess::displayTicket() void EsTikProcess::displayTicket()
{ {
#define _SPLIT_VER(ver) ( (ver>>10) & 0x3f), ( (ver>>4) & 0x3f), ( (ver>>0) & 0xf) #define _SPLIT_VER(ver) (uint32_t)((ver>>10) & 0x3f) << "." << (uint32_t)((ver>>4) & 0x3f) << "." << (uint32_t)((ver>>0) & 0xf)
#define _HEXDUMP_U(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02X", var[a__a__A]); } while(0)
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0)
const nn::es::TicketBody_V2& body = mTik.getBody(); const nn::es::TicketBody_V2& body = mTik.getBody();
@ -123,14 +112,26 @@ void EsTikProcess::displayTicket()
std::cout << " Title Key:" << std::endl; std::cout << " Title Key:" << std::endl;
std::cout << " EncMode: " << getTitleKeyPersonalisationStr(body.getTitleKeyEncType()) << std::endl; std::cout << " EncMode: " << getTitleKeyPersonalisationStr(body.getTitleKeyEncType()) << std::endl;
std::cout << " KeyGeneration: " << std::dec << (uint32_t)body.getCommonKeyId() << std::endl; std::cout << " KeyGeneration: " << std::dec << (uint32_t)body.getCommonKeyId() << std::endl;
std::cout << " Data:" << std::endl; if (body.getTitleKeyEncType() == nn::es::ticket::RSA2048)
size_t size = body.getTitleKeyEncType() == nn::es::ticket::RSA2048 ? fnd::rsa::kRsa2048Size : fnd::aes::kAes128KeySize; {
fnd::SimpleTextOutput::hexDump(body.getEncTitleKey(), size, 0x10, 6); std::cout << " Data:" << std::endl;
for (size_t i = 0; i < 0x10; i++)
std::cout << " " << fnd::SimpleTextOutput::arrayToString(body.getEncTitleKey() + 0x10*i, 0x10, true, ":") << std::endl;
}
else if (body.getTitleKeyEncType() == nn::es::ticket::AES128_CBC)
{
std::cout << " Data:" << std::endl;
std::cout << " " << fnd::SimpleTextOutput::arrayToString(body.getEncTitleKey(), 0x10, true, ":") << std::endl;
}
else
{
std::cout << " Data: <cannot display>" << std::endl;
}
printf(" Version: v%d.%d.%d", _SPLIT_VER(body.getTicketVersion())); std::cout << " Version: v" << _SPLIT_VER(body.getTicketVersion());
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
printf(" (%d)", body.getTicketVersion()); std::cout << " (" << (uint32_t)body.getTicketVersion() << ")";
printf("\n"); std::cout << std::endl;
std::cout << " License Type: " << getLicenseTypeStr(body.getLicenseType()) << std::endl; std::cout << " License Type: " << getLicenseTypeStr(body.getLicenseType()) << std::endl;
@ -163,9 +164,6 @@ void EsTikProcess::displayTicket()
std::cout << " SectionNum: 0x" << std::hex << body.getSectionNum() << std::endl; std::cout << " SectionNum: 0x" << std::hex << body.getSectionNum() << std::endl;
std::cout << " SectionEntrySize: 0x" << std::hex << body.getSectionEntrySize() << std::endl; std::cout << " SectionEntrySize: 0x" << std::hex << body.getSectionEntrySize() << std::endl;
#undef _HEXDUMP_L
#undef _HEXDUMP_U
#undef _SPLIT_VER #undef _SPLIT_VER
} }

View file

@ -2,22 +2,23 @@
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/Vec.h> #include <fnd/Vec.h>
#include <nn/pki/SignedData.h> #include <nn/pki/SignedData.h>
#include <nn/pki/CertificateBody.h> #include <nn/pki/CertificateBody.h>
#include <nn/es/TicketBody_V2.h> #include <nn/es/TicketBody_V2.h>
#include "nstool.h" #include "KeyConfiguration.h"
#include "common.h"
class EsTikProcess class EsTikProcess
{ {
public: public:
EsTikProcess(); EsTikProcess();
~EsTikProcess();
void process(); void process();
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setKeyset(const sKeyset* keyset); void setKeyCfg(const KeyConfiguration& keycfg);
void setCertificateChain(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs); void setCertificateChain(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs);
void setCliOutputMode(CliOutputMode mode); void setCliOutputMode(CliOutputMode mode);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -25,9 +26,8 @@ public:
private: private:
const std::string kModuleName = "EsTikProcess"; const std::string kModuleName = "EsTikProcess";
fnd::IFile* mFile; fnd::SharedPtr<fnd::IFile> mFile;
bool mOwnIFile; KeyConfiguration mKeyCfg;
const sKeyset* mKeyset;
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;

View file

@ -1,9 +1,8 @@
#include "nstool.h" #include "common.h"
#include "HashTreeWrappedIFile.h" #include "HashTreeWrappedIFile.h"
#include "OffsetAdjustedIFile.h" #include "OffsetAdjustedIFile.h"
HashTreeWrappedIFile::HashTreeWrappedIFile(fnd::IFile* file, bool ownIFile, const HashTreeMeta& hdr) : HashTreeWrappedIFile::HashTreeWrappedIFile(const fnd::SharedPtr<fnd::IFile>& file, const HashTreeMeta& hdr) :
mOwnIFile(ownIFile),
mFile(file), mFile(file),
mData(nullptr), mData(nullptr),
mDataHashLayer(), mDataHashLayer(),
@ -12,18 +11,9 @@ HashTreeWrappedIFile::HashTreeWrappedIFile(fnd::IFile* file, bool ownIFile, cons
initialiseDataLayer(hdr); initialiseDataLayer(hdr);
} }
HashTreeWrappedIFile::~HashTreeWrappedIFile()
{
if (mOwnIFile)
{
delete mFile;
}
delete mData;
}
size_t HashTreeWrappedIFile::size() size_t HashTreeWrappedIFile::size()
{ {
return mData->size(); return (*mData)->size();
} }
void HashTreeWrappedIFile::seek(size_t offset) void HashTreeWrappedIFile::seek(size_t offset)
@ -118,7 +108,7 @@ void HashTreeWrappedIFile::initialiseDataLayer(const HashTreeMeta& hdr)
cur.alloc(align(layer.size, layer.block_size)); cur.alloc(align(layer.size, layer.block_size));
// read layer // read layer
mFile->read(cur.data(), layer.offset, layer.size); (*mFile)->read(cur.data(), layer.offset, layer.size);
// validate blocks // validate blocks
size_t validate_size; size_t validate_size;
@ -145,7 +135,7 @@ void HashTreeWrappedIFile::initialiseDataLayer(const HashTreeMeta& hdr)
} }
// generate reader for data layer // generate reader for data layer
mData = new OffsetAdjustedIFile(mFile, SHARED_IFILE, hdr.getDataLayer().offset, hdr.getDataLayer().size); mData = new OffsetAdjustedIFile(mFile, hdr.getDataLayer().offset, hdr.getDataLayer().size);
mDataOffset = 0; mDataOffset = 0;
mDataBlockSize = hdr.getDataLayer().block_size; mDataBlockSize = hdr.getDataLayer().block_size;
@ -160,17 +150,17 @@ void HashTreeWrappedIFile::initialiseDataLayer(const HashTreeMeta& hdr)
void HashTreeWrappedIFile::readData(size_t block_offset, size_t block_num) void HashTreeWrappedIFile::readData(size_t block_offset, size_t block_num)
{ {
mData->seek(block_offset * mDataBlockSize); (*mData)->seek(block_offset * mDataBlockSize);
fnd::sha::sSha256Hash hash; fnd::sha::sSha256Hash hash;
// determine read size // determine read size
size_t read_len = 0; size_t read_len = 0;
if ((block_offset + block_num) == getBlockNum(mData->size())) if ((block_offset + block_num) == getBlockNum((*mData)->size()))
{ {
read_len = (block_num-1) * mDataBlockSize + getRemanderBlockReadSize(mData->size()); read_len = (block_num-1) * mDataBlockSize + getRemanderBlockReadSize((*mData)->size());
memset(mCache.data(), 0, block_num * mDataBlockSize); memset(mCache.data(), 0, block_num * mDataBlockSize);
} }
else if ((block_offset + block_num) < getBlockNum(mData->size())) else if ((block_offset + block_num) < getBlockNum((*mData)->size()))
{ {
read_len = block_num * mDataBlockSize; read_len = block_num * mDataBlockSize;
} }
@ -180,7 +170,7 @@ void HashTreeWrappedIFile::readData(size_t block_offset, size_t block_num)
} }
// read // read
mData->read(mCache.data(), block_offset * mDataBlockSize, read_len); (*mData)->read(mCache.data(), block_offset * mDataBlockSize, read_len);
if (block_num > mCacheBlockNum) if (block_num > mCacheBlockNum)
{ {

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <sstream> #include <sstream>
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/Vec.h> #include <fnd/Vec.h>
#include <fnd/sha.h> #include <fnd/sha.h>
#include "HashTreeMeta.h" #include "HashTreeMeta.h"
@ -9,8 +10,7 @@
class HashTreeWrappedIFile : public fnd::IFile class HashTreeWrappedIFile : public fnd::IFile
{ {
public: public:
HashTreeWrappedIFile(fnd::IFile* file, bool ownIFile, const HashTreeMeta& hdr); HashTreeWrappedIFile(const fnd::SharedPtr<fnd::IFile>& file, const HashTreeMeta& hdr);
~HashTreeWrappedIFile();
size_t size(); size_t size();
void seek(size_t offset); void seek(size_t offset);
@ -23,11 +23,10 @@ private:
static const size_t kDefaultCacheSize = 0x10000; static const size_t kDefaultCacheSize = 0x10000;
std::stringstream mErrorSs; std::stringstream mErrorSs;
bool mOwnIFile; fnd::SharedPtr<fnd::IFile> mFile;
fnd::IFile* mFile;
// data file // data file
fnd::IFile* mData; fnd::SharedPtr<fnd::IFile> mData;
size_t mDataOffset; size_t mDataOffset;
size_t mDataBlockSize; size_t mDataBlockSize;
fnd::List<fnd::sha::sSha256Hash> mDataHashLayer; fnd::List<fnd::sha::sSha256Hash> mDataHashLayer;

View file

@ -0,0 +1,376 @@
#include "KeyConfiguration.h"
#include <fnd/ResourceFileReader.h>
#include <fnd/SimpleTextOutput.h>
#include <nn/hac/AesKeygen.h>
#include <nn/hac/NcaUtils.h>
KeyConfiguration::KeyConfiguration()
{
clearGeneralKeyConfiguration();
clearNcaExternalKeys();
}
KeyConfiguration::KeyConfiguration(const KeyConfiguration& other)
{
*this = other;
}
void KeyConfiguration::operator=(const KeyConfiguration& other)
{
mAcidSignKey = other.mAcidSignKey;
mPkg2SignKey = other.mPkg2SignKey;
mNcaHeader0SignKey = other.mNcaHeader0SignKey;
mXciHeaderSignKey = other.mXciHeaderSignKey;
mNcaHeaderKey = other.mNcaHeaderKey;
mXciHeaderKey = other.mXciHeaderKey;
for (size_t i = 0; i < kMasterKeyNum; i++)
{
mPkg2Key[i] = other.mPkg2Key[i];
mPkg1Key[i] = other.mPkg1Key[i];
mNcaKeyAreaEncryptionKey[0][i] = other.mNcaKeyAreaEncryptionKey[0][i];
mNcaKeyAreaEncryptionKey[1][i] = other.mNcaKeyAreaEncryptionKey[1][i];
mNcaKeyAreaEncryptionKey[2][i] = other.mNcaKeyAreaEncryptionKey[2][i];
mNcaKeyAreaEncryptionKeyHw[0][i] = other.mNcaKeyAreaEncryptionKeyHw[0][i];
mNcaKeyAreaEncryptionKeyHw[1][i] = other.mNcaKeyAreaEncryptionKeyHw[1][i];
mNcaKeyAreaEncryptionKeyHw[2][i] = other.mNcaKeyAreaEncryptionKeyHw[2][i];
mETicketCommonKey[i] = other.mETicketCommonKey[i];
}
mPkiRootKeyList = other.mPkiRootKeyList;
mNcaExternalContentKeyList = other.mNcaExternalContentKeyList;
}
void KeyConfiguration::importHactoolGenericKeyfile(const std::string& path)
{
clearGeneralKeyConfiguration();
fnd::ResourceFileReader res;
try
{
res.processFile(path);
}
catch (const fnd::Exception&)
{
throw fnd::Exception(kModuleName, "Failed to open key file: " + path);
}
// internally used sources
fnd::aes::sAes128Key master_key[kMasterKeyNum] = { kNullAesKey };
fnd::aes::sAes128Key package2_key_source = kNullAesKey;
fnd::aes::sAes128Key ticket_titlekek_source = kNullAesKey;
fnd::aes::sAes128Key key_area_key_source[kNcaKeakNum] = { kNullAesKey, kNullAesKey, kNullAesKey };
fnd::aes::sAes128Key aes_kek_generation_source = kNullAesKey;
fnd::aes::sAes128Key aes_key_generation_source = kNullAesKey;
fnd::aes::sAes128Key nca_header_kek_source = kNullAesKey;
fnd::aes::sAesXts128Key nca_header_key_source = kNullAesXtsKey;
fnd::rsa::sRsa4096Key pki_root_sign_key = kNullRsa4096Key;
#define _CONCAT_2_STRINGS(str1, str2) ((str1) + "_" + (str2))
#define _CONCAT_3_STRINGS(str1, str2, str3) _CONCAT_2_STRINGS(_CONCAT_2_STRINGS(str1, str2), str3)
std::string key,val;
fnd::Vec<byte_t> dec_array;
#define _SAVE_KEYDATA(key_name, array, len) \
key = (key_name); \
val = res[key]; \
if (val.empty() == false) { \
fnd::SimpleTextOutput::stringToArray(val, dec_array); \
if (dec_array.size() != len) \
throw fnd::Exception(kModuleName, "Key: \"" + key_name + "\" has incorrect length"); \
memcpy(array, dec_array.data(), len); \
}
for (size_t nameidx = 0; nameidx < kNameVariantNum; nameidx++)
{
// import sources
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg2Base[nameidx], kKeyStr, kSourceStr), package2_key_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[nameidx], kSourceStr), ticket_titlekek_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[0], kSourceStr), key_area_key_source[0].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[1], kSourceStr), key_area_key_source[1].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[2], kSourceStr), key_area_key_source[2].key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kKekGenBase[nameidx], kSourceStr), aes_kek_generation_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kKeyGenBase[nameidx], kSourceStr), aes_key_generation_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kXciHeaderBase[nameidx], kKekStr, kSourceStr), nca_header_kek_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kXciHeaderBase[nameidx], kKeyStr, kSourceStr), nca_header_key_source.key, 0x20);
// Store Key Variants/Derivatives
for (size_t mkeyidx = 0; mkeyidx < kMasterKeyNum; mkeyidx++)
{
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kMasterBase[nameidx], kKeyStr, kKeyIndex[mkeyidx]), master_key[mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg1Base[nameidx], kKeyStr, kKeyIndex[mkeyidx]), mPkg1Key[mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg2Base[nameidx], kKeyStr, kKeyIndex[mkeyidx]), mPkg2Key[mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[nameidx], kKeyIndex[mkeyidx]), mETicketCommonKey[mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[0], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKey[0][mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[1], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKey[1][mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[2], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKey[2][mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyHwBase[nameidx], kNcaKeyAreaKeyIndexStr[0], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKeyHw[0][mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyHwBase[nameidx], kNcaKeyAreaKeyIndexStr[1], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKeyHw[1][mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyHwBase[nameidx], kNcaKeyAreaKeyIndexStr[2], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKeyHw[2][mkeyidx].key, 0x10);
}
// store nca header key
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[nameidx], kKeyStr), mNcaHeaderKey.key[0], 0x20);
// store xci header key
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kKeyStr), mXciHeaderKey.key, 0x10);
// store rsa keys
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[nameidx], kRsaKeyPrivate), mNcaHeader0SignKey.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[nameidx], kRsaKeyModulus), mNcaHeader0SignKey.modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kRsaKeyPrivate), mXciHeaderSignKey.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kRsaKeyModulus), mXciHeaderSignKey.modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase[nameidx], kRsaKeyPrivate), mAcidSignKey.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase[nameidx], kRsaKeyModulus), mAcidSignKey.modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkg2Base[nameidx], kRsaKeyPrivate), mPkg2SignKey.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkg2Base[nameidx], kRsaKeyModulus), mPkg2SignKey.modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase[nameidx], kRsaKeyPrivate), pki_root_sign_key.priv_exponent, fnd::rsa::kRsa4096Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase[nameidx], kRsaKeyModulus), pki_root_sign_key.modulus, fnd::rsa::kRsa4096Size);
}
#undef _SAVE_KEYDATA
#undef _CONCAT_3_STRINGS
#undef _CONCAT_2_STRINGS
// Derive keys
for (size_t i = 0; i < kMasterKeyNum; i++)
{
if (master_key[i] != kNullAesKey)
{
if (aes_kek_generation_source != kNullAesKey && aes_key_generation_source != kNullAesKey)
{
if (i == 0 && nca_header_kek_source != kNullAesKey && nca_header_key_source != kNullAesXtsKey)
{
if (mNcaHeaderKey == kNullAesXtsKey)
{
fnd::aes::sAes128Key nca_header_kek;
nn::hac::AesKeygen::generateKey(nca_header_kek.key, aes_kek_generation_source.key, nca_header_kek_source.key, aes_key_generation_source.key, master_key[i].key);
nn::hac::AesKeygen::generateKey(mNcaHeaderKey.key[0], nca_header_key_source.key[0], nca_header_kek.key);
nn::hac::AesKeygen::generateKey(mNcaHeaderKey.key[1], nca_header_key_source.key[1], nca_header_kek.key);
}
}
for (size_t j = 0; j < nn::hac::nca::kKeyAreaEncryptionKeyNum; j++)
{
if (key_area_key_source[j] != kNullAesKey && mNcaKeyAreaEncryptionKey[j][i] == kNullAesKey)
{
nn::hac::AesKeygen::generateKey(mNcaKeyAreaEncryptionKey[j][i].key, aes_kek_generation_source.key, key_area_key_source[j].key, aes_key_generation_source.key, master_key[i].key);
}
}
}
if (ticket_titlekek_source != kNullAesKey && mETicketCommonKey[i] == kNullAesKey)
{
nn::hac::AesKeygen::generateKey(mETicketCommonKey[i].key, ticket_titlekek_source.key, master_key[i].key);
}
if (package2_key_source != kNullAesKey && mPkg2Key[i] == kNullAesKey)
{
nn::hac::AesKeygen::generateKey(mPkg2Key[i].key, package2_key_source.key, master_key[i].key);
}
}
}
// populate pki root keys
if (pki_root_sign_key != kNullRsa4096Key)
{
sPkiRootKey tmp;
tmp.name = nn::pki::sign::kRootIssuerStr;
tmp.key_type = nn::pki::sign::SIGN_ALGO_RSA4096;
tmp.rsa4096_key = pki_root_sign_key;
mPkiRootKeyList.addElement(tmp);
}
}
void KeyConfiguration::clearGeneralKeyConfiguration()
{
mAcidSignKey = kNullRsa2048Key;
mPkg2SignKey = kNullRsa2048Key;
mNcaHeader0SignKey = kNullRsa2048Key;
mXciHeaderSignKey = kNullRsa2048Key;
mPkiRootKeyList.clear();
mNcaHeaderKey = kNullAesXtsKey;
mXciHeaderKey = kNullAesKey;
for (size_t i = 0; i < kMasterKeyNum; i++)
{
mPkg1Key[i] = kNullAesKey;
mPkg2Key[i] = kNullAesKey;
mETicketCommonKey[i] = kNullAesKey;
for (size_t j = 0; j < kNcaKeakNum; j++)
{
mNcaKeyAreaEncryptionKey[j][i] = kNullAesKey;
mNcaKeyAreaEncryptionKey[j][i] = kNullAesKey;
}
}
}
void KeyConfiguration::clearNcaExternalKeys()
{
mNcaExternalContentKeyList.clear();
}
bool KeyConfiguration::getNcaHeaderKey(fnd::aes::sAesXts128Key& key) const
{
return copyOutKeyResourceIfExists(mNcaHeaderKey, key, kNullAesXtsKey);
}
bool KeyConfiguration::getNcaHeader0SignKey(fnd::rsa::sRsa2048Key& key) const
{
return copyOutKeyResourceIfExists(mNcaHeader0SignKey, key, kNullRsa2048Key);
}
bool KeyConfiguration::getAcidSignKey(fnd::rsa::sRsa2048Key& key) const
{
return copyOutKeyResourceIfExists(mAcidSignKey, key, kNullRsa2048Key);
}
bool KeyConfiguration::getNcaKeyAreaEncryptionKey(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const
{
if (keak_type >= kNcaKeakNum || masterkey_index >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mNcaKeyAreaEncryptionKey[keak_type][masterkey_index], key, kNullAesKey);
}
bool KeyConfiguration::getNcaKeyAreaEncryptionKeyHw(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const
{
if (keak_type >= kNcaKeakNum || masterkey_index >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mNcaKeyAreaEncryptionKeyHw[keak_type][masterkey_index], key, kNullAesKey);
}
void KeyConfiguration::addNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], const fnd::aes::sAes128Key& key)
{
sNcaExternalContentKey tmp;
memcpy(tmp.rights_id.data, rights_id, nn::hac::nca::kRightsIdLen);
tmp.key = key;
if (mNcaExternalContentKeyList.hasElement(tmp))
return;
mNcaExternalContentKeyList.addElement(tmp);
}
bool KeyConfiguration::getNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], fnd::aes::sAes128Key& key) const
{
sRightsId id;
bool res_exists = false;
memcpy(id.data, rights_id, nn::hac::nca::kRightsIdLen);
for (size_t i = 0; i < mNcaExternalContentKeyList.size(); i++)
{
if (mNcaExternalContentKeyList[i].rights_id == id)
{
res_exists = true;
key = mNcaExternalContentKeyList[i].key;
break;
}
}
return res_exists;
}
bool KeyConfiguration::getPkg1Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const
{
if (masterkey_index >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mPkg1Key[masterkey_index], key, kNullAesKey);
}
bool KeyConfiguration::getPkg2Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const
{
if (masterkey_index >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mPkg2Key[masterkey_index], key, kNullAesKey);
}
bool KeyConfiguration::getPkg2SignKey(fnd::rsa::sRsa2048Key& key) const
{
return copyOutKeyResourceIfExists(mPkg2SignKey, key, kNullRsa2048Key);
}
bool KeyConfiguration::getXciHeaderSignKey(fnd::rsa::sRsa2048Key& key) const
{
return copyOutKeyResourceIfExists(mXciHeaderSignKey, key, kNullRsa2048Key);
}
bool KeyConfiguration::getXciHeaderKey(fnd::aes::sAes128Key& key) const
{
return copyOutKeyResourceIfExists(mXciHeaderKey, key, kNullAesKey);
}
bool KeyConfiguration::getETicketCommonKey(byte_t masterkey_index, fnd::aes::sAes128Key& key) const
{
if (masterkey_index >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mETicketCommonKey[masterkey_index], key, kNullAesKey);
}
bool KeyConfiguration::getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa4096Key& key) const
{
bool res_exists = false;
for (size_t i = 0; i < mPkiRootKeyList.size(); i++)
{
if (root_name == mPkiRootKeyList[i].name && mPkiRootKeyList[i].key_type == nn::pki::sign::SIGN_ALGO_RSA4096)
{
res_exists = true;
key = mPkiRootKeyList[i].rsa4096_key;
break;
}
}
return res_exists;
}
bool KeyConfiguration::getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa2048Key& key) const
{
bool res_exists = false;
for (size_t i = 0; i < mPkiRootKeyList.size(); i++)
{
if (root_name == mPkiRootKeyList[i].name && mPkiRootKeyList[i].key_type == nn::pki::sign::SIGN_ALGO_RSA2048)
{
res_exists = true;
key = mPkiRootKeyList[i].rsa2048_key;
break;
}
}
return res_exists;
}
bool KeyConfiguration::getPkiRootSignKey(const std::string& root_name, fnd::ecdsa::sEcdsa240Key& key) const
{
bool res_exists = false;
for (size_t i = 0; i < mPkiRootKeyList.size(); i++)
{
if (root_name == mPkiRootKeyList[i].name && mPkiRootKeyList[i].key_type == nn::pki::sign::SIGN_ALGO_ECDSA240)
{
res_exists = true;
key = mPkiRootKeyList[i].ecdsa240_key;
break;
}
}
return res_exists;
}

View file

@ -0,0 +1,209 @@
#pragma once
#include <string>
#include <cstring>
#include <fnd/types.h>
#include <fnd/aes.h>
#include <fnd/rsa.h>
#include <fnd/ecdsa.h>
#include <nn/hac/nca.h>
#include <nn/pki/SignedData.h>
#include <nn/es/TicketBody_V2.h>
class KeyConfiguration
{
public:
KeyConfiguration();
KeyConfiguration(const KeyConfiguration& other);
void operator=(const KeyConfiguration& other);
void importHactoolGenericKeyfile(const std::string& path);
//void importHactoolTitleKeyfile(const std::string& path);
void clearGeneralKeyConfiguration();
void clearNcaExternalKeys();
// nca keys
bool getNcaHeaderKey(fnd::aes::sAesXts128Key& key) const;
bool getNcaHeader0SignKey(fnd::rsa::sRsa2048Key& key) const;
bool getAcidSignKey(fnd::rsa::sRsa2048Key& key) const;
bool getNcaKeyAreaEncryptionKey(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const;
bool getNcaKeyAreaEncryptionKeyHw(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const;
// external content keys
void addNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], const fnd::aes::sAes128Key& key);
bool getNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], fnd::aes::sAes128Key& key) const;
// pkg1/pkg2
bool getPkg1Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const;
bool getPkg2Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const;
bool getPkg2SignKey(fnd::rsa::sRsa2048Key& key) const;
// xci keys
bool getXciHeaderSignKey(fnd::rsa::sRsa2048Key& key) const;
bool getXciHeaderKey(fnd::aes::sAes128Key& key) const;
// ticket
bool getETicketCommonKey(byte_t masterkey_index, fnd::aes::sAes128Key& key) const;
// pki
bool getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa4096Key& key) const;
bool getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa2048Key& key) const;
bool getPkiRootSignKey(const std::string& root_name, fnd::ecdsa::sEcdsa240Key& key) const;
private:
const std::string kModuleName = "KeyConfiguration";
const fnd::aes::sAes128Key kNullAesKey = {{0}};
const fnd::aes::sAesXts128Key kNullAesXtsKey = {{{0}}};
const fnd::rsa::sRsa4096Key kNullRsa4096Key = {{0}, {0}, {0}};
const fnd::rsa::sRsa2048Key kNullRsa2048Key = {{0}, {0}, {0}};
static const size_t kMasterKeyNum = 0x20;
static const size_t kNcaKeakNum = nn::hac::nca::kKeyAreaEncryptionKeyNum;
// keynames
enum NameVariantIndex
{
NNTOOLS,
LEGACY_HACTOOL,
LEGACY_0
};
static const size_t kNameVariantNum = 3;
const std::string kMasterBase[kNameVariantNum] = { "master", "master", "master" };
const std::string kPkg1Base[kNameVariantNum] = { "package1", "package1", "package1" };
const std::string kPkg2Base[kNameVariantNum] = { "package2", "package2", "package2" };
const std::string kXciHeaderBase[kNameVariantNum] = { "xci_header", "xci_header", "xci_header" };
const std::string kNcaHeaderBase[kNameVariantNum] = { "nca_header", "header", "nca_header" };
const std::string kAcidBase[kNameVariantNum] = { "acid", "acid", "acid" };
const std::string kPkiRootBase[kNameVariantNum] = { "pki_root", "pki_root", "pki_root" };
const std::string kTicketCommonKeyBase[kNameVariantNum] = { "ticket_commonkey", "titlekek", "ticket_commonkey" };
const std::string kNcaKeyAreaEncKeyBase[kNameVariantNum] = { "nca_key_area_key", "key_area_key", "nca_body_keak" };
const std::string kNcaKeyAreaEncKeyHwBase[kNameVariantNum] = { "nca_key_area_key_hw", "key_area_hw_key", "nca_key_area_key_hw" };
const std::string kKekGenBase[kNameVariantNum] = { "aes_kek_generation", "aes_kek_generation", "aes_kek_generation" };
const std::string kKeyGenBase[kNameVariantNum] = { "aes_key_generation", "aes_key_generation", "aes_key_generation" };
// misc str
const std::string kKeyStr = "key";
const std::string kKekStr = "kek";
const std::string kSourceStr = "source";
const std::string kRsaKeyModulus = "sign_key_modulus";
const std::string kRsaKeyPrivate = "sign_key_private";
const std::string kNcaKeyAreaKeyIndexStr[kNcaKeakNum] = { "application", "ocean", "system" };
const std::string kKeyIndex[kMasterKeyNum] = {"00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f"};
struct sRightsId
{
byte_t data[nn::hac::nca::kRightsIdLen];
void operator=(const sRightsId& other)
{
memcpy(this->data, other.data, nn::hac::nca::kRightsIdLen);
}
bool operator==(const sRightsId& other) const
{
return memcmp(this->data, other.data, nn::hac::nca::kRightsIdLen) == 0;
}
bool operator!=(const sRightsId& other) const
{
return !(operator==(other));
}
};
struct sNcaExternalContentKey
{
sRightsId rights_id;
fnd::aes::sAes128Key key;
void operator=(const sNcaExternalContentKey& other)
{
rights_id = other.rights_id;
key = other.key;
}
bool operator==(const sNcaExternalContentKey& other) const
{
return (rights_id == other.rights_id) \
&& (key == other.key);
}
bool operator!=(const sNcaExternalContentKey& other) const
{
return !(operator==(other));
}
};
struct sPkiRootKey
{
std::string name;
nn::pki::sign::SignatureAlgo key_type;
fnd::rsa::sRsa4096Key rsa4096_key;
fnd::rsa::sRsa2048Key rsa2048_key;
fnd::ecdsa::sEcdsa240Key ecdsa240_key;
void operator=(const sPkiRootKey& other)
{
name = other.name;
key_type = other.key_type;
rsa4096_key = other.rsa4096_key;
rsa2048_key = other.rsa2048_key;
ecdsa240_key = other.ecdsa240_key;
}
bool operator==(const sPkiRootKey& other) const
{
return (name == other.name) \
&& (key_type == other.key_type) \
&& (rsa4096_key == other.rsa4096_key) \
&& (rsa2048_key == other.rsa2048_key) \
&& (ecdsa240_key == other.ecdsa240_key);
}
bool operator!=(const sPkiRootKey& other) const
{
return !(operator==(other));
}
};
/* general key config */
// acid
fnd::rsa::sRsa2048Key mAcidSignKey;
// pkg1 and pkg2
fnd::aes::sAes128Key mPkg1Key[kMasterKeyNum];
fnd::rsa::sRsa2048Key mPkg2SignKey;
fnd::aes::sAes128Key mPkg2Key[kMasterKeyNum];
// nca
fnd::rsa::sRsa2048Key mNcaHeader0SignKey;
fnd::aes::sAesXts128Key mNcaHeaderKey;
fnd::aes::sAes128Key mNcaKeyAreaEncryptionKey[kNcaKeakNum][kMasterKeyNum];
fnd::aes::sAes128Key mNcaKeyAreaEncryptionKeyHw[kNcaKeakNum][kMasterKeyNum];
// xci
fnd::rsa::sRsa2048Key mXciHeaderSignKey;
fnd::aes::sAes128Key mXciHeaderKey;
// ticket
fnd::aes::sAes128Key mETicketCommonKey[kMasterKeyNum];
// pki
fnd::List<sPkiRootKey> mPkiRootKeyList;
/* Nca External Keys */
fnd::List<sNcaExternalContentKey> mNcaExternalContentKeyList;
template <class T>
bool copyOutKeyResourceIfExists(const T& src, T& dst, const T& null_sample) const
{
bool resource_exists = false;
if (src != null_sample)
{
resource_exists = true;
dst = src;
}
return resource_exists;
}
};

View file

@ -1,11 +1,185 @@
#include <sstream> #include <sstream>
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h> #include <fnd/SimpleTextOutput.h>
#include "OffsetAdjustedIFile.h" #include "OffsetAdjustedIFile.h"
#include "NacpProcess.h" #include "NacpProcess.h"
const char* getLanguageStr(nn::hac::nacp::Language var) NacpProcess::NacpProcess() :
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false)
{
}
void NacpProcess::process()
{
importNacp();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
displayNacp();
}
void NacpProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
{
mFile = file;
}
void NacpProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void NacpProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
const nn::hac::ApplicationControlPropertyBinary& NacpProcess::getApplicationControlPropertyBinary() const
{
return mNacp;
}
void NacpProcess::importNacp()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
scratch.alloc((*mFile)->size());
(*mFile)->read(scratch.data(), 0, scratch.size());
mNacp.fromBytes(scratch.data(), scratch.size());
}
void NacpProcess::displayNacp()
{
std::cout << "[ApplicationControlProperty]" << std::endl;
std::cout << " Menu Description:" << std::endl;
std::cout << " DisplayVersion: " << mNacp.getDisplayVersion() << std::endl;
if (mNacp.getIsbn().empty() == false || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
std::cout << " ISBN: " << mNacp.getIsbn() << std::endl;
for (size_t i = 0; i < mNacp.getTitle().size(); i++)
{
std::cout << " " << getLanguageStr(mNacp.getTitle()[i].language) << " Title:" << std::endl;
std::cout << " Name: " << mNacp.getTitle()[i].name << std::endl;
std::cout << " Publisher: " << mNacp.getTitle()[i].publisher << std::endl;
}
std::cout << " Logo:" << std::endl;
std::cout << " Type: " << getLogoTypeStr(mNacp.getLogoType()) << std::endl;
std::cout << " Handling: " << getLogoHandlingStr(mNacp.getLogoHandling()) << std::endl;
std::cout << " AddOnContent:" << std::endl;
std::cout << " BaseId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getAocBaseId() << std::endl;
std::cout << " RegistrationType: " << getAocRegistrationTypeStr(mNacp.getAocRegistrationType()) << std::endl;
std::cout << " RuntimeInstallMode: " << getRuntimeAocInstallModeStr(mNacp.getRuntimeAocInstallMode()) << std::endl;
std::cout << " Play Log:" << std::endl;
std::cout << " PlayLogPolicy: " << getPlayLogPolicyStr(mNacp.getPlayLogPolicy()) << std::endl;
std::cout << " PlayLogQueryCapability: " << getPlayLogQueryCapabilityStr(mNacp.getPlayLogQueryCapability()) << std::endl;
if (mNacp.getPlayLogQueryableApplicationId().size() > 0)
{
std::cout << " PlayLogQueryableApplicationId:" << std::endl;
for (size_t i = 0; i < mNacp.getPlayLogQueryableApplicationId().size(); i++)
{
std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getPlayLogQueryableApplicationId()[i] << std::endl;
}
}
std::cout << " Parental Controls:" << std::endl;
std::cout << " ParentalControlFlag: " << getParentalControlFlagStr(mNacp.getParentalControlFlag()) << std::endl;
for (size_t i = 0; i < mNacp.getRatingAge().size(); i++)
{
std::cout << " Age Restriction:" << std::endl;
std::cout << " Agency: " << getOrganisationStr(mNacp.getRatingAge()[i].organisation) << std::endl;
std::cout << " Age: " << std::dec << (uint32_t)mNacp.getRatingAge()[i].age << std::endl;
}
if (mNacp.getBcatPassphase().empty() == false || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
std::cout << " BCAT:" << std::endl;
std::cout << " BcatPassphase: " << mNacp.getBcatPassphase() << std::endl;
std::cout << " DeliveryCacheStorageSize: 0x" << std::hex << mNacp.getBcatDeliveryCacheStorageSize() << std::endl;
}
if (mNacp.getLocalCommunicationId().size() > 0)
{
std::cout << " Local Area Communication:" << std::endl;
std::cout << " LocalCommunicationId:" << std::endl;
for (size_t i = 0; i < mNacp.getLocalCommunicationId().size(); i++)
{
std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getLocalCommunicationId()[i] << std::endl;
}
}
std::cout << " SaveData:" << std::endl;
std::cout << " SaveDatawOwnerId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getSaveDatawOwnerId() << std::endl;
if (mNacp.getUserAccountSaveDataSize().journal_size > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
std::cout << " UserAccountSaveData:" << std::endl;
std::cout << " Size: " << getSaveDataSizeStr(mNacp.getUserAccountSaveDataSize().size) << std::endl;
std::cout << " JournalSize: " << getSaveDataSizeStr(mNacp.getUserAccountSaveDataSize().journal_size) << std::endl;
}
if (mNacp.getDeviceSaveDataSize().journal_size > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
std::cout << " DeviceSaveData:" << std::endl;
std::cout << " Size: " << getSaveDataSizeStr(mNacp.getDeviceSaveDataSize().size) << std::endl;
std::cout << " JournalSize: " << getSaveDataSizeStr(mNacp.getDeviceSaveDataSize().journal_size) << std::endl;
}
if (mNacp.getUserAccountSaveDataMax().journal_size > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
std::cout << " UserAccountSaveDataMax:" << std::endl;
std::cout << " Size: " << getSaveDataSizeStr(mNacp.getUserAccountSaveDataMax().size) << std::endl;
std::cout << " JournalSize: " << getSaveDataSizeStr(mNacp.getUserAccountSaveDataMax().journal_size) << std::endl;
}
if (mNacp.getDeviceSaveDataMax().journal_size > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
std::cout << " DeviceSaveDataMax:" << std::endl;
std::cout << " Size: " << getSaveDataSizeStr(mNacp.getDeviceSaveDataMax().size) << std::endl;
std::cout << " JournalSize: " << getSaveDataSizeStr(mNacp.getDeviceSaveDataMax().journal_size) << std::endl;
}
if (mNacp.getTemporaryStorageSize() > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
std::cout << " TemporaryStorageSize: " << getSaveDataSizeStr(mNacp.getTemporaryStorageSize()) << std::endl;
}
if (mNacp.getCacheStorageSize().journal_size > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
std::cout << " CacheStorage:" << std::endl;
std::cout << " Size: " << getSaveDataSizeStr(mNacp.getCacheStorageSize().size) << std::endl;
std::cout << " JournalSize: " << getSaveDataSizeStr(mNacp.getCacheStorageSize().journal_size) << std::endl;
std::cout << " MaxDataAndJournalSize: " << getSaveDataSizeStr(mNacp.getCacheStorageDataAndJournalSizeMax()) << std::endl;
std::cout << " StorageIndexMax: 0x" << std::hex << mNacp.getCacheStorageIndexMax() << std::endl;
}
std::cout << " Other Flags:" << std::endl;
std::cout << " StartupUserAccount: " << getStartupUserAccountStr(mNacp.getStartupUserAccount()) << std::endl;
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
std::cout << " TouchScreenUsageMode: " << getTouchScreenUsageModeStr(mNacp.getTouchScreenUsageMode()) << std::endl;
}
std::cout << " AttributeFlag: " << getAttributeFlagStr(mNacp.getAttributeFlag()) << std::endl;
std::cout << " CrashReportMode: " << getCrashReportModeStr(mNacp.getCrashReportMode()) << std::endl;
std::cout << " HDCP: " << getHdcpStr(mNacp.getHdcp()) << std::endl;
std::cout << " ScreenshotMode: " << getScreenshotModeStr(mNacp.getScreenshotMode()) << std::endl;
std::cout << " VideoCaptureMode: " << getVideoCaptureModeStr(mNacp.getVideoCaptureMode()) << std::endl;
std::cout << " DataLossConfirmation: " << getDataLossConfirmationStr(mNacp.getDataLossConfirmation()) << std::endl;
std::cout << " RepairFlag: " << getRepairFlagStr(mNacp.getRepairFlag()) << std::endl;
std::cout << " ProgramIndex: 0x" << std::hex << std::setw(2) << std::setfill('0') << (uint32_t)mNacp.getProgramIndex() << std::endl;
if (mNacp.getApplicationErrorCodeCategory().empty() == false || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
std::cout << " ApplicationErrorCodeCategory: " << mNacp.getApplicationErrorCodeCategory() << std::endl;
}
if (mNacp.getSeedForPsuedoDeviceId() > 0 || mNacp.getPresenceGroupId() > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
std::cout << " Other Ids:" << std::endl;
if (mNacp.getSeedForPsuedoDeviceId() > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
std::cout << " SeedForPsuedoDeviceId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getSeedForPsuedoDeviceId() << std::endl;
if (mNacp.getPresenceGroupId() > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
std::cout << " PresenceGroupId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getPresenceGroupId() << std::endl;
}
}
const char* NacpProcess::getLanguageStr(nn::hac::nacp::Language var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::LANG_AmericanEnglish): case (nn::hac::nacp::LANG_AmericanEnglish):
@ -56,12 +230,14 @@ const char* getLanguageStr(nn::hac::nacp::Language var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getStartupUserAccountStr(nn::hac::nacp::StartupUserAccount var) const char* NacpProcess::getStartupUserAccountStr(nn::hac::nacp::StartupUserAccount var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::USER_None): case (nn::hac::nacp::USER_None):
@ -76,12 +252,14 @@ const char* getStartupUserAccountStr(nn::hac::nacp::StartupUserAccount var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getTouchScreenUsageModeStr(nn::hac::nacp::TouchScreenUsageMode var) const char* NacpProcess::getTouchScreenUsageModeStr(nn::hac::nacp::TouchScreenUsageMode var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::TOUCH_None): case (nn::hac::nacp::TOUCH_None):
@ -96,12 +274,14 @@ const char* getTouchScreenUsageModeStr(nn::hac::nacp::TouchScreenUsageMode var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getAocRegistrationTypeStr(nn::hac::nacp::AocRegistrationType var) const char* NacpProcess::getAocRegistrationTypeStr(nn::hac::nacp::AocRegistrationType var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::AOC_AllOnLaunch): case (nn::hac::nacp::AOC_AllOnLaunch):
@ -113,12 +293,14 @@ const char* getAocRegistrationTypeStr(nn::hac::nacp::AocRegistrationType var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getAttributeFlagStr(nn::hac::nacp::AttributeFlag var) const char* NacpProcess::getAttributeFlagStr(nn::hac::nacp::AttributeFlag var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::ATTR_None): case (nn::hac::nacp::ATTR_None):
@ -133,12 +315,14 @@ const char* getAttributeFlagStr(nn::hac::nacp::AttributeFlag var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getParentalControlFlagStr(nn::hac::nacp::ParentalControlFlag var) const char* NacpProcess::getParentalControlFlagStr(nn::hac::nacp::ParentalControlFlag var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::PC_None): case (nn::hac::nacp::PC_None):
@ -150,12 +334,14 @@ const char* getParentalControlFlagStr(nn::hac::nacp::ParentalControlFlag var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getScreenshotModeStr(nn::hac::nacp::ScreenshotMode var) const char* NacpProcess::getScreenshotModeStr(nn::hac::nacp::ScreenshotMode var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::SCRN_Allow): case (nn::hac::nacp::SCRN_Allow):
@ -167,12 +353,14 @@ const char* getScreenshotModeStr(nn::hac::nacp::ScreenshotMode var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getVideoCaptureModeStr(nn::hac::nacp::VideoCaptureMode var) const char* NacpProcess::getVideoCaptureModeStr(nn::hac::nacp::VideoCaptureMode var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::VCAP_Disable): case (nn::hac::nacp::VCAP_Disable):
@ -187,12 +375,14 @@ const char* getVideoCaptureModeStr(nn::hac::nacp::VideoCaptureMode var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getDataLossConfirmationStr(nn::hac::nacp::DataLossConfirmation var) const char* NacpProcess::getDataLossConfirmationStr(nn::hac::nacp::DataLossConfirmation var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::DLOSS_None): case (nn::hac::nacp::DLOSS_None):
@ -204,12 +394,14 @@ const char* getDataLossConfirmationStr(nn::hac::nacp::DataLossConfirmation var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getPlayLogPolicyStr(nn::hac::nacp::PlayLogPolicy var) const char* NacpProcess::getPlayLogPolicyStr(nn::hac::nacp::PlayLogPolicy var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::PLP_All): case (nn::hac::nacp::PLP_All):
@ -224,12 +416,14 @@ const char* getPlayLogPolicyStr(nn::hac::nacp::PlayLogPolicy var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getOrganisationStr(nn::hac::nacp::Organisation var) const char* NacpProcess::getOrganisationStr(nn::hac::nacp::Organisation var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::ORGN_CERO): case (nn::hac::nacp::ORGN_CERO):
@ -271,12 +465,14 @@ const char* getOrganisationStr(nn::hac::nacp::Organisation var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getLogoTypeStr(nn::hac::nacp::LogoType var) const char* NacpProcess::getLogoTypeStr(nn::hac::nacp::LogoType var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::LOGO_LicensedByNintendo): case (nn::hac::nacp::LOGO_LicensedByNintendo):
@ -291,12 +487,14 @@ const char* getLogoTypeStr(nn::hac::nacp::LogoType var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getLogoHandlingStr(nn::hac::nacp::LogoHandling var) const char* NacpProcess::getLogoHandlingStr(nn::hac::nacp::LogoHandling var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::LHND_Auto): case (nn::hac::nacp::LHND_Auto):
@ -308,12 +506,14 @@ const char* getLogoHandlingStr(nn::hac::nacp::LogoHandling var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getRuntimeAocInstallModeStr(nn::hac::nacp::RuntimeAocInstallMode var) const char* NacpProcess::getRuntimeAocInstallModeStr(nn::hac::nacp::RuntimeAocInstallMode var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::RTAOC_Deny): case (nn::hac::nacp::RTAOC_Deny):
@ -325,12 +525,14 @@ const char* getRuntimeAocInstallModeStr(nn::hac::nacp::RuntimeAocInstallMode var
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getCrashReportModeStr(nn::hac::nacp::CrashReportMode var) const char* NacpProcess::getCrashReportModeStr(nn::hac::nacp::CrashReportMode var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::CREP_Deny): case (nn::hac::nacp::CREP_Deny):
@ -342,12 +544,14 @@ const char* getCrashReportModeStr(nn::hac::nacp::CrashReportMode var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getHdcpStr(nn::hac::nacp::Hdcp var) const char* NacpProcess::getHdcpStr(nn::hac::nacp::Hdcp var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::HDCP_None): case (nn::hac::nacp::HDCP_None):
@ -359,12 +563,14 @@ const char* getHdcpStr(nn::hac::nacp::Hdcp var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getPlayLogQueryCapabilityStr(nn::hac::nacp::PlayLogQueryCapability var) const char* NacpProcess::getPlayLogQueryCapabilityStr(nn::hac::nacp::PlayLogQueryCapability var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::PLQC_None): case (nn::hac::nacp::PLQC_None):
@ -379,12 +585,14 @@ const char* getPlayLogQueryCapabilityStr(nn::hac::nacp::PlayLogQueryCapability v
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
const char* getRepairFlagStr(nn::hac::nacp::RepairFlag var) const char* NacpProcess::getRepairFlagStr(nn::hac::nacp::RepairFlag var) const
{ {
const char* str = nullptr; const char* str = nullptr;
switch(var) switch(var)
{ {
case (nn::hac::nacp::REPF_None): case (nn::hac::nacp::REPF_None):
@ -396,10 +604,11 @@ const char* getRepairFlagStr(nn::hac::nacp::RepairFlag var)
default: default:
str = "Unknown"; str = "Unknown";
} }
return str; return str;
} }
std::string getSaveDataSizeStr(int64_t size) std::string NacpProcess::getSaveDataSizeStr(int64_t size) const
{ {
static const int64_t kKiloByte = 1024; static const int64_t kKiloByte = 1024;
static const int64_t kMegaByte = 1024 * 1024; static const int64_t kMegaByte = 1024 * 1024;
@ -422,179 +631,3 @@ std::string getSaveDataSizeStr(int64_t size)
return sstr.str(); return sstr.str();
} }
NacpProcess::NacpProcess() :
mFile(nullptr),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false)
{
}
NacpProcess::~NacpProcess()
{
if (mOwnIFile)
{
delete mFile;
}
}
void NacpProcess::process()
{
fnd::Vec<byte_t> scratch;
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
scratch.alloc(mFile->size());
mFile->read(scratch.data(), 0, scratch.size());
mNacp.fromBytes(scratch.data(), scratch.size());
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
displayNacp();
}
void NacpProcess::setInputFile(fnd::IFile* file, bool ownIFile)
{
mFile = file;
mOwnIFile = ownIFile;
}
void NacpProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void NacpProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
const nn::hac::ApplicationControlPropertyBinary& NacpProcess::getApplicationControlPropertyBinary() const
{
return mNacp;
}
void NacpProcess::displayNacp()
{
printf("[ApplicationControlProperty]\n");
printf(" Menu Description:\n");
printf(" DisplayVersion: %s\n", mNacp.getDisplayVersion().c_str());
if (mNacp.getIsbn().empty() == false || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
printf(" ISBN: %s\n", mNacp.getIsbn().c_str());
for (size_t i = 0; i < mNacp.getTitle().size(); i++)
{
printf(" %s Title:\n", getLanguageStr(mNacp.getTitle()[i].language));
printf(" Name: %s\n", mNacp.getTitle()[i].name.c_str());
printf(" Publisher: %s\n", mNacp.getTitle()[i].publisher.c_str());
}
printf(" Logo:\n");
printf(" Type: %s\n", getLogoTypeStr(mNacp.getLogoType()));
printf(" Handling: %s\n", getLogoHandlingStr(mNacp.getLogoHandling()));
printf(" AddOnContent:\n");
printf(" BaseId: 0x%016" PRIx64 "\n", mNacp.getAocBaseId());
printf(" RegistrationType: %s\n", getAocRegistrationTypeStr(mNacp.getAocRegistrationType()));
printf(" RuntimeInstallMode: %s\n", getRuntimeAocInstallModeStr(mNacp.getRuntimeAocInstallMode()));
printf(" Play Log:\n");
printf(" PlayLogPolicy: %s\n", getPlayLogPolicyStr(mNacp.getPlayLogPolicy()));
printf(" PlayLogQueryCapability: %s\n", getPlayLogQueryCapabilityStr(mNacp.getPlayLogQueryCapability()));
if (mNacp.getPlayLogQueryableApplicationId().size() > 0)
{
printf(" PlayLogQueryableApplicationId:\n");
for (size_t i = 0; i < mNacp.getPlayLogQueryableApplicationId().size(); i++)
{
printf(" 0x%016" PRIx64 "\n", mNacp.getPlayLogQueryableApplicationId()[i]);
}
}
printf(" Parental Controls:\n");
printf(" ParentalControlFlag: %s\n", getParentalControlFlagStr(mNacp.getParentalControlFlag()));
for (size_t i = 0; i < mNacp.getRatingAge().size(); i++)
{
printf(" Age Restriction:\n");
printf(" Agency: %s\n", getOrganisationStr(mNacp.getRatingAge()[i].organisation));
printf(" Age: %d\n", mNacp.getRatingAge()[i].age);
}
if (mNacp.getBcatPassphase().empty() == false || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" BCAT:\n");
printf(" BcatPassphase: %s\n", mNacp.getBcatPassphase().c_str());
printf(" DeliveryCacheStorageSize: 0x%016" PRIx64 "\n", mNacp.getBcatDeliveryCacheStorageSize());
}
if (mNacp.getLocalCommunicationId().size() > 0)
{
printf(" Local Area Communication:\n");
printf(" LocalCommunicationId:\n");
for (size_t i = 0; i < mNacp.getLocalCommunicationId().size(); i++)
{
printf(" 0x%016" PRIx64 "\n", mNacp.getLocalCommunicationId()[i]);
}
}
printf(" SaveData:\n");
printf(" SaveDatawOwnerId: 0x%016" PRIx64 "\n", mNacp.getSaveDatawOwnerId());
if (mNacp.getUserAccountSaveDataSize().journal_size > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" UserAccountSaveData:\n");
printf(" Size: %s\n", getSaveDataSizeStr(mNacp.getUserAccountSaveDataSize().size).c_str());
printf(" JournalSize: %s\n", getSaveDataSizeStr(mNacp.getUserAccountSaveDataSize().journal_size).c_str());
}
if (mNacp.getDeviceSaveDataSize().journal_size > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" DeviceSaveData:\n");
printf(" Size: %s\n", getSaveDataSizeStr(mNacp.getDeviceSaveDataSize().size).c_str());
printf(" JournalSize: %s\n", getSaveDataSizeStr(mNacp.getDeviceSaveDataSize().journal_size).c_str());
}
if (mNacp.getUserAccountSaveDataMax().journal_size > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" UserAccountSaveDataMax:\n");
printf(" Size: %s\n", getSaveDataSizeStr(mNacp.getUserAccountSaveDataMax().size).c_str());
printf(" JournalSize: %s\n", getSaveDataSizeStr(mNacp.getUserAccountSaveDataMax().journal_size).c_str());
}
if (mNacp.getDeviceSaveDataMax().journal_size > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" DeviceSaveDataMax:\n");
printf(" Size: %s\n", getSaveDataSizeStr(mNacp.getDeviceSaveDataMax().size).c_str());
printf(" JournalSize: %s\n", getSaveDataSizeStr(mNacp.getDeviceSaveDataMax().journal_size).c_str());
}
if (mNacp.getTemporaryStorageSize() > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" TemporaryStorageSize: %s\n", getSaveDataSizeStr(mNacp.getTemporaryStorageSize()).c_str());
}
if (mNacp.getCacheStorageSize().journal_size > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" CacheStorage:\n");
printf(" Size: %s\n", getSaveDataSizeStr(mNacp.getCacheStorageSize().size).c_str());
printf(" JournalSize: %s\n", getSaveDataSizeStr(mNacp.getCacheStorageSize().journal_size).c_str());
printf(" MaxDataAndJournalSize: %s\n", getSaveDataSizeStr(mNacp.getCacheStorageDataAndJournalSizeMax()).c_str());
printf(" StorageIndexMax: 0x%" PRIx16 "\n", mNacp.getCacheStorageIndexMax());
}
printf(" Other Flags:\n");
printf(" StartupUserAccount: %s\n", getStartupUserAccountStr(mNacp.getStartupUserAccount()));
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" TouchScreenUsageMode: %s\n", getTouchScreenUsageModeStr(mNacp.getTouchScreenUsageMode()));
}
printf(" AttributeFlag: %s\n", getAttributeFlagStr(mNacp.getAttributeFlag()));
printf(" CrashReportMode: %s\n", getCrashReportModeStr(mNacp.getCrashReportMode()));
printf(" HDCP: %s\n", getHdcpStr(mNacp.getHdcp()));
printf(" ScreenshotMode: %s\n", getScreenshotModeStr(mNacp.getScreenshotMode()));
printf(" VideoCaptureMode: %s\n", getVideoCaptureModeStr(mNacp.getVideoCaptureMode()));
printf(" DataLossConfirmation: %s\n", getDataLossConfirmationStr(mNacp.getDataLossConfirmation()));
printf(" RepairFlag: %s\n", getRepairFlagStr(mNacp.getRepairFlag()));
printf(" ProgramIndex: 0x%02x\n", mNacp.getProgramIndex());
if (mNacp.getApplicationErrorCodeCategory().empty() == false || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" ApplicationErrorCodeCategory: %s\n", mNacp.getApplicationErrorCodeCategory().c_str());
}
if (mNacp.getSeedForPsuedoDeviceId() > 0 || mNacp.getPresenceGroupId() > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{
printf(" Other Ids:\n");
if (mNacp.getSeedForPsuedoDeviceId() > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
printf(" SeedForPsuedoDeviceId: 0x%016" PRIx64 "\n", mNacp.getSeedForPsuedoDeviceId());
if (mNacp.getPresenceGroupId() > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
printf(" PresenceGroupId: 0x%016" PRIx64 "\n", mNacp.getPresenceGroupId());
}
}

View file

@ -2,19 +2,19 @@
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/ApplicationControlPropertyBinary.h> #include <nn/hac/ApplicationControlPropertyBinary.h>
#include "nstool.h" #include "common.h"
class NacpProcess class NacpProcess
{ {
public: public:
NacpProcess(); NacpProcess();
~NacpProcess();
void process(); void process();
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setCliOutputMode(CliOutputMode type); void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -23,12 +23,31 @@ public:
private: private:
const std::string kModuleName = "NacpProcess"; const std::string kModuleName = "NacpProcess";
fnd::IFile* mFile; fnd::SharedPtr<fnd::IFile> mFile;
bool mOwnIFile;
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;
nn::hac::ApplicationControlPropertyBinary mNacp; nn::hac::ApplicationControlPropertyBinary mNacp;
void importNacp();
void displayNacp(); void displayNacp();
const char* getLanguageStr(nn::hac::nacp::Language var) const;
const char* getStartupUserAccountStr(nn::hac::nacp::StartupUserAccount var) const;
const char* getTouchScreenUsageModeStr(nn::hac::nacp::TouchScreenUsageMode var) const;
const char* getAocRegistrationTypeStr(nn::hac::nacp::AocRegistrationType var) const;
const char* getAttributeFlagStr(nn::hac::nacp::AttributeFlag var) const;
const char* getParentalControlFlagStr(nn::hac::nacp::ParentalControlFlag var) const;
const char* getScreenshotModeStr(nn::hac::nacp::ScreenshotMode var) const;
const char* getVideoCaptureModeStr(nn::hac::nacp::VideoCaptureMode var) const;
const char* getDataLossConfirmationStr(nn::hac::nacp::DataLossConfirmation var) const;
const char* getPlayLogPolicyStr(nn::hac::nacp::PlayLogPolicy var) const;
const char* getOrganisationStr(nn::hac::nacp::Organisation var) const;
const char* getLogoTypeStr(nn::hac::nacp::LogoType var) const;
const char* getLogoHandlingStr(nn::hac::nacp::LogoHandling var) const;
const char* getRuntimeAocInstallModeStr(nn::hac::nacp::RuntimeAocInstallMode var) const;
const char* getCrashReportModeStr(nn::hac::nacp::CrashReportMode var) const;
const char* getHdcpStr(nn::hac::nacp::Hdcp var) const;
const char* getPlayLogQueryCapabilityStr(nn::hac::nacp::PlayLogQueryCapability var) const;
const char* getRepairFlagStr(nn::hac::nacp::RepairFlag var) const;
std::string getSaveDataSizeStr(int64_t size) const;
}; };

File diff suppressed because it is too large Load diff

View file

@ -1,24 +1,25 @@
#pragma once #pragma once
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/SimpleFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/NcaHeader.h> #include <nn/hac/NcaHeader.h>
#include "HashTreeMeta.h" #include "HashTreeMeta.h"
#include "KeyConfiguration.h"
#include "nstool.h" #include "common.h"
class NcaProcess class NcaProcess
{ {
public: public:
NcaProcess(); NcaProcess();
~NcaProcess();
void process(); void process();
// generic // generic
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setKeyset(const sKeyset* keyset); void setKeyCfg(const KeyConfiguration& keycfg);
void setCliOutputMode(CliOutputMode type); void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -34,9 +35,8 @@ private:
const std::string kNpdmExefsPath = "main.npdm"; const std::string kNpdmExefsPath = "main.npdm";
// user options // user options
fnd::IFile* mFile; fnd::SharedPtr<fnd::IFile> mFile;
bool mOwnIFile; KeyConfiguration mKeyCfg;
const sKeyset* mKeyset;
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;
@ -84,15 +84,14 @@ private:
return !(*this == other); return !(*this == other);
} }
}; };
fnd::List<sKeyAreaKey> keak_list; fnd::List<sKeyAreaKey> kak_list;
sOptional<fnd::aes::sAes128Key> aes_ctr; sOptional<fnd::aes::sAes128Key> aes_ctr;
sOptional<fnd::aes::sAesXts128Key> aes_xts; } mContentKey;
} mBodyKeys;
struct sPartitionInfo struct sPartitionInfo
{ {
fnd::IFile* reader; fnd::SharedPtr<fnd::IFile> reader;
std::string fail_reason; std::string fail_reason;
size_t offset; size_t offset;
size_t size; size_t size;
@ -105,9 +104,20 @@ private:
fnd::aes::sAesIvCtr aes_ctr; fnd::aes::sAesIvCtr aes_ctr;
} mPartitions[nn::hac::nca::kPartitionNum]; } mPartitions[nn::hac::nca::kPartitionNum];
void importHeader();
void generateNcaBodyEncryptionKeys(); void generateNcaBodyEncryptionKeys();
void generatePartitionConfiguration(); void generatePartitionConfiguration();
void validateNcaSignatures(); void validateNcaSignatures();
void displayHeader(); void displayHeader();
void processPartitions(); void processPartitions();
const char* getFormatVersionStr(nn::hac::NcaHeader::FormatVersion format_ver) const;
const char* getDistributionTypeStr(nn::hac::nca::DistributionType dist_type) const;
const char* getContentTypeStr(nn::hac::nca::ContentType cont_type) const;
const char* getEncryptionTypeStr(nn::hac::nca::EncryptionType enc_type) const;
const char* getHashTypeStr(nn::hac::nca::HashType hash_type) const;
const char* getFormatTypeStr(nn::hac::nca::FormatType format_type) const;
const char* getKaekIndexStr(nn::hac::nca::KeyAreaEncryptionKeyIndex keak_index) const;
const char* getContentTypeForMountStr(nn::hac::nca::ContentType cont_type) const;
const char* getProgramPartitionNameStr(size_t i) const;
}; };

File diff suppressed because it is too large Load diff

View file

@ -2,20 +2,21 @@
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/NpdmBinary.h> #include <nn/hac/NpdmBinary.h>
#include "KeyConfiguration.h"
#include "nstool.h" #include "common.h"
class NpdmProcess class NpdmProcess
{ {
public: public:
NpdmProcess(); NpdmProcess();
~NpdmProcess();
void process(); void process();
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setKeyset(const sKeyset* keyset); void setKeyCfg(const KeyConfiguration& keycfg);
void setCliOutputMode(CliOutputMode type); void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -24,14 +25,15 @@ public:
private: private:
const std::string kModuleName = "NpdmProcess"; const std::string kModuleName = "NpdmProcess";
fnd::IFile* mFile; fnd::SharedPtr<fnd::IFile> mFile;
bool mOwnIFile; KeyConfiguration mKeyCfg;
const sKeyset* mKeyset;
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;
nn::hac::NpdmBinary mNpdm; nn::hac::NpdmBinary mNpdm;
void importNpdm();
void validateAcidSignature(const nn::hac::AccessControlInfoDescBinary& acid); void validateAcidSignature(const nn::hac::AccessControlInfoDescBinary& acid);
void validateAciFromAcid(const nn::hac::AccessControlInfoBinary& aci, const nn::hac::AccessControlInfoDescBinary& acid); void validateAciFromAcid(const nn::hac::AccessControlInfoBinary& aci, const nn::hac::AccessControlInfoDescBinary& acid);
@ -41,4 +43,14 @@ private:
void displayFac(const nn::hac::FileSystemAccessControlBinary& fac); void displayFac(const nn::hac::FileSystemAccessControlBinary& fac);
void displaySac(const nn::hac::ServiceAccessControlBinary& sac); void displaySac(const nn::hac::ServiceAccessControlBinary& sac);
void displayKernelCap(const nn::hac::KernelCapabilityBinary& kern); void displayKernelCap(const nn::hac::KernelCapabilityBinary& kern);
const char* getInstructionTypeStr(nn::hac::npdm::InstructionType type) const;
const char* getProcAddressSpaceTypeStr(nn::hac::npdm::ProcAddrSpaceType type) const;
const char* getAcidFlagStr(nn::hac::aci::Flag flag) const;
const char* getMiscFlagStr(nn::hac::MiscFlagsHandler::Flags flag) const;
const char* getFsaRightStr(nn::hac::fac::FsAccessFlag flag) const;
const char* getSaveDataOwnerAccessModeStr(nn::hac::fac::SaveDataOwnerIdAccessType type) const;
const char* getSystemCallStr(byte_t syscall_id) const;
const char* getMemMapPermStr(nn::hac::MemoryMappingHandler::MemoryPerm type) const;
const char* getMemMapTypeStr(nn::hac::MemoryMappingHandler::MappingType type) const;
}; };

View file

@ -1,3 +1,5 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h> #include <fnd/SimpleTextOutput.h>
#include <fnd/Vec.h> #include <fnd/Vec.h>
#include <fnd/lz4.h> #include <fnd/lz4.h>
@ -6,28 +8,14 @@
#include "NroProcess.h" #include "NroProcess.h"
NroProcess::NroProcess(): NroProcess::NroProcess():
mFile(nullptr), mFile(),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)), mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false) mVerify(false)
{ {
} }
NroProcess::~NroProcess()
{
if (mOwnIFile)
{
delete mFile;
}
}
void NroProcess::process() void NroProcess::process()
{ {
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
importHeader(); importHeader();
importCodeSegments(); importCodeSegments();
@ -40,10 +28,9 @@ void NroProcess::process()
mAssetProc.process(); mAssetProc.process();
} }
void NroProcess::setInputFile(fnd::IFile* file, bool ownIFile) void NroProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
{ {
mFile = file; mFile = file;
mOwnIFile = ownIFile;
} }
void NroProcess::setCliOutputMode(CliOutputMode type) void NroProcess::setCliOutputMode(CliOutputMode type)
@ -99,22 +86,28 @@ const RoMetadataProcess& NroProcess::getRoMetadataProcess() const
void NroProcess::importHeader() void NroProcess::importHeader()
{ {
fnd::Vec<byte_t> scratch; fnd::Vec<byte_t> scratch;
if (mFile->size() < sizeof(nn::hac::sNroHeader))
if (*mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
if ((*mFile)->size() < sizeof(nn::hac::sNroHeader))
{ {
throw fnd::Exception(kModuleName, "Corrupt NRO: file too small"); throw fnd::Exception(kModuleName, "Corrupt NRO: file too small");
} }
scratch.alloc(sizeof(nn::hac::sNroHeader)); scratch.alloc(sizeof(nn::hac::sNroHeader));
mFile->read(scratch.data(), 0, scratch.size()); (*mFile)->read(scratch.data(), 0, scratch.size());
mHdr.fromBytes(scratch.data(), scratch.size()); mHdr.fromBytes(scratch.data(), scratch.size());
// setup homebrew extension // setup homebrew extension
nn::hac::sNroHeader* raw_hdr = (nn::hac::sNroHeader*)scratch.data(); nn::hac::sNroHeader* raw_hdr = (nn::hac::sNroHeader*)scratch.data();
if (((le_uint64_t*)raw_hdr->reserved_0)->get() == nn::hac::nro::kNroHomebrewStructMagic && mFile->size() > mHdr.getNroSize()) if (((le_uint64_t*)raw_hdr->reserved_0)->get() == nn::hac::nro::kNroHomebrewStructMagic && (*mFile)->size() > mHdr.getNroSize())
{ {
mIsHomebrewNro = true; mIsHomebrewNro = true;
mAssetProc.setInputFile(new OffsetAdjustedIFile(mFile, false, mHdr.getNroSize(), mFile->size() - mHdr.getNroSize()), true); mAssetProc.setInputFile(new OffsetAdjustedIFile(mFile, mHdr.getNroSize(), (*mFile)->size() - mHdr.getNroSize()));
mAssetProc.setCliOutputMode(mCliOutputMode); mAssetProc.setCliOutputMode(mCliOutputMode);
mAssetProc.setVerifyMode(mVerify); mAssetProc.setVerifyMode(mVerify);
} }
@ -125,50 +118,43 @@ void NroProcess::importHeader()
void NroProcess::importCodeSegments() void NroProcess::importCodeSegments()
{ {
mTextBlob.alloc(mHdr.getTextInfo().size); mTextBlob.alloc(mHdr.getTextInfo().size);
mFile->read(mTextBlob.data(), mHdr.getTextInfo().memory_offset, mTextBlob.size()); (*mFile)->read(mTextBlob.data(), mHdr.getTextInfo().memory_offset, mTextBlob.size());
mRoBlob.alloc(mHdr.getRoInfo().size); mRoBlob.alloc(mHdr.getRoInfo().size);
mFile->read(mRoBlob.data(), mHdr.getRoInfo().memory_offset, mRoBlob.size()); (*mFile)->read(mRoBlob.data(), mHdr.getRoInfo().memory_offset, mRoBlob.size());
mDataBlob.alloc(mHdr.getDataInfo().size); mDataBlob.alloc(mHdr.getDataInfo().size);
mFile->read(mDataBlob.data(), mHdr.getDataInfo().memory_offset, mDataBlob.size()); (*mFile)->read(mDataBlob.data(), mHdr.getDataInfo().memory_offset, mDataBlob.size());
} }
void NroProcess::displayHeader() void NroProcess::displayHeader()
{ {
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0) std::cout << "[NRO Header]" << std::endl;
printf("[NRO Header]\n"); std::cout << " RoCrt: " << fnd::SimpleTextOutput::arrayToString(mHdr.getRoCrt().data, nn::hac::nro::kRoCrtSize, false, "") << std::endl;
printf(" RoCrt: "); std::cout << " ModuleId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getModuleId().data, nn::hac::nro::kModuleIdSize, false, "") << std::endl;
_HEXDUMP_L(mHdr.getRoCrt().data, nn::hac::nro::kRoCrtSize); std::cout << " NroSize: 0x" << std::hex << mHdr.getNroSize() << std::endl;
printf("\n"); std::cout << " Program Sections:" << std::endl;
printf(" ModuleId: "); std::cout << " .text:" << std::endl;
_HEXDUMP_L(mHdr.getModuleId().data, nn::hac::nro::kModuleIdSize); std::cout << " Offset: 0x" << std::hex << mHdr.getTextInfo().memory_offset << std::endl;
printf("\n"); std::cout << " Size: 0x" << std::hex << mHdr.getTextInfo().size << std::endl;
printf(" NroSize: 0x%" PRIx32 "\n", mHdr.getNroSize()); std::cout << " .ro:" << std::endl;
printf(" Program Sections:\n"); std::cout << " Offset: 0x" << std::hex << mHdr.getRoInfo().memory_offset << std::endl;
printf(" .text:\n"); std::cout << " Size: 0x" << std::hex << mHdr.getRoInfo().size << std::endl;
printf(" Offset: 0x%" PRIx32 "\n", mHdr.getTextInfo().memory_offset);
printf(" Size: 0x%" PRIx32 "\n", mHdr.getTextInfo().size);
printf(" .ro:\n");
printf(" Offset: 0x%" PRIx32 "\n", mHdr.getRoInfo().memory_offset);
printf(" Size: 0x%" PRIx32 "\n", mHdr.getRoInfo().size);
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{ {
printf(" .api_info:\n"); std::cout << " .api_info:" << std::endl;
printf(" Offset: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().memory_offset); std::cout << " Offset: 0x" << std::hex << mHdr.getRoEmbeddedInfo().memory_offset << std::endl;
printf(" Size: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().size); std::cout << " Size: 0x" << std::hex << mHdr.getRoEmbeddedInfo().size << std::endl;
printf(" .dynstr:\n"); std::cout << " .dynstr:" << std::endl;
printf(" Offset: 0x%" PRIx32 "\n", mHdr.getRoDynStrInfo().memory_offset); std::cout << " Offset: 0x" << std::hex << mHdr.getRoDynStrInfo().memory_offset << std::endl;
printf(" Size: 0x%" PRIx32 "\n", mHdr.getRoDynStrInfo().size); std::cout << " Size: 0x" << std::hex << mHdr.getRoDynStrInfo().size << std::endl;
printf(" .dynsym:\n"); std::cout << " .dynsym:" << std::endl;
printf(" Offset: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().memory_offset); std::cout << " Offset: 0x" << std::hex << mHdr.getRoDynSymInfo().memory_offset << std::endl;
printf(" Size: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().size); std::cout << " Size: 0x" << std::hex << mHdr.getRoDynSymInfo().size << std::endl;
} }
printf(" .data:\n"); std::cout << " .data:" << std::endl;
printf(" Offset: 0x%" PRIx32 "\n", mHdr.getDataInfo().memory_offset); std::cout << " Offset: 0x" << std::hex << mHdr.getDataInfo().memory_offset << std::endl;
printf(" Size: 0x%" PRIx32 "\n", mHdr.getDataInfo().size); std::cout << " Size: 0x" << std::hex << mHdr.getDataInfo().size << std::endl;
printf(" .bss:\n"); std::cout << " .bss:" << std::endl;
printf(" Size: 0x%" PRIx32 "\n", mHdr.getBssSize()); std::cout << " Size: 0x" << std::hex << mHdr.getBssSize() << std::endl;
#undef _HEXDUMP_L
} }
void NroProcess::processRoMeta() void NroProcess::processRoMeta()

View file

@ -3,22 +3,22 @@
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/npdm.h> #include <nn/hac/npdm.h>
#include <nn/hac/NroHeader.h> #include <nn/hac/NroHeader.h>
#include "AssetProcess.h" #include "AssetProcess.h"
#include "nstool.h" #include "common.h"
#include "RoMetadataProcess.h" #include "RoMetadataProcess.h"
class NroProcess class NroProcess
{ {
public: public:
NroProcess(); NroProcess();
~NroProcess();
void process(); void process();
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setCliOutputMode(CliOutputMode type); void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -36,9 +36,7 @@ public:
private: private:
const std::string kModuleName = "NroProcess"; const std::string kModuleName = "NroProcess";
fnd::IFile* mFile; fnd::SharedPtr<fnd::IFile> mFile;
bool mOwnIFile;
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;

View file

@ -1,3 +1,5 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h> #include <fnd/SimpleTextOutput.h>
#include <fnd/Vec.h> #include <fnd/Vec.h>
#include <fnd/lz4.h> #include <fnd/lz4.h>
@ -5,28 +7,14 @@
#include "NsoProcess.h" #include "NsoProcess.h"
NsoProcess::NsoProcess(): NsoProcess::NsoProcess():
mFile(nullptr), mFile(),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)), mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false) mVerify(false)
{ {
} }
NsoProcess::~NsoProcess()
{
if (mOwnIFile)
{
delete mFile;
}
}
void NsoProcess::process() void NsoProcess::process()
{ {
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
importHeader(); importHeader();
importCodeSegments(); importCodeSegments();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
@ -35,10 +23,9 @@ void NsoProcess::process()
processRoMeta(); processRoMeta();
} }
void NsoProcess::setInputFile(fnd::IFile* file, bool ownIFile) void NsoProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
{ {
mFile = file; mFile = file;
mOwnIFile = ownIFile;
} }
void NsoProcess::setCliOutputMode(CliOutputMode type) void NsoProcess::setCliOutputMode(CliOutputMode type)
@ -74,13 +61,19 @@ const RoMetadataProcess& NsoProcess::getRoMetadataProcess() const
void NsoProcess::importHeader() void NsoProcess::importHeader()
{ {
fnd::Vec<byte_t> scratch; fnd::Vec<byte_t> scratch;
if (mFile->size() < sizeof(nn::hac::sNsoHeader))
if (*mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
if ((*mFile)->size() < sizeof(nn::hac::sNsoHeader))
{ {
throw fnd::Exception(kModuleName, "Corrupt NSO: file too small"); throw fnd::Exception(kModuleName, "Corrupt NSO: file too small");
} }
scratch.alloc(sizeof(nn::hac::sNsoHeader)); scratch.alloc(sizeof(nn::hac::sNsoHeader));
mFile->read(scratch.data(), 0, scratch.size()); (*mFile)->read(scratch.data(), 0, scratch.size());
mHdr.fromBytes(scratch.data(), scratch.size()); mHdr.fromBytes(scratch.data(), scratch.size());
} }
@ -95,7 +88,7 @@ void NsoProcess::importCodeSegments()
if (mHdr.getTextSegmentInfo().is_compressed) if (mHdr.getTextSegmentInfo().is_compressed)
{ {
scratch.alloc(mHdr.getTextSegmentInfo().file_layout.size); scratch.alloc(mHdr.getTextSegmentInfo().file_layout.size);
mFile->read(scratch.data(), mHdr.getTextSegmentInfo().file_layout.offset, scratch.size()); (*mFile)->read(scratch.data(), mHdr.getTextSegmentInfo().file_layout.offset, scratch.size());
mTextBlob.alloc(mHdr.getTextSegmentInfo().memory_layout.size); mTextBlob.alloc(mHdr.getTextSegmentInfo().memory_layout.size);
fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mTextBlob.data(), (uint32_t)mTextBlob.size(), decompressed_len); fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mTextBlob.data(), (uint32_t)mTextBlob.size(), decompressed_len);
if (decompressed_len != mTextBlob.size()) if (decompressed_len != mTextBlob.size())
@ -106,7 +99,7 @@ void NsoProcess::importCodeSegments()
else else
{ {
mTextBlob.alloc(mHdr.getTextSegmentInfo().file_layout.size); mTextBlob.alloc(mHdr.getTextSegmentInfo().file_layout.size);
mFile->read(mTextBlob.data(), mHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.size()); (*mFile)->read(mTextBlob.data(), mHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.size());
} }
if (mHdr.getTextSegmentInfo().is_hashed) if (mHdr.getTextSegmentInfo().is_hashed)
{ {
@ -121,7 +114,7 @@ void NsoProcess::importCodeSegments()
if (mHdr.getRoSegmentInfo().is_compressed) if (mHdr.getRoSegmentInfo().is_compressed)
{ {
scratch.alloc(mHdr.getRoSegmentInfo().file_layout.size); scratch.alloc(mHdr.getRoSegmentInfo().file_layout.size);
mFile->read(scratch.data(), mHdr.getRoSegmentInfo().file_layout.offset, scratch.size()); (*mFile)->read(scratch.data(), mHdr.getRoSegmentInfo().file_layout.offset, scratch.size());
mRoBlob.alloc(mHdr.getRoSegmentInfo().memory_layout.size); mRoBlob.alloc(mHdr.getRoSegmentInfo().memory_layout.size);
fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mRoBlob.data(), (uint32_t)mRoBlob.size(), decompressed_len); fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mRoBlob.data(), (uint32_t)mRoBlob.size(), decompressed_len);
if (decompressed_len != mRoBlob.size()) if (decompressed_len != mRoBlob.size())
@ -132,7 +125,7 @@ void NsoProcess::importCodeSegments()
else else
{ {
mRoBlob.alloc(mHdr.getRoSegmentInfo().file_layout.size); mRoBlob.alloc(mHdr.getRoSegmentInfo().file_layout.size);
mFile->read(mRoBlob.data(), mHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.size()); (*mFile)->read(mRoBlob.data(), mHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.size());
} }
if (mHdr.getRoSegmentInfo().is_hashed) if (mHdr.getRoSegmentInfo().is_hashed)
{ {
@ -147,7 +140,7 @@ void NsoProcess::importCodeSegments()
if (mHdr.getDataSegmentInfo().is_compressed) if (mHdr.getDataSegmentInfo().is_compressed)
{ {
scratch.alloc(mHdr.getDataSegmentInfo().file_layout.size); scratch.alloc(mHdr.getDataSegmentInfo().file_layout.size);
mFile->read(scratch.data(), mHdr.getDataSegmentInfo().file_layout.offset, scratch.size()); (*mFile)->read(scratch.data(), mHdr.getDataSegmentInfo().file_layout.offset, scratch.size());
mDataBlob.alloc(mHdr.getDataSegmentInfo().memory_layout.size); mDataBlob.alloc(mHdr.getDataSegmentInfo().memory_layout.size);
fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mDataBlob.data(), (uint32_t)mDataBlob.size(), decompressed_len); fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mDataBlob.data(), (uint32_t)mDataBlob.size(), decompressed_len);
if (decompressed_len != mDataBlob.size()) if (decompressed_len != mDataBlob.size())
@ -158,7 +151,7 @@ void NsoProcess::importCodeSegments()
else else
{ {
mDataBlob.alloc(mHdr.getDataSegmentInfo().file_layout.size); mDataBlob.alloc(mHdr.getDataSegmentInfo().file_layout.size);
mFile->read(mDataBlob.data(), mHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.size()); (*mFile)->read(mDataBlob.data(), mHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.size());
} }
if (mHdr.getDataSegmentInfo().is_hashed) if (mHdr.getDataSegmentInfo().is_hashed)
{ {
@ -172,72 +165,61 @@ void NsoProcess::importCodeSegments()
void NsoProcess::displayNsoHeader() void NsoProcess::displayNsoHeader()
{ {
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0) std::cout << "[NSO Header]" << std::endl;
std::cout << " ModuleId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getModuleId().data, nn::hac::nso::kModuleIdSize, false, "") << std::endl;
printf("[NSO Header]\n");
printf(" ModuleId: ");
_HEXDUMP_L(mHdr.getModuleId().data, nn::hac::nso::kModuleIdSize);
printf("\n");
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
{ {
printf(" Program Segments:\n"); std::cout << " Program Segments:" << std::endl;
printf(" .module_name:\n"); std::cout << " .module_name:" << std::endl;
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getModuleNameInfo().offset); std::cout << " FileOffset: 0x" << std::hex << mHdr.getModuleNameInfo().offset << std::endl;
printf(" FileSize: 0x%" PRIx32 "\n", mHdr.getModuleNameInfo().size); std::cout << " FileSize: 0x" << std::hex << mHdr.getModuleNameInfo().size << std::endl;
printf(" .text:\n"); std::cout << " .text:" << std::endl;
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().file_layout.offset); std::cout << " FileOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.offset << std::endl;
printf(" FileSize: 0x%" PRIx32 "%s\n", mHdr.getTextSegmentInfo().file_layout.size, mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : ""); std::cout << " FileSize: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.size << (mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl;
printf(" .ro:\n"); std::cout << " .ro:" << std::endl;
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().file_layout.offset); std::cout << " FileOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.offset << std::endl;
printf(" FileSize: 0x%" PRIx32 "%s\n", mHdr.getRoSegmentInfo().file_layout.size, mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : ""); std::cout << " FileSize: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.size << (mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl;
printf(" .data:\n"); std::cout << " .data:" << std::endl;
printf(" FileOffset: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().file_layout.offset); std::cout << " FileOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.offset << std::endl;
printf(" FileSize: 0x%" PRIx32 "%s\n", mHdr.getDataSegmentInfo().file_layout.size, mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : ""); std::cout << " FileSize: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.size << (mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl;
} }
printf(" Program Sections:\n"); std::cout << " Program Sections:" << std::endl;
printf(" .text:\n"); std::cout << " .text:" << std::endl;
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().memory_layout.offset); std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.offset << std::endl;
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getTextSegmentInfo().memory_layout.size); std::cout << " MemorySize: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.size << std::endl;
if (mHdr.getTextSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) if (mHdr.getTextSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{ {
printf(" Hash: "); std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getTextSegmentInfo().hash.bytes, 32, false, "") << std::endl;
_HEXDUMP_L(mHdr.getTextSegmentInfo().hash.bytes, 32);
printf("\n");
} }
printf(" .ro:\n"); std::cout << " .ro:" << std::endl;
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.offset); std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.offset << std::endl;
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoSegmentInfo().memory_layout.size); std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.size << std::endl;
if (mHdr.getRoSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) if (mHdr.getRoSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{ {
printf(" Hash: "); std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getRoSegmentInfo().hash.bytes, 32, false, "") << std::endl;
_HEXDUMP_L(mHdr.getRoSegmentInfo().hash.bytes, 32);
printf("\n");
} }
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{ {
printf(" .api_info:\n"); std::cout << " .api_info:" << std::endl;
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().offset); std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoEmbeddedInfo().offset << std::endl;
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().size); std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoEmbeddedInfo().size << std::endl;
printf(" .dynstr:\n"); std::cout << " .dynstr:" << std::endl;
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoDynStrInfo().offset); std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoDynStrInfo().offset << std::endl;
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoDynStrInfo().size); std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoDynStrInfo().size << std::endl;
printf(" .dynsym:\n"); std::cout << " .dynsym:" << std::endl;
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().offset); std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoDynSymInfo().offset << std::endl;
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().size); std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoDynSymInfo().size << std::endl;
} }
printf(" .data:\n"); std::cout << " .data:" << std::endl;
printf(" MemoryOffset: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().memory_layout.offset); std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.offset << std::endl;
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getDataSegmentInfo().memory_layout.size); std::cout << " MemorySize: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.size << std::endl;
if (mHdr.getDataSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) if (mHdr.getDataSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{ {
printf(" Hash: "); std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getDataSegmentInfo().hash.bytes, 32, false, "") << std::endl;
_HEXDUMP_L(mHdr.getDataSegmentInfo().hash.bytes, 32);
printf("\n");
} }
printf(" .bss:\n"); std::cout << " .bss:" << std::endl;
printf(" MemorySize: 0x%" PRIx32 "\n", mHdr.getBssSize()); std::cout << " MemorySize: 0x" << std::hex << mHdr.getBssSize() << std::endl;
#undef _HEXDUMP_L
} }
void NsoProcess::processRoMeta() void NsoProcess::processRoMeta()

View file

@ -3,21 +3,21 @@
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/npdm.h> #include <nn/hac/npdm.h>
#include <nn/hac/NsoHeader.h> #include <nn/hac/NsoHeader.h>
#include "nstool.h" #include "common.h"
#include "RoMetadataProcess.h" #include "RoMetadataProcess.h"
class NsoProcess class NsoProcess
{ {
public: public:
NsoProcess(); NsoProcess();
~NsoProcess();
void process(); void process();
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setCliOutputMode(CliOutputMode type); void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -29,9 +29,7 @@ public:
private: private:
const std::string kModuleName = "NsoProcess"; const std::string kModuleName = "NsoProcess";
fnd::IFile* mFile; fnd::SharedPtr<fnd::IFile> mFile;
bool mOwnIFile;
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;
nn::hac::npdm::InstructionType mInstructionType; nn::hac::npdm::InstructionType mInstructionType;

View file

@ -1,21 +1,11 @@
#include "OffsetAdjustedIFile.h" #include "OffsetAdjustedIFile.h"
OffsetAdjustedIFile::OffsetAdjustedIFile(fnd::IFile* file, bool ownIFile, size_t offset, size_t size) : OffsetAdjustedIFile::OffsetAdjustedIFile(const fnd::SharedPtr<fnd::IFile>& file, size_t offset, size_t size) :
mOwnIFile(ownIFile),
mFile(file), mFile(file),
mBaseOffset(offset), mBaseOffset(offset),
mCurrentOffset(0), mCurrentOffset(0),
mSize(size) mSize(size)
{ {
}
OffsetAdjustedIFile::~OffsetAdjustedIFile()
{
if (mOwnIFile)
{
delete mFile;
}
} }
size_t OffsetAdjustedIFile::size() size_t OffsetAdjustedIFile::size()
@ -31,8 +21,8 @@ void OffsetAdjustedIFile::seek(size_t offset)
void OffsetAdjustedIFile::read(byte_t* out, size_t len) void OffsetAdjustedIFile::read(byte_t* out, size_t len)
{ {
// assert proper position in file // assert proper position in file
mFile->seek(mCurrentOffset + mBaseOffset); (*mFile)->seek(mCurrentOffset + mBaseOffset);
mFile->read(out, len); (*mFile)->read(out, len);
seek(mCurrentOffset + len); seek(mCurrentOffset + len);
} }
@ -45,8 +35,8 @@ void OffsetAdjustedIFile::read(byte_t* out, size_t offset, size_t len)
void OffsetAdjustedIFile::write(const byte_t* out, size_t len) void OffsetAdjustedIFile::write(const byte_t* out, size_t len)
{ {
// assert proper position in file // assert proper position in file
mFile->seek(mCurrentOffset + mBaseOffset); (*mFile)->seek(mCurrentOffset + mBaseOffset);
mFile->write(out, len); (*mFile)->write(out, len);
seek(mCurrentOffset + len); seek(mCurrentOffset + len);
} }

View file

@ -1,10 +1,10 @@
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
class OffsetAdjustedIFile : public fnd::IFile class OffsetAdjustedIFile : public fnd::IFile
{ {
public: public:
OffsetAdjustedIFile(fnd::IFile* file, bool ownIFile, size_t offset, size_t size); OffsetAdjustedIFile(const fnd::SharedPtr<fnd::IFile>& file, size_t offset, size_t size);
~OffsetAdjustedIFile();
size_t size(); size_t size();
void seek(size_t offset); void seek(size_t offset);
@ -13,8 +13,7 @@ public:
void write(const byte_t* out, size_t len); void write(const byte_t* out, size_t len);
void write(const byte_t* out, size_t offset, size_t len); void write(const byte_t* out, size_t offset, size_t len);
private: private:
bool mOwnIFile; fnd::SharedPtr<fnd::IFile> mFile;
fnd::IFile* mFile;
size_t mBaseOffset, mCurrentOffset; size_t mBaseOffset, mCurrentOffset;
size_t mSize; size_t mSize;
}; };

View file

@ -1,10 +1,11 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleFile.h> #include <fnd/SimpleFile.h>
#include <fnd/io.h> #include <fnd/io.h>
#include "PfsProcess.h" #include "PfsProcess.h"
PfsProcess::PfsProcess() : PfsProcess::PfsProcess() :
mFile(nullptr), mFile(),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)), mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false), mVerify(false),
mExtractPath(), mExtractPath(),
@ -15,36 +16,9 @@ PfsProcess::PfsProcess() :
{ {
} }
PfsProcess::~PfsProcess()
{
if (mOwnIFile)
{
delete mFile;
}
}
void PfsProcess::process() void PfsProcess::process()
{ {
fnd::Vec<byte_t> scratch; importHeader();
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
// open minimum header to get full header size
scratch.alloc(sizeof(nn::hac::sPfsHeader));
mFile->read(scratch.data(), 0, scratch.size());
if (validateHeaderMagic(((nn::hac::sPfsHeader*)scratch.data())) == false)
{
throw fnd::Exception(kModuleName, "Corrupt Header");
}
size_t pfsHeaderSize = determineHeaderSize(((nn::hac::sPfsHeader*)scratch.data()));
// open minimum header to get full header size
scratch.alloc(pfsHeaderSize);
mFile->read(scratch.data(), 0, scratch.size());
mPfs.fromBytes(scratch.data(), scratch.size());
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
{ {
@ -58,10 +32,9 @@ void PfsProcess::process()
extractFs(); extractFs();
} }
void PfsProcess::setInputFile(fnd::IFile* file, bool ownIFile) void PfsProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
{ {
mFile = file; mFile = file;
mOwnIFile = ownIFile;
} }
void PfsProcess::setCliOutputMode(CliOutputMode type) void PfsProcess::setCliOutputMode(CliOutputMode type)
@ -95,32 +68,64 @@ const nn::hac::PfsHeader& PfsProcess::getPfsHeader() const
return mPfs; return mPfs;
} }
void PfsProcess::importHeader()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
// open minimum header to get full header size
scratch.alloc(sizeof(nn::hac::sPfsHeader));
(*mFile)->read(scratch.data(), 0, scratch.size());
if (validateHeaderMagic(((nn::hac::sPfsHeader*)scratch.data())) == false)
{
throw fnd::Exception(kModuleName, "Corrupt Header");
}
size_t pfsHeaderSize = determineHeaderSize(((nn::hac::sPfsHeader*)scratch.data()));
// open minimum header to get full header size
scratch.alloc(pfsHeaderSize);
(*mFile)->read(scratch.data(), 0, scratch.size());
mPfs.fromBytes(scratch.data(), scratch.size());
}
void PfsProcess::displayHeader() void PfsProcess::displayHeader()
{ {
printf("[PartitionFS]\n"); std::cout << "[PartitionFS]" << std::endl;
printf(" Type: %s\n", mPfs.getFsType() == mPfs.TYPE_PFS0? "PFS0" : "HFS0"); std::cout << " Type: " << getFsTypeStr(mPfs.getFsType()) << std::endl;
printf(" FileNum: %" PRId64 "\n", (uint64_t)mPfs.getFileList().size()); std::cout << " FileNum: " << std::dec << mPfs.getFileList().size() << std::endl;
if (mMountName.empty() == false) if (mMountName.empty() == false)
printf(" MountPoint: %s%s\n", mMountName.c_str(), mMountName.at(mMountName.length()-1) != '/' ? "/" : ""); {
std::cout << " MountPoint: " << mMountName;
if (mMountName.at(mMountName.length()-1) != '/')
std::cout << "/";
std::cout << std::endl;
}
} }
void PfsProcess::displayFs() void PfsProcess::displayFs()
{ {
for (size_t i = 0; i < mPfs.getFileList().size(); i++) for (size_t i = 0; i < mPfs.getFileList().size(); i++)
{ {
printf(" %s", mPfs.getFileList()[i].name.c_str()); const nn::hac::PfsHeader::sFile& file = mPfs.getFileList()[i];
std::cout << " " << file.name;
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
{ {
if (mPfs.getFsType() == mPfs.TYPE_PFS0) switch (mPfs.getFsType())
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ")\n", (uint64_t)mPfs.getFileList()[i].offset, (uint64_t)mPfs.getFileList()[i].size); {
else case (nn::hac::PfsHeader::TYPE_PFS0):
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ", hash_protected_size=0x%" PRIx64 ")\n", (uint64_t)mPfs.getFileList()[i].offset, (uint64_t)mPfs.getFileList()[i].size, (uint64_t)mPfs.getFileList()[i].hash_protected_size); std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ")";
} break;
else case (nn::hac::PfsHeader::TYPE_HFS0):
{ std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ", hash_protected_size=0x" << file.hash_protected_size << ")";
printf("\n"); break;
} }
}
std::cout << std::endl;
} }
} }
@ -147,7 +152,7 @@ void PfsProcess::validateHfs()
for (size_t i = 0; i < file.size(); i++) for (size_t i = 0; i < file.size(); i++)
{ {
mCache.alloc(file[i].hash_protected_size); mCache.alloc(file[i].hash_protected_size);
mFile->read(mCache.data(), file[i].offset, file[i].hash_protected_size); (*mFile)->read(mCache.data(), file[i].offset, file[i].hash_protected_size);
fnd::sha::Sha256(mCache.data(), file[i].hash_protected_size, hash.bytes); fnd::sha::Sha256(mCache.data(), file[i].hash_protected_size, hash.bytes);
if (hash != file[i].hash) if (hash != file[i].hash)
{ {
@ -178,12 +183,32 @@ void PfsProcess::extractFs()
printf("extract=[%s]\n", file_path.c_str()); printf("extract=[%s]\n", file_path.c_str());
outFile.open(file_path, outFile.Create); outFile.open(file_path, outFile.Create);
mFile->seek(file[i].offset); (*mFile)->seek(file[i].offset);
for (size_t j = 0; j < ((file[i].size / kCacheSize) + ((file[i].size % kCacheSize) != 0)); j++) for (size_t j = 0; j < ((file[i].size / kCacheSize) + ((file[i].size % kCacheSize) != 0)); j++)
{ {
mFile->read(mCache.data(), _MIN(file[i].size - (kCacheSize * j),kCacheSize)); (*mFile)->read(mCache.data(), _MIN(file[i].size - (kCacheSize * j),kCacheSize));
outFile.write(mCache.data(), _MIN(file[i].size - (kCacheSize * j),kCacheSize)); outFile.write(mCache.data(), _MIN(file[i].size - (kCacheSize * j),kCacheSize));
} }
outFile.close(); outFile.close();
} }
} }
const char* PfsProcess::getFsTypeStr(nn::hac::PfsHeader::FsType type) const
{
const char* str = nullptr;
switch (type)
{
case (nn::hac::PfsHeader::TYPE_PFS0):
str = "PFS0";
break;
case (nn::hac::PfsHeader::TYPE_HFS0):
str = "HFS0";
break;
default:
str = "Unknown";
break;
}
return str;
}

View file

@ -2,20 +2,20 @@
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/PfsHeader.h> #include <nn/hac/PfsHeader.h>
#include "nstool.h" #include "common.h"
class PfsProcess class PfsProcess
{ {
public: public:
PfsProcess(); PfsProcess();
~PfsProcess();
void process(); void process();
// generic // generic
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setCliOutputMode(CliOutputMode type); void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -30,8 +30,7 @@ private:
const std::string kModuleName = "PfsProcess"; const std::string kModuleName = "PfsProcess";
static const size_t kCacheSize = 0x10000; static const size_t kCacheSize = 0x10000;
fnd::IFile* mFile; fnd::SharedPtr<fnd::IFile> mFile;
bool mOwnIFile;
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;
@ -44,10 +43,13 @@ private:
nn::hac::PfsHeader mPfs; nn::hac::PfsHeader mPfs;
void importHeader();
void displayHeader(); void displayHeader();
void displayFs(); void displayFs();
size_t determineHeaderSize(const nn::hac::sPfsHeader* hdr); size_t determineHeaderSize(const nn::hac::sPfsHeader* hdr);
bool validateHeaderMagic(const nn::hac::sPfsHeader* hdr); bool validateHeaderMagic(const nn::hac::sPfsHeader* hdr);
void validateHfs(); void validateHfs();
void extractFs(); void extractFs();
const char* getFsTypeStr(nn::hac::PfsHeader::FsType type) const;
}; };

View file

@ -1,6 +1,5 @@
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <fnd/SimpleTextOutput.h> #include <fnd/SimpleTextOutput.h>
#include <nn/pki/SignUtils.h> #include <nn/pki/SignUtils.h>
#include "OffsetAdjustedIFile.h" #include "OffsetAdjustedIFile.h"
@ -8,29 +7,16 @@
#include "PkiValidator.h" #include "PkiValidator.h"
PkiCertProcess::PkiCertProcess() : PkiCertProcess::PkiCertProcess() :
mFile(nullptr), mFile(),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)), mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false) mVerify(false)
{ {
} }
PkiCertProcess::~PkiCertProcess()
{
if (mOwnIFile)
{
delete mFile;
}
}
void PkiCertProcess::process() void PkiCertProcess::process()
{ {
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
importCerts(); importCerts();
if (mVerify) if (mVerify)
validateCerts(); validateCerts();
@ -38,15 +24,14 @@ void PkiCertProcess::process()
displayCerts(); displayCerts();
} }
void PkiCertProcess::setInputFile(fnd::IFile* file, bool ownIFile) void PkiCertProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
{ {
mFile = file; mFile = file;
mOwnIFile = ownIFile;
} }
void PkiCertProcess::setKeyset(const sKeyset* keyset) void PkiCertProcess::setKeyCfg(const KeyConfiguration& keycfg)
{ {
mKeyset = keyset; mKeyCfg = keycfg;
} }
void PkiCertProcess::setCliOutputMode(CliOutputMode mode) void PkiCertProcess::setCliOutputMode(CliOutputMode mode)
@ -63,8 +48,13 @@ void PkiCertProcess::importCerts()
{ {
fnd::Vec<byte_t> scratch; fnd::Vec<byte_t> scratch;
scratch.alloc(mFile->size()); if (*mFile == nullptr)
mFile->read(scratch.data(), 0, scratch.size()); {
throw fnd::Exception(kModuleName, "No file reader set.");
}
scratch.alloc((*mFile)->size());
(*mFile)->read(scratch.data(), 0, scratch.size());
nn::pki::SignedData<nn::pki::CertificateBody> cert; nn::pki::SignedData<nn::pki::CertificateBody> cert;
for (size_t f_pos = 0; f_pos < scratch.size(); f_pos += cert.getBytes().size()) for (size_t f_pos = 0; f_pos < scratch.size(); f_pos += cert.getBytes().size())
@ -80,7 +70,7 @@ void PkiCertProcess::validateCerts()
try try
{ {
pki.setRootKey(mKeyset->pki.root_sign_key); pki.setKeyCfg(mKeyCfg);
pki.addCertificates(mCert); pki.addCertificates(mCert);
} }
catch (const fnd::Exception& e) catch (const fnd::Exception& e)
@ -100,15 +90,11 @@ void PkiCertProcess::displayCerts()
void PkiCertProcess::displayCert(const nn::pki::SignedData<nn::pki::CertificateBody>& cert) void PkiCertProcess::displayCert(const nn::pki::SignedData<nn::pki::CertificateBody>& cert)
{ {
#define _SPLIT_VER(ver) ( (ver>>26) & 0x3f), ( (ver>>20) & 0x3f), ( (ver>>16) & 0xf), (ver & 0xffff)
#define _HEXDUMP_U(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02X", var[a__a__A]); } while(0)
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0)
std::cout << "[NNPKI Certificate]" << std::endl; std::cout << "[NNPKI Certificate]" << std::endl;
std::cout << " SignType " << getSignTypeStr(cert.getSignature().getSignType()); std::cout << " SignType " << getSignTypeStr(cert.getSignature().getSignType());
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
std::cout << " (0x" << std::hex << cert.getSignature().getSignType() << ") (" << getEndiannessStr(cert.getSignature().isLittleEndian()); std::cout << " (0x" << std::hex << cert.getSignature().getSignType() << ") (" << getEndiannessStr(cert.getSignature().isLittleEndian()) << ")";
std::cout << std::endl; std::cout << std::endl;
std::cout << " Issuer: " << cert.getBody().getIssuer() << std::endl; std::cout << " Issuer: " << cert.getBody().getIssuer() << std::endl;
@ -130,9 +116,9 @@ void PkiCertProcess::displayCert(const nn::pki::SignedData<nn::pki::CertificateB
else if (cert.getBody().getPublicKeyType() == nn::pki::cert::RSA2048) else if (cert.getBody().getPublicKeyType() == nn::pki::cert::RSA2048)
{ {
std::cout << " PublicKey:" << std::endl; std::cout << " PublicKey:" << std::endl;
std::cout << " Public Exponent:" << std::endl;
fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().modulus, getHexDumpLen(fnd::rsa::kRsa2048Size), 0x10, 6);
std::cout << " Modulus:" << std::endl; std::cout << " Modulus:" << std::endl;
fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().modulus, getHexDumpLen(fnd::rsa::kRsa2048Size), 0x10, 6);
std::cout << " Public Exponent:" << std::endl;
fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().public_exponent, fnd::rsa::kRsaPublicExponentSize, 0x10, 6); fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().public_exponent, fnd::rsa::kRsaPublicExponentSize, 0x10, 6);
} }
else if (cert.getBody().getPublicKeyType() == nn::pki::cert::ECDSA240) else if (cert.getBody().getPublicKeyType() == nn::pki::cert::ECDSA240)
@ -143,12 +129,6 @@ void PkiCertProcess::displayCert(const nn::pki::SignedData<nn::pki::CertificateB
std::cout << " S:" << std::endl; std::cout << " S:" << std::endl;
fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().s, getHexDumpLen(fnd::ecdsa::kEcdsa240Size), 0x10, 6); fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().s, getHexDumpLen(fnd::ecdsa::kEcdsa240Size), 0x10, 6);
} }
#undef _HEXDUMP_L
#undef _HEXDUMP_U
#undef _SPLIT_VER
} }
size_t PkiCertProcess::getHexDumpLen(size_t max_size) const size_t PkiCertProcess::getHexDumpLen(size_t max_size) const

View file

@ -2,22 +2,23 @@
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/List.h> #include <fnd/List.h>
#include <fnd/Vec.h> #include <fnd/Vec.h>
#include <nn/pki/SignedData.h> #include <nn/pki/SignedData.h>
#include <nn/pki/CertificateBody.h> #include <nn/pki/CertificateBody.h>
#include "nstool.h" #include "KeyConfiguration.h"
#include "common.h"
class PkiCertProcess class PkiCertProcess
{ {
public: public:
PkiCertProcess(); PkiCertProcess();
~PkiCertProcess();
void process(); void process();
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setKeyset(const sKeyset* keyset); void setKeyCfg(const KeyConfiguration& keycfg);
void setCliOutputMode(CliOutputMode type); void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -25,9 +26,8 @@ private:
const std::string kModuleName = "PkiCertProcess"; const std::string kModuleName = "PkiCertProcess";
static const size_t kSmallHexDumpLen = 0x10; static const size_t kSmallHexDumpLen = 0x10;
fnd::IFile* mFile; fnd::SharedPtr<fnd::IFile> mFile;
bool mOwnIFile; KeyConfiguration mKeyCfg;
const sKeyset* mKeyset;
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;

View file

@ -1,15 +1,15 @@
#include "PkiValidator.h"
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <nn/pki/SignUtils.h> #include <nn/pki/SignUtils.h>
#include "PkiValidator.h"
PkiValidator::PkiValidator() PkiValidator::PkiValidator()
{ {
clearCertificates(); clearCertificates();
} }
void PkiValidator::setRootKey(const fnd::rsa::sRsa4096Key& root_key) void PkiValidator::setKeyCfg(const KeyConfiguration& keycfg)
{ {
// save a copy of the certificate bank // save a copy of the certificate bank
fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>> old_certs = mCertificateBank; fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>> old_certs = mCertificateBank;
@ -18,7 +18,7 @@ void PkiValidator::setRootKey(const fnd::rsa::sRsa4096Key& root_key)
mCertificateBank.clear(); mCertificateBank.clear();
// overwrite the root key // overwrite the root key
mRootKey = root_key; mKeyCfg = keycfg;
// if there were certificates before, reimport them (so they are checked against the new root key) // if there were certificates before, reimport them (so they are checked against the new root key)
if (old_certs.size() > 0) if (old_certs.size() > 0)
@ -96,13 +96,28 @@ void PkiValidator::validateSignature(const std::string& issuer, nn::pki::sign::S
int sig_validate_res = -1; int sig_validate_res = -1;
// special case if signed by Root // special case if signed by Root
if (issuer == nn::pki::sign::kRootIssuerStr) if (issuer.find('-', 0) == std::string::npos)
{ {
if (sign_algo != nn::pki::sign::SIGN_ALGO_RSA4096) fnd::rsa::sRsa4096Key rsa4096_pub;
fnd::rsa::sRsa2048Key rsa2048_pub;
fnd::ecdsa::sEcdsa240Key ecdsa_pub;
if (mKeyCfg.getPkiRootSignKey(issuer, rsa4096_pub) == true && sign_algo == nn::pki::sign::SIGN_ALGO_RSA4096)
{ {
throw fnd::Exception(kModuleName, "Issued by Root, but does not have a RSA4096 signature"); sig_validate_res = fnd::rsa::pkcs::rsaVerify(rsa4096_pub, getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data());
}
else if (mKeyCfg.getPkiRootSignKey(issuer, rsa2048_pub) == true && sign_algo == nn::pki::sign::SIGN_ALGO_RSA2048)
{
sig_validate_res = fnd::rsa::pkcs::rsaVerify(rsa2048_pub, getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data());
}
else if (mKeyCfg.getPkiRootSignKey(issuer, ecdsa_pub) == true && sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240)
{
throw fnd::Exception(kModuleName, "ECDSA signatures are not supported");
}
else
{
throw fnd::Exception(kModuleName, "Public key for issuer \"" + issuer + "\" does not exist.");
} }
sig_validate_res = fnd::rsa::pkcs::rsaVerify(mRootKey, getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data());
} }
else else
{ {

View file

@ -6,13 +6,14 @@
#include <nn/pki/SignedData.h> #include <nn/pki/SignedData.h>
#include <nn/pki/CertificateBody.h> #include <nn/pki/CertificateBody.h>
#include <string> #include <string>
#include "KeyConfiguration.h"
class PkiValidator class PkiValidator
{ {
public: public:
PkiValidator(); PkiValidator();
void setRootKey(const fnd::rsa::sRsa4096Key& root_key); void setKeyCfg(const KeyConfiguration& keycfg);
void addCertificates(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs); void addCertificates(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs);
void addCertificate(const nn::pki::SignedData<nn::pki::CertificateBody>& cert); void addCertificate(const nn::pki::SignedData<nn::pki::CertificateBody>& cert);
void clearCertificates(); void clearCertificates();
@ -22,8 +23,7 @@ public:
private: private:
const std::string kModuleName = "NNPkiValidator"; const std::string kModuleName = "NNPkiValidator";
KeyConfiguration mKeyCfg;
fnd::rsa::sRsa4096Key mRootKey;
fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>> mCertificateBank; fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>> mCertificateBank;
void makeCertIdent(const nn::pki::SignedData<nn::pki::CertificateBody>& cert, std::string& ident) const; void makeCertIdent(const nn::pki::SignedData<nn::pki::CertificateBody>& cert, std::string& ident) const;

View file

@ -1,4 +1,6 @@
#include <sstream> #include <sstream>
#include <iostream>
#include <iomanip>
#include <fnd/types.h> #include <fnd/types.h>
#include "RoMetadataProcess.h" #include "RoMetadataProcess.h"
@ -23,12 +25,8 @@ RoMetadataProcess::RoMetadataProcess() :
void RoMetadataProcess::process() void RoMetadataProcess::process()
{ {
if (mRoBlob.size() == 0)
{
throw fnd::Exception(kModuleName, "No ro binary set.");
}
importApiList(); importApiList();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
displayRoMetaData(); displayRoMetaData();
} }
@ -101,6 +99,11 @@ const fnd::List<ElfSymbolParser::sElfSymbol>& RoMetadataProcess::getSymbolList()
void RoMetadataProcess::importApiList() void RoMetadataProcess::importApiList()
{ {
if (mRoBlob.size() == 0)
{
throw fnd::Exception(kModuleName, "No ro binary set.");
}
if (mApiInfo.size > 0) if (mApiInfo.size > 0)
{ {
std::stringstream list_stream(std::string((char*)mRoBlob.data() + mApiInfo.offset, mApiInfo.size)); std::stringstream list_stream(std::string((char*)mRoBlob.data() + mApiInfo.offset, mApiInfo.size));
@ -133,71 +136,71 @@ void RoMetadataProcess::displayRoMetaData()
if (api_num > 0 && (mListApi || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))) if (api_num > 0 && (mListApi || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)))
{ {
printf("[SDK API List]\n"); std::cout << "[SDK API List]" << std::endl;
if (mSdkVerApiList.size() > 0) if (mSdkVerApiList.size() > 0)
{ {
printf(" Sdk Revision: %s\n", mSdkVerApiList[0].getModuleName().c_str()); std::cout << " Sdk Revision: " << mSdkVerApiList[0].getModuleName() << std::endl;
} }
if (mPublicApiList.size() > 0) if (mPublicApiList.size() > 0)
{ {
printf(" Public APIs:\n"); std::cout << " Public APIs:" << std::endl;
for (size_t i = 0; i < mPublicApiList.size(); i++) for (size_t i = 0; i < mPublicApiList.size(); i++)
{ {
printf(" %s (vender: %s)\n", mPublicApiList[i].getModuleName().c_str(), mPublicApiList[i].getVenderName().c_str()); std::cout << " " << mPublicApiList[i].getModuleName() << " (vender: " << mPublicApiList[i].getVenderName() << ")" << std::endl;
} }
} }
if (mDebugApiList.size() > 0) if (mDebugApiList.size() > 0)
{ {
printf(" Debug APIs:\n"); std::cout << " Debug APIs:" << std::endl;
for (size_t i = 0; i < mDebugApiList.size(); i++) for (size_t i = 0; i < mDebugApiList.size(); i++)
{ {
printf(" %s (vender: %s)\n", mDebugApiList[i].getModuleName().c_str(), mDebugApiList[i].getVenderName().c_str()); std::cout << " " << mDebugApiList[i].getModuleName() << " (vender: " << mDebugApiList[i].getVenderName() << ")" << std::endl;
} }
} }
if (mPrivateApiList.size() > 0) if (mPrivateApiList.size() > 0)
{ {
printf(" Private APIs:\n"); std::cout << " Private APIs:" << std::endl;
for (size_t i = 0; i < mPrivateApiList.size(); i++) for (size_t i = 0; i < mPrivateApiList.size(); i++)
{ {
printf(" %s (vender: %s)\n", mPrivateApiList[i].getModuleName().c_str(), mPrivateApiList[i].getVenderName().c_str()); std::cout << " " << mPrivateApiList[i].getModuleName() << " (vender: " << mPrivateApiList[i].getVenderName() << ")" << std::endl;
} }
} }
} }
if (mSymbolList.getSymbolList().size() > 0 && (mListSymbols || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))) if (mSymbolList.getSymbolList().size() > 0 && (mListSymbols || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)))
{ {
printf("[Symbol List]\n"); std::cout << "[Symbol List]" << std::endl;
for (size_t i = 0; i < mSymbolList.getSymbolList().size(); i++) for (size_t i = 0; i < mSymbolList.getSymbolList().size(); i++)
{ {
const ElfSymbolParser::sElfSymbol& symbol = mSymbolList.getSymbolList()[i]; const ElfSymbolParser::sElfSymbol& symbol = mSymbolList.getSymbolList()[i];
printf(" %s [SHN=%s (%04x)][STT=%s][STB=%s]\n", symbol.name.c_str(), getSectionIndexStr(symbol.shn_index), symbol.shn_index, getSymbolTypeStr(symbol.symbol_type), getSymbolBindingStr(symbol.symbol_binding)); std::cout << " " << symbol.name << " [SHN=" << getSectionIndexStr(symbol.shn_index) << " (" << std::hex << std::setw(4) << std::setfill('0') << symbol.shn_index << ")][STT=" << getSymbolTypeStr(symbol.symbol_type) << "][STB=" << getSymbolBindingStr(symbol.symbol_binding) << "]" << std::endl;
} }
} }
} }
const char* RoMetadataProcess::getSectionIndexStr(nn::hac::elf::SpecialSectionIndex shn_index) const const char* RoMetadataProcess::getSectionIndexStr(uint16_t shn_index) const
{ {
const char* str; const char* str;
switch (shn_index) switch (shn_index)
{ {
case (nn::hac::elf::SHN_UNDEF): case (fnd::elf::SHN_UNDEF):
str = "UNDEF"; str = "UNDEF";
break; break;
case (nn::hac::elf::SHN_LOPROC): case (fnd::elf::SHN_LOPROC):
str = "LOPROC"; str = "LOPROC";
break; break;
case (nn::hac::elf::SHN_HIPROC): case (fnd::elf::SHN_HIPROC):
str = "HIPROC"; str = "HIPROC";
break; break;
case (nn::hac::elf::SHN_LOOS): case (fnd::elf::SHN_LOOS):
str = "LOOS"; str = "LOOS";
break; break;
case (nn::hac::elf::SHN_HIOS): case (fnd::elf::SHN_HIOS):
str = "HIOS"; str = "HIOS";
break; break;
case (nn::hac::elf::SHN_ABS): case (fnd::elf::SHN_ABS):
str = "ABS"; str = "ABS";
break; break;
case (nn::hac::elf::SHN_COMMON): case (fnd::elf::SHN_COMMON):
str = "COMMON"; str = "COMMON";
break; break;
default: default:
@ -207,36 +210,36 @@ const char* RoMetadataProcess::getSectionIndexStr(nn::hac::elf::SpecialSectionIn
return str; return str;
} }
const char* RoMetadataProcess::getSymbolTypeStr(nn::hac::elf::SymbolType symbol_type) const const char* RoMetadataProcess::getSymbolTypeStr(byte_t symbol_type) const
{ {
const char* str; const char* str;
switch (symbol_type) switch (symbol_type)
{ {
case (nn::hac::elf::STT_NOTYPE): case (fnd::elf::STT_NOTYPE):
str = "NOTYPE"; str = "NOTYPE";
break; break;
case (nn::hac::elf::STT_OBJECT): case (fnd::elf::STT_OBJECT):
str = "OBJECT"; str = "OBJECT";
break; break;
case (nn::hac::elf::STT_FUNC): case (fnd::elf::STT_FUNC):
str = "FUNC"; str = "FUNC";
break; break;
case (nn::hac::elf::STT_SECTION): case (fnd::elf::STT_SECTION):
str = "SECTION"; str = "SECTION";
break; break;
case (nn::hac::elf::STT_FILE): case (fnd::elf::STT_FILE):
str = "FILE"; str = "FILE";
break; break;
case (nn::hac::elf::STT_LOOS): case (fnd::elf::STT_LOOS):
str = "LOOS"; str = "LOOS";
break; break;
case (nn::hac::elf::STT_HIOS): case (fnd::elf::STT_HIOS):
str = "HIOS"; str = "HIOS";
break; break;
case (nn::hac::elf::STT_LOPROC): case (fnd::elf::STT_LOPROC):
str = "LOPROC"; str = "LOPROC";
break; break;
case (nn::hac::elf::STT_HIPROC): case (fnd::elf::STT_HIPROC):
str = "HIPROC"; str = "HIPROC";
break; break;
default: default:
@ -246,30 +249,30 @@ const char* RoMetadataProcess::getSymbolTypeStr(nn::hac::elf::SymbolType symbol_
return str; return str;
} }
const char* RoMetadataProcess::getSymbolBindingStr(nn::hac::elf::SymbolBinding symbol_binding) const const char* RoMetadataProcess::getSymbolBindingStr(byte_t symbol_binding) const
{ {
const char* str; const char* str;
switch (symbol_binding) switch (symbol_binding)
{ {
case (nn::hac::elf::STB_LOCAL): case (fnd::elf::STB_LOCAL):
str = "LOCAL"; str = "LOCAL";
break; break;
case (nn::hac::elf::STB_GLOBAL): case (fnd::elf::STB_GLOBAL):
str = "GLOBAL"; str = "GLOBAL";
break; break;
case (nn::hac::elf::STB_WEAK): case (fnd::elf::STB_WEAK):
str = "WEAK"; str = "WEAK";
break; break;
case (nn::hac::elf::STB_LOOS): case (fnd::elf::STB_LOOS):
str = "LOOS"; str = "LOOS";
break; break;
case (nn::hac::elf::STB_HIOS): case (fnd::elf::STB_HIOS):
str = "HIOS"; str = "HIOS";
break; break;
case (nn::hac::elf::STB_LOPROC): case (fnd::elf::STB_LOPROC):
str = "LOPROC"; str = "LOPROC";
break; break;
case (nn::hac::elf::STB_HIPROC): case (fnd::elf::STB_HIPROC):
str = "HIPROC"; str = "HIPROC";
break; break;
default: default:

View file

@ -6,7 +6,7 @@
#include <nn/hac/npdm.h> #include <nn/hac/npdm.h>
#include "nstool.h" #include "common.h"
#include "SdkApiString.h" #include "SdkApiString.h"
#include "ElfSymbolParser.h" #include "ElfSymbolParser.h"
@ -62,7 +62,7 @@ private:
void importApiList(); void importApiList();
void displayRoMetaData(); void displayRoMetaData();
const char* getSectionIndexStr(nn::hac::elf::SpecialSectionIndex shn_index) const; const char* getSectionIndexStr(uint16_t shn_index) const;
const char* getSymbolTypeStr(nn::hac::elf::SymbolType symbol_type) const; const char* getSymbolTypeStr(byte_t symbol_type) const;
const char* getSymbolBindingStr(nn::hac::elf::SymbolBinding symbol_binding) const; const char* getSymbolBindingStr(byte_t symbol_binding) const;
}; };

View file

@ -1,11 +1,12 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h> #include <fnd/SimpleTextOutput.h>
#include <fnd/SimpleFile.h> #include <fnd/SimpleFile.h>
#include <fnd/io.h> #include <fnd/io.h>
#include "RomfsProcess.h" #include "RomfsProcess.h"
RomfsProcess::RomfsProcess() : RomfsProcess::RomfsProcess() :
mFile(nullptr), mFile(),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)), mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false), mVerify(false),
mExtractPath(), mExtractPath(),
@ -20,36 +21,24 @@ RomfsProcess::RomfsProcess() :
mRootDir.file_list.clear(); mRootDir.file_list.clear();
} }
RomfsProcess::~RomfsProcess()
{
if (mOwnIFile)
{
delete mFile;
}
}
void RomfsProcess::process() void RomfsProcess::process()
{ {
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
resolveRomfs(); resolveRomfs();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
{ {
displayHeader(); displayHeader();
if (mListFs || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) if (mListFs || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
displayFs(); displayFs();
} }
if (mExtract) if (mExtract)
extractFs(); extractFs();
} }
void RomfsProcess::setInputFile(fnd::IFile* file, bool ownIFile) void RomfsProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
{ {
mFile = file; mFile = file;
mOwnIFile = ownIFile;
} }
void RomfsProcess::setCliOutputMode(CliOutputMode type) void RomfsProcess::setCliOutputMode(CliOutputMode type)
@ -87,19 +76,19 @@ void RomfsProcess::printTab(size_t tab) const
{ {
for (size_t i = 0; i < tab; i++) for (size_t i = 0; i < tab; i++)
{ {
printf(" "); std::cout << " ";
} }
} }
void RomfsProcess::displayFile(const sFile& file, size_t tab) const void RomfsProcess::displayFile(const sFile& file, size_t tab) const
{ {
printTab(tab); printTab(tab);
printf("%s", file.name.c_str()); std::cout << file.name;
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
{ {
printf(" (offset=0x%" PRIx64 ", size=0x%" PRIx64 ")", file.offset, file.size); std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ")";
} }
putchar('\n'); std::cout << std::endl;
} }
void RomfsProcess::displayDir(const sDirectory& dir, size_t tab) const void RomfsProcess::displayDir(const sDirectory& dir, size_t tab) const
@ -107,7 +96,7 @@ void RomfsProcess::displayDir(const sDirectory& dir, size_t tab) const
if (dir.name.empty() == false) if (dir.name.empty() == false)
{ {
printTab(tab); printTab(tab);
printf("%s\n", dir.name.c_str()); std::cout << dir.name << std::endl;
} }
for (size_t i = 0; i < dir.dir_list.size(); i++) for (size_t i = 0; i < dir.dir_list.size(); i++)
@ -122,11 +111,16 @@ void RomfsProcess::displayDir(const sDirectory& dir, size_t tab) const
void RomfsProcess::displayHeader() void RomfsProcess::displayHeader()
{ {
printf("[RomFS]\n"); std::cout << "[RomFS]" << std::endl;
printf(" DirNum: %" PRId64 "\n", (uint64_t)mDirNum); std::cout << " DirNum: " << std::dec << mDirNum << std::endl;
printf(" FileNum: %" PRId64 "\n", (uint64_t)mFileNum); std::cout << " FileNum: " << std::dec << mFileNum << std::endl;
if (mMountName.empty() == false) if (mMountName.empty() == false)
printf(" MountPoint: %s%s\n", mMountName.c_str(), mMountName.at(mMountName.length()-1) != '/' ? "/" : ""); {
std::cout << " MountPoint: " << mMountName;
if (mMountName.at(mMountName.length()-1) != '/')
std::cout << "/";
std::cout << std::endl;
}
} }
void RomfsProcess::displayFs() void RomfsProcess::displayFs()
@ -156,13 +150,13 @@ void RomfsProcess::extractDir(const std::string& path, const sDirectory& dir)
fnd::io::appendToPath(file_path, dir.file_list[i].name); fnd::io::appendToPath(file_path, dir.file_list[i].name);
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
printf("extract=[%s]\n", file_path.c_str()); std::cout << "extract=[" << file_path << "]" << std::endl;
outFile.open(file_path, outFile.Create); outFile.open(file_path, outFile.Create);
mFile->seek(dir.file_list[i].offset); (*mFile)->seek(dir.file_list[i].offset);
for (size_t j = 0; j < ((dir.file_list[i].size / kCacheSize) + ((dir.file_list[i].size % kCacheSize) != 0)); j++) for (size_t j = 0; j < ((dir.file_list[i].size / kCacheSize) + ((dir.file_list[i].size % kCacheSize) != 0)); j++)
{ {
mFile->read(mCache.data(), _MIN(dir.file_list[i].size - (kCacheSize * j),kCacheSize)); (*mFile)->read(mCache.data(), _MIN(dir.file_list[i].size - (kCacheSize * j),kCacheSize));
outFile.write(mCache.data(), _MIN(dir.file_list[i].size - (kCacheSize * j),kCacheSize)); outFile.write(mCache.data(), _MIN(dir.file_list[i].size - (kCacheSize * j),kCacheSize));
} }
outFile.close(); outFile.close();
@ -254,8 +248,13 @@ void RomfsProcess::importDirectory(uint32_t dir_offset, sDirectory& dir)
void RomfsProcess::resolveRomfs() void RomfsProcess::resolveRomfs()
{ {
if (*mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
// read header // read header
mFile->read((byte_t*)&mHdr, 0, sizeof(nn::hac::sRomfsHeader)); (*mFile)->read((byte_t*)&mHdr, 0, sizeof(nn::hac::sRomfsHeader));
// logic check on the header layout // logic check on the header layout
if (validateHeaderLayout(&mHdr) == false) if (validateHeaderLayout(&mHdr) == false)
@ -265,13 +264,13 @@ void RomfsProcess::resolveRomfs()
// read directory nodes // read directory nodes
mDirNodes.alloc(mHdr.sections[nn::hac::romfs::DIR_NODE_TABLE].size.get()); mDirNodes.alloc(mHdr.sections[nn::hac::romfs::DIR_NODE_TABLE].size.get());
mFile->read(mDirNodes.data(), mHdr.sections[nn::hac::romfs::DIR_NODE_TABLE].offset.get(), mDirNodes.size()); (*mFile)->read(mDirNodes.data(), mHdr.sections[nn::hac::romfs::DIR_NODE_TABLE].offset.get(), mDirNodes.size());
//printf("[RAW DIR NODES]\n"); //printf("[RAW DIR NODES]\n");
//fnd::SimpleTextOutput::hxdStyleDump(mDirNodes.data(), mDirNodes.size()); //fnd::SimpleTextOutput::hxdStyleDump(mDirNodes.data(), mDirNodes.size());
// read file nodes // read file nodes
mFileNodes.alloc(mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].size.get()); mFileNodes.alloc(mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].size.get());
mFile->read(mFileNodes.data(), mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].offset.get(), mFileNodes.size()); (*mFile)->read(mFileNodes.data(), mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].offset.get(), mFileNodes.size());
//printf("[RAW FILE NODES]\n"); //printf("[RAW FILE NODES]\n");
//fnd::SimpleTextOutput::hxdStyleDump(mFileNodes.data(), mFileNodes.size()); //fnd::SimpleTextOutput::hxdStyleDump(mFileNodes.data(), mFileNodes.size());

View file

@ -2,11 +2,12 @@
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/Vec.h> #include <fnd/Vec.h>
#include <fnd/List.h> #include <fnd/List.h>
#include <nn/hac/romfs.h> #include <nn/hac/romfs.h>
#include "nstool.h" #include "common.h"
class RomfsProcess class RomfsProcess
{ {
@ -77,12 +78,11 @@ public:
}; };
RomfsProcess(); RomfsProcess();
~RomfsProcess();
void process(); void process();
// generic // generic
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setCliOutputMode(CliOutputMode type); void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -96,8 +96,7 @@ private:
const std::string kModuleName = "RomfsProcess"; const std::string kModuleName = "RomfsProcess";
static const size_t kCacheSize = 0x10000; static const size_t kCacheSize = 0x10000;
fnd::IFile* mFile; fnd::SharedPtr<fnd::IFile> mFile;
bool mOwnIFile;
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;

View file

@ -1,10 +1,12 @@
#include "UserSettings.h" #include "UserSettings.h"
#include "version.h" #include "version.h"
#include "PkiValidator.h" #include "PkiValidator.h"
#include "KeyConfiguration.h"
#include <vector> #include <vector>
#include <string> #include <string>
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
#include <iomanip>
#include <cstdlib> #include <cstdlib>
#include <fnd/io.h> #include <fnd/io.h>
#include <fnd/SimpleFile.h> #include <fnd/SimpleFile.h>
@ -37,14 +39,16 @@ void UserSettings::parseCmdArgs(const std::vector<std::string>& arg_list)
populateCmdArgs(arg_list, args); populateCmdArgs(arg_list, args);
populateKeyset(args); populateKeyset(args);
populateUserSettings(args); populateUserSettings(args);
if (_HAS_BIT(mOutputMode, OUTPUT_KEY_DATA))
dumpKeyConfig();
} }
void UserSettings::showHelp() void UserSettings::showHelp()
{ {
printf("NSTool v%d.%d.%d (C) %s\n", VER_MAJOR, VER_MINOR, VER_PATCH, AUTHORS); printf("%s v%d.%d.%d (C) %s\n", APP_NAME, VER_MAJOR, VER_MINOR, VER_PATCH, AUTHORS);
printf("Built: %s %s\n\n", __TIME__, __DATE__); printf("Built: %s %s\n\n", __TIME__, __DATE__);
printf("Usage: nstool [options... ] <file>\n"); printf("Usage: %s [options... ] <file>\n", BIN_NAME);
printf("\n General Options:\n"); printf("\n General Options:\n");
printf(" -d, --dev Use devkit keyset.\n"); printf(" -d, --dev Use devkit keyset.\n");
printf(" -k, --keyset Specify keyset file.\n"); printf(" -k, --keyset Specify keyset file.\n");
@ -55,18 +59,18 @@ void UserSettings::showHelp()
printf(" --showlayout Show layout metadata.\n"); printf(" --showlayout Show layout metadata.\n");
printf(" -v, --verbose Verbose output.\n"); printf(" -v, --verbose Verbose output.\n");
printf("\n XCI (GameCard Image)\n"); printf("\n XCI (GameCard Image)\n");
printf(" nstool [--listfs] [--update <dir> --logo <dir> --normal <dir> --secure <dir>] <.xci file>\n"); printf(" %s [--listfs] [--update <dir> --logo <dir> --normal <dir> --secure <dir>] <.xci file>\n", BIN_NAME);
printf(" --listfs Print file system in embedded partitions.\n"); printf(" --listfs Print file system in embedded partitions.\n");
printf(" --update Extract \"update\" partition to directory.\n"); printf(" --update Extract \"update\" partition to directory.\n");
printf(" --logo Extract \"logo\" partition to directory.\n"); printf(" --logo Extract \"logo\" partition to directory.\n");
printf(" --normal Extract \"normal\" partition to directory.\n"); printf(" --normal Extract \"normal\" partition to directory.\n");
printf(" --secure Extract \"secure\" partition to directory.\n"); printf(" --secure Extract \"secure\" partition to directory.\n");
printf("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)\n"); printf("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)\n");
printf(" nstool [--listfs] [--fsdir <dir>] <file>\n"); printf(" %s [--listfs] [--fsdir <dir>] <file>\n", BIN_NAME);
printf(" --listfs Print file system.\n"); printf(" --listfs Print file system.\n");
printf(" --fsdir Extract file system to directory.\n"); printf(" --fsdir Extract file system to directory.\n");
printf("\n NCA (Nintendo Content Archive)\n"); printf("\n NCA (Nintendo Content Archive)\n");
printf(" nstool [--listfs] [--bodykey <key> --titlekey <key>] [--part0 <dir> ...] <.nca file>\n"); printf(" %s [--listfs] [--bodykey <key> --titlekey <key>] [--part0 <dir> ...] <.nca file>\n", BIN_NAME);
printf(" --listfs Print file system in embedded partitions.\n"); printf(" --listfs Print file system in embedded partitions.\n");
printf(" --titlekey Specify title key extracted from ticket.\n"); printf(" --titlekey Specify title key extracted from ticket.\n");
printf(" --bodykey Specify body encryption key.\n"); printf(" --bodykey Specify body encryption key.\n");
@ -77,12 +81,12 @@ void UserSettings::showHelp()
printf(" --part2 Extract \"partition 2\" to directory.\n"); printf(" --part2 Extract \"partition 2\" to directory.\n");
printf(" --part3 Extract \"partition 3\" to directory.\n"); printf(" --part3 Extract \"partition 3\" to directory.\n");
printf("\n NSO (Nintendo Software Object), NRO (Nintendo Relocatable Object)\n"); printf("\n NSO (Nintendo Software Object), NRO (Nintendo Relocatable Object)\n");
printf(" nstool [--listapi --listsym] [--insttype <inst. type>] <file>\n"); printf(" %s [--listapi --listsym] [--insttype <inst. type>] <file>\n", BIN_NAME);
printf(" --listapi Print SDK API List.\n"); printf(" --listapi Print SDK API List.\n");
printf(" --listsym Print Code Symbols.\n"); printf(" --listsym Print Code Symbols.\n");
printf(" --insttype Specify instruction type [64bit|32bit] (64bit is assumed).\n"); printf(" --insttype Specify instruction type [64bit|32bit] (64bit is assumed).\n");
printf("\n ASET (Homebrew Asset Blob)\n"); printf("\n ASET (Homebrew Asset Blob)\n");
printf(" nstool [--listfs] [--icon <file> --nacp <file> --fsdir <dir>] <file>\n"); printf(" %s [--listfs] [--icon <file> --nacp <file> --fsdir <dir>] <file>\n", BIN_NAME);
printf(" --listfs Print filesystem in embedded RomFS partition.\n"); printf(" --listfs Print filesystem in embedded RomFS partition.\n");
printf(" --icon Extract icon partition to file.\n"); printf(" --icon Extract icon partition to file.\n");
printf(" --nacp Extract NACP partition to file.\n"); printf(" --nacp Extract NACP partition to file.\n");
@ -95,9 +99,9 @@ const std::string UserSettings::getInputPath() const
return mInputPath; return mInputPath;
} }
const sKeyset& UserSettings::getKeyset() const const KeyConfiguration& UserSettings::getKeyCfg() const
{ {
return mKeyset; return mKeyCfg;
} }
FileType UserSettings::getFileType() const FileType UserSettings::getFileType() const
@ -385,36 +389,23 @@ void UserSettings::populateCmdArgs(const std::vector<std::string>& arg_list, sCm
void UserSettings::populateKeyset(sCmdArgs& args) void UserSettings::populateKeyset(sCmdArgs& args)
{ {
fnd::aes::sAes128Key zeros_aes_key;
fnd::aes::sAesXts128Key zeros_aes_xts_key;
memset((void*)&zeros_aes_key, 0, sizeof(fnd::aes::sAes128Key));
memset((void*)&zeros_aes_xts_key, 0, sizeof(fnd::aes::sAesXts128Key));
memset((void*)&mKeyset, 0, sizeof(sKeyset));
fnd::ResourceFileReader res;
if (args.keyset_path.isSet) if (args.keyset_path.isSet)
{ {
res.processFile(*args.keyset_path); mKeyCfg.importHactoolGenericKeyfile(*args.keyset_path);
} }
else else
{ {
// open other resource files in $HOME/.switch/prod.keys (or $HOME/.switch/dev.keys if -d/--dev is set). // open other resource files in $HOME/.switch/prod.keys (or $HOME/.switch/dev.keys if -d/--dev is set).
std::string home;
if (home.empty()) fnd::io::getEnvironVar(home, "HOME");
if (home.empty()) fnd::io::getEnvironVar(home, "USERPROFILE");
if (home.empty()) return;
const std::string kKeysetNameStr[2] = {"prod.keys", "dev.keys"};
const std::string kHomeSwitchDirStr = ".switch";
std::string keyset_path; std::string keyset_path;
fnd::io::appendToPath(keyset_path, home); getSwitchPath(keyset_path);
fnd::io::appendToPath(keyset_path, kHomeSwitchDirStr); if (keyset_path.empty())
fnd::io::appendToPath(keyset_path, kKeysetNameStr[args.devkit_keys.isSet ? *args.devkit_keys : 0]); return;
fnd::io::appendToPath(keyset_path, kGeneralKeyfileName[args.devkit_keys.isSet]);
try try
{ {
res.processFile(keyset_path); mKeyCfg.importHactoolGenericKeyfile(keyset_path);
} }
catch (const fnd::Exception&) catch (const fnd::Exception&)
{ {
@ -423,131 +414,28 @@ void UserSettings::populateKeyset(sCmdArgs& args)
} }
// suffix
const std::string kKeyStr = "key";
const std::string kKekStr = "kek";
const std::string kSourceStr = "source";
const std::string kRsaKeySuffix[2] = {"sign_key_private", "sign_key_modulus"};
const std::string kKeyIndex[kMasterKeyNum] = {"00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f"};
// keyname bases
const std::string kMasterBase = "master";
const std::string kPackage1Base = "package1";
const std::string kPackage2Base = "package2";
const std::string kXciHeaderBase = "xci_header";
const std::string kNcaHeaderBase[2] = {"header", "nca_header"};
const std::string kKekGenSource = "aes_kek_generation";
const std::string kKeyGenSource = "aes_key_generation";
const std::string kAcidBase = "acid";
const std::string kPkiRootBase = "pki_root";
const std::string kTicketCommonKeyBase[2] = { "titlekek", "ticket_commonkey" };
const std::string kNcaBodyBase[2] = {"key_area_key", "nca_body_keak"};
const std::string kNcaBodyKeakIndexName[3] = {"application", "ocean", "system"};
// sources
fnd::aes::sAes128Key master_key[kMasterKeyNum] = { zeros_aes_key };
fnd::aes::sAes128Key package2_key_source = zeros_aes_key;
fnd::aes::sAes128Key ticket_titlekek_source = zeros_aes_key;
fnd::aes::sAes128Key key_area_key_source[3] = { zeros_aes_key, zeros_aes_key, zeros_aes_key };
fnd::aes::sAes128Key aes_kek_generation_source = zeros_aes_key;
fnd::aes::sAes128Key aes_key_generation_source = zeros_aes_key;
fnd::aes::sAes128Key nca_header_kek_source = zeros_aes_key;
fnd::aes::sAesXts128Key nca_header_key_source = zeros_aes_xts_key;
#define _CONCAT_2_STRINGS(str1, str2) ((str1) + "_" + (str2))
#define _CONCAT_3_STRINGS(str1, str2, str3) ((str1) + "_"+ (str2) + "_" + (str3))
std::string key,val;
#define _SAVE_KEYDATA(key_name, array, len) \
key = (key_name); \
val = res[key]; \
if (val.empty() == false) { \
decodeHexStringToBytes(key, val, (byte_t*)array, len); \
}
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPackage2Base, kKeyStr, kSourceStr), package2_key_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[0], kSourceStr), ticket_titlekek_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[1], kSourceStr), ticket_titlekek_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[0], kNcaBodyKeakIndexName[0], kSourceStr), key_area_key_source[0].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[0], kNcaBodyKeakIndexName[1], kSourceStr), key_area_key_source[1].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[0], kNcaBodyKeakIndexName[2], kSourceStr), key_area_key_source[2].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[1], kNcaBodyKeakIndexName[0], kSourceStr), key_area_key_source[0].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[1], kNcaBodyKeakIndexName[1], kSourceStr), key_area_key_source[1].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[1], kNcaBodyKeakIndexName[2], kSourceStr), key_area_key_source[2].key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kKekGenSource, kSourceStr), aes_kek_generation_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kKeyGenSource, kSourceStr), aes_key_generation_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaHeaderBase[0], kKekStr, kSourceStr), nca_header_kek_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaHeaderBase[0], kKeyStr, kSourceStr), nca_header_key_source.key, 0x20);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaHeaderBase[1], kKekStr, kSourceStr), nca_header_kek_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaHeaderBase[1], kKeyStr, kSourceStr), nca_header_key_source.key, 0x20);
// Store Key Variants/Derivatives
for (size_t i = 0; i < kMasterKeyNum; i++)
{
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kMasterBase, kKeyStr, kKeyIndex[i]), master_key[i].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPackage1Base, kKeyStr, kKeyIndex[i]), mKeyset.package1_key[i].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPackage2Base, kKeyStr, kKeyIndex[i]), mKeyset.package2_key[i].key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[0], kKeyIndex[i]), mKeyset.ticket.titlekey_kek[i].key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[1], kKeyIndex[i]), mKeyset.ticket.titlekey_kek[i].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[0], kNcaBodyKeakIndexName[0], kKeyIndex[i]), mKeyset.nca.key_area_key[0][i].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[0], kNcaBodyKeakIndexName[1], kKeyIndex[i]), mKeyset.nca.key_area_key[1][i].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[0], kNcaBodyKeakIndexName[2], kKeyIndex[i]), mKeyset.nca.key_area_key[2][i].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[1], kNcaBodyKeakIndexName[0], kKeyIndex[i]), mKeyset.nca.key_area_key[0][i].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[1], kNcaBodyKeakIndexName[1], kKeyIndex[i]), mKeyset.nca.key_area_key[1][i].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaBodyBase[1], kNcaBodyKeakIndexName[2], kKeyIndex[i]), mKeyset.nca.key_area_key[2][i].key, 0x10);
}
// store nca header key
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[0], kKeyStr), mKeyset.nca.header_key.key[0], 0x20);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kKeyStr), mKeyset.nca.header_key.key[0], 0x20);
// store xci header key
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kKeyStr), mKeyset.xci.header_key.key, 0x10);
// store rsa keys
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kRsaKeySuffix[0]), mKeyset.nca.header_sign_key.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kRsaKeySuffix[1]), mKeyset.nca.header_sign_key.modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kRsaKeySuffix[0]), mKeyset.xci.header_sign_key.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kRsaKeySuffix[1]), mKeyset.xci.header_sign_key.modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[0]), mKeyset.acid_sign_key.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[1]), mKeyset.acid_sign_key.modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[0]), mKeyset.package2_sign_key.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[1]), mKeyset.package2_sign_key.modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase, kRsaKeySuffix[0]), mKeyset.pki.root_sign_key.priv_exponent, fnd::rsa::kRsa4096Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase, kRsaKeySuffix[1]), mKeyset.pki.root_sign_key.modulus, fnd::rsa::kRsa4096Size);
// save keydata from input args
if (args.nca_bodykey.isSet) if (args.nca_bodykey.isSet)
{ {
if (args.nca_bodykey.var.length() == (sizeof(fnd::aes::sAes128Key)*2)) fnd::aes::sAes128Key tmp_key;
{ fnd::Vec<byte_t> tmp_raw;
decodeHexStringToBytes("--bodykey", args.nca_bodykey.var, mKeyset.nca.manual_body_key_aesctr.key, sizeof(fnd::aes::sAes128Key)); fnd::SimpleTextOutput::stringToArray(args.nca_bodykey.var, tmp_raw);
} if (tmp_raw.size() != sizeof(fnd::aes::sAes128Key))
else throw fnd::Exception(kModuleName, "Key: \"--bodykey\" has incorrect length");
{ memcpy(tmp_key.key, tmp_raw.data(), 16);
decodeHexStringToBytes("--bodykey", args.nca_bodykey.var, mKeyset.nca.manual_body_key_aesxts.key[0], sizeof(fnd::aes::sAesXts128Key)); mKeyCfg.addNcaExternalContentKey(kDummyRightsIdForUserBodyKey, tmp_key);
}
} }
if (args.nca_titlekey.isSet) if (args.nca_titlekey.isSet)
{ {
if (args.nca_titlekey.var.length() == (sizeof(fnd::aes::sAes128Key)*2)) fnd::aes::sAes128Key tmp_key;
{ fnd::Vec<byte_t> tmp_raw;
decodeHexStringToBytes("--titlekey", args.nca_titlekey.var, mKeyset.nca.manual_title_key_aesctr.key, sizeof(fnd::aes::sAes128Key)); fnd::SimpleTextOutput::stringToArray(args.nca_titlekey.var, tmp_raw);
} if (tmp_raw.size() != sizeof(fnd::aes::sAes128Key))
else throw fnd::Exception(kModuleName, "Key: \"--titlekey\" has incorrect length");
{ memcpy(tmp_key.key, tmp_raw.data(), 16);
decodeHexStringToBytes("--titlekey", args.nca_titlekey.var, mKeyset.nca.manual_title_key_aesxts.key[0], sizeof(fnd::aes::sAesXts128Key)); mKeyCfg.addNcaExternalContentKey(kDummyRightsIdForUserTitleKey, tmp_key);
}
} }
// import certificate chain // import certificate chain
@ -601,7 +489,7 @@ void UserSettings::populateKeyset(sCmdArgs& args)
try try
{ {
pki_validator.setRootKey(mKeyset.pki.root_sign_key); pki_validator.setKeyCfg(mKeyCfg);
pki_validator.addCertificates(mCertChain); pki_validator.addCertificates(mCertChain);
pki_validator.validateSignature(tik.getBody().getIssuer(), tik.getSignature().getSignType(), tik.getSignature().getSignature(), tik_hash); pki_validator.validateSignature(tik.getBody().getIssuer(), tik.getSignature().getSignType(), tik.getSignature().getSignature(), tik_hash);
} }
@ -615,75 +503,24 @@ void UserSettings::populateKeyset(sCmdArgs& args)
// extract title key // extract title key
if (tik.getBody().getTitleKeyEncType() == nn::es::ticket::AES128_CBC) if (tik.getBody().getTitleKeyEncType() == nn::es::ticket::AES128_CBC)
{ {
memcpy(mKeyset.nca.manual_title_key_aesctr.key, tik.getBody().getEncTitleKey(), fnd::aes::kAes128KeySize); fnd::aes::sAes128Key enc_title_key;
memcpy(enc_title_key.key, tik.getBody().getEncTitleKey(), 16);
fnd::aes::sAes128Key common_key, external_content_key;
if (mKeyCfg.getETicketCommonKey(nn::hac::NcaUtils::getMasterKeyRevisionFromKeyGeneration(tik.getBody().getCommonKeyId()), common_key) == true)
{
nn::hac::AesKeygen::generateKey(external_content_key.key, tik.getBody().getEncTitleKey(), common_key.key);
mKeyCfg.addNcaExternalContentKey(tik.getBody().getRightsId(), external_content_key);
}
else
{
std::cout << "[WARNING] Titlekey not imported from ticket because commonkey was not available" << std::endl;
}
} }
else else
{ {
std::cout << "[WARNING] Titlekey not imported from ticket because it is personalised" << std::endl; std::cout << "[WARNING] Titlekey not imported from ticket because it is personalised" << std::endl;
} }
} }
#undef _SAVE_KEYDATA
#undef _CONCAT_3_STRINGS
#undef _CONCAT_2_STRINGS
// Derive keys
for (size_t i = 0; i < kMasterKeyNum; i++)
{
if (master_key[i] != zeros_aes_key)
{
if (aes_kek_generation_source != zeros_aes_key && aes_key_generation_source != zeros_aes_key)
{
if (i == 0 && nca_header_kek_source != zeros_aes_key && nca_header_key_source != zeros_aes_xts_key)
{
if (mKeyset.nca.header_key == zeros_aes_xts_key)
{
fnd::aes::sAes128Key nca_header_kek;
nn::hac::AesKeygen::generateKey(nca_header_kek.key, aes_kek_generation_source.key, nca_header_kek_source.key, aes_key_generation_source.key, master_key[i].key);
nn::hac::AesKeygen::generateKey(mKeyset.nca.header_key.key[0], nca_header_key_source.key[0], nca_header_kek.key);
nn::hac::AesKeygen::generateKey(mKeyset.nca.header_key.key[1], nca_header_key_source.key[1], nca_header_kek.key);
//printf("nca header key[0] ");
//fnd::SimpleTextOutput::hexDump(mKeyset.nca.header_key.key[0], 0x10);
//printf("nca header key[1] ");
//fnd::SimpleTextOutput::hexDump(mKeyset.nca.header_key.key[1], 0x10);
}
}
for (size_t j = 0; j < nn::hac::nca::kKeyAreaEncryptionKeyNum; j++)
{
if (key_area_key_source[j] != zeros_aes_key && mKeyset.nca.key_area_key[j][i] == zeros_aes_key)
{
nn::hac::AesKeygen::generateKey(mKeyset.nca.key_area_key[j][i].key, aes_kek_generation_source.key, key_area_key_source[j].key, aes_key_generation_source.key, master_key[i].key);
//printf("nca keak %d/%02d ", j, i);
//fnd::SimpleTextOutput::hexDump(mKeyset.nca.key_area_key[j][i].key, 0x10);
}
}
}
if (ticket_titlekek_source != zeros_aes_key && mKeyset.ticket.titlekey_kek[i] == zeros_aes_key)
{
nn::hac::AesKeygen::generateKey(mKeyset.ticket.titlekey_kek[i].key, ticket_titlekek_source.key, master_key[i].key);
//printf("ticket titlekek %02d ", i);
//fnd::SimpleTextOutput::hexDump(mKeyset.ticket.titlekey_kek[i].key, 0x10);
}
if (package2_key_source != zeros_aes_key && mKeyset.package2_key[i] == zeros_aes_key)
{
nn::hac::AesKeygen::generateKey(mKeyset.package2_key[i].key, package2_key_source.key, master_key[i].key);
//printf("package2 key %02d ", i);
//fnd::SimpleTextOutput::hexDump(mKeyset.package2_key[i].key, 0x10);
}
}
/*
for (size_t j = 0; j < nn::hac::nca::kKeyAreaEncryptionKeyNum; j++)
{
if (mKeyset.nca.key_area_key[j][i] != zeros_aes_key)
{
printf("nca body keak %d/%02d ", j, i);
fnd::SimpleTextOutput::hexDump(mKeyset.nca.key_area_key[j][i].key, 0x10);
}
}
*/
}
} }
void UserSettings::populateUserSettings(sCmdArgs& args) void UserSettings::populateUserSettings(sCmdArgs& args)
@ -747,21 +584,6 @@ void UserSettings::populateUserSettings(sCmdArgs& args)
throw fnd::Exception(kModuleName, "Unknown file type."); throw fnd::Exception(kModuleName, "Unknown file type.");
} }
void UserSettings::decodeHexStringToBytes(const std::string& name, const std::string& str, byte_t* out, size_t out_len)
{
size_t size = str.size();
if ((size % 2) || ((size / 2) != out_len))
{
throw fnd::Exception(kModuleName, "Key: \"" + name + "\" has incorrect length");
}
for (size_t i = 0; i < out_len; i++)
{
out[i] = (charToByte(str[i * 2]) << 4) | charToByte(str[(i * 2) + 1]);
}
}
FileType UserSettings::getFileTypeFromString(const std::string& type_str) FileType UserSettings::getFileTypeFromString(const std::string& type_str)
{ {
std::string str = type_str; std::string str = type_str;
@ -804,7 +626,7 @@ FileType UserSettings::getFileTypeFromString(const std::string& type_str)
FileType UserSettings::determineFileTypeFromFile(const std::string& path) FileType UserSettings::determineFileTypeFromFile(const std::string& path)
{ {
static const size_t kMaxReadSize = 0x4000; static const size_t kMaxReadSize = 0x5000;
FileType file_type = FILE_INVALID; FileType file_type = FILE_INVALID;
fnd::SimpleFile file; fnd::SimpleFile file;
fnd::Vec<byte_t> scratch; fnd::Vec<byte_t> scratch;
@ -840,12 +662,6 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
// test nca // test nca
else if (determineValidNcaFromSample(scratch)) else if (determineValidNcaFromSample(scratch))
file_type = FILE_NCA; file_type = FILE_NCA;
// test cnmt
else if (determineValidCnmtFromSample(scratch))
file_type = FILE_CNMT;
// test nacp
else if (determineValidNacpFromSample(scratch))
file_type = FILE_NACP;
// test nso // test nso
else if (_ASSERT_SIZE(sizeof(nn::hac::sNsoHeader)) && _TYPE_PTR(nn::hac::sNsoHeader)->st_magic.get() == nn::hac::nso::kNsoStructMagic) else if (_ASSERT_SIZE(sizeof(nn::hac::sNsoHeader)) && _TYPE_PTR(nn::hac::sNsoHeader)->st_magic.get() == nn::hac::nso::kNsoStructMagic)
file_type = FILE_NSO; file_type = FILE_NSO;
@ -861,6 +677,16 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
// test hb asset // test hb asset
else if (_ASSERT_SIZE(sizeof(nn::hac::sAssetHeader)) && _TYPE_PTR(nn::hac::sAssetHeader)->st_magic.get() == nn::hac::aset::kAssetStructMagic) else if (_ASSERT_SIZE(sizeof(nn::hac::sAssetHeader)) && _TYPE_PTR(nn::hac::sAssetHeader)->st_magic.get() == nn::hac::aset::kAssetStructMagic)
file_type = FILE_HB_ASSET; file_type = FILE_HB_ASSET;
// do heuristics
// test cnmt
else if (determineValidCnmtFromSample(scratch))
file_type = FILE_CNMT;
// test nacp
else if (determineValidNacpFromSample(scratch))
file_type = FILE_NACP;
// else unrecognised // else unrecognised
else else
file_type = FILE_INVALID; file_type = FILE_INVALID;
@ -880,7 +706,9 @@ bool UserSettings::determineValidNcaFromSample(const fnd::Vec<byte_t>& sample) c
if (sample.size() < nn::hac::nca::kHeaderSize) if (sample.size() < nn::hac::nca::kHeaderSize)
return false; return false;
nn::hac::NcaUtils::decryptNcaHeader(sample.data(), nca_raw, mKeyset.nca.header_key); fnd::aes::sAesXts128Key header_key;
mKeyCfg.getNcaHeaderKey(header_key);
nn::hac::NcaUtils::decryptNcaHeader(sample.data(), nca_raw, header_key);
if (nca_header->st_magic.get() != nn::hac::nca::kNca2StructMagic && nca_header->st_magic.get() != nn::hac::nca::kNca3StructMagic) if (nca_header->st_magic.get() != nn::hac::nca::kNca2StructMagic && nca_header->st_magic.get() != nn::hac::nca::kNca3StructMagic)
return false; return false;
@ -1019,3 +847,186 @@ nn::hac::npdm::InstructionType UserSettings::getInstructionTypeFromString(const
return type; return type;
} }
void UserSettings::getHomePath(std::string& path) const
{
// open other resource files in $HOME/.switch/prod.keys (or $HOME/.switch/dev.keys if -d/--dev is set).
path.clear();
if (path.empty()) fnd::io::getEnvironVar(path, "HOME");
if (path.empty()) fnd::io::getEnvironVar(path, "USERPROFILE");
if (path.empty()) return;
}
void UserSettings::getSwitchPath(std::string& path) const
{
std::string home;
home.clear();
getHomePath(home);
if (home.empty())
return;
path.clear();
fnd::io::appendToPath(path, home);
fnd::io::appendToPath(path, kHomeSwitchDirStr);
}
void UserSettings::dumpKeyConfig() const
{
fnd::aes::sAes128Key aes_key;
fnd::aes::sAesXts128Key aesxts_key;
fnd::rsa::sRsa2048Key rsa2048_key;
fnd::rsa::sRsa4096Key rsa4096_key;
const std::string kKeyIndex[kMasterKeyNum] = {"00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f"};
std::cout << "[KeyConfiguration]" << std::endl;
std::cout << " NCA Keys:" << std::endl;
if (mKeyCfg.getNcaHeader0SignKey(rsa2048_key) == true)
dumpRsa2048Key(rsa2048_key, "Header Signature[0] Key", 2);
if (mKeyCfg.getNcaHeaderKey(aesxts_key) == true)
dumpAesXtsKey(aesxts_key, "Header Encryption Key", 2);
for (size_t i = 0; i < kMasterKeyNum; i++)
{
if (mKeyCfg.getNcaKeyAreaEncryptionKey(i,0, aes_key) == true)
dumpAesKey(aes_key, "KeyAreaEncryptionKey-Application-" + kKeyIndex[i], 2);
if (mKeyCfg.getNcaKeyAreaEncryptionKey(i,1, aes_key) == true)
dumpAesKey(aes_key, "KeyAreaEncryptionKey-Ocean-" + kKeyIndex[i], 2);
if (mKeyCfg.getNcaKeyAreaEncryptionKey(i,2, aes_key) == true)
dumpAesKey(aes_key, "KeyAreaEncryptionKey-System-" + kKeyIndex[i], 2);
}
for (size_t i = 0; i < kMasterKeyNum; i++)
{
if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(i,0, aes_key) == true)
dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-Application-" + kKeyIndex[i], 2);
if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(i,1, aes_key) == true)
dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-Ocean-" + kKeyIndex[i], 2);
if (mKeyCfg.getNcaKeyAreaEncryptionKeyHw(i,2, aes_key) == true)
dumpAesKey(aes_key, "KeyAreaEncryptionKeyHw-System-" + kKeyIndex[i], 2);
}
std::cout << " XCI Keys:" << std::endl;
if (mKeyCfg.getXciHeaderSignKey(rsa2048_key) == true)
dumpRsa2048Key(rsa2048_key, "Header Signature Key", 2);
if (mKeyCfg.getXciHeaderKey(aes_key) == true)
dumpAesKey(aes_key, "Extended Header Encryption Key", 2);
if (mKeyCfg.getAcidSignKey(rsa2048_key) == true)
dumpRsa2048Key(rsa2048_key, "ACID Signer Key", 1);
std::cout << " Package1 Keys:" << std::endl;
for (size_t i = 0; i < kMasterKeyNum; i++)
{
if (mKeyCfg.getPkg1Key(i, aes_key) == true)
dumpAesKey(aes_key, "EncryptionKey-" + kKeyIndex[i], 2);
}
std::cout << " Package2 Keys:" << std::endl;
if (mKeyCfg.getPkg2SignKey(rsa2048_key) == true)
dumpRsa2048Key(rsa2048_key, "Signature Key", 2);
for (size_t i = 0; i < kMasterKeyNum; i++)
{
if (mKeyCfg.getPkg2Key(i, aes_key) == true)
dumpAesKey(aes_key, "EncryptionKey-" + kKeyIndex[i], 2);
}
std::cout << " ETicket Keys:" << std::endl;
for (size_t i = 0; i < kMasterKeyNum; i++)
{
if (mKeyCfg.getETicketCommonKey(i, aes_key) == true)
dumpAesKey(aes_key, "CommonKey-" + kKeyIndex[i], 2);
}
if (mKeyCfg.getPkiRootSignKey("Root", rsa4096_key) == true)
dumpRsa4096Key(rsa4096_key, "NNPKI Root Key", 1);
}
void UserSettings::dumpRsa2048Key(const fnd::rsa::sRsa2048Key& key, const std::string& name, size_t indent) const
{
std::string indent_str;
indent_str.clear();
for (size_t i = 0; i < indent; i++)
{
indent_str += " ";
}
std::cout << indent_str << name << ":" << std::endl;
if (key.modulus[0] != 0x00 && key.modulus[1] != 0x00)
{
std::cout << indent_str << " Modulus:" << std::endl;
for (size_t i = 0; i < 0x10; i++)
{
std::cout << indent_str << " " << fnd::SimpleTextOutput::arrayToString(key.modulus + i * 0x10, 0x10, true, ":") << std::endl;
}
}
if (key.priv_exponent[0] != 0x00 && key.priv_exponent[1] != 0x00)
{
std::cout << indent_str << " Private Exponent:" << std::endl;
for (size_t i = 0; i < 0x10; i++)
{
std::cout << indent_str << " " << fnd::SimpleTextOutput::arrayToString(key.priv_exponent + i * 0x10, 0x10, true, ":") << std::endl;
}
}
}
void UserSettings::dumpRsa4096Key(const fnd::rsa::sRsa4096Key& key, const std::string& name, size_t indent) const
{
std::string indent_str;
indent_str.clear();
for (size_t i = 0; i < indent; i++)
{
indent_str += " ";
}
std::cout << indent_str << name << ":" << std::endl;
if (key.modulus[0] != 0x00 && key.modulus[1] != 0x00)
{
std::cout << indent_str << " Modulus:" << std::endl;
for (size_t i = 0; i < 0x20; i++)
{
std::cout << indent_str << " " << fnd::SimpleTextOutput::arrayToString(key.modulus + i * 0x10, 0x10, true, ":") << std::endl;
}
}
if (key.priv_exponent[0] != 0x00 && key.priv_exponent[1] != 0x00)
{
std::cout << indent_str << " Private Exponent:" << std::endl;
for (size_t i = 0; i < 0x20; i++)
{
std::cout << indent_str << " " << fnd::SimpleTextOutput::arrayToString(key.priv_exponent + i * 0x10, 0x10, true, ":") << std::endl;
}
}
}
void UserSettings::dumpAesKey(const fnd::aes::sAes128Key& key, const std::string& name, size_t indent) const
{
std::string indent_str;
indent_str.clear();
for (size_t i = 0; i < indent; i++)
{
indent_str += " ";
}
std::cout << indent_str << name << ":" << std::endl;
std::cout << indent_str << " " << fnd::SimpleTextOutput::arrayToString(key.key, 0x10, true, ":") << std::endl;
}
void UserSettings::dumpAesXtsKey(const fnd::aes::sAesXts128Key& key, const std::string& name, size_t indent) const
{
std::string indent_str;
indent_str.clear();
for (size_t i = 0; i < indent; i++)
{
indent_str += " ";
}
std::cout << indent_str << name << ":" << std::endl;
std::cout << indent_str << " " << fnd::SimpleTextOutput::arrayToString(key.key[0], 0x20, true, ":") << std::endl;
}

View file

@ -7,7 +7,8 @@
#include <nn/pki/SignedData.h> #include <nn/pki/SignedData.h>
#include <nn/pki/CertificateBody.h> #include <nn/pki/CertificateBody.h>
#include <nn/hac/npdm.h> #include <nn/hac/npdm.h>
#include "nstool.h" #include "common.h"
#include "KeyConfiguration.h"
class UserSettings class UserSettings
{ {
@ -19,7 +20,7 @@ public:
// generic options // generic options
const std::string getInputPath() const; const std::string getInputPath() const;
const sKeyset& getKeyset() const; const KeyConfiguration& getKeyCfg() const;
FileType getFileType() const; FileType getFileType() const;
bool isVerifyFile() const; bool isVerifyFile() const;
CliOutputMode getCliOutputMode() const; CliOutputMode getCliOutputMode() const;
@ -47,6 +48,11 @@ public:
private: private:
const std::string kModuleName = "UserSettings"; const std::string kModuleName = "UserSettings";
const std::string kHomeSwitchDirStr = ".switch";
const std::string kGeneralKeyfileName[2] = { "prod.keys", "dev.keys" };
const std::string kTitleKeyfileName = "title.keys";
struct sCmdArgs struct sCmdArgs
{ {
sCmdArgs() {} sCmdArgs() {}
@ -81,7 +87,7 @@ private:
std::string mInputPath; std::string mInputPath;
FileType mFileType; FileType mFileType;
sKeyset mKeyset; KeyConfiguration mKeyCfg;
bool mVerifyFile; bool mVerifyFile;
CliOutputMode mOutputMode; CliOutputMode mOutputMode;
@ -109,7 +115,6 @@ private:
void populateCmdArgs(const std::vector<std::string>& arg_list, sCmdArgs& cmd_args); void populateCmdArgs(const std::vector<std::string>& arg_list, sCmdArgs& cmd_args);
void populateKeyset(sCmdArgs& args); void populateKeyset(sCmdArgs& args);
void populateUserSettings(sCmdArgs& args); void populateUserSettings(sCmdArgs& args);
void decodeHexStringToBytes(const std::string& name, const std::string& str, byte_t* out, size_t out_len);
FileType getFileTypeFromString(const std::string& type_str); FileType getFileTypeFromString(const std::string& type_str);
FileType determineFileTypeFromFile(const std::string& path); FileType determineFileTypeFromFile(const std::string& path);
bool determineValidNcaFromSample(const fnd::Vec<byte_t>& sample) const; bool determineValidNcaFromSample(const fnd::Vec<byte_t>& sample) const;
@ -118,4 +123,12 @@ private:
bool determineValidEsCertFromSample(const fnd::Vec<byte_t>& sample) const; bool determineValidEsCertFromSample(const fnd::Vec<byte_t>& sample) const;
bool determineValidEsTikFromSample(const fnd::Vec<byte_t>& sample) const; bool determineValidEsTikFromSample(const fnd::Vec<byte_t>& sample) const;
nn::hac::npdm::InstructionType getInstructionTypeFromString(const std::string& type_str); nn::hac::npdm::InstructionType getInstructionTypeFromString(const std::string& type_str);
void getHomePath(std::string& path) const;
void getSwitchPath(std::string& path) const;
void dumpKeyConfig() const;
void dumpRsa2048Key(const fnd::rsa::sRsa2048Key& key, const std::string& name, size_t indent) const;
void dumpRsa4096Key(const fnd::rsa::sRsa4096Key& key, const std::string& name, size_t indent) const;
void dumpAesKey(const fnd::aes::sAes128Key& key, const std::string& name, size_t indent) const;
void dumpAesXtsKey(const fnd::aes::sAesXts128Key& key, const std::string& name, size_t indent) const;
}; };

View file

@ -1,12 +1,12 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h> #include <fnd/SimpleTextOutput.h>
#include <nn/hac/XciUtils.h> #include <nn/hac/XciUtils.h>
#include "OffsetAdjustedIFile.h" #include "OffsetAdjustedIFile.h"
#include "XciProcess.h" #include "XciProcess.h"
XciProcess::XciProcess() : XciProcess::XciProcess() :
mFile(nullptr), mFile(),
mOwnIFile(false),
mKeyset(nullptr),
mCliOutputMode(_BIT(OUTPUT_BASIC)), mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false), mVerify(false),
mListFs(false), mListFs(false),
@ -15,38 +15,13 @@ XciProcess::XciProcess() :
{ {
} }
XciProcess::~XciProcess()
{
if (mOwnIFile)
{
delete mFile;
}
}
void XciProcess::process() void XciProcess::process()
{ {
fnd::Vec<byte_t> scratch; importHeader();
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
// read header page
mFile->read((byte_t*)&mHdrPage, 0, sizeof(nn::hac::sXciHeaderPage));
// allocate memory for and decrypt sXciHeader
scratch.alloc(sizeof(nn::hac::sXciHeader));
nn::hac::XciUtils::decryptXciHeader((const byte_t*)&mHdrPage.header, scratch.data(), mKeyset->xci.header_key.key);
// validate header signature // validate header signature
if (mVerify) if (mVerify)
{
validateXciSignature(); validateXciSignature();
}
// deserialise header
mHdr.fromBytes(scratch.data(), scratch.size());
// display header // display header
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
@ -59,15 +34,14 @@ void XciProcess::process()
processPartitionPfs(); processPartitionPfs();
} }
void XciProcess::setInputFile(fnd::IFile* file, bool ownIFile) void XciProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
{ {
mFile = file; mFile = file;
mOwnIFile = ownIFile;
} }
void XciProcess::setKeyset(const sKeyset* keyset) void XciProcess::setKeyCfg(const KeyConfiguration& keycfg)
{ {
mKeyset = keyset; mKeyCfg = keycfg;
} }
void XciProcess::setCliOutputMode(CliOutputMode type) void XciProcess::setCliOutputMode(CliOutputMode type)
@ -90,93 +64,117 @@ void XciProcess::setListFs(bool list_fs)
mListFs = list_fs; mListFs = list_fs;
} }
void XciProcess::importHeader()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
// read header page
(*mFile)->read((byte_t*)&mHdrPage, 0, sizeof(nn::hac::sXciHeaderPage));
// allocate memory for and decrypt sXciHeader
scratch.alloc(sizeof(nn::hac::sXciHeader));
fnd::aes::sAes128Key header_key;
mKeyCfg.getXciHeaderKey(header_key);
nn::hac::XciUtils::decryptXciHeader((const byte_t*)&mHdrPage.header, scratch.data(), header_key.key);
// deserialise header
mHdr.fromBytes(scratch.data(), scratch.size());
}
void XciProcess::displayHeader() void XciProcess::displayHeader()
{ {
printf("[XCI Header]\n"); std::cout << "[XCI Header]" << std::endl;
printf(" CardHeaderVersion: %d\n", mHdr.getCardHeaderVersion()); std::cout << " CardHeaderVersion: " << std::dec << (uint32_t)mHdr.getCardHeaderVersion() << std::endl;
printf(" RomSize: %s", getRomSizeStr(mHdr.getRomSizeType())); std::cout << " RomSize: " << getRomSizeStr(mHdr.getRomSizeType());
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
printf(" (0x%x)", mHdr.getRomSizeType()); std::cout << " (0x" << std::hex << (uint32_t)mHdr.getRomSizeType() << ")";
printf("\n"); std::cout << std::endl;
printf(" PackageId: 0x%" PRIx64 "\n", mHdr.getPackageId()); std::cout << " PackageId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getPackageId() << std::endl;
printf(" Flags: 0x%x\n", mHdr.getFlags()); std::cout << " Flags: 0x" << std::dec << (uint32_t)mHdr.getFlags() << std::endl;
if (mHdr.getFlags() != 0) if (mHdr.getFlags() != 0)
{ {
for (uint32_t i = 0; i < 8; i++) for (uint32_t i = 0; i < 8; i++)
{ {
if (_HAS_BIT(mHdr.getFlags(), i)) if (_HAS_BIT(mHdr.getFlags(), i))
{ {
printf(" %s\n", getHeaderFlagStr(i)); std::cout << " " << getHeaderFlagStr(i) << std::endl;
} }
} }
} }
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{ {
printf(" InitialData:\n"); std::cout << " InitialData:" << std::endl;
printf(" KekIndex: %d\n", mHdr.getKekIndex()); std::cout << " KekIndex: " << std::dec << (uint32_t)mHdr.getKekIndex() << std::endl;
printf(" TitleKeyDecIndex: %d\n", mHdr.getTitleKeyDecIndex()); std::cout << " TitleKeyDecIndex: " << std::dec << (uint32_t)mHdr.getTitleKeyDecIndex() << std::endl;
printf(" Hash:\n"); std::cout << " Hash:" << std::endl;
fnd::SimpleTextOutput::hexDump(mHdr.getInitialDataHash().bytes, sizeof(mHdr.getInitialDataHash().bytes), 0x10, 6); std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getInitialDataHash().bytes, 0x10, true, ":") << std::endl;
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getInitialDataHash().bytes+0x10, 0x10, true, ":") << std::endl;
} }
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{ {
printf(" Enc Header AES-IV:\n"); std::cout << " Extended Header AesCbc IV:" << std::endl;
fnd::SimpleTextOutput::hexDump(mHdr.getAesCbcIv().iv, sizeof(mHdr.getAesCbcIv().iv), 0x10, 4); std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getAesCbcIv().iv, sizeof(mHdr.getAesCbcIv().iv), true, ":") << std::endl;
} }
printf(" SelSec: 0x%x\n", mHdr.getSelSec()); std::cout << " SelSec: 0x" << std::hex << mHdr.getSelSec() << std::endl;
printf(" SelT1Key: 0x%x\n", mHdr.getSelT1Key()); std::cout << " SelT1Key: 0x" << std::hex << mHdr.getSelT1Key() << std::endl;
printf(" SelKey: 0x%x\n", mHdr.getSelKey()); std::cout << " SelKey: 0x" << std::hex << mHdr.getSelKey() << std::endl;
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT)) if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
{ {
printf(" RomAreaStartPage: 0x%0x", mHdr.getRomAreaStartPage()); std::cout << " RomAreaStartPage: 0x" << std::hex << mHdr.getRomAreaStartPage();
if (mHdr.getRomAreaStartPage() != (uint32_t)(-1)) if (mHdr.getRomAreaStartPage() != (uint32_t)(-1))
printf(" (0x%" PRIx64 ")", nn::hac::XciUtils::blockToAddr(mHdr.getRomAreaStartPage())); std::cout << " (0x" << std::hex << nn::hac::XciUtils::blockToAddr(mHdr.getRomAreaStartPage()) << ")";
printf("\n"); std::cout << std::endl;
printf(" BackupAreaStartPage: 0x%0x", mHdr.getBackupAreaStartPage()); std::cout << " BackupAreaStartPage: 0x" << std::hex << mHdr.getBackupAreaStartPage();
if (mHdr.getBackupAreaStartPage() != (uint32_t)(-1)) if (mHdr.getBackupAreaStartPage() != (uint32_t)(-1))
printf(" (0x%" PRIx64 ")", nn::hac::XciUtils::blockToAddr(mHdr.getBackupAreaStartPage())); std::cout << " (0x" << std::hex << nn::hac::XciUtils::blockToAddr(mHdr.getBackupAreaStartPage()) << ")";
printf("\n"); std::cout << std::endl;
printf(" ValidDataEndPage: 0x%x", mHdr.getValidDataEndPage()); std::cout << " ValidDataEndPage: 0x" << std::hex << mHdr.getValidDataEndPage();
if (mHdr.getValidDataEndPage() != (uint32_t)(-1)) if (mHdr.getValidDataEndPage() != (uint32_t)(-1))
printf(" (0x%" PRIx64 ")", nn::hac::XciUtils::blockToAddr(mHdr.getValidDataEndPage())); std::cout << " (0x" << std::hex << nn::hac::XciUtils::blockToAddr(mHdr.getValidDataEndPage()) << ")";
printf("\n"); std::cout << std::endl;
printf(" LimArea: 0x%x", mHdr.getLimAreaPage()); std::cout << " LimArea: 0x" << std::hex << mHdr.getLimAreaPage();
if (mHdr.getLimAreaPage() != (uint32_t)(-1)) if (mHdr.getLimAreaPage() != (uint32_t)(-1))
printf(" (0x%" PRIx64 ")", nn::hac::XciUtils::blockToAddr(mHdr.getLimAreaPage())); std::cout << " (0x" << std::hex << nn::hac::XciUtils::blockToAddr(mHdr.getLimAreaPage()) << ")";
printf("\n"); std::cout << std::endl;
printf(" PartitionFs Header:\n"); std::cout << " PartitionFs Header:" << std::endl;
printf(" Offset: 0x%" PRIx64 "\n", mHdr.getPartitionFsAddress()); std::cout << " Offset: 0x" << std::hex << mHdr.getPartitionFsAddress() << std::endl;
printf(" Size: 0x%" PRIx64 "\n", mHdr.getPartitionFsSize()); std::cout << " Size: 0x" << std::hex << mHdr.getPartitionFsSize() << std::endl;
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
{ {
printf(" Hash:\n"); std::cout << " Hash:" << std::endl;
fnd::SimpleTextOutput::hexDump(mHdr.getPartitionFsHash().bytes, sizeof(mHdr.getPartitionFsHash().bytes), 0x10, 6); std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getPartitionFsHash().bytes, 0x10, true, ":") << std::endl;
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getPartitionFsHash().bytes+0x10, 0x10, true, ":") << std::endl;
} }
} }
if (mHdr.getFwVerMinor() != 0) if (mHdr.getFwVerMinor() != 0)
{ {
printf("[XCI Extended Header]\n"); std::cout << "[XCI Extended Header]" << std::endl;
printf(" FwVersion: v%d.%d\n", mHdr.getFwVerMajor(), mHdr.getFwVerMinor()); std::cout << " FwVersion: v" << std::dec << mHdr.getFwVerMajor() << "." << mHdr.getFwVerMinor() << std::endl;
printf(" AccCtrl1: 0x%x\n", mHdr.getAccCtrl1()); std::cout << " AccCtrl1: 0x" << std::hex << mHdr.getAccCtrl1() << std::endl;
printf(" CardClockRate: %s\n", getCardClockRate(mHdr.getAccCtrl1())); std::cout << " CardClockRate: " << getCardClockRate(mHdr.getAccCtrl1()) << std::endl;
printf(" Wait1TimeRead: 0x%x\n", mHdr.getWait1TimeRead()); std::cout << " Wait1TimeRead: 0x" << std::hex << mHdr.getWait1TimeRead() << std::endl;
printf(" Wait2TimeRead: 0x%x\n", mHdr.getWait2TimeRead()); std::cout << " Wait2TimeRead: 0x" << std::hex << mHdr.getWait2TimeRead() << std::endl;
printf(" Wait1TimeWrite: 0x%x\n", mHdr.getWait1TimeWrite()); std::cout << " Wait1TimeWrite: 0x" << std::hex << mHdr.getWait1TimeWrite() << std::endl;
printf(" Wait2TimeWrite: 0x%x\n", mHdr.getWait2TimeWrite()); std::cout << " Wait2TimeWrite: 0x" << std::hex << mHdr.getWait2TimeWrite() << std::endl;
printf(" FwMode: 0x%x\n", mHdr.getFwMode()); std::cout << " FwMode: 0x" << std::hex << mHdr.getFwMode() << std::endl;
printf(" Update Partition Info:\n"); std::cout << " Update Partition Info:" << std::endl;
#define _SPLIT_VER(ver) ( (ver>>26) & 0x3f), ( (ver>>20) & 0x3f), ( (ver>>16) & 0xf), (ver & 0xffff) #define _SPLIT_VER(ver) std::dec << ((ver>>26) & 0x3f) << "." << ((ver>>20) & 0x3f) << "." << ((ver>>16) & 0xf) << "." << (ver & 0xffff)
printf(" CUP Version: v%" PRId32 " (%d.%d.%d.%d)\n", mHdr.getUppVersion(), _SPLIT_VER(mHdr.getUppVersion())); std::cout << " CUP Version: v" << std::dec << mHdr.getUppVersion() << " (" << _SPLIT_VER(mHdr.getUppVersion()) << ")" << std::endl;
#undef _SPLIT_VER #undef _SPLIT_VER
printf(" CUP TitleId: %016" PRIx64 "\n", mHdr.getUppId()); std::cout << " CUP TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getUppId() << std::endl;
printf(" Partition Hash: "); std::cout << " Partition Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getUppHash(), 8, true, ":") << std::endl;
fnd::SimpleTextOutput::hexDump(mHdr.getUppHash(), 8);
} }
} }
@ -185,18 +183,20 @@ bool XciProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* t
fnd::Vec<byte_t> scratch; fnd::Vec<byte_t> scratch;
fnd::sha::sSha256Hash calc_hash; fnd::sha::sSha256Hash calc_hash;
scratch.alloc(len); scratch.alloc(len);
mFile->read(scratch.data(), offset, scratch.size()); (*mFile)->read(scratch.data(), offset, scratch.size());
fnd::sha::Sha256(scratch.data(), scratch.size(), calc_hash.bytes); fnd::sha::Sha256(scratch.data(), scratch.size(), calc_hash.bytes);
return calc_hash.compare(test_hash); return calc_hash.compare(test_hash);
} }
void XciProcess::validateXciSignature() void XciProcess::validateXciSignature()
{ {
fnd::rsa::sRsa2048Key header_sign_key;
fnd::sha::sSha256Hash calc_hash; fnd::sha::sSha256Hash calc_hash;
fnd::sha::Sha256((byte_t*)&mHdrPage.header, sizeof(nn::hac::sXciHeader), calc_hash.bytes); fnd::sha::Sha256((byte_t*)&mHdrPage.header, sizeof(nn::hac::sXciHeader), calc_hash.bytes);
if (fnd::rsa::pkcs::rsaVerify(mKeyset->xci.header_sign_key, fnd::sha::HASH_SHA256, calc_hash.bytes, mHdrPage.signature) != 0) mKeyCfg.getXciHeaderSignKey(header_sign_key);
if (fnd::rsa::pkcs::rsaVerify(header_sign_key, fnd::sha::HASH_SHA256, calc_hash.bytes, mHdrPage.signature) != 0)
{ {
printf("[WARNING] XCI Header Signature: FAIL \n"); std::cout << "[WARNING] XCI Header Signature: FAIL" << std::endl;
} }
} }
@ -204,9 +204,9 @@ void XciProcess::processRootPfs()
{ {
if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize(), mHdr.getPartitionFsHash().bytes) == false) if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize(), mHdr.getPartitionFsHash().bytes) == false)
{ {
printf("[WARNING] XCI Root HFS0: FAIL (bad hash)\n"); std::cout << "[WARNING] XCI Root HFS0: FAIL (bad hash)" << std::endl;
} }
mRootPfs.setInputFile(new OffsetAdjustedIFile(mFile, SHARED_IFILE, mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize()), OWN_IFILE); mRootPfs.setInputFile(new OffsetAdjustedIFile(mFile, mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize()));
mRootPfs.setListFs(mListFs); mRootPfs.setListFs(mListFs);
mRootPfs.setVerifyMode(false); mRootPfs.setVerifyMode(false);
mRootPfs.setCliOutputMode(mCliOutputMode); mRootPfs.setCliOutputMode(mCliOutputMode);
@ -222,11 +222,11 @@ void XciProcess::processPartitionPfs()
// this must be validated here because only the size of the root partiton header is known at verification time // this must be validated here because only the size of the root partiton header is known at verification time
if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress() + rootPartitions[i].offset, rootPartitions[i].hash_protected_size, rootPartitions[i].hash.bytes) == false) if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress() + rootPartitions[i].offset, rootPartitions[i].hash_protected_size, rootPartitions[i].hash.bytes) == false)
{ {
printf("[WARNING] XCI %s Partition HFS0: FAIL (bad hash)\n", rootPartitions[i].name.c_str()); std::cout << "[WARNING] XCI " << rootPartitions[i].name << " Partition HFS0: FAIL (bad hash)" << std::endl;
} }
PfsProcess tmp; PfsProcess tmp;
tmp.setInputFile(new OffsetAdjustedIFile(mFile, SHARED_IFILE, mHdr.getPartitionFsAddress() + rootPartitions[i].offset, rootPartitions[i].size), OWN_IFILE); tmp.setInputFile(new OffsetAdjustedIFile(mFile, mHdr.getPartitionFsAddress() + rootPartitions[i].offset, rootPartitions[i].size));
tmp.setListFs(mListFs); tmp.setListFs(mListFs);
tmp.setVerifyMode(mVerify); tmp.setVerifyMode(mVerify);
tmp.setCliOutputMode(mCliOutputMode); tmp.setCliOutputMode(mCliOutputMode);
@ -240,7 +240,8 @@ void XciProcess::processPartitionPfs()
const char* XciProcess::getRomSizeStr(byte_t rom_size) const const char* XciProcess::getRomSizeStr(byte_t rom_size) const
{ {
const char* str = "unknown"; const char* str = nullptr;
switch (rom_size) switch (rom_size)
{ {
case (nn::hac::xci::ROM_SIZE_1GB): case (nn::hac::xci::ROM_SIZE_1GB):
@ -261,13 +262,18 @@ const char* XciProcess::getRomSizeStr(byte_t rom_size) const
case (nn::hac::xci::ROM_SIZE_32GB): case (nn::hac::xci::ROM_SIZE_32GB):
str = "32GB"; str = "32GB";
break; break;
default:
str = "Unknown";
break;
} }
return str; return str;
} }
const char* XciProcess::getHeaderFlagStr(byte_t flag) const const char* XciProcess::getHeaderFlagStr(byte_t flag) const
{ {
const char* str = "unknown"; const char* str = nullptr;
switch (flag) switch (flag)
{ {
case (nn::hac::xci::FLAG_AUTOBOOT): case (nn::hac::xci::FLAG_AUTOBOOT):
@ -279,14 +285,19 @@ const char* XciProcess::getHeaderFlagStr(byte_t flag) const
case (nn::hac::xci::FLAG_REPAIR_TOOL): case (nn::hac::xci::FLAG_REPAIR_TOOL):
str = "RepairTool"; str = "RepairTool";
break; break;
default:
str = "Unknown";
break;
} }
return str; return str;
} }
const char* XciProcess::getCardClockRate(uint32_t acc_ctrl_1) const const char* XciProcess::getCardClockRate(uint32_t acc_ctrl_1) const
{ {
const char* str = "unknown"; const char* str = nullptr;
switch (acc_ctrl_1) switch (acc_ctrl_1)
{ {
case (nn::hac::xci::CLOCK_RATE_25): case (nn::hac::xci::CLOCK_RATE_25):
@ -295,7 +306,10 @@ const char* XciProcess::getCardClockRate(uint32_t acc_ctrl_1) const
case (nn::hac::xci::CLOCK_RATE_50): case (nn::hac::xci::CLOCK_RATE_50):
str = "50 MHz"; str = "50 MHz";
break; break;
default:
str = "Unknown";
break;
} }
return str; return str;
} }

View file

@ -2,25 +2,24 @@
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/List.h> #include <fnd/List.h>
#include <nn/hac/XciHeader.h> #include <nn/hac/XciHeader.h>
#include "KeyConfiguration.h"
#include "nstool.h"
#include "PfsProcess.h" #include "PfsProcess.h"
#include "common.h"
class XciProcess class XciProcess
{ {
public: public:
XciProcess(); XciProcess();
~XciProcess();
void process(); void process();
// generic // generic
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setKeyset(const sKeyset* keyset); void setKeyCfg(const KeyConfiguration& keycfg);
void setCliOutputMode(CliOutputMode type); void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -32,9 +31,8 @@ private:
const std::string kModuleName = "XciProcess"; const std::string kModuleName = "XciProcess";
const std::string kXciMountPointName = "gamecard:/"; const std::string kXciMountPointName = "gamecard:/";
fnd::IFile* mFile; fnd::SharedPtr<fnd::IFile> mFile;
bool mOwnIFile; KeyConfiguration mKeyCfg;
const sKeyset* mKeyset;
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;
@ -62,6 +60,7 @@ private:
PfsProcess mRootPfs; PfsProcess mRootPfs;
fnd::List<sExtractInfo> mExtractInfo; fnd::List<sExtractInfo> mExtractInfo;
void importHeader();
void displayHeader(); void displayHeader();
bool validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash); bool validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash);
void validateXciSignature(); void validateXciSignature();

View file

@ -0,0 +1,60 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/aes.h>
#include <fnd/rsa.h>
#include <nn/hac/nca.h>
static const size_t kMasterKeyNum = 0x20;
static const size_t kNcaKeakNum = nn::hac::nca::kKeyAreaEncryptionKeyNum;
enum FileType
{
FILE_XCI,
FILE_NSP,
FILE_PARTITIONFS,
FILE_ROMFS,
FILE_NCA,
FILE_NPDM,
FILE_CNMT,
FILE_NSO,
FILE_NRO,
FILE_NACP,
FILE_PKI_CERT,
FILE_ES_TIK,
FILE_HB_ASSET,
FILE_INVALID = -1,
};
enum CliOutputModeFlag
{
OUTPUT_BASIC,
OUTPUT_LAYOUT,
OUTPUT_KEY_DATA,
OUTPUT_EXTENDED
};
typedef byte_t CliOutputMode;
template <typename T>
struct sOptional
{
bool isSet;
T var;
inline sOptional() : isSet(false) {}
inline sOptional(const T& other) : isSet(true), var(other) {}
inline sOptional(const sOptional& other) : isSet(other.isSet), var(other.var) {}
inline const T& operator=(const T& other) { isSet = true; var = other; return var; }
inline const sOptional<T>& operator=(const sOptional<T>& other)
{
isSet = other.isSet;
if (isSet) {
var = other.var;
}
return *this;
}
inline T& operator*() { return var; }
};
const byte_t kDummyRightsIdForUserTitleKey[nn::hac::nca::kRightsIdLen] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
const byte_t kDummyRightsIdForUserBodyKey[nn::hac::nca::kRightsIdLen] = {0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};

View file

@ -1,5 +1,6 @@
#include <cstdio> #include <cstdio>
#include <fnd/SimpleFile.h> #include <fnd/SimpleFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/StringConv.h> #include <fnd/StringConv.h>
#include "UserSettings.h" #include "UserSettings.h"
#include "XciProcess.h" #include "XciProcess.h"
@ -35,13 +36,15 @@ int main(int argc, char** argv)
try { try {
user_set.parseCmdArgs(args); user_set.parseCmdArgs(args);
fnd::SharedPtr<fnd::IFile> inputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read));
if (user_set.getFileType() == FILE_XCI) if (user_set.getFileType() == FILE_XCI)
{ {
XciProcess xci; XciProcess xci;
xci.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); xci.setInputFile(inputFile);
xci.setKeyset(&user_set.getKeyset()); xci.setKeyCfg(user_set.getKeyCfg());
xci.setCliOutputMode(user_set.getCliOutputMode()); xci.setCliOutputMode(user_set.getCliOutputMode());
xci.setVerifyMode(user_set.isVerifyFile()); xci.setVerifyMode(user_set.isVerifyFile());
@ -61,7 +64,7 @@ int main(int argc, char** argv)
{ {
PfsProcess pfs; PfsProcess pfs;
pfs.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); pfs.setInputFile(inputFile);
pfs.setCliOutputMode(user_set.getCliOutputMode()); pfs.setCliOutputMode(user_set.getCliOutputMode());
pfs.setVerifyMode(user_set.isVerifyFile()); pfs.setVerifyMode(user_set.isVerifyFile());
@ -75,7 +78,7 @@ int main(int argc, char** argv)
{ {
RomfsProcess romfs; RomfsProcess romfs;
romfs.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); romfs.setInputFile(inputFile);
romfs.setCliOutputMode(user_set.getCliOutputMode()); romfs.setCliOutputMode(user_set.getCliOutputMode());
romfs.setVerifyMode(user_set.isVerifyFile()); romfs.setVerifyMode(user_set.isVerifyFile());
@ -89,8 +92,8 @@ int main(int argc, char** argv)
{ {
NcaProcess nca; NcaProcess nca;
nca.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); nca.setInputFile(inputFile);
nca.setKeyset(&user_set.getKeyset()); nca.setKeyCfg(user_set.getKeyCfg());
nca.setCliOutputMode(user_set.getCliOutputMode()); nca.setCliOutputMode(user_set.getCliOutputMode());
nca.setVerifyMode(user_set.isVerifyFile()); nca.setVerifyMode(user_set.isVerifyFile());
@ -111,8 +114,8 @@ int main(int argc, char** argv)
{ {
NpdmProcess npdm; NpdmProcess npdm;
npdm.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); npdm.setInputFile(inputFile);
npdm.setKeyset(&user_set.getKeyset()); npdm.setKeyCfg(user_set.getKeyCfg());
npdm.setCliOutputMode(user_set.getCliOutputMode()); npdm.setCliOutputMode(user_set.getCliOutputMode());
npdm.setVerifyMode(user_set.isVerifyFile()); npdm.setVerifyMode(user_set.isVerifyFile());
@ -122,7 +125,7 @@ int main(int argc, char** argv)
{ {
CnmtProcess cnmt; CnmtProcess cnmt;
cnmt.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); cnmt.setInputFile(inputFile);
cnmt.setCliOutputMode(user_set.getCliOutputMode()); cnmt.setCliOutputMode(user_set.getCliOutputMode());
cnmt.setVerifyMode(user_set.isVerifyFile()); cnmt.setVerifyMode(user_set.isVerifyFile());
@ -132,7 +135,7 @@ int main(int argc, char** argv)
{ {
NsoProcess obj; NsoProcess obj;
obj.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); obj.setInputFile(inputFile);
obj.setCliOutputMode(user_set.getCliOutputMode()); obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile()); obj.setVerifyMode(user_set.isVerifyFile());
@ -146,7 +149,7 @@ int main(int argc, char** argv)
{ {
NroProcess obj; NroProcess obj;
obj.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); obj.setInputFile(inputFile);
obj.setCliOutputMode(user_set.getCliOutputMode()); obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile()); obj.setVerifyMode(user_set.isVerifyFile());
@ -169,7 +172,7 @@ int main(int argc, char** argv)
{ {
NacpProcess nacp; NacpProcess nacp;
nacp.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); nacp.setInputFile(inputFile);
nacp.setCliOutputMode(user_set.getCliOutputMode()); nacp.setCliOutputMode(user_set.getCliOutputMode());
nacp.setVerifyMode(user_set.isVerifyFile()); nacp.setVerifyMode(user_set.isVerifyFile());
@ -179,8 +182,8 @@ int main(int argc, char** argv)
{ {
PkiCertProcess cert; PkiCertProcess cert;
cert.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); cert.setInputFile(inputFile);
cert.setKeyset(&user_set.getKeyset()); cert.setKeyCfg(user_set.getKeyCfg());
cert.setCliOutputMode(user_set.getCliOutputMode()); cert.setCliOutputMode(user_set.getCliOutputMode());
cert.setVerifyMode(user_set.isVerifyFile()); cert.setVerifyMode(user_set.isVerifyFile());
@ -190,8 +193,8 @@ int main(int argc, char** argv)
{ {
EsTikProcess tik; EsTikProcess tik;
tik.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); tik.setInputFile(inputFile);
tik.setKeyset(&user_set.getKeyset()); tik.setKeyCfg(user_set.getKeyCfg());
tik.setCertificateChain(user_set.getCertificateChain()); tik.setCertificateChain(user_set.getCertificateChain());
tik.setCliOutputMode(user_set.getCliOutputMode()); tik.setCliOutputMode(user_set.getCliOutputMode());
tik.setVerifyMode(user_set.isVerifyFile()); tik.setVerifyMode(user_set.isVerifyFile());
@ -202,7 +205,7 @@ int main(int argc, char** argv)
{ {
AssetProcess obj; AssetProcess obj;
obj.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); obj.setInputFile(inputFile);
obj.setCliOutputMode(user_set.getCliOutputMode()); obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile()); obj.setVerifyMode(user_set.isVerifyFile());

View file

@ -1,110 +0,0 @@
#pragma once
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/aes.h>
#include <fnd/rsa.h>
#include <nn/hac/nca.h>
static const size_t kMasterKeyNum = 0x20;
static const size_t kNcaKeakNum = nn::hac::nca::kKeyAreaEncryptionKeyNum;
enum IFileOwnershipMode
{
SHARED_IFILE = false,
OWN_IFILE = true
};
enum FileType
{
FILE_XCI,
FILE_NSP,
FILE_PARTITIONFS,
FILE_ROMFS,
FILE_NCA,
FILE_NPDM,
FILE_CNMT,
FILE_NSO,
FILE_NRO,
FILE_NACP,
FILE_PKI_CERT,
FILE_ES_TIK,
FILE_HB_ASSET,
FILE_INVALID = -1,
};
enum CliOutputModeFlag
{
OUTPUT_BASIC,
OUTPUT_LAYOUT,
OUTPUT_KEY_DATA,
OUTPUT_EXTENDED
};
typedef byte_t CliOutputMode;
template <typename T>
struct sOptional
{
bool isSet;
T var;
inline sOptional() : isSet(false) {}
inline const T& operator=(const T& other) { isSet = true; var = other; return var; }
inline const sOptional<T>& operator=(const sOptional<T>& other)
{
isSet = other.isSet;
if (isSet) {
var = other.var;
}
return *this;
}
inline T& operator*() { return var; }
};
struct sKeyset
{
fnd::rsa::sRsa2048Key acid_sign_key;
fnd::aes::sAes128Key package1_key[kMasterKeyNum];
fnd::rsa::sRsa2048Key package2_sign_key;
fnd::aes::sAes128Key package2_key[kMasterKeyNum];
struct sNcaData
{
fnd::rsa::sRsa2048Key header_sign_key;
fnd::aes::sAesXts128Key header_key;
fnd::aes::sAes128Key key_area_key[kNcaKeakNum][kMasterKeyNum];
fnd::aes::sAes128Key manual_title_key_aesctr;
fnd::aes::sAesXts128Key manual_title_key_aesxts;
fnd::aes::sAes128Key manual_body_key_aesctr;
fnd::aes::sAesXts128Key manual_body_key_aesxts;
} nca;
struct sXciData
{
fnd::rsa::sRsa2048Key header_sign_key;
fnd::aes::sAes128Key header_key;
} xci;
struct sTicketData
{
fnd::rsa::sRsa2048Key sign_key;
fnd::aes::sAes128Key titlekey_kek[kMasterKeyNum];
} ticket;
struct sPkiData
{
fnd::rsa::sRsa4096Key root_sign_key;
} pki;
};
inline byte_t charToByte(char chr)
{
if (chr >= 'a' && chr <= 'f')
return (chr - 'a') + 0xa;
else if (chr >= 'A' && chr <= 'F')
return (chr - 'A') + 0xa;
else if (chr >= '0' && chr <= '9')
return chr - '0';
return 0;
}

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#define APP_NAME "NSTool"
#define BIN_NAME "nstool"
#define VER_MAJOR 1 #define VER_MAJOR 1
#define VER_MINOR 0 #define VER_MINOR 0
#define VER_PATCH 0 #define VER_PATCH 4
#define AUTHORS "jakcron" #define AUTHORS "jakcron"