Integrate SNMP probe into SAM API

This commit is contained in:
CinderSocket
2026-03-09 10:25:40 -07:00
parent 088aef1f2f
commit a4940efeb8
12 changed files with 446 additions and 7 deletions
+4
View File
@@ -15,19 +15,23 @@ test-host:
lib/host_tests/vendor/munit/munit.c \
lib/host_tests/test_main.c \
lib/host_tests/test_lrc.c \
lib/host_tests/test_sam_key_label.c \
lib/host_tests/test_ccid_logic.c \
lib/host_tests/test_t1_existing.c \
lib/host_tests/test_t1_protocol.c \
lib/host_tests/test_snmp.c \
lib/host_tests/test_uhf_status_label.c \
lib/host_tests/t1_test_stubs.c \
lib/host_tests/bit_buffer_mock.c \
lrc.c \
ccid_logic.c \
t_1_logic.c \
t_1.c \
sam_key_label.c \
snmp_ber_view.c \
snmp_codec.c \
snmp_response_view.c \
uhf_status_label.c \
uhf_tag_config_view.c \
uhf_snmp_probe.c \
-o build/host_tests/seader_tests
+4
View File
@@ -2,17 +2,21 @@
extern MunitSuite test_lrc_suite;
extern MunitSuite test_ccid_logic_suite;
extern MunitSuite test_sam_key_label_suite;
extern MunitSuite test_t1_existing_suite;
extern MunitSuite test_t1_protocol_suite;
extern MunitSuite test_snmp_suite;
extern MunitSuite test_uhf_status_label_suite;
int main(int argc, char* argv[]) {
MunitSuite child_suites[] = {
{"/lrc", test_lrc_suite.tests, NULL, 1, MUNIT_SUITE_OPTION_NONE},
{"/sam-key-label", test_sam_key_label_suite.tests, NULL, 1, MUNIT_SUITE_OPTION_NONE},
{"/t1", test_t1_existing_suite.tests, NULL, 1, MUNIT_SUITE_OPTION_NONE},
{"/t1", test_t1_protocol_suite.tests, NULL, 1, MUNIT_SUITE_OPTION_NONE},
{"/ccid", test_ccid_logic_suite.tests, NULL, 1, MUNIT_SUITE_OPTION_NONE},
{"/snmp", test_snmp_suite.tests, NULL, 1, MUNIT_SUITE_OPTION_NONE},
{"/uhf-status-label", test_uhf_status_label_suite.tests, NULL, 1, MUNIT_SUITE_OPTION_NONE},
{NULL, NULL, NULL, 0, 0},
};
MunitSuite main_suite = {
+80
View File
@@ -0,0 +1,80 @@
#include "munit.h"
#include "sam_key_label.h"
static MunitResult test_formats_no_sam(const MunitParameter params[], void* fixture) {
(void)params;
(void)fixture;
char label[SEADER_SAM_KEY_LABEL_MAX_LEN] = {0};
seader_sam_key_label_format(false, NULL, 0U, label, sizeof(label));
munit_assert_string_equal(label, "NO SAM");
return MUNIT_OK;
}
static MunitResult test_formats_standard_key_for_missing_value(
const MunitParameter params[],
void* fixture) {
(void)params;
(void)fixture;
char label[SEADER_SAM_KEY_LABEL_MAX_LEN] = {0};
const uint8_t zeros[] = {0x00, 0x00, 0x00};
seader_sam_key_label_format(true, NULL, 0U, label, sizeof(label));
munit_assert_string_equal(label, "SAM: Standard Key");
seader_sam_key_label_format(true, zeros, sizeof(zeros), label, sizeof(label));
munit_assert_string_equal(label, "SAM: Standard Key");
return MUNIT_OK;
}
static MunitResult test_formats_standard_key_for_eight_zero_bytes(
const MunitParameter params[],
void* fixture) {
(void)params;
(void)fixture;
char label[SEADER_SAM_KEY_LABEL_MAX_LEN] = {0};
const uint8_t zero64[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
seader_sam_key_label_format(true, zero64, sizeof(zero64), label, sizeof(label));
munit_assert_string_equal(label, "SAM: Standard Key");
return MUNIT_OK;
}
static MunitResult test_formats_ascii_ice_value(const MunitParameter params[], void* fixture) {
(void)params;
(void)fixture;
char label[SEADER_SAM_KEY_LABEL_MAX_LEN] = {0};
const uint8_t ice[] = {'I', 'C', 'E', '1', '8', '0', '3'};
seader_sam_key_label_format(true, ice, sizeof(ice), label, sizeof(label));
munit_assert_string_equal(label, "SAM: ICE1803");
return MUNIT_OK;
}
static MunitResult test_sanitizes_non_printable_bytes(const MunitParameter params[], void* fixture) {
(void)params;
(void)fixture;
char label[SEADER_SAM_KEY_LABEL_MAX_LEN] = {0};
const uint8_t mixed[] = {'A', 0x00, 0x1F, 'Z'};
seader_sam_key_label_format(true, mixed, sizeof(mixed), label, sizeof(label));
munit_assert_string_equal(label, "SAM: A??Z");
return MUNIT_OK;
}
static MunitTest test_sam_key_label_cases[] = {
{(char*)"/no-sam", test_formats_no_sam, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL},
{(char*)"/standard-key", test_formats_standard_key_for_missing_value, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL},
{(char*)"/standard-key-zero64", test_formats_standard_key_for_eight_zero_bytes, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL},
{(char*)"/ascii", test_formats_ascii_ice_value, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL},
{(char*)"/sanitize", test_sanitizes_non_printable_bytes, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL},
{NULL, NULL, NULL, NULL, 0, NULL},
};
MunitSuite test_sam_key_label_suite = {
"",
test_sam_key_label_cases,
NULL,
1,
MUNIT_SUITE_OPTION_NONE,
};
+34
View File
@@ -0,0 +1,34 @@
#include "munit.h"
#include "uhf_status_label.h"
static MunitResult test_formats_none(const MunitParameter params[], void* fixture) {
(void)params;
(void)fixture;
char label[SEADER_UHF_STATUS_LABEL_MAX_LEN] = {0};
seader_uhf_status_label_format(false, false, false, false, label, sizeof(label));
munit_assert_string_equal(label, "UHF: none");
return MUNIT_OK;
}
static MunitResult test_formats_supported_key_states(const MunitParameter params[], void* fixture) {
(void)params;
(void)fixture;
char label[SEADER_UHF_STATUS_LABEL_MAX_LEN] = {0};
seader_uhf_status_label_format(true, false, true, true, label, sizeof(label));
munit_assert_string_equal(label, "UHF: Monza 4QT [no key]/Higgs 3");
return MUNIT_OK;
}
static MunitTest test_uhf_status_label_cases[] = {
{(char*)"/none", test_formats_none, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL},
{(char*)"/supported-key-states", test_formats_supported_key_states, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL},
{NULL, NULL, NULL, NULL, 0, NULL},
};
MunitSuite test_uhf_status_label_suite = {
"",
test_uhf_status_label_cases,
NULL,
1,
MUNIT_SUITE_OPTION_NONE,
};
+163 -7
View File
@@ -1,5 +1,8 @@
#include "sam_api.h"
#include "sam_key_label.h"
#include "trace_log.h"
#include "uhf_snmp_probe.h"
#include "uhf_status_label.h"
#include <toolbox/path.h>
#include <toolbox/version.h>
#include <bit_lib/bit_lib.h>
@@ -16,6 +19,99 @@ const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33,
const uint8_t seader_oid[] =
{0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x04};
static void seader_sam_set_state(
Seader* seader,
SeaderSamState state,
SeaderSamIntent intent,
SamCommand_PR command);
static void seader_publish_sam_status(Seader* seader) {
if(seader && seader->view_dispatcher) {
view_dispatcher_send_custom_event(
seader->view_dispatcher, SeaderCustomEventSamStatusUpdated);
}
}
static void seader_update_sam_key_label(Seader* seader, const uint8_t* value, size_t value_len) {
if(!seader) {
return;
}
seader_sam_key_label_format(
seader->sam_present,
value,
value_len,
seader->sam_key_label,
sizeof(seader->sam_key_label));
seader_publish_sam_status(seader);
}
static void seader_update_uhf_status_label(Seader* seader) {
if(!seader) {
return;
}
seader_uhf_status_label_format(
seader->snmp_probe.has_monza4qt,
seader->snmp_probe.monza4qt_key_present,
seader->snmp_probe.has_higgs3,
seader->snmp_probe.higgs3_key_present,
seader->uhf_status_label,
sizeof(seader->uhf_status_label));
seader_publish_sam_status(seader);
}
static bool seader_snmp_probe_send_next_request(Seader* seader) {
SeaderWorker* seader_worker = seader ? seader->worker : NULL;
SeaderUartBridge* seader_uart = seader_worker ? seader_worker->uart : NULL;
uint8_t* scratch = seader_uart ? (seader_uart->tx_buf + MAX_FRAME_HEADERS) : NULL;
uint8_t* message = seader_uart ? seader_uart->rx_buf : NULL;
size_t message_len = 0U;
if(!seader || !scratch || !message) {
return false;
}
if(!seader_uhf_snmp_probe_build_next_request(
&seader->snmp_probe,
scratch,
SEADER_UART_RX_BUF_SIZE - MAX_FRAME_HEADERS,
message,
SEADER_UART_RX_BUF_SIZE,
&message_len)) {
return false;
}
return seader_worker_send_process_snmp_message(seader, message, message_len);
}
static void seader_snmp_probe_finish(Seader* seader) {
if(!seader) {
return;
}
seader_sam_set_state(seader, SeaderSamStateIdle, SeaderSamIntentNone, SamCommand_PR_NOTHING);
}
static void seader_start_snmp_probe(Seader* seader) {
if(!seader || !seader->sam_present) {
return;
}
seader_uhf_snmp_probe_init(&seader->snmp_probe);
seader_uhf_status_label_format(
false, false, false, false, seader->uhf_status_label, sizeof(seader->uhf_status_label));
seader_sam_set_state(
seader,
SeaderSamStateCapabilityPending,
SeaderSamIntentMaintenance,
SamCommand_PR_processSNMPMessage);
if(!seader_snmp_probe_send_next_request(seader)) {
seader_snmp_probe_finish(seader);
}
}
#ifdef ASN1_DEBUG
char asn1_log[SEADER_UART_RX_BUF_SIZE] = {0};
#endif
@@ -539,6 +635,8 @@ void seader_worker_send_serial_number(Seader* seader) {
void seader_worker_send_version(Seader* seader) {
SamCommand_t samCommand = {0};
samCommand.present = SamCommand_PR_version;
seader->sam_present = true;
seader_update_sam_key_label(seader, NULL, 0U);
seader_sam_set_state(
seader, SeaderSamStateVersionPending, SeaderSamIntentMaintenance, samCommand.present);
@@ -550,6 +648,26 @@ void seader_worker_send_version(Seader* seader) {
seader, &payload, ExternalApplicationA, SAMInterface, ExternalApplicationA);
}
bool seader_worker_send_process_snmp_message(
Seader* seader,
const uint8_t* message,
size_t message_len) {
if(!seader || !message || message_len == 0U || message_len > UINT16_MAX) return false;
SamCommand_t samCommand = {0};
samCommand.present = SamCommand_PR_processSNMPMessage;
samCommand.choice.processSNMPMessage.buf = (uint8_t*)message;
samCommand.choice.processSNMPMessage.size = message_len;
Payload_t payload = {0};
payload.present = Payload_PR_samCommand;
payload.choice.samCommand = samCommand;
seader_send_payload(
seader, &payload, ExternalApplicationA, SAMInterface, ExternalApplicationA);
return true;
}
void seader_send_card_detected(Seader* seader, CardDetails_t* cardDetails) {
CardDetected_t cardDetected = {
.detectedCardDetails = *cardDetails,
@@ -792,7 +910,7 @@ bool seader_sam_save_serial_QR(Seader* seader, char* serial) {
bool seader_parse_serial_number(Seader* seader, uint8_t* buf, size_t size) {
// Create hex string for QR code (needs to be persistent)
char hex_string[size * 2 + 1];
for(uint8_t i = 0; i < size; i++) {
for(size_t i = 0; i < size; i++) {
snprintf(hex_string + (i * 2), sizeof(hex_string) - (i * 2), "%02x", buf[i]);
}
hex_string[size * 2] = '\0';
@@ -888,8 +1006,30 @@ bool seader_parse_sam_response(Seader* seader, SamResponse_t* samResponse) {
case SeaderSamStateSerialPending:
FURI_LOG_I(TAG, "samResponse serial");
seader_parse_serial_number(seader, samResponse->buf, samResponse->size);
seader_sam_set_state(
seader, SeaderSamStateIdle, SeaderSamIntentNone, SamCommand_PR_NOTHING);
seader_start_snmp_probe(seader);
break;
case SeaderSamStateCapabilityPending:
FURI_LOG_I(TAG, "samResponse processSNMPMessage");
if(!seader_uhf_snmp_probe_consume_response(
&seader->snmp_probe, samResponse->buf, samResponse->size)) {
seader_update_sam_key_label(seader, NULL, 0U);
seader_snmp_probe_finish(seader);
break;
}
if(seader->snmp_probe.stage >= SeaderUhfSnmpProbeStageReadTagConfig) {
seader_update_sam_key_label(
seader, seader->snmp_probe.ice_value_storage, seader->snmp_probe.ice_value_len);
seader_update_uhf_status_label(seader);
}
if(seader->snmp_probe.stage == SeaderUhfSnmpProbeStageDone) {
seader_snmp_probe_finish(seader);
} else if(
seader->snmp_probe.stage == SeaderUhfSnmpProbeStageFailed ||
!seader_snmp_probe_send_next_request(seader)) {
seader_snmp_probe_finish(seader);
}
break;
case SeaderSamStateDetectPending:
FURI_LOG_I(TAG, "samResponse cardDetected");
@@ -1199,7 +1339,7 @@ void seader_mfc_transmit(
size_t tx_size = bit_buffer_get_size_bytes(tx_buffer);
uint8_t* tx_data = malloc(tx_size);
if(tx_data) {
for(uint8_t i = 0; i < tx_size; i++) {
for(size_t i = 0; i < tx_size; i++) {
tx_data[i] = bit_buffer_get_byte(tx_buffer, i);
}
seader_log_hex_data(TAG, "NFC Send without parity", tx_data, tx_size);
@@ -1230,7 +1370,7 @@ void seader_mfc_transmit(
// Log the BitBuffer contents efficiently
uint8_t* rx_data = malloc(length);
if(rx_data) {
for(uint8_t i = 0; i < length; i++) {
for(size_t i = 0; i < length; i++) {
rx_data[i] = bit_buffer_get_byte(rx_buffer, i);
}
seader_log_hex_data(TAG, "NFC Response without parity", rx_data, length);
@@ -1277,7 +1417,7 @@ void seader_mfc_transmit(
// Log the BitBuffer contents efficiently
uint8_t* rx_data_parity = malloc(length);
if(rx_data_parity) {
for(uint8_t i = 0; i < length; i++) {
for(size_t i = 0; i < length; i++) {
rx_data_parity[i] = bit_buffer_get_byte(rx_buffer, i);
}
seader_log_hex_data(TAG, "NFC Response with parity", rx_data_parity, length);
@@ -1410,7 +1550,23 @@ bool seader_worker_state_machine(
case Payload_PR_errorResponse:
FURI_LOG_W(TAG, "Payload_PR_errorResponse");
processed = true;
view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit);
if(seader->sam_state == SeaderSamStateCapabilityPending) {
ErrorResponse_t* err = &payload->choice.errorResponse;
if(seader_uhf_snmp_probe_consume_error(
&seader->snmp_probe, err->errorCode, err->data.buf, err->data.size)) {
seader_update_uhf_status_label(seader);
if(seader->snmp_probe.stage == SeaderUhfSnmpProbeStageDone) {
seader_snmp_probe_finish(seader);
} else if(!seader_snmp_probe_send_next_request(seader)) {
seader_snmp_probe_finish(seader);
}
} else {
seader_snmp_probe_finish(seader);
}
} else {
view_dispatcher_send_custom_event(
seader->view_dispatcher, SeaderCustomEventWorkerExit);
}
break;
default:
FURI_LOG_W(TAG, "unhandled payload");
+5
View File
@@ -36,3 +36,8 @@ bool seader_process_success_response_i(
size_t len,
bool online,
SeaderPollerContainer* spc);
bool seader_worker_send_process_snmp_message(
Seader* seader,
const uint8_t* message,
size_t message_len);
+56
View File
@@ -0,0 +1,56 @@
#include "sam_key_label.h"
#include <ctype.h>
#include <stdio.h>
#include <string.h>
static bool seader_sam_key_label_is_missing(const uint8_t* value, size_t value_len) {
if(!value || value_len == 0U) {
return true;
}
for(size_t i = 0; i < value_len; i++) {
if(value[i] != 0x00U) {
return false;
}
}
return true;
}
void seader_sam_key_label_format(
bool sam_present,
const uint8_t* elite_ice_value,
size_t elite_ice_value_len,
char* out,
size_t out_size) {
if(!out || out_size == 0U) {
return;
}
out[0] = '\0';
if(!sam_present) {
snprintf(out, out_size, "NO SAM");
return;
}
if(seader_sam_key_label_is_missing(elite_ice_value, elite_ice_value_len)) {
snprintf(out, out_size, "SAM: Standard Key");
return;
}
size_t pos = 0U;
pos += (size_t)snprintf(out + pos, out_size - pos, "SAM: ");
if(pos >= out_size) {
out[out_size - 1U] = '\0';
return;
}
for(size_t i = 0; i < elite_ice_value_len && pos + 1U < out_size; i++) {
unsigned char ch = elite_ice_value[i];
out[pos++] = isprint(ch) ? (char)ch : '?';
}
out[pos] = '\0';
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define SEADER_SAM_KEY_LABEL_MAX_LEN 32U
void seader_sam_key_label_format(
bool sam_present,
const uint8_t* elite_ice_value,
size_t elite_ice_value_len,
char* out,
size_t out_size);
+6
View File
@@ -33,9 +33,15 @@ Seader* seader_alloc() {
seader->samCommand = SamCommand_PR_NOTHING;
seader->sam_state = SeaderSamStateIdle;
seader->sam_intent = SeaderSamIntentNone;
seader->sam_present = false;
seader_sam_key_label_format(
false, NULL, 0U, seader->sam_key_label, sizeof(seader->sam_key_label));
seader_uhf_status_label_format(
false, false, false, false, seader->uhf_status_label, sizeof(seader->uhf_status_label));
memset(seader->detected_card_types, 0, sizeof(seader->detected_card_types));
seader->detected_card_type_count = 0;
seader->selected_read_type = SeaderCredentialTypeNone;
seader_uhf_snmp_probe_init(&seader->snmp_probe);
seader->worker = seader_worker_alloc();
seader->view_dispatcher = view_dispatcher_alloc();
+9
View File
@@ -56,6 +56,9 @@
#include "seader_worker.h"
#include "seader_credential.h"
#include "apdu_log.h"
#include "sam_key_label.h"
#include "uhf_snmp_probe.h"
#include "uhf_status_label.h"
#define WORKER_ALL_RX_EVENTS \
(WorkerEvtStop | WorkerEvtRxDone | WorkerEvtCfgChange | WorkerEvtLineCfgSet | \
@@ -78,6 +81,7 @@ enum SeaderCustomEvent {
SeaderCustomEventPollerDetect,
SeaderCustomEventPollerSuccess,
SeaderCustomEventSamStatusUpdated,
};
typedef enum {
@@ -107,6 +111,7 @@ typedef enum {
SeaderSamStateClearPending,
SeaderSamStateVersionPending,
SeaderSamStateSerialPending,
SeaderSamStateCapabilityPending,
} SeaderSamState;
typedef enum {
@@ -129,8 +134,12 @@ struct Seader {
SamCommand_PR samCommand;
SeaderSamState sam_state;
SeaderSamIntent sam_intent;
bool sam_present;
uint8_t ATR[SEADER_MAX_ATR_SIZE];
size_t ATR_len;
char sam_key_label[SEADER_SAM_KEY_LABEL_MAX_LEN];
char uhf_status_label[SEADER_UHF_STATUS_LABEL_MAX_LEN];
SeaderUhfSnmpProbe snmp_probe;
char text_store[SEADER_TEXT_STORE_SIZE + 1];
char read_error[SEADER_TEXT_STORE_SIZE + 1];
+57
View File
@@ -0,0 +1,57 @@
#include "uhf_status_label.h"
#include <stdio.h>
#include <string.h>
static size_t seader_uhf_append_family(
char* out,
size_t out_size,
size_t pos,
bool* wrote_any,
const char* name,
bool key_present) {
if(*wrote_any) {
pos += (size_t)snprintf(out + pos, out_size - pos, "/");
} else {
pos += (size_t)snprintf(out + pos, out_size - pos, "UHF: ");
*wrote_any = true;
}
pos += (size_t)snprintf(out + pos, out_size - pos, "%s", name);
if(!key_present) {
pos += (size_t)snprintf(out + pos, out_size - pos, " [no key]");
}
return pos;
}
void seader_uhf_status_label_format(
bool has_monza4qt,
bool monza4qt_key_present,
bool has_higgs3,
bool higgs3_key_present,
char* out,
size_t out_size) {
bool wrote_any = false;
size_t pos = 0U;
if(!out || out_size == 0U) {
return;
}
out[0] = '\0';
if(has_monza4qt) {
pos = seader_uhf_append_family(
out, out_size, pos, &wrote_any, "Monza 4QT", monza4qt_key_present);
}
if(has_higgs3) {
pos = seader_uhf_append_family(
out, out_size, pos, &wrote_any, "Higgs 3", higgs3_key_present);
}
if(!wrote_any) {
snprintf(out, out_size, "UHF: none");
} else if(pos >= out_size) {
out[out_size - 1U] = '\0';
}
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#define SEADER_UHF_STATUS_LABEL_MAX_LEN 64U
void seader_uhf_status_label_format(
bool has_monza4qt,
bool monza4qt_key_present,
bool has_higgs3,
bool higgs3_key_present,
char* out,
size_t out_size);