Skip to content

Session establishing performance improved, completed function implementation#5

Merged
MiaKoring merged 4 commits into
amethystsoft:mainfrom
MiaKoring:main
May 7, 2026
Merged

Session establishing performance improved, completed function implementation#5
MiaKoring merged 4 commits into
amethystsoft:mainfrom
MiaKoring:main

Conversation

@MiaKoring
Copy link
Copy Markdown
Member

@MiaKoring MiaKoring commented May 7, 2026

Overview

This PR implements session management improvements and completes several function implementations for the Swift Secret Service library. A significant performance optimization is made to Diffie-Hellman key generation, and multiple new public APIs are added for prompt handling, object locking/unlocking, and session management.

Key Changes

Performance Improvement

  • ietf1024.swift: Optimized DH private key generation by reducing random material from 128 bytes to 16 bytes (AES.randomIV(16) instead of AES.randomIV(128)), improving session establishment performance without compromising security.

New Public APIs

  • Session.swift: Added disconnect() async method to properly close Secret Service sessions
  • Prompt.swift: Added prompt(_:windowID:) and dismissPrompt(_:) methods for DBus-based prompt control
  • Service.swift:
    • Added unlock(objects:) and lock(objects:) async methods for object locking/unlocking operations
    • Added setAlias(_:collection:) method for alias management
    • Added internal dbusClientConnection property for cleaner connection access
  • Signals.swift: Implemented awaitPromptCompleted() async method to subscribe to and handle prompt completion signals from DBus

Helper Utilities

  • MessageDecoding.swift: Enhanced message decoding helpers:
    • Updated decodeCreateItem() with optional function name parameter for better error reporting
    • Added decodeLock() and decodeLock() wrapper methods for Lock/Unlock response payloads
    • Improved error handling consistency across decoding functions

Documentation and Testing

  • Collection.swift & Item.swift: Clarified documentation for prompt result handling in DBus operations
  • Package.swift: Added attaswift/BigInt dependency and updated test target configuration to include Logging product
  • Integration Tests: Refactored test suite to use @Suite(.serialized) pattern, consolidated test item creation via private helper, and added new testLockUnlock test case with comprehensive prompt handling

Highlights

This PR demonstrates excellent engineering by combining performance optimization with thoughtful API design. The reduction in DH key generation material is particularly noteworthy—it maintains cryptographic integrity while significantly improving session establishment speed. The new async/await-based APIs follow modern Swift concurrency patterns, and the addition of proper error handling throughout ensures robust operation in both successful and edge-case scenarios.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

Warning

Rate limit exceeded

@MiaKoring has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 32 minutes and 23 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5d4a9a1a-64e8-4142-852c-7ebca75d5140

📥 Commits

Reviewing files that changed from the base of the PR and between 7c1cdf6 and a571560.

📒 Files selected for processing (3)
  • Sources/SecretService/Helper/ietf1024.swift
  • Sources/SecretService/Secret/Service.swift
  • Tests/swift-secret-serviceTests/IntegrationTests/swift_secret_serviceTests.swift
📝 Walkthrough

Walkthrough

This PR extends the SecretService client library with lock/unlock functionality and supporting prompt/session management. It adds external package dependencies, implements new DBus method calls for lock/unlock/alias operations, introduces signal subscription for prompt completion, and refactors integration tests with a new test case for lock/unlock flows.

Changes

Lock/Unlock Feature Implementation

Layer / File(s) Summary
Package Dependencies
Package.swift
Adds attaswift/BigInt and apple/swift-log (test) dependencies.
Message Decoding Helpers
Sources/SecretService/Helper/MessageDecoding.swift
decodeCreateItem accepts optional function name parameter; new decodeLock and decodeUnlock helpers parse lock/unlock response payloads containing object paths and optional prompts.
Cryptographic Changes
Sources/SecretService/Helper/ietf1024.swift
IETF1024DH private key generation RNG size reduced from 128 to 16 bytes.
API Contract Documentation
Sources/SecretService/Secret/Collection.swift, Sources/SecretService/Secret/Item.swift
Documentation clarifies that prompt results for deleteCollection, createItem, and deleteItem should be single ObjectPath values.
DBus Connection and Interface
Sources/SecretService/Secret/Service.swift
Adds SecS.Iface.prompt DBus interface constant and dbusClientConnection computed property for DBus client access.
Core Lock/Unlock Methods
Sources/SecretService/Secret/Service.swift
Implements unlock(objects:) and lock(objects:) methods invoking DBus Unlock/Lock calls and decoding responses into tuples of locked/unlocked paths plus optional prompt.
Alias Management
Sources/SecretService/Secret/Service.swift
New setAlias(name:collection:) method calls DBus SetAlias with error handling.
Prompt Control
Sources/SecretService/Secret/Prompt.swift
Adds prompt(prompt:windowID:) and dismissPrompt(prompt:) methods for explicit DBus-based prompt invocation and dismissal with configurable window ID.
Session Management
Sources/SecretService/Secret/Session.swift
New disconnect() method closes the current SecretService session via DBus Close call.
Signal Handling
Sources/SecretService/Secret/Signals.swift
Introduces Signals enum with Prompt.completed case and awaitPromptCompleted() method that subscribes to DBus prompt completion signals, returning dismissal status and result.
Test Refactoring and Integration
Tests/swift-secret-serviceTests/IntegrationTests/swift_secret_serviceTests.swift
Refactors test container from class to @Suite struct; introduces createTestItem helper; enhances prompt handling with logging and signal awaiting in testCreateDeleteCollection; adds new testLockUnlock test case.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title partially relates to the changeset—it mentions 'completed function implementation,' but omits the main additions: Unlock/Lock/Prompt/Dismiss/SetAlias APIs, decoding helpers, and Diffie-Hellman key material fix.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (3)
Sources/SecretService/Secret/Signals.swift (2)

