Skip to content

Enable credential complementation#124

Draft
d2dyno1 wants to merge 12 commits intof_vault4from
f_complementation
Draft

Enable credential complementation#124
d2dyno1 wants to merge 12 commits intof_vault4from
f_complementation

Conversation

@d2dyno1
Copy link
Copy Markdown
Member

@d2dyno1 d2dyno1 commented Apr 30, 2026

No description provided.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds support for “credential complementation” (unlocking a vault using an alternative credential such as a key file/biometric) by persisting and consuming a complementation shares file, and updates UI + tests to exercise the new flow.

Changes:

  • Introduces a new vault complementation shares artifact (sfcomplement.sfsconfig) and cryptographic wrap/unwrap logic for complemented unlock.
  • Adds a new ModifyComplementationAsync path (routine + SDK service surface) and updates unlock logic to support alternative credentials.
  • Updates login/credentials UI to surface alternative login options and adds NUnit coverage for complementation scenarios.

Reviewed changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/SecureFolderFS.Tests/VaultTests/CredentialTests.cs Adds tests for add/replace/remove complementation and primary-change scenarios.
tests/SecureFolderFS.Tests/CliTests/CommandDiscoveryTests.cs Updates CliFx namespace usage for command discovery.
tests/SecureFolderFS.Tests/CliTests/CliTestHost.cs Updates CliFx app builder usage and command registration style.
src/Shared/SecureFolderFS.Shared/Models/ComplementationCredentials.cs Adds model for passing current/new primary/new complement credentials into complementation routine.
src/Sdk/SecureFolderFS.Sdk/ViewModels/Views/Credentials/CredentialsSelectionViewModel.cs Changes UI flagging for whether complementation is available based on selected auth VM capability.
src/Sdk/SecureFolderFS.Sdk/ViewModels/Views/Credentials/CredentialsConfirmationViewModel.cs Routes certain credential changes through the new complementation routine and builds complementation credentials.
src/Sdk/SecureFolderFS.Sdk/ViewModels/Controls/LoginViewModel.cs Adds “alternative login” UI mode and selection handling for complemented vaults.
src/Sdk/SecureFolderFS.Sdk/Services/IVaultManagerService.cs Adds ModifyComplementationAsync to the SDK service interface.
src/Platforms/SecureFolderFS.Uno/Views/Vault/VaultLoginPage.xaml Wires new alternative-login bindings into Uno login options.
src/Platforms/SecureFolderFS.Uno/UserControls/LoginOptions.xaml.cs Adds dependency properties + handler for authentication option selection.
src/Platforms/SecureFolderFS.Uno/UserControls/LoginOptions.xaml Adds ComboBox UI for selecting among alternative authentication options.
src/Platforms/SecureFolderFS.Uno/UserControls/InterfaceRoot/VaultPreviewRootControl.xaml Wires alternative-login bindings in preview root login options.
src/Platforms/SecureFolderFS.Uno/Platforms/Windows/ServiceImplementation/WindowsVaultCredentialsService.cs Enumerates login methods including complementation.
src/Platforms/SecureFolderFS.Uno/Platforms/MacCatalyst/ServiceImplementation/MacOsVaultCredentialsService.cs Enumerates login methods including complementation.
src/Platforms/SecureFolderFS.Uno/Platforms/Desktop/ServiceImplementation/SkiaVaultCredentialsService.cs Enumerates login methods including complementation.
src/Platforms/SecureFolderFS.Uno/Dialogs/CredentialsDialog.xaml Adjusts complementation checkbox presentation in credentials dialog.
src/Platforms/SecureFolderFS.UI/ViewModels/Authentication/KeyFileViewModel.cs Enables key file authentication to be used as a complement.
src/Platforms/SecureFolderFS.UI/ServiceImplementation/VaultManagerService.cs Implements ModifyComplementationAsync by invoking the new core routine.
src/Platforms/SecureFolderFS.UI/ServiceImplementation/BaseVaultCredentialsService.cs Adds shared helper to enumerate methods + complementation for login view model creation.
src/Platforms/SecureFolderFS.Maui/Platforms/iOS/ServiceImplementation/IOSVaultCredentialsService.cs Enumerates login methods including complementation.
src/Platforms/SecureFolderFS.Maui/Platforms/Android/ServiceImplementation/AndroidVaultCredentialsService.cs Enumerates login methods including complementation.
src/Core/SecureFolderFS.Core/VaultAccess/VaultWriter.cs Adds writer for the new complementation shares file.
src/Core/SecureFolderFS.Core/VaultAccess/VaultReader.cs Adds reader for the new complementation shares file.
src/Core/SecureFolderFS.Core/VaultAccess/VaultParser.cs Adds HKDF derivation + AES-GCM wrap/unwrap helpers for complementation secrets.
src/Core/SecureFolderFS.Core/Routines/Operational/VaultRoutines.cs Exposes new ModifyComplementation() routine entrypoint.
src/Core/SecureFolderFS.Core/Routines/Operational/UnlockRoutine.cs Adds complemented unlock path using derived/unwrap complement secret.
src/Core/SecureFolderFS.Core/Routines/Operational/ModifyComplementationRoutine.cs Adds routine to add/replace/remove complementation and update keystore/config/shares.
src/Core/SecureFolderFS.Core/DataModels/VaultSharesDataModel.cs Adds data model for persisted complementation shares.
src/Core/SecureFolderFS.Core/Constants.cs Adds filename constant for the complementation shares file.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/SecureFolderFS.Tests/VaultTests/CredentialTests.cs
Comment thread src/Core/SecureFolderFS.Core/Routines/Operational/UnlockRoutine.cs Outdated
Comment thread src/Core/SecureFolderFS.Core/Routines/Operational/UnlockRoutine.cs
Comment thread src/Core/SecureFolderFS.Core/VaultAccess/VaultWriter.cs
@d2dyno1 d2dyno1 mentioned this pull request May 1, 2026
8 tasks
…r explicit separation of primary/complement keys

