Skip to content

Move TC uplink security onto upstream F Prime SDLS interfaces - #513

Draft
nateinaction wants to merge 1 commit into
mainfrom
tc-security-decryptor-sdls
Draft

Move TC uplink security onto upstream F Prime SDLS interfaces#513
nateinaction wants to merge 1 commit into
mainfrom
tc-security-decryptor-sdls

Conversation

@nateinaction

@nateinaction nateinaction commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Replace Components::TcSecurityDeframer (a project-local pass-through component written before F Prime had a security hook) with Components::TcSecurityDecryptor, a decryptor implementing Svc.Ccsds.CcsdsSdlsDecrypt behind the upstream Svc::Ccsds::CcsdsSdlsDeframer introduced in F Prime 4.3.0.
  • The security association index is now supplied by the deframer instead of being parsed from the frame, so MAC verification moves to the PSA multipart API (psa_mac_verify_setup/update/update/psa_mac_verify_finish) to keep the SA index inside the authenticated span.
  • Wire format, the authenticated-flag policy design, ground-side Framing plugin, and all command opcodes are unchanged. No SdlsSaRouter or SdlsFileKeyManager adopted — both rejections are documented in the component SDD, along with the seam Move HMAC auth key off firmware image onto internal-flash key store #472 (key store) and feat(TcSecurityDeframer): expose active key SPIs and fingerprints #490 (key fingerprints) can land through.

Test plan

  • make test-unit — all 8 host gtest suites pass (added coverage that the SA index participates in the MAC)
  • make generate build — clean full Zephyr flight build; GET_SEQ_NUM opcodes confirmed unchanged (0x2100B000/0x2200B000) and CurrentSequenceNumber channels present in the topology dictionary for UART/LoRa
  • make check-console-disabled
  • Bench integration on the flight control board: flashed, ran sync_sequence_number then the broader integration suite (11 passed, 0 failed); event log shows the anti-replay path organically rejecting a duplicate frame (SequenceNumberInvalid) without advancing the counter, no AuthenticationFailed/ParsingFailed/DecryptionFailed events, commsBufferManager high-water stable
  • LoRa-link round-trip and unauthenticated-bypass/truncated-frame scenarios — not exercised on this bench (no radio hardware attached, no raw-frame-injection tooling exists yet in the ground tooling; noted as a pre-existing follow-up gap)

Replace Components::TcSecurityDeframer, a project-local pass-through
component from before F Prime had a security hook, with
Components::TcSecurityDecryptor: a decryptor implementing
Svc.Ccsds.CcsdsSdlsDecrypt behind the upstream Svc::Ccsds::CcsdsSdlsDeframer
introduced in F Prime 4.3.0. The security association index is now supplied
by the deframer rather than parsed from the frame, so MAC verification moves
to the PSA multipart API to keep the SA index inside the authenticated span.
Wire format, authenticated-flag policy, and all command opcodes are
unchanged; no SdlsSaRouter or SdlsFileKeyManager adopted (documented in the
component SDD).

Verified: host unit tests, a full Zephyr build (opcodes/telemetry channels
confirmed in the topology dictionary), and a bench integration run on the
flight control board showing a clean authenticated round-trip and the
anti-replay path rejecting a duplicate frame without advancing the counter.
@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nateinaction nateinaction left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

Reviewed the rename and re-platforming onto Svc.Ccsds.CcsdsSdlsDeframer / CcsdsSdlsDecrypt. The substance is correct. Verified:

  • Wire format compatibility holds. authenticateFrame MACs saIndex(2, BE) || [SeqNum|Data], which matches Framing/src/authenticate_plugin.py's HMAC(SPI|SeqNum|Data) exactly.
  • Test vectors are right. Recomputed both new vectors in test_TcSecurityDecryptor_Authenticator.cpp.
  • Opcodes preserved. ReferenceDeploymentTopologyDictionary.json shows 0x2100B000 / 0x2200B000 for GET_SEQ_NUM, keeping the Bypasser.cpp literals valid; CurrentSequenceNumber exists on both links.
  • Buffer ownership is sound on both the SUCCESS and DECRYPTION_FAILURE paths. Dropping the un-slice in the return handler is safe: BufferManager::bufferSendIn_handler only asserts the pointer is inside the allocation and that the size did not grow.
  • Truncated MAC works in the multipart PSA pathpsa_mac_finalize_alg_and_key_validation only rejects < 4 bytes, and the imported key's PSA_KEY_USAGE_VERIFY_MESSAGE is the flag psa_mac_verify_setup requires. No fallback needed.
  • No base-id collisions. The new sdlsDeframer base id +0x0C000 is clear in all three subtopologies; logOut / logTextOut / timeCaller are auto-connected, and the unconnected errorNotify is guarded upstream.

