Use PACS2-only HF credential parsing

This commit is contained in:
CinderSocket
2026-03-08 16:16:32 -07:00
parent c8a5f30884
commit 28a630dced
6 changed files with 98 additions and 141 deletions
+3 -2
View File
@@ -17,9 +17,9 @@ static const asn_INTEGER_enum_map_t asn_MAP_FrameProtocol_value2enum_1[] = {
{ 4, 6, "iclass" }
};
static const unsigned int asn_MAP_FrameProtocol_enum2value_1[] = {
0, /* none(0) */
2, /* iclass(4) */
1 /* nfc(2) */
1, /* nfc(2) */
0 /* none(0) */
};
static const asn_INTEGER_specifics_t asn_SPC_FrameProtocol_specs_1 = {
asn_MAP_FrameProtocol_value2enum_1, /* "tag" => N; sorted by tag */
@@ -47,3 +47,4 @@ asn_TYPE_descriptor_t asn_DEF_FrameProtocol = {
0, 0, /* Defined elsewhere */
&asn_SPC_FrameProtocol_specs_1 /* Additional specs */
};
+9 -19
View File
@@ -8,15 +8,6 @@
#include "SamCommand.h"
asn_TYPE_member_t asn_MBR_SamCommand_1[] = {
{ ATF_NOFLAGS, 0, offsetof(struct SamCommand, choice.requestPacs),
(ASN_TAG_CLASS_CONTEXT | (1 << 2)),
-1, /* IMPLICIT tag at current level */
&asn_DEF_RequestPacs,
0,
{ 0, 0, 0 },
0, 0, /* No default value */
"requestPacs"
},
{ ATF_NOFLAGS, 0, offsetof(struct SamCommand, choice.version),
(ASN_TAG_CLASS_CONTEXT | (2 << 2)),
-1, /* IMPLICIT tag at current level */
@@ -82,14 +73,13 @@ asn_TYPE_member_t asn_MBR_SamCommand_1[] = {
},
};
static const asn_TYPE_tag2member_t asn_MAP_SamCommand_tag2el_1[] = {
{ (ASN_TAG_CLASS_CONTEXT | (1 << 2)), 0, 0, 0 }, /* requestPacs */
{ (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 1, 0, 0 }, /* version */
{ (ASN_TAG_CLASS_CONTEXT | (13 << 2)), 2, 0, 0 }, /* cardDetected */
{ (ASN_TAG_CLASS_CONTEXT | (20 << 2)), 3, 0, 0 }, /* processSNMPMessage */
{ (ASN_TAG_CLASS_CONTEXT | (22 << 2)), 4, 0, 0 }, /* serialNumber */
{ (ASN_TAG_CLASS_CONTEXT | (26 << 2)), 7, 0, 0 }, /* processConfigCard */
{ (ASN_TAG_CLASS_CONTEXT | (30 << 2)), 6, 0, 0 }, /* requestPacs2 */
{ (ASN_TAG_CLASS_CONTEXT | (43 << 2)), 5, 0, 0 } /* getItemKCV */
{ (ASN_TAG_CLASS_CONTEXT | (2 << 2)), 0, 0, 0 }, /* version */
{ (ASN_TAG_CLASS_CONTEXT | (13 << 2)), 1, 0, 0 }, /* cardDetected */
{ (ASN_TAG_CLASS_CONTEXT | (20 << 2)), 2, 0, 0 }, /* processSNMPMessage */
{ (ASN_TAG_CLASS_CONTEXT | (22 << 2)), 3, 0, 0 }, /* serialNumber */
{ (ASN_TAG_CLASS_CONTEXT | (26 << 2)), 6, 0, 0 }, /* processConfigCard */
{ (ASN_TAG_CLASS_CONTEXT | (30 << 2)), 5, 0, 0 }, /* requestPacs2 */
{ (ASN_TAG_CLASS_CONTEXT | (43 << 2)), 4, 0, 0 } /* getItemKCV */
};
asn_CHOICE_specifics_t asn_SPC_SamCommand_specs_1 = {
sizeof(struct SamCommand),
@@ -97,7 +87,7 @@ asn_CHOICE_specifics_t asn_SPC_SamCommand_specs_1 = {
offsetof(struct SamCommand, present),
sizeof(((struct SamCommand *)0)->present),
asn_MAP_SamCommand_tag2el_1,
8, /* Count of tags in the map */
7, /* Count of tags in the map */
0, 0,
-1 /* Extensions start */
};
@@ -111,7 +101,7 @@ asn_TYPE_descriptor_t asn_DEF_SamCommand = {
0, /* No tags (count) */
{ 0, 0, CHOICE_constraint },
asn_MBR_SamCommand_1,
8, /* Elements count */
7, /* Elements count */
&asn_SPC_SamCommand_specs_1 /* Additional specs */
};
+2 -4
View File
@@ -12,11 +12,11 @@
#include <asn_application.h>
/* Including external dependencies */
#include "RequestPacs.h"
#include <NULL.h>
#include "CardDetected.h"
#include <OCTET_STRING.h>
#include "NoArguments.h"
#include "RequestPacs.h"
#include <constr_CHOICE.h>
#ifdef __cplusplus
@@ -26,7 +26,6 @@ extern "C" {
/* Dependencies */
typedef enum SamCommand_PR {
SamCommand_PR_NOTHING, /* No components present */
SamCommand_PR_requestPacs,
SamCommand_PR_version,
SamCommand_PR_cardDetected,
SamCommand_PR_processSNMPMessage,
@@ -40,7 +39,6 @@ typedef enum SamCommand_PR {
typedef struct SamCommand {
SamCommand_PR present;
union SamCommand_u {
RequestPacs_t requestPacs;
NULL_t version;
CardDetected_t cardDetected;
OCTET_STRING_t processSNMPMessage;
@@ -57,7 +55,7 @@ typedef struct SamCommand {
/* Implementation */
extern asn_TYPE_descriptor_t asn_DEF_SamCommand;
extern asn_CHOICE_specifics_t asn_SPC_SamCommand_specs_1;
extern asn_TYPE_member_t asn_MBR_SamCommand_1[8];
extern asn_TYPE_member_t asn_MBR_SamCommand_1[7];
#ifdef __cplusplus
}
+84 -114
View File
@@ -180,10 +180,8 @@ static void seader_sam_set_state(
static SeaderSamIntent seader_sam_card_intent(const Seader* seader) {
if(seader->credential->type == SeaderCredentialTypeConfig) {
return SeaderSamIntentConfig;
} else if(seader->is_debug_enabled) {
return SeaderSamIntentReadPacs2;
} else {
return SeaderSamIntentReadPacs;
return SeaderSamIntentReadPacs2;
}
}
@@ -500,24 +498,6 @@ void seader_send_response(
seader_send_payload(seader, &payload, from, to, replyTo);
}
void seader_send_request_pacs(Seader* seader) {
RequestPacs_t requestPacs = {0};
requestPacs.contentElementTag = ContentElementTag_implicitFormatPhysicalAccessBits;
SamCommand_t samCommand = {0};
samCommand.present = SamCommand_PR_requestPacs;
seader_sam_set_state(
seader, SeaderSamStateConversation, SeaderSamIntentReadPacs, samCommand.present);
samCommand.choice.requestPacs = requestPacs;
Payload_t payload = {0};
payload.present = Payload_PR_samCommand;
payload.choice.samCommand = samCommand;
seader_send_payload(
seader, &payload, ExternalApplicationA, SAMInterface, ExternalApplicationA);
}
void seader_send_request_pacs2(Seader* seader) {
OCTET_STRING_t oid = {
.buf = (uint8_t*)seader_oid,
@@ -607,83 +587,86 @@ void seader_send_no_card_detected(Seader* seader) {
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_CardDetails, &cardDetails);
}
bool seader_unpack_pacs(Seader* seader, uint8_t* buf, size_t size) {
SeaderCredential* seader_credential = seader->credential;
PAC_t pac = {0};
PAC_t* pac_p = &pac;
bool rtn = false;
asn_dec_rval_t rval = asn_decode(0, ATS_DER, &asn_DEF_PAC, (void**)&pac_p, buf, size);
if(rval.code == RC_OK) {
#ifdef ASN1_DEBUG
char pacDebug[384] = {0};
(&asn_DEF_PAC)
->op->print_struct(&asn_DEF_PAC, &pac, 1, seader_print_struct_callback, pacDebug);
if(strlen(pacDebug) > 0) {
FURI_LOG_D(TAG, "Received pac: %s", pacDebug);
}
#endif
if(seader_credential->sio[0] == 0x30) {
seader_log_hex_data(TAG, "SIO", seader_credential->sio, seader_credential->sio_len);
#ifdef ASN1_DEBUG
SIO_t sio = {0};
SIO_t* sio_p = &sio;
rval = asn_decode(
0,
ATS_DER,
&asn_DEF_SIO,
(void**)&sio_p,
seader_credential->sio,
seader_credential->sio_len);
if(rval.code == RC_OK) {
FURI_LOG_D(TAG, "Decoded SIO");
char sioDebug[384] = {0};
(&asn_DEF_SIO)
->op->print_struct(
&asn_DEF_SIO, &sio, 1, seader_print_struct_callback, sioDebug);
if(strlen(sioDebug) > 0) {
FURI_LOG_D(TAG, "SIO: %s", sioDebug);
}
} else {
FURI_LOG_W(TAG, "Failed to decode SIO %d consumed", rval.consumed);
}
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_SIO, &sio);
#endif
}
if(pac.size <= sizeof(seader_credential->credential)) {
// TODO: make credential into a 12 byte array
seader_credential->bit_length = pac.size * 8 - pac.bits_unused;
uint64_t credential_val = 0;
memcpy(&credential_val, pac.buf, pac.size);
credential_val = __builtin_bswap64(credential_val);
// After bswap64, the bits are left-aligned in the 64-bit word
// We need to shift them right by (64 - bit_length) to get the value
seader_credential->credential = credential_val >> (64 - seader_credential->bit_length);
FURI_LOG_D(
TAG,
"credential (%d) %016llx",
seader_credential->bit_length,
seader_credential->credential);
rtn = true;
} else {
// PACS too big (probably bad data)
seader_abort_active_read(seader);
}
} else {
FURI_LOG_W(TAG, "Failed to decode PAC %d consumed, size %d", rval.consumed, size);
seader_abort_active_read(seader);
static bool seader_store_pacs_bits(
SeaderCredential* credential,
const uint8_t* payload,
size_t payload_size,
uint8_t unused_bits) {
if(!credential || !payload || payload_size == 0 || payload_size > sizeof(credential->credential) ||
unused_bits > 7) {
return false;
}
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_PAC, &pac);
return rtn;
const uint8_t bit_length = payload_size * 8 - unused_bits;
if(bit_length == 0) {
return false;
}
uint64_t credential_val = 0;
memcpy(&credential_val, payload, payload_size);
credential_val = __builtin_bswap64(credential_val);
credential->bit_length = bit_length;
credential->credential = credential_val >> (64 - bit_length);
return true;
}
static bool seader_unpack_pacs2_bits(Seader* seader, const OCTET_STRING_t* pacs_bits) {
SeaderCredential* seader_credential = seader->credential;
if(!pacs_bits || !pacs_bits->buf || pacs_bits->size < 2) {
FURI_LOG_W(TAG, "Malformed pacs2 bits");
return false;
}
seader_log_hex_data(TAG, "PACS2 bits", pacs_bits->buf, pacs_bits->size);
if(seader_credential->sio[0] == 0x30) {
seader_log_hex_data(TAG, "SIO", seader_credential->sio, seader_credential->sio_len);
#ifdef ASN1_DEBUG
asn_dec_rval_t rval;
SIO_t sio = {0};
SIO_t* sio_p = &sio;
rval = asn_decode(
0,
ATS_DER,
&asn_DEF_SIO,
(void**)&sio_p,
seader_credential->sio,
seader_credential->sio_len);
if(rval.code == RC_OK) {
FURI_LOG_D(TAG, "Decoded SIO");
char sioDebug[384] = {0};
(&asn_DEF_SIO)->op->print_struct(
&asn_DEF_SIO, &sio, 1, seader_print_struct_callback, sioDebug);
if(strlen(sioDebug) > 0) {
FURI_LOG_D(TAG, "SIO: %s", sioDebug);
}
} else {
FURI_LOG_W(TAG, "Failed to decode SIO %d consumed", rval.consumed);
}
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_SIO, &sio);
#endif
}
const uint8_t unused_bits = pacs_bits->buf[0];
const uint8_t* payload = pacs_bits->buf + 1;
const size_t payload_size = pacs_bits->size - 1;
FURI_LOG_D(TAG, "PACS2 unused_bits=%u payload_size=%zu", unused_bits, payload_size);
if(!seader_store_pacs_bits(seader_credential, payload, payload_size, unused_bits)) {
FURI_LOG_W(TAG, "Failed to store PACS2 bits");
return false;
}
FURI_LOG_D(
TAG,
"credential (%d) %016llx",
seader_credential->bit_length,
seader_credential->credential);
return true;
}
// 800201298106683d052026b6820101
@@ -839,7 +822,6 @@ static void seader_abort_active_read(Seader* seader) {
}
bool seader_parse_sam_response2(Seader* seader, SamResponse2_t* samResponse) {
uint8_t buffer[10];
switch(samResponse->present) {
case SamResponse2_PR_pacs:
FURI_LOG_I(TAG, "samResponse2 SamResponse2_PR_pacs");
@@ -857,14 +839,13 @@ bool seader_parse_sam_response2(Seader* seader, SamResponse2_t* samResponse) {
Pacs2_t pacs2 = samResponse->choice.pacs;
OCTET_STRING_t* pacs = pacs2.bits;
buffer[0] = 0x03;
buffer[1] = pacs->size & 0xFF;
memcpy(buffer + 2, pacs->buf, pacs->size);
if(seader_unpack_pacs(seader, buffer, pacs->size + 2)) {
if(seader_unpack_pacs2_bits(seader, pacs)) {
view_dispatcher_send_custom_event(
seader->view_dispatcher, SeaderCustomEventPollerSuccess);
seader_sam_set_state(
seader, SeaderSamStateIdle, SeaderSamIntentNone, SamCommand_PR_NOTHING);
} else {
seader_abort_active_read(seader);
}
break;
case SamResponse2_PR_NOTHING:
@@ -886,15 +867,7 @@ bool seader_parse_sam_response(Seader* seader, SamResponse_t* samResponse) {
switch(seader->sam_state) {
case SeaderSamStateConversation:
case SeaderSamStateFinishing:
if(seader->sam_intent == SeaderSamIntentReadPacs) {
FURI_LOG_I(TAG, "samResponse read PACS");
if(seader_unpack_pacs(seader, samResponse->buf, samResponse->size)) {
view_dispatcher_send_custom_event(
seader->view_dispatcher, SeaderCustomEventPollerSuccess);
seader_sam_set_state(
seader, SeaderSamStateIdle, SeaderSamIntentNone, SamCommand_PR_NOTHING);
}
} else if(seader->sam_intent == SeaderSamIntentConfig) {
if(seader->sam_intent == SeaderSamIntentConfig) {
FURI_LOG_I(TAG, "samResponse config");
seader_worker->stage = SeaderPollerEventTypeFail;
seader_sam_set_state(
@@ -921,8 +894,6 @@ bool seader_parse_sam_response(Seader* seader, SamResponse_t* samResponse) {
seader_send_process_config_card(seader);
} else if(seader->sam_intent == SeaderSamIntentReadPacs2) {
seader_send_request_pacs2(seader);
} else if(seader->sam_intent == SeaderSamIntentReadPacs) {
seader_send_request_pacs(seader);
} else {
FURI_LOG_W(TAG, "Unexpected detect intent=%d", seader->sam_intent);
seader_abort_active_read(seader);
@@ -1379,8 +1350,7 @@ void seader_parse_nfc_off(Seader* seader) {
seader_send_response(seader, &response, ExternalApplicationA, SAMInterface, 0);
if(seader->sam_state == SeaderSamStateConversation &&
(seader->sam_intent == SeaderSamIntentReadPacs ||
seader->sam_intent == SeaderSamIntentReadPacs2 ||
(seader->sam_intent == SeaderSamIntentReadPacs2 ||
seader->sam_intent == SeaderSamIntentConfig)) {
seader_sam_set_state(
seader, SeaderSamStateFinishing, seader->sam_intent, seader->samCommand);
-1
View File
@@ -22,7 +22,6 @@ ErrorResponse ::= SEQUENCE {
}
SamCommand ::= CHOICE {
requestPacs [1] RequestPacs,
version [2] NULL,
cardDetected [13] CardDetected,
processSNMPMessage [20] OCTET STRING,
-1
View File
@@ -110,7 +110,6 @@ typedef enum {
typedef enum {
SeaderSamIntentNone,
SeaderSamIntentReadPacs,
SeaderSamIntentReadPacs2,
SeaderSamIntentConfig,
SeaderSamIntentMaintenance,