Made the BackendManager.GetOperations' decryption methods static and public so the restores VolumeDecryptor can use the same code.

This commit is contained in:
Carl Johnsen
2025-02-19 16:09:32 +01:00
parent e2e930b2d7
commit 2c06820281
5 changed files with 1229 additions and 1280 deletions
@@ -64,11 +64,6 @@ partial class BackendManager
{
TempFile? tmpfile = null;
Context.Statwriter.SendEvent(BackendActionType.Get, BackendEventType.Started, RemoteFilename, Size);
using var encryption = Context.Options.NoEncryption
? null
: (DynamicLoader.EncryptionLoader.GetModule(Context.Options.EncryptionModule, Context.Options.Passphrase, Context.Options.RawOptions)
?? throw new Exception(Strings.BackendMananger.EncryptionModuleNotFound(Context.Options.EncryptionModule))
);
try
{
@@ -94,7 +89,7 @@ partial class BackendManager
}
// Perform decryption after hash validation, if needed
tmpfile = DecryptFile(tmpfile, DetectEncryptionModule(encryption));
tmpfile = DecryptFile(tmpfile, RemoteFilename, Context.Options);
return (tmpfile, fileHash, dataSizeDownloaded);
}
@@ -161,40 +156,40 @@ partial class BackendManager
/// </summary>
/// <param name="encryption">The default encryption module</param>
/// <returns>The encryption module to use</returns>
private IEncryption? DetectEncryptionModule(IEncryption? encryption)
private static IEncryption? DetectEncryptionModule(string filename, Options options, IEncryption? encryption)
{
try
{
// Auto-guess the encryption module
var ext = (System.IO.Path.GetExtension(RemoteFilename) ?? "").TrimStart('.');
var ext = (System.IO.Path.GetExtension(filename) ?? "").TrimStart('.');
if (!ext.Equals(encryption?.FilenameExtension, StringComparison.OrdinalIgnoreCase))
{
// Check if the file is not encrypted
if (DynamicLoader.CompressionLoader.Keys.Contains(ext, StringComparer.OrdinalIgnoreCase))
{
if (encryption != null)
Logging.Log.WriteVerboseMessage(LOGTAG, "AutomaticDecryptionDetection", "Filename extension \"{0}\" does not match encryption module \"{1}\", guessing that it is not encrypted", ext, Context.Options.EncryptionModule);
Logging.Log.WriteVerboseMessage(LOGTAG, "AutomaticDecryptionDetection", "Filename extension \"{0}\" does not match encryption module \"{1}\", guessing that it is not encrypted", ext, options.EncryptionModule);
return null;
}
// Check if the file is encrypted with something else
else if (DynamicLoader.EncryptionLoader.Keys.Contains(ext, StringComparer.OrdinalIgnoreCase))
{
Logging.Log.WriteVerboseMessage(LOGTAG, "AutomaticDecryptionDetection", "Filename extension \"{0}\" does not match encryption module \"{1}\", attempting to use matching encryption module", ext, Context.Options.EncryptionModule);
Logging.Log.WriteVerboseMessage(LOGTAG, "AutomaticDecryptionDetection", "Filename extension \"{0}\" does not match encryption module \"{1}\", attempting to use matching encryption module", ext, options.EncryptionModule);
try
{
return DynamicLoader.EncryptionLoader.GetModule(ext, Context.Options.Passphrase, Context.Options.RawOptions)
return DynamicLoader.EncryptionLoader.GetModule(ext, options.Passphrase, options.RawOptions)
?? encryption;
}
catch (Exception ex)
{
Logging.Log.WriteWarningMessage(LOGTAG, "AutomaticDecryptionDetection", ex, "Failed to load encryption module \"{0}\", using specified encryption module \"{1}\"", ext, Context.Options.EncryptionModule);
Logging.Log.WriteWarningMessage(LOGTAG, "AutomaticDecryptionDetection", ex, "Failed to load encryption module \"{0}\", using specified encryption module \"{1}\"", ext, options.EncryptionModule);
}
}
// Fallback, lets see what happens...
else
{
Logging.Log.WriteVerboseMessage(LOGTAG, "AutomaticDecryptionDetection", "Filename extension \"{0}\" does not match encryption module \"{1}\", attempting to use specified encryption module as no others match", ext, Context.Options.EncryptionModule);
Logging.Log.WriteVerboseMessage(LOGTAG, "AutomaticDecryptionDetection", "Filename extension \"{0}\" does not match encryption module \"{1}\", attempting to use specified encryption module as no others match", ext, options.EncryptionModule);
}
}
@@ -213,7 +208,7 @@ partial class BackendManager
/// <param name="tempFile">The encrypted file</param>
/// <param name="decrypter">Then encryption module to use, or <c>null</c> for no encryption</param>
/// <returns>The decrypted file</returns>
private TempFile DecryptFile(TempFile tempFile, IEncryption? decrypter)
private static TempFile DecryptFile(TempFile tempFile, IEncryption? decrypter)
{
// Support no encryption
if (decrypter == null)
@@ -244,5 +239,22 @@ partial class BackendManager
}
}
}
/// <summary>
/// Decrypts a file using the specified options
/// </summary>
/// <param name="tmpfile">The file to decrypt</param>
/// <param name="filename">The name of the file. Used for detecting encryption algorithm if not specified in options or if it differs from the options</param>
/// <param name="options">The Duplicati options</param>
/// <returns>The decrypted file</returns>
public static TempFile DecryptFile(TempFile tmpfile, string filename, Options options)
{
using var encryption = options.NoEncryption
? null
: (DynamicLoader.EncryptionLoader.GetModule(options.EncryptionModule, options.Passphrase, options.RawOptions)
?? throw new Exception(Strings.BackendMananger.EncryptionModuleNotFound(options.EncryptionModule))
);
return DecryptFile(tmpfile, DetectEncryptionModule(filename, options, encryption));
}
}
}
@@ -139,6 +139,18 @@ internal partial class BackendManager : IBackendManager
return Convert.ToBase64String(hasher.ComputeHash(fs));
}
/// <summary>
/// Decrypts a file using the specified options
/// </summary>
/// <param name="tmpfile">The file to decrypt</param>
/// <param name="filename">The name of the file. Used for detecting encryption algorithm if not specified in options or if it differs from the options</param>
/// <param name="options">The Duplicati options</param>
/// <returns>The decrypted file</returns>
public TempFile DecryptFile(TempFile volume, string volume_name, Options options)
{
return GetOperation.DecryptFile(volume, volume_name, options);
}
/// <summary>
/// Deletes a remote file
/// </summary>
@@ -54,6 +54,15 @@ internal interface IBackendManager : IDisposable
/// <returns>An enumerable of file entries</returns>
Task<IEnumerable<IFileEntry>> ListAsync(CancellationToken cancelToken);
/// <summary>
/// Decrypts the given file and returns the decrypted file
/// </summary>
/// <param name="volume">The file to decrypt</param>
/// <param name="volume_name">The name of the file. Used for detecting encryption algorithm if not specified in options or if it differs from the options</param>
/// <param name="options">The Duplicati options</param>
/// <returns>The decrypted file</returns>
TempFile DecryptFile(TempFile volume, string volume_name, Options options);
/// <summary>
/// Deletes a file on the backend
/// </summary>
@@ -43,7 +43,7 @@ namespace Duplicati.Library.Main.Operation.Restore
/// Runs the volume decryptor process.
/// </summary>
/// <param name="options">The restore options.</param>
public static Task Run(Channels channels, Options options)
public static Task Run(Channels channels, IBackendManager backend, Options options)
{
return AutomationExtensions.RunTask(
new
@@ -58,13 +58,6 @@ namespace Duplicati.Library.Main.Operation.Restore
Stopwatch sw_decrypt = options.InternalProfiling ? new() : null;
try
{
// Taken from BackendManager.GetOperation
using var encryption = options.NoEncryption
? null
: (DynamicLoader.EncryptionLoader.GetModule(options.EncryptionModule, options.Passphrase, options.RawOptions)
?? throw new Exception(Strings.BackendMananger.EncryptionModuleNotFound(options.EncryptionModule))
);
while (true)
{
// Get the block request and volume from the `VolumeDownloader` process.
@@ -73,7 +66,7 @@ namespace Duplicati.Library.Main.Operation.Restore
sw_read?.Stop();
sw_decrypt?.Start();
var tmpfile = DecryptFile(volume, DetectEncryptionModule(volume_name, options, encryption));
var tmpfile = backend.DecryptFile(volume, volume_name, options);
var bvr = new BlockVolumeReader(options.CompressionModule, tmpfile, options);
sw_decrypt?.Stop();
@@ -99,83 +92,6 @@ namespace Duplicati.Library.Main.Operation.Restore
}
});
}
// Taken from BackendManager.GetOperation
public static Library.Utility.TempFile DecryptFile(Library.Utility.TempFile tempFile, Interface.IEncryption? decrypter)
{
// Support no encryption
if (decrypter == null)
return tempFile;
Library.Utility.TempFile? decryptTarget = null;
// Always dispose the source file
using (tempFile)
using (new Logging.Timer(LOGTAG, "DecryptFile", "Decrypting " + tempFile))
{
try
{
decryptTarget = new Library.Utility.TempFile();
try { decrypter.Decrypt(tempFile, decryptTarget); }
// If we fail here, make sure that we throw a crypto exception
catch (System.Security.Cryptography.CryptographicException) { throw; }
catch (Exception ex) { throw new System.Security.Cryptography.CryptographicException(ex.Message, ex); }
var result = decryptTarget;
decryptTarget = null;
return result;
}
finally
{
// Remove temp files on failure
decryptTarget?.Dispose();
}
}
}
private static Interface.IEncryption? DetectEncryptionModule(string remotefilename, Options options, Interface.IEncryption? encryption)
{
try
{
// Auto-guess the encryption module
var ext = (System.IO.Path.GetExtension(remotefilename) ?? "").TrimStart('.');
if (!ext.Equals(encryption?.FilenameExtension, StringComparison.OrdinalIgnoreCase))
{
// Check if the file is not encrypted
if (DynamicLoader.CompressionLoader.Keys.Contains(ext, StringComparer.OrdinalIgnoreCase))
{
if (encryption != null)
Logging.Log.WriteVerboseMessage(LOGTAG, "AutomaticDecryptionDetection", "Filename extension \"{0}\" does not match encryption module \"{1}\", guessing that it is not encrypted", ext, options.EncryptionModule);
return null;
}
// Check if the file is encrypted with something else
else if (DynamicLoader.EncryptionLoader.Keys.Contains(ext, StringComparer.OrdinalIgnoreCase))
{
Logging.Log.WriteVerboseMessage(LOGTAG, "AutomaticDecryptionDetection", "Filename extension \"{0}\" does not match encryption module \"{1}\", attempting to use matching encryption module", ext, options.EncryptionModule);
try
{
return DynamicLoader.EncryptionLoader.GetModule(ext, options.Passphrase, options.RawOptions)
?? encryption;
}
catch (Exception ex)
{
Logging.Log.WriteWarningMessage(LOGTAG, "AutomaticDecryptionDetection", ex, "Failed to load encryption module \"{0}\", using specified encryption module \"{1}\"", ext, options.EncryptionModule);
}
}
// Fallback, lets see what happens...
else
{
Logging.Log.WriteVerboseMessage(LOGTAG, "AutomaticDecryptionDetection", "Filename extension \"{0}\" does not match encryption module \"{1}\", attempting to use specified encryption module as no others match", ext, options.EncryptionModule);
}
}
return encryption;
}
// If we fail here, make sure that we throw a crypto exception
catch (System.Security.Cryptography.CryptographicException) { throw; }
catch (Exception ex) { throw new System.Security.Cryptography.CryptographicException(ex.Message, ex); }
}
}
}
File diff suppressed because it is too large Load Diff