| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889 |
- #include "xinput_auth.h"
- #include "xinput_descriptors.h"
- #include "config.h"
- #include "usb_app_drivers.h"
- #include "src/libxsm3/excrypt.h"
- #include "src/libxsm3/usbdsec.h"
- #include "src/libxsm3/xsm3.h"
- #include <algorithm>
- #include <cstdio>
- #include <cstdlib>
- #include <cstring>
- #include <vector>
- #define CHECK(condition) check(bool(condition), #condition, __LINE__)
- static void check(bool condition, const char *expression, int line)
- {
- if (!condition) {
- fprintf(stderr, "%s:%d: %s\n", __FILE__, line, expression);
- abort();
- }
- }
- using Bytes = std::vector<uint8_t>;
- struct Fixture {
- const char *console;
- const char *nonce;
- const char *identification;
- const char *init;
- const char *init_response;
- const char *verify[3];
- const char *verify_response[3];
- };
- #include "fixtures.h"
- static Bytes hex(const char *text)
- {
- CHECK(strlen(text) % 2 == 0);
- Bytes result;
- while (*text) {
- unsigned byte;
- CHECK(sscanf(text, "%2x", &byte) == 1);
- result.push_back(byte);
- text += 2;
- }
- return result;
- }
- static void checksum(Bytes &packet)
- {
- CHECK(packet.size() == size_t(packet[4]) + 6);
- packet.back() = 0;
- for (size_t i = 5; i + 1 < packet.size(); i++)
- packet.back() ^= packet[i];
- }
- static void checkPacket(const Bytes &packet, size_t length)
- {
- CHECK(packet.size() == length);
- CHECK(packet[0] == 0x49 && packet[1] == 0x4C);
- CHECK(packet[2] == 0 && packet[3] == 0);
- Bytes copy = packet;
- checksum(copy);
- CHECK(copy == packet);
- }
- static uint32_t lockDepth;
- static unsigned notifications, rngCalls, workerStarts;
- static bool workerAvailable = true, rngAvailable = true;
- static bool resetInRng, replaceInRng;
- static uint8_t *ep0;
- static uint16_t offered, transferred;
- static unsigned transfers;
- static const tusb_control_request_t *callbackRequest;
- static const Bytes *callbackPayload;
- static uint8_t callbackEndpoint;
- static xfer_result_t callbackResult;
- static uint32_t callbackLength;
- static unsigned callbacks;
- extern "C" bool __wrap_usbd_control_xfer_cb(uint8_t rhport, uint8_t ep,
- xfer_result_t result,
- uint32_t length);
- extern "C" bool __real_usbd_control_xfer_cb(uint8_t rhport, uint8_t ep,
- xfer_result_t result,
- uint32_t length)
- {
- CHECK(rhport == 0 && ep == callbackEndpoint);
- CHECK(result == callbackResult && length == callbackLength);
- CHECK(xinputAuthReceived(callbackRequest->wLength) ==
- (ep == 0 && result == XFER_RESULT_SUCCESS &&
- length == callbackRequest->wLength));
- callbacks++;
- // Model TinyUSB copying only received bytes, never the SETUP wLength.
- if (ep == 0 && !callbackPayload->empty()) {
- CHECK(ep0 != nullptr);
- size_t copied = std::min<size_t>(length, offered);
- copied = std::min(copied, callbackPayload->size());
- memcpy(ep0, callbackPayload->data(), copied);
- }
- return xinputClassDriver()->control_xfer_cb(0, CONTROL_STAGE_DATA,
- callbackRequest);
- }
- static bool data(const tusb_control_request_t &req, const Bytes &payload,
- uint32_t actual, uint8_t ep = 0,
- xfer_result_t result = XFER_RESULT_SUCCESS)
- {
- CHECK(!xinputAuthReceived(req.wLength));
- callbackRequest = &req;
- callbackPayload = &payload;
- callbackEndpoint = ep;
- callbackResult = result;
- callbackLength = actual;
- unsigned before = callbacks;
- bool ok = __wrap_usbd_control_xfer_cb(0, ep, result, actual);
- CHECK(callbacks == before + 1);
- CHECK(!xinputAuthReceived(req.wLength));
- callbackRequest = nullptr;
- callbackPayload = nullptr;
- return ok;
- }
- static bool data(const tusb_control_request_t &req, const Bytes &payload = {})
- {
- bool input = req.bmRequestType & 0x80;
- return data(req, payload, input ? transferred : req.wLength,
- input ? 0x80 : 0);
- }
- static tusb_control_request_t request(uint8_t command, uint16_t length,
- uint8_t type = 0xC1,
- uint16_t index = 0x0103)
- {
- return { type, command, 0, index, length };
- }
- bool tud_control_xfer(uint8_t rhport, const tusb_control_request_t *req,
- void *buffer, uint16_t length)
- {
- CHECK(rhport == 0);
- CHECK(lockDepth == 0);
- CHECK(buffer != nullptr);
- CHECK(length <= 46);
- ep0 = static_cast<uint8_t *>(buffer);
- offered = length;
- transferred = std::min(length, req->wLength);
- transfers++;
- return true;
- }
- uint32_t xinputAuthLock()
- {
- return lockDepth++;
- }
- void xinputAuthUnlock(uint32_t mask)
- {
- CHECK(lockDepth == mask + 1);
- lockDepth = mask;
- }
- bool xinputAuthStartWorker()
- {
- workerStarts++;
- return workerAvailable;
- }
- void xinputAuthNotify()
- {
- CHECK(lockDepth == 0);
- notifications++;
- }
- void xinputAuthBoardSerial(char serial[13])
- {
- memcpy(serial, "012345ABCDEF", 13);
- }
- static bool setup(const tusb_control_request_t &req)
- {
- ep0 = nullptr;
- offered = transferred = 0;
- unsigned before = transfers;
- bool ok = xinputAuthControl(0, CONTROL_STAGE_SETUP, &req);
- CHECK(lockDepth == 0);
- CHECK(transfers == before + unsigned(ok));
- return ok;
- }
- static void out(uint8_t command, const Bytes &packet, uint16_t index = 0x0103,
- uint8_t type = 0x41)
- {
- auto req = request(command, packet.size(), type, index);
- unsigned before = notifications;
- CHECK(setup(req));
- CHECK(offered == packet.size() && transferred == packet.size());
- CHECK(notifications == before);
- CHECK(data(req, packet));
- CHECK(notifications == before + 1);
- CHECK(xinputAuthControl(0, CONTROL_STAGE_ACK, &req));
- CHECK(notifications == before + 1);
- }
- bool xsm3_random_bytes(uint8_t *buffer, size_t length)
- {
- CHECK(lockDepth == 0);
- CHECK(length == 16);
- rngCalls++;
- for (size_t i = 0; i < length; i++)
- buffer[i] = 0x90 + i;
- if (resetInRng) {
- resetInRng = false;
- xinputAuthReset();
- }
- if (replaceInRng) {
- replaceInRng = false;
- out(0x82, hex(fixtures[1].init));
- }
- return rngAvailable;
- }
- static Bytes in(uint8_t command, uint16_t length = 65535,
- uint16_t index = 0x0103, uint8_t type = 0xC1)
- {
- auto req = request(command, length, type, index);
- CHECK(setup(req));
- Bytes result(ep0, ep0 + transferred);
- unsigned before = transfers;
- CHECK(data(req));
- CHECK(xinputAuthControl(0, CONTROL_STAGE_ACK, &req));
- CHECK(transfers == before);
- return result;
- }
- static void state(uint8_t expected)
- {
- CHECK(in(0x86) == Bytes({ expected, 0 }));
- }
- static void noReply()
- {
- CHECK(!setup(request(0x83, 46)));
- }
- static void failed()
- {
- noReply();
- CHECK(!setup(request(0x86, 2)));
- CHECK(!setup(request(0x87, 22, 0x41)));
- xinputAuthProcess();
- noReply();
- }
- /*
- * This console uses only primitives, never xsm3_do_challenge_* or its globals.
- * Every packet is also compared with the independent OpenSSL/Python fixture,
- * so matching bugs in the shared primitives cannot bless a broken exchange.
- */
- class Console {
- public:
- const Fixture &fixture;
- Bytes nonce, id, key1, key2, controller, hash, salt;
- explicit Console(const Fixture &data)
- : fixture(data)
- , nonce(hex(data.nonce))
- , id(hex(data.console))
- , key1(16)
- , key2(16)
- , controller(16)
- , hash(20)
- , salt(16)
- {
- uint8_t digest[20], kv1[16], kv2[16];
- ExCryptSha(id.data(), id.size(), digest);
- auto root1 = hex("828078683a523a9810f40c127066dcba");
- auto root2 = hex("66621a78f8609c8a269a04aed85c1ec8");
- UsbdSecXSM3AuthenticationCrypt(root1.data(), digest, 16, kv1,
- 1);
- UsbdSecXSM3AuthenticationCrypt(root2.data(), digest + 4, 16,
- kv2, 1);
- Bytes swapped(nonce.begin() + 8, nonce.end());
- swapped.insert(swapped.end(), nonce.begin(), nonce.begin() + 8);
- UsbdSecXSM3AuthenticationCrypt(kv1, nonce.data(), 16,
- key1.data(), 1);
- UsbdSecXSM3AuthenticationCrypt(kv2, swapped.data(), 16,
- key2.data(), 1);
- }
- Bytes init()
- {
- Bytes plain = nonce;
- plain.insert(plain.end(), id.begin(), id.end());
- Bytes packet(34);
- packet[0] = 0x49;
- packet[1] = 0x4C;
- packet[4] = 28;
- auto cryptKey = hex("e35bfb1ccdad325bf70e07fd623da7c4");
- auto macKey = hex("8f2908380b5bfe687c26462a51f2bc19");
- uint8_t mac[8];
- UsbdSecXSM3AuthenticationCrypt(cryptKey.data(), plain.data(),
- 24, packet.data() + 5, 1);
- UsbdSecXSM3AuthenticationMac(macKey.data(), nullptr,
- packet.data() + 5, 24, mac);
- memcpy(packet.data() + 29, mac + 4, 4);
- checksum(packet);
- CHECK(packet == hex(fixture.init));
- return packet;
- }
- void acceptInit(const Bytes &response)
- {
- checkPacket(response, 46);
- CHECK(response == hex(fixture.init_response));
- uint8_t plain[32];
- UsbdSecXSM3AuthenticationCrypt(key1.data(), response.data() + 5,
- 32, plain, 0);
- CHECK(std::equal(nonce.begin(), nonce.end(), plain + 16));
- memcpy(controller.data(), plain, 16);
- ExCryptSha(plain, sizeof plain, hash.data());
- memcpy(salt.data(), controller.data() + 12, 4);
- memcpy(salt.data() + 4, nonce.data() + 12, 4);
- }
- Bytes verify(unsigned iteration)
- {
- CHECK(iteration < 3);
- for (unsigned i = 0; i < 8; i++)
- salt[8 + i] = 0x60 + iteration * 8 + i;
- Bytes packet(22);
- packet[0] = 0x49;
- packet[1] = 0x4C;
- packet[4] = 16;
- UsbdSecXSM3AuthenticationCrypt(controller.data(),
- salt.data() + 8, 8,
- packet.data() + 5, 1);
- UsbdSecXSM3AuthenticationMac(hash.data(), salt.data(),
- packet.data() + 5, 8,
- packet.data() + 13);
- checksum(packet);
- CHECK(packet == hex(fixture.verify[iteration]));
- return packet;
- }
- void acceptVerify(Bytes response, unsigned iteration)
- {
- checkPacket(response, 22);
- CHECK(response == hex(fixture.verify_response[iteration]));
- uint8_t mac[8];
- UsbdSecXSM3AuthenticationMac(key2.data(), salt.data(),
- response.data() + 5, 8, mac);
- CHECK(memcmp(mac, response.data() + 13, 8) == 0);
- }
- };
- static void begin(Console &console, uint16_t index = 0x0103,
- uint8_t type = 0x41)
- {
- unsigned before = rngCalls;
- out(0x82, console.init(), index, type);
- CHECK(rngCalls == before);
- state(1);
- noReply();
- xinputAuthProcess();
- CHECK(rngCalls == before + 1);
- state(2);
- console.acceptInit(in(0x83));
- xinputAuthProcess();
- CHECK(rngCalls == before + 1);
- }
- static void exchange(Console &console)
- {
- for (unsigned i = 0; i < 3; i++) {
- unsigned before = rngCalls;
- out(0x87, console.verify(i));
- state(1);
- noReply();
- xinputAuthProcess();
- CHECK(rngCalls == before);
- state(2);
- Bytes response = in(0x83);
- CHECK(in(0x83) == response);
- console.acceptVerify(response, i);
- }
- }
- static void cryptoKnownAnswers()
- {
- uint8_t digest[20];
- ExCryptSha(reinterpret_cast<const uint8_t *>("abc"), 3, digest);
- CHECK(Bytes(digest, digest + 20) ==
- hex("a9993e364706816aba3e25717850c26c9cd0d89d"));
- ExCryptSha(reinterpret_cast<const uint8_t *>(""), 0, digest);
- CHECK(Bytes(digest, digest + 20) ==
- hex("da39a3ee5e6b4b0d3255bfef95601890afd80709"));
- // This vendor helper only supports single-block SHA-1 (at most 55 bytes).
- uint8_t message[55];
- for (unsigned i = 0; i < sizeof message; i++)
- message[i] = i;
- ExCryptSha(message, 32, digest);
- CHECK(Bytes(digest, digest + 20) ==
- hex("ae5bd8efea5322c4d9986d06680a781392f9a642"));
- ExCryptSha(message, sizeof message, digest);
- CHECK(Bytes(digest, digest + 20) ==
- hex("8ae2d46729cfe68ff927af5eec9c7d1b66d65ac2"));
- uint64_t key;
- auto keyBytes = hex("133457799bbcdff1");
- memcpy(&key, keyBytes.data(), 8);
- EXCRYPT_DES_STATE des;
- ExCryptDesKey(&des, &key);
- auto plain = hex("0123456789abcdef");
- uint8_t encrypted[8], recovered[8];
- ExCryptDesEcb(&des, plain.data(), encrypted, 1);
- CHECK(Bytes(encrypted, encrypted + 8) == hex("85e813540f0ab405"));
- ExCryptDesEcb(&des, encrypted, recovered, 0);
- CHECK(Bytes(recovered, recovered + 8) == plain);
- uint8_t parity[256], input[256];
- for (unsigned i = 0; i < 256; i++)
- input[i] = i;
- ExCryptDesParity(input, sizeof input, parity);
- for (unsigned i = 0; i < 256; i++)
- CHECK((parity[i] & 0xFE) == (i & 0xFE));
- // DES ignores parity bits; this helper does not normalize odd parity.
- for (unsigned i = 0; i < 256; i += 8) {
- EXCRYPT_DES_STATE original, adjusted;
- memcpy(&key, input + i, 8);
- ExCryptDesKey(&original, &key);
- memcpy(&key, parity + i, 8);
- ExCryptDesKey(&adjusted, &key);
- CHECK(memcmp(&original, &adjusted, sizeof original) == 0);
- }
- uint8_t macKey[16], data[24];
- for (unsigned i = 0; i < sizeof macKey; i++)
- macKey[i] = i;
- for (unsigned i = 0; i < sizeof data; i++)
- data[i] = i;
- auto salt = hex("ffffffffffffffff1020304050607080");
- UsbdSecXSM3AuthenticationMac(macKey, salt.data(), data, sizeof data,
- encrypted);
- CHECK(Bytes(encrypted, encrypted + 8) == hex(mac_wrap));
- CHECK(salt == hex(salt_wrap));
- puts("PASS SHA-1/DES known answers, DES parity, salted MAC wrap");
- }
- static void descriptors()
- {
- static_assert(sizeof(tusb_control_request_t) == 8, "SETUP size");
- CHECK(sizeof XINPUT_CONFIG_BODY == 144);
- CHECK(sizeof XINPUT_CONFIG_BODY + 9 == 153);
- const uint8_t counts[] = { 2, 4, 1, 0 };
- const uint8_t protocols[] = { 1, 3, 2, 0x13 };
- const uint8_t classLengths[] = { 17, 27, 9, 6 };
- const uint8_t endpoints[] = { 0x81, 0x02, 0x83, 0x04, 0x85, 0x06, 0x86 };
- const uint8_t intervals[] = { 1, 8, 2, 4, 0x40, 0x10, 0x10 };
- unsigned interfaces = 0, endpoint = 0, localEndpoints = 0;
- unsigned classDescriptors = 0;
- for (size_t offset = 0; offset < sizeof XINPUT_CONFIG_BODY;) {
- const uint8_t *d = XINPUT_CONFIG_BODY + offset;
- CHECK(d[0] >= 2 && offset + d[0] <= sizeof XINPUT_CONFIG_BODY);
- switch (d[1]) {
- case 4:
- if (interfaces)
- CHECK(localEndpoints == counts[interfaces - 1]);
- CHECK(interfaces < 4 && d[0] == 9);
- CHECK(d[2] == interfaces && d[3] == 0);
- CHECK(d[4] == counts[interfaces] && d[5] == 0xFF);
- CHECK(d[6] == (interfaces == 3 ? 0xFD : 0x5D));
- CHECK(d[7] == protocols[interfaces]);
- CHECK(d[8] == (interfaces == 3 ? 4 : 0));
- if (interfaces == 3)
- CHECK(offset + 8 ==
- XINPUT_SECURITY_STRING_OFFSET);
- interfaces++;
- localEndpoints = 0;
- break;
- case 5:
- CHECK(interfaces > 0 && endpoint < 7);
- CHECK(d[0] == 7 && d[2] == endpoints[endpoint]);
- CHECK(d[3] == 3 && d[4] == 32 && d[5] == 0);
- CHECK(d[6] == intervals[endpoint]);
- endpoint++;
- localEndpoints++;
- break;
- case 0x21:
- case 0x41:
- CHECK(interfaces > 0 &&
- classDescriptors == interfaces - 1);
- CHECK(d[0] == classLengths[classDescriptors]);
- CHECK(d[1] == (interfaces == 4 ? 0x41 : 0x21));
- classDescriptors++;
- break;
- default:
- CHECK(false);
- }
- offset += d[0];
- }
- CHECK(interfaces == 4 && endpoint == 7 && classDescriptors == 4);
- CHECK(localEndpoints == 0);
- CHECK(XINPUT_SECURITY_STRING_OFFSET == 137);
- CHECK(strcmp(XINPUT_SECURITY_STRING,
- "Xbox Security Method 3, Version 1.00, \xc2\xa9 2005 Microsoft "
- "Corporation. All rights reserved.") == 0);
- puts("PASS descriptor lengths, interfaces, endpoints, security string");
- }
- static const uint16_t lengths[] = {
- 0, 1, 2, 5, 21, 22, 23, 28, 29, 33, 34,
- 35, 45, 46, 47, 255, 256, 32767, 32768, 65534, 65535,
- };
- static void identificationAndBounds()
- {
- xinputAuthPrepare();
- CHECK(workerStarts == 1);
- CHECK(strlen(xinputAuthSerial()) == 12);
- CHECK(strcmp(xinputAuthSerial(), "012345ABCDEF") == 0);
- auto expected = hex(fixtures[0].identification);
- for (uint16_t index : { 3, 0x0103 }) {
- for (uint16_t length : lengths) {
- Bytes id = in(0x81, length, index);
- CHECK(offered == 29);
- Bytes truncated = expected;
- truncated.resize(std::min<size_t>(length, 29));
- CHECK(id == truncated);
- }
- }
- CHECK(in(0x81, 65535, 0, 0xC0) == expected);
- CHECK(in(0x81) == expected);
- CHECK(memcmp(expected.data() + 5, xinputAuthSerial(), 12) == 0);
- Bytes checked = expected;
- checksum(checked);
- CHECK(checked == expected);
- state(1);
- noReply();
- CHECK(!setup(request(0x87, 22, 0x41)));
- Console console(fixtures[0]);
- begin(console);
- for (uint8_t command : { 0x82, 0x87 }) {
- for (uint16_t length : lengths) {
- if (length == (command == 0x82 ? 34 : 22))
- continue;
- CHECK(!setup(request(command, length, 0x41)));
- CHECK(in(0x83) == hex(fixtures[0].init_response));
- }
- }
- for (uint8_t command : { 0x83, 0x84, 0x86 }) {
- Bytes full = in(command);
- for (uint16_t length : lengths) {
- Bytes reply = in(command, length);
- CHECK(offered == full.size());
- Bytes truncated = full;
- truncated.resize(std::min<size_t>(length, full.size()));
- CHECK(reply == truncated);
- }
- }
- for (uint8_t command : { 0x81, 0x82, 0x83, 0x84, 0x86, 0x87 }) {
- bool output = command == 0x82 || command == 0x87;
- uint16_t length = command == 0x82 ? 34 : 22;
- uint8_t correct = output ? 0x41 : 0xC1;
- CHECK(!setup(request(command, length, correct ^ 0x80)));
- for (uint8_t type : { 0x00, 0x01, 0x21, 0x42, 0x43, 0x80, 0x81,
- 0xA1, 0xC2, 0xC3 })
- CHECK(!setup(request(command, length, type)));
- for (uint16_t index : { 0, 1, 2, 4, 0x0100, 0x0203, 65535 })
- CHECK(!setup(request(command, length, correct, index)));
- CHECK(!setup(request(command, length, correct - 1, 3)));
- }
- for (uint8_t command : { 0x00, 0x80, 0x85, 0x88, 0xFF }) {
- CHECK(!setup(request(command, 34, 0x41)));
- CHECK(!setup(request(command, 46)));
- }
- CHECK(in(0x83) == hex(fixtures[0].init_response));
- puts("PASS identification, serial stability, lengths and request routing");
- }
- static void sessions()
- {
- for (unsigned round = 0; round < 2; round++) {
- for (const auto &fixture : fixtures) {
- Console console(fixture);
- if (round)
- xinputAuthReset();
- begin(console, round ? 3 : 0x0103);
- auto keepalive =
- request(0x84, 0, 0x41, round ? 3 : 0x0103);
- keepalive.wValue = 3;
- unsigned notified = notifications;
- CHECK(setup(keepalive));
- CHECK(offered == 0);
- CHECK(xinputAuthControl(0, CONTROL_STAGE_ACK,
- &keepalive));
- CHECK(notifications == notified);
- state(2);
- CHECK(in(0x83) == hex(fixture.init_response));
- exchange(console);
- for (uint16_t length : lengths) {
- auto expected = hex(fixture.verify_response[2]);
- expected.resize(std::min<size_t>(length, 22));
- CHECK(in(0x83, length) == expected);
- CHECK(offered == 22);
- }
- }
- }
- Console device(fixtures[0]);
- begin(device, 0, 0x40);
- CHECK(in(0x83, 65535, 0, 0xC0) == hex(fixtures[0].init_response));
- exchange(device);
- CHECK(in(0x81) == hex(fixtures[0].identification));
- state(1);
- noReply();
- CHECK(strcmp(xinputAuthSerial(), "012345ABCDEF") == 0);
- puts("PASS two consoles, OUT keepalive, reinit/reset, repeated verification, IN truncation");
- }
- static void stagesAndReset()
- {
- xinputAuthReset();
- auto req = request(0x82, 34, 0x41);
- CHECK(!data(req));
- CHECK(setup(req));
- unsigned before = notifications, randomBefore = rngCalls;
- CHECK(xinputAuthControl(0, CONTROL_STAGE_ACK, &req));
- xinputAuthProcess();
- CHECK(notifications == before && rngCalls == randomBefore);
- auto init = hex(fixtures[0].init);
- CHECK(data(req, init));
- CHECK(notifications == before + 1);
- CHECK(!data(req, init));
- xinputAuthReset();
- xinputAuthProcess();
- CHECK(rngCalls == randomBefore);
- state(1);
- noReply();
- CHECK(setup(req));
- xinputAuthReset();
- CHECK(!data(req, init));
- Console console(fixtures[0]);
- begin(console);
- auto replyReq = request(0x83, 46);
- CHECK(setup(replyReq));
- uint8_t *owned = ep0;
- Bytes snapshot(owned, owned + offered);
- xinputAuthReset();
- CHECK(Bytes(owned, owned + snapshot.size()) == snapshot);
- CHECK(data(replyReq));
- CHECK(xinputAuthControl(0, CONTROL_STAGE_ACK, &replyReq));
- noReply();
- out(0x82, console.init());
- CHECK(setup(request(0x86, 2)));
- owned = ep0;
- CHECK(owned[0] == 1 && owned[1] == 0);
- xinputAuthProcess();
- CHECK(owned[0] == 1 && owned[1] == 0);
- state(2);
- resetInRng = true;
- out(0x82, console.init());
- xinputAuthProcess();
- CHECK(!resetInRng);
- state(1);
- noReply();
- CHECK(!setup(request(0x87, 22, 0x41)));
- begin(console);
- exchange(console);
- replaceInRng = true;
- out(0x82, console.init());
- xinputAuthProcess();
- CHECK(!replaceInRng);
- state(1);
- noReply();
- xinputAuthProcess();
- Console replacement(fixtures[1]);
- replacement.acceptInit(in(0x83));
- exchange(replacement);
- puts("PASS EP0 stages, buffer ownership, reset/replacement during crypto");
- }
- static void invalidChallenges()
- {
- for (bool verify : { false, true }) {
- for (unsigned corruption = 0; corruption < 4; corruption++) {
- Console console(fixtures[0]);
- begin(console);
- Bytes packet = verify ? console.verify(0) :
- console.init();
- switch (corruption) {
- case 0:
- packet.back() ^= 1;
- break;
- case 1:
- packet[verify ? 13 : 29] ^= 1;
- checksum(packet);
- break;
- case 2:
- packet[4] = 0;
- break;
- case 3:
- packet[4] = 255;
- break;
- }
- unsigned before = rngCalls;
- out(verify ? 0x87 : 0x82, packet);
- noReply();
- state(1);
- xinputAuthProcess();
- CHECK(rngCalls == before);
- failed();
- CHECK(std::all_of(xsm3_challenge_response,
- xsm3_challenge_response + 48,
- [](uint8_t b) { return b == 0; }));
- Console recovery(fixtures[1]);
- begin(recovery);
- exchange(recovery);
- }
- }
- Console console(fixtures[0]);
- begin(console);
- rngAvailable = false;
- out(0x82, console.init());
- xinputAuthProcess();
- failed();
- rngAvailable = true;
- begin(console);
- exchange(console);
- workerAvailable = false;
- xinputAuthPrepare();
- CHECK(!setup(request(0x81, 29)));
- CHECK(!setup(request(0x82, 34, 0x41)));
- workerAvailable = true;
- xinputAuthPrepare();
- CHECK(in(0x81) == hex(fixtures[0].identification));
- begin(console);
- exchange(console);
- puts("PASS malformed packets, checksum/MAC failures, RNG/worker failures");
- }
- static void receivedLengths()
- {
- unsigned rejected = 0;
- for (bool verify : { false, true }) {
- uint16_t expected = verify ? 22 : 34;
- auto reject = [&](uint32_t actual, uint8_t endpoint,
- xfer_result_t result) {
- Console console(fixtures[0]);
- begin(console);
- Bytes packet = verify ? console.verify(0) :
- console.init();
- auto req =
- request(verify ? 0x87 : 0x82, expected, 0x41);
- CHECK(setup(req));
- uint8_t *buffer = ep0;
- unsigned before = notifications,
- randomBefore = rngCalls;
- CHECK(!data(req, packet, actual, endpoint, result));
- if (endpoint == 0) {
- size_t copied =
- std::min<uint32_t>(actual, expected);
- CHECK(std::equal(packet.begin(),
- packet.begin() + copied,
- buffer));
- CHECK(std::all_of(
- buffer + copied, buffer + expected,
- [](uint8_t b) { return b == 0; }));
- }
- CHECK(notifications == before && !xinputAuthBusy());
- CHECK(xinputAuthControl(0, CONTROL_STAGE_ACK, &req));
- CHECK(notifications == before);
- failed();
- CHECK(rngCalls == randomBefore);
- // A later full-length callback cannot revive the rejected job.
- CHECK(!data(req));
- CHECK(notifications == before);
- Console recovery(fixtures[1]);
- begin(recovery);
- exchange(recovery);
- rejected++;
- };
- for (uint32_t actual = 0; actual < expected; actual++)
- reject(actual, 0, XFER_RESULT_SUCCESS);
- for (uint32_t actual : { uint32_t(expected + 1), 46u, 64u, 255u,
- 256u, 65535u, 65536u, UINT32_MAX })
- reject(actual, 0, XFER_RESULT_SUCCESS);
- for (auto result : { XFER_RESULT_FAILED, XFER_RESULT_STALLED,
- XFER_RESULT_TIMEOUT, XFER_RESULT_INVALID })
- reject(expected, 0, result);
- for (uint8_t endpoint : { 1, 2, 0x80, 0x81, 0xFF })
- reject(expected, endpoint, XFER_RESULT_SUCCESS);
- }
- CHECK(rejected == 90);
- puts("PASS 90 EP0 rejects: every short init/verify, oversized counts, "
- "failed results, wrong endpoints, recovery");
- }
- extern "C" const uint16_t *__wrap_tud_descriptor_string_cb(uint8_t index,
- uint16_t langid);
- static unsigned stringDelegations;
- static uint8_t delegatedIndex;
- static uint16_t delegatedLanguage;
- static const uint16_t fallbackString[] = { 0x0304, 'Z' };
- extern "C" const uint16_t *__real_tud_descriptor_string_cb(uint8_t index,
- uint16_t langid)
- {
- stringDelegations++;
- delegatedIndex = index;
- delegatedLanguage = langid;
- return index == 0xFF ? nullptr : fallbackString;
- }
- static void stringDescriptors()
- {
- static const char expected[] =
- "Xbox Security Method 3, Version 1.00, \xA9 2005 Microsoft "
- "Corporation. All rights reserved.";
- static_assert(sizeof expected == 89, "88 security code units plus NUL");
- for (uint8_t mode = 0; mode <= MODE_MAX; mode++) {
- g_usbMode = mode;
- for (unsigned index = 0; index <= 255; index++) {
- for (uint16_t language :
- { 0, 0x0409, 0x0411, 0xFFFF }) {
- unsigned before = stringDelegations;
- const uint16_t *descriptor =
- __wrap_tud_descriptor_string_cb(
- index, language);
- if (mode == MODE_XBOX && index == 4) {
- CHECK(stringDelegations == before);
- CHECK(descriptor != nullptr);
- CHECK(descriptor[0] == 0x03B2);
- for (size_t i = 0; i < 88; i++) {
- uint16_t code =
- uint8_t(expected[i]);
- CHECK(descriptor[i + 1] ==
- code);
- }
- // Include the tail beyond two EP0 packets; USB
- // strings have no terminating UTF-16 NUL.
- Bytes bytes;
- for (size_t i = 0; i < 89; i++) {
- bytes.push_back(descriptor[i] &
- 0xFF);
- bytes.push_back(descriptor[i] >>
- 8);
- }
- CHECK(bytes.size() == 178 &&
- bytes[0] == 178);
- CHECK(bytes[176] == '.' &&
- bytes[177] == 0);
- } else {
- CHECK(stringDelegations == before + 1);
- CHECK(delegatedIndex == index);
- CHECK(delegatedLanguage == language);
- CHECK(descriptor ==
- (index == 255 ? nullptr :
- fallbackString));
- }
- }
- }
- }
- g_usbMode = MODE_XBOX;
- puts("PASS actual string wrapper: full 178 bytes, all indices/modes, "
- "language and null delegation");
- }
- void modeTests();
- int main()
- {
- cryptoKnownAnswers();
- descriptors();
- identificationAndBounds();
- sessions();
- stagesAndReset();
- invalidChallenges();
- receivedLengths();
- stringDescriptors();
- modeTests();
- CHECK(lockDepth == 0);
- puts("PASS all native XInput regression tests");
- return 0;
- }
|