-
Notifications
You must be signed in to change notification settings - Fork 28
/
console-status-verification.ts
158 lines (130 loc) · 4.07 KB
/
console-status-verification.ts
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
import crypto from 'node:crypto';
import express from 'express';
import xmlbuilder from 'xmlbuilder';
import { Device } from '@/models/device';
import { getValueFromHeaders } from '@/util';
async function consoleStatusVerificationMiddleware(request: express.Request, response: express.Response, next: express.NextFunction): Promise<void> {
if (!request.certificate || !request.certificate.valid) {
response.status(400).send(xmlbuilder.create({
error: {
code: '0110',
message: 'Unlinked device'
}
}).end());
return;
}
const deviceIDHeader = getValueFromHeaders(request.headers, 'x-nintendo-device-id');
if (!deviceIDHeader) {
response.status(400).send(xmlbuilder.create({
error: {
code: '0002',
message: 'deviceId format is invalid'
}
}).end());
return;
}
const deviceID = Number(deviceIDHeader);
if (isNaN(deviceID)) {
response.status(400).send(xmlbuilder.create({
error: {
code: '0002',
message: 'deviceId format is invalid'
}
}).end());
return;
}
const serialNumber = getValueFromHeaders(request.headers, 'x-nintendo-serial-number');
// TODO - Verify serial numbers somehow?
// * This is difficult to do safely because serial numbers are
// * inherently insecure.
// * Information about their structure can be found here:
// * https://www.3dbrew.org/wiki/Serials
// * Given this, anyone can generate a valid serial number which
// * passes these checks, even if the serial number isn't real.
// * The 3DS also futher complicates things, as it never sends
// * the complete serial number. The 3DS omits the check digit,
// * meaning any attempt to verify the serial number of a 3DS
// * family of console will ALWAYS fail. Nintendo likely just
// * has a database of all known serials which they are able to
// * compare against. We are not so lucky
if (!serialNumber) {
response.status(400).send(xmlbuilder.create({
error: {
code: '0002',
message: 'serialNumber format is invalid'
}
}).end());
return;
}
let device = await Device.findOne({
serial: serialNumber,
});
const certificateHash = crypto.createHash('sha256').update(request.certificate._certificate).digest('base64');
if (!device && request.certificate.consoleType === '3ds') {
// * A 3DS console document will ALWAYS be created by NASC before
// * Hitting the NNAS server. NASC stores the serial number at
// * the time the device document was created. Therefore we can
// * know that serial tampering happened on the 3DS if this fails
// * to find a device document.
response.status(400).send(xmlbuilder.create({
error: {
code: '0002',
message: 'serialNumber format is invalid'
}
}).end());
return;
} else if (device && !device.certificate_hash && request.certificate.consoleType === '3ds') {
device.certificate_hash = certificateHash;
await device.save();
}
device = await Device.findOne({
certificate_hash: certificateHash,
});
if (!device) {
// * Device must be a fresh Wii U
device = await Device.create({
model: 'wup',
device_id: deviceID,
serial: serialNumber,
linked_pids: [],
certificate_hash: certificateHash
});
}
if (device.serial !== serialNumber) {
// TODO - Change this to a different error
response.status(400).send(xmlbuilder.create({
error: {
cause: 'Bad Request',
code: '1600',
message: 'Unable to process request'
}
}).end());
return;
}
const certificateDeviceID = parseInt(request.certificate.certificateName.slice(2).split('-')[0], 16);
if (deviceID !== certificateDeviceID) {
// TODO - Change this to a different error
response.status(400).send(xmlbuilder.create({
error: {
cause: 'Bad Request',
code: '1600',
message: 'Unable to process request'
}
}).end());
return;
}
if (device.access_level < 0) {
response.status(400).send(xmlbuilder.create({
errors: {
error: {
code: '0012',
message: 'Device has been banned by game server' // TODO - This is not the right error message
}
}
}).end());
return;
}
request.device = device;
return next();
}
export default consoleStatusVerificationMiddleware;