From ed576a07e599380c94785695fd0fc0251671ff8f Mon Sep 17 00:00:00 2001 From: jay candel Date: Tue, 15 Oct 2024 06:12:22 +0800 Subject: [PATCH 1/6] add decode_char --- .../main/nfc/plugins/supported_cards/ndef.c | 61 +++++++++++++++---- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c index 05b31f32b..aabc2da41 100644 --- a/applications/main/nfc/plugins/supported_cards/ndef.c +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -40,6 +40,18 @@ static void furi_string_cat(str, "\n"); } +static char decode_char(const char* str) { + int high = (str[1] >= 'A' && str[1] <= 'F') ? (str[1] - 'A' + 10) : + (str[1] >= 'a' && str[1] <= 'f') ? (str[1] - 'a' + 10) : + (str[1] >= '0' && str[1] <= '9') ? (str[1] - '0') : + 0; + int low = (str[2] >= 'A' && str[2] <= 'F') ? (str[2] - 'A' + 10) : + (str[2] >= 'a' && str[2] <= 'f') ? (str[2] - 'a' + 10) : + (str[2] >= '0' && str[2] <= '9') ? (str[2] - '0') : + 0; + return (high << 4) | low; +} + static void parse_ndef_uri(FuriString* str, const uint8_t* payload, uint32_t payload_len) { // https://learn.adafruit.com/adafruit-pn532-rfid-nfc/ndef#uri-records-0x55-slash-u-607763 const char* prepends[] = { @@ -86,29 +98,56 @@ static void parse_ndef_uri(FuriString* str, const uint8_t* payload, uint32_t pay prepend = prepends[prepend_type]; } size_t prepend_len = strlen(prepend); - size_t uri_len = prepend_len + (payload_len - 1); - char* const uri_buf = malloc(uri_len); + + char* uri_buf = malloc(uri_len + 1); + if(!uri_buf) { + furi_string_cat_printf(str, "Memory allocation failed\n"); + return; + } + memcpy(uri_buf, prepend, prepend_len); memcpy(uri_buf + prepend_len, payload + 1, payload_len - 1); - char* uri = uri_buf; + uri_buf[uri_len] = '\0'; + + char* decoded_uri = + malloc(uri_len * 2 + 1); // Worst case scenario: every character is percent-encoded + if(!decoded_uri) { + furi_string_cat_printf(str, "Memory allocation failed\n"); + free(uri_buf); + return; + } + + size_t decoded_len = 0; + for(size_t i = 0; i < uri_len; i++) { + if(uri_buf[i] == '%' && i + 2 < uri_len) { + decoded_uri[decoded_len++] = decode_char(&uri_buf[i]); + i += 2; + } else { + decoded_uri[decoded_len++] = uri_buf[i]; + } + } + decoded_uri[decoded_len] = '\0'; const char* type = "URI"; - if(strncmp(uri, "http", strlen("http")) == 0) { + const char* uri_content = decoded_uri; + if(strncmp(decoded_uri, "http", 4) == 0) { type = "URL"; - } else if(strncmp(uri, "tel:", strlen("tel:")) == 0) { + } else if(strncmp(decoded_uri, "tel:", 4) == 0) { type = "Phone"; - uri += strlen("tel:"); - uri_len -= strlen("tel:"); - } else if(strncmp(uri, "mailto:", strlen("mailto:")) == 0) { + uri_content += 4; + decoded_len -= 4; + } else if(strncmp(decoded_uri, "mailto:", 7) == 0) { type = "Mail"; - uri += strlen("mailto:"); - uri_len -= strlen("mailto:"); + uri_content += 7; + decoded_len -= 7; } furi_string_cat_printf(str, "%s\n", type); - print_data(str, NULL, (uint8_t*)uri, uri_len, false); + print_data(str, NULL, (uint8_t*)uri_content, decoded_len, false); + free(uri_buf); + free(decoded_uri); } static void parse_ndef_text(FuriString* str, const uint8_t* payload, uint32_t payload_len) { From 179667a32061bfaee9e25adc41a2aed1e76becd1 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 20 Oct 2024 04:54:40 +0100 Subject: [PATCH 2/6] Skip double malloc(), can just tell printf() how long the string is --- applications/main/nfc/plugins/supported_cards/ndef.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c index aabc2da41..84df6c189 100644 --- a/applications/main/nfc/plugins/supported_cards/ndef.c +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -27,11 +27,7 @@ static void print_data(FuriString* str, const char* prefix, const uint8_t* buf, size_t len, bool force_hex) { if(prefix) furi_string_cat_printf(str, "%s: ", prefix); if(!force_hex && is_text(buf, len)) { - char* tmp = malloc(len + 1); - memcpy(tmp, buf, len); - tmp[len] = '\0'; - furi_string_cat_printf(str, "%s", tmp); - free(tmp); + furi_string_cat_printf(str, "%.*s", len, buf); } else { for(uint8_t i = 0; i < len; i++) { furi_string_cat_printf(str, "%02X ", buf[i]); From 54d690389b5149a6b37b4f05e24364e144e3e7ab Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 20 Oct 2024 04:55:13 +0100 Subject: [PATCH 3/6] Decode URI within the same buffer --- .../main/nfc/plugins/supported_cards/ndef.c | 46 +++++++------------ 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c index 84df6c189..9a7c407f6 100644 --- a/applications/main/nfc/plugins/supported_cards/ndef.c +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -94,56 +94,42 @@ static void parse_ndef_uri(FuriString* str, const uint8_t* payload, uint32_t pay prepend = prepends[prepend_type]; } size_t prepend_len = strlen(prepend); - size_t uri_len = prepend_len + (payload_len - 1); - - char* uri_buf = malloc(uri_len + 1); - if(!uri_buf) { - furi_string_cat_printf(str, "Memory allocation failed\n"); - return; - } + size_t uri_len = prepend_len + (payload_len - 1); + char* const uri_buf = malloc(uri_len); // const to keep the original pointer to free later memcpy(uri_buf, prepend, prepend_len); memcpy(uri_buf + prepend_len, payload + 1, payload_len - 1); - uri_buf[uri_len] = '\0'; - - char* decoded_uri = - malloc(uri_len * 2 + 1); // Worst case scenario: every character is percent-encoded - if(!decoded_uri) { - furi_string_cat_printf(str, "Memory allocation failed\n"); - free(uri_buf); - return; - } + char* uri = uri_buf; // cursor we can iterate and shift freely + // Encoded chars take 3 bytes (%AB), decoded chars take 1 byte + // We can decode by iterating and overwriting the same buffer size_t decoded_len = 0; - for(size_t i = 0; i < uri_len; i++) { - if(uri_buf[i] == '%' && i + 2 < uri_len) { - decoded_uri[decoded_len++] = decode_char(&uri_buf[i]); - i += 2; + for(size_t encoded_idx = 0; encoded_idx < uri_len; encoded_idx++) { + if(uri[encoded_idx] == '%' && encoded_idx + 2 < uri_len) { + uri[decoded_len++] = decode_char(&uri[encoded_idx]); + encoded_idx += 2; } else { - decoded_uri[decoded_len++] = uri_buf[i]; + uri[decoded_len++] = uri[encoded_idx]; } } - decoded_uri[decoded_len] = '\0'; const char* type = "URI"; - const char* uri_content = decoded_uri; - if(strncmp(decoded_uri, "http", 4) == 0) { + if(strncmp(uri, "http", 4) == 0) { type = "URL"; - } else if(strncmp(decoded_uri, "tel:", 4) == 0) { + } else if(strncmp(uri, "tel:", 4) == 0) { type = "Phone"; - uri_content += 4; + uri += 4; decoded_len -= 4; - } else if(strncmp(decoded_uri, "mailto:", 7) == 0) { + } else if(strncmp(uri, "mailto:", 7) == 0) { type = "Mail"; - uri_content += 7; + uri += 7; decoded_len -= 7; } furi_string_cat_printf(str, "%s\n", type); - print_data(str, NULL, (uint8_t*)uri_content, decoded_len, false); + print_data(str, NULL, (uint8_t*)uri, decoded_len, false); free(uri_buf); - free(decoded_uri); } static void parse_ndef_text(FuriString* str, const uint8_t* payload, uint32_t payload_len) { From 3f114ae4dc0c49968fb85183fb7cbb38c49f65b5 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 20 Oct 2024 05:32:22 +0100 Subject: [PATCH 4/6] Only decode if next 2 chars are valid hex --- .../main/nfc/plugins/supported_cards/ndef.c | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c index 9a7c407f6..ded41d09a 100644 --- a/applications/main/nfc/plugins/supported_cards/ndef.c +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -37,14 +37,14 @@ static void } static char decode_char(const char* str) { - int high = (str[1] >= 'A' && str[1] <= 'F') ? (str[1] - 'A' + 10) : - (str[1] >= 'a' && str[1] <= 'f') ? (str[1] - 'a' + 10) : - (str[1] >= '0' && str[1] <= '9') ? (str[1] - '0') : - 0; - int low = (str[2] >= 'A' && str[2] <= 'F') ? (str[2] - 'A' + 10) : - (str[2] >= 'a' && str[2] <= 'f') ? (str[2] - 'a' + 10) : - (str[2] >= '0' && str[2] <= '9') ? (str[2] - '0') : - 0; + uint8_t high = (str[1] >= 'A' && str[1] <= 'F') ? (str[1] - 'A' + 10) : + (str[1] >= 'a' && str[1] <= 'f') ? (str[1] - 'a' + 10) : + (str[1] >= '0' && str[1] <= '9') ? (str[1] - '0') : + 0; + uint8_t low = (str[2] >= 'A' && str[2] <= 'F') ? (str[2] - 'A' + 10) : + (str[2] >= 'a' && str[2] <= 'f') ? (str[2] - 'a' + 10) : + (str[2] >= '0' && str[2] <= '9') ? (str[2] - '0') : + 0; return (high << 4) | low; } @@ -106,11 +106,16 @@ static void parse_ndef_uri(FuriString* str, const uint8_t* payload, uint32_t pay size_t decoded_len = 0; for(size_t encoded_idx = 0; encoded_idx < uri_len; encoded_idx++) { if(uri[encoded_idx] == '%' && encoded_idx + 2 < uri_len) { - uri[decoded_len++] = decode_char(&uri[encoded_idx]); - encoded_idx += 2; - } else { - uri[decoded_len++] = uri[encoded_idx]; + char hi = toupper(uri[encoded_idx + 1]); + char lo = toupper(uri[encoded_idx + 2]); + if(((hi >= 'A' && hi <= 'F') || (hi >= '0' && hi <= '9')) && + ((lo >= 'A' && lo <= 'F') || (lo >= '0' && lo <= '9'))) { + uri[decoded_len++] = decode_char(&uri[encoded_idx]); + encoded_idx += 2; + continue; + } } + uri[decoded_len++] = uri[encoded_idx]; } const char* type = "URI"; From 0dd71de5b83e513550befa367d0e615dbac34d4d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 20 Oct 2024 05:34:34 +0100 Subject: [PATCH 5/6] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 617951345..c60483e67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,6 +109,7 @@ - NFC: - Added 6 new Mifare Classic keys from Bulgaria Hotel (#216 by @z3r0l1nk) - NDEF parser supports NTAG I2C Plus 1k and 2k chips too (by @RocketGod-git) + - NDEF parser decodes URL-encoded URI characters (#267 by @jaylikesbunda) - UL: Add iq aparts hotel key (by @xMasterX) - OFW/UL: Rename 'Detect Reader' to 'Extract MFC Keys' (by @bettse & @xMasterX) - OFW: Plantain parser improvements (by @assasinfil) From 5e5fc41de2602eb206f4e7452682381285bbf949 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 20 Oct 2024 05:40:37 +0100 Subject: [PATCH 6/6] Clearer code for hex decode Co-authored-by: jaylikesbunda --- .../main/nfc/plugins/supported_cards/ndef.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/ndef.c b/applications/main/nfc/plugins/supported_cards/ndef.c index ded41d09a..45809c847 100644 --- a/applications/main/nfc/plugins/supported_cards/ndef.c +++ b/applications/main/nfc/plugins/supported_cards/ndef.c @@ -36,16 +36,15 @@ static void furi_string_cat(str, "\n"); } +static inline uint8_t hex_to_int(char c) { + if(c >= '0' && c <= '9') return c - '0'; + if(c >= 'A' && c <= 'F') return c - 'A' + 10; + if(c >= 'a' && c <= 'f') return c - 'a' + 10; + return 0; +} + static char decode_char(const char* str) { - uint8_t high = (str[1] >= 'A' && str[1] <= 'F') ? (str[1] - 'A' + 10) : - (str[1] >= 'a' && str[1] <= 'f') ? (str[1] - 'a' + 10) : - (str[1] >= '0' && str[1] <= '9') ? (str[1] - '0') : - 0; - uint8_t low = (str[2] >= 'A' && str[2] <= 'F') ? (str[2] - 'A' + 10) : - (str[2] >= 'a' && str[2] <= 'f') ? (str[2] - 'a' + 10) : - (str[2] >= '0' && str[2] <= '9') ? (str[2] - '0') : - 0; - return (high << 4) | low; + return (hex_to_int(str[1]) << 4) | hex_to_int(str[2]); } static void parse_ndef_uri(FuriString* str, const uint8_t* payload, uint32_t payload_len) {