Three inline findings below, plus one that is not anchorable because the file is not in the diff:

Stale debug configs — .vscode/launch.json:38 (low)

Three debug configurations still point at build-gtest/test_TcSecurityDeframer_{Authenticator,Parser,Validator}. After this rename make test-unit produces test_TcSecurityDecryptor_*, so all three fail to launch. This file was missed by the rename fan-out.

// drops the frame and returns the buffer for deallocation.
this->log_WARNING_HI_ParsingFailed(static_cast<PacketParserStatus::T>(parseResult.status));
this->dataReturnOut_out(0, data, contextOut);
this->decryptOut_out(0, Svc::Ccsds::SdlsStatus::DECRYPTION_FAILURE, data, contextOut);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium — unthrottled event amplification from the uplink

Routing the parse-failure drop through decryptOut_out(DECRYPTION_FAILURE, ...) makes the upstream deframer emit Svc.Ccsds.CcsdsSdlsDeframer.DecryptionFailed, which is declared with no throttle, in addition to the local ParsingFailed (throttle 2).

Producing a droppable frame requires no secret — only a well-formed TC primary header and a correct FECF CRC16. So anyone transmitting on the LoRa/UART link can emit one unthrottled WARNING_HI event per frame indefinitely, crowding real events out of the ComQueue.

The interface offers a quieter drop: bufferReturnOut is documented as "Port for returning the incoming iv/data buffer for deallocation". Calling

this->bufferReturnOut_out(0, data, contextOut);

reaches CcsdsSdlsDeframer::bufferReturnIn_handler -> dataReturnOut_out -> TcDeframer, returning the buffer with no upstream event.

@ downstream by the router.
passive component TcSecurityDecryptor {

import Svc.Ccsds.CcsdsSdlsDecrypt

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low — recursive acquisition of the guarded-port mutex

import Svc.Ccsds.CcsdsSdlsDecrypt makes both decryptIn and decryptReturnIn guarded; the generated TcSecurityDecryptorComponentAc.cpp (lines 1038 and 1066) confirms both call the same this->lock().

The whole downstream chain is synchronous (spacePacketDeframer -> ProvesRouter::dataIn_handler, which calls dataReturnOut_out on the same stack), so decryptReturnIn_handler re-enters m_guardedPortMutex while decryptIn_handler still holds it. This only works because Zephyr's k_mutex is recursive — the removed component deliberately declared sync input port dataReturnIn to avoid exactly this.

On any POSIX host build, Os::Posix::Mutex uses PTHREAD_MUTEX_ERRORCHECK and would FW_ASSERT, which will block re-enabling the commented-out component UT in CMakeLists.txt:52. Worth an explicit comment at minimum, or overriding lock() / unLock().


//! CCSDS 355.0-B-2 Section E2.2
//! The Security Parameter Index is stripped by the upstream Svc.Ccsds.CcsdsSdlsDeframer before
//! this component sees the frame; kSpiSize is kept only for the MAC-prefix fallback in Authenticator.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low — comment refers to code that does not exist

This says kSpiSize is "kept only for the MAC-prefix fallback in Authenticator", but no such fallback was implemented: Authenticator.cpp:113 hardcodes const uint8_t saIndexBytes[2]. kSpiSize is now referenced nowhere, so the comment points a future reader at code that is not there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant