Conversation
There was a problem hiding this comment.
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
ModifyComplementationAsyncpath (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.
…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>
|
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:
If you need me to access, download, or install something from one of these locations, you can either:
|
There was a problem hiding this comment.
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.
| 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); |
| <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}" /> |
| 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."); |
| 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, |
| 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; |
No description provided.