mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-05-12 11:18:11 -07:00
Implemented SC operations
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
@@ -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
@@ -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],
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
+834
-2
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user