Files
UniXNU/drivers/NativeKeyStoreEvents/NativeKeyStoreEvents.cpp
T
2026-10-19 18:49:31 +08:00

163 lines
8.3 KiB
C++

// SPDX-License-Identifier: BSD-2-Clause-Patent
// AppleKeyStore for an ephemeral offline fixture: a notification service (the public IOKit matching class used by
// MKBEventsRegister) and, only when sheng-aks=1 is set, a user client that answers the state queries of the unchanged
// libaks/MobileKeyBag truthfully for this environment: no keybag, no passcode, nothing locked. It provides no key,
// keybag, passcode or data-protection operation; every other selector fails with kIOReturnError exactly as an unreachable
// AppleKeyStore does. It protects nothing and must not be taken for secure storage.
//
// Answers (selectors and layouts read from the unchanged target's libaks/MobileKeyBag, see docs/touch-nt36532e.md; the set that is
// enough for the system daemons on iOS 16.7 was established on hardware by github.com/dipa-studio/ios-sweet, whose notes document it):
// 0, 4, 5, 9, 12, 13, 26, 27, 55, 101, 102 succeed, no outputs (open handshake, bag/assertion/notify bookkeeping)
// 14 aks_get_system scalar out: system keybag handle 0 (keybagd then needs no systembag.kb)
// 7 aks_get_lock_state(device) scalar out: bit 2 = unlocked since boot
// 17, 35 aks_get_(extended_)device_state struct out: DER SET{ SEQUENCE{ UTF8String key, INTEGER value } ... }
// keys ss sls sb sfa sgs sas sgpe srcd sms bh; ss = 4, sls = lock state (0 unlocked; 3 would be "no passcode").
#include <IOKit/IOService.h>
#include <IOKit/IOUserClient.h>
extern "C" void native_keyevents_open_refused(void);
extern "C" bool native_aks_state_enabled(void);
extern "C" uint32_t native_aks_lock_state(void);
extern "C" void native_aks_call(uint32_t selector, int32_t result);
extern "C" void native_aks_opened(void);
namespace {
constexpr uint32_t kSelLockState = 7, kSelGetSystem = 14, kSelDeviceState = 17, kSelExtendedDeviceState = 35;
constexpr uint64_t kUnlockedSinceBoot = 4;
constexpr uint32_t kStateFlags = 4;
constexpr uint64_t kSystemHandle = 0;
constexpr IOReturn kAksError = (IOReturn)0xe00002bc; // what libaks reports itself when the connection is missing
constexpr IOReturn kAksUnsupported = (IOReturn)0xe00002c7;
struct DerWriter {
uint8_t *p; size_t cap, n = 0; bool ok = true;
void put(uint8_t b) { if (n < cap) p[n++] = b; else ok = false; }
void len(size_t l) { if (l < 0x80) put((uint8_t)l); else { put(0x81); put((uint8_t)l); } } // entries stay below 256 bytes
};
// Smallest two's-complement big-endian INTEGER content for a non-negative value.
size_t derIntegerContent(uint64_t v, uint8_t out[9]) {
uint8_t raw[8];
for (int i = 0; i < 8; ++i) raw[i] = (uint8_t)(v >> (56 - 8 * i));
size_t start = 0;
while (start < 7 && raw[start] == 0) ++start;
size_t n = 0;
if (raw[start] & 0x80) out[n++] = 0;
while (start < 8) out[n++] = raw[start++];
return n;
}
size_t entrySize(const char *key, uint64_t value) {
uint8_t c[9]; size_t kl = 0; while (key[kl]) ++kl;
size_t body = (2 + kl) + (2 + derIntegerContent(value, c));
return 2 + body;
}
} // namespace
// DER SET of SEQUENCE{UTF8String, INTEGER}. Returns the length, or 0 when the buffer is too small.
extern "C" size_t native_aks_encode_state(uint8_t *out, size_t capacity, uint32_t lockState) {
struct Entry { const char *key; uint64_t value; };
const Entry entries[] = {{"ss", kStateFlags}, {"sls", lockState}, {"sb", 0}, {"sfa", 0}, {"sgs", 0},
{"sas", 0}, {"sgpe", 0}, {"srcd", 0}, {"sms", 0}, {"bh", 0}};
size_t total = 0;
for (const Entry &e : entries) total += entrySize(e.key, e.value);
DerWriter w{out, capacity};
w.put(0x31); w.len(total);
for (const Entry &e : entries) {
uint8_t c[9]; size_t kl = 0; while (e.key[kl]) ++kl;
size_t il = derIntegerContent(e.value, c);
w.put(0x30); w.len((2 + kl) + (2 + il));
w.put(0x0c); w.put((uint8_t)kl); for (size_t i = 0; i < kl; ++i) w.put((uint8_t)e.key[i]);
w.put(0x02); w.put((uint8_t)il); for (size_t i = 0; i < il; ++i) w.put(c[i]);
}
return w.ok ? w.n : 0;
}
class AppleKeyStoreUserClient final : public IOUserClient {
OSDeclareDefaultStructors(AppleKeyStoreUserClient)
public:
IOReturn clientClose() override { terminate(); return kIOReturnSuccess; }
IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments *args, IOExternalMethodDispatch *,
OSObject *, void *) override {
IOReturn rc = kAksUnsupported;
if (!args) rc = kAksError;
else switch (selector) {
// Handshake right after IOServiceOpen (a failure makes libaks drop the connection and reopen on every call), and the
// bag/assertion/notification calls that need no answer in an environment without keybags.
case 0: case 4: case 5: case 9: case 12: case 13: case 26: case 27: case 55: case 101: case 102:
args->scalarOutputCount = 0;
rc = kIOReturnSuccess;
break;
case kSelGetSystem:
if (args->scalarOutputCount < 1 || !args->scalarOutput) { rc = kIOReturnBadArgument; break; }
args->scalarOutput[0] = kSystemHandle;
args->scalarOutputCount = 1;
rc = kIOReturnSuccess;
break;
case kSelLockState:
rc = (args->scalarInputCount == 1 && args->scalarOutputCount == 1 && args->scalarOutput)
? kIOReturnSuccess : kIOReturnBadArgument;
if (rc == kIOReturnSuccess) args->scalarOutput[0] = kUnlockedSinceBoot;
break;
case kSelDeviceState:
case kSelExtendedDeviceState: {
if (args->scalarInputCount > 1 || args->scalarOutputCount != 0) { rc = kIOReturnBadArgument; break; }
uint8_t der[160];
size_t n = native_aks_encode_state(der, sizeof der, native_aks_lock_state());
if (!n) { rc = kIOReturnNoSpace; break; }
if (args->structureOutputDescriptor) {
if (n > args->structureOutputDescriptor->getLength()) { rc = kIOReturnNoSpace; break; }
args->structureOutputDescriptor->writeBytes(0, der, n);
args->structureOutputDescriptorSize = (uint32_t)n;
rc = kIOReturnSuccess;
} else if (args->structureOutput && n <= args->structureOutputSize) {
for (size_t k = 0; k < n; ++k) ((uint8_t *)args->structureOutput)[k] = der[k];
args->structureOutputSize = (uint32_t)n;
rc = kIOReturnSuccess;
} else {
rc = kIOReturnNoSpace;
}
break;
}
default: break;
}
native_aks_call(selector, (int32_t)rc);
return rc;
}
};
OSDefineMetaClassAndStructors(AppleKeyStoreUserClient, IOUserClient)
class AppleKeyStore final : public IOService {
OSDeclareDefaultStructors(AppleKeyStore)
IOReturn open(task_t task, void *security, UInt32 type, OSDictionary *properties, IOUserClient **out) {
if (out) *out = nullptr;
if (!native_aks_state_enabled() || !out) { native_keyevents_open_refused(); return kIOReturnNotReady; }
AppleKeyStoreUserClient *client = new AppleKeyStoreUserClient;
if (!client) return kIOReturnNoMemory;
if (!client->initWithTask(task, security, type, properties) || !client->attach(this)) {
client->release(); return kIOReturnError;
}
if (!client->start(this)) { client->detach(this); client->release(); return kIOReturnError; }
native_aks_opened();
*out = client;
return kIOReturnSuccess;
}
public:
bool start(IOService *provider) override {
if (!IOService::start(provider)) return false;
setName("Native keystore state service");
if (!setProperty("NativeKeyStoreEvents", true) ||
!setProperty("KeyOperationsAvailable", false)) {
IOService::stop(provider);
return false;
}
registerService();
return true;
}
IOReturn newUserClient(task_t t, void *s, UInt32 type, OSDictionary *p, IOUserClient **out) override {
return open(t, s, type, p, out);
}
IOReturn newUserClient(task_t t, void *s, UInt32 type, IOUserClient **out) override {
return open(t, s, type, nullptr, out);
}
};
OSDefineMetaClassAndStructors(AppleKeyStore, IOService)
extern "C" IOService *native_keyevents_allocate(void) { return new AppleKeyStore; }