Command-line ACU that measures asymmetric pairing and OSDP-SC2 session performance against a peripheral device.
Bringing a PD onto a secure channel under OSDP-SC2 costs real time, and until it is measured there is no way to tell whether that cost sits in the post-quantum cryptography, the serial link, or the device's own processing. That distinction matters most on embedded hardware, where the wrong assumption leads to optimising the wrong thing.
OSDP-SC2-Benchmark acts as an ACU against a live PD and profiles two operations: the asymmetric
pairing exchange (ML-KEM-768 key encapsulation and ML-DSA-44 signatures over osdp_PAIR), and
repeated SC2 session establishment. Rather than reporting a single total, it breaks pairing into ten
measured spans and attributes each to ACU cryptography, the serial link, or PD processing. Results
are written as a Markdown report.
Key features:
- Per-operation timing profile of the pairing exchange, rolled up by cost centre
- Repeated SC2 session establishment with latency statistics
- Reply-interval timing for
osdp_ID,osdp_CAPandosdp_KEYSETon each session - Pairing fragment size derived from the PD's own reported receive buffer capability
- Markdown report with the derivation and caveats stated inline
- Offline self-test of the profiler's arithmetic
The tool targets the OSDP-SC2 Annex to the Open Supervised Device Protocol, adopted by the Security Industry Association to standardise access control hardware communication. SC2 replaces the AES-128 secure channel of OSDP v2.2 with AES-256-GCM and adds asymmetric pairing.
Development note: asymmetric pairing is experimental and not published to NuGet. This project
references a local OSDP.Net checkout on the feature/osdp-sc2 branch. See Installation.
- .NET 10.0
- Windows and Linux (any platform with a serial port
System.IO.Portscan open)
Requires a PD reachable over a serial connection. The PD must trust the OSDP.Net demonstration certificate authority for pairing to succeed.
No NuGet package is published. Build from source.
- .NET 10.0 SDK
- A local clone of OSDP.Net on the
feature/osdp-sc2branch, checked out as a sibling directory. The project file references..\OSDP.Net\src\OSDP.Net\OSDP.Net.csproj, and theOSDP.Net.Pairingnamespace exists only on that branch.
git clone https://github.com/Z-bit-Systems-LLC/OSDP-SC2-Benchmark.git
cd OSDP-SC2-Benchmark
dotnet build -c ReleaseIf the build reports that OSDP.Net.Pairing cannot be found, the dependency is stale or on the wrong
branch. Build it explicitly first:
dotnet build ../OSDP.Net/src/OSDP.Net/OSDP.Net.csproj -c Release -f net10.0Run a full benchmark against a PD on COM3. Only --port is required.
dotnet run -c Release -- --port COM3This runs three phases in order and writes benchmark-report.md:
- Interrogation —
osdp_IDandosdp_CAPin cleartext, which set the pairing fragment size - Pairing — one asymmetric pairing exchange, profiled per operation
- SC2 — five session establishments, each issuing
osdp_ID,osdp_CAPandosdp_KEYSET
Verify the profiler's arithmetic without any hardware:
dotnet run -c Release -- --self-testPairing succeeds only while the PD holds no secure channel. Once paired, the PD rejects the cleartext
osdp_PAIR with NAK: CommunicationSecurityNotMet, because a PD with an established SC2 session
NAKs every non-secure command. Reset the PD to its unpaired state before each run that measures
pairing. This is why --pair-iterations defaults to 1: raising it requires a manual reset
between trials, and without one every trial after the first fails.
A failed pairing attempt leaves no session behind, so a run that fails this way can be retried without another reset.
If the SCBK is already known, skip pairing entirely. This needs no PD reset and can be repeated freely, which makes it the practical choice for comparing baud rates or PD firmware revisions.
dotnet run -c Release -- --port COM3 --mode sc2 --scbk 0123...ef --sc2-iterations 25By default the pairing fragment size is derived from the PD's ReceiveBufferSize capability. That
capability describes the whole OSDP packet, not the payload, so 32 bytes are reserved for framing
— 5 header, 1 command code, 6 pairing multipart header and 2 CRC for a cleartext fragment, with the
remainder covering a security block and GCM tag on a secured channel. A PD that reports no receive
buffer size falls back to 1024 bytes.
# Override the derivation, e.g. to compare fragmentation strategies
dotnet run -c Release -- --port COM3 --fragment-size 256Notes:
--fragment-sizegoverns ACU to PD fragments only. The PD chooses its own reply fragment size, so the inbound pairing message is unaffected by this setting.--acu-receive-sizesendsosdp_ACURXSIZEto advertise how large a packet the ACU can accept, which is the only ACU-side lever over PD-to-ACU fragmentation. A PD that does not implement the command NAKs it withUnknown Command Code; the run continues and the report records the failure.
The pairing profile attributes each span to a cost centre. Two of them need care:
- ACU crypto is measured on the machine running the benchmark. It is a floor for the ACU side and says nothing about the PD.
- PD turnaround is the gap between the ACU finishing a request and the first reply fragment arriving. It contains the PD's own work plus the bus polling interval, so it is an upper bound on PD processing rather than a measurement of it.
When the PD is a software simulator, every PD-side figure is a best case. Embedded hardware will be slower on those spans, while the serial-link spans carry over largely unchanged.
| Term | Meaning |
|---|---|
| ACU | Access Control Unit — the controller. This tool acts as one. |
| PD | Peripheral Device — reader or controller being measured. |
| Pairing | Asymmetric exchange (osdp_PAIR) that authenticates ACU and PD and derives an SCBK. Uses ML-KEM-768 and ML-DSA-44. |
| SCBK | Secure Channel Base Key. 32 bytes under SC2. |
| SC2 | OSDP-SC2 secure channel: AES-256-GCM with KMAC key derivation. |
| Fragment | One chunk of a multipart message. Large pairing messages are split across many, each a separate command and reply. |
| Cost centre | Attribution of a measured span to ACU crypto, serial link, PD processing, or bus overhead. |
| Option | Default | Description |
|---|---|---|
--port <name> |
required | Serial port, e.g. COM3 or /dev/ttyUSB0 |
--baud <rate> |
38400 |
Baud rate; must match the PD |
--address <0-127> |
0 |
PD address |
--mode <both|pairing|sc2> |
both |
Which phases to run |
--pair-iterations <n> |
1 |
Pairing trials; >1 needs a manual PD reset per trial |
--sc2-iterations <n> |
5 |
SC2 session establishments |
--fragment-size <n> |
derived | Pairing fragment payload bytes, ACU to PD only |
--acu-receive-size <n> |
1440 |
Packet size advertised via osdp_ACURXSIZE; 0 skips |
--pair-timeout <sec> |
30 |
Per-pairing timeout |
--sc2-timeout <sec> |
15 |
Per-handshake timeout |
--scbk <hex> |
— | Pre-shared 32-byte SCBK as 64 hex characters |
--output <path> |
benchmark-report.md |
Report path |
--self-test |
— | Verify profiler arithmetic and exit; no hardware needed |
--help |
— | Show usage |
| Code | Meaning |
|---|---|
0 |
Run completed and the report was written |
1 |
Usage error, or --help |
2 |
Unhandled failure; the message and stack trace go to stderr |
A run exits 0 even when trials fail. Failures are data, and the report records the success rate and
a failure breakdown. Check the report rather than the exit code to judge a run.
Pairing spans are derived from the IProgress<PairingProgress> reports that
ControlPanel.PairDevice emits. The benchmark implements IProgress<T> directly rather than using
System.Progress<T>, which marshals callbacks through the synchronization context or thread pool and
would fold scheduling delay into millisecond-scale spans.
Those callbacks fire after a fragment's round trip completes, so a naive span boundary charges the first fragment to the preceding compute step. The profiler corrects this by moving the measured per-fragment mean back to the wire. Where a message produced only one callback there is no gap to measure, its wire time cannot be separated from the adjacent turnaround, and the report says so explicitly instead of reporting zero.
Command failures during a session are recorded and the run continues, so one unsupported command does not discard an entire measurement:
try
{
var identity = await controlPanel.IdReport(connectionId, address);
timings.Add(new CommandTiming("osdp_ID", stopwatch.Elapsed.TotalMilliseconds, true, identity));
}
catch (Exception ex)
{
// Record the failure as a data point rather than aborting the run
timings.Add(new CommandTiming("osdp_ID", stopwatch.Elapsed.TotalMilliseconds, false,
$"{ex.GetType().Name}: {ex.Message}"));
}The profiler's span arithmetic is verified against a synthetic exchange with hand-computed expectations, checking each span, the total, and that the spans reconcile with no gap or double-count:
dotnet run -c Release -- --self-testRun this after changing the profiler. A real pairing measurement costs a manual PD reset to repeat, so the arithmetic is worth validating before spending one — the self-test has already caught a span origin error that a hardware run would have wasted a reset to discover.
- OSDP.Net — the underlying protocol implementation
- SIA OSDP standard
- Z-bit Systems
- Baud rate sweep, the remaining ACU-side lever over inbound message time
- Measurement against embedded PD hardware rather than a software simulator
- Per-operation profiling of the SC2 handshake, currently measured end-to-end only
- Optional true key rotation on
osdp_KEYSET, which currently re-installs the same SCBK so the session stays usable for the next cycle
We welcome contributions! Please:
- Submit pull requests against the
mainbranch - Follow existing code style and conventions
- Include tests for new features
- Update documentation as needed
For collaboration inquiries or questions, contact us through Z-bit Systems, LLC.
Hardware Vendor Collaboration
We encourage OSDP hardware vendors to use this project to characterise SC2 and pairing performance on their devices. Core functionality is under an open source license to help increase adoption rates of OSDP-SC2. Contact Z-bit Systems, LLC for inquiries regarding commercial integration or custom development.
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
- OSDP.Net — protocol implementation this tool is built on
- Security Industry Association — OSDP standard and the SC2 Annex
For questions, issues, or feature requests:
- Open an issue on GitHub Issues
- Contact Z-bit Systems, LLC
About Z-bit Systems, LLC
Z-bit Systems specializes in access control systems, physical security integration, and enterprise security software solutions. We provide commercial support, custom development, and consulting services. Learn more at z-bitco.com.