11using System ;
2+ using System . Linq ;
3+ using System . Security . Cryptography ;
24using System . Threading ;
35using System . Threading . Tasks ;
46using SecureFolderFS . Core . Cryptography ;
79using SecureFolderFS . Core . Validators ;
810using SecureFolderFS . Core . VaultAccess ;
911using SecureFolderFS . Shared . ComponentModel ;
12+ using SecureFolderFS . Shared . Models ;
1013using SecureFolderFS . Shared . SecureStore ;
1114
1215namespace SecureFolderFS . Core . Routines . Operational
@@ -17,6 +20,7 @@ internal sealed class UnlockRoutine : ICredentialsRoutine
1720 private readonly VaultReader _vaultReader ;
1821 private V4VaultKeystoreDataModel ? _keystoreDataModel ;
1922 private V4VaultConfigurationDataModel ? _configDataModel ;
23+ private VaultSharesDataModel ? _sharesDataModel ;
2024 private SecureKey ? _dekKey ;
2125 private SecureKey ? _macKey ;
2226
@@ -30,6 +34,7 @@ public async Task InitAsync(CancellationToken cancellationToken)
3034 {
3135 _configDataModel = await _vaultReader . ReadV4ConfigurationAsync ( cancellationToken ) ;
3236 _keystoreDataModel = await _vaultReader . ReadKeystoreAsync < V4VaultKeystoreDataModel > ( cancellationToken ) ;
37+ _sharesDataModel = await _vaultReader . ReadComplementationAsync ( cancellationToken ) ;
3338 }
3439
3540 /// <inheritdoc/>
@@ -38,11 +43,75 @@ public void SetCredentials(IKeyUsage passkey)
3843 ArgumentNullException . ThrowIfNull ( _configDataModel ) ;
3944 ArgumentNullException . ThrowIfNull ( _keystoreDataModel ) ;
4045
41- var derived = passkey . UseKey ( key => VaultParser . V4DeriveKeystore ( key , _keystoreDataModel ) ) ;
46+ var authenticationMethod = AuthenticationMethod . FromString ( _configDataModel . AuthenticationMethod ) ;
47+ var derived = string . IsNullOrWhiteSpace ( authenticationMethod . Complementation )
48+ ? passkey . UseKey ( key => VaultParser . V4DeriveKeystore ( key , _keystoreDataModel ) )
49+ : DeriveComplementedKeystore ( passkey , authenticationMethod ) ;
50+
4251 _dekKey = SecureKey . TakeOwnership ( derived . dekKey ) ;
4352 _macKey = SecureKey . TakeOwnership ( derived . macKey ) ;
4453 }
4554
55+ private ( byte [ ] dekKey , byte [ ] macKey ) DeriveComplementedKeystore ( IKeyUsage passkey , AuthenticationMethod authenticationMethod )
56+ {
57+ ArgumentNullException . ThrowIfNull ( _configDataModel ) ;
58+ ArgumentNullException . ThrowIfNull ( _keystoreDataModel ) ;
59+
60+ Exception ? lastException = null ;
61+ var primaryMethodId = authenticationMethod . Methods . FirstOrDefault ( ) ?? throw new InvalidOperationException ( "Primary authentication is missing." ) ;
62+
63+ try
64+ {
65+ return passkey . UseKey ( key =>
66+ {
67+ Span < byte > complementSecret = stackalloc byte [ 32 ] ;
68+ try
69+ {
70+ VaultParser . V4DeriveComplementKey ( key , _configDataModel . Uid , primaryMethodId , complementSecret ) ;
71+ return VaultParser . V4DeriveKeystore ( complementSecret , _keystoreDataModel ) ;
72+ }
73+ finally
74+ {
75+ CryptographicOperations . ZeroMemory ( complementSecret ) ;
76+ }
77+ } ) ;
78+ }
79+ catch ( CryptographicException ex )
80+ {
81+ lastException = ex ;
82+ }
83+
84+ foreach ( var share in _sharesDataModel ? . Shares ?? [ ] )
85+ {
86+ if ( share . WrappedComplementSecret is null
87+ || share . Tag is null
88+ || share . Nonce is null
89+ || share . AuthenticationMethodId is null )
90+ continue ;
91+
92+ if ( ! string . Equals ( share . AuthenticationMethodId , authenticationMethod . Complementation , StringComparison . Ordinal ) )
93+ continue ;
94+
95+ byte [ ] ? complementSecret = null ;
96+ try
97+ {
98+ complementSecret = passkey . UseKey ( key => VaultParser . V4UnwrapComplementSecret ( key , _configDataModel . Uid , share ) ) ;
99+ return VaultParser . V4DeriveKeystore ( complementSecret , _keystoreDataModel ) ;
100+ }
101+ catch ( CryptographicException ex )
102+ {
103+ lastException = ex ;
104+ }
105+ finally
106+ {
107+ if ( complementSecret is not null )
108+ CryptographicOperations . ZeroMemory ( complementSecret ) ;
109+ }
110+ }
111+
112+ throw lastException ?? new CryptographicException ( "The complemented credentials could not unlock this vault." ) ;
113+ }
114+
46115 /// <inheritdoc/>
47116 public async Task < IDisposable > FinalizeAsync ( CancellationToken cancellationToken )
48117 {
0 commit comments