[nx|nstool] Replaced AciHeader,AciBinary,AcidBinary.

This commit is contained in:
jakcron 2018-06-27 13:03:46 +08:00
parent 726c272f04
commit b7207e8429
16 changed files with 726 additions and 885 deletions

View file

@ -0,0 +1,53 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/ISerialisable.h>
#include <nx/aci.h>
#include <nx/FacBinary.h>
#include <nx/SacBinary.h>
#include <nx/KcBinary.h>
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<byte_t>& 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<byte_t> mRawBinary;
// variables
uint64_t mProgramId;
nx::FacBinary mFileSystemAccessControl;
nx::SacBinary mServiceAccessControl;
nx::KcBinary mKernelCapabilities;
};
}

View file

@ -0,0 +1,88 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/List.h>
#include <fnd/ISerialisable.h>
#include <nx/aci.h>
#include <nx/FacBinary.h>
#include <nx/SacBinary.h>
#include <nx/KcBinary.h>
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<byte_t>& 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<aci::Flag>& getFlagList() const;
void setFlagList(const fnd::List<aci::Flag>& 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<byte_t> mRawBinary;
// variables
crypto::rsa::sRsa2048Key mNcaHeaderSignature2Key;
fnd::List<aci::Flag> mFlags;
sProgramIdRestrict mProgramIdRestrict;
nx::FacBinary mFileSystemAccessControl;
nx::SacBinary mServiceAccessControl;
nx::KcBinary mKernelCapabilities;
};
}

View file

@ -1,51 +0,0 @@
#pragma once
#include <string>
#include <fnd/List.h>
#include <nx/AciHeader.h>
#include <nx/FacBinary.h>
#include <nx/SacBinary.h>
#include <nx/KcBinary.h>
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<byte_t>& 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<byte_t> mRawBinary;
// variables
FacBinary mFac;
SacBinary mSac;
KcBinary mKc;
};
}

View file

@ -1,109 +0,0 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/ISerialisable.h>
#include <nx/aci.h>
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<byte_t>& 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<byte_t> 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();
};
}

View file

@ -1,42 +0,0 @@
#pragma once
#include <string>
#include <nx/AciBinary.h>
#include <crypto/rsa.h>
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<byte_t>& 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<byte_t> mRawBinary;
// variables
crypto::rsa::sRsa2048Key mEmbeddedPublicKey;
};
}

View file

@ -2,8 +2,8 @@
#include <string>
#include <fnd/List.h>
#include <nx/NpdmHeader.h>
#include <nx/AciBinary.h>
#include <nx/AcidBinary.h>
#include <nx/AccessControlInfoBinary.h>
#include <nx/AccessControlInfoDescBinary.h>
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<byte_t> mRawBinary;
// variables
AciBinary mAci;
AcidBinary mAcid;
AccessControlInfoBinary mAci;
AccessControlInfoDescBinary mAcid;
};
}

View file

@ -1,5 +1,6 @@
#pragma once
#include <fnd/types.h>
#include <crypto/rsa.h>
#include <nx/macro.h>
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;
byte_t reserved_00[0xC];
le_uint64_t program_id;
} program_id_info;
struct sAciSection
byte_t reserved_01[0x8];
sAciSection fac;
sAciSection sac;
sAciSection kc;
};
struct sAciDescHeader
{
le_uint32_t offset; // aligned by 0x10 from the last one
le_uint32_t size;
} fac, sac, kc;
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)
}

View file

@ -0,0 +1,172 @@
#include <nx/AccessControlInfoBinary.h>
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<byte_t>& 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;
}

View file

@ -0,0 +1,250 @@
#include <nx/AccessControlInfoDescBinary.h>
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<byte_t>& 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::aci::Flag>& nx::AccessControlInfoDescBinary::getFlagList() const
{
return mFlags;
}
void nx::AccessControlInfoDescBinary::setFlagList(const fnd::List<nx::aci::Flag>& 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;
}

View file

@ -1,139 +0,0 @@
#include <nx/AciBinary.h>
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<byte_t>& 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;
}

View file

@ -1,302 +0,0 @@
#include <nx/AciHeader.h>
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<byte_t>& 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);
}

View file

@ -1,123 +0,0 @@
#include <nx/AcidBinary.h>
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<byte_t>& 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;
}

View file

@ -1,5 +1,7 @@
#include <nx/NpdmBinary.h>
#include <fnd/SimpleTextOutput.h>
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;
}

View file

@ -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");
}

View file

@ -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)

View file

@ -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);