summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/crypto/key_manager.cpp124
-rw-r--r--src/core/crypto/key_manager.h96
-rw-r--r--src/core/hle/service/es/es.cpp18
3 files changed, 194 insertions, 44 deletions
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index ef139902d..558790a49 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -37,6 +37,7 @@
37namespace Core::Crypto { 37namespace Core::Crypto {
38 38
39constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; 39constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
40constexpr u64 FULL_TICKET_SIZE = 0x400;
40 41
41using namespace Common; 42using namespace Common;
42 43
@@ -55,6 +56,78 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
55 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, 56 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
56}; 57};
57 58
59u64 GetSignatureTypeDataSize(SignatureType type) {
60 switch (type) {
61 case SignatureType::RSA_4096_SHA1:
62 case SignatureType::RSA_4096_SHA256:
63 return 0x200;
64 case SignatureType::RSA_2048_SHA1:
65 case SignatureType::RSA_2048_SHA256:
66 return 0x100;
67 case SignatureType::ECDSA_SHA1:
68 case SignatureType::ECDSA_SHA256:
69 return 0x3C;
70 }
71 UNREACHABLE();
72}
73
74u64 GetSignatureTypePaddingSize(SignatureType type) {
75 switch (type) {
76 case SignatureType::RSA_4096_SHA1:
77 case SignatureType::RSA_4096_SHA256:
78 case SignatureType::RSA_2048_SHA1:
79 case SignatureType::RSA_2048_SHA256:
80 return 0x3C;
81 case SignatureType::ECDSA_SHA1:
82 case SignatureType::ECDSA_SHA256:
83 return 0x40;
84 }
85 UNREACHABLE();
86}
87
88TicketData& Ticket::GetData() {
89 switch (sig_type) {
90 case SignatureType::RSA_4096_SHA1:
91 case SignatureType::RSA_4096_SHA256:
92 return rsa_4096.data;
93 case SignatureType::RSA_2048_SHA1:
94 case SignatureType::RSA_2048_SHA256:
95 return rsa_2048.data;
96 case SignatureType::ECDSA_SHA1:
97 case SignatureType::ECDSA_SHA256:
98 return ecdsa.data;
99 }
100 UNREACHABLE();
101}
102
103const TicketData& Ticket::GetData() const {
104 switch (sig_type) {
105 case SignatureType::RSA_4096_SHA1:
106 case SignatureType::RSA_4096_SHA256:
107 return rsa_4096.data;
108 case SignatureType::RSA_2048_SHA1:
109 case SignatureType::RSA_2048_SHA256:
110 return rsa_2048.data;
111 case SignatureType::ECDSA_SHA1:
112 case SignatureType::ECDSA_SHA256:
113 return ecdsa.data;
114 }
115 UNREACHABLE();
116}
117
118u64 Ticket::GetSize() const {
119 return sizeof(SignatureType) + GetSignatureTypeDataSize(sig_type) +
120 GetSignatureTypePaddingSize(sig_type) + sizeof(TicketData);
121}
122
123Ticket Ticket::SynthesizeCommon(Key128 title_key, std::array<u8, 16> rights_id) {
124 Ticket out{};
125 out.sig_type = SignatureType::RSA_2048_SHA256;
126 out.GetData().rights_id = rights_id;
127 out.GetData().title_key_common = title_key;
128 return out;
129}
130
58Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { 131Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) {
59 Key128 out{}; 132 Key128 out{};
60 133
@@ -259,7 +332,7 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
259 return Loader::ResultStatus::Success; 332 return Loader::ResultStatus::Success;
260} 333}
261 334
262std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) { 335std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) {
263 if (!ticket_save.IsOpen()) 336 if (!ticket_save.IsOpen())
264 return {}; 337 return {};
265 338
@@ -268,14 +341,14 @@ std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
268 return {}; 341 return {};
269 } 342 }
270 343
271 std::vector<TicketRaw> out; 344 std::vector<Ticket> out;
272 for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) { 345 for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) {
273 if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 && 346 if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 &&
274 buffer[offset + 3] == 0x0) { 347 buffer[offset + 3] == 0x0) {
275 out.emplace_back(); 348 out.emplace_back();
276 auto& next = out.back(); 349 auto& next = out.back();
277 std::memcpy(&next, buffer.data() + offset, sizeof(TicketRaw)); 350 std::memcpy(&next, buffer.data() + offset, sizeof(Ticket));
278 offset += next.size(); 351 offset += FULL_TICKET_SIZE;
279 } 352 }
280 } 353 }
281 354
@@ -327,29 +400,25 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
327 return offset; 400 return offset;
328} 401}
329 402
330std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, 403std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
331 const RSAKeyPair<2048>& key) { 404 const RSAKeyPair<2048>& key) {
332 u32 cert_authority; 405 const auto issuer = ticket.GetData().issuer;
333 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority)); 406 if (issuer == std::array<u8, 0x40>{})
334 if (cert_authority == 0)
335 return {}; 407 return {};
336 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) { 408 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') {
337 LOG_INFO(Crypto, 409 LOG_INFO(Crypto,
338 "Attempting to parse ticket with non-standard certificate authority {:08X}.", 410 "Attempting to parse ticket with non-standard certificate authority {:08X}.",
339 cert_authority); 411 issuer);
340 } 412 }
341 413
342 Key128 rights_id; 414 Key128 rights_id = ticket.GetData().rights_id;
343 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
344 415
345 if (rights_id == Key128{}) 416 if (rights_id == Key128{})
346 return {}; 417 return {};
347 418
348 Key128 key_temp{}; 419 if (!std::any_of(ticket.GetData().title_key_common_pad.begin(),
349 420 ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) {
350 if (!std::any_of(ticket.begin() + 0x190, ticket.begin() + 0x280, [](u8 b) { return b != 0; })) { 421 return std::make_pair(rights_id, ticket.GetData().title_key_common);
351 std::memcpy(key_temp.data(), ticket.data() + 0x180, key_temp.size());
352 return std::make_pair(rights_id, key_temp);
353 } 422 }
354 423
355 mbedtls_mpi D; // RSA Private Exponent 424 mbedtls_mpi D; // RSA Private Exponent
@@ -364,7 +433,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
364 433
365 mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size()); 434 mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size());
366 mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size()); 435 mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size());
367 mbedtls_mpi_read_binary(&S, ticket.data() + 0x180, 0x100); 436 mbedtls_mpi_read_binary(&S, ticket.GetData().title_key_block.data(), 0x100);
368 437
369 mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr); 438 mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr);
370 439
@@ -388,6 +457,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
388 return {}; 457 return {};
389 ASSERT(*offset > 0); 458 ASSERT(*offset > 0);
390 459
460 Key128 key_temp{};
391 std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size()); 461 std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size());
392 462
393 return std::make_pair(rights_id, key_temp); 463 return std::make_pair(rights_id, key_temp);
@@ -411,6 +481,16 @@ KeyManager::KeyManager() {
411 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true); 481 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true);
412 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "console.keys", false); 482 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "console.keys", false);
413 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "console.keys_autogenerated", false); 483 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "console.keys_autogenerated", false);
484
485 for (const auto& key : s128_keys) {
486 if (key.first.type == S128KeyType::Titlekey) {
487 u128 rights_id{key.first.field1, key.first.field2};
488 Key128 rights_id_2;
489 std::memcpy(rights_id_2.data(), rights_id.data(), rights_id_2.size());
490 const auto ticket = Ticket::SynthesizeCommon(key.second, rights_id_2);
491 common_tickets.insert_or_assign(rights_id, ticket);
492 }
493 }
414} 494}
415 495
416static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) { 496static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
@@ -1029,15 +1109,15 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
1029 DeriveBase(); 1109 DeriveBase();
1030} 1110}
1031 1111
1032const std::map<u128, TicketRaw>& KeyManager::GetCommonTickets() const { 1112const std::map<u128, Ticket>& KeyManager::GetCommonTickets() const {
1033 return common_tickets; 1113 return common_tickets;
1034} 1114}
1035 1115
1036const std::map<u128, TicketRaw>& KeyManager::GetPersonalizedTickets() const { 1116const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const {
1037 return personal_tickets; 1117 return personal_tickets;
1038} 1118}
1039 1119
1040bool KeyManager::AddTicketCommon(TicketRaw raw) { 1120bool KeyManager::AddTicketCommon(Ticket raw) {
1041 const auto rsa_key = GetETicketRSAKey(); 1121 const auto rsa_key = GetETicketRSAKey();
1042 if (rsa_key == RSAKeyPair<2048>{}) 1122 if (rsa_key == RSAKeyPair<2048>{})
1043 return false; 1123 return false;
@@ -1053,7 +1133,7 @@ bool KeyManager::AddTicketCommon(TicketRaw raw) {
1053 return true; 1133 return true;
1054} 1134}
1055 1135
1056bool KeyManager::AddTicketPersonalized(TicketRaw raw) { 1136bool KeyManager::AddTicketPersonalized(Ticket raw) {
1057 const auto rsa_key = GetETicketRSAKey(); 1137 const auto rsa_key = GetETicketRSAKey();
1058 if (rsa_key == RSAKeyPair<2048>{}) 1138 if (rsa_key == RSAKeyPair<2048>{})
1059 return false; 1139 return false;
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 8a67b172d..ff6bd08e1 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -11,6 +11,7 @@
11 11
12#include <boost/container/flat_map.hpp> 12#include <boost/container/flat_map.hpp>
13#include <fmt/format.h> 13#include <fmt/format.h>
14#include "common/common_funcs.h"
14#include "common/common_types.h" 15#include "common/common_types.h"
15#include "core/crypto/partition_data_manager.h" 16#include "core/crypto/partition_data_manager.h"
16#include "core/file_sys/vfs_types.h" 17#include "core/file_sys/vfs_types.h"
@@ -30,7 +31,76 @@ constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
30using Key128 = std::array<u8, 0x10>; 31using Key128 = std::array<u8, 0x10>;
31using Key256 = std::array<u8, 0x20>; 32using Key256 = std::array<u8, 0x20>;
32using SHA256Hash = std::array<u8, 0x20>; 33using SHA256Hash = std::array<u8, 0x20>;
33using TicketRaw = std::array<u8, 0x400>; 34
35enum class SignatureType {
36 RSA_4096_SHA1 = 0x10000,
37 RSA_2048_SHA1 = 0x10001,
38 ECDSA_SHA1 = 0x10002,
39 RSA_4096_SHA256 = 0x10003,
40 RSA_2048_SHA256 = 0x10004,
41 ECDSA_SHA256 = 0x10005,
42};
43
44u64 GetSignatureTypeDataSize(SignatureType type);
45u64 GetSignatureTypePaddingSize(SignatureType type);
46
47enum class TitleKeyType : u8 {
48 Common = 0,
49 Personalized = 1,
50};
51
52struct TicketData {
53 std::array<u8, 0x40> issuer;
54 union {
55 std::array<u8, 0x100> title_key_block;
56
57 struct {
58 Key128 title_key_common;
59 std::array<u8, 0xF0> title_key_common_pad;
60 };
61 };
62
63 INSERT_PADDING_BYTES(0x1);
64 TitleKeyType type;
65 INSERT_PADDING_BYTES(0x3);
66 u8 revision;
67 INSERT_PADDING_BYTES(0xA);
68 u64 ticket_id;
69 u64 device_id;
70 std::array<u8, 0x10> rights_id;
71 u32 account_id;
72 INSERT_PADDING_BYTES(0x14C);
73};
74static_assert(sizeof(TicketData) == 0x2C0, "TicketData has incorrect size.");
75
76struct Ticket {
77 SignatureType sig_type;
78 union {
79 struct {
80 std::array<u8, 0x200> sig_data;
81 INSERT_PADDING_BYTES(0x3C);
82 TicketData data;
83 } rsa_4096;
84
85 struct {
86 std::array<u8, 0x100> sig_data;
87 INSERT_PADDING_BYTES(0x3C);
88 TicketData data;
89 } rsa_2048;
90
91 struct {
92 std::array<u8, 0x3C> sig_data;
93 INSERT_PADDING_BYTES(0x40);
94 TicketData data;
95 } ecdsa;
96 };
97
98 TicketData& GetData();
99 const TicketData& GetData() const;
100 u64 GetSize() const;
101
102 static Ticket SynthesizeCommon(Key128 title_key, std::array<u8, 0x10> rights_id);
103};
34 104
35static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); 105static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
36static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big."); 106static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big.");
@@ -158,8 +228,8 @@ public:
158 228
159 static bool KeyFileExists(bool title); 229 static bool KeyFileExists(bool title);
160 230
161 // Call before using the sd seed to attempt to derive it if it dosen't exist. Needs system save 231 // Call before using the sd seed to attempt to derive it if it dosen't exist. Needs system
162 // 8*43 and the private file to exist. 232 // save 8*43 and the private file to exist.
163 void DeriveSDSeedLazy(); 233 void DeriveSDSeedLazy();
164 234
165 bool BaseDeriveNecessary() const; 235 bool BaseDeriveNecessary() const;
@@ -169,19 +239,19 @@ public:
169 239
170 void PopulateFromPartitionData(PartitionDataManager& data); 240 void PopulateFromPartitionData(PartitionDataManager& data);
171 241
172 const std::map<u128, TicketRaw>& GetCommonTickets() const; 242 const std::map<u128, Ticket>& GetCommonTickets() const;
173 const std::map<u128, TicketRaw>& GetPersonalizedTickets() const; 243 const std::map<u128, Ticket>& GetPersonalizedTickets() const;
174 244
175 bool AddTicketCommon(TicketRaw raw); 245 bool AddTicketCommon(Ticket raw);
176 bool AddTicketPersonalized(TicketRaw raw); 246 bool AddTicketPersonalized(Ticket raw);
177 247
178private: 248private:
179 std::map<KeyIndex<S128KeyType>, Key128> s128_keys; 249 std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
180 std::map<KeyIndex<S256KeyType>, Key256> s256_keys; 250 std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
181 251
182 // Map from rights ID to ticket 252 // Map from rights ID to ticket
183 std::map<u128, TicketRaw> common_tickets; 253 std::map<u128, Ticket> common_tickets;
184 std::map<u128, TicketRaw> personal_tickets; 254 std::map<u128, Ticket> personal_tickets;
185 255
186 std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{}; 256 std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
187 std::array<std::array<u8, 0x90>, 0x20> keyblobs{}; 257 std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
@@ -216,11 +286,11 @@ std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblo
216std::optional<Key128> DeriveSDSeed(); 286std::optional<Key128> DeriveSDSeed();
217Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys); 287Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
218 288
219std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save); 289std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save);
220 290
221// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset 291// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority
222// 0x140-0x144 is zero) 292// (offset 0x140-0x144 is zero)
223std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, 293std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
224 const RSAKeyPair<2048>& eticket_extended_key); 294 const RSAKeyPair<2048>& eticket_extended_key);
225 295
226} // namespace Core::Crypto 296} // namespace Core::Crypto
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index d17fb778c..7e01f88b9 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -75,15 +75,15 @@ private:
75 const auto ticket = ctx.ReadBuffer(); 75 const auto ticket = ctx.ReadBuffer();
76 const auto cert = ctx.ReadBuffer(1); 76 const auto cert = ctx.ReadBuffer(1);
77 77
78 if (ticket.size() < sizeof(Core::Crypto::TicketRaw)) { 78 if (ticket.size() < sizeof(Core::Crypto::Ticket)) {
79 LOG_ERROR(Service_ETicket, "The input buffer is not large enough!"); 79 LOG_ERROR(Service_ETicket, "The input buffer is not large enough!");
80 IPC::ResponseBuilder rb{ctx, 2}; 80 IPC::ResponseBuilder rb{ctx, 2};
81 rb.Push(ERROR_INVALID_ARGUMENT); 81 rb.Push(ERROR_INVALID_ARGUMENT);
82 return; 82 return;
83 } 83 }
84 84
85 Core::Crypto::TicketRaw raw; 85 Core::Crypto::Ticket raw{};
86 std::memcpy(raw.data(), ticket.data(), sizeof(Core::Crypto::TicketRaw)); 86 std::memcpy(&raw, ticket.data(), sizeof(Core::Crypto::Ticket));
87 87
88 if (!keys.AddTicketPersonalized(raw)) { 88 if (!keys.AddTicketPersonalized(raw)) {
89 LOG_ERROR(Service_ETicket, "The ticket could not be imported!"); 89 LOG_ERROR(Service_ETicket, "The ticket could not be imported!");
@@ -203,7 +203,7 @@ private:
203 203
204 IPC::ResponseBuilder rb{ctx, 4}; 204 IPC::ResponseBuilder rb{ctx, 4};
205 rb.Push(RESULT_SUCCESS); 205 rb.Push(RESULT_SUCCESS);
206 rb.Push<u64>(ticket.size()); 206 rb.Push<u64>(ticket.GetSize());
207 } 207 }
208 208
209 void GetPersonalizedTicketSize(Kernel::HLERequestContext& ctx) { 209 void GetPersonalizedTicketSize(Kernel::HLERequestContext& ctx) {
@@ -219,7 +219,7 @@ private:
219 219
220 IPC::ResponseBuilder rb{ctx, 4}; 220 IPC::ResponseBuilder rb{ctx, 4};
221 rb.Push(RESULT_SUCCESS); 221 rb.Push(RESULT_SUCCESS);
222 rb.Push<u64>(ticket.size()); 222 rb.Push<u64>(ticket.GetSize());
223 } 223 }
224 224
225 void GetCommonTicketData(Kernel::HLERequestContext& ctx) { 225 void GetCommonTicketData(Kernel::HLERequestContext& ctx) {
@@ -233,8 +233,8 @@ private:
233 233
234 const auto ticket = keys.GetCommonTickets().at(rights_id); 234 const auto ticket = keys.GetCommonTickets().at(rights_id);
235 235
236 const auto write_size = std::min(ticket.size(), ctx.GetWriteBufferSize()); 236 const auto write_size = std::min(ticket.GetSize(), ctx.GetWriteBufferSize());
237 ctx.WriteBuffer(ticket.data(), write_size); 237 ctx.WriteBuffer(&ticket, write_size);
238 238
239 IPC::ResponseBuilder rb{ctx, 4}; 239 IPC::ResponseBuilder rb{ctx, 4};
240 rb.Push(RESULT_SUCCESS); 240 rb.Push(RESULT_SUCCESS);
@@ -252,8 +252,8 @@ private:
252 252
253 const auto ticket = keys.GetPersonalizedTickets().at(rights_id); 253 const auto ticket = keys.GetPersonalizedTickets().at(rights_id);
254 254
255 const auto write_size = std::min(ticket.size(), ctx.GetWriteBufferSize()); 255 const auto write_size = std::min(ticket.GetSize(), ctx.GetWriteBufferSize());
256 ctx.WriteBuffer(ticket.data(), write_size); 256 ctx.WriteBuffer(&ticket, write_size);
257 257
258 IPC::ResponseBuilder rb{ctx, 4}; 258 IPC::ResponseBuilder rb{ctx, 4};
259 rb.Push(RESULT_SUCCESS); 259 rb.Push(RESULT_SUCCESS);