3-7: 💤 Low value

Signals.Prompt.completed is exported but never referenced — dead public API.

The enum is published to callers but isn't used anywhere: awaitPromptCompleted() doesn't use it as a return type, a key, or in any other capacity. Either wire it into the API (e.g., as a typed return value) or remove it to keep the public surface clean.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/SecretService/Secret/Signals.swift` around lines 3 - 7,
Signals.Prompt.completed is a dead public API; either remove the unused public
case or wire it into the API by making the awaiting API use the enum. Update
either the declaration in Signals (remove the public Prompt enum or make it
internal/private) or change awaitPromptCompleted() to return Signals.Prompt (or
use Signals.Prompt as the notification payload) so the completed case is
actually produced and consumed; modify the Symbols: Signals,
Signals.Prompt.completed and awaitPromptCompleted() accordingly to keep the
public surface consistent.

19-29: ⚡ Quick win

Any malformed first signal throws rather than being skipped.

The guard inside for await message in signalStream throws .unexpectedResponse on the first ill-formed message, terminating the whole call. Since the subscription is already filtered to interface: SecS.Iface.prompt, member: "Completed", the chance of a stray non-conforming message is low in practice, but if one arrives (e.g., from a misbehaving client on the bus), the caller gets an error instead of waiting for the next real signal. continue-ing over bad messages is more resilient.

♻️ Proposed fix: skip malformed signals instead of throwing
         for await message in signalStream {
             guard
                 message.messageType == .signal,
                 message.body.count >= 2,
                 let dismissed = message.body[0].boolean
             else {
-                throw .unexpectedResponse(for: "Signal Prompt.Completed")
+                continue
             }
             
             return (dismissed: dismissed, result: message.body[1])
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/SecretService/Secret/Signals.swift` around lines 19 - 29, In the
async loop that iterates "for await message in signalStream" in Signals.swift,
don't throw .unexpectedResponse inside the guard that checks
message.messageType, message.body.count, and message.body[0].boolean; instead
skip malformed signals by replacing the throw with continue so the subscription
keeps waiting for the next valid "Prompt.Completed" signal, and keep the final
throw after the loop for the stream-ending case.
Sources/SecretService/Secret/Prompt.swift (1)

8-55: ⚡ Quick win

getSession() is called but its result is never used in either prompt() or dismissPrompt().

Both functions do let (session, _) = try getSession() and then never reference session in the request — the request path is the prompt's own object path, not the session's. The D-Bus Prompt and Dismiss calls don't require a session path at all.

This creates an unintentional precondition: callers must have an active session to prompt/dismiss, which the spec doesn't require and will cause a spurious failure if the session has expired or hasn't been opened yet.

