Skip to content

Security: TelemetryWorks/irig106-time

Security

docs/security.md

Security Considerations — irig106-time

Document: SECURITY.md Crate: irig106-time v0.6.0 Date: 2026-03-25


1. Threat Model

irig106-time parses binary data from sources that should be treated as untrusted input:

Source Trust Level Risk
Ch10 files from own recorders Medium Could be corrupted by hardware failure
Ch10 files from third parties Low Could be intentionally malformed
Live UDP streams (Ch10 over IP) Low Network-adjacent attacker could inject packets
Archived data from data lakes Low Integrity may have degraded over storage lifetime
Synthetic/test data Medium Generator bugs create edge cases

The crate processes this data in aerospace analysis pipelines where a parsing crash could halt mission-critical post-flight analysis.


2. Security Properties

2.1 No Panics on Malformed Input

Guarantee: Every public function that accepts &[u8] returns Result<T, TimeError>. No panic on any byte sequence.

Verification:

  • 8 fuzz targets covering every parsing path (see fuzz/)
  • 10,000-iteration property tests on random inputs (see tests/properties.rs)
  • Explicit BufferTooShort checks before all slice indexing

2.2 No Unsafe Code

The crate uses zero unsafe blocks. All byte-level parsing uses safe Rust methods (u16::from_le_bytes, bitwise ops, slice indexing with bounds checks).

2.3 No Integer Overflow

Operation Risk Mitigation
Rtc::to_nanos() 48-bit × 100 could overflow u64 Max value = 2^48 × 100 = 2.81 × 10^16 < u64::MAX (1.84 × 10^19) ✓
Ertc::to_nanos() 64-bit × 100 overflows u64 Returns u128
elapsed_ticks() Subtraction could underflow Uses wrapping_sub + 48-bit mask
add_nanos() Large delta + midnight could overflow Uses u64 intermediate, carries to days
BCD decoding Multi-digit multiply Max value per field < 1000; no overflow in u16

2.4 No Denial of Service

Vector Risk Mitigation
Huge correlation table Memory exhaustion TimeCorrelator accepts at most what the caller inserts; no amplification
Infinite loop on malformed BCD CPU stall All parsing is single-pass, bounded by buffer length
Checksum computation Timing side-channel Not relevant (checksum is not a secret)

2.5 Validated Field Ranges

Every decoded value is checked against the IRIG 106 specification ranges before being returned. A TimeError::OutOfRange is returned for:

  • Hours > 23, minutes > 59, seconds > 59
  • Day-of-year 0 or > 366, month 0 or > 12, day 0 or > 31
  • IEEE-1588 nanoseconds >= 1,000,000,000
  • BCD nibbles > 9

3. Fuzzing Guide

3.1 Setup

# Requires nightly Rust
rustup install nightly
cargo +nightly install cargo-fuzz

3.2 Running Fuzz Targets

# Run a specific target (recommended: start with BCD parsers)
cargo +nightly fuzz run fuzz_bcd_day
cargo +nightly fuzz run fuzz_bcd_dmy

# Run with a time limit
cargo +nightly fuzz run fuzz_secondary_header -- -max_total_time=300

# All available targets:
#   fuzz_bcd_day          — DayFormatTime::from_le_bytes
#   fuzz_bcd_dmy          — DmyFormatTime::from_le_bytes
#   fuzz_rtc              — Rtc construction and arithmetic
#   fuzz_secondary_header — Secondary header checksum + parsing
#   fuzz_intra_packet     — IntraPacketTime dispatch
#   fuzz_ieee1588         — Ieee1588Time parsing
#   fuzz_csdw             — CSDW field extraction
#   fuzz_correlation      — TimeCorrelator with random ref points

3.3 Corpus Seeding

For better coverage, seed the fuzz corpus with real Ch10 time packet payloads:

mkdir -p fuzz/corpus/fuzz_bcd_day
# Extract time packet payloads from real Ch10 files and place in corpus dir

3.4 What to Look For

  • Panics — Any panic is a bug. File as a security issue.
  • Assertion failures in fuzz targets — The fuzz targets assert post-conditions (e.g., parsed hours ≤ 23). A failure means the validation logic has a gap.
  • OOM / infinite allocation — The correlation fuzz target caps at 100 refs.

4. Property-Based Testing Guide

# Run property tests (10,000 iterations each, no external deps)
cargo test --test properties

# The tests use a built-in PRNG (Xorshift) so they're deterministic
# and reproducible without proptest. When proptest is added as a
# dev-dependency, the tests can be upgraded to use its strategies
# for better coverage and shrinking.

4.1 Key Properties Verified

Property Why It Matters
from_raw always produces ≤ 48-bit value Prevents arithmetic overflow in elapsed_ticks
LE bytes round-trip Data integrity through encode/decode
elapsed_ticks result ≤ 48 bits No corruption in nanosecond conversion
add_nanos then sub_nanos = identity Correlation engine correctness
add_nanos is monotonic Time never goes backward unexpectedly
CSDW field extraction is idempotent No hidden state corrupting repeated reads

5. Recommendations for Downstream Consumers

  1. Always check Result — Don't .unwrap() in production code. A malformed time packet should not crash the analysis pipeline.

  2. Validate before correlating — Check that the TimeCorrelator has reference points before calling .correlate(). The NoReferencePoint error is expected for files that lack time packets.

  3. Monitor for time jumps — Use detect_time_jump() to identify GPS lock events or clock resets. Don't silently interpolate across a 5-second jump.

  4. Don't trust secondary header checksums alone — A valid checksum does not guarantee the time value is correct — only that the bytes are internally consistent.

  5. Prefer nearest reference — The correlation engine already uses nearest-point lookup, but if you're building your own pipeline, don't use a single reference point for an entire file. RTC drift is real.

There aren’t any published security advisories