// Simulator backend: flash is emulated in a file (SKEY_FLASH_FILE), randomness // comes from the OS, and HID packets travel over stdin/stdout. // // The button: without SKEY_BUTTON_FILE every request is approved. With it, // creating that file is a press (its mtime is the press time), and the // simulator deletes it when the press is used. #include #include #include #include #include #include #include #include #include "host.h" #include "platform.h" static uint8_t flash[STORAGE_SIZE]; static const char *flash_path; static const char *button_path; static struct timespec start; void platform_init(void) { flash_path = getenv("SKEY_FLASH_FILE"); if (!flash_path) flash_path = "skey_flash.bin"; button_path = getenv("SKEY_BUTTON_FILE"); memset(flash, 0xff, sizeof(flash)); FILE *f = fopen(flash_path, "rb"); if (f) { if (fread(flash, 1, sizeof(flash), f) != sizeof(flash)) memset(flash, 0xff, sizeof(flash)); fclose(f); } clock_gettime(CLOCK_MONOTONIC, &start); } uint32_t platform_millis(void) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); return (uint32_t)((now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - start.tv_nsec) / 1000000); } void platform_random(uint8_t *buf, size_t len) { while (len > 0) { ssize_t n = getrandom(buf, len, 0); if (n < 0) platform_panic("getrandom failed"); buf += n; len -= (size_t)n; } } void platform_panic(const char *msg) { fprintf(stderr, "skey_sim: %s\n", msg); abort(); } bool platform_button_take_press(uint32_t max_age_ms) { struct stat st; struct timespec now; if (!button_path) return true; if (stat(button_path, &st) != 0) return false; unlink(button_path); clock_gettime(CLOCK_REALTIME, &now); int64_t age_ms = (int64_t)(now.tv_sec - st.st_mtim.tv_sec) * 1000 + (now.tv_nsec - st.st_mtim.tv_nsec) / 1000000; return age_ms <= (int64_t)max_age_ms; } void platform_led(bool on) { (void)on; } void platform_wink(void) { fprintf(stderr, "skey_sim: wink\n"); } static void save_flash(void) { FILE *f = fopen(flash_path, "wb"); if (!f || fwrite(flash, 1, sizeof(flash), f) != sizeof(flash)) platform_panic("cannot write flash file"); fclose(f); } void platform_flash_read(uint32_t offset, void *buf, size_t len) { if (offset > STORAGE_SIZE || len > STORAGE_SIZE - offset) platform_panic("flash read out of range"); memcpy(buf, flash + offset, len); } void platform_flash_erase(uint32_t offset) { if (offset % STORAGE_SECTOR_SIZE || offset >= STORAGE_SIZE) platform_panic("bad flash erase"); memset(flash + offset, 0xff, STORAGE_SECTOR_SIZE); save_flash(); } // Behaves like NOR flash: whole pages only, and programming only clears bits. void platform_flash_program(uint32_t offset, const void *buf, size_t len) { const uint8_t *src = buf; if (offset % STORAGE_PAGE_SIZE || len % STORAGE_PAGE_SIZE || offset > STORAGE_SIZE || len > STORAGE_SIZE - offset) platform_panic("bad flash program"); for (size_t i = 0; i < len; i++) flash[offset + i] &= src[i]; save_flash(); } void platform_hid_send(const uint8_t packet[HID_PACKET_SIZE]) { fwrite(packet, 1, HID_PACKET_SIZE, stdout); fflush(stdout); } bool host_receive(uint8_t packet[HID_PACKET_SIZE], int timeout_ms) { struct pollfd pfd = {.fd = STDIN_FILENO, .events = POLLIN}; if (poll(&pfd, 1, timeout_ms) <= 0) return false; for (size_t got = 0; got < HID_PACKET_SIZE;) { ssize_t n = read(STDIN_FILENO, packet + got, HID_PACKET_SIZE - got); if (n <= 0) exit(0); // the host closed the pipe: unplugged got += (size_t)n; } return true; } bool platform_hid_receive(uint8_t packet[HID_PACKET_SIZE]) { return host_receive(packet, 0); }