A modular Python framework for CANopen security research and fuzzing on Windows + PCAN hardware.
Current release: v0.3.0
This project was developed with AI assistance (GitHub Copilot/Claude) and is intended for RESEARCH AND EDUCATIONAL PURPOSES ONLY.
⚠️ NOT for production use without extensive independent review and validation⚠️ NOT safety-certified or suitable for safety-critical systems⚠️ AI-generated code may contain bugs, security vulnerabilities, or logic errors⚠️ CAN bus fuzzing can disrupt network operations and potentially damage equipment⚠️ Use only in isolated test environments with proper supervision⚠️ Testing on live operational systems could cause system failures, data loss, or safety hazards
- ✅ Use in isolated lab environments only
- ✅ Obtain proper authorization before testing
- ✅ Conduct thorough code review before deployment
- ✅ Test on non-critical systems first
- ✅ Have rollback/recovery procedures in place
- ✅ Comply with all applicable laws and regulations
This software is provided "AS IS" without warranty of any kind. The authors and contributors assume NO LIABILITY for any damages, losses, or consequences resulting from its use.
Licensed under the Apache License 2.0 - see LICENSE for full terms, including:
- Strong liability disclaimers (Section 7 & 8)
- Explicit patent grants (Section 3)
- Contribution terms (Section 5)
- Independent security audit recommended before production use
- Thorough testing required on target hardware
- Compliance verification for safety-critical applications
- Professional review of AI-generated code sections
By using this software, you acknowledge these risks and agree to use it responsibly and at your own risk.
# Install
pip install -e .
# Run full automated test suite (all 12 stages)
python -m canopen_security_platform.orchestrator.run_full_security_suite
# Or run individual operations
cansec --bitrate 250000 enumerate # Discovery
cansec --bitrate 250000 fuzz-sdo 1 # Fuzz a nodeSee ORCHESTRATOR_QUICK_GUIDE.md for full test suite configuration and examples.
The platform is organized into functional layers:
- HAL: Direct PCAN hardware interface
- Discovery: Passive, SDO, and LSS scanning
- OD: Object dictionary loading, XDD conversion, hidden scanning
- Fuzzing: Modular fuzzers for SDO, PDO, NMT, LSS
- Monitoring: Event tracking and oracle callbacks
- Orchestrator: Full 12-stage automated security test suite
- CLI: Command-line entry point with argparse
- Utils: Shared logging and frame utilities
pip install -e .This installs canopen-security-platform in editable mode and registers the global cansec command.
cansec --helpBy default, the CLI looks for OD files in the project root under these folder names (in order):
od_filesobject_dictionaryobject_dictionariesodeds
Inside the first matching folder, file types are selected in this priority:
.eds.xdc.xdd
If a file is found, it is loaded automatically (and .xdc/.xdd are converted to .eds when a converter is available).
Example:
CANOpen-Security-Platform/
od_files/
device.eds
The platform includes a complete 19-stage automated security test suite that runs all discovery, scanning, and fuzzing operations sequentially with integrated monitoring and reporting.
# Run with default configuration from config.yaml
python -m canopen_security_platform.orchestrator.run_full_security_suite
# Run with custom configuration
python -m canopen_security_platform.orchestrator.run_full_security_suite --config custom.yaml| Stage | Name | Purpose |
|---|---|---|
| 1 | PCAN Bus Connection | Establish CAN interface connection |
| 2 | Initialize Monitoring | Set up Oracle anomaly detection |
| 3 | Passive Discovery | Listen for heartbeats, EMCY, boot-up |
| 4 | Active SDO Discovery | Probe nodes for device identity |
| 5 | LSS Discovery | Scan for unconfigured nodes |
| 6 | OD Loading | Load EDS/XDD/XDC device descriptions |
| 7 | Hidden Object Scanning | Brute-force scan for undocumented OD indices |
| 8 | SDO Fuzzing | Send 31 malformed SDO requests |
| 9 | PDO Fuzzing | Send 137 PDO mutations |
| 10 | NMT Fuzzing | Send 73 NMT state machine violations + heartbeat/guard time tests |
| 11 | EMCY Fuzzing | Send ~180 EMCY error codes, state transitions, and recovery scenarios |
| 12 | SYNC Fuzzing | Test synchronization robustness (counter overflow, bursts, jitter) |
| 13 | Concurrent Fuzzing | Test race conditions between PDO, SDO, and NMT messages |
| 14 | CAN ID Fuzzing | COB-ID boundary, reserved-ID, and conflict mutation tests |
| 15 | Timing Fuzzing | Timing attacks (jitter, drift, burst, timeout edge cases) |
| 16 | Resource Fuzzing | Resource exhaustion and recovery stress scenarios |
| 17 | State Machine Fuzzing | Illegal transition and state persistence attack sequences |
| 18 | Monitoring Results | Collect detected anomalies and events |
| 19 | Report Generation | Create HTML and JSON reports |
Edit canopen_security_platform/orchestrator/config.yaml:
tests:
fuzz_sdo: true
fuzz_pdo: true # ⚠ can disrupt operations on active networks
fuzz_nmt: true
fuzz_emcy: true # NEW: Tier 1 - Emergency message fuzzing
fuzz_sync: true # NEW: Tier 1 - Synchronization fuzzing
fuzz_concurrent: true # NEW: Tier 1 - Race condition testing
fuzz_canid: true # Phase 1 - COB-ID boundary and conflict testing
fuzz_timing: true # Phase 1 - Timing and delay attack testing
fuzz_resource: true # Phase 2 - Resource exhaustion testing
fuzz_state_machine: true # Phase 2 - Illegal state transition testing
object_dictionary:
device_descriptions_dir: "od_files"
# NEW Tier 1 Fuzzer Configuration
fuzzing_emcy:
enabled: true
iterations: 30
strategies: [error_code_fuzzing, manufacturer_specific, error_register_mutation,
rapid_burst, state_transition, recovery_sequence]
fuzzing_sync:
enabled: true
iterations: 20
strategies: [counter_overflow, missing_frames, burst_flood, jitter_timing,
out_of_order, duplicate_counter, backward_counter]
fuzzing_concurrent:
enabled: true
iterations: 15
strategies: [sdo_interleaving, sdo_during_pdo, nmt_during_transfer,
pdo_mapping_change, sync_during_sdo, broadcast_nmt]
reporting:
output_dir: "reports"Latest verified run (2026-04-03 with Tier 1 Fuzzing):
- Duration: ~45-60 seconds (extended with Tier 1)
- Stages Completed: 15/15 (0 failures)
- Nodes Discovered: 1 (Node 97 via passive)
- CAN Frames Transmitted: 400+ (171 baseline + 180+ EMCY + 150+ SYNC + 50+ Concurrent)
- Mutations Sent: 400+ (39 SDO + 137 PDO + 73 NMT + 22 EMCY strategies + 9 SYNC strategies + 7 Concurrent strategies)
- OD Objects Loaded: 45 from EDS
- Errors: 0
- New Tier 1 Fuzzers: 4 (EMCY, SYNC, Concurrent + NMT extensions)
- Status: ✅ Verified on lab hardware (research use)
The platform now explicitly separates two event classes during fuzzing:
fuzz_input_sent: expected malformed/invalid frames generated by fuzzers (not a failure by itself)device_anomaly_detected: reportable issue where device behavior is improper during/after fuzzing
Examples of reportable anomalies include unexpected reboot/reset, heartbeat loss/timeouts, invalid state transitions, communication lockups, and unsafe protocol behavior.
See WHATS_CHANGED.md for detailed test results and ORCHESTRATOR_QUICK_GUIDE.md for advanced usage.
Direct PCAN interface with context manager support.
from canopen_security_platform.hal.bus_pcan import BusInterface
config = {"bitrate": 250000, "channel": "PCAN_USBBUS1"}
with BusInterface(config=config) as bus:
msg = bus.recv(timeout=1.0)
bus.send(msg)Methods:
send(frame),recv(timeout),iterate(timeout)flush_tx(),flush_rx()- Context manager support
The discovery layer has been fully implemented with passive listening, active SDO probing, and LSS scanning. All components now include robust parsing, error handling, retries, and metadata aggregation.
from canopen_security_platform.discovery.passive import PassiveDiscovery
passive = PassiveDiscovery(bus)
nodes = passive.run(timeout=5.0) # Returns Set[int]
node_info = passive.get_all_node_info()Features added:
- Detailed COB‑ID classification (heartbeat, EMCY, TPDO/RPDO)
- NMT state tracking with transitions
- EMCY parsing and event logging
- Noise filtering via timeouts
from canopen_security_platform.discovery.sdo_probe import SDOProbe
probe = SDOProbe(network)
results = probe.scan(start=1, end=127) # Dict[node_id, {info}]Features added:
- Automatic querying of 0x1000, 0x1008, 0x1009, 0x100A, 0x1018
- Retry logic, timeout handling, and scan statistics
- Identity object parsing (vendor ID, product code, etc.)
from canopen_security_platform.discovery.lss_scan import LSSScanner
lss = LSSScanner(network)
identities = lss.fast_scan() # List[(VendorID, ProductCode, Revision, Serial)]Features added:
- Native python-canopen support with fallback manual binary search stub
- Node ID assignment utilities and selective mode queries
- Timeout management and identification helpers
from canopen_security_platform.discovery.enumerator import NodeEnumerator
enum = NodeEnumerator(bus=bus)
enum.discover_all()
inventory = enum.get_inventory()Features added:
- Coordinated passive, SDO, and LSS discovery with timing metadata
- Inventory aggregation, node summaries, and error reporting
- Optional per-method control and targeted SDO probes
The OD layer now supports full EDS handling, XDD conversion, live OD syncing, hidden-object scanning, and metadata reporting.
from canopen_security_platform.od.eds_loader import EDSLoader
loader = EDSLoader(cache_dir=".cache")
od = loader.load("device.eds")
metadata = loader.get_od_metadata(od)Features added:
- In‑memory and optional disk cache
- File existence checks and validation
- Metadata extraction and index categorization
- Error handling with informative logs
from canopen_security_platform.od.xdd_converter import XDDConverter
converter = XDDConverter()
eds = converter.convert("device.xdd")Features added:
- Automatic tool discovery and availability checks
- Output validation and timeout handling
- Detailed error reporting
from canopen_security_platform.od.runtime_od import RuntimeObjectDictionary
runtime_od = RuntimeObjectDictionary()
runtime_od.register_node(node, sync_on_register=True)
values = runtime_od.sync_from_node(node)Features added:
- Sync object values from remote nodes
- Modification history with source tagging
- Queryable tracked values and sync status
hidden_scanner.py - Optimized hidden object enumeration
from canopen_security_platform.od.hidden_scanner import HiddenObjectScanner
scanner = HiddenObjectScanner(network, max_workers=8)
hidden = scanner.scan_node(1)
diffs = scanner.diff_with_eds(1, od)
report = scanner.export_report(1, "reports/scan1.json")Features added:
- Priority ranges and parallel scanning
- Subindex enumeration with timeout control
- Detailed diffs (hidden/missing/subindex mismatches)
- JSON report export with statistics
All fuzzers follow the same pattern and include multiple mutation strategies:
from canopen_security_platform.fuzzing.sdo_fuzzer import SDOFuzzer
fuzzer = SDOFuzzer(bus=bus, od=od, node_id=1, oracle=oracle_callback)
fuzzer.execute()Implemented strategies:
- Command specifier mutations
- Wrong length fields
- Overflow/underflow boundary values
- Illegal index access
- Read/write violations
- Segmentation/toggle errors
Implemented strategies:
- COB-ID mutation
- Mapping mutation
- Transmission type abuse
- Timing mutation and SYNC timing stress
- Data payload fuzzing
Implemented strategies:
- Rapid transitions
- Illegal transitions
- Broadcast attacks
- Command field corruption
Implemented strategies:
- State confusion
- Bit timing fuzzing
- Device identification fuzzing
- Rapid command sequences
- Timing side-channel probing
Monitoring now records every EMCY, heartbeat, timeout, and reboot, with persistence, alert rules, and reporting built in.
from canopen_security_platform.monitoring.oracle import Oracle, AlertRule
oracle = Oracle(persist_dir="./logs")
# add a simple rule that flags any EMCY event
rule = AlertRule(
name="any_emcy",
event_type="emcy",
condition=lambda e: True,
severity="warning",
)
oracle.add_alert_rule(rule)Features added:
- Event log with optional JSONL persistence
- AlertRule system for custom detection
- Node summaries and statistics
- Exportable reports with recent events and triggered alerts
from canopen_security_platform.monitoring.event_handlers import EventHandlers
handlers = EventHandlers(oracle=oracle, async_mode=True)
handlers.attach(network)Features added:
- EMCY, heartbeat, and SYNC callbacks
- Asynchronous processing via background queue/thread
- Detailed frame parsing and error handling
- Stop method for clean shutdown
cansec [--bitrate BITRATE] [--channel CHANNEL] COMMAND [ARGS]Commands:
enumerate– Run all discoveryod-dump <node>– Dump OD (basic listing)scan-hidden <node>– Scan hidden objectsfuzz-sdo <node>– Run SDO fuzzfuzz-pdo <node>– Run PDO fuzzfuzz-nmt <node>– Run NMT fuzzfuzz-lss– Run LSS fuzz
For OD-aware commands (od-dump, scan-hidden, fuzz-sdo, fuzz-pdo, fuzz-nmt, fuzz-lss), the CLI automatically loads a reference OD from default folders when available: od_files, object_dictionary, object_dictionaries, od, eds.
- logging_utils.py – Centralized logging
- frame_utils.py – CAN message helpers
| Component | Status | Notes |
|---|---|---|
| HAL (PCAN) | ✅ Ready | Full context manager |
| Passive Discovery | ✅ Ready | Extensive parsing, NMT & EMCY tracking |
| SDO Probe | ✅ Ready | Retries, identity queries, stats |
| LSS Scan | ✅ Ready | Fast-scan skeleton & assignment APIs |
| EDS/XDD | ✅ Ready | Caching, validation, converter wrapper |
| OD Runtime | ✅ Ready | Live syncing & modification log |
| Hidden Scanner | ✅ Ready | Parallel scan & JSON reporting |
| SDO/PDO/NMT/LSS Fuzzers | ✅ Ready | Full fuzzing suite with OD utilization |
| EMCY/SYNC/Concurrent Fuzzers | ✅ Ready | Tier 1 - 22 strategies (v0.1.0) |
| CAN ID Fuzzer | ✅ Ready | 10 COB-ID mutation strategies (v0.2.0) |
| Timing Attack Fuzzer | ✅ Ready | 12 timing/delay attack strategies (v0.2.0) |
| Oracle | ✅ Ready | Persistence & alert rules added |
| Event Handlers | ✅ Ready | Async CAN callbacks integrated |
| Orchestrator | ✅ Ready | 19-stage automated test suite, reporting fixes validated on hardware (v0.3.0) |
| CLI | ✅ Ready | All commands implemented |
- Discovery stack complete (passive, SDO, LSS)
- OD pipeline complete (EDS load, XDD/XDC conversion hooks, hidden scan)
- Fuzzing engines available: SDO, PDO, NMT, LSS, EMCY, SYNC, Concurrent (v0.1.0)
- CAN ID Fuzzer (10 strategies) — COB-ID boundary, reserved, conflict testing (v0.2.0)
- Timing Attack Fuzzer (12 strategies) — jitter, gaps, drift, burst attacks (v0.2.0)
- Auto bitrate detection — probes 250k/125k/500k/1M/50k/20k/10k on PCAN_USBBUS1 (v0.2.1)
- Reporting pipeline fix — JSON/HTML now include all 10 fuzzers and correct suite duration (v0.3.0)
- Orchestrator integrated (19-stage automated test suite)
- Extreme in-depth config profile (
config_extreme_indepth.yaml) - Unit tests present across major fuzzing modules, including Phase 2 fuzzers
- Full 19-stage suite validated on physical PCAN device (April 14, 2026)
Focus: Expand fuzzing variants, test case coverage, and attack vectors
-
Fuzzing Variants Expansion
- Protocol boundary fuzz variants (min/max payload sizes, DLC edge cases)
- Cross-protocol interaction fuzzing (NMT during PDO, SDO during SYNC, etc.)
-
Test Case Expansion
- Negative test case library (malformed frames, protocol violations, spec deviations)
- CANopen standard compliance test suites (DS301/402 coverage expansion)
- Device-specific test profiles (manufacturer-specific behavior detection)
- Stress test scenarios (sustained high-rate messaging, rapid resets, recovery cycles)
- Recovery/resilience testing (node behavior after device resets/timeouts)
-
Enhanced Fuzzing Configurations
- Extreme in-depth testing profile (created:
config_extreme_indepth.yaml) - Targeted fuzzing presets (per-protocol, per-vulnerability-class)
- Mutation corpus optimization (learn from prior runs)
- Extreme in-depth testing profile (created:
Focus: Vulnerability analysis, advanced fuzzing strategies, and trend analysis
-
Vulnerability Analysis & Scoring
- Correlation of fuzzing results across protocol layers
- Vulnerability clustering and pattern matching
- Machine-readable risk scoring (CVSS-like per node/device/vulnerability)
- Attack vector chaining detection (multi-step exploits)
-
Advanced Fuzzing Strategies
- Genetic algorithm-based fuzzing (evolve effective test cases)
- Behavior-driven fuzzing (adapt mutations based on device responses)
- Differential fuzzing (compare behavior across device variants)
- Replay attack generation and validation
- Timing side-channel analysis expansion
-
Baseline & Trending
- Baseline-vs-current diff reporting between runs
- Regression detection (new vulnerabilities vs historical)
- Device firmware-specific vulnerability patterns
- Trend analysis and risk evolution tracking
Focus: Enterprise integration, persistence, dashboards (not core offensive testing)
-
Persistent Data Backend
- SQLite/PostgreSQL event storage integration
- Historical event querying and analysis
- Multi-run comparison and archival
-
Real-time Monitoring & Dashboards
- Live fleet/node health monitoring dashboard
- Anomaly trend visualization
- Real-time fuzzing progress tracking
-
Enterprise Integration
- SIEM integrations (Splunk, ELK, others) for alert forwarding
- CI/CD integration (coverage gates, automated regression testing)
- Report automation and scheduling
- Compliance automation (audit trails, generated evidence)
-
Extended Protocol Coverage
- Segmented/block transfer edge case testing
- Advanced LSS features (selective mode deep testing)
- Multi-master CANopen scenarios
- Custom/proprietary protocol extensions
# Passive 5-second listen (default 250kbit/s)
cansec enumerate
# With explicit bitrate
cansec --bitrate 250000 enumerate
# Custom channel
cansec --bitrate 250000 --channel PCAN_USBBUS1 enumerate
# Passive-only discovery
cansec enumerate --passive-only --timeout 5
# Detect available CAN interfaces
cansec --detectHidden Object Scan
If a default OD file is present in one of the auto-discovery folders (od_files, object_dictionary, object_dictionaries, od, eds), scan-hidden will automatically diff scan results against it.
cansec --bitrate 250000 scan-hidden 1
# Limit scan to a small index range (hex)
cansec --bitrate 250000 scan-hidden 1 --range 1000-10FFod-dump also uses the default OD auto-discovery folders (od_files, object_dictionary, object_dictionaries, od, eds). If live SDO OD enumeration is empty or unavailable, it falls back to the discovered reference OD.
cansec --bitrate 250000 od-dump 1fuzz-sdo loads the default discovered OD as runtime reference context when available.
cansec --bitrate 250000 fuzz-sdo 1fuzz-pdo, fuzz-nmt, and fuzz-lss also load the default discovered OD as runtime reference context when available.
cansec --bitrate 250000 fuzz-pdo 1
cansec --bitrate 250000 fuzz-nmt 1
cansec --bitrate 250000 fuzz-lssfrom canopen_security_platform.hal.bus_pcan import BusInterface
from canopen_security_platform.discovery.enumerator import NodeEnumerator
with BusInterface(config={"bitrate": 250000}) as bus:
enum = NodeEnumerator(bus=bus)
enum.discover_all()
print(enum.get_inventory())- LSS fast scan includes a simplified binary-search fallback
od-dump --outputis not yet implemented (prints basic listing)- SDO probing depends on a node responding to 0x1000; some devices may restrict access
- All code uses type hints for IDE support
- Uses logging instead of print()
- No external CLI dependencies except CANopen Editor (optional for XDD)
This project is licensed under the Apache License 2.0.
See LICENSE file for the full text.
Key Points:
- ✅ Free to use, modify, and distribute
- ✅ Explicit patent grant from contributors
- ✅ Strong liability protection
- ✅ Commercial use allowed
⚠️ Must preserve copyright and license notices⚠️ State changes made to files
Copyright © 2026 CANopen Security Platform Contributors