Skip to content

Releases: cryptspeak/csCardputer

v0.1.2

Choose a tag to compare

@0x00001312 0x00001312 released this 01 Jul 17:39

Fixed

  • TCP: toggling TCP connections in Settings crashed the device with a double-free/use-after-free. retireTCPClient() called delete on a raw pointer already owned by RNS::Interface's shared_ptr; teardown then freed it a second time. Fixed by letting tcpIfaces.clear() be the sole destructor.
  • TCP: TCPClientInterface::stop() returned early when WiFi dropped while a connect task was still in flight, leaving the task alive with a dangling self pointer. If the destructor ran first, the task's subsequent _connectState write hit freed memory. Fixed by always waiting for the connect task; ESP32 lwIP aborts pending sockets within milliseconds of network loss.
  • TCP: a reloaded TCPClientInterface inherited stale HDLC frame state, hub transport ID, and reconnect backoff from its previous run. start() now resets all per-connection state. A re-announce fires 4 s after new clients are created so the hub learns our address on the fresh connection.
  • Names: non-contact peer names disappeared after every reboot. Three interlocking bugs: (1) no priority tracking — TCP hub announces could fill the 60-entry on-disk cap, displacing peers we'd actually messaged; (2) eviction race — the RAM guard could evict a name before the first message exchange marked it persistent, with no recovery path; (3) missing dirty flag — if the 5 s save timer fired before a message exchange, markNamePersistent returned early without scheduling a re-save, so the entry was never promoted to a guaranteed slot. All three fixed.
  • Names: lookupName() short-circuited on the 12-char placeholder assigned by notePeerInterface() when a peer messages us before ever announcing, blocking recall_app_data() from returning their real display name.
  • Stamps: stamp computation could grind forever or crash on high proof-of-work costs. Added STAMP_HARD_MAX=16 (hard reject above ESP32-S3 feasibility ceiling), a 30 s abort timeout, and try/catch OOM protection in the stamp task. Stack doubled to 8 KB. UserConfig ceiling clamped to 1–16.

Added

  • UI: "Show Address" in the Peers tab action menu and Messages tab context menu — displays the peer's full 32-char LXMF destination hash as two formatted lines for identity verification against spoofing.

Changed

  • Settings: WiFi mode and Auto LAN changes now chain a "Reboot now / Later" popup immediately after saving instead of showing an easily-missed toast.
  • Peers: non-contact peers sorted by most-recently-heard time (active nodes float to the top); saved contacts keep alphabetical order.

v0.1.1

Choose a tag to compare

@github-actions github-actions released this 30 Jun 17:34

Full Changelog: v0.1.0...v0.1.1

v0.1.0

Choose a tag to compare

@0x00001312 0x00001312 released this 30 Jun 10:15

