diff --git a/firmware/src/commands.c b/firmware/src/commands.c index 91d3866..9087dcc 100644 --- a/firmware/src/commands.c +++ b/firmware/src/commands.c @@ -67,7 +67,16 @@ static void disp_hid() { const char *joy_map[] = {"Switch", "Steam", "Arcade", "PS4"}; printf("[HID]\n"); - printf(" Keymap: %s\n", joy_map[diva_cfg->hid.joy_map % 4]); + + int joy = diva_cfg->hid.joy_map % 4; + + const char *key_info = "\0"; + if (joy == 3) { + const ps4key_t *key = (const ps4key_t *)savedata_get_global(); + key_info = ps4key_key_valid(key) ? " (Key loaded)" : " (No key, disconnected every 8 minutes)"; + } + + printf(" Keymap: %s%s\n", joy_map[joy], key_info); } void handle_display(int argc, char *argv[]) @@ -431,7 +440,7 @@ static void handle_factory_reset() static void handle_ps4key(int argc, char *argv[]) { - static ps4key_data_t ps4key_data; + static ps4key_t key; const char *usage = "Usage: ps4key \n"; if (argc != 1) { @@ -440,23 +449,21 @@ static void handle_ps4key(int argc, char *argv[]) } if (strncasecmp(argv[0], "clear", strlen(argv[0])) == 0) { - ps4key_clear_data(&ps4key_data); + memset(&key, 0, sizeof(key)); savedata_clear_global(); printf("PS4 key data cleared.\n"); return; } - const char *error = NULL; - if (!ps4key_parse_text(argv[0], &ps4key_data, &error)) { - printf("PS4 key import failed: %s\n", error ? error : "Unknown error."); + const char *error = "Unknown"; + if (!ps4key_parse_text(argv[0], &key, &error)) { + printf("PS4 key import failed: %s\n", error); return; } - savedata_write_global(&ps4key_data, sizeof(ps4key_data)); - printf("PS4 key stored: serial=%s pem=%u bytes sig=%u bytes.\n", - ps4key_data.serial, - (unsigned)ps4key_data.pem_len, - (unsigned)ps4key_data.sig_len); + savedata_write_global(&key, sizeof(key)); + printf("PS4 key stored: Serial: %s, PEM: %d bytes, sig: %d bytes\n", + ps4key_get_serial(&key), key.pem_len - 1, key.sig_len); } void commands_init() diff --git a/firmware/src/ps4key.c b/firmware/src/ps4key.c index 9ac7283..39f2bae 100644 --- a/firmware/src/ps4key.c +++ b/firmware/src/ps4key.c @@ -1,16 +1,13 @@ -#include "ps4key.h" +/* + * PS4 Key import and de-serialization + * WHowe + */ #include +#include #include -typedef struct __attribute__((packed)) { - uint8_t magic[4]; - uint8_t version; - uint16_t serial_len; - uint16_t pem_len; - uint16_t sig_len; - uint32_t crc32; -} ps4key_wire_header_t; +#include "ps4key.h" static uint32_t crc32_calc(const uint8_t *data, size_t size) { @@ -109,133 +106,156 @@ static bool base64_decode(const char *input, uint8_t *output, static bool serial_is_valid(const uint8_t *serial, size_t size) { - if (size != PS4KEY_SERIAL_LENGTH) { + if (size != (PS4KEY_SERIAL_LENGTH + 1)) { return false; } - for (size_t i = 0; i < size; i++) { + for (size_t i = 0; i < PS4KEY_SERIAL_LENGTH; i++) { if (!isdigit(serial[i])) { return false; } } + if (serial[PS4KEY_SERIAL_LENGTH] != '\0') { + return false; + } + return true; } static bool pem_is_valid(const char *pem) { - return strstr(pem, "-----BEGIN RSA PRIVATE KEY-----") != NULL && - strstr(pem, "-----END RSA PRIVATE KEY-----") != NULL; + return strstr(pem, "-----BEGIN RSA PRIVATE KEY-----") && + strstr(pem, "-----END RSA PRIVATE KEY-----"); } -void ps4key_clear_data(ps4key_data_t *data) +static void set_error(const char **error, const char *message) { - if (data == NULL) { - return; + if (error) { + *error = message; } - memset(data, 0, sizeof(*data)); } -bool ps4key_parse_text(const char *text, ps4key_data_t *data, const char **error) +static bool ps4key_validate(const ps4key_t *key, const char **error) { - static uint8_t decoded[sizeof(ps4key_wire_header_t) + - PS4KEY_SERIAL_LENGTH + - PS4KEY_PEM_MAX_LENGTH + - PS4KEY_SIG_MAX_LENGTH]; - - if ((text == NULL) || (data == NULL)) { - if (error) { - *error = "Missing input."; - } + if (key == NULL) { + set_error(error, "Missing input."); return false; } - if (strncmp(text, PS4KEY_TEXT_PREFIX, strlen(PS4KEY_TEXT_PREFIX)) != 0) { - if (error) { - *error = "Missing PS4K1 prefix."; - } + if ((key->magic != PS4KEY_STORAGE_MAGIC) || + (key->version != PS4KEY_STORAGE_VERSION)) { + set_error(error, "Unexpected serialization header."); return false; } - size_t decoded_len = 0; - if (!base64_decode(text + strlen(PS4KEY_TEXT_PREFIX), decoded, - sizeof(decoded), &decoded_len)) { - if (error) { - *error = "Base64 decode failed."; - } + if ((key->serial_len != (PS4KEY_SERIAL_LENGTH + 1)) || + (key->pem_len <= 1) || (key->pem_len > (PS4KEY_PEM_MAX_LENGTH + 1)) || + (key->sig_len == 0) || (key->sig_len > PS4KEY_SIG_MAX_LENGTH)) { + set_error(error, "Serialized part lengths are invalid."); return false; } - if (decoded_len < sizeof(ps4key_wire_header_t)) { - if (error) { - *error = "Serialized data is too short."; - } + size_t payload_len = key->serial_len + key->pem_len + key->sig_len; + if (payload_len > sizeof(key->payload)) { + set_error(error, "Serialized length mismatch."); return false; } - const ps4key_wire_header_t *header = (const ps4key_wire_header_t *)decoded; - if ((memcmp(header->magic, "PS4K", 4) != 0) || - (header->version != PS4KEY_STORAGE_VERSION)) { - if (error) { - *error = "Unexpected serialization header."; - } + if (crc32_calc(key->payload, payload_len) != key->crc32) { + set_error(error, "CRC32 mismatch."); return false; } - size_t payload_len = (size_t)header->serial_len + header->pem_len + header->sig_len; - if (decoded_len != sizeof(*header) + payload_len) { - if (error) { - *error = "Serialized length mismatch."; - } + if (!serial_is_valid(key->payload, key->serial_len)) { + set_error(error, "Invalid serial."); return false; } - if ((header->serial_len != PS4KEY_SERIAL_LENGTH) || - (header->pem_len == 0) || (header->pem_len > PS4KEY_PEM_MAX_LENGTH) || - (header->sig_len == 0) || (header->sig_len > PS4KEY_SIG_MAX_LENGTH)) { - if (error) { - *error = "Serialized part lengths are invalid."; - } + const uint8_t *pem_ptr = key->payload + key->serial_len; + if (pem_ptr[key->pem_len - 1] != '\0') { + set_error(error, "PEM data is not null terminated."); return false; } - const uint8_t *payload = decoded + sizeof(*header); - if (crc32_calc(payload, payload_len) != header->crc32) { - if (error) { - *error = "CRC32 mismatch."; - } - return false; - } - - if (!serial_is_valid(payload, header->serial_len)) { - if (error) { - *error = "Serial must be exactly 16 decimal digits."; - } - return false; - } - - const char *pem = (const char *)(payload + header->serial_len); - const uint8_t *sig = payload + header->serial_len + header->pem_len; - - ps4key_clear_data(data); - data->magic = PS4KEY_STORAGE_MAGIC; - data->version = PS4KEY_STORAGE_VERSION; - data->serial_len = header->serial_len; - data->pem_len = header->pem_len; - data->sig_len = header->sig_len; - data->crc32 = header->crc32; - memcpy(data->serial, payload, header->serial_len); - memcpy(data->pem, pem, header->pem_len); - memcpy(data->sig, sig, header->sig_len); - data->serial[header->serial_len] = '\0'; - data->pem[header->pem_len] = '\0'; - - if (!pem_is_valid(data->pem)) { - if (error) { - *error = "PEM content does not look like an RSA private key."; - } - ps4key_clear_data(data); + if (!pem_is_valid((const char *)pem_ptr)) { + set_error(error, "PEM content does not look like an RSA private key."); return false; } return true; -} \ No newline at end of file +} + +bool ps4key_parse_text(const char *text, ps4key_t *key, const char **error) +{ + static uint8_t decoded[sizeof(ps4key_t)]; + + if ((text == NULL) || (key == NULL)) { + set_error(error, "Missing input."); + return false; + } + + if (strncmp(text, PS4KEY_TEXT_PREFIX, strlen(PS4KEY_TEXT_PREFIX)) != 0) { + set_error(error, "Missing PS4K1 prefix."); + return false; + } + + size_t decoded_len = 0; + if (!base64_decode(text + strlen(PS4KEY_TEXT_PREFIX), decoded, sizeof(decoded), &decoded_len)) { + set_error(error, "Base64 decode failed."); + return false; + } + + const size_t header_len = offsetof(ps4key_t, payload); + if (decoded_len < header_len) { + set_error(error, "Serialized data is too short."); + return false; + } + const ps4key_t *src = (const ps4key_t *)decoded; + size_t payload_len = src->serial_len + src->pem_len + src->sig_len; + if (decoded_len != header_len + payload_len) { + set_error(error, "Serialized length mismatch."); + return false; + } + + memset(key, 0, sizeof(*key)); + memcpy(key, decoded, decoded_len); + + return ps4key_validate(key, error); +} + +bool ps4key_key_valid(const ps4key_t *key) +{ + return ps4key_validate(key, NULL); +} + +const char *ps4key_get_serial(const ps4key_t *key) +{ + if (key == NULL) { + return NULL; + } + return (const char *)key->payload; +} + +const char *ps4key_get_pem(const ps4key_t *key) +{ + if (key == NULL) { + return NULL; + } + return (const char *)(key->payload + key->serial_len); +} + +const uint8_t *ps4key_get_sig(const ps4key_t *key) +{ + if (key == NULL) { + return NULL; + } + return key->payload + key->serial_len + key->pem_len; +} + +size_t ps4key_get_sig_len(const ps4key_t *key) +{ + if (key == NULL) { + return 0; + } + return key->sig_len; +} diff --git a/firmware/src/ps4key.h b/firmware/src/ps4key.h index 884c4ff..1829b20 100644 --- a/firmware/src/ps4key.h +++ b/firmware/src/ps4key.h @@ -1,5 +1,6 @@ /* - * PS4 key serialization support + * PS4 Key import and de-serialization + * WHowe */ #ifndef PS4KEY_H @@ -15,20 +16,25 @@ #define PS4KEY_SERIAL_LENGTH 16 #define PS4KEY_PEM_MAX_LENGTH 2048 #define PS4KEY_SIG_MAX_LENGTH 512 +#define PS4KEY_PAYLOAD_MAX_LENGTH (PS4KEY_SERIAL_LENGTH + 1 + PS4KEY_PEM_MAX_LENGTH + 1 + PS4KEY_SIG_MAX_LENGTH) typedef struct __attribute__((packed)) { uint32_t magic; - uint16_t version; + uint8_t version; + uint8_t reserved; uint16_t serial_len; uint16_t pem_len; uint16_t sig_len; uint32_t crc32; - char serial[PS4KEY_SERIAL_LENGTH + 1]; - char pem[PS4KEY_PEM_MAX_LENGTH + 1]; - uint8_t sig[PS4KEY_SIG_MAX_LENGTH]; -} ps4key_data_t; + uint8_t payload[PS4KEY_PAYLOAD_MAX_LENGTH]; +} ps4key_t; -void ps4key_clear_data(ps4key_data_t *data); -bool ps4key_parse_text(const char *text, ps4key_data_t *data, const char **error); +bool ps4key_parse_text(const char *text, ps4key_t *key, const char **error); +bool ps4key_key_valid(const ps4key_t *key); + +const char *ps4key_get_serial(const ps4key_t *key); +const char *ps4key_get_pem(const ps4key_t *key); +const uint8_t *ps4key_get_sig(const ps4key_t *key); +size_t ps4key_get_sig_len(const ps4key_t *key); #endif \ No newline at end of file diff --git a/firmware/src/savedata.c b/firmware/src/savedata.c index bf8f6f9..11b757c 100644 --- a/firmware/src/savedata.c +++ b/firmware/src/savedata.c @@ -32,7 +32,7 @@ static uint32_t my_magic = 0xcafecafe; #define SAVE_TIMEOUT_US 5000000 #define SAVE_SECTOR_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE) -#define GLOBAL_SECTOR_NUM 2 +#define GLOBAL_SECTOR_NUM 1 #define GLOBAL_SECTOR_SIZE (GLOBAL_SECTOR_NUM * FLASH_SECTOR_SIZE) #define GLOBAL_SECTOR_OFFSET (SAVE_SECTOR_OFFSET - GLOBAL_SECTOR_SIZE) @@ -164,16 +164,9 @@ void savedata_request(bool immediately) } } - -void savedata_read_global(size_t offset, void *data, size_t size) +void *savedata_get_global() { - if ((data == NULL) || (size == 0) || (offset >= GLOBAL_SECTOR_SIZE)) { - return; - } - if (size > GLOBAL_SECTOR_SIZE - offset) { - size = GLOBAL_SECTOR_SIZE - offset; - } - memcpy(data, (void *)(XIP_BASE + GLOBAL_SECTOR_OFFSET + offset), size); + return (void *)(XIP_BASE + GLOBAL_SECTOR_OFFSET); } static void do_write_global(void *param) @@ -211,7 +204,7 @@ void savedata_write_global(const void *data, size_t size) param[0] = (uintptr_t)global_write_buffer; param[1] = padded; - printf("Program Global %8x ", GLOBAL_SECTOR_OFFSET); + printf("Program Global %08x ", GLOBAL_SECTOR_OFFSET); if (flash_safe_execute(do_write_global, param, 1000) != PICO_OK) { printf("Failed!\n"); } else { diff --git a/firmware/src/savedata.h b/firmware/src/savedata.h index 1a2357c..c6c848b 100644 --- a/firmware/src/savedata.h +++ b/firmware/src/savedata.h @@ -20,7 +20,7 @@ void savedata_loop(); void *savedata_alloc(size_t size, void *def, void (*after_load)()); void savedata_request(bool immediately); -void savedata_read_global(size_t offset, void *data, size_t size); +void *savedata_get_global(); void savedata_write_global(const void *data, size_t size); void savedata_clear_global(); size_t savedata_global_size(); diff --git a/tools/ps4key_packer.html b/tools/ps4key_packer.html index da0d2dc..94971e1 100644 --- a/tools/ps4key_packer.html +++ b/tools/ps4key_packer.html @@ -3,7 +3,7 @@ - PS4 Key Serialization for Diva Pico + PS4 Key Packer for Diva Pico
-

PS4 Key Serialization for Diva Pico

-

Pack signature.bin, serial.txt, and key.pem into one copy-friendly plain text string for the new ps4key CLI command. The format is PS4K1 + Base64(binary payload) with a CRC32 over the raw payload bytes.

+

PS4 Key Packer for Diva Pico

-
-
-

Input

-

Provide the binary signature, the 16-digit serial, and the RSA private key text. Validation is intentionally strict to catch common mistakes before flashing.

+
+
+
+

Input

+

Provide the binary signature, the serial, and the RSA private key files.

+ + - - + + - - + + - - - -
- +
+
-
-
+
+

Output

+ +
+
-
-

Output

-

Single-line plain text, suitable for direct paste into the CLI as ps4key <serialized_key>.

- -
- +
+
+ +
+
+
-
@@ -316,20 +318,20 @@ function assertInputs(serial, pem, sigBytes) { if (!sigBytes || sigBytes.length === 0) { - throw new Error("请上传 signature.bin 二进制文件。"); + throw new Error("Please upload the signature.bin file."); } if (sigBytes.length > SIG_MAX_LENGTH) { - throw new Error(`signature.bin 二进制文件过大,最大 ${SIG_MAX_LENGTH} 字节。`); + throw new Error(`signature.bin is too large, max ${SIG_MAX_LENGTH} bytes.`); } if (!/^\d{16}$/.test(serial)) { - throw new Error("serial.txt 必须是 16 位十进制字符串。"); + throw new Error("serial.txt must be a 16-digit decimal string."); } if (!pem.includes("-----BEGIN RSA PRIVATE KEY-----") || !pem.includes("-----END RSA PRIVATE KEY-----")) { - throw new Error("key.pem 必须包含 BEGIN/END RSA PRIVATE KEY 标记。"); + throw new Error("key.pem must contain BEGIN/END RSA PRIVATE KEY markers."); } if (pem.length === 0 || pem.length > PEM_MAX_LENGTH) { - throw new Error(`key.pem 长度必须在 1-${PEM_MAX_LENGTH} 字符之间。`); + throw new Error(`key.pem length must be 1-${PEM_MAX_LENGTH} characters.`); } } @@ -357,20 +359,25 @@ function packPayload(serial, pem, sigBytes) { const serialBytes = encoder.encode(serial); const pemBytes = encoder.encode(pem); - const payloadLength = serialBytes.length + pemBytes.length + sigBytes.length; + const serialLen = serialBytes.length + 1; + const pemLen = pemBytes.length + 1; + const payloadLength = serialLen + pemLen + sigBytes.length; const payload = new Uint8Array(payloadLength); payload.set(serialBytes, 0); - payload.set(pemBytes, serialBytes.length); - payload.set(sigBytes, serialBytes.length + pemBytes.length); + payload[serialBytes.length] = 0; + payload.set(pemBytes, serialLen); + payload[serialLen + pemBytes.length] = 0; + payload.set(sigBytes, serialLen + pemLen); - const header = new Uint8Array(15); + const header = new Uint8Array(16); header.set([0x50, 0x53, 0x34, 0x4b], 0); header[4] = 1; + header[5] = 0; const view = new DataView(header.buffer); - view.setUint16(5, serialBytes.length, true); - view.setUint16(7, pemBytes.length, true); - view.setUint16(9, sigBytes.length, true); - view.setUint32(11, crc32(payload), true); + view.setUint16(6, serialLen, true); + view.setUint16(8, pemLen, true); + view.setUint16(10, sigBytes.length, true); + view.setUint32(12, crc32(payload), true); const packed = new Uint8Array(header.length + payload.length); packed.set(header, 0); @@ -393,17 +400,18 @@ async function doPack() { try { - // 前置文件选择校验 + + // Pre-file selection validation if (!sigFileInput.files || !sigFileInput.files[0]) { - setStatus("error", "请先选择 sig.bin 文件。"); + setStatus("error", "Please select sig.bin file."); return; } if (!serialFileInput.files || !serialFileInput.files[0]) { - setStatus("error", "请先选择 serial.txt 文件。"); + setStatus("error", "Please select serial.txt file."); return; } if (!pemFileInput.files || !pemFileInput.files[0]) { - setStatus("error", "请先选择 key.pem 文件。"); + setStatus("error", "Please select key.pem file."); return; } @@ -411,16 +419,17 @@ const serialText = await readFileAsText(serialFileInput); const pemText = await readFileAsText(pemFileInput); + if (!sigBytes || sigBytes.length === 0) { - setStatus("error", "sig.bin 文件读取失败或为空。"); + setStatus("error", "sig.bin file read failed or empty."); return; } if (!serialText || serialText.trim().length === 0) { - setStatus("error", "serial.txt 文件读取失败或为空。"); + setStatus("error", "serial.txt file read failed or empty."); return; } if (!pemText || pemText.trim().length === 0) { - setStatus("error", "key.pem 文件读取失败或为空。"); + setStatus("error", "key.pem file read failed or empty."); return; } @@ -431,13 +440,7 @@ const packed = packPayload(serial, pem, sigBytes); output.value = packed; - setMeta([ - `serial: ${serial.length} bytes`, - `pem: ${pem.length} bytes`, - `sig: ${sigBytes.length} bytes`, - `output: ${packed.length} chars` - ]); - setStatus("ok", "打包成功。"); + setStatus("ok", `Packed successfully, total ${packed.length} chars.`); } catch (error) { output.value = ""; setMeta([]); @@ -447,7 +450,6 @@ async function copyOutput() { if (!output.value) { - setOutputStatus("warn", "Nothing to copy."); return; } @@ -458,7 +460,6 @@ } catch (error) { output.focus(); output.select(); - setOutputStatus("warn", "Clipboard API is unavailable. The output has been selected."); } }