♻️ Proposed fix: remove the unused `getSession()` calls
 public func prompt(
     _ prompt: String,
     windowID: String?
 ) async throws(SecSError) {
-    let (session, _) = try getSession()
-    
     let request = DBusRequest.createMethodCall(

 // ...

 public func dismissPrompt(
     _ prompt: String
 ) async throws(SecSError) {
-    let (session, _) = try getSession()
-    
     let request = DBusRequest.createMethodCall(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/SecretService/Secret/Prompt.swift` around lines 8 - 55, The calls to
getSession() in prompt(_:) and dismissPrompt(_:) are unnecessary and create a
spurious dependency on an active session; remove the lines that call let
(session, _) = try getSession() from both methods (prompt and dismissPrompt) so
the DBusRequest uses the provided prompt object path directly, preserving the
existing send(request) handling and error checks unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/SecretService/Helper/ietf1024.swift`:
- Line 27: The DH private exponent generation uses AES.randomIV(16) producing a
128-bit value (hexPrivateKey) which is below the NIST-recommended 160-bit
minimum; update the call that creates hexPrivateKey (the AES.randomIV invocation
used to derive the private exponent in ietf1024.swift) to produce 20 bytes (160
bits) instead of 16 bytes so the generated private exponent meets the minimum
entropy requirement.

In `@Sources/SecretService/Secret/Service.swift`:
- Around line 218-226: The doc comment for the setAlias method incorrectly
references org.freedesktop.Secret.Service.ReadAlias; update the documentation to
reference the correct DBus method name (org.freedesktop.Secret.Service.SetAlias)
and ensure the descriptive text still matches setAlias's behavior (e.g., mention
any implementations that only support a default alias if applicable). Locate the
comment above the public func setAlias(...) and replace the incorrect method
identifier and any mismatched wording so the docs accurately describe setAlias.
- Around line 134-141: The documentation for the lock method incorrectly says
"objects to unlock" — update the doc comment in Service.swift for the lock
function so the parameter description reads "objects to lock" (or similar
correct wording) and update any related return description if it was copied from
unlock; specifically edit the doc block above the lock(...) method (reference
symbol: lock) to correct the parameter and ensure the overall doc matches lock
behavior.
- Around line 25-27: The force-cast in dbusClientConnection can trap for
non-DBusClient.Connection types; change the accessor to safely cast (e.g. var
dbusClientConnection: DBusClient.Connection? { connection as?
DBusClient.Connection }) and update call sites (Prompt.swift, Signals.swift) to
guard-let unwrap it and throw or propagate a descriptive error from
SecretService (or the calling function) when the cast fails; keep
SecretService.init accepting the DBusServerConnection protocol but ensure
runtime failures are handled instead of using as!.

In
`@Tests/swift-secret-serviceTests/IntegrationTests/swift_secret_serviceTests.swift`:
- Around line 135-139: The test currently logs when result.dismissed is true but
then continues to assign collection = result.result.objectPath, which can be
invalid; change the block handling result.dismissed to return early (or
otherwise skip the assignment) just like the pattern in testLockUnlock, i.e.,
inside the if result.dismissed branch for the prompt handling replace the sole
logger.info call with logger.info(...) followed by an immediate return so
collection is not set when result.dismissed is true.
- Around line 125-143: The test drops result.collection by only assigning the
local variable collection inside the prompt-handling branch; update the logic
around the createCollection result (the tuple with result.collection and
result.prompt) so that when result.prompt is nil you assign collection =
result.collection before the guard, and when result.prompt is non-nil keep the
existing prompt flow (awaitPromptCompleted and assign collection =
result.result.objectPath). Ensure the guard let collection check then succeeds
for the no-prompt code path.

---

Nitpick comments:
In `@Sources/SecretService/Secret/Prompt.swift`:
- Around line 8-55: The calls to getSession() in prompt(_:) and
dismissPrompt(_:) are unnecessary and create a spurious dependency on an active
session; remove the lines that call let (session, _) = try getSession() from
both methods (prompt and dismissPrompt) so the DBusRequest uses the provided
prompt object path directly, preserving the existing send(request) handling and
error checks unchanged.

In `@Sources/SecretService/Secret/Signals.swift`:
- Around line 3-7: Signals.Prompt.completed is a dead public API; either remove
the unused public case or wire it into the API by making the awaiting API use
the enum. Update either the declaration in Signals (remove the public Prompt
enum or make it internal/private) or change awaitPromptCompleted() to return
Signals.Prompt (or use Signals.Prompt as the notification payload) so the
completed case is actually produced and consumed; modify the Symbols: Signals,
Signals.Prompt.completed and awaitPromptCompleted() accordingly to keep the
public surface consistent.
- Around line 19-29: In the async loop that iterates "for await message in
signalStream" in Signals.swift, don't throw .unexpectedResponse inside the guard
that checks message.messageType, message.body.count, and
message.body[0].boolean; instead skip malformed signals by replacing the throw
with continue so the subscription keeps waiting for the next valid
"Prompt.Completed" signal, and keep the final throw after the loop for the
stream-ending case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 28bc1fb7-e600-46b9-b51d-1709223a9af0

📥 Commits

Reviewing files that changed from the base of the PR and between c304681 and 7c1cdf6.

📒 Files selected for processing (10)
  • Package.swift
  • Sources/SecretService/Helper/MessageDecoding.swift
  • Sources/SecretService/Helper/ietf1024.swift
  • Sources/SecretService/Secret/Collection.swift
  • Sources/SecretService/Secret/Item.swift
  • Sources/SecretService/Secret/Prompt.swift
  • Sources/SecretService/Secret/Service.swift
  • Sources/SecretService/Secret/Session.swift
  • Sources/SecretService/Secret/Signals.swift
  • Tests/swift-secret-serviceTests/IntegrationTests/swift_secret_serviceTests.swift

Comment thread Sources/SecretService/Helper/ietf1024.swift Outdated
Comment thread Sources/SecretService/Secret/Service.swift
Comment thread Sources/SecretService/Secret/Service.swift
Comment thread Sources/SecretService/Secret/Service.swift
Comment thread Tests/swift-secret-serviceTests/IntegrationTests/swift_secret_serviceTests.swift Outdated
@MiaKoring MiaKoring merged commit 01a1df4 into amethystsoft:main May 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant