testing

package
v0.20.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 7, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package testing provides test utilities including a wire-level PN532 simulator.

The VirtualPN532 type implements io.ReadWriter and simulates the PN532 chip at the frame protocol level, as specified in the PN532 User Manual section 6.2.

Protocol Reference: PN532 User Manual, section 6.2 "Host controller communication protocol" - Normal Information Frame: §6.2.1.1 - Extended Information Frame: §6.2.1.2 - ACK Frame: §6.2.1.3 - NACK Frame: §6.2.1.4 - Error Frame: §6.2.1.5

Index

Constants

View Source
const (
	CmdGetFirmwareVersion  = 0x02
	CmdInListPassiveTarget = 0x4A
	CmdInDataExchange      = 0x40
	CmdInRelease           = 0x52
	CmdSAMConfiguration    = 0x14
	CmdInSelect            = 0x54
)

Command bytes for reference

View Source
const MIFAREKeyA = 0x00

MIFAREKeyA is the key type constant for Key A

View Source
const MIFAREKeyB = 0x01

MIFAREKeyB is the key type constant for Key B

Variables

View Source
var (
	// TestNTAG213UID is a sample NTAG213 UID
	TestNTAG213UID = []byte{0x04, 0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56}

	// TestMIFARE1KUID is a sample MIFARE Classic 1K UID
	TestMIFARE1KUID = []byte{0x12, 0x34, 0x56, 0x78}

	// TestMIFARE4KUID is a sample MIFARE Classic 4K UID
	TestMIFARE4KUID = []byte{0xAB, 0xCD, 0xEF, 0x01}
)

Common UIDs for testing

View Source
var (
	// ACKFrame is sent to acknowledge successful frame reception (§6.2.1.3)
	// Format: PREAMBLE START_CODE 00 FF POSTAMBLE
	ACKFrame = []byte{0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00}

	// NACKFrame requests retransmission of last response (§6.2.1.4)
	// Format: PREAMBLE START_CODE FF 00 POSTAMBLE
	NACKFrame = []byte{0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00}
)

ACK and NACK frames from PN532 User Manual §6.2.1.3 and §6.2.1.4

Functions

func BuildDataExchangeResponse

func BuildDataExchangeResponse(data []byte) []byte

BuildDataExchangeResponse creates an InDataExchange response

func BuildErrorResponse

func BuildErrorResponse(cmd, errorCode byte) []byte

BuildErrorResponse creates an error response for any command

func BuildFirmwareVersionResponse

func BuildFirmwareVersionResponse() []byte

BuildFirmwareVersionResponse creates a GetFirmwareVersion response

func BuildNoTagResponse

func BuildNoTagResponse() []byte

BuildNoTagResponse creates an empty InListPassiveTarget response

func BuildSAMConfigurationResponse

func BuildSAMConfigurationResponse() []byte

BuildSAMConfigurationResponse creates a SAMConfiguration response BuildSAMConfigurationResponse creates a SAMConfiguration response BuildSAMConfigurationResponse creates a SAMConfiguration response

func BuildTagDetectionResponse

func BuildTagDetectionResponse(tagType string, uid []byte) []byte

BuildTagDetectionResponse creates an InListPassiveTarget response

Types

type BufferedJitteryConnection added in v0.9.0

type BufferedJitteryConnection struct {
	// contains filtered or unexported fields
}

BufferedJitteryConnection is a more sophisticated jittery connection that properly buffers data from the backend to support fragmentation without data loss.

func NewBufferedJitteryConnection added in v0.9.0

func NewBufferedJitteryConnection(backend io.ReadWriter, config JitterConfig) *BufferedJitteryConnection

NewBufferedJitteryConnection creates a jittery connection with proper buffering. This version correctly handles fragmentation by buffering backend reads.

func (*BufferedJitteryConnection) ClearBuffer added in v0.9.0

func (j *BufferedJitteryConnection) ClearBuffer()

ClearBuffer clears any buffered read data.

func (*BufferedJitteryConnection) Read added in v0.9.0

func (j *BufferedJitteryConnection) Read(buf []byte) (int, error)

Read reads from the backend with simulated jitter and fragmentation.

func (*BufferedJitteryConnection) ResetStallState added in v0.9.0

func (j *BufferedJitteryConnection) ResetStallState()

ResetStallState resets the stall tracking state.

func (*BufferedJitteryConnection) Write added in v0.9.0

func (j *BufferedJitteryConnection) Write(data []byte) (int, error)

Write passes writes through to the backend without modification.

type CommandLogEntry added in v0.14.0

type CommandLogEntry struct {
	Timestamp time.Time
	Args      []byte
	Cmd       byte
}

CommandLogEntry records a command sent to the transport

type JitterConfig added in v0.9.0

type JitterConfig struct {
	MaxLatencyMs      int
	FragmentMinBytes  int
	StallAfterBytes   int
	StallDuration     time.Duration
	Seed              uint64
	FragmentReads     bool
	USBBoundaryStress bool
}

JitterConfig configures the behavior of JitteryConnection.

func DefaultJitterConfig added in v0.9.0

func DefaultJitterConfig() JitterConfig

DefaultJitterConfig returns a sensible default configuration for testing.

type JitteryConnection added in v0.9.0

type JitteryConnection struct {
	// contains filtered or unexported fields
}

JitteryConnection wraps an io.ReadWriter to simulate real-world transport conditions like USB-UART bridges (FTDI, CH340) with unpredictable latency and fragmented data delivery.

This is useful for testing parser robustness, timeout handling, and race conditions that only manifest with realistic timing.

func NewJitteryConnection added in v0.9.0

func NewJitteryConnection(backend io.ReadWriter, config JitterConfig) *JitteryConnection

NewJitteryConnection wraps a backend io.ReadWriter with jitter simulation.

func (*JitteryConnection) Read added in v0.9.0

func (j *JitteryConnection) Read(buf []byte) (int, error)

Read reads from the backend with simulated jitter and fragmentation.

func (*JitteryConnection) ResetStallState added in v0.9.0

func (j *JitteryConnection) ResetStallState()

ResetStallState resets the stall tracking state. Call this between test operations if needed.

func (*JitteryConnection) Write added in v0.9.0

func (j *JitteryConnection) Write(data []byte) (int, error)

Write passes writes through to the backend without modification. Jitter only affects reads to simulate realistic UART/USB behavior.

type SimulatorPowerMode added in v0.9.0

type SimulatorPowerMode int

SimulatorPowerMode represents the PN532 power state (§3.1.2)

const (
	PowerModeNormal    SimulatorPowerMode = iota // CPU running
	PowerModePowerDown                           // Oscillator stopped
)

type SimulatorState added in v0.9.0

type SimulatorState struct {
	PowerMode      SimulatorPowerMode
	RFFieldOn      bool
	SAMConfigured  bool
	SelectedTarget int // -1 = none, 1-2 = target number
}

SimulatorState tracks the internal state of the simulated PN532

type SimulatorTransport added in v0.14.0

type SimulatorTransport struct {
	CommandLog []CommandLogEntry
	// contains filtered or unexported fields
}

SimulatorTransport wraps VirtualPN532 and implements pn532.Transport interface. This allows using the wire-level simulator with the high-level Device API for integration testing.

func NewSimulatorTransport added in v0.14.0

func NewSimulatorTransport(sim *VirtualPN532) *SimulatorTransport

NewSimulatorTransport creates a new transport backed by VirtualPN532

func (*SimulatorTransport) ClearCommandLog added in v0.14.0

func (t *SimulatorTransport) ClearCommandLog()

ClearCommandLog clears the command log

func (*SimulatorTransport) Close added in v0.14.0

func (t *SimulatorTransport) Close() error

Close closes the transport

func (*SimulatorTransport) GetCommandCount added in v0.14.0

func (t *SimulatorTransport) GetCommandCount(cmd byte) int

GetCommandCount returns how many times a command was sent

func (*SimulatorTransport) GetSimulator added in v0.14.0

func (t *SimulatorTransport) GetSimulator() *VirtualPN532

GetSimulator returns the underlying VirtualPN532 for test setup

func (*SimulatorTransport) HasCommand added in v0.14.0

func (t *SimulatorTransport) HasCommand(cmd byte) bool

HasCommand checks if a specific command was sent

func (*SimulatorTransport) IsConnected added in v0.14.0

func (t *SimulatorTransport) IsConnected() bool

IsConnected returns whether the transport is connected

func (*SimulatorTransport) SendCommand added in v0.14.0

func (t *SimulatorTransport) SendCommand(ctx context.Context, cmd byte, args []byte) ([]byte, error)

SendCommand sends a command to the simulated PN532 and returns the response. It handles frame encoding/decoding internally with context support.

func (*SimulatorTransport) SetTimeout added in v0.14.0

func (t *SimulatorTransport) SetTimeout(timeout time.Duration) error

SetTimeout sets the read timeout

func (*SimulatorTransport) Type added in v0.14.0

Type returns the transport type

type TransportType added in v0.14.0

type TransportType string

TransportType mirrors pn532.TransportType to avoid import cycle

const (
	// TransportMock represents a mock transport for testing
	TransportMock TransportType = "mock"
)

type VirtualPN532 added in v0.9.0

type VirtualPN532 struct {
	// contains filtered or unexported fields
}

VirtualPN532 simulates a PN532 chip at the wire protocol level. It implements io.ReadWriter to plug directly into transport layer tests.

The simulator enforces the PN532 protocol as specified in the User Manual: - Frame format validation (checksums, structure) - ACK/NACK handshake - State machine (power modes, RF field, target selection) - Command-specific responses

func NewVirtualPN532 added in v0.9.0

func NewVirtualPN532() *VirtualPN532

NewVirtualPN532 creates a new wire-level PN532 simulator. The simulator starts in PowerModeNormal with no RF field and no tags.

func (*VirtualPN532) AddTag added in v0.9.0

func (v *VirtualPN532) AddTag(tag *VirtualTag)

AddTag adds a virtual tag that can be detected by InListPassiveTarget.

func (*VirtualPN532) DropNextACK added in v0.9.0

func (v *VirtualPN532) DropNextACK()

DropNextACK causes the simulator to not send ACK for the next command.

func (*VirtualPN532) GetState added in v0.9.0

func (v *VirtualPN532) GetState() SimulatorState

GetState returns the current simulator state.

func (*VirtualPN532) HasPendingResponse added in v0.9.0

func (v *VirtualPN532) HasPendingResponse() bool

HasPendingResponse returns true if the simulator has response data waiting to be read. This is useful for I2C ready status simulation.

func (*VirtualPN532) InjectChecksumError added in v0.9.0

func (v *VirtualPN532) InjectChecksumError()

InjectChecksumError causes the next response to have an invalid checksum.

func (*VirtualPN532) InjectNACK added in v0.9.0

func (v *VirtualPN532) InjectNACK()

InjectNACK causes the simulator to expect a NACK and retransmit.

func (*VirtualPN532) InjectPreACKGarbage added in v0.9.0

func (v *VirtualPN532) InjectPreACKGarbage(garbage []byte)

InjectPreACKGarbage causes the simulator to inject the specified garbage bytes before the next ACK frame. This tests the UART transport's tryProcessPreAckData handling, which was a critical Windows bug fix for garbage data appearing before ACK frames on noisy UART lines.

func (*VirtualPN532) Read added in v0.9.0

func (v *VirtualPN532) Read(buf []byte) (int, error)

Read implements io.Reader - returns response data to the host controller.

func (*VirtualPN532) RemoveAllTags added in v0.9.0

func (v *VirtualPN532) RemoveAllTags()

RemoveAllTags removes all virtual tags.

func (*VirtualPN532) Reset added in v0.9.0

func (v *VirtualPN532) Reset()

Reset clears all state and buffers.

func (*VirtualPN532) SetACKFailures added in v0.9.1

func (v *VirtualPN532) SetACKFailures(count int)

SetACKFailures configures the simulator to skip sending ACK frames for the specified number of commands. After that many failures, ACKs resume. This tests transport-level retry logic when ACK is not received.

Example: SetACKFailures(2) causes the first 2 commands to not receive ACK, forcing the transport to retry. The 3rd attempt will receive ACK normally.

func (*VirtualPN532) SetCollisionMode added in v0.9.0

func (v *VirtualPN532) SetCollisionMode(enabled bool)

SetCollisionMode enables or disables collision simulation mode. When enabled and multiple tags are present, InListPassiveTarget will return a collision error (0x06) instead of detecting tags. This simulates the real PN532 behavior when multiple tags are in the RF field and the anti-collision algorithm fails.

func (*VirtualPN532) SetFirmwareVersion added in v0.9.0

func (v *VirtualPN532) SetFirmwareVersion(ic, ver, rev, support byte)

SetFirmwareVersion configures the firmware version returned by GetFirmwareVersion.

func (*VirtualPN532) SetPowerGlitch added in v0.9.0

func (v *VirtualPN532) SetPowerGlitch(afterBytes int)

SetPowerGlitch configures the simulator to simulate a power glitch by truncating the next response frame after the specified number of bytes. This tests recovery code when power is interrupted mid-communication. Set to 0 to disable power glitch simulation.

Example: SetPowerGlitch(3) would truncate a response like:

[00 00 FF 04 FC D5 03 ...] -> [00 00 FF]

leaving an incomplete frame that the transport must handle.

func (*VirtualPN532) SetTag added in v0.9.0

func (v *VirtualPN532) SetTag(tag *VirtualTag)

SetTag is a convenience method that removes all existing tags and adds a single tag. Useful for test scenarios where only one tag is needed.

func (*VirtualPN532) SetZombieMode added in v0.9.0

func (v *VirtualPN532) SetZombieMode(enabled bool)

SetZombieMode enables or disables zombie mode. When enabled, the simulator will ACK commands but never send the response frame. This tests timeout handling and recovery when the PN532 becomes unresponsive after acknowledging a command (e.g., due to power issues, hardware fault, or firmware hang).

func (*VirtualPN532) Write added in v0.9.0

func (v *VirtualPN532) Write(data []byte) (int, error)

Write implements io.Writer - receives data from the host controller. This parses incoming frames and generates appropriate responses.

type VirtualTag

type VirtualTag struct {
	Type   string
	UID    []byte
	Memory [][]byte

	Present bool
	// contains filtered or unexported fields
}

VirtualTag represents a simulated NFC tag for testing

func NewVirtualFudanClone added in v0.14.0

func NewVirtualFudanClone(uid []byte) *VirtualTag

NewVirtualFudanClone creates a virtual Fudan FM11NT021 clone tag. Fudan clones have UID prefix 0x1D and don't support FAST_READ (0x3A) or GET_VERSION (0x60). They are functionally NTAG213-compatible but with limited command support. See: https://github.com/RfidResearchGroup/proxmark3/issues/2457

func NewVirtualMIFARE1K

func NewVirtualMIFARE1K(uid []byte) *VirtualTag

NewVirtualMIFARE1K creates a virtual MIFARE Classic 1K tag

func NewVirtualMIFARE4K

func NewVirtualMIFARE4K(uid []byte) *VirtualTag

NewVirtualMIFARE4K creates a virtual MIFARE Classic 4K tag

func NewVirtualNTAG213

func NewVirtualNTAG213(uid []byte) *VirtualTag

NewVirtualNTAG213 creates a virtual NTAG213 tag with default content

func (*VirtualTag) Authenticate added in v0.9.0

func (v *VirtualTag) Authenticate(sector int, keyType byte, key []byte) error

Authenticate authenticates a sector with the given key keyType: 0x00 for Key A, 0x01 for Key B

func (*VirtualTag) GetAuthenticatedSector added in v0.9.0

func (v *VirtualTag) GetAuthenticatedSector() int

GetAuthenticatedSector returns the currently authenticated sector (-1 if none)

func (*VirtualTag) GetNDEFText

func (v *VirtualTag) GetNDEFText() string

GetNDEFText extracts text from the NDEF message (simplified)

func (*VirtualTag) GetUIDString

func (v *VirtualTag) GetUIDString() string

GetUIDString returns the UID as a hex string

func (*VirtualTag) Insert

func (v *VirtualTag) Insert()

Insert sets the tag as present

func (*VirtualTag) IsAuthenticated added in v0.9.0

func (v *VirtualTag) IsAuthenticated(sector int) bool

IsAuthenticated returns true if authenticated to the given sector

func (*VirtualTag) ReadBlock

func (v *VirtualTag) ReadBlock(block int) ([]byte, error)

ReadBlock reads a specific memory block

func (*VirtualTag) ReadNTAGPages added in v0.14.0

func (v *VirtualTag) ReadNTAGPages(startPage, numPages int) ([]byte, error)

ReadNTAGPages reads multiple consecutive 4-byte pages and returns them concatenated. This simulates the NTAG READ command which returns 4 pages (16 bytes) at once.

func (*VirtualTag) Remove

func (v *VirtualTag) Remove()

Remove sets the tag as not present

func (*VirtualTag) ResetAuthentication added in v0.9.0

func (v *VirtualTag) ResetAuthentication()

ResetAuthentication clears the authentication state

func (*VirtualTag) SetNDEFText

func (v *VirtualTag) SetNDEFText(text string) error

SetNDEFText sets a simple text NDEF message

func (*VirtualTag) SetSectorKey added in v0.9.0

func (v *VirtualTag) SetSectorKey(sector int, keys []byte) error

SetSectorKey sets custom keys for a sector keys should be 12 bytes: 6 bytes Key A + 6 bytes Key B

func (*VirtualTag) SetSupportsFastRead added in v0.14.0

func (v *VirtualTag) SetSupportsFastRead(supported bool)

SetSupportsFastRead allows tests to configure whether the tag supports FAST_READ

func (*VirtualTag) SupportsFastRead added in v0.14.0

func (v *VirtualTag) SupportsFastRead() bool

SupportsFastRead returns whether this tag supports the FAST_READ (0x3A) command. Genuine NXP tags support FAST_READ, but clone tags (like Fudan FM11NT021) typically don't.

func (*VirtualTag) WriteBlock

func (v *VirtualTag) WriteBlock(block int, data []byte) error

WriteBlock writes data to a specific memory block

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL