add personalised -> common convert. patch bad common tickets. fix yati installing nca's if already installed.
- dumped nsp now have the tik/cert at the end of the file table, rather than the beginning. - dumped nsp patches the ticket if needed (no personalised dumping yet). - installing titles will now patch the ticket, performing personalised -> common convert if needed, as well as fixing bad common tickets. - yati no longer tries to install ncas if they already exist. - ticket only option now actually works. - fixed some translations. - removed unused error codes.
This commit is contained in:
@@ -303,6 +303,7 @@ public:
|
||||
option::OptionBool m_skip_rsa_header_fixed_key_verify{INI_SECTION, "skip_rsa_header_fixed_key_verify", false};
|
||||
option::OptionBool m_skip_rsa_npdm_fixed_key_verify{INI_SECTION, "skip_rsa_npdm_fixed_key_verify", false};
|
||||
option::OptionBool m_ignore_distribution_bit{INI_SECTION, "ignore_distribution_bit", false};
|
||||
option::OptionBool m_convert_to_common_ticket{INI_SECTION, "convert_to_common_ticket", true};
|
||||
option::OptionBool m_convert_to_standard_crypto{INI_SECTION, "convert_to_standard_crypto", false};
|
||||
option::OptionBool m_lower_master_key{INI_SECTION, "lower_master_key", false};
|
||||
option::OptionBool m_lower_system_version{INI_SECTION, "lower_system_version", false};
|
||||
@@ -313,6 +314,7 @@ public:
|
||||
option::OptionBool m_dump_trim_xci{"dump", "trim_xci", false};
|
||||
option::OptionBool m_dump_label_trim_xci{"dump", "label_trim_xci", false};
|
||||
option::OptionBool m_dump_usb_transfer_stream{"dump", "usb_transfer_stream", true};
|
||||
option::OptionBool m_dump_convert_to_common_ticket{"dump", "convert_to_common_ticket", true};
|
||||
|
||||
// todo: move this into it's own menu
|
||||
option::OptionLong m_text_scroll_speed{"accessibility", "text_scroll_speed", 1}; // normal
|
||||
|
||||
@@ -582,6 +582,11 @@ enum class SphairaResult : Result {
|
||||
EsFailedDecryptPersonalisedTicket,
|
||||
EsBadDecryptedPersonalisedTicketSize,
|
||||
EsBadTicketSize,
|
||||
// found ticket has missmatching rights_id from it's name.
|
||||
EsInvalidTicketBadRightsId,
|
||||
EsInvalidTicketFromatVersion,
|
||||
EsInvalidTicketKeyType,
|
||||
EsInvalidTicketKeyRevision,
|
||||
|
||||
OwoBadArgs,
|
||||
|
||||
@@ -630,21 +635,12 @@ enum class SphairaResult : Result {
|
||||
YatiTicketNotFound,
|
||||
// found ticket has missmatching rights_id from it's name.
|
||||
YatiInvalidTicketBadRightsId,
|
||||
YatiInvalidTicketVersion,
|
||||
YatiInvalidTicketKeyType,
|
||||
YatiInvalidTicketKeyRevision,
|
||||
// cert not found for the ticket.
|
||||
YatiCertNotFound,
|
||||
// unable to fetch header from ncm database.
|
||||
YatiNcmDbCorruptHeader,
|
||||
// unable to total infos from ncm database.
|
||||
YatiNcmDbCorruptInfos,
|
||||
|
||||
// found ticket has missmatching rights_id from it's name.
|
||||
TicketInvalidTicketBadRightsId,
|
||||
TicketInvalidTicketVersion,
|
||||
TicketInvalidTicketKeyType,
|
||||
TicketInvalidTicketKeyRevision,
|
||||
};
|
||||
|
||||
#define MAKE_SPHAIRA_RESULT_ENUM(x) Result_##x = MAKERESULT(Module_Sphaira, (Result)SphairaResult::x)
|
||||
@@ -719,6 +715,10 @@ enum : Result {
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsFailedDecryptPersonalisedTicket),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsBadDecryptedPersonalisedTicketSize),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsBadTicketSize),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsInvalidTicketBadRightsId),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsInvalidTicketFromatVersion),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsInvalidTicketKeyType),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(EsInvalidTicketKeyRevision),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(OwoBadArgs),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbCancelled),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadMagic),
|
||||
@@ -750,16 +750,9 @@ enum : Result {
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidNczZstdError),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiTicketNotFound),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidTicketBadRightsId),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidTicketVersion),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidTicketKeyType),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiInvalidTicketKeyRevision),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiCertNotFound),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiNcmDbCorruptHeader),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(YatiNcmDbCorruptInfos),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(TicketInvalidTicketBadRightsId),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(TicketInvalidTicketVersion),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(TicketInvalidTicketKeyType),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(TicketInvalidTicketKeyRevision),
|
||||
};
|
||||
|
||||
#undef MAKE_SPHAIRA_RESULT_ENUM
|
||||
|
||||
@@ -2,49 +2,132 @@
|
||||
|
||||
#include <switch.h>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include "ncm.hpp"
|
||||
#include "keys.hpp"
|
||||
|
||||
namespace sphaira::es {
|
||||
|
||||
enum { TicketModule = 507 };
|
||||
|
||||
enum : Result {
|
||||
// found ticket has missmatching rights_id from it's name.
|
||||
Result_InvalidTicketBadRightsId = MAKERESULT(TicketModule, 71),
|
||||
Result_InvalidTicketVersion = MAKERESULT(TicketModule, 72),
|
||||
Result_InvalidTicketKeyType = MAKERESULT(TicketModule, 73),
|
||||
Result_InvalidTicketKeyRevision = MAKERESULT(TicketModule, 74),
|
||||
enum TitleKeyType : u8 {
|
||||
TitleKeyType_Common = 0,
|
||||
TitleKeyType_Personalized = 1,
|
||||
};
|
||||
|
||||
enum TicketSigantureType {
|
||||
TicketSigantureType_RSA_4096_SHA1 = 0x010000,
|
||||
TicketSigantureType_RSA_2048_SHA1 = 0x010001,
|
||||
TicketSigantureType_ECDSA_SHA1 = 0x010002,
|
||||
TicketSigantureType_RSA_4096_SHA256 = 0x010003,
|
||||
TicketSigantureType_RSA_2048_SHA256 = 0x010004,
|
||||
TicketSigantureType_ECDSA_SHA256 = 0x010005,
|
||||
TicketSigantureType_HMAC_SHA1_160 = 0x010006,
|
||||
enum SigType : u32 {
|
||||
SigType_Rsa4096Sha1 = 65536,
|
||||
SigType_Rsa2048Sha1 = 65537,
|
||||
SigType_Ecc480Sha1 = 65538,
|
||||
SigType_Rsa4096Sha256 = 65539,
|
||||
SigType_Rsa2048Sha256 = 65540,
|
||||
SigType_Ecc480Sha256 = 65541,
|
||||
SigType_Hmac160Sha1 = 65542
|
||||
};
|
||||
|
||||
enum TicketTitleKeyType {
|
||||
TicketTitleKeyType_Common = 0,
|
||||
TicketTitleKeyType_Personalized = 1,
|
||||
enum PubKeyType : u32 {
|
||||
PubKeyType_Rsa4096 = 0,
|
||||
PubKeyType_Rsa2048 = 1,
|
||||
PubKeyType_Ecc480 = 2
|
||||
};
|
||||
|
||||
enum TicketPropertiesBitfield {
|
||||
TicketPropertiesBitfield_None = 0,
|
||||
// temporary ticket, removed on restart
|
||||
TicketPropertiesBitfield_Temporary = 1 << 4,
|
||||
struct SignatureBlockRsa4096 {
|
||||
SigType sig_type;
|
||||
u8 sign[0x200];
|
||||
u8 reserved_1[0x3C];
|
||||
};
|
||||
static_assert(sizeof(SignatureBlockRsa4096) == 0x240);
|
||||
|
||||
struct SignatureBlockRsa2048 {
|
||||
SigType sig_type;
|
||||
u8 sign[0x100];
|
||||
u8 reserved_1[0x3C];
|
||||
};
|
||||
static_assert(sizeof(SignatureBlockRsa2048) == 0x140);
|
||||
|
||||
struct SignatureBlockEcc480 {
|
||||
SigType sig_type;
|
||||
u8 sign[0x3C];
|
||||
u8 reserved_1[0x40];
|
||||
};
|
||||
static_assert(sizeof(SignatureBlockEcc480) == 0x80);
|
||||
|
||||
struct SignatureBlockHmac160 {
|
||||
SigType sig_type;
|
||||
u8 sign[0x14];
|
||||
u8 reserved_1[0x28];
|
||||
};
|
||||
static_assert(sizeof(SignatureBlockHmac160) == 0x40);
|
||||
|
||||
struct CertHeader {
|
||||
char issuer[0x40];
|
||||
PubKeyType pub_key_type;
|
||||
char subject[0x40]; /* ServerId, DeviceId */
|
||||
u32 date;
|
||||
};
|
||||
static_assert(sizeof(CertHeader) == 0x88);
|
||||
|
||||
struct PublicKeyBlockRsa4096 {
|
||||
u8 public_key[0x200];
|
||||
u32 public_exponent;
|
||||
u8 reserved_1[0x34];
|
||||
};
|
||||
static_assert(sizeof(PublicKeyBlockRsa4096) == 0x238);
|
||||
|
||||
struct PublicKeyBlockRsa2048 {
|
||||
u8 public_key[0x100];
|
||||
u32 public_exponent;
|
||||
u8 reserved_1[0x34];
|
||||
};
|
||||
static_assert(sizeof(PublicKeyBlockRsa2048) == 0x138);
|
||||
|
||||
struct PublicKeyBlockEcc480 {
|
||||
u8 public_key[0x3C];
|
||||
u8 reserved_1[0x3C];
|
||||
};
|
||||
static_assert(sizeof(PublicKeyBlockEcc480) == 0x78);
|
||||
|
||||
template<typename Sig, typename Pub>
|
||||
struct Cert {
|
||||
Sig signature_block;
|
||||
CertHeader cert_header;
|
||||
Pub public_key_block;
|
||||
};
|
||||
|
||||
using CertRsa4096PubRsa4096 = Cert<SignatureBlockRsa4096, PublicKeyBlockRsa4096>;
|
||||
using CertRsa4096PubRsa2048 = Cert<SignatureBlockRsa4096, PublicKeyBlockRsa2048>;
|
||||
using CertRsa4096PubEcc480 = Cert<SignatureBlockRsa4096, PublicKeyBlockEcc480>;
|
||||
|
||||
using CertRsa2048PubRsa4096 = Cert<SignatureBlockRsa2048, PublicKeyBlockRsa4096>;
|
||||
using CertRsa2048PubRsa2048 = Cert<SignatureBlockRsa2048, PublicKeyBlockRsa2048>;
|
||||
using CertRsa2048PubEcc480 = Cert<SignatureBlockRsa2048, PublicKeyBlockEcc480>;
|
||||
|
||||
using CertEcc480PubRsa4096 = Cert<SignatureBlockEcc480, PublicKeyBlockRsa4096>;
|
||||
using CertEcc480PubRsa2048 = Cert<SignatureBlockEcc480, PublicKeyBlockRsa2048>;
|
||||
using CertEcc480PubEcc480 = Cert<SignatureBlockEcc480, PublicKeyBlockEcc480>;
|
||||
|
||||
using CertHmac160PubRsa4096 = Cert<SignatureBlockHmac160, PublicKeyBlockRsa4096>;
|
||||
using CertHmac160PubRsa2048 = Cert<SignatureBlockHmac160, PublicKeyBlockRsa2048>;
|
||||
using CertHmac160PubEcc480 = Cert<SignatureBlockHmac160, PublicKeyBlockEcc480>;
|
||||
|
||||
static_assert(sizeof(CertRsa4096PubRsa4096) == 0x500);
|
||||
static_assert(sizeof(CertRsa4096PubRsa2048) == 0x400);
|
||||
static_assert(sizeof(CertRsa4096PubEcc480) == 0x340);
|
||||
static_assert(sizeof(CertRsa2048PubRsa4096) == 0x400);
|
||||
static_assert(sizeof(CertRsa2048PubRsa2048) == 0x300);
|
||||
static_assert(sizeof(CertRsa2048PubEcc480) == 0x240);
|
||||
static_assert(sizeof(CertEcc480PubRsa4096) == 0x340);
|
||||
static_assert(sizeof(CertEcc480PubRsa2048) == 0x240);
|
||||
static_assert(sizeof(CertEcc480PubEcc480) == 0x180);
|
||||
static_assert(sizeof(CertHmac160PubRsa4096) == 0x300);
|
||||
static_assert(sizeof(CertHmac160PubRsa2048) == 0x200);
|
||||
static_assert(sizeof(CertHmac160PubEcc480) == 0x140);
|
||||
|
||||
struct TicketData {
|
||||
u8 issuer[0x40];
|
||||
char issuer[0x40];
|
||||
u8 title_key_block[0x100];
|
||||
u8 ticket_version1;
|
||||
u8 format_version;
|
||||
u8 title_key_type;
|
||||
u16 ticket_version2;
|
||||
u8 license_type;
|
||||
u16 version;
|
||||
TitleKeyType license_type;
|
||||
u8 master_key_revision;
|
||||
u16 properties_bitfield;
|
||||
u8 _0x148[0x8];
|
||||
@@ -52,10 +135,29 @@ struct TicketData {
|
||||
u64 device_id;
|
||||
FsRightsId rights_id;
|
||||
u32 account_id;
|
||||
u8 _0x174[0xC];
|
||||
u32 sect_total_size;
|
||||
u32 sect_hdr_offset;
|
||||
u16 sect_hdr_count;
|
||||
u16 sect_hdr_entry_size;
|
||||
};
|
||||
static_assert(sizeof(TicketData) == 0x180);
|
||||
|
||||
template<typename Sig>
|
||||
struct Ticket {
|
||||
Sig signature_block;
|
||||
TicketData data;
|
||||
};
|
||||
|
||||
using TicketRsa4096 = Ticket<SignatureBlockRsa4096>;
|
||||
using TicketRsa2048 = Ticket<SignatureBlockRsa2048>;
|
||||
using TicketEcc480 = Ticket<SignatureBlockEcc480>;
|
||||
using TicketHmac160 = Ticket<SignatureBlockHmac160>;
|
||||
|
||||
static_assert(sizeof(TicketRsa4096) == 0x3C0);
|
||||
static_assert(sizeof(TicketRsa2048) == 0x2C0);
|
||||
static_assert(sizeof(TicketEcc480) == 0x200);
|
||||
static_assert(sizeof(TicketHmac160) == 0x1C0);
|
||||
|
||||
struct EticketRsaDeviceKey {
|
||||
u8 ctr[AES_128_KEY_SIZE];
|
||||
u8 private_exponent[0x100];
|
||||
@@ -89,12 +191,16 @@ Result GetCommonTicketAndCertificateSize(u64 *tik_size_out, u64 *cert_size_out,
|
||||
Result GetCommonTicketAndCertificateData(u64 *tik_size_out, u64 *cert_size_out, void* tik_buf, u64 tik_size, void* cert_buf, u64 cert_size, const FsRightsId* rightsId); // [4.0.0+]
|
||||
|
||||
// ticket functions.
|
||||
Result GetTicketDataOffset(std::span<const u8> ticket, u64& out);
|
||||
Result GetTicketDataOffset(std::span<const u8> ticket, u64& out, bool is_cert = false);
|
||||
Result GetTicketData(std::span<const u8> ticket, es::TicketData* out);
|
||||
Result SetTicketData(std::span<u8> ticket, const es::TicketData* in);
|
||||
|
||||
// gets the title key and performs RSA-2048-OAEP if needed.
|
||||
Result GetTitleKey(keys::KeyEntry& out, const TicketData& data, const keys::Keys& keys);
|
||||
Result DecryptTitleKey(keys::KeyEntry& out, u8 key_gen, const keys::Keys& keys);
|
||||
Result PatchTicket(std::span<u8> ticket, const keys::Keys& keys);
|
||||
Result EncryptTitleKey(keys::KeyEntry& out, u8 key_gen, const keys::Keys& keys);
|
||||
|
||||
Result ShouldPatchTicket(const TicketData& data, std::span<const u8> ticket, std::span<const u8> cert_chain, bool patch_personalised, bool& should_patch);
|
||||
Result ShouldPatchTicket(std::span<const u8> ticket, std::span<const u8> cert_chain, bool patch_personalised, bool& should_patch);
|
||||
Result PatchTicket(std::vector<u8>& ticket, std::span<const u8> cert_chain, u8 key_gen, const keys::Keys& keys, bool patch_personalised);
|
||||
|
||||
} // namespace sphaira::es
|
||||
|
||||
@@ -48,6 +48,9 @@ struct Config {
|
||||
// if set, it will ignore the distribution bit in the nca header.
|
||||
bool ignore_distribution_bit{};
|
||||
|
||||
// converts a personalised ticket to common.
|
||||
bool convert_to_common_ticket{};
|
||||
|
||||
// converts titlekey to standard crypto, also known as "ticketless".
|
||||
// this will not work with addon (dlc), so, addon tickets will be installed.
|
||||
bool convert_to_standard_crypto{};
|
||||
@@ -69,6 +72,7 @@ struct ConfigOverride {
|
||||
std::optional<bool> skip_rsa_header_fixed_key_verify{};
|
||||
std::optional<bool> skip_rsa_npdm_fixed_key_verify{};
|
||||
std::optional<bool> ignore_distribution_bit{};
|
||||
std::optional<bool> convert_to_common_ticket{};
|
||||
std::optional<bool> convert_to_standard_crypto{};
|
||||
std::optional<bool> lower_master_key{};
|
||||
std::optional<bool> lower_system_version{};
|
||||
|
||||
Reference in New Issue
Block a user