diff --git a/lib/libnx/include/nx/AccessControlInfoBinary.h b/lib/libnx/include/nx/AccessControlInfoBinary.h new file mode 100644 index 0000000..3eedace --- /dev/null +++ b/lib/libnx/include/nx/AccessControlInfoBinary.h @@ -0,0 +1,53 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace nx +{ + class AccessControlInfoBinary : public fnd::ISerialisable + { + public: + AccessControlInfoBinary(); + AccessControlInfoBinary(const AccessControlInfoBinary& other); + + void operator=(const AccessControlInfoBinary& other); + bool operator==(const AccessControlInfoBinary& other) const; + bool operator!=(const AccessControlInfoBinary& other) const; + + // export/import binary + void toBytes(); + void fromBytes(const byte_t* data, size_t len); + const fnd::Vec& getBytes() const; + + // variables + void clear(); + + uint64_t getProgramId() const; + void setProgramId(uint64_t program_id); + + const nx::FacBinary& getFileSystemAccessControl() const; + void setFileSystemAccessControl(const FacBinary& fac); + + const nx::SacBinary& getServiceAccessControl() const; + void setServiceAccessControl(const SacBinary& sac); + + const nx::KcBinary& getKernelCapabilities() const; + void setKernelCapabilities(const KcBinary& kc); + private: + const std::string kModuleName = "ACCESS_CONTROL_INFO_BINARY"; + + // raw data + fnd::Vec mRawBinary; + + // variables + uint64_t mProgramId; + nx::FacBinary mFileSystemAccessControl; + nx::SacBinary mServiceAccessControl; + nx::KcBinary mKernelCapabilities; + }; +} \ No newline at end of file diff --git a/lib/libnx/include/nx/AccessControlInfoDescBinary.h b/lib/libnx/include/nx/AccessControlInfoDescBinary.h new file mode 100644 index 0000000..362bb22 --- /dev/null +++ b/lib/libnx/include/nx/AccessControlInfoDescBinary.h @@ -0,0 +1,88 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nx +{ + class AccessControlInfoDescBinary : public fnd::ISerialisable + { + public: + struct sProgramIdRestrict + { + uint64_t min; + uint64_t max; + + void operator=(const sProgramIdRestrict& other) + { + min = other.min; + max = other.max; + } + + bool operator==(const sProgramIdRestrict& other) const + { + return (min == other.min) \ + && (max == other.max); + } + + bool operator!=(const sProgramIdRestrict& other) const + { + return !(*this == other); + } + }; + + AccessControlInfoDescBinary(); + AccessControlInfoDescBinary(const AccessControlInfoDescBinary& other); + + void operator=(const AccessControlInfoDescBinary& other); + bool operator==(const AccessControlInfoDescBinary& other) const; + bool operator!=(const AccessControlInfoDescBinary& other) const; + + // export/import binary + void toBytes(); + void fromBytes(const byte_t* data, size_t len); + const fnd::Vec& getBytes() const; + + void generateSignature(const crypto::rsa::sRsa2048Key& key); + void validateSignature(const crypto::rsa::sRsa2048Key& key) const; + + // variables + void clear(); + + const crypto::rsa::sRsa2048Key& getNcaHeaderSignature2Key() const; + void setNcaHeaderSignature2Key(const crypto::rsa::sRsa2048Key& key); + + const fnd::List& getFlagList() const; + void setFlagList(const fnd::List& flags); + + const sProgramIdRestrict& getProgramIdRestrict() const; + void setProgramIdRestrict(const sProgramIdRestrict& pid_restrict); + + const FacBinary& getFileSystemAccessControl() const; + void setFileSystemAccessControl(const FacBinary& fac); + + const SacBinary& getServiceAccessControl() const; + void setServiceAccessControl(const SacBinary& sac); + + const KcBinary& getKernelCapabilities() const; + void setKernelCapabilities(const KcBinary& kc); + private: + const std::string kModuleName = "ACCESS_CONTROL_INFO_DESC_BINARY"; + + // raw data + fnd::Vec mRawBinary; + + // variables + crypto::rsa::sRsa2048Key mNcaHeaderSignature2Key; + fnd::List mFlags; + sProgramIdRestrict mProgramIdRestrict; + nx::FacBinary mFileSystemAccessControl; + nx::SacBinary mServiceAccessControl; + nx::KcBinary mKernelCapabilities; + }; +} \ No newline at end of file diff --git a/lib/libnx/include/nx/AciBinary.h b/lib/libnx/include/nx/AciBinary.h deleted file mode 100644 index b8d43b4..0000000 --- a/lib/libnx/include/nx/AciBinary.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -namespace nx -{ - class AciBinary : - public AciHeader - { - public: - AciBinary(); - AciBinary(const AciBinary& other); - - void operator=(const AciBinary& other); - bool operator==(const AciBinary& other) const; - bool operator!=(const AciBinary& other) const; - - // export/import binary - virtual void toBytes(); - virtual void fromBytes(const byte_t* bytes, size_t len); - const fnd::Vec& getBytes() const; - - // variables - virtual void clear(); - - const FacBinary& getFac() const; - void setFac(const FacBinary& fac); - - const SacBinary& getSac() const; - void setSac(const SacBinary& sac); - - const KcBinary& getKc() const; - void setKc(const KcBinary& kc); - - private: - const std::string kModuleName = "ACI_BINARY"; - - // raw binary - fnd::Vec mRawBinary; - - // variables - FacBinary mFac; - SacBinary mSac; - KcBinary mKc; - }; -} - diff --git a/lib/libnx/include/nx/AciHeader.h b/lib/libnx/include/nx/AciHeader.h deleted file mode 100644 index 2d6e2c6..0000000 --- a/lib/libnx/include/nx/AciHeader.h +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace nx -{ - class AciHeader : - public fnd::ISerialisable - { - public: - enum AciType - { - TYPE_ACI0, // for Access Control Info - TYPE_ACID // for Access Control Info Desc - }; - - struct sSection - { - size_t offset; - size_t size; - - void operator=(const sSection& other) - { - offset = other.offset; - size = other.size; - } - - bool operator==(const sSection& other) const - { - return (offset == other.offset) \ - && (size == other.size); - } - - bool operator!=(const sSection& other) const - { - return !operator==(other); - } - }; - - AciHeader(); - AciHeader(const AciHeader& other); - - void operator=(const AciHeader& other); - bool operator==(const AciHeader& other) const; - bool operator!=(const AciHeader& other) const; - - // export/import binary - virtual void toBytes(); - virtual void fromBytes(const byte_t* bytes, size_t len); - const fnd::Vec& getBytes() const; - - // variables - virtual void clear(); - size_t getAciSize() const; - - // ACI0 only - uint64_t getProgramId() const; - void setProgramId(uint64_t program_id); - - // ACID only - size_t getAcidSize() const; - //void setAcidSize(size_t size); - uint64_t getProgramIdMin() const; - void setProgramIdMin(uint64_t program_id); - uint64_t getProgramIdMax() const; - void setProgramIdMax(uint64_t program_id); - - // ACID & ACI0 - void setHeaderOffset(size_t offset); - AciType getAciType() const; - void setAciType(AciType type); - bool isProduction() const; - void setIsProduction(bool isProduction); - bool isUnqualifiedApproval() const; - void setIsUnqualifiedApproval(bool isUnqualifiedApproval); - const sSection& getFacPos() const; - void setFacSize(size_t size); - const sSection& getSacPos() const; - void setSacSize(size_t size); - const sSection& getKcPos() const; - void setKcSize(size_t size); - - private: - const std::string kModuleName = "ACI_HEADER"; - - // raw data - fnd::Vec mRawBinary; - - // ACI variables - uint64_t mProgramId; - - // ACID variables - size_t mAcidSize; - uint64_t mProgramIdMin; - uint64_t mProgramIdMax; - - // ACI(D) variables - size_t mHeaderOffset; - AciType mType; - bool mIsProduction; - bool mIsUnqualifiedApproval; - sSection mFac, mSac, mKc; - - void calculateSectionOffsets(); - }; -} - diff --git a/lib/libnx/include/nx/AcidBinary.h b/lib/libnx/include/nx/AcidBinary.h deleted file mode 100644 index c8bb558..0000000 --- a/lib/libnx/include/nx/AcidBinary.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include -#include -#include - -namespace nx -{ - class AcidBinary : - public AciBinary - { - public: - AcidBinary(); - AcidBinary(const AcidBinary& other); - - void operator=(const AcidBinary& other); - bool operator==(const AcidBinary& other) const; - bool operator!=(const AcidBinary& other) const; - - // export/import binary - void toBytes(); - void signBinary(const crypto::rsa::sRsa2048Key& key); - void fromBytes(const byte_t* bytes, size_t len); - void verifyBinary(const crypto::rsa::sRsa2048Key& key) const; - const fnd::Vec& getBytes() const; - - // variables - virtual void clear(); - - const crypto::rsa::sRsa2048Key& getNcaHeader2RsaKey() const; - void setNcaHeader2RsaKey(const crypto::rsa::sRsa2048Key& key); - - private: - const std::string kModuleName = "ACID_BINARY"; - - // raw binary - fnd::Vec mRawBinary; - - // variables - crypto::rsa::sRsa2048Key mEmbeddedPublicKey; - }; -} - diff --git a/lib/libnx/include/nx/NpdmBinary.h b/lib/libnx/include/nx/NpdmBinary.h index c09ae6c..e65ac85 100644 --- a/lib/libnx/include/nx/NpdmBinary.h +++ b/lib/libnx/include/nx/NpdmBinary.h @@ -2,8 +2,8 @@ #include #include #include -#include -#include +#include +#include namespace nx @@ -27,11 +27,11 @@ namespace nx // variables void clear(); - const AciBinary& getAci() const; - void setAci(const AciBinary& aci); + const AccessControlInfoBinary& getAci() const; + void setAci(const AccessControlInfoBinary& aci); - const AcidBinary& getAcid() const; - void setAcid(const AcidBinary& acid); + const AccessControlInfoDescBinary& getAcid() const; + void setAcid(const AccessControlInfoDescBinary& acid); private: const std::string kModuleName = "NPDM_BINARY"; @@ -39,8 +39,8 @@ namespace nx fnd::Vec mRawBinary; // variables - AciBinary mAci; - AcidBinary mAcid; + AccessControlInfoBinary mAci; + AccessControlInfoDescBinary mAcid; }; } diff --git a/lib/libnx/include/nx/aci.h b/lib/libnx/include/nx/aci.h index 38551bd..a3340f3 100644 --- a/lib/libnx/include/nx/aci.h +++ b/lib/libnx/include/nx/aci.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include namespace nx @@ -8,35 +9,45 @@ namespace nx { static const uint32_t kAciStructMagic = _MAKE_STRUCT_MAGIC("ACI0"); static const uint32_t kAciDescStructMagic = _MAKE_STRUCT_MAGIC("ACID"); - static const size_t kAciAlignSize = 0x10; + static const size_t kSectionAlignSize = 0x10; - enum Flags + enum Flag { FLAG_PRODUCTION, FLAG_UNQUALIFIED_APPROVAL }; } #pragma pack(push,1) + struct sAciSection + { + le_uint32_t offset; + le_uint32_t size; + }; + struct sAciHeader { le_uint32_t st_magic; - le_uint32_t size; // includes prefacing signature, set only in ACID made by SDK (it enables easy resigning) - byte_t reserved_0[4]; - le_uint32_t flags; // set in ACID only - union uProgramIdInfo - { - struct sRestrictProgramId - { - le_uint64_t min; - le_uint64_t max; - } program_id_restrict; - le_uint64_t program_id; - } program_id_info; - struct sAciSection - { - le_uint32_t offset; // aligned by 0x10 from the last one - le_uint32_t size; - } fac, sac, kc; + byte_t reserved_00[0xC]; + le_uint64_t program_id; + byte_t reserved_01[0x8]; + sAciSection fac; + sAciSection sac; + sAciSection kc; + }; + + struct sAciDescHeader + { + byte_t signature[crypto::rsa::kRsa2048Size]; + byte_t nca_rsa_signature2_modulus[crypto::rsa::kRsa2048Size]; + le_uint32_t st_magic; + le_uint32_t signed_size; + byte_t reserved_00[0x4]; + le_uint32_t flags; + le_uint64_t program_id_min; + le_uint64_t program_id_max; + sAciSection fac; + sAciSection sac; + sAciSection kc; }; #pragma pack(pop) } \ No newline at end of file diff --git a/lib/libnx/source/AccessControlInfoBinary.cpp b/lib/libnx/source/AccessControlInfoBinary.cpp new file mode 100644 index 0000000..bdf3460 --- /dev/null +++ b/lib/libnx/source/AccessControlInfoBinary.cpp @@ -0,0 +1,172 @@ +#include + +nx::AccessControlInfoBinary::AccessControlInfoBinary() +{ + clear(); +} + +nx::AccessControlInfoBinary::AccessControlInfoBinary(const AccessControlInfoBinary & other) +{ + *this = other; +} + +void nx::AccessControlInfoBinary::operator=(const AccessControlInfoBinary & other) +{ + mRawBinary = other.mRawBinary; + mProgramId = other.mProgramId; + mFileSystemAccessControl = other.mFileSystemAccessControl; + mServiceAccessControl = other.mServiceAccessControl; + mKernelCapabilities = other.mKernelCapabilities; +} + +bool nx::AccessControlInfoBinary::operator==(const AccessControlInfoBinary & other) const +{ + return (mProgramId == other.mProgramId) \ + && (mFileSystemAccessControl == other.mFileSystemAccessControl) \ + && (mServiceAccessControl == other.mServiceAccessControl) \ + && (mKernelCapabilities == other.mKernelCapabilities); +} + +bool nx::AccessControlInfoBinary::operator!=(const AccessControlInfoBinary & other) const +{ + return !(*this == other); +} + +void nx::AccessControlInfoBinary::toBytes() +{ + if (mFileSystemAccessControl.getBytes().size() == 0) + mFileSystemAccessControl.toBytes(); + + if (mServiceAccessControl.getBytes().size() == 0) + mServiceAccessControl.toBytes(); + + if (mKernelCapabilities.getBytes().size() == 0) + mKernelCapabilities.toBytes(); + + // determine section layout + struct sLayout { + uint32_t offset, size; + } fac, sac, kc; + + fac.offset = align(sizeof(sAciHeader), aci::kSectionAlignSize); + fac.size = (uint32_t)mFileSystemAccessControl.getBytes().size(); + sac.offset = align(fac.offset + fac.size, aci::kSectionAlignSize); + sac.size = (uint32_t)mServiceAccessControl.getBytes().size(); + kc.offset = align(sac.offset + sac.size, aci::kSectionAlignSize); + kc.size = (uint32_t)mKernelCapabilities.getBytes().size(); + + // get total size + size_t total_size = _MAX(_MAX(fac.offset + fac.size, sac.offset + sac.size), kc.offset + kc.size); + + mRawBinary.alloc(total_size); + sAciHeader* hdr = (sAciHeader*)mRawBinary.data(); + + // set type + hdr->st_magic = aci::kAciStructMagic; + + // set program id + hdr->program_id = mProgramId; + + // set offset/size + hdr->fac.offset = fac.offset; + hdr->fac.size = fac.size; + hdr->sac.offset = sac.offset; + hdr->sac.size = sac.size; + hdr->kc.offset = kc.offset; + hdr->kc.size = kc.size; +} + +void nx::AccessControlInfoBinary::fromBytes(const byte_t* data, size_t len) +{ + // check size + if (len < sizeof(sAciHeader)) + { + throw fnd::Exception(kModuleName, "AccessControlInfo binary is too small"); + } + + // clear variables + clear(); + + // save a copy of the header + sAciHeader hdr; + memcpy((void*)&hdr, data, sizeof(sAciHeader)); + + // check magic + if (hdr.st_magic.get() != aci::kAciStructMagic) + { + throw fnd::Exception(kModuleName, "AccessControlInfo header corrupt"); + } + + // get total size + size_t total_size = _MAX(_MAX(hdr.fac.offset.get() + hdr.fac.size.get(), hdr.sac.offset.get() + hdr.sac.size.get()), hdr.kc.offset.get() + hdr.kc.size.get()); + + // validate binary size + if (len < total_size) + { + throw fnd::Exception(kModuleName, "AccessControlInfo binary is too small"); + } + + // allocate memory for header + mRawBinary.alloc(total_size); + memcpy(mRawBinary.data(), data, mRawBinary.size()); + + // save variables + mProgramId = hdr.program_id.get(); + mFileSystemAccessControl.fromBytes(mRawBinary.data() + hdr.fac.offset.get(), hdr.fac.size.get()); + mServiceAccessControl.fromBytes(mRawBinary.data() + hdr.sac.offset.get(), hdr.sac.size.get()); + mKernelCapabilities.fromBytes(mRawBinary.data() + hdr.kc.offset.get(), hdr.kc.size.get()); +} + +const fnd::Vec& nx::AccessControlInfoBinary::getBytes() const +{ + return mRawBinary; +} + +void nx::AccessControlInfoBinary::clear() +{ + mRawBinary.clear(); + mProgramId = 0; + mFileSystemAccessControl.clear(); + mServiceAccessControl.clear(); + mKernelCapabilities.clear(); +} + +uint64_t nx::AccessControlInfoBinary::getProgramId() const +{ + return mProgramId; +} + +void nx::AccessControlInfoBinary::setProgramId(uint64_t program_id) +{ + mProgramId = program_id; +} + +const nx::FacBinary& nx::AccessControlInfoBinary::getFileSystemAccessControl() const +{ + return mFileSystemAccessControl; +} + +void nx::AccessControlInfoBinary::setFileSystemAccessControl(const nx::FacBinary& fac) +{ + mFileSystemAccessControl = fac; +} + +const nx::SacBinary& nx::AccessControlInfoBinary::getServiceAccessControl() const +{ + return mServiceAccessControl; +} + +void nx::AccessControlInfoBinary::setServiceAccessControl(const nx::SacBinary& sac) +{ + mServiceAccessControl = sac; +} + +const nx::KcBinary& nx::AccessControlInfoBinary::getKernelCapabilities() const +{ + return mKernelCapabilities; +} + +void nx::AccessControlInfoBinary::setKernelCapabilities(const nx::KcBinary& kc) +{ + mKernelCapabilities = kc; +} \ No newline at end of file diff --git a/lib/libnx/source/AccessControlInfoDescBinary.cpp b/lib/libnx/source/AccessControlInfoDescBinary.cpp new file mode 100644 index 0000000..7fa7310 --- /dev/null +++ b/lib/libnx/source/AccessControlInfoDescBinary.cpp @@ -0,0 +1,250 @@ +#include + +nx::AccessControlInfoDescBinary::AccessControlInfoDescBinary() +{ + clear(); +} + +nx::AccessControlInfoDescBinary::AccessControlInfoDescBinary(const AccessControlInfoDescBinary & other) +{ + *this = other; +} + +void nx::AccessControlInfoDescBinary::operator=(const AccessControlInfoDescBinary & other) +{ + mRawBinary = other.mRawBinary; + mNcaHeaderSignature2Key = other.mNcaHeaderSignature2Key; + mFlags = other.mFlags; + mProgramIdRestrict = other.mProgramIdRestrict; + mFileSystemAccessControl = other.mFileSystemAccessControl; + mServiceAccessControl = other.mServiceAccessControl; + mKernelCapabilities = other.mKernelCapabilities; +} + +bool nx::AccessControlInfoDescBinary::operator==(const AccessControlInfoDescBinary & other) const +{ + return (mNcaHeaderSignature2Key == other.mNcaHeaderSignature2Key) \ + && (mFlags == other.mFlags) \ + && (mProgramIdRestrict == other.mProgramIdRestrict) \ + && (mFileSystemAccessControl == other.mFileSystemAccessControl) \ + && (mServiceAccessControl == other.mServiceAccessControl) \ + && (mKernelCapabilities == other.mKernelCapabilities); +} + +bool nx::AccessControlInfoDescBinary::operator!=(const AccessControlInfoDescBinary & other) const +{ + return !(*this == other); +} + +void nx::AccessControlInfoDescBinary::toBytes() +{ + if (mFileSystemAccessControl.getBytes().size() == 0) + mFileSystemAccessControl.toBytes(); + + if (mServiceAccessControl.getBytes().size() == 0) + mServiceAccessControl.toBytes(); + + if (mKernelCapabilities.getBytes().size() == 0) + mKernelCapabilities.toBytes(); + + // determine section layout + struct sLayout { + uint32_t offset, size; + } fac, sac, kc; + + fac.offset = align(sizeof(sAciDescHeader), aci::kSectionAlignSize); + fac.size = (uint32_t)mFileSystemAccessControl.getBytes().size(); + sac.offset = align(fac.offset + fac.size, aci::kSectionAlignSize); + sac.size = (uint32_t)mServiceAccessControl.getBytes().size(); + kc.offset = align(sac.offset + sac.size, aci::kSectionAlignSize); + kc.size = (uint32_t)mKernelCapabilities.getBytes().size(); + + // get total size + size_t total_size = _MAX(_MAX(fac.offset + fac.size, sac.offset + sac.size), kc.offset + kc.size); + + mRawBinary.alloc(total_size); + sAciDescHeader* hdr = (sAciDescHeader*)mRawBinary.data(); + + // set rsa modulus + memcpy(hdr->nca_rsa_signature2_modulus, mNcaHeaderSignature2Key.modulus, crypto::rsa::kRsa2048Size); + + // set type + hdr->st_magic = aci::kAciDescStructMagic; + + // set "acid size" + hdr->signed_size = total_size - crypto::rsa::kRsa2048Size; + + // set flags + uint32_t flags = 0; + for (size_t i = 0; i < mFlags.size(); i++) + flags |= _BIT(mFlags[i]); + hdr->flags = flags; + + // set program id restrict settings + hdr->program_id_min = mProgramIdRestrict.min; + hdr->program_id_max = mProgramIdRestrict.max; + + // set offset/size + hdr->fac.offset = fac.offset; + hdr->fac.size = fac.size; + hdr->sac.offset = sac.offset; + hdr->sac.size = sac.size; + hdr->kc.offset = kc.offset; + hdr->kc.size = kc.size; +} + +void nx::AccessControlInfoDescBinary::fromBytes(const byte_t* data, size_t len) +{ + // check size + if (len < sizeof(sAciDescHeader)) + { + throw fnd::Exception(kModuleName, "AccessControlInfoDesc binary is too small"); + } + + // clear variables + clear(); + + // save a copy of the header + sAciDescHeader hdr; + memcpy((void*)&hdr, data, sizeof(sAciDescHeader)); + + // check magic + if (hdr.st_magic.get() != aci::kAciDescStructMagic) + { + throw fnd::Exception(kModuleName, "AccessControlInfoDesc header corrupt"); + } + + // get total size + size_t total_size = _MAX(_MAX(hdr.fac.offset.get() + hdr.fac.size.get(), hdr.sac.offset.get() + hdr.sac.size.get()), hdr.kc.offset.get() + hdr.kc.size.get()); + + // validate binary size + if (len < total_size) + { + throw fnd::Exception(kModuleName, "AccessControlInfoDesc binary is too small"); + } + + // allocate memory for header + mRawBinary.alloc(total_size); + memcpy(mRawBinary.data(), data, mRawBinary.size()); + + // save variables + memcpy(mNcaHeaderSignature2Key.modulus, hdr.nca_rsa_signature2_modulus, crypto::rsa::kRsa2048Size); + + for (size_t i = 0; i < 32; i++) + { + if (_HAS_BIT(hdr.flags.get(), i)) + mFlags.addElement((aci::Flag)i); + } + + mProgramIdRestrict.min = hdr.program_id_min.get(); + mProgramIdRestrict.max = hdr.program_id_max.get(); + + mFileSystemAccessControl.fromBytes(mRawBinary.data() + hdr.fac.offset.get(), hdr.fac.size.get()); + mServiceAccessControl.fromBytes(mRawBinary.data() + hdr.sac.offset.get(), hdr.sac.size.get()); + mKernelCapabilities.fromBytes(mRawBinary.data() + hdr.kc.offset.get(), hdr.kc.size.get()); +} + +const fnd::Vec& nx::AccessControlInfoDescBinary::getBytes() const +{ + return mRawBinary; +} + +void nx::AccessControlInfoDescBinary::generateSignature(const crypto::rsa::sRsa2048Key& key) +{ + if (mRawBinary.size() == 0) + toBytes(); + + byte_t hash[crypto::sha::kSha256HashLen]; + crypto::sha::Sha256(mRawBinary.data() + crypto::rsa::kRsa2048Size, mRawBinary.size() - crypto::rsa::kRsa2048Size, hash); + + if (crypto::rsa::pkcs::rsaSign(key, crypto::sha::HASH_SHA256, hash, mRawBinary.data()) != 0) + { + throw fnd::Exception(kModuleName, "Failed to sign Access Control Info Desc"); + } +} + +void nx::AccessControlInfoDescBinary::validateSignature(const crypto::rsa::sRsa2048Key& key) const +{ + if (mRawBinary.size() == 0) + throw fnd::Exception(kModuleName, "No Access Control Info Desc binary exists to verify"); + + byte_t hash[crypto::sha::kSha256HashLen]; + crypto::sha::Sha256(mRawBinary.data() + crypto::rsa::kRsa2048Size, mRawBinary.size() - crypto::rsa::kRsa2048Size, hash); + + if (crypto::rsa::pss::rsaVerify(key, crypto::sha::HASH_SHA256, hash, mRawBinary.data()) != 0) + { + throw fnd::Exception(kModuleName, "Failed to verify Access Control Info Desc"); + } +} + +void nx::AccessControlInfoDescBinary::clear() +{ + mRawBinary.clear(); + memset((void*)&mNcaHeaderSignature2Key, 0, sizeof(mNcaHeaderSignature2Key)); + mFlags.clear(); + mProgramIdRestrict.min = 0; + mProgramIdRestrict.max = 0; + mFileSystemAccessControl.clear(); + mServiceAccessControl.clear(); + mKernelCapabilities.clear(); +} + +const crypto::rsa::sRsa2048Key& nx::AccessControlInfoDescBinary::getNcaHeaderSignature2Key() const +{ + return mNcaHeaderSignature2Key; +} + +void nx::AccessControlInfoDescBinary::setNcaHeaderSignature2Key(const crypto::rsa::sRsa2048Key& key) +{ + mNcaHeaderSignature2Key = key; +} + +const fnd::List& nx::AccessControlInfoDescBinary::getFlagList() const +{ + return mFlags; +} + +void nx::AccessControlInfoDescBinary::setFlagList(const fnd::List& flags) +{ + mFlags = flags; +} + +const nx::AccessControlInfoDescBinary::sProgramIdRestrict& nx::AccessControlInfoDescBinary::getProgramIdRestrict() const +{ + return mProgramIdRestrict; +} + +void nx::AccessControlInfoDescBinary::setProgramIdRestrict(const sProgramIdRestrict& pid_restrict) +{ + mProgramIdRestrict = pid_restrict; +} + +const nx::FacBinary& nx::AccessControlInfoDescBinary::getFileSystemAccessControl() const +{ + return mFileSystemAccessControl; +} + +void nx::AccessControlInfoDescBinary::setFileSystemAccessControl(const nx::FacBinary& fac) +{ + mFileSystemAccessControl = fac; +} + +const nx::SacBinary& nx::AccessControlInfoDescBinary::getServiceAccessControl() const +{ + return mServiceAccessControl; +} + +void nx::AccessControlInfoDescBinary::setServiceAccessControl(const nx::SacBinary& sac) +{ + mServiceAccessControl = sac; +} + +const nx::KcBinary& nx::AccessControlInfoDescBinary::getKernelCapabilities() const +{ + return mKernelCapabilities; +} + +void nx::AccessControlInfoDescBinary::setKernelCapabilities(const nx::KcBinary& kc) +{ + mKernelCapabilities = kc; +} \ No newline at end of file diff --git a/lib/libnx/source/AciBinary.cpp b/lib/libnx/source/AciBinary.cpp deleted file mode 100644 index 85880ea..0000000 --- a/lib/libnx/source/AciBinary.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include - -nx::AciBinary::AciBinary() -{ - clear(); -} - -nx::AciBinary::AciBinary(const AciBinary & other) -{ - *this = other; -} - -void nx::AciBinary::operator=(const AciBinary & other) -{ - if (other.getBytes().size()) - { - fromBytes(other.getBytes().data(), other.getBytes().size()); - } - else - { - AciHeader::operator=(other); - mFac = other.mFac; - mSac = other.mSac; - mKc = other.mKc; - } -} - -bool nx::AciBinary::operator==(const AciBinary & other) const -{ - return (AciHeader::operator==(other)) \ - && (mFac == other.mFac) \ - && (mSac == other.mSac) \ - && (mKc == other.mKc); -} - -bool nx::AciBinary::operator!=(const AciBinary & other) const -{ - return !(*this == other); -} - -void nx::AciBinary::toBytes() -{ - // export components - mFac.toBytes(); - mSac.toBytes(); - mKc.toBytes(); - - // set sizes - setFacSize(mFac.getBytes().size()); - setSacSize(mSac.getBytes().size()); - setKcSize(mKc.getBytes().size()); - - // export header - AciHeader::toBytes(); - - // allocate binary - mRawBinary.alloc(getAciSize()); - - // copy header - memcpy(mRawBinary.data(), AciHeader::getBytes().data(), AciHeader::getBytes().size()); - - // copy components - memcpy(mRawBinary.data() + getFacPos().offset, mFac.getBytes().data(), mFac.getBytes().size()); - memcpy(mRawBinary.data() + getSacPos().offset, mSac.getBytes().data(), mSac.getBytes().size()); - memcpy(mRawBinary.data() + getKcPos().offset, mKc.getBytes().data(), mKc.getBytes().size()); -} - -void nx::AciBinary::fromBytes(const byte_t * bytes, size_t len) -{ - clear(); - - AciHeader::fromBytes(bytes, len); - - if (getAciSize() > len) - { - throw fnd::Exception(kModuleName, "ACI binary too small"); - } - - mRawBinary.alloc(getAciSize()); - memcpy(mRawBinary.data(), bytes, mRawBinary.size()); - - if (getFacPos().size > 0) - { - mFac.fromBytes(mRawBinary.data() + getFacPos().offset, getFacPos().size); - } - if (getSacPos().size > 0) - { - mSac.fromBytes(mRawBinary.data() + getSacPos().offset, getSacPos().size); - } - if (getKcPos().size > 0) - { - mKc.fromBytes(mRawBinary.data() + getKcPos().offset, getKcPos().size); - } - -} - -const fnd::Vec& nx::AciBinary::getBytes() const -{ - return mRawBinary; -} - -void nx::AciBinary::clear() -{ - AciHeader::clear(); - mRawBinary.clear(); - mFac.clear(); - mSac.clear(); - mKc.clear(); -} - -const nx::FacBinary & nx::AciBinary::getFac() const -{ - return mFac; -} - -void nx::AciBinary::setFac(const FacBinary & fac) -{ - mFac = fac; -} - -const nx::SacBinary & nx::AciBinary::getSac() const -{ - return mSac; -} - -void nx::AciBinary::setSac(const SacBinary & sac) -{ - mSac = sac; -} - -const nx::KcBinary & nx::AciBinary::getKc() const -{ - return mKc; -} - -void nx::AciBinary::setKc(const KcBinary & kc) -{ - mKc = kc; -} \ No newline at end of file diff --git a/lib/libnx/source/AciHeader.cpp b/lib/libnx/source/AciHeader.cpp deleted file mode 100644 index 41f7e2b..0000000 --- a/lib/libnx/source/AciHeader.cpp +++ /dev/null @@ -1,302 +0,0 @@ -#include - -nx::AciHeader::AciHeader() -{ - clear(); -} - -nx::AciHeader::AciHeader(const AciHeader & other) -{ - *this = other; -} - -void nx::AciHeader::operator=(const AciHeader & other) -{ - if (other.getBytes().size()) - { - fromBytes(other.getBytes().data(), other.getBytes().size()); - } - else - { - mHeaderOffset = other.mHeaderOffset; - mType = other.mType; - mIsProduction = other.mIsProduction; - mIsUnqualifiedApproval = other.mIsUnqualifiedApproval; - mAcidSize = other.mAcidSize; - mProgramIdMin = other.mProgramIdMin; - mProgramIdMax = other.mProgramIdMax; - mProgramId = other.mProgramId; - mFac = other.mFac; - mSac = other.mSac; - mKc = other.mKc; - } -} - -bool nx::AciHeader::operator==(const AciHeader & other) const -{ - return (mHeaderOffset == other.mHeaderOffset) \ - && (mType == other.mType) \ - && (mIsProduction == other.mIsProduction) \ - && (mIsUnqualifiedApproval == other.mIsUnqualifiedApproval) \ - && (mAcidSize == other.mAcidSize) \ - && (mProgramIdMin == other.mProgramIdMin) \ - && (mProgramIdMax == other.mProgramIdMax) \ - && (mProgramId == other.mProgramId) \ - && (mFac == other.mFac) \ - && (mSac == other.mSac) \ - && (mKc == other.mKc); -} - -bool nx::AciHeader::operator!=(const AciHeader & other) const -{ - return !(*this == other); -} - -void nx::AciHeader::toBytes() -{ - mRawBinary.alloc(sizeof(sAciHeader)); - sAciHeader* hdr = (sAciHeader*)mRawBinary.data(); - - // set type - switch (mType) - { - case (TYPE_ACI0): - hdr->st_magic = aci::kAciStructMagic; - break; - case (TYPE_ACID): - hdr->st_magic = aci::kAciDescStructMagic; - break; - default: - throw fnd::Exception(kModuleName, "Unexpected ACI type"); - } - - // set offset/size - calculateSectionOffsets(); - hdr->fac.offset = (uint32_t)mFac.offset; - hdr->fac.size = (uint32_t)mFac.size; - hdr->sac.offset = (uint32_t)mSac.offset; - hdr->sac.size = (uint32_t)mSac.size; - hdr->kc.offset = (uint32_t)mKc.offset; - hdr->kc.size = (uint32_t)mKc.size; - - uint32_t flags = 0; - if (mIsProduction) - flags |= _BIT(aci::FLAG_PRODUCTION); - if (mIsUnqualifiedApproval) - flags |= _BIT(aci::FLAG_UNQUALIFIED_APPROVAL); - - hdr->flags = flags; - - if (mType == TYPE_ACI0) - { - // set program - hdr->program_id_info.program_id = mProgramId; - } - else if (mType == TYPE_ACID) - { - mAcidSize = getAciSize(); - hdr->size = (uint32_t)mAcidSize; - hdr->program_id_info.program_id_restrict.min = mProgramIdMin; - hdr->program_id_info.program_id_restrict.max = mProgramIdMax; - } -} - -void nx::AciHeader::fromBytes(const byte_t * bytes, size_t len) -{ - if (len < sizeof(sAciHeader)) - { - throw fnd::Exception(kModuleName, "ACI header too small"); - } - - clear(); - - mRawBinary.alloc(sizeof(sAciHeader)); - memcpy(mRawBinary.data(), bytes, sizeof(sAciHeader)); - - sAciHeader* hdr = (sAciHeader*)mRawBinary.data(); - - switch (hdr->st_magic.get()) - { - case (aci::kAciStructMagic): - mType = TYPE_ACI0; - break; - case (aci::kAciDescStructMagic): - mType = TYPE_ACID; - break; - default: - throw fnd::Exception(kModuleName, "ACI header corrupt"); - } - - - if (mType == TYPE_ACI0) - { - mProgramId = hdr->program_id_info.program_id.get(); - mIsProduction = false; - mIsUnqualifiedApproval = false; - mAcidSize = 0; - mProgramIdMin = 0; - mProgramIdMax = 0; - } - else if (mType == TYPE_ACID) - { - mProgramId = 0; - mIsProduction = _HAS_BIT(hdr->flags.get(), aci::FLAG_PRODUCTION); - mIsUnqualifiedApproval = _HAS_BIT(hdr->flags.get(), aci::FLAG_UNQUALIFIED_APPROVAL); - mAcidSize = hdr->size.get(); - mProgramIdMin = hdr->program_id_info.program_id_restrict.min.get(); - mProgramIdMax = hdr->program_id_info.program_id_restrict.max.get(); - } - - // the header offset is the _MIN(sac.offset, fac.offset, kc.offset) - sizeof(sHeader) - mHeaderOffset = _MAX(_MIN(hdr->sac.offset.get(), _MIN(hdr->fac.offset.get(), hdr->kc.offset.get())), align(sizeof(sAciHeader), aci::kAciAlignSize)) - align(sizeof(sAciHeader), aci::kAciAlignSize); - - mFac.offset = hdr->fac.offset.get() - mHeaderOffset; - mFac.size = hdr->fac.size.get(); - mSac.offset = hdr->sac.offset.get() - mHeaderOffset; - mSac.size = hdr->sac.size.get(); - mKc.offset = hdr->kc.offset.get() - mHeaderOffset; - mKc.size = hdr->kc.size.get(); -} - -const fnd::Vec& nx::AciHeader::getBytes() const -{ - return mRawBinary; -} - -void nx::AciHeader::clear() -{ - mRawBinary.clear(); - mHeaderOffset = 0; - mType = TYPE_ACI0; - mProgramId = 0; - mProgramIdMin = 0; - mProgramIdMax = 0; - mAcidSize = 0; - mIsProduction = false; - mIsUnqualifiedApproval = false; - mFac.offset = 0; - mFac.size = 0; - mSac.offset = 0; - mSac.size = 0; - mKc.offset = 0; - mKc.size = 0; -} - -size_t nx::AciHeader::getAciSize() const -{ - return _MAX(_MAX(_MAX(mSac.offset + mSac.size, mKc.offset + mKc.size), mFac.offset + mFac.size), sizeof(sAciHeader)); -} - -size_t nx::AciHeader::getAcidSize() const -{ - return mAcidSize; -} - -/* -void nx::AciHeader::setAcidSize(size_t size) -{ - mAcidSize = size; -} -*/ - -uint64_t nx::AciHeader::getProgramIdMin() const -{ - return mProgramIdMin; -} - -void nx::AciHeader::setProgramIdMin(uint64_t program_id) -{ - mProgramIdMin = program_id; -} - -uint64_t nx::AciHeader::getProgramIdMax() const -{ - return mProgramIdMax; -} - -void nx::AciHeader::setProgramIdMax(uint64_t program_id) -{ - mProgramIdMax = program_id; -} - -void nx::AciHeader::setHeaderOffset(size_t offset) -{ - mHeaderOffset = offset; -} - -nx::AciHeader::AciType nx::AciHeader::getAciType() const -{ - return mType; -} - -void nx::AciHeader::setAciType(AciType type) -{ - mType = type; -} - -bool nx::AciHeader::isProduction() const -{ - return mIsProduction; -} - -void nx::AciHeader::setIsProduction(bool isProduction) -{ - mIsProduction = isProduction; -} - -bool nx::AciHeader::isUnqualifiedApproval() const -{ - return mIsUnqualifiedApproval; -} - -void nx::AciHeader::setIsUnqualifiedApproval(bool isUnqualifiedApproval) -{ - mIsUnqualifiedApproval = isUnqualifiedApproval; -} - -uint64_t nx::AciHeader::getProgramId() const -{ - return mProgramId; -} - -void nx::AciHeader::setProgramId(uint64_t program_id) -{ - mProgramId = program_id; -} - -const nx::AciHeader::sSection & nx::AciHeader::getFacPos() const -{ - return mFac; -} - -void nx::AciHeader::setFacSize(size_t size) -{ - mFac.size = size; -} - -const nx::AciHeader::sSection & nx::AciHeader::getSacPos() const -{ - return mSac; -} - -void nx::AciHeader::setSacSize(size_t size) -{ - mSac.size = size; -} - -const nx::AciHeader::sSection & nx::AciHeader::getKcPos() const -{ - return mKc; -} - -void nx::AciHeader::setKcSize(size_t size) -{ - mKc.size = size; -} - -void nx::AciHeader::calculateSectionOffsets() -{ - mFac.offset = align(mHeaderOffset, aci::kAciAlignSize) + align(sizeof(sAciHeader), aci::kAciAlignSize); - mSac.offset = mFac.offset + align(mFac.size, aci::kAciAlignSize); - mKc.offset = mSac.offset + align(mSac.size, aci::kAciAlignSize); -} \ No newline at end of file diff --git a/lib/libnx/source/AcidBinary.cpp b/lib/libnx/source/AcidBinary.cpp deleted file mode 100644 index b607a75..0000000 --- a/lib/libnx/source/AcidBinary.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include - -nx::AcidBinary::AcidBinary() -{ - clear(); -} - -nx::AcidBinary::AcidBinary(const AcidBinary & other) -{ - *this = other; -} - -bool nx::AcidBinary::operator==(const AcidBinary & other) const -{ - return AciBinary::operator==(other) \ - && (mEmbeddedPublicKey == other.mEmbeddedPublicKey); -} - -bool nx::AcidBinary::operator!=(const AcidBinary & other) const -{ - return !(*this == other); -} - -void nx::AcidBinary::operator=(const AcidBinary & other) -{ - if (other.getBytes().size()) - { - fromBytes(other.getBytes().data(), other.getBytes().size()); - } - else - { - AciBinary::operator=(other); - mEmbeddedPublicKey = other.mEmbeddedPublicKey; - } -} - -void nx::AcidBinary::toBytes() -{ - AciBinary::setHeaderOffset(crypto::rsa::kRsa2048Size); // not include signature - AciBinary::toBytes(); - mRawBinary.alloc(AciBinary::getBytes().size() + crypto::rsa::kRsa2048Size * 2); - - memcpy(mRawBinary.data() + crypto::rsa::kRsa2048Size, mEmbeddedPublicKey.modulus, crypto::rsa::kRsa2048Size); - memcpy(mRawBinary.data() + crypto::rsa::kRsa2048Size * 2, AciBinary::getBytes().data(), AciBinary::getBytes().size()); -} - -void nx::AcidBinary::signBinary(const crypto::rsa::sRsa2048Key & key) -{ - if (mRawBinary.size() == 0) - { - toBytes(); - } - - byte_t hash[crypto::sha::kSha256HashLen]; - crypto::sha::Sha256(mRawBinary.data() + crypto::rsa::kRsa2048Size, mRawBinary.size() - crypto::rsa::kRsa2048Size, hash); - - if (crypto::rsa::pkcs::rsaSign(key, crypto::sha::HASH_SHA256, hash, mRawBinary.data()) != 0) - { - throw fnd::Exception(kModuleName, "Failed to sign ACID"); - } -} - -void nx::AcidBinary::fromBytes(const byte_t * bytes, size_t len) -{ - if (len <= crypto::rsa::kRsa2048Size*2) - { - throw fnd::Exception(kModuleName, "ACID binary too small"); - } - - clear(); - - // import aci binary past sig + pubkey - AciBinary::fromBytes(bytes + crypto::rsa::kRsa2048Size * 2, len - crypto::rsa::kRsa2048Size * 2); - - // save internal copy - size_t acid_size = AciBinary::getBytes().size() + crypto::rsa::kRsa2048Size * 2; - if (acid_size > len) - { - throw fnd::Exception(kModuleName, "ACID binary too small"); - } - - mRawBinary.alloc(acid_size); - memcpy(mRawBinary.data(), bytes, mRawBinary.size()); - memcpy(mEmbeddedPublicKey.modulus, bytes + crypto::rsa::kRsa2048Size, crypto::rsa::kRsa2048Size); -} - -void nx::AcidBinary::verifyBinary(const crypto::rsa::sRsa2048Key & key) const -{ - if (mRawBinary.size() == 0) - { - throw fnd::Exception(kModuleName, "No ACID binary exists to verify"); - } - - byte_t hash[crypto::sha::kSha256HashLen]; - crypto::sha::Sha256(mRawBinary.data() + crypto::rsa::kRsa2048Size, mRawBinary.size() - crypto::rsa::kRsa2048Size, hash); - - if (crypto::rsa::pss::rsaVerify(key, crypto::sha::HASH_SHA256, hash, mRawBinary.data()) != 0) - { - throw fnd::Exception(kModuleName, "Failed to verify ACID"); - } -} - -const fnd::Vec& nx::AcidBinary::getBytes() const -{ - return mRawBinary; -} - -void nx::AcidBinary::clear() -{ - AciBinary::clear(); - mRawBinary.clear(); - mEmbeddedPublicKey = crypto::rsa::sRsa2048Key(); -} - -const crypto::rsa::sRsa2048Key & nx::AcidBinary::getNcaHeader2RsaKey() const -{ - return mEmbeddedPublicKey; -} - -void nx::AcidBinary::setNcaHeader2RsaKey(const crypto::rsa::sRsa2048Key & key) -{ - mEmbeddedPublicKey = key; -} diff --git a/lib/libnx/source/NpdmBinary.cpp b/lib/libnx/source/NpdmBinary.cpp index fab9275..f5ea3dd 100644 --- a/lib/libnx/source/NpdmBinary.cpp +++ b/lib/libnx/source/NpdmBinary.cpp @@ -1,5 +1,7 @@ #include +#include + nx::NpdmBinary::NpdmBinary() : mAci(), mAcid() @@ -42,11 +44,7 @@ bool nx::NpdmBinary::operator!=(const NpdmBinary & other) const void nx::NpdmBinary::toBytes() { - mAci.toBytes(); - mAcid.toBytes(); - - setAciSize(mAci.getBytes().size()); - setAcidSize(mAcid.getBytes().size()); + throw fnd::Exception(kModuleName, "toBytes() not implemented."); } void nx::NpdmBinary::fromBytes(const byte_t* data, size_t len) @@ -90,22 +88,22 @@ void nx::NpdmBinary::clear() mAcid.clear(); } -const nx::AciBinary & nx::NpdmBinary::getAci() const +const nx::AccessControlInfoBinary & nx::NpdmBinary::getAci() const { return mAci; } -void nx::NpdmBinary::setAci(const AciBinary & aci) +void nx::NpdmBinary::setAci(const AccessControlInfoBinary & aci) { mAci = aci; } -const nx::AcidBinary & nx::NpdmBinary::getAcid() const +const nx::AccessControlInfoDescBinary & nx::NpdmBinary::getAcid() const { return mAcid; } -void nx::NpdmBinary::setAcid(const AcidBinary & acid) +void nx::NpdmBinary::setAcid(const AccessControlInfoDescBinary & acid) { mAcid = acid; } \ No newline at end of file diff --git a/programs/nstool/source/NcaProcess.cpp b/programs/nstool/source/NcaProcess.cpp index 42d0e48..89f5d80 100644 --- a/programs/nstool/source/NcaProcess.cpp +++ b/programs/nstool/source/NcaProcess.cpp @@ -618,7 +618,7 @@ void NcaProcess::validateNcaSignatures() npdm.setCliOutputMode(0); npdm.process(); - if (crypto::rsa::pss::rsaVerify(npdm.getNpdmBinary().getAcid().getNcaHeader2RsaKey(), crypto::sha::HASH_SHA256, mHdrHash.bytes, mHdrBlock.signature_acid) != 0) + if (crypto::rsa::pss::rsaVerify(npdm.getNpdmBinary().getAcid().getNcaHeaderSignature2Key(), crypto::sha::HASH_SHA256, mHdrHash.bytes, mHdrBlock.signature_acid) != 0) { printf("[WARNING] NCA Header ACID Signature: FAIL \n"); } diff --git a/programs/nstool/source/NpdmProcess.cpp b/programs/nstool/source/NpdmProcess.cpp index f003a72..30b12f0 100644 --- a/programs/nstool/source/NpdmProcess.cpp +++ b/programs/nstool/source/NpdmProcess.cpp @@ -44,17 +44,17 @@ void NpdmProcess::process() // aci binary displayAciHdr(mNpdm.getAci()); - displayFac(mNpdm.getAci().getFac()); - displaySac(mNpdm.getAci().getSac()); - displayKernelCap(mNpdm.getAci().getKc()); + displayFac(mNpdm.getAci().getFileSystemAccessControl()); + displaySac(mNpdm.getAci().getServiceAccessControl()); + displayKernelCap(mNpdm.getAci().getKernelCapabilities()); // acid binary if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) { - displayAciHdr(mNpdm.getAcid()); - displayFac(mNpdm.getAcid().getFac()); - displaySac(mNpdm.getAcid().getSac()); - displayKernelCap(mNpdm.getAcid().getKc()); + displayAciDescHdr(mNpdm.getAcid()); + displayFac(mNpdm.getAcid().getFileSystemAccessControl()); + displaySac(mNpdm.getAcid().getServiceAccessControl()); + displayKernelCap(mNpdm.getAcid().getKernelCapabilities()); } } } @@ -87,7 +87,41 @@ const nx::NpdmBinary& NpdmProcess::getNpdmBinary() const const std::string kInstructionType[2] = { "32Bit", "64Bit" }; const std::string kProcAddrSpace[4] = { "Unknown", "64Bit", "32Bit", "32Bit no reserved" }; -const std::string kAciType[2] = { "ACI0", "ACID" }; +const std::string kAcidFlag[32] = +{ + "Production", + "UnqualifiedApproval", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown" +}; const std::string kMiscFlag[15] = { "EnableDebug", "ForceDebug", "bit2", "bit3", "bit4", "bit5", "bit6", "bit7", "bit8", "bit9", "bit10", "bit11", "bit12", "bit13", "bit14"}; const std::string kFsaFlag[64] = { @@ -293,10 +327,10 @@ const std::string kMemMapType[2] = { "Io", "Static" }; const std::string kAcidTarget[2] = { "Development", "Production" }; -void NpdmProcess::validateAcidSignature(const nx::AcidBinary& acid) +void NpdmProcess::validateAcidSignature(const nx::AccessControlInfoDescBinary& acid) { try { - acid.verifyBinary(mKeyset->acid_sign_key); + acid.validateSignature(mKeyset->acid_sign_key); } catch (...) { printf("[WARNING] ACID Signature: FAIL\n"); @@ -304,193 +338,193 @@ void NpdmProcess::validateAcidSignature(const nx::AcidBinary& acid) } -void NpdmProcess::validateAciFromAcid(const nx::AciBinary& aci, const nx::AcidBinary& acid) +void NpdmProcess::validateAciFromAcid(const nx::AccessControlInfoBinary& aci, const nx::AccessControlInfoDescBinary& acid) { // check Program ID - if (acid.getProgramIdMin() > 0 && aci.getProgramId() < acid.getProgramIdMin()) + if (acid.getProgramIdRestrict().min > 0 && aci.getProgramId() < acid.getProgramIdRestrict().min) { printf("[WARNING] ACI ProgramId: FAIL (Outside Legal Range)\n"); } - else if (acid.getProgramIdMax() > 0 && aci.getProgramId() > acid.getProgramIdMax()) + else if (acid.getProgramIdRestrict().max > 0 && aci.getProgramId() > acid.getProgramIdRestrict().max) { printf("[WARNING] ACI ProgramId: FAIL (Outside Legal Range)\n"); } - for (size_t i = 0; i < aci.getFac().getFsaRightsList().size(); i++) + for (size_t i = 0; i < aci.getFileSystemAccessControl().getFsaRightsList().size(); i++) { bool fsaRightFound = false; - for (size_t j = 0; j < acid.getFac().getFsaRightsList().size() && fsaRightFound == false; j++) + for (size_t j = 0; j < acid.getFileSystemAccessControl().getFsaRightsList().size() && fsaRightFound == false; j++) { - if (aci.getFac().getFsaRightsList()[i] == acid.getFac().getFsaRightsList()[j]) + if (aci.getFileSystemAccessControl().getFsaRightsList()[i] == acid.getFileSystemAccessControl().getFsaRightsList()[j]) fsaRightFound = true; } if (fsaRightFound == false) { - printf("[WARNING] ACI/FAC FsaRights: FAIL (%s not permitted)\n", kFsaFlag[aci.getFac().getFsaRightsList()[i]].c_str()); + printf("[WARNING] ACI/FAC FsaRights: FAIL (%s not permitted)\n", kFsaFlag[aci.getFileSystemAccessControl().getFsaRightsList()[i]].c_str()); } } - for (size_t i = 0; i < aci.getFac().getContentOwnerIdList().size(); i++) + for (size_t i = 0; i < aci.getFileSystemAccessControl().getContentOwnerIdList().size(); i++) { bool rightFound = false; - for (size_t j = 0; j < acid.getFac().getContentOwnerIdList().size() && rightFound == false; j++) + for (size_t j = 0; j < acid.getFileSystemAccessControl().getContentOwnerIdList().size() && rightFound == false; j++) { - if (aci.getFac().getContentOwnerIdList()[i] == acid.getFac().getContentOwnerIdList()[j]) + if (aci.getFileSystemAccessControl().getContentOwnerIdList()[i] == acid.getFileSystemAccessControl().getContentOwnerIdList()[j]) rightFound = true; } if (rightFound == false) { - printf("[WARNING] ACI/FAC ContentOwnerId: FAIL (%08x not permitted)\n", aci.getFac().getContentOwnerIdList()[i]); + printf("[WARNING] ACI/FAC ContentOwnerId: FAIL (%08x not permitted)\n", aci.getFileSystemAccessControl().getContentOwnerIdList()[i]); } } - for (size_t i = 0; i < aci.getFac().getSaveDataOwnerIdList().size(); i++) + for (size_t i = 0; i < aci.getFileSystemAccessControl().getSaveDataOwnerIdList().size(); i++) { bool rightFound = false; - for (size_t j = 0; j < acid.getFac().getSaveDataOwnerIdList().size() && rightFound == false; j++) + for (size_t j = 0; j < acid.getFileSystemAccessControl().getSaveDataOwnerIdList().size() && rightFound == false; j++) { - if (aci.getFac().getSaveDataOwnerIdList()[i] == acid.getFac().getSaveDataOwnerIdList()[j]) + if (aci.getFileSystemAccessControl().getSaveDataOwnerIdList()[i] == acid.getFileSystemAccessControl().getSaveDataOwnerIdList()[j]) rightFound = true; } if (rightFound == false) { - printf("[WARNING] ACI/FAC ContentOwnerId: FAIL (%08x not permitted)\n", aci.getFac().getSaveDataOwnerIdList()[i]); + printf("[WARNING] ACI/FAC ContentOwnerId: FAIL (%08x not permitted)\n", aci.getFileSystemAccessControl().getSaveDataOwnerIdList()[i]); } } // check SAC - for (size_t i = 0; i < aci.getSac().getServiceList().size(); i++) + for (size_t i = 0; i < aci.getServiceAccessControl().getServiceList().size(); i++) { bool rightFound = false; - for (size_t j = 0; j < acid.getSac().getServiceList().size() && rightFound == false; j++) + for (size_t j = 0; j < acid.getServiceAccessControl().getServiceList().size() && rightFound == false; j++) { - if (aci.getSac().getServiceList()[i] == acid.getSac().getServiceList()[j]) + if (aci.getServiceAccessControl().getServiceList()[i] == acid.getServiceAccessControl().getServiceList()[j]) rightFound = true; } if (rightFound == false) { - printf("[WARNING] ACI/SAC ServiceList: FAIL (%s%s not permitted)\n", aci.getSac().getServiceList()[i].getName().c_str(), aci.getSac().getServiceList()[i].isServer()? " (Server)" : ""); + printf("[WARNING] ACI/SAC ServiceList: FAIL (%s%s not permitted)\n", aci.getServiceAccessControl().getServiceList()[i].getName().c_str(), aci.getServiceAccessControl().getServiceList()[i].isServer()? " (Server)" : ""); } } // check KC // check thread info - if (aci.getKc().getThreadInfo().getMaxCpuId() != acid.getKc().getThreadInfo().getMaxCpuId()) + if (aci.getKernelCapabilities().getThreadInfo().getMaxCpuId() != acid.getKernelCapabilities().getThreadInfo().getMaxCpuId()) { - printf("[WARNING] ACI/KC ThreadInfo/MaxCpuId: FAIL (%d not permitted)\n", aci.getKc().getThreadInfo().getMaxCpuId()); + printf("[WARNING] ACI/KC ThreadInfo/MaxCpuId: FAIL (%d not permitted)\n", aci.getKernelCapabilities().getThreadInfo().getMaxCpuId()); } - if (aci.getKc().getThreadInfo().getMinCpuId() != acid.getKc().getThreadInfo().getMinCpuId()) + if (aci.getKernelCapabilities().getThreadInfo().getMinCpuId() != acid.getKernelCapabilities().getThreadInfo().getMinCpuId()) { - printf("[WARNING] ACI/KC ThreadInfo/MinCpuId: FAIL (%d not permitted)\n", aci.getKc().getThreadInfo().getMinCpuId()); + printf("[WARNING] ACI/KC ThreadInfo/MinCpuId: FAIL (%d not permitted)\n", aci.getKernelCapabilities().getThreadInfo().getMinCpuId()); } - if (aci.getKc().getThreadInfo().getMaxPriority() != acid.getKc().getThreadInfo().getMaxPriority()) + if (aci.getKernelCapabilities().getThreadInfo().getMaxPriority() != acid.getKernelCapabilities().getThreadInfo().getMaxPriority()) { - printf("[WARNING] ACI/KC ThreadInfo/MaxPriority: FAIL (%d not permitted)\n", aci.getKc().getThreadInfo().getMaxPriority()); + printf("[WARNING] ACI/KC ThreadInfo/MaxPriority: FAIL (%d not permitted)\n", aci.getKernelCapabilities().getThreadInfo().getMaxPriority()); } - if (aci.getKc().getThreadInfo().getMinPriority() != acid.getKc().getThreadInfo().getMinPriority()) + if (aci.getKernelCapabilities().getThreadInfo().getMinPriority() != acid.getKernelCapabilities().getThreadInfo().getMinPriority()) { - printf("[WARNING] ACI/KC ThreadInfo/MinPriority: FAIL (%d not permitted)\n", aci.getKc().getThreadInfo().getMinPriority()); + printf("[WARNING] ACI/KC ThreadInfo/MinPriority: FAIL (%d not permitted)\n", aci.getKernelCapabilities().getThreadInfo().getMinPriority()); } // check system calls - for (size_t i = 0; i < aci.getKc().getSystemCalls().getSystemCalls().size(); i++) + for (size_t i = 0; i < aci.getKernelCapabilities().getSystemCalls().getSystemCalls().size(); i++) { bool rightFound = false; - for (size_t j = 0; j < acid.getKc().getSystemCalls().getSystemCalls().size() && rightFound == false; j++) + for (size_t j = 0; j < acid.getKernelCapabilities().getSystemCalls().getSystemCalls().size() && rightFound == false; j++) { - if (aci.getKc().getSystemCalls().getSystemCalls()[i] == acid.getKc().getSystemCalls().getSystemCalls()[j]) + if (aci.getKernelCapabilities().getSystemCalls().getSystemCalls()[i] == acid.getKernelCapabilities().getSystemCalls().getSystemCalls()[j]) rightFound = true; } if (rightFound == false) { - printf("[WARNING] ACI/KC SystemCallList: FAIL (%s not permitted)\n", kSysCall[aci.getKc().getSystemCalls().getSystemCalls()[i]].c_str()); + printf("[WARNING] ACI/KC SystemCallList: FAIL (%s not permitted)\n", kSysCall[aci.getKernelCapabilities().getSystemCalls().getSystemCalls()[i]].c_str()); } } // check memory maps - for (size_t i = 0; i < aci.getKc().getMemoryMaps().getMemoryMaps().size(); i++) + for (size_t i = 0; i < aci.getKernelCapabilities().getMemoryMaps().getMemoryMaps().size(); i++) { bool rightFound = false; - for (size_t j = 0; j < acid.getKc().getMemoryMaps().getMemoryMaps().size() && rightFound == false; j++) + for (size_t j = 0; j < acid.getKernelCapabilities().getMemoryMaps().getMemoryMaps().size() && rightFound == false; j++) { - if (aci.getKc().getMemoryMaps().getMemoryMaps()[i] == acid.getKc().getMemoryMaps().getMemoryMaps()[j]) + if (aci.getKernelCapabilities().getMemoryMaps().getMemoryMaps()[i] == acid.getKernelCapabilities().getMemoryMaps().getMemoryMaps()[j]) rightFound = true; } if (rightFound == false) { - const nx::MemoryMappingHandler::sMemoryMapping& map = aci.getKc().getMemoryMaps().getMemoryMaps()[i]; + const nx::MemoryMappingHandler::sMemoryMapping& map = aci.getKernelCapabilities().getMemoryMaps().getMemoryMaps()[i]; printf("[WARNING] ACI/KC MemoryMap: FAIL (0x%016" PRIx64 " - 0x%016" PRIx64 " (perm=%s) (type=%s) not permitted)\n", (uint64_t)map.addr << 12, ((uint64_t)(map.addr + map.size) << 12) - 1, kMemMapPerm[map.perm].c_str(), kMemMapType[map.type].c_str()); } } - for (size_t i = 0; i < aci.getKc().getMemoryMaps().getIoMemoryMaps().size(); i++) + for (size_t i = 0; i < aci.getKernelCapabilities().getMemoryMaps().getIoMemoryMaps().size(); i++) { bool rightFound = false; - for (size_t j = 0; j < acid.getKc().getMemoryMaps().getIoMemoryMaps().size() && rightFound == false; j++) + for (size_t j = 0; j < acid.getKernelCapabilities().getMemoryMaps().getIoMemoryMaps().size() && rightFound == false; j++) { - if (aci.getKc().getMemoryMaps().getIoMemoryMaps()[i] == acid.getKc().getMemoryMaps().getIoMemoryMaps()[j]) + if (aci.getKernelCapabilities().getMemoryMaps().getIoMemoryMaps()[i] == acid.getKernelCapabilities().getMemoryMaps().getIoMemoryMaps()[j]) rightFound = true; } if (rightFound == false) { - const nx::MemoryMappingHandler::sMemoryMapping& map = aci.getKc().getMemoryMaps().getIoMemoryMaps()[i]; + const nx::MemoryMappingHandler::sMemoryMapping& map = aci.getKernelCapabilities().getMemoryMaps().getIoMemoryMaps()[i]; printf("[WARNING] ACI/KC IoMemoryMap: FAIL (0x%016" PRIx64 " - 0x%016" PRIx64 " (perm=%s) (type=%s) not permitted)\n", (uint64_t)map.addr << 12, ((uint64_t)(map.addr + map.size) << 12) - 1, kMemMapPerm[map.perm].c_str(), kMemMapType[map.type].c_str()); } } // check interupts - for (size_t i = 0; i < aci.getKc().getInterupts().getInteruptList().size(); i++) + for (size_t i = 0; i < aci.getKernelCapabilities().getInterupts().getInteruptList().size(); i++) { bool rightFound = false; - for (size_t j = 0; j < acid.getKc().getInterupts().getInteruptList().size() && rightFound == false; j++) + for (size_t j = 0; j < acid.getKernelCapabilities().getInterupts().getInteruptList().size() && rightFound == false; j++) { - if (aci.getKc().getInterupts().getInteruptList()[i] == acid.getKc().getInterupts().getInteruptList()[j]) + if (aci.getKernelCapabilities().getInterupts().getInteruptList()[i] == acid.getKernelCapabilities().getInterupts().getInteruptList()[j]) rightFound = true; } if (rightFound == false) { - printf("[WARNING] ACI/KC InteruptsList: FAIL (0x%0x not permitted)\n", aci.getKc().getInterupts().getInteruptList()[i]); + printf("[WARNING] ACI/KC InteruptsList: FAIL (0x%0x not permitted)\n", aci.getKernelCapabilities().getInterupts().getInteruptList()[i]); } } // check misc params - if (aci.getKc().getMiscParams().getProgramType() != acid.getKc().getMiscParams().getProgramType()) + if (aci.getKernelCapabilities().getMiscParams().getProgramType() != acid.getKernelCapabilities().getMiscParams().getProgramType()) { - printf("[WARNING] ACI/KC ProgramType: FAIL (%d not permitted)\n", aci.getKc().getMiscParams().getProgramType()); + printf("[WARNING] ACI/KC ProgramType: FAIL (%d not permitted)\n", aci.getKernelCapabilities().getMiscParams().getProgramType()); } // check kernel version - uint32_t aciKernelVersion = (uint32_t)aci.getKc().getKernelVersion().getVerMajor() << 16 | (uint32_t)aci.getKc().getKernelVersion().getVerMinor(); - uint32_t acidKernelVersion = (uint32_t)acid.getKc().getKernelVersion().getVerMajor() << 16 | (uint32_t)acid.getKc().getKernelVersion().getVerMinor(); + uint32_t aciKernelVersion = (uint32_t)aci.getKernelCapabilities().getKernelVersion().getVerMajor() << 16 | (uint32_t)aci.getKernelCapabilities().getKernelVersion().getVerMinor(); + uint32_t acidKernelVersion = (uint32_t)acid.getKernelCapabilities().getKernelVersion().getVerMajor() << 16 | (uint32_t)acid.getKernelCapabilities().getKernelVersion().getVerMinor(); if (aciKernelVersion < acidKernelVersion) { - printf("[WARNING] ACI/KC RequiredKernelVersion: FAIL (%d.%d not permitted)\n", aci.getKc().getKernelVersion().getVerMajor(), aci.getKc().getKernelVersion().getVerMinor()); + printf("[WARNING] ACI/KC RequiredKernelVersion: FAIL (%d.%d not permitted)\n", aci.getKernelCapabilities().getKernelVersion().getVerMajor(), aci.getKernelCapabilities().getKernelVersion().getVerMinor()); } // check handle table size - if (aci.getKc().getHandleTableSize().getHandleTableSize() > acid.getKc().getHandleTableSize().getHandleTableSize()) + if (aci.getKernelCapabilities().getHandleTableSize().getHandleTableSize() > acid.getKernelCapabilities().getHandleTableSize().getHandleTableSize()) { - printf("[WARNING] ACI/KC HandleTableSize: FAIL (0x%x too large)\n", aci.getKc().getHandleTableSize().getHandleTableSize()); + printf("[WARNING] ACI/KC HandleTableSize: FAIL (0x%x too large)\n", aci.getKernelCapabilities().getHandleTableSize().getHandleTableSize()); } // check misc flags - for (size_t i = 0; i < aci.getKc().getMiscFlags().getFlagList().size(); i++) + for (size_t i = 0; i < aci.getKernelCapabilities().getMiscFlags().getFlagList().size(); i++) { bool rightFound = false; - for (size_t j = 0; j < acid.getKc().getMiscFlags().getFlagList().size() && rightFound == false; j++) + for (size_t j = 0; j < acid.getKernelCapabilities().getMiscFlags().getFlagList().size() && rightFound == false; j++) { - if (aci.getKc().getMiscFlags().getFlagList()[i] == acid.getKc().getMiscFlags().getFlagList()[j]) + if (aci.getKernelCapabilities().getMiscFlags().getFlagList()[i] == acid.getKernelCapabilities().getMiscFlags().getFlagList()[j]) rightFound = true; } if (rightFound == false) { - printf("[WARNING] ACI/KC MiscFlag: FAIL (%s not permitted)\n", kMiscFlag[aci.getKc().getMiscFlags().getFlagList()[i]].c_str()); + printf("[WARNING] ACI/KC MiscFlag: FAIL (%s not permitted)\n", kMiscFlag[aci.getKernelCapabilities().getMiscFlags().getFlagList()[i]].c_str()); } } } @@ -514,26 +548,26 @@ void NpdmProcess::displayNpdmHeader(const nx::NpdmHeader& hdr) } } -void NpdmProcess::displayAciHdr(const nx::AciHeader& aci) +void NpdmProcess::displayAciHdr(const nx::AccessControlInfoBinary& aci) { printf("[Access Control Info]\n"); - printf(" ACI Type: %s\n", kAciType[aci.getAciType()].c_str()); - if (aci.getAciType() == nx::AciBinary::TYPE_ACI0) - { - printf(" ProgramID: %016" PRIx64 "\n", aci.getProgramId()); - } - else if (aci.getAciType() == nx::AciBinary::TYPE_ACID) - { + printf(" ProgramID: 0x%016" PRIx64 "\n", aci.getProgramId()); +} - printf(" ACID Size: %" PRIx64 "\n", (uint64_t)aci.getAcidSize()); +void NpdmProcess::displayAciDescHdr(const nx::AccessControlInfoDescBinary& acid) +{ + printf("[Access Control Info Desc]\n"); + if (acid.getFlagList().size() > 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + { printf(" Flags: \n"); - printf(" Production: %s\n", aci.isProduction() ? "TRUE" : "FALSE"); - printf(" UnqualifiedApproval: %s\n", aci.isUnqualifiedApproval() ? "TRUE" : "FALSE"); - printf(" ProgramID Restriction\n"); - printf(" Min: %016" PRIx64 "\n", aci.getProgramIdMin()); - printf(" Max: %016" PRIx64 "\n", aci.getProgramIdMax()); - + for (size_t i = 0; i < acid.getFlagList().size(); i++) + { + printf(" %s (%d)\n", kAcidFlag[acid.getFlagList()[i]].c_str(), acid.getFlagList()[i]); + } } + printf(" ProgramID Restriction\n"); + printf(" Min: 0x%016" PRIx64 "\n", acid.getProgramIdRestrict().min); + printf(" Max: 0x%016" PRIx64 "\n", acid.getProgramIdRestrict().max); } void NpdmProcess::displayFac(const nx::FacBinary& fac) diff --git a/programs/nstool/source/NpdmProcess.h b/programs/nstool/source/NpdmProcess.h index 4500d0f..b0b977c 100644 --- a/programs/nstool/source/NpdmProcess.h +++ b/programs/nstool/source/NpdmProcess.h @@ -32,11 +32,12 @@ private: nx::NpdmBinary mNpdm; - void validateAcidSignature(const nx::AcidBinary& acid); - void validateAciFromAcid(const nx::AciBinary& aci, const nx::AcidBinary& acid); + void validateAcidSignature(const nx::AccessControlInfoDescBinary& acid); + void validateAciFromAcid(const nx::AccessControlInfoBinary& aci, const nx::AccessControlInfoDescBinary& acid); void displayNpdmHeader(const nx::NpdmHeader& hdr); - void displayAciHdr(const nx::AciHeader& aci); + void displayAciHdr(const nx::AccessControlInfoBinary& aci); + void displayAciDescHdr(const nx::AccessControlInfoDescBinary& aci); void displayFac(const nx::FacBinary& fac); void displaySac(const nx::SacBinary& sac); void displayKernelCap(const nx::KcBinary& kern);