diff --git a/picokeys_sdk_import.cmake b/picokeys_sdk_import.cmake index 8263d9b..3445c42 100644 --- a/picokeys_sdk_import.cmake +++ b/picokeys_sdk_import.cmake @@ -515,6 +515,10 @@ endif() if(ENABLE_EMULATION) if(APPLE) add_definitions("-Wno-deprecated-declarations") + target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE + "-framework IOKit" + "-framework CoreFoundation" + ) endif() add_compile_definitions(ENABLE_EMULATION) list(APPEND PICOKEYS_SOURCES diff --git a/src/serial.c b/src/serial.c index 3190510..b708ca0 100644 --- a/src/serial.c +++ b/src/serial.c @@ -22,20 +22,252 @@ #include "esp_efuse.h" #endif #include +#include +#ifndef ESP_PLATFORM +#include +#endif + +#if __APPLE__ +#include +#include + +static int get_macos_serial(uint8_t *out) { + io_service_t platformExpert; + CFTypeRef serialNumberAsCFString; + char serial[32] = {0}; + if (!out) { + return -1; + } + out[0] = '\0'; + platformExpert = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("IOPlatformExpertDevice")); + if (!platformExpert) { + return -2; + } + serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert, CFSTR("IOPlatformSerialNumber"), kCFAllocatorDefault, 0); + IOObjectRelease(platformExpert); + if (!serialNumberAsCFString) { + return -3; + } + Boolean ok = CFStringGetCString(serialNumberAsCFString, serial, sizeof(serial), kCFStringEncodingUTF8); + CFRelease(serialNumberAsCFString); + if (ok) { + mbedtls_sha256((const unsigned char *)serial, strlen(serial), pico_serial_hash, false); + memcpy(out, pico_serial_hash, PICO_UNIQUE_BOARD_ID_SIZE_BYTES); + } + return ok ? 0 : -4; +} +#elif _MSC_VER +#include +#include + +#pragma comment(lib, "wbemuuid.lib") + +int get_system_uuid(char *out) { + char serial[64] = {0}; + HRESULT hr; + IWbemLocator *locator = NULL; + IWbemServices *svc = NULL; + IEnumWbemClassObject *enumerator = NULL; + IWbemClassObject *obj = NULL; + ULONG returned = 0; + VARIANT vtProp; + hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) + return -1; + hr = CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); + if (FAILED(hr) && hr != RPC_E_TOO_LATE) { + CoUninitialize(); + return -2; + } + hr = CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *)&locator); + if (FAILED(hr)) { + CoUninitialize(); + return -3; + } + hr = locator->lpVtbl->ConnectServer(locator, L"ROOT\\CIMV2", NULL, NULL, NULL, 0, NULL, NULL, &svc); + if (FAILED(hr)) { + locator->lpVtbl->Release(locator); + CoUninitialize(); + return -4; + } + hr = CoSetProxyBlanket((IUnknown *)svc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); + if (FAILED(hr)) { + svc->lpVtbl->Release(svc); + locator->lpVtbl->Release(locator); + CoUninitialize(); + return -5; + } + hr = svc->lpVtbl->ExecQuery(svc, L"WQL", L"SELECT UUID FROM Win32_ComputerSystemProduct", WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &enumerator); + if (FAILED(hr) || !enumerator) { + svc->lpVtbl->Release(svc); + locator->lpVtbl->Release(locator); + CoUninitialize(); + return -6; + } + hr = enumerator->lpVtbl->Next( enumerator, WBEM_INFINITE, 1, &obj, &returned); + if (returned == 0 || !obj) { + enumerator->lpVtbl->Release(enumerator); + svc->lpVtbl->Release(svc); + locator->lpVtbl->Release(locator); + CoUninitialize(); + return -7; + } + VariantInit(&vtProp); + hr = obj->lpVtbl->Get(obj, L"UUID", 0, &vtProp, NULL, NULL); + if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR && vtProp.bstrVal) { + WideCharToMultiByte(CP_UTF8, 0, vtProp.bstrVal, -1, serial, (int)sizeof(serial), NULL, NULL); + } + VariantClear(&vtProp); + obj->lpVtbl->Release(obj); + enumerator->lpVtbl->Release(enumerator); + svc->lpVtbl->Release(svc); + locator->lpVtbl->Release(locator); + CoUninitialize(); + if (serial[0]) { + mbedtls_sha256((const unsigned char *)serial, strlen(serial), pico_serial_hash, false); + memcpy(out, pico_serial_hash, PICO_UNIQUE_BOARD_ID_SIZE_BYTES); + } + return serial[0] ? 0 : -8; +} +#elif __linux__ +#include +#include + +static int read_first_line(const char *path, char *out, size_t out_len) { + FILE *f; + if (!out || out_len == 0) { + return -1; + } + out[0] = '\0'; + f = fopen(path, "r"); + if (!f) { + return -2; + } + if (!fgets(out, out_len, f)) { + fclose(f); + return -3; + } + fclose(f); + out[strcspn(out, "\r\n")] = '\0'; + return out[0] ? 0 : -4; +} + +static int is_bad_value(const char *s) { + if (!s || !s[0]) + return 1; + if (strcasecmp(s, "None") == 0) + return 1; + if (strcasecmp(s, "Unknown") == 0) + return 1; + if (strcasecmp(s, "Default string") == 0) + return 1; + if (strcasecmp(s, "To be filled by O.E.M.") == 0) + return 1; + if (strcmp(s, "00000000-0000-0000-0000-000000000000") == 0) + return 1; + if (strcmp(s, "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF") == 0) + return 1; + return 0; +} + +static int append_field(char *out, size_t out_len, const char *prefix,const char *path) { + char value[256]; + if (read_first_line(path, value, sizeof(value)) != 0) { + return -1; + } + if (is_bad_value(value)) { + return -2; + } + strncat(out, prefix, out_len - strlen(out) - 1); + strncat(out, value, out_len - strlen(out) - 1); + strncat(out, ";", out_len - strlen(out) - 1); + return 0; +} + +int get_linux_hardware_id(char *out) { + if (!out) { + return -1; + } + char serial[256] = {0}; + append_field(serial, sizeof(serial), "UUID=", "/sys/class/dmi/id/product_uuid"); + append_field(serial, sizeof(serial), "BOARD=", "/sys/class/dmi/id/board_serial"); + append_field(serial, sizeof(serial), "PRODUCT=", "/sys/class/dmi/id/product_serial"); + append_field(serial, sizeof(serial), "CHASSIS=", "/sys/class/dmi/id/chassis_serial"); + append_field(serial, sizeof(serial), "MACHINE=", "/etc/machine-id"); + if (serial[0]) { + mbedtls_sha256((const unsigned char *)serial, strlen(serial), pico_serial_hash, false); + memcpy(out, pico_serial_hash, PICO_UNIQUE_BOARD_ID_SIZE_BYTES); + } + return serial[0] ? 0 : -2; +} +#endif char pico_serial_str[2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1]; uint8_t pico_serial_hash[32]; picokey_serial_t pico_serial; + +static int serial_id_is_zero(const uint8_t *id, size_t len) { + for (size_t i = 0; i < len; i++) { + if (id[i] != 0) { + return 0; + } + } + return 1; +} + +#ifndef ESP_PLATFORM +static void serial_fill_fallback_id(picokey_serial_t *serial, int err) { + uint8_t seed[64] = {0}; + struct timespec ts = {0}; + uintptr_t self_ptr = (uintptr_t)serial; + uintptr_t init_ptr = (uintptr_t)&serial_init; + size_t off = 0; + + (void)timespec_get(&ts, TIME_UTC); + memcpy(seed + off, &err, sizeof(err)); + off += sizeof(err); + memcpy(seed + off, &ts.tv_sec, sizeof(ts.tv_sec)); + off += sizeof(ts.tv_sec); + memcpy(seed + off, &ts.tv_nsec, sizeof(ts.tv_nsec)); + off += sizeof(ts.tv_nsec); + memcpy(seed + off, &self_ptr, sizeof(self_ptr)); + off += sizeof(self_ptr); + memcpy(seed + off, &init_ptr, sizeof(init_ptr)); + off += sizeof(init_ptr); + + mbedtls_sha256(seed, off, pico_serial_hash, false); + memcpy(serial->id, pico_serial_hash, sizeof(serial->id)); +} +#endif + #ifdef ESP_PLATFORM #define pico_get_unique_board_id(a) do { uint32_t value; esp_efuse_read_block(EFUSE_BLK1, &value, 0, 32); memcpy((uint8_t *)(a), &value, sizeof(uint32_t)); esp_efuse_read_block(EFUSE_BLK1, &value, 32, 32); memcpy((uint8_t *)(a)+4, &value, sizeof(uint32_t)); } while(0) #else -#ifndef PICO_PLATFORM -#define pico_get_unique_board_id(a) memset(a, 0, sizeof(*(a))) +#if __APPLE__ +#define pico_get_unique_board_id(a) get_macos_serial((uint8_t *)(a)) +#elif _MSC_VER +#define pico_get_unique_board_id(a) get_system_uuid((char *)(a)) +#elif __linux__ +#define pico_get_unique_board_id(a) get_linux_hardware_id((char *)(a)) +#else +#error "Unsupported platform" #endif #endif void serial_init(void) { + int serial_rc = 0; + +#ifndef ESP_PLATFORM + serial_rc = pico_get_unique_board_id(&pico_serial); +#else pico_get_unique_board_id(&pico_serial); +#endif + + if (serial_rc != 0 || serial_id_is_zero(pico_serial.id, sizeof(pico_serial.id))) { + printf("serial init: failed to read stable hardware id (rc=%d); using fallback id\n", serial_rc); + serial_fill_fallback_id(&pico_serial, serial_rc); + } + memset(pico_serial_str, 0, sizeof(pico_serial_str)); for (size_t i = 0; i < sizeof(pico_serial); i++) { snprintf(&pico_serial_str[2 * i], 3, "%02X", pico_serial.id[i]); diff --git a/src/serial.h b/src/serial.h index 3004845..f8e2b0f 100644 --- a/src/serial.h +++ b/src/serial.h @@ -21,7 +21,11 @@ #include #if !defined (PICO_PLATFORM) +#if __APPLE__ +#define PICO_UNIQUE_BOARD_ID_SIZE_BYTES 16 +#else #define PICO_UNIQUE_BOARD_ID_SIZE_BYTES 8 +#endif typedef struct { uint8_t id[PICO_UNIQUE_BOARD_ID_SIZE_BYTES]; } picokey_serial_t; #else #include "pico/unique_id.h"