From 6d401fbd8df2d150a5e1a259b1fb82e1033cf7a8 Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Wed, 20 Sep 2017 09:58:43 +0200 Subject: [PATCH] Changed all hash algorithms to use the `Cng` version. This fixes #2621 --- .../CommandLine/RecoveryTool/Recompress.cs | 2 +- Duplicati/CommandLine/RecoveryTool/Restore.cs | 4 +- Duplicati/Library/Backend/Backblaze/B2.cs | 2 +- Duplicati/Library/Main/BackendManager.cs | 8 +-- .../Main/Database/LocalRepairDatabase.cs | 2 +- .../Library/Main/Operation/BackupHandler.cs | 6 +- .../Main/Operation/RecreateDatabaseHandler.cs | 2 +- .../Library/Main/Operation/RepairHandler.cs | 4 +- .../Library/Main/Operation/RestoreHandler.cs | 10 +-- .../Library/Main/Operation/TestHandler.cs | 2 +- Duplicati/Library/Main/Options.cs | 2 +- Duplicati/Library/Main/Utility.cs | 2 +- .../Utility/Duplicati.Library.Utility.csproj | 1 + .../Library/Utility/HashAlgorithmHelper.cs | 64 +++++++++++++++++++ .../Library/Utility/HashCalculatingStream.cs | 2 +- Duplicati/Library/Utility/Utility.cs | 2 +- 16 files changed, 90 insertions(+), 25 deletions(-) create mode 100644 Duplicati/Library/Utility/HashAlgorithmHelper.cs diff --git a/Duplicati/CommandLine/RecoveryTool/Recompress.cs b/Duplicati/CommandLine/RecoveryTool/Recompress.cs index f96271fcb..95a5eab42 100644 --- a/Duplicati/CommandLine/RecoveryTool/Recompress.cs +++ b/Duplicati/CommandLine/RecoveryTool/Recompress.cs @@ -204,7 +204,7 @@ namespace Duplicati.CommandLine.RecoveryTool textJSON = sourceStreamReader.ReadToEnd(); JToken token = JObject.Parse(textJSON); var fileInfoBlocks = new FileInfo(Path.Combine(targetfolder, cmfileNew.Replace("vol/", ""))); - var filehasher = System.Security.Cryptography.HashAlgorithm.Create(m_Options.FileHashAlgorithm); + var filehasher = HashAlgorithmHelper.Create(m_Options.FileHashAlgorithm); using (var fileStream = fileInfoBlocks.Open(FileMode.Open)) { diff --git a/Duplicati/CommandLine/RecoveryTool/Restore.cs b/Duplicati/CommandLine/RecoveryTool/Restore.cs index 2b5456585..8a357c941 100644 --- a/Duplicati/CommandLine/RecoveryTool/Restore.cs +++ b/Duplicati/CommandLine/RecoveryTool/Restore.cs @@ -90,8 +90,8 @@ namespace Duplicati.CommandLine.RecoveryTool return 100; } - var blockhasher = string.IsNullOrWhiteSpace(blockhash_str) ? null : System.Security.Cryptography.HashAlgorithm.Create(blockhash_str); - var filehasher = string.IsNullOrWhiteSpace(filehash_str) ? null : System.Security.Cryptography.HashAlgorithm.Create(filehash_str); + var blockhasher = string.IsNullOrWhiteSpace(blockhash_str) ? null : Library.Utility.HashAlgorithmHelper.Create(blockhash_str); + var filehasher = string.IsNullOrWhiteSpace(filehash_str) ? null : Library.Utility.HashAlgorithmHelper.Create(filehash_str); if (blockhasher == null) throw new Duplicati.Library.Interface.UserInformationException(string.Format("Block hash algorithm not valid: {0}", blockhash_str)); diff --git a/Duplicati/Library/Backend/Backblaze/B2.cs b/Duplicati/Library/Backend/Backblaze/B2.cs index 82753f029..383e2e009 100644 --- a/Duplicati/Library/Backend/Backblaze/B2.cs +++ b/Duplicati/Library/Backend/Backblaze/B2.cs @@ -177,7 +177,7 @@ namespace Duplicati.Library.Backend.Backblaze var p = measure.Position; // Compute the hash - using(var hashalg = System.Security.Cryptography.HashAlgorithm.Create("sha1")) + using(var hashalg = Duplicati.Library.Utility.HashAlgorithmHelper.Create("sha1")) sha1 = Library.Utility.Utility.ByteArrayAsHexString(hashalg.ComputeHash(measure)); // Reset the stream position diff --git a/Duplicati/Library/Main/BackendManager.cs b/Duplicati/Library/Main/BackendManager.cs index 19065964d..8e56ac660 100644 --- a/Duplicati/Library/Main/BackendManager.cs +++ b/Duplicati/Library/Main/BackendManager.cs @@ -451,14 +451,14 @@ namespace Duplicati.Library.Main public static string CalculateFileHash(string filename) { using (System.IO.FileStream fs = System.IO.File.OpenRead(filename)) - using (var hasher = System.Security.Cryptography.HashAlgorithm.Create(VOLUME_HASH)) + using (var hasher = HashAlgorithmHelper.Create(VOLUME_HASH)) return Convert.ToBase64String(hasher.ComputeHash(fs)); } /// Calculate file hash directly on stream object (for piping) public static string CalculateFileHash(System.IO.Stream stream) { - using (var hasher = System.Security.Cryptography.HashAlgorithm.Create(VOLUME_HASH)) + using (var hasher = HashAlgorithmHelper.Create(VOLUME_HASH)) return Convert.ToBase64String(hasher.ComputeHash(stream)); } @@ -469,7 +469,7 @@ namespace Duplicati.Library.Main public static System.Security.Cryptography.CryptoStream GetFileHasherStream (System.IO.Stream stream, System.Security.Cryptography.CryptoStreamMode mode, out Func getHash) { - var hasher = System.Security.Cryptography.HashAlgorithm.Create(VOLUME_HASH); + var hasher = HashAlgorithmHelper.Create(VOLUME_HASH); System.Security.Cryptography.CryptoStream retHasherStream = new System.Security.Cryptography.CryptoStream(stream, hasher, mode); getHash = () => @@ -678,7 +678,7 @@ namespace Duplicati.Library.Main IndexVolumeWriter wr = null; try { - var hashsize = System.Security.Cryptography.HashAlgorithm.Create(m_options.BlockHashAlgorithm).HashSize / 8; + var hashsize = HashAlgorithmHelper.Create(m_options.BlockHashAlgorithm).HashSize / 8; wr = new IndexVolumeWriter(m_options); using(var rd = new IndexVolumeReader(p.CompressionModule, item.Indexfile.Item2.LocalFilename, m_options, hashsize)) wr.CopyFrom(rd, x => x == oldname ? newname : x); diff --git a/Duplicati/Library/Main/Database/LocalRepairDatabase.cs b/Duplicati/Library/Main/Database/LocalRepairDatabase.cs index 021731496..fa6458a00 100644 --- a/Duplicati/Library/Main/Database/LocalRepairDatabase.cs +++ b/Duplicati/Library/Main/Database/LocalRepairDatabase.cs @@ -334,7 +334,7 @@ namespace Duplicati.Library.Main.Database public void FixMissingBlocklistHashes(string blockhashalgorithm, long blocksize) { - var blockhasher = System.Security.Cryptography.HashAlgorithm.Create(blockhashalgorithm); + var blockhasher = Library.Utility.HashAlgorithmHelper.Create(blockhashalgorithm); var hashsize = blockhasher.HashSize / 8; var blocklistbuffer = new byte[blocksize]; int blocklistoffset = 0; diff --git a/Duplicati/Library/Main/Operation/BackupHandler.cs b/Duplicati/Library/Main/Operation/BackupHandler.cs index 403e53aca..1766d8094 100644 --- a/Duplicati/Library/Main/Operation/BackupHandler.cs +++ b/Duplicati/Library/Main/Operation/BackupHandler.cs @@ -378,7 +378,7 @@ namespace Duplicati.Library.Main.Operation { if (m_options.IndexfilePolicy != Options.IndexFileStrategy.None) { - var blockhasher = System.Security.Cryptography.HashAlgorithm.Create(m_options.BlockHashAlgorithm); + var blockhasher = Library.Utility.HashAlgorithmHelper.Create(m_options.BlockHashAlgorithm); var hashsize = blockhasher.HashSize / 8; foreach (var blockfile in m_database.GetMissingIndexFiles()) @@ -696,8 +696,8 @@ namespace Duplicati.Library.Main.Operation m_blockbuffer = new byte[m_options.Blocksize * Math.Max(1, m_options.FileReadBufferSize / m_options.Blocksize)]; m_blocklistbuffer = new byte[m_options.Blocksize]; - m_blockhasher = System.Security.Cryptography.HashAlgorithm.Create(m_options.BlockHashAlgorithm); - m_filehasher = System.Security.Cryptography.HashAlgorithm.Create(m_options.FileHashAlgorithm); + m_blockhasher = Library.Utility.HashAlgorithmHelper.Create(m_options.BlockHashAlgorithm); + m_filehasher = Library.Utility.HashAlgorithmHelper.Create(m_options.FileHashAlgorithm); if (m_blockhasher == null) throw new UserInformationException(Strings.Common.InvalidHashAlgorithm(m_options.BlockHashAlgorithm)); diff --git a/Duplicati/Library/Main/Operation/RecreateDatabaseHandler.cs b/Duplicati/Library/Main/Operation/RecreateDatabaseHandler.cs index bf1941c38..0bee788da 100644 --- a/Duplicati/Library/Main/Operation/RecreateDatabaseHandler.cs +++ b/Duplicati/Library/Main/Operation/RecreateDatabaseHandler.cs @@ -299,7 +299,7 @@ namespace Duplicati.Library.Main.Operation if (!m_options.RepairOnlyPaths) { - var hashalg = System.Security.Cryptography.HashAlgorithm.Create(m_options.BlockHashAlgorithm); + var hashalg = Library.Utility.HashAlgorithmHelper.Create(m_options.BlockHashAlgorithm); if (hashalg == null) throw new UserInformationException(Strings.Common.InvalidHashAlgorithm(m_options.BlockHashAlgorithm)); var hashsize = hashalg.HashSize / 8; diff --git a/Duplicati/Library/Main/Operation/RepairHandler.cs b/Duplicati/Library/Main/Operation/RepairHandler.cs index bf7031b73..27b8bd564 100644 --- a/Duplicati/Library/Main/Operation/RepairHandler.cs +++ b/Duplicati/Library/Main/Operation/RepairHandler.cs @@ -113,7 +113,7 @@ namespace Duplicati.Library.Main.Operation var tp = FilelistProcessor.RemoteListAnalysis(backend, m_options, db, m_result.BackendWriter, null); var buffer = new byte[m_options.Blocksize]; - var blockhasher = System.Security.Cryptography.HashAlgorithm.Create(m_options.BlockHashAlgorithm); + var blockhasher = Library.Utility.HashAlgorithmHelper.Create(m_options.BlockHashAlgorithm); var hashsize = blockhasher.HashSize / 8; if (blockhasher == null) @@ -300,7 +300,7 @@ namespace Duplicati.Library.Main.Operation newEntry = w; w.SetRemoteFilename(n.Name); - var h = System.Security.Cryptography.HashAlgorithm.Create(m_options.BlockHashAlgorithm); + var h = Library.Utility.HashAlgorithmHelper.Create(m_options.BlockHashAlgorithm); foreach(var blockvolume in db.GetBlockVolumesFromIndexName(n.Name)) { diff --git a/Duplicati/Library/Main/Operation/RestoreHandler.cs b/Duplicati/Library/Main/Operation/RestoreHandler.cs index de1eaa740..1d02eb6ed 100644 --- a/Duplicati/Library/Main/Operation/RestoreHandler.cs +++ b/Duplicati/Library/Main/Operation/RestoreHandler.cs @@ -124,8 +124,8 @@ namespace Duplicati.Library.Main.Operation //Figure out what files are to be patched, and what blocks are needed PrepareBlockAndFileList(database, m_options, filter, m_result); - blockhasher = System.Security.Cryptography.HashAlgorithm.Create(m_options.BlockHashAlgorithm); - filehasher = System.Security.Cryptography.HashAlgorithm.Create(m_options.FileHashAlgorithm); + blockhasher = Library.Utility.HashAlgorithmHelper.Create(m_options.BlockHashAlgorithm); + filehasher = Library.Utility.HashAlgorithmHelper.Create(m_options.FileHashAlgorithm); if (blockhasher == null) throw new UserInformationException(Strings.Common.InvalidHashAlgorithm(m_options.BlockHashAlgorithm)); if (!blockhasher.CanReuseTransform) @@ -194,7 +194,7 @@ namespace Duplicati.Library.Main.Operation var blocksize = options.Blocksize; var updateCounter = 0L; var fullblockverification = options.FullBlockVerification; - var blockhasher = fullblockverification ? System.Security.Cryptography.HashAlgorithm.Create(options.BlockHashAlgorithm) : null; + var blockhasher = fullblockverification ? Library.Utility.HashAlgorithmHelper.Create(options.BlockHashAlgorithm) : null; using (var blockmarker = database.CreateBlockMarker()) using(var volumekeeper = database.GetMissingBlockData(blocks, options.Blocksize)) @@ -345,8 +345,8 @@ namespace Duplicati.Library.Main.Operation Utility.VerifyParameters(database, m_options); m_blockbuffer = new byte[m_options.Blocksize]; - var blockhasher = System.Security.Cryptography.HashAlgorithm.Create(m_options.BlockHashAlgorithm); - var filehasher = System.Security.Cryptography.HashAlgorithm.Create(m_options.FileHashAlgorithm); + var blockhasher = Library.Utility.HashAlgorithmHelper.Create(m_options.BlockHashAlgorithm); + var filehasher = Library.Utility.HashAlgorithmHelper.Create(m_options.FileHashAlgorithm); if (blockhasher == null) throw new UserInformationException(Strings.Common.InvalidHashAlgorithm(m_options.BlockHashAlgorithm)); if (!blockhasher.CanReuseTransform) diff --git a/Duplicati/Library/Main/Operation/TestHandler.cs b/Duplicati/Library/Main/Operation/TestHandler.cs index f17b03757..3f7c41382 100644 --- a/Duplicati/Library/Main/Operation/TestHandler.cs +++ b/Duplicati/Library/Main/Operation/TestHandler.cs @@ -198,7 +198,7 @@ namespace Duplicati.Library.Main.Operation /// A value between 0 and 1 that indicates how many blocks are tested in a dblock file public static KeyValuePair>> TestVolumeInternals(LocalTestDatabase db, IRemoteVolume vol, string tf, Options options, ILogWriter log, double sample_percent) { - var blockhasher = System.Security.Cryptography.HashAlgorithm.Create(options.BlockHashAlgorithm); + var blockhasher = Library.Utility.HashAlgorithmHelper.Create(options.BlockHashAlgorithm); if (blockhasher == null) throw new UserInformationException(Strings.Common.InvalidHashAlgorithm(options.BlockHashAlgorithm)); diff --git a/Duplicati/Library/Main/Options.cs b/Duplicati/Library/Main/Options.cs index c21f0bab4..84dd8de5c 100644 --- a/Duplicati/Library/Main/Options.cs +++ b/Duplicati/Library/Main/Options.cs @@ -1372,7 +1372,7 @@ namespace Duplicati.Library.Main { get { - return System.Security.Cryptography.HashAlgorithm.Create(BlockHashAlgorithm).HashSize / 8; + return Duplicati.Library.Utility.HashAlgorithmHelper.Create(BlockHashAlgorithm).HashSize / 8; } } diff --git a/Duplicati/Library/Main/Utility.cs b/Duplicati/Library/Main/Utility.cs index 216fdf70d..5fd8584a5 100644 --- a/Duplicati/Library/Main/Utility.cs +++ b/Duplicati/Library/Main/Utility.cs @@ -50,7 +50,7 @@ namespace Duplicati.Library.Main using (var ms = new System.IO.MemoryStream()) using (var w = new StreamWriter(ms, Encoding.UTF8)) - using(var filehasher = System.Security.Cryptography.HashAlgorithm.Create(options.FileHashAlgorithm)) + using(var filehasher = Library.Utility.HashAlgorithmHelper.Create(options.FileHashAlgorithm)) { if (filehasher == null) throw new Duplicati.Library.Interface.UserInformationException(Strings.Common.InvalidHashAlgorithm(options.FileHashAlgorithm)); diff --git a/Duplicati/Library/Utility/Duplicati.Library.Utility.csproj b/Duplicati/Library/Utility/Duplicati.Library.Utility.csproj index f95a5afa7..6f1f6323c 100644 --- a/Duplicati/Library/Utility/Duplicati.Library.Utility.csproj +++ b/Duplicati/Library/Utility/Duplicati.Library.Utility.csproj @@ -72,6 +72,7 @@ + diff --git a/Duplicati/Library/Utility/HashAlgorithmHelper.cs b/Duplicati/Library/Utility/HashAlgorithmHelper.cs new file mode 100644 index 000000000..31d07e2f6 --- /dev/null +++ b/Duplicati/Library/Utility/HashAlgorithmHelper.cs @@ -0,0 +1,64 @@ +// Copyright (C) 2017, The Duplicati Team +// http://www.duplicati.com, info@duplicati.com +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +using System; +using System.Collections.Generic; +using System.Security.Cryptography; + +namespace Duplicati.Library.Utility +{ + /// + /// This class helps picking the fastest hash algorithm implementation, + /// which is what should do, but does not. + /// + public static class HashAlgorithmHelper + { + /// + /// Cache of known types + /// + private static readonly Dictionary _knownTypes = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Static initializer to pre-populate the type table + /// + static HashAlgorithmHelper() + { + _knownTypes["MD5"] = typeof(MD5Cng); + _knownTypes["SHA1"] = typeof(SHA1Cng); + _knownTypes["SHA256"] = typeof(SHA256Cng); + _knownTypes["SHA384"] = typeof(SHA384Cng); + _knownTypes["SHA512"] = typeof(SHA512Cng); + } + + /// + /// Create the hash algorithm with the specified name. + /// + /// The hash algorithm. + /// The name of the algorithm to create. + public static HashAlgorithm Create(string name) + { + Type known; + if (!_knownTypes.TryGetValue(name, out known)) + known = _knownTypes[name] = Type.GetType("System.Security.Cryptography." + name.ToUpperInvariant() + "Cng", false, true); + + if (known == null || known.GetConstructor(Type.EmptyTypes) == null) + return HashAlgorithm.Create(name); + else + return (HashAlgorithm)Activator.CreateInstance(known); + + } + } +} diff --git a/Duplicati/Library/Utility/HashCalculatingStream.cs b/Duplicati/Library/Utility/HashCalculatingStream.cs index 68ea4ab92..6ff39e23d 100644 --- a/Duplicati/Library/Utility/HashCalculatingStream.cs +++ b/Duplicati/Library/Utility/HashCalculatingStream.cs @@ -49,7 +49,7 @@ namespace Duplicati.Library.Utility private int m_hashbufferLength = 0; public HashCalculatingStream(System.IO.Stream basestream, string hashalgorithm) - : this(basestream, System.Security.Cryptography.HashAlgorithm.Create(hashalgorithm)) + : this(basestream, HashAlgorithmHelper.Create(hashalgorithm)) { } diff --git a/Duplicati/Library/Utility/Utility.cs b/Duplicati/Library/Utility/Utility.cs index d6813fe4c..7d205e7c4 100644 --- a/Duplicati/Library/Utility/Utility.cs +++ b/Duplicati/Library/Utility/Utility.cs @@ -551,7 +551,7 @@ namespace Duplicati.Library.Utility /// The base64 encoded hash public static string CalculateHash(System.IO.Stream stream) { - return Convert.ToBase64String(System.Security.Cryptography.HashAlgorithm.Create(HashAlgorithm).ComputeHash(stream)); + return Convert.ToBase64String(HashAlgorithmHelper.Create(HashAlgorithm).ComputeHash(stream)); } ///