From 46b3575d158b7c2adcd8cfcd702cf0087fa7fcc0 Mon Sep 17 00:00:00 2001 From: "kenneth.skovhede@gmail.com" Date: Sun, 12 Oct 2008 22:34:24 +0000 Subject: [PATCH] Added basic cryptography support. git-svn-id: https://duplicati.googlecode.com/svn/trunk@52 59da171f-624f-0410-aa54-27559c288bec --- Duplicati/Encryption/AESEncryption.cs | 81 +++++++++++++ .../Encryption/Duplicati.Encryption.csproj | 56 +++++++++ Duplicati/Encryption/EncryptionBase.cs | 34 ++++++ Duplicati/Encryption/GPGEncryption.cs | 111 ++++++++++++++++++ Duplicati/Encryption/IEncryption.cs | 41 +++++++ .../Encryption/Properties/AssemblyInfo.cs | 35 ++++++ Duplicati/Main/RSyncDir.cs | 2 - 7 files changed, 358 insertions(+), 2 deletions(-) create mode 100644 Duplicati/Encryption/AESEncryption.cs create mode 100644 Duplicati/Encryption/Duplicati.Encryption.csproj create mode 100644 Duplicati/Encryption/EncryptionBase.cs create mode 100644 Duplicati/Encryption/GPGEncryption.cs create mode 100644 Duplicati/Encryption/IEncryption.cs create mode 100644 Duplicati/Encryption/Properties/AssemblyInfo.cs diff --git a/Duplicati/Encryption/AESEncryption.cs b/Duplicati/Encryption/AESEncryption.cs new file mode 100644 index 000000000..0e993e057 --- /dev/null +++ b/Duplicati/Encryption/AESEncryption.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Duplicati.Encryption +{ + /// + /// Implements AES encryption + /// + public class AESEncryption : EncryptionBase, IEncryption + { + /// + /// The AES instance + /// + private System.Security.Cryptography.Rijndael m_instance; + + /// + /// Constructs a new AES encryption/decyption instance + /// + /// The key used for encryption. The key gets stretched through SHA hashing to fit the key size requirements + public AESEncryption(string key) + { + m_instance = System.Security.Cryptography.Rijndael.Create(); + System.Security.Cryptography.SHA256 sha = System.Security.Cryptography.SHA256.Create(); + int len = m_instance.IV.Length + m_instance.Key.Length; + System.IO.MemoryStream ms = new System.IO.MemoryStream(); + + //We stretch the key material by issuing cascading hash operations. + //This somewhat alters the characteristics of the key, but as long + //as the hashing is cryptographically safe and does not induce aliasing + //with the encryption, it does not reduce the strength of the encryption + byte[] tmp = System.Text.Encoding.UTF8.GetBytes(key); + while (ms.Length < len) + { + if (!sha.CanReuseTransform) + sha = System.Security.Cryptography.SHA256.Create(); + sha.Initialize(); + + tmp = sha.ComputeHash(tmp); + ms.Write(tmp, 0, (int)Math.Min(tmp.Length, len - ms.Length)); + } + + byte[] realkey = new byte[m_instance.Key.Length]; + byte[] iv = new byte[m_instance.IV.Length]; + ms.Position = 0; + if (ms.Read(iv, 0, iv.Length) != iv.Length) + throw new Exception("Bad key stretch"); + if (ms.Read(realkey, 0, realkey.Length) != realkey.Length) + throw new Exception("Bad key stretch"); + ms.Dispose(); + + m_instance.IV = iv; + m_instance.Key = realkey; + m_instance.Mode = System.Security.Cryptography.CipherMode.CBC; + } + + #region IEncryption Members + + public override void Encrypt(System.IO.Stream input, System.IO.Stream output) + { + if (m_instance.Mode == System.Security.Cryptography.CipherMode.ECB) + throw new Exception("Useless encryption detected"); + + System.Security.Cryptography.ICryptoTransform ct = m_instance.CreateEncryptor(); + using (System.Security.Cryptography.CryptoStream cs = new System.Security.Cryptography.CryptoStream(input, ct, System.Security.Cryptography.CryptoStreamMode.Write)) + Core.Utility.CopyStream(cs, output); + } + + public override void Decrypt(System.IO.Stream input, System.IO.Stream output) + { + if (m_instance.Mode == System.Security.Cryptography.CipherMode.ECB) + throw new Exception("Useless encryption detected"); + + System.Security.Cryptography.ICryptoTransform ct = m_instance.CreateDecryptor(); + using (System.Security.Cryptography.CryptoStream cs = new System.Security.Cryptography.CryptoStream(input, ct, System.Security.Cryptography.CryptoStreamMode.Read)) + Core.Utility.CopyStream(cs, output); + } + + #endregion + } +} diff --git a/Duplicati/Encryption/Duplicati.Encryption.csproj b/Duplicati/Encryption/Duplicati.Encryption.csproj new file mode 100644 index 000000000..3fee9ef5d --- /dev/null +++ b/Duplicati/Encryption/Duplicati.Encryption.csproj @@ -0,0 +1,56 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {94484FDB-2EFA-4CF0-9BE6-A561157B4F87} + Library + Properties + Duplicati.Encryption + Duplicati.Encryption + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65} + Duplicati.Core + + + + + \ No newline at end of file diff --git a/Duplicati/Encryption/EncryptionBase.cs b/Duplicati/Encryption/EncryptionBase.cs new file mode 100644 index 000000000..70a11275a --- /dev/null +++ b/Duplicati/Encryption/EncryptionBase.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Duplicati.Encryption +{ + /// + /// Simple helper class that implements the filebased functions, and wraps them onto the stream based ones + /// + public abstract class EncryptionBase : IEncryption + { + #region IEncryption Members + + public virtual void Encrypt(string inputfile, string outputfile) + { + using (System.IO.FileStream fs1 = System.IO.File.OpenRead(inputfile)) + using (System.IO.FileStream fs2 = System.IO.File.Create(outputfile)) + this.Encrypt(fs1, fs2); + } + + public abstract void Encrypt(System.IO.Stream input, System.IO.Stream output); + + public virtual void Decrypt(string inputfile, string outputfile) + { + using (System.IO.FileStream fs1 = System.IO.File.OpenRead(inputfile)) + using (System.IO.FileStream fs2 = System.IO.File.Create(outputfile)) + this.Decrypt(fs1, fs2); + } + + public abstract void Decrypt(System.IO.Stream input, System.IO.Stream output); + + #endregion + } +} diff --git a/Duplicati/Encryption/GPGEncryption.cs b/Duplicati/Encryption/GPGEncryption.cs new file mode 100644 index 000000000..5fac85aed --- /dev/null +++ b/Duplicati/Encryption/GPGEncryption.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace Duplicati.Encryption +{ + /// + /// Implements a encryption/decryption with the GNU Privacy Guard (GPG) + /// + public class GPGEncryption : EncryptionBase, IEncryption + { + /// + /// The PGP program to use, should be with absolute path + /// + public static string PGP_PROGRAM = "pgp"; + + /// + /// Always present commandline args + /// + private const string COMMANDLINE_ARGS = "--symmetric --armor"; + + /// + /// Commandline switches for encryption + /// + private const string ENCRYPTION_ARGS = "--encrypt"; + + /// + /// Commandline switches for decryption + /// + private const string DECRYPTION_ARGS = "--decrypt"; + + /// + /// The key used for cryptographic operations + /// + private string m_key; + + /// + /// An optional key, used for signature verification + /// + private string m_signaturekey; + + /// + /// Constructs a new instance of the PGP encryption/decryption class. + /// + /// The key used to encrypt/decrypt data + /// The key used to generate a signature with (currently unused) + public GPGEncryption(string key, string signaturekey) + { + m_key = key; + m_signaturekey = signaturekey; + } + + #region IEncryption Members + + public override void Encrypt(System.IO.Stream input, System.IO.Stream output) + { + this.Execute(COMMANDLINE_ARGS + " " + ENCRYPTION_ARGS, input, output); + } + + public override void Decrypt(System.IO.Stream input, System.IO.Stream output) + { + this.Execute(COMMANDLINE_ARGS + " " + DECRYPTION_ARGS, input, output); + } + + /// + /// Internal helper that wraps GPG usage + /// + /// The commandline arguments + /// The input stream + /// The output stream + private void Execute(string args, System.IO.Stream input, System.IO.Stream output) + { + System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(); + psi.Arguments = args; + psi.CreateNoWindow = true; + psi.FileName = PGP_PROGRAM; + psi.RedirectStandardError = true; + psi.RedirectStandardInput = true; + psi.RedirectStandardOutput = true; + psi.UseShellExecute = false; + psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; + + System.Diagnostics.Process p = System.Diagnostics.Process.Start(psi); + p.StandardInput.WriteLine(m_key); + + //Prevent blocking of the output buffer + System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(Runner)); + t.Start(new object[] {p.StandardOutput.BaseStream, output}); + + //Copy input stream to GPG + Core.Utility.CopyStream(input, p.StandardInput.BaseStream); + p.StandardInput.Close(); + + if (!t.Join(5000)) + throw new Exception("Failure while invoking GnuPG, program won't flush output"); + + if (!p.WaitForExit(5000)) + throw new Exception("Failure while invoking GnuPG, program won't terminate"); + } + + private void Runner(object x) + { + //Unwrap arguments and read stream + object[] tmp = (object[])x; + Core.Utility.CopyStream((Stream)tmp[0], (Stream)tmp[1]); + } + + #endregion + } +} diff --git a/Duplicati/Encryption/IEncryption.cs b/Duplicati/Encryption/IEncryption.cs new file mode 100644 index 000000000..0ca5893a3 --- /dev/null +++ b/Duplicati/Encryption/IEncryption.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace Duplicati.Encryption +{ + /// + /// Public interface for an encryption method + /// + public interface IEncryption + { + /// + /// Encrypts the contents of the inputfile, and saves the result as the outputfile. + /// + /// The file to encrypt + /// The encrypted file + void Encrypt(string inputfile, string outputfile); + + /// + /// Encrypts the contents of the input stream, and writes the result to the output stream. + /// + /// The stream to encrypt + /// The encrypted stream + void Encrypt(Stream input, Stream output); + + /// + /// Decrypts the contents of the input file and saves the result as the outputfile + /// + /// The file to decrypt + /// The decrypted output file + void Decrypt(string inputfile, string outputfile); + + /// + /// Dencrypts the contents of the input stream, and writes the result to the output stream. + /// + /// The stream to decrypt + /// The decrypted stream + void Decrypt(Stream input, Stream output); + } +} diff --git a/Duplicati/Encryption/Properties/AssemblyInfo.cs b/Duplicati/Encryption/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..d99f1bb9f --- /dev/null +++ b/Duplicati/Encryption/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Duplicati.Encryption")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("HexaD")] +[assembly: AssemblyProduct("Duplicati.Encryption")] +[assembly: AssemblyCopyright("Copyright © HexaD 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ea360f7d-b721-452e-8a7d-60da8ad1094f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Duplicati/Main/RSyncDir.cs b/Duplicati/Main/RSyncDir.cs index bd04d5ecf..05b5c7d08 100644 --- a/Duplicati/Main/RSyncDir.cs +++ b/Duplicati/Main/RSyncDir.cs @@ -174,10 +174,8 @@ namespace Duplicati.Main.RSync string s = m_unproccesed[next]; m_unproccesed.RemoveAt(next); - //TODO: Add the delta to zip, don't use temp file if (ProccessDiff(s, sigfile)) totalSize = AddFileToCompression(s, c); - } if (m_unproccesed.Count == 0)