Implemented SC operations

This commit is contained in:
Antiklesys
2026-05-11 13:54:49 +08:00
parent b269b83a83
commit abe54994d2
8 changed files with 1145 additions and 4 deletions
+1 -1
View File
@@ -36,7 +36,7 @@ APP_CFLAGS = $(PLATFORM_DEFS)
SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c lfadc.c
SRC_HF = hfops.c
SRC_ISO15693 = iso15693.c iso15693tools.c
SRC_ISO14443a = iso14443a.c secc.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_common.c sam_mfc.c sam_seos.c
SRC_ISO14443a = iso14443a.c secc.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_common.c sam_mfc.c sam_seos.c sam_sc.c
#UNUSED: mifaresniff.c
SRC_ISO14443b = iso14443b.c
+6
View File
@@ -69,6 +69,7 @@
#include "sam_picopass.h"
#include "sam_seos.h"
#include "sam_mfc.h"
#include "sam_sc.h"
#include "cmac_calc.h"
#ifdef WITH_LCD
@@ -2530,6 +2531,11 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_SAM_SC: {
sam_sc_handler(packet);
break;
}
#endif
#ifdef WITH_FPC_USART
+19 -1
View File
@@ -158,6 +158,23 @@ int sam_send_payload(
const uint8_t *const payload,
const uint16_t *payload_len,
uint8_t *response,
uint16_t *response_len
) {
return sam_send_payload_ex(addr_src, addr_dest, addr_reply, 0x00,
payload, payload_len,
response, response_len);
}
int sam_send_payload_ex(
const uint8_t addr_src,
const uint8_t addr_dest,
const uint8_t addr_reply,
const uint8_t scFlag,
const uint8_t *const payload,
const uint16_t *payload_len,
uint8_t *response,
uint16_t *response_len
) {
@@ -171,13 +188,14 @@ int sam_send_payload(
buf[3] = 0x63; // P2
buf[4] = SAM_TX_ASN1_PREFIX_LENGTH + (uint8_t) * payload_len; // LEN
// Grace routing header: FROM, TO, REPLY-TO, 0x00, 0x00, scFlag
buf[5] = addr_src;
buf[6] = addr_dest;
buf[7] = addr_reply;
buf[8] = 0x00;
buf[9] = 0x00;
buf[10] = 0x00;
buf[10] = scFlag;
memcpy(
&buf[11],
+23
View File
@@ -27,6 +27,7 @@ int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen);
void switch_clock_to_ticks(void);
void switch_clock_to_countsspclk(void);
// Backwards-compatible wrapper that calls sam_send_payload_ex with scFlag=0x00.
int sam_send_payload(
const uint8_t addr_src,
const uint8_t addr_dest,
@@ -39,6 +40,28 @@ int sam_send_payload(
uint16_t *response_len
);
// Extended variant that lets the caller set the Grace routing scFlag byte.
//
// The Grace routing header is 6 bytes: FROM, TO, REPLY-TO, 0x00, 0x00, scFlag.
// During InitAuth the scFlag is 0x00; after the SAM authenticates the host it
// returns a session-bound scFlag (typically 0x81) that must be echoed in the
// routing header of every subsequent wrapped APDU (ContinueAuth, wrap/unwrap).
// The original sam_send_payload hardcoded scFlag=0x00 which made it impossible
// to drive a real secure-channel session. New SC code paths must use this _ex
// variant instead.
int sam_send_payload_ex(
const uint8_t addr_src,
const uint8_t addr_dest,
const uint8_t addr_reply,
const uint8_t scFlag,
const uint8_t *const payload,
const uint16_t *payload_len,
uint8_t *response,
uint16_t *response_len
);
int sam_get_version(bool info);
int sam_get_serial_number(void);
+160
View File
@@ -0,0 +1,160 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// HID Artemis SAM secure-channel transport (see sam_sc.h for design notes).
//-----------------------------------------------------------------------------
#include "sam_sc.h"
#include <string.h>
#include "BigBuf.h"
#include "appmain.h"
#include "cmd.h"
#include "dbprint.h"
#include "i2c.h" // ISO7816_MAX_FRAME, I2C_Reset_EnterMainProgram
#include "proxmark3_arm.h"
#include "sam_common.h"
#include "ticks.h"
#include "util.h" // LED_D_ON, LEDsoff
// Tracks whether the SIM module has been initialised since the last reset.
// Set true after the first successful sam_sc_handler() invocation; cleared by
// sam_sc_session_invalidate() (called from any other firmware path that takes
// over the SIM module - currently a manual hook left for future wiring) and
// by SAM_SC_FLAG_FORCE_RESET / SAM_SC_FLAG_RELEASE.
static bool s_sam_sc_session_active = false;
void sam_sc_session_invalidate(void) {
s_sam_sc_session_active = false;
}
void sam_sc_handler(const PacketCommandNG *c) {
if (c == NULL || c->length < SAM_SC_HEADER_LEN) {
reply_ng(CMD_HF_SAM_SC, PM3_EINVARG, NULL, 0);
return;
}
const uint8_t *body = c->data.asBytes;
const uint8_t flags = body[SAM_SC_OFF_FLAGS];
const uint8_t addr_src = body[SAM_SC_OFF_ADDR_SRC];
const uint8_t addr_dest = body[SAM_SC_OFF_ADDR_DEST];
const uint8_t addr_reply = body[SAM_SC_OFF_ADDR_REPLY];
const uint8_t scFlag = body[SAM_SC_OFF_SCFLAG];
const bool force_reset = !!(flags & SAM_SC_FLAG_FORCE_RESET);
const bool release = !!(flags & SAM_SC_FLAG_RELEASE);
const bool no_payload = !!(flags & SAM_SC_FLAG_NO_PAYLOAD);
const uint8_t *payload = body + SAM_SC_HEADER_LEN;
uint16_t payload_len = (uint16_t)(c->length - SAM_SC_HEADER_LEN);
if (no_payload) {
// Caller is just managing session state (open/close); no SAM traffic.
payload_len = 0;
} else if (payload_len == 0) {
reply_ng(CMD_HF_SAM_SC, PM3_EINVARG, NULL, 0);
return;
}
LED_D_ON();
set_tracing(true);
// Reset the SAM only if the caller asked for it OR this is the first SC
// op since boot / since the previous session was released. Crucially
// this dispatcher does NOT reset on every call the way sam_picopass_get_pacs
// does, so the SAM-side session-flag binding established by ContinueAuth
// survives across multiple CMD_HF_SAM_SC invocations.
//
// After every reset we issue a sam_get_version() warmup ping. This
// mirrors what sam_picopass_get_pacs does (which is what `hf iclass sam
// --info` runs through). Without this warmup, the FIRST sam_send_payload_ex
// after I2C_Reset can time out - the 8051<->SAM UART link needs a
// sacrificial round-trip to settle. The version response is discarded.
if (force_reset || s_sam_sc_session_active == false) {
I2C_Reset_EnterMainProgram();
StartTicks();
sam_get_version(false);
s_sam_sc_session_active = true;
}
int res = PM3_SUCCESS;
if (no_payload == false) {
uint8_t *response = BigBuf_calloc(ISO7816_MAX_FRAME);
if (response == NULL) {
res = PM3_EMALLOC;
goto out;
}
uint16_t response_len = ISO7816_MAX_FRAME;
res = sam_send_payload_ex(
addr_src, addr_dest, addr_reply, scFlag,
payload, &payload_len,
response, &response_len
);
if (res != PM3_SUCCESS) {
// Whatever happened on the wire, the session may be in an
// inconsistent state. Mark dirty so the next call re-opens.
s_sam_sc_session_active = false;
}
if (release) {
// Caller requested an explicit teardown after this op (typically
// after a samCommandSecureChannelTerminate). Do a full reset to
// bring the SAM back to a clean idle state.
I2C_Reset_EnterMainProgram();
s_sam_sc_session_active = false;
}
// Reformat the buffer for the host: prepend the SAM-assigned scFlag
// (firmware-side index 4 of the routing tail), then the SAM payload
// (firmware-side index 5 onward). See sam_sc.h for the wire layout.
// memmove is safe across the overlapping ranges (dst < src by 4).
if (res == PM3_SUCCESS && response_len >= 6) {
uint8_t sc_flag = response[4];
uint16_t sam_payload_len = (uint16_t)(response_len - 5);
memmove(response + 1, response + 5, sam_payload_len);
response[0] = sc_flag;
response_len = (uint16_t)(1 + sam_payload_len);
reply_ng(CMD_HF_SAM_SC, PM3_SUCCESS, response, response_len);
} else if (res == PM3_SUCCESS) {
// sam_send_payload_ex succeeded but the response is too short
// to contain a routing tail + SAM payload. Treat as exchange
// error so the host knows the result is unusable.
reply_ng(CMD_HF_SAM_SC, PM3_ECARDEXCHANGE, NULL, 0);
} else {
// sam_send_payload_ex failed. Propagate the error; no payload.
reply_ng(CMD_HF_SAM_SC, res, NULL, 0);
}
BigBuf_free();
goto done;
}
// SAM_SC_FLAG_NO_PAYLOAD path: caller wants to manage session state only.
if (release) {
I2C_Reset_EnterMainProgram();
s_sam_sc_session_active = false;
}
out:
reply_ng(CMD_HF_SAM_SC, res, NULL, 0);
done:
set_tracing(false);
LEDsoff();
}
+96
View File
@@ -0,0 +1,96 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// HID Artemis SAM secure-channel transport.
//
// CMD_HF_SAM_PICOPASS is structurally unsuitable for sustaining an SCP02 /
// Grace secure channel because it (a) hard-resets the SAM at the start of
// every CLI invocation, wiping the SAM's internal session-flag binding, and
// (b) routes raw payloads through sam_send_request_iso15 whose loop is
// designed to relay 0x61-tagged SAM responses to an iCLASS card via NFC.
//
// This handler is a separate dispatcher that:
// - Resets the SAM only on the very first call after device boot, OR when
// the host explicitly asks for a reset.
// - Skips the sam_get_version sanity ping.
// - Sends a single SAM payload with a host-supplied scFlag and returns the
// SAM's raw response - no NFC card-edge involvement at all.
//
// The host owns the SCP02 / Grace KDF + wrap/unwrap state machine; this
// firmware module is a thin transport pipe so that state can survive across
// CLI invocations on the SAM side.
//-----------------------------------------------------------------------------
#ifndef __SAM_SC_H
#define __SAM_SC_H
#include "common.h"
#include "pm3_cmd.h"
// CMD_HF_SAM_SC payload layout:
//
// [0] flags byte
// BITMASK(0) SAM_SC_FLAG_FORCE_RESET - I2C_Reset before this op,
// marks session uninitialised
// BITMASK(1) SAM_SC_FLAG_RELEASE - I2C_Reset after this op,
// marks session uninitialised
// BITMASK(2) SAM_SC_FLAG_NO_PAYLOAD - send no payload, just
// manage session state
// (open / close)
// [1] addr_src Grace routing FROM byte (typically 0x44)
// [2] addr_dest Grace routing TO byte (typically 0x0A = SAM)
// [3] addr_reply Grace routing REPLY-TO (typically 0x44)
// [4] scFlag Grace routing scFlag (0x00 for InitAuth;
// server-assigned thereafter)
// [5...] SAM payload bytes starting with 0xA0 (or whatever SAM TLV the
// host wants delivered raw; the firmware does not interpret).
//
// Reply: reply_ng(CMD_HF_SAM_SC, status, payload, payload_len)
// payload[0] = SAM-assigned scFlag (the byte the host MUST echo in the
// routing header of the next request - 0x00 during
// InitAuth, server-assigned thereafter)
// payload[1..] = raw SAM response starting at the first byte after the
// routing tail (typically 0xBD for Path A/B, 0xBE for
// Path C errorResponse)
// payload_len = 1 + len(SAM response)
//
// The scFlag is the load-bearing piece of state for sustaining a Grace
// secure channel: the SAM assigns it during InitAuth and binds the
// authenticated session to it. Subsequent ContinueAuth and wrapped APDUs
// MUST carry the same scFlag in their outgoing routing header or the SAM
// will reject them. Surfacing it as the first byte of the reply lets the
// host save and replay it on the next CMD_HF_SAM_SC call without any
// additional probing.
#define SAM_SC_FLAG_FORCE_RESET (1 << 0)
#define SAM_SC_FLAG_RELEASE (1 << 1)
#define SAM_SC_FLAG_NO_PAYLOAD (1 << 2)
// Wire-layout offsets within the CMD_HF_SAM_SC packet body.
#define SAM_SC_OFF_FLAGS 0
#define SAM_SC_OFF_ADDR_SRC 1
#define SAM_SC_OFF_ADDR_DEST 2
#define SAM_SC_OFF_ADDR_REPLY 3
#define SAM_SC_OFF_SCFLAG 4
#define SAM_SC_HEADER_LEN 5
void sam_sc_handler(const PacketCommandNG *c);
// Forces the next sam_sc_handler() call to perform an I2C reset before sending
// its payload. Intended to be called by other firmware paths that may have
// taken over the SIM module (e.g. CMD_HF_SAM_PICOPASS, CMD_SMART_*) and would
// otherwise leave a stale "session active" flag visible to sam_sc_handler.
void sam_sc_session_invalidate(void);
#endif
File diff suppressed because it is too large Load Diff
+6
View File
@@ -892,6 +892,12 @@ typedef struct {
#define CMD_HF_SEOS_SIMULATE 0x0903
// HID SAM secure-channel transport (separate dispatcher from CMD_HF_SAM_PICOPASS).
// Used to drive InitAuth / ContinueAuth / wrap-unwrap traffic where the SAM's
// session state must persist across calls (no I2C reset, no GetVersion ping,
// no iso15 NFC-relay loop) and the routing scFlag byte must be host-supplied.
#define CMD_HF_SAM_SC 0x0904
#define CMD_UNKNOWN 0xFFFF
// Mifare simulation flags