Agent-Logs-Url: https://github.com/securefolderfs-community/SecureFolderFS/sessions/14c473bd-90b2-45f9-97c1-c6b0c6e075b2

Co-authored-by: d2dyno1 <53011783+d2dyno1@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI commented May 2, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • crl.godaddy.com
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/SecureFolderFS/SecureFolderFS/src/Platforms/SecureFolderFS.Uno/SecureFolderFS.Uno.csproj --packages /tmp/codeql-scratch-d52628fd4f0752cc/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-d52628fd4f0752cc/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-d52628fd4f0752cc/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)
  • ocsp.godaddy.com
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/SecureFolderFS/SecureFolderFS/src/Platforms/SecureFolderFS.Uno/SecureFolderFS.Uno.csproj --packages /tmp/codeql-scratch-d52628fd4f0752cc/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-d52628fd4f0752cc/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-d52628fd4f0752cc/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 34 out of 34 changed files in this pull request and generated 8 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +165 to +176
private void RemoveComplementation(ComplementationCredentials credentials, AuthenticationMethod oldAuthentication)
{
var currentPrimaryKey = ExportKey(credentials.CurrentCredential);
byte[]? targetPasskey = null;
byte[]? complementSecret = null;
byte[]? softwareEntropy = null;

try
{
targetPasskey = credentials.NewPrimaryCredential is null ? currentPrimaryKey : ExportKey(credentials.NewPrimaryCredential);
complementSecret = DeriveComplementSecret(currentPrimaryKey, GetPrimaryMethod(oldAuthentication));
softwareEntropy = DecryptSoftwareEntropy(complementSecret);
Comment on lines +104 to +113
<uco:OptionsControl Title="Login method" IsVisible="{Binding ViewModel.LoginViewModel.IsAlternativeLogin, Mode=OneWay}">
<uco:OptionsControl.Slot>
<ucc:ModernPicker
BindingContext="{x:Reference ThisPage}"
HorizontalTextAlignment="End"
IsTransparent="True"
ItemDisplayBinding="{Binding Title, Mode=OneWay, x:DataType={x:Type vm:AuthenticationViewModel}}"
ItemsSource="{Binding ViewModel.LoginViewModel.AuthenticationOptions}"
SelectedIndexChanged="AuthenticationOptions_SelectedIndexChanged"
SelectedItem="{Binding ViewModel.LoginViewModel.SelectedAuthenticationOption, Mode=TwoWay}" />
Comment on lines +137 to +147
var currentCredential = GetCredentialAt(OldPasskey, 0) ?? OldPasskey;
var oldComplementation = configuredProcedure.Complementation;
var newComplementation = updatedProcedure.Complementation;
var primaryChanged = !configuredProcedure.Methods.SequenceEqual(updatedProcedure.Methods);

if (string.IsNullOrWhiteSpace(oldComplementation) && !string.IsNullOrWhiteSpace(newComplementation))
{
return new()
{
CurrentCredential = currentCredential,
NewComplementCredential = GetCredentialAt(key, 1) ?? key
<uco:OptionsContainer VerticalOptions="End">
<uco:OptionsContainer.InnerContent>
<VerticalStackLayout>
<uco:OptionsControl Title="Login method" IsVisible="{Binding ViewModel.LoginViewModel.IsAlternativeLogin, Mode=OneWay}">
IsChecked="{Binding IsComplementing, Mode=TwoWay}"
IsEnabled="{Binding IsComplementationAvailable, Mode=OneWay}"
VerticalOptions="Center" />
<Label Text="Use as substitute" VerticalOptions="Center" />
SelectedAuthenticationOption = AuthenticationOptions.FirstOrDefault();
CurrentViewModel = SelectedAuthenticationOption is not null
? SelectedAuthenticationOption
: new ErrorViewModel("No authentication methods available.");
Comment on lines +137 to +170
var currentCredential = GetCredentialAt(OldPasskey, 0) ?? OldPasskey;
var oldComplementation = configuredProcedure.Complementation;
var newComplementation = updatedProcedure.Complementation;
var primaryChanged = !configuredProcedure.Methods.SequenceEqual(updatedProcedure.Methods);

if (string.IsNullOrWhiteSpace(oldComplementation) && !string.IsNullOrWhiteSpace(newComplementation))
{
return new()
{
CurrentCredential = currentCredential,
NewComplementCredential = GetCredentialAt(key, 1) ?? key
};
}

if (!string.IsNullOrWhiteSpace(oldComplementation) && string.IsNullOrWhiteSpace(newComplementation))
{
return new()
{
CurrentCredential = currentCredential,
NewPrimaryCredential = updatedProcedure.Methods.Length > 1 ? key : null
};
}

if (!string.IsNullOrWhiteSpace(oldComplementation) && !string.IsNullOrWhiteSpace(newComplementation))
{
var updatePrimaryCredential = primaryChanged || _authenticationStage == AuthenticationStage.FirstStageOnly;
var updateComplementCredential = !string.Equals(oldComplementation, newComplementation, StringComparison.Ordinal) ||
_authenticationStage == AuthenticationStage.ProceedingStageOnly;

return new()
{
CurrentCredential = currentCredential,
CurrentComplementCredential = GetCredentialAt(OldPasskey, 1),
NewPrimaryCredential = updatePrimaryCredential ? GetCredentialAt(key, 0) ?? key : null,
Comment on lines +98 to +104
protected static IEnumerable<string> EnumerateLoginMethods(AuthenticationMethod unlockProcedure)
{
foreach (var item in unlockProcedure.Methods)
yield return item;

if (!string.IsNullOrWhiteSpace(unlockProcedure.Complementation))
yield return unlockProcedure.Complementation;
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.

3 participants