summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/file_sys/content_archive.cpp44
-rw-r--r--src/core/file_sys/content_archive.h2
2 files changed, 39 insertions, 7 deletions
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 79e70f6ef..3529166ac 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -76,12 +76,17 @@ bool IsValidNCA(const NCAHeader& header) {
76 return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); 76 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
77} 77}
78 78
79boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const { 79u8 NCA::GetCryptoRevision() const {
80 u8 master_key_id = header.crypto_type; 80 u8 master_key_id = header.crypto_type;
81 if (header.crypto_type_2 > master_key_id) 81 if (header.crypto_type_2 > master_key_id)
82 master_key_id = header.crypto_type_2; 82 master_key_id = header.crypto_type_2;
83 if (master_key_id > 0) 83 if (master_key_id > 0)
84 --master_key_id; 84 --master_key_id;
85 return master_key_id;
86}
87
88boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
89 const auto master_key_id = GetCryptoRevision();
85 90
86 if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index)) 91 if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
87 return boost::none; 92 return boost::none;
@@ -108,33 +113,58 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
108 return out; 113 return out;
109} 114}
110 115
111VirtualFile NCA::Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const { 116boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const {
117 const auto master_key_id = GetCryptoRevision();
118
119 u128 rights_id{};
120 memcpy(rights_id.data(), header.rights_id.data(), 16);
121 if (rights_id == u128{})
122 return boost::none;
123
124 auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
125 if (titlekey == Core::Crypto::Key128{})
126 return boost::none;
127 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
128 keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB);
129 cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt);
130
131 return titlekey;
132}
133
134VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) const {
112 if (!encrypted) 135 if (!encrypted)
113 return in; 136 return in;
114 137
115 switch (header.raw.header.crypto_type) { 138 switch (s_header.raw.header.crypto_type) {
116 case NCASectionCryptoType::NONE: 139 case NCASectionCryptoType::NONE:
117 LOG_DEBUG(Crypto, "called with mode=NONE"); 140 LOG_DEBUG(Crypto, "called with mode=NONE");
118 return in; 141 return in;
119 case NCASectionCryptoType::CTR: 142 case NCASectionCryptoType::CTR:
120 LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); 143 LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
121 { 144 {
122 const auto key = GetKeyAreaKey(NCASectionCryptoType::CTR); 145 boost::optional<Core::Crypto::Key128> key = boost::none;
146 if (std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
147 [](char c) { return c == 0; }) == header.rights_id.end()) {
148 key = GetKeyAreaKey(NCASectionCryptoType::CTR);
149 } else {
150 key = GetTitlekey();
151 }
152
123 if (key == boost::none) 153 if (key == boost::none)
124 return nullptr; 154 return nullptr;
125 auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>( 155 auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(
126 std::move(in), key.value(), starting_offset); 156 std::move(in), key.value(), starting_offset);
127 std::vector<u8> iv(16); 157 std::vector<u8> iv(16);
128 for (u8 i = 0; i < 8; ++i) 158 for (u8 i = 0; i < 8; ++i)
129 iv[i] = header.raw.section_ctr[0x8 - i - 1]; 159 iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
130 out->SetIV(iv); 160 out->SetIV(iv);
131 return std::static_pointer_cast<VfsFile>(out); 161 return std::static_pointer_cast<VfsFile>(out);
132 } 162 }
133 case NCASectionCryptoType::XTS: 163 case NCASectionCryptoType::XTS:
134 // TODO(DarkLordZach): Implement XTSEncryptionLayer and title key encryption. 164 // TODO(DarkLordZach): Implement XTSEncryptionLayer.
135 default: 165 default:
136 LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}", 166 LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}",
137 static_cast<u8>(header.raw.header.crypto_type)); 167 static_cast<u8>(s_header.raw.header.crypto_type));
138 return nullptr; 168 return nullptr;
139 } 169 }
140} 170}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 6492163b5..a8879d9a8 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -95,7 +95,9 @@ protected:
95 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 95 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
96 96
97private: 97private:
98 u8 GetCryptoRevision() const;
98 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; 99 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
100 boost::optional<Core::Crypto::Key128> GetTitlekey() const;
99 VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const; 101 VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const;
100 102
101 std::vector<VirtualDir> dirs; 103 std::vector<VirtualDir> dirs;