-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
IOSC.h
279 lines (239 loc) · 7.58 KB
/
IOSC.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Implementation of an IOSC-like API, but much simpler since we only support actual keys.
#pragma once
#include <array>
#include <cstddef>
#include <utility>
#include <vector>
#include "Common/CommonTypes.h"
#include "Common/Crypto/AES.h"
#include "Common/Crypto/ec.h"
class PointerWrap;
namespace IOS
{
namespace ES
{
class CertReader;
} // namespace ES
enum class SignatureType : u32
{
RSA4096 = 0x00010000,
RSA2048 = 0x00010001,
ECC = 0x00010002,
};
enum class PublicKeyType : u32
{
RSA4096 = 0,
RSA2048 = 1,
ECC = 2,
};
#pragma pack(push, 4)
struct SignatureRSA4096
{
SignatureType type;
u8 sig[0x200];
u8 fill[0x3c];
char issuer[0x40];
};
static_assert(sizeof(SignatureRSA4096) == 0x280, "Wrong size for SignatureRSA4096");
struct SignatureRSA2048
{
SignatureType type;
u8 sig[0x100];
u8 fill[0x3c];
char issuer[0x40];
};
static_assert(sizeof(SignatureRSA2048) == 0x180, "Wrong size for SignatureRSA2048");
struct SignatureECC
{
SignatureType type;
Common::ec::Signature sig;
u8 fill[0x40];
char issuer[0x40];
};
static_assert(sizeof(SignatureECC) == 0xc0, "Wrong size for SignatureECC");
// Source: https://wiibrew.org/wiki/Certificate_chain
struct CertHeader
{
PublicKeyType public_key_type;
char name[0x40];
u32 id;
};
using RSA2048PublicKey = std::array<u8, 0x100>;
struct CertRSA4096RSA2048
{
SignatureRSA4096 signature;
CertHeader header;
RSA2048PublicKey public_key;
u8 exponent[0x4];
u8 pad[0x34];
};
static_assert(sizeof(CertRSA4096RSA2048) == 0x400, "Wrong size for CertRSA4096RSA2048");
struct CertRSA2048RSA2048
{
SignatureRSA2048 signature;
CertHeader header;
RSA2048PublicKey public_key;
u8 exponent[0x4];
u8 pad[0x34];
};
static_assert(sizeof(CertRSA2048RSA2048) == 0x300, "Wrong size for CertRSA2048RSA2048");
/// Used for device certificates
struct CertRSA2048ECC
{
SignatureRSA2048 signature;
CertHeader header;
Common::ec::PublicKey public_key;
std::array<u8, 60> padding;
};
static_assert(sizeof(CertRSA2048ECC) == 0x240, "Wrong size for CertRSA2048ECC");
/// Used for device signed certificates
struct CertECC
{
SignatureECC signature;
CertHeader header;
Common::ec::PublicKey public_key;
std::array<u8, 60> padding;
};
static_assert(sizeof(CertECC) == 0x180, "Wrong size for CertECC");
#pragma pack(pop)
namespace HLE
{
enum ReturnCode : s32;
class IOSC final
{
public:
using Handle = u32;
enum class ConsoleType
{
Retail,
RVT,
};
// We use the same default key handle IDs as the actual IOSC because there are ioctlvs
// that accept arbitrary key handles from the PPC, so the IDs must match.
// More information on default handles: https://wiibrew.org/wiki/IOS/Syscalls
enum DefaultHandle : u32
{
// NG private key. ECC-233 private signing key (per-console)
HANDLE_CONSOLE_KEY = 0,
// Console ID
HANDLE_CONSOLE_ID = 1,
// NAND FS AES-128 key
HANDLE_FS_KEY = 2,
// NAND FS HMAC
HANDLE_FS_MAC = 3,
// Common key
HANDLE_COMMON_KEY = 4,
// PRNG seed
HANDLE_PRNG_KEY = 5,
// SD AES-128 key
HANDLE_SD_KEY = 6,
// boot2 version (writable)
HANDLE_BOOT2_VERSION = 7,
// Unknown
HANDLE_UNKNOWN_8 = 8,
// Unknown
HANDLE_UNKNOWN_9 = 9,
// Filesystem version (writable)
HANDLE_FS_VERSION = 10,
// New common key (aka Korean common key)
HANDLE_NEW_COMMON_KEY = 11,
HANDLE_ROOT_KEY = 0xfffffff,
};
static constexpr std::array<DefaultHandle, 2> COMMON_KEY_HANDLES = {HANDLE_COMMON_KEY,
HANDLE_NEW_COMMON_KEY};
enum ObjectType : u8
{
TYPE_SECRET_KEY = 0,
TYPE_PUBLIC_KEY = 1,
TYPE_DATA = 3,
};
enum ObjectSubType : u8
{
SUBTYPE_AES128 = 0,
SUBTYPE_MAC = 1,
SUBTYPE_RSA2048 = 2,
SUBTYPE_RSA4096 = 3,
SUBTYPE_ECC233 = 4,
SUBTYPE_DATA = 5,
SUBTYPE_VERSION = 6
};
IOSC(ConsoleType console_type = ConsoleType::Retail);
~IOSC();
// Create an object for use with the other functions that operate on objects.
ReturnCode CreateObject(Handle* handle, ObjectType type, ObjectSubType subtype, u32 pid);
// Delete an object. Built-in objects cannot be deleted.
ReturnCode DeleteObject(Handle handle, u32 pid);
// Import a secret, encrypted key into dest_handle, which will be decrypted using decrypt_handle.
ReturnCode ImportSecretKey(Handle dest_handle, Handle decrypt_handle, u8* iv,
const u8* encrypted_key, u32 pid);
// Import a secret key that is already decrypted.
ReturnCode ImportSecretKey(Handle dest_handle, const u8* decrypted_key, u32 pid);
// Import a public key. public_key_exponent must be passed for RSA keys.
ReturnCode ImportPublicKey(Handle dest_handle, const u8* public_key,
const u8* public_key_exponent, u32 pid);
// Compute an AES key from an ECDH shared secret.
ReturnCode ComputeSharedKey(Handle dest_handle, Handle private_handle, Handle public_handle,
u32 pid);
// AES encrypt/decrypt.
ReturnCode Encrypt(Handle key_handle, u8* iv, const u8* input, size_t size, u8* output,
u32 pid) const;
ReturnCode Decrypt(Handle key_handle, u8* iv, const u8* input, size_t size, u8* output,
u32 pid) const;
ReturnCode CryptOFB(u8* key, u8* iv, u8* input, size_t size, u8* output);
ReturnCode VerifyPublicKeySign(const std::array<u8, 20>& sha1, Handle signer_handle,
const std::vector<u8>& signature, u32 pid) const;
// Import a certificate (signed by the certificate in signer_handle) into dest_handle.
ReturnCode ImportCertificate(const ES::CertReader& cert, Handle signer_handle, Handle dest_handle,
u32 pid);
// Ownership
ReturnCode GetOwnership(Handle handle, u32* owner) const;
ReturnCode SetOwnership(Handle handle, u32 owner, u32 pid);
bool IsUsingDefaultId() const;
u32 GetDeviceId() const;
CertECC GetDeviceCertificate() const;
void Sign(u8* sig_out, u8* ap_cert_out, u64 title_id, const u8* data, u32 data_size) const;
void DoState(PointerWrap& p);
private:
struct KeyEntry
{
KeyEntry();
KeyEntry(ObjectType type_, ObjectSubType subtype_, std::vector<u8>&& data_, u32 owner_mask_);
KeyEntry(ObjectType type_, ObjectSubType subtype_, std::vector<u8>&& data_, u32 misc_data_,
u32 owner_mask_);
void DoState(PointerWrap& p);
bool in_use = false;
ObjectType type{};
ObjectSubType subtype{};
std::vector<u8> data;
u32 misc_data = 0;
u32 owner_mask = 0;
};
// The Wii's IOSC is limited to 32 entries, including 12 built-in entries.
using KeyEntries = std::array<KeyEntry, 32>;
enum class SearchMode
{
IncludeRootKey,
ExcludeRootKey,
};
void LoadDefaultEntries();
void LoadEntries();
KeyEntries::iterator FindFreeEntry();
KeyEntry* FindEntry(Handle handle);
const KeyEntry* FindEntry(Handle handle, SearchMode mode = SearchMode::ExcludeRootKey) const;
Handle GetHandleFromIterator(KeyEntries::iterator iterator) const;
bool HasOwnership(Handle handle, u32 pid) const;
bool IsDefaultHandle(Handle handle) const;
ReturnCode DecryptEncrypt(Common::AES::Mode mode, Handle key_handle, u8* iv, const u8* input,
size_t size, u8* output, u32 pid) const;
ConsoleType m_console_type{ConsoleType::Retail};
KeyEntries m_key_entries;
KeyEntry m_root_key_entry;
Common::ec::Signature m_console_signature{};
u32 m_ms_id = 0;
u32 m_ca_id = 0;
u32 m_console_key_id = 0;
};
} // namespace HLE
} // namespace IOS