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
- Variables
- func BuildDataExchangeResponse(data []byte) []byte
- func BuildErrorResponse(cmd, errorCode byte) []byte
- func BuildFirmwareVersionResponse() []byte
- func BuildNoTagResponse() []byte
- func BuildSAMConfigurationResponse() []byte
- func BuildTagDetectionResponse(tagType string, uid []byte) []byte
- type BufferedJitteryConnection
- type CommandLogEntry
- type JitterConfig
- type JitteryConnection
- type SimulatorPowerMode
- type SimulatorState
- type SimulatorTransport
- func (t *SimulatorTransport) ClearCommandLog()
- func (t *SimulatorTransport) Close() error
- func (t *SimulatorTransport) GetCommandCount(cmd byte) int
- func (t *SimulatorTransport) GetSimulator() *VirtualPN532
- func (t *SimulatorTransport) HasCommand(cmd byte) bool
- func (t *SimulatorTransport) IsConnected() bool
- func (t *SimulatorTransport) SendCommand(ctx context.Context, cmd byte, args []byte) ([]byte, error)
- func (t *SimulatorTransport) SetTimeout(timeout time.Duration) error
- func (*SimulatorTransport) Type() TransportType
- type TransportType
- type VirtualPN532
- func (v *VirtualPN532) AddTag(tag *VirtualTag)
- func (v *VirtualPN532) DropNextACK()
- func (v *VirtualPN532) GetState() SimulatorState
- func (v *VirtualPN532) HasPendingResponse() bool
- func (v *VirtualPN532) InjectChecksumError()
- func (v *VirtualPN532) InjectNACK()
- func (v *VirtualPN532) InjectPreACKGarbage(garbage []byte)
- func (v *VirtualPN532) Read(buf []byte) (int, error)
- func (v *VirtualPN532) RemoveAllTags()
- func (v *VirtualPN532) Reset()
- func (v *VirtualPN532) SetACKFailures(count int)
- func (v *VirtualPN532) SetCollisionMode(enabled bool)
- func (v *VirtualPN532) SetFirmwareVersion(ic, ver, rev, support byte)
- func (v *VirtualPN532) SetPowerGlitch(afterBytes int)
- func (v *VirtualPN532) SetTag(tag *VirtualTag)
- func (v *VirtualPN532) SetZombieMode(enabled bool)
- func (v *VirtualPN532) Write(data []byte) (int, error)
- type VirtualTag
- func (v *VirtualTag) Authenticate(sector int, keyType byte, key []byte) error
- func (v *VirtualTag) GetAuthenticatedSector() int
- func (v *VirtualTag) GetNDEFText() string
- func (v *VirtualTag) GetUIDString() string
- func (v *VirtualTag) Insert()
- func (v *VirtualTag) IsAuthenticated(sector int) bool
- func (v *VirtualTag) ReadBlock(block int) ([]byte, error)
- func (v *VirtualTag) ReadNTAGPages(startPage, numPages int) ([]byte, error)
- func (v *VirtualTag) Remove()
- func (v *VirtualTag) ResetAuthentication()
- func (v *VirtualTag) SetNDEFText(text string) error
- func (v *VirtualTag) SetSectorKey(sector int, keys []byte) error
- func (v *VirtualTag) SetSupportsFastRead(supported bool)
- func (v *VirtualTag) SupportsFastRead() bool
- func (v *VirtualTag) WriteBlock(block int, data []byte) error
Constants ¶
const ( CmdGetFirmwareVersion = 0x02 CmdInListPassiveTarget = 0x4A CmdInDataExchange = 0x40 CmdInRelease = 0x52 CmdSAMConfiguration = 0x14 CmdInSelect = 0x54 )
Command bytes for reference
const MIFAREKeyA = 0x00
MIFAREKeyA is the key type constant for Key A
const MIFAREKeyB = 0x01
MIFAREKeyB is the key type constant for Key B
Variables ¶
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
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 ¶
BuildDataExchangeResponse creates an InDataExchange response
func BuildErrorResponse ¶
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 ¶
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.
type CommandLogEntry ¶ added in v0.14.0
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.
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
func (*SimulatorTransport) Type() TransportType
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).
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) 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) 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