Added

  • Security: optional duress password (Settings > Security > Duress
    Password). Entering it at the boot unlock screen instead of the real
    password wipes the identity, messages, contacts, and settings on every
    storage tier (NVS, SD, flash), then reboots straight into the normal
    first-time-setup screen with no "wiped" indicator shown. The new
    Duress module stores a verifier blob in NVS and checks candidate
    passwords by reusing IdentityCrypto's wrap/unwrap envelope, so a
    wrong guess never derives a real key. The wipe routine itself is a new
    shared FactoryWipe module, also used by the existing manual Factory
    Reset. Configured via a small popup (Enable/Disable/Reset), with
    validation that the duress password differs from the real one. See
    docs/duress-password.md.
  • Security: optional auto-lock (Settings > Security > Auto-Lock).
    Pick an idle timeout — 30 min, 1h, 4h, 8h, 12h, or Disabled (the
    default) — and once the device has been idle that long it reboots
    back to the at-rest password screen, discarding the decrypted identity
    from RAM until the real password is re-entered.
  • Messaging: anti-spam proof-of-work stamps (LXStamper), for peers
    that require one before accepting a message. Stamps generate in the
    background on their own task so sending stays responsive while the
    device grinds the proof of work; a confirm overlay asks first if the
    required cost is high enough to take a while. Settings > Messaging
    gets a stamp cost ceiling editor — stamps above it prompt instead of
    generating silently. See docs/lxmf-stamps.md.
  • Radio: new Diagnostics screen (Settings > Radio > Diagnostics),
    separate from the existing Frequency/SF/BW/CR/TX Power editor (now
    under Settings > Radio > Config). Three pages, cycled with ;/.:
    Live (TX/RX throughput graph, RSSI/SNR/noise floor, airtime usage,
    chip/link status, time since last RX/TX), Counters (packet/failure/
    CRC-fail/queue-drop counts and decoded radio device errors, clearable),
    and Network (current PHY config, Reticulum path/link counts, last
    announce time, known peers, LXMF outbox depth, heap/loop health).
    Backed by new packet/error counters on SX1262/LoRaInterface and a new
    reusable StatGraph widget.
  • GPS: system clock now syncs to UTC directly from the GPS RMC
    sentence instead of relying on a user-picked timezone. For local time
    display, GPSManager checks the GPS fix's coordinates against a new
    TimeZoneDB table of real POSIX TZ rules covering major regions, so
    DST transitions land on the correct date; positions outside the table
    fall back to a longitude/season estimate. Settings > Time adds a
    manual override (toggle + UTC offset field) for when the automatic
    estimate is wrong. The status bar clock now has its own permanent
    spot instead of alternating with battery percentage, and dims until
    GPS confirms a real satellite fix.

Fixed

  • LoRa: the radio had no collision avoidance at all — it keyed up
    immediately whenever it wasn't itself mid-TX/RX, with no notion of
    another node transmitting on the same channel. On a shared
    half-duplex channel with more than one active node, this was a direct
    cause of delayed/dropped/reordered delivery. Added listen-before-talk:
    SX1262::dcd() detects an in-progress carrier/header via the modem's
    PREAMBLE_DET/HEADER_DET IRQ flags, and LoRaInterface now queues
    instead of transmitting into a busy channel, draining the queue once
    the channel clears after a randomized contention window sized from
    the radio's own symbol time.
  • LoRa: fixed a duplicate-detection bug in microReticulum's
    packet-hashlist eviction — it's a std::set<Bytes> ordered by hash
    value, but eviction was erasing from .begin() as if that were
    insertion order, so culling discarded whichever hashes were
    numerically smallest instead of the actually-oldest ones. Under
    sustained traffic this silently undermined duplicate detection. Now
    tracks real insertion order separately
    (0x00001312/microReticulum@3481921).
  • Routing: routing and reply targets broke across every reboot —
    the message store was initialized before the identity was loaded, and
    saved conversation directories were keyed by a truncated hash too
    short to address a reply back to. Fixed by initializing the message
    store with the loaded identity before refresh, and resolving saved
    conversation directories to full peer hashes for reply targets.
  • LXMF: sends to a peer with no known path were gated on a flat
    10s retry timer even after the path had already arrived, so the best
    case was a full 10-20s wait. sendDirect() now re-checks
    recall()/has_path() every queue-drain pass (10Hz) and only
    throttles the actual network-touching request_path()/
    ensureOutboundLink() calls to the 10s interval.
  • LXMF: replaced the reactive, one-shot path nudge for single-hop
    peers (raced the reply it was meant to fix, and missed any relayed
    message) with a durable per-peer preferred-interface record in
    AnnounceManager, persisted across reboots alongside saved contacts.
  • LXMF: peer names could go stale or vanish after a reboot, or even
    mid-session, because names were only ever learned from
    AnnounceManager's own rate-limited, debounced announce handler. Name
    lookups now fall back to RNS's own Identity::recall_app_data()
    cache — populated for every announce Transport validates, independent
    of AnnounceManager — and opportunistically backfill the on-disk name
    cache when they resolve a name from it.
  • LXMF: a peer name change never marked the UI dirty, so
    MessagesScreen wouldn't pick up an updated name while idle on
    screen; a name-change callback now calls ui.markContentDirty()
    directly.
  • microReticulum: picked up two upstream fixes via dependency pin
    bumps: the previously-stubbed Link retransmission watchdog plus two
    resource-transfer bugs (windowed part requests, and outbound/inbound
    resource slots never freeing after a transfer concludes;
    a71a676); and a RequestReceipt constructor that threw
    new std::invalid_argument(...) (a pointer, not a value), which
    silently slipped past any upstream catch (const std::exception&)
    and leaked the heap-allocated exception (72bdc81).

Changed

  • Settings > Wi-Fi > Mode now opens a popup listing OFF/AP/STA with the
    active mode marked, instead of silently cycling to the next mode on
    every Enter press.
  • Unified every small fixed-choice setting (Audio, Time Source,
    Auto-discover LAN, Theme input mode, Auto-Lock, WiFi Mode, Duress
    Password) onto one shared OptionPopup widget, replacing a mix of
    silent cycle-on-Enter toggles, a dedicated full-screen page, and an
    ad-hoc popup. Full-screen lists are now reserved for genuinely
    open-ended choices (WiFi scan, TCP connections, Theme presets). Also
    cleaned up unclear labels (Radio Config's SF/BW/CR, SD Card's
    "Initialize Standalone", TCP's "+ Add Connection", Display's bare
    "Name:") and moved SD Card to sit with About/Factory Reset instead of
    next to frequently-tuned settings. No stored values, validation, or
    applied behavior changed — UI/IA only.
  • Duress Password moved from a dedicated settings page into the same
    popup style, with Enable/Disable/Reset rows.

Removed

  • LXMF propagation node (offline messaging via PN) client, added
    earlier in this cycle and fully removed before release: PN sync kept
    crashing and destabilizing routing on real hardware, and chasing it
    down kept colliding with the parts of this firmware that need to be
    solid — delivery, routing, the radio link. PropagationClient is
    deleted along with every call site, Settings UI screen, and config
    field that existed only to support it. Direct LXMF delivery, anti-spam
    stamps, routing/interface stickiness, and announce handling are
    unaffected — a message either reaches its recipient directly now, or
    it fails, same as before PN existed. docs/propagation-nodes.md kept
    as a short note on why this was dropped.
  • Dead radio-preset code on the Home screen, and the status card it fed.

Build

  • microReticulum dependency now points at a personal fork (pinned
    commit) so the stamp/field support LXStamper needs, plus the
    reliability fixes above, can land as real commits there instead of
    local patches.
  • The packaged factory .bin now lands in the .pio build dir instead
    of the project root.

Docs

  • New docs/duress-password.md and docs/lxmf-stamps.md, plus
    docs/propagation-nodes.md explaining why propagation-node support
    was attempted and then removed. Cross-linked from the boot sequence,
    threat model, encryption overview, and UI framework docs.
  • Noted a one-time UX side effect for installs migrating from upstream
    ratspeak/rsCardputer configs: the old boot-time timezone picker is
    gone (superseded by GPS time sync), so first boot after migrating
    re-runs LoRa region/frequency setup via the new RadioSetupScreen
    instead of inheriting it from a timezone pick.

Full diff: https://github.com/0x00001312/rsCardputer-CE/compare/v0.0.5...v0.1.0

v0.0.5

Choose a tag to compare

@github-actions github-actions released this 27 Jun 15:47

v0.0.4

Choose a tag to compare

@0x00001312 0x00001312 released this 27 Jun 10:23

Changelog

Added

  • Boot screen now opens with a short Cryptspeak intro animation: the speech-bubble/keyhole mark wipes in top-to-bottom, "CRYPTSPEAK" decrypts into place letter-by-letter through scrambled noise glyphs, then the subtitle/version/progress bar fade in together instead of popping in at full brightness. The mark is drawn with the live theme accent color, so it matches whatever preset is active. Adds a fixed one-time ~1s cosmetic delay at the very start of boot; nothing else in the boot sequence is reordered or delayed.
  • LoRa interface now defaults to Reticulum's roaming mode instead of the library default, matching this firmware's use as a mobile endpoint rather than a fixed gateway.

Fixed

  • Radio: image/PLL calibration was issued from the wrong standby mode (STDBY_XOSC instead of the datasheet-required STDBY_RC) whenever the configured LoRa region changed (e.g. Americas 915MHz → Europe 868MHz). This left the chip locked on the previously calibrated band until a happenstance no-op Settings save "fixed" it — a real bug that's been present since the radio driver was written.
  • Radio: isTxBusy() checked the timeout deadline before checking the TX-done IRQ flag, so a loop() call delayed past the deadline by UI rendering could discard a transmission that had actually completed successfully. The radio driver now checks the IRQ first, and a genuine TX timeout is retried once automatically instead of silently dropping the packet. Also logs if the SPI mutex fails to allocate, which previously failed silently.
  • Transport: TCPClientInterface's RX/TX frame buffers were static, shared across every instance under the assumption only one TCP hub connection runs at a time. Since up to 4 hub connections can run concurrently, two in-flight connections could silently corrupt each other's buffered bytes. Buffers are now per-instance. Also fixed Header2 hub transport_id learning, which was cached once from the first packet and never re-checked — if a hub later regenerated its identity without dropping the TCP socket, outgoing packets kept using the stale id.
  • UI: password/unlock screen had a dead backspace handler (an unreachable code path meant to return focus to the password field from an empty confirm field) and a layout bug where setup mode's two-field box overlapped the status/hint text. Both fixed, and field labels are now left-aligned to their box instead of floating centered above it.

Removed

  • The M5Launcher boot-mode chooser and the bundled GPLv3 RNode TNC firmware image, along with the dual-image OTA partition-switching code that supported them. This fork only ever ships the standalone LXMF messenger, so the dual-image infrastructure inherited from upstream was dead weight — and the OTA mode-switch code was actively harmful, since it unconditionally forced the boot partition back to slot 0 on every boot. Does not affect RNode on-air protocol compatibility; the SX1262 driver and LoRa transport used by standalone mode are untouched.
  • Dead code: an unused HotkeyManager tab-cycle callback that was never invoked, and a UIManager render flag that was written once and never read.

Docs

  • Added six technical docs covering the Reticulum/LXMF core, every network transport interface, the SX1262 radio driver, the hardware/peripheral layer, and the UI framework — filling in the gaps left by the existing encryption/storage docs.
  • Fixed the Makefile/merge_firmware.py build-and-package flow, which never actually worked under a pipx-installed PlatformIO. make package now produces dist/rscardputer-standalone.zip end to end.

Full diff: https://github.com/0x00001312/rsCardputer-CE/compare/v0.0.3...v0.0.4

Note: This is still Beta Software, some issues may occur. The TX-timeout/retry fix has not yet been verified on real hardware.

v0.0.3

Choose a tag to compare

@0x00001312 0x00001312 released this 26 Jun 18:02

Changelog

Added

  • Custom UI color themes, configurable from Settings → Display → Theme. 9 built-in presets (Default, Amber Retro, Ocean Blue, Mono, High Contrast, Voidframe, Nightshade, Phosphor, Glitchline) plus a [Custom] entry, chosen the same way as any preset.
  • Two ways to set a custom color: type a 6-digit hex code directly, or use an optional RGB mixer with accelerating hold-to-repeat — a tap moves a channel by exactly 1, holding ramps both the repeat rate and step size together for a fast but still controllable sweep.
  • Theme data lives in its own file, theme.json (flash + SD), deliberately outside the encrypted settings blob — color data isn't sensitive, and it has to be available before the password gate even renders. Strict per-field validation and a 2KB size cap mean a corrupted or maliciously edited file can never do worse than look ugly. Full design and threat reasoning: docs/theme-config.md.

Changed

  • Settings reorganized: Theme moved under Display; brightness/dim/off timeout moved into a new Display → Screen submenu.
  • Removed the radio frequency presets (bloat) — radio fields are plain manual entry only now.
  • The 7 editable theme fields are renamed and reordered to describe what they actually control rather than abstract design-system terms: Background, Text, Subtext, Highlight, Header, Warning, Error.

Full diff: https://github.com/0x00001312/rsCardputer-CE/compare/v0.0.2...v0.0.3

Note: This is still Beta Software, some issues may occur.

v0.0.2

Choose a tag to compare

@0x00001312 0x00001312 released this 23 Jun 19:58

Changelog

Note: This is still Beta Software, some issues may occur.

v0.0.1

v0.0.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 23 Jun 17:34

Cryptspeak v0.0.1 — Independent Release Line

This is the first release under Cryptspeak's own versioning. Starting now,
this fork follows its own release cadence and version numbers, decoupled
from upstream ratspeak/rsCardputer's
numbering and the old "-CE" tag suffix.

All prior releases on this fork (v2.0.0-CE, v2.0.1-CE) have been
removed.
They predate this independent line, one of them shipped before a
critical security fix (see below), and both were early beta snapshots. If
you're running either, upgrade.

Security

  • Critical: Ed25519 signature verification bypass. Upstream microReticulum
    had a signature-verification bug that could allow identity spoofing. Fixed
    here first by switching to an emergency fork
    (0x00001312/microReticulum)
    ahead of upstream, then switched back to
    ratspeak/microReticulum once
    upstream shipped an equivalent fix (commit 3ddf3c3). v2.0.0-CE predates
    this fix and was vulnerable
    — this is the main reason it's been removed
    rather than left up.

Encryption: full at-rest coverage

The original Crypto Edition work added:

  • Identity: PBKDF2-HMAC-SHA256 (65,536 iterations) password-derived key
    wrapping the Reticulum identity private key, AES-256-CTR + HMAC-SHA256,
    constant-time MAC verification before decrypt.
  • Messages: at-rest encryption keyed via HKDF-SHA256 from the identity's
    private key, same AES-256-CTR + HMAC-SHA256 construction.
  • VeraCrypt-style password gate at boot, with lockout after 10 wrong
    attempts per power cycle, and automatic migration of any pre-existing
    plaintext identity/messages.

This release closes the two gaps that remained:

  • Contacts (saved names + the name cache) — previously plaintext JSON,
    now encrypted with the same construction, keyed independently so a
    contacts key can't decrypt messages or vice versa.
  • Device settings (WiFi AP/STA passwords, TCP hub addresses, radio
    config, display name) — same gap, same fix.

Both new domains share a generalized AtRestCrypto engine rather than
duplicating the crypto three times over, and migrate automatically and
immediately on first boot once the identity is unlocked — no prompt needed,
since both datasets are small. This also fixes a latent bug where the
settings NVS backup used putString/getString, which silently truncates
on embedded NUL bytes that an encrypted blob can legitimately contain.

Full design write-up, including the threat model: docs/.

Branding

Product is now Cryptspeak on-device (boot screen, name-input, settings)
and in the README, with rsCardputer-CE kept as the underlying codebase
name. Network defaults (TCP hub seeds, WiFi AP password) and internal
storage paths are unchanged — those are functional, not cosmetic.

Also included

  • RNode: hardened SX1262 host-reconnect handling (setModulationParams/
    setPacketParams now force standby before applying radio parameters —
    the chip silently ignores those opcodes outside standby).
  • Build workflow and Makefile refactored for this fork's own CI.

Credits

Full diff since fork point: https://github.com/0x00001312/rsCardputer-CE/compare/1b85b670...v0.0.1