diff --git a/SyncPro.Certificates/CertificateHelper.cs b/SyncPro.Certificates/CertificateHelper.cs
new file mode 100644
index 0000000..32c20ad
--- /dev/null
+++ b/SyncPro.Certificates/CertificateHelper.cs
@@ -0,0 +1,83 @@
+namespace SyncPro.Certificates
+{
+ using System;
+ using System.Security.Cryptography.X509Certificates;
+
+ using CERTENROLLLib;
+
+ public static class CertificateHelper
+ {
+ public static X509Certificate2 CreateSelfSignedCertificate(string subjectName)
+ {
+ var distinguishedName = new CX500DistinguishedName();
+ distinguishedName.Encode(
+ "CN=" + subjectName,
+ X500NameFlags.XCN_CERT_NAME_STR_NONE);
+
+ CCspInformations objCSPs = new CCspInformations();
+ CCspInformation objCSP = new CCspInformation();
+
+ objCSP.InitializeFromName(
+ "Microsoft Enhanced RSA and AES Cryptographic Provider");
+
+ objCSPs.Add(objCSP);
+
+ // Build the private key
+ CX509PrivateKey privateKey = new CX509PrivateKey();
+
+ privateKey.MachineContext = false;
+ privateKey.Length = 2048;
+ privateKey.CspInformations = objCSPs;
+ privateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE;
+ privateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_ALL_USAGES;
+ privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
+
+ // Create the private key in the CSP's protected storage
+ privateKey.Create();
+
+ // Build the algorithm identifier
+ var hashobj = new CObjectId();
+ hashobj.InitializeFromAlgorithmName(
+ ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
+ ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY,
+ AlgorithmFlags.AlgorithmFlagsNone,
+ "SHA256");
+
+ // Create the self-signing request from the private key
+ var certificateRequest = new CX509CertificateRequestCertificate();
+ certificateRequest.InitializeFromPrivateKey(
+ X509CertificateEnrollmentContext.ContextUser,
+ privateKey,
+ string.Empty);
+
+ certificateRequest.Subject = distinguishedName;
+ certificateRequest.Issuer = distinguishedName;
+ certificateRequest.NotBefore = DateTime.Now.AddDays(-1);
+ certificateRequest.NotAfter = DateTime.Now.AddYears(100);
+ certificateRequest.HashAlgorithm = hashobj;
+
+ certificateRequest.Encode();
+
+ var enrollment = new CX509Enrollment();
+
+ // Load the certificate request
+ enrollment.InitializeFromRequest(certificateRequest);
+ enrollment.CertificateFriendlyName = subjectName;
+
+ // Output the request in base64 and install it back as the response
+ string csr = enrollment.CreateRequest();
+
+ // Install the response
+ enrollment.InstallResponse(
+ InstallResponseRestrictionFlags.AllowUntrustedCertificate,
+ csr,
+ EncodingType.XCN_CRYPT_STRING_BASE64,
+ string.Empty);
+
+ // Get the new certificate without the private key
+ byte[] certificateData = Convert.FromBase64String(enrollment.Certificate);
+
+ return new X509Certificate2(certificateData);
+ }
+ }
+}
diff --git a/SyncPro.Certificates/Properties/AssemblyInfo.cs b/SyncPro.Certificates/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..ded8f82
--- /dev/null
+++ b/SyncPro.Certificates/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+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("SyncPro.Certificates")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SyncPro.Certificates")]
+[assembly: AssemblyCopyright("Copyright © 2018")]
+[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("11febc64-ee05-4095-bd49-9fc7fabcc1df")]
+
+// 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 Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SyncPro.Certificates/SyncPro.Certificates.csproj b/SyncPro.Certificates/SyncPro.Certificates.csproj
new file mode 100644
index 0000000..b55bd45
--- /dev/null
+++ b/SyncPro.Certificates/SyncPro.Certificates.csproj
@@ -0,0 +1,65 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {11FEBC64-EE05-4095-BD49-9FC7FABCC1DF}
+ Library
+ Properties
+ SyncPro.Certificates
+ SyncPro.Certificates
+ v4.5.2
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {728AB348-217D-11DA-B2A4-000E7BBB2B09}
+ 1
+ 0
+ 0
+ tlbimp
+ False
+ True
+
+
+
+
+
\ No newline at end of file
diff --git a/SyncPro.Core/Configuration/TriggerConfiguration.cs b/SyncPro.Core/Configuration/TriggerConfiguration.cs
index 089585a..cc5cfb1 100644
--- a/SyncPro.Core/Configuration/TriggerConfiguration.cs
+++ b/SyncPro.Core/Configuration/TriggerConfiguration.cs
@@ -11,9 +11,16 @@ public class TriggerConfiguration
public int HourlyMinutesPastSyncTime { get; set; }
}
+ public enum EncryptionMode
+ {
+ None = 0,
+ Encrypt = 1,
+ Decrypt = 2
+ }
+
public class EncryptionConfiguration
{
- public bool IsEnabled { get; set; }
+ public EncryptionMode Mode { get; set; }
public string CertificateThumbprint { get; set; }
}
diff --git a/SyncPro.Core/Runtime/EncryptionManager.cs b/SyncPro.Core/Runtime/EncryptionManager.cs
index a66aef2..dbb89c4 100644
--- a/SyncPro.Core/Runtime/EncryptionManager.cs
+++ b/SyncPro.Core/Runtime/EncryptionManager.cs
@@ -6,12 +6,7 @@ namespace SyncPro.Runtime
using System.Security.Cryptography.X509Certificates;
using SyncPro.Adapters;
-
- public enum EncryptionMode
- {
- Encrypt,
- Decrypt
- }
+ using SyncPro.Configuration;
public class EncryptionManager : IDisposable
{
@@ -54,6 +49,8 @@ public EncryptionManager(
Pre.ThrowIfArgumentNull(encryptionCertificate, nameof(encryptionCertificate));
Pre.ThrowIfArgumentNull(outputStream, nameof(outputStream));
+ Pre.ThrowIfTrue(mode == EncryptionMode.None, "Encryption mode cannot be None");
+
this.encryptionCertificate = encryptionCertificate;
this.Mode = mode;
this.sourceFileSize = sourceFileSize;
diff --git a/SyncPro.Core/Runtime/SyncRelationship.cs b/SyncPro.Core/Runtime/SyncRelationship.cs
index e687f3d..5eebf49 100644
--- a/SyncPro.Core/Runtime/SyncRelationship.cs
+++ b/SyncPro.Core/Runtime/SyncRelationship.cs
@@ -5,10 +5,12 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
+ using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using SyncPro.Adapters;
+ using SyncPro.Certificates;
using SyncPro.Configuration;
using SyncPro.Data;
using SyncPro.Tracing;
@@ -100,7 +102,7 @@ private SyncRelationship(RelationshipConfiguration configuration)
this.ThrottlingValue = configuration.ThrottlingConfiguration.Value;
this.ThrottlingScaleFactor = configuration.ThrottlingConfiguration.ScaleFactor;
- this.EncryptionIsEnabled = configuration.EncryptionConfiguration.IsEnabled;
+ this.EncryptionMode = configuration.EncryptionConfiguration.Mode;
this.EncryptionCertificateThumbprint = configuration.EncryptionConfiguration.CertificateThumbprint;
this.State = SyncRelationshipState.NotInitialized;
@@ -137,8 +139,8 @@ public async Task SaveAsync()
this.Configuration.TriggerConfiguration.HourlyMinutesPastSyncTime = this.TriggerHourlyMinutesPastSyncTime;
this.Configuration.TriggerConfiguration.ScheduleInterval = this.TriggerScheduleInterval;
- this.Configuration.EncryptionConfiguration.IsEnabled = this.EncryptionIsEnabled;
- this.Configuration.EncryptionConfiguration.CertificateThumbprint = this.EncryptionCertificateThumbprint;
+ // Set the encryption mode for adapters that may need it. Other encryption configuration is set below.
+ this.Configuration.EncryptionConfiguration.Mode = this.EncryptionMode;
// If the relaionship contains adapters that arent in the configuration, add them
foreach (AdapterConfiguration adapterConfig in this.Adapters.Select(a => a.Configuration))
@@ -208,9 +210,20 @@ public async Task SaveAsync()
adapterBase.SaveConfiguration();
}
- // Set the creation time of the adapter
+ // Check if we are creating this relationship for the first time
if (this.Configuration.InitiallyCreatedUtc == DateTime.MinValue)
{
+ // Create the encryption certificate if needed
+ if (this.EncryptionMode == EncryptionMode.Encrypt && this.EncryptionCreateCertificate)
+ {
+ string subjectName = "SyncProEncryption " + this.Configuration.RelationshipId.ToString("D").ToLowerInvariant();
+
+ X509Certificate2 encryptionCert = CertificateHelper.CreateSelfSignedCertificate(subjectName);
+
+ this.EncryptionCertificateThumbprint = encryptionCert.Thumbprint;
+ this.Configuration.EncryptionConfiguration.CertificateThumbprint = encryptionCert.Thumbprint;
+ }
+
this.Configuration.InitiallyCreatedUtc = DateTime.UtcNow;
}
@@ -409,14 +422,20 @@ public TriggerScheduleInterval TriggerScheduleInterval
#region Encryption Properties
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
- private bool encryptionIsEnabled;
+ private EncryptionMode encryptionMode;
- public bool EncryptionIsEnabled
+ public EncryptionMode EncryptionMode
{
- get { return this.encryptionIsEnabled; }
- set { this.SetProperty(ref this.encryptionIsEnabled, value); }
+ get { return this.encryptionMode; }
+ set { this.SetProperty(ref this.encryptionMode, value); }
}
+ ///
+ /// Indicates whether the certificate should be created when the relationship is saved
+ /// for the first time.
+ ///
+ public bool EncryptionCreateCertificate { get; set; }
+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string encryptionCertificateThumbprint;
diff --git a/SyncPro.Core/Runtime/SyncRun.cs b/SyncPro.Core/Runtime/SyncRun.cs
index d4c7f92..a0c31c5 100644
--- a/SyncPro.Core/Runtime/SyncRun.cs
+++ b/SyncPro.Core/Runtime/SyncRun.cs
@@ -11,6 +11,7 @@
using System.Threading.Tasks;
using SyncPro.Adapters;
+ using SyncPro.Configuration;
using SyncPro.Data;
using SyncPro.Tracing;
@@ -335,7 +336,7 @@ private async Task SyncInternalAsync()
{ "AnalyzeResultId", this.AnalyzeResult.Id },
});
- if (this.relationship.EncryptionIsEnabled)
+ if (this.relationship.EncryptionMode != Configuration.EncryptionMode.None)
{
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
@@ -887,18 +888,25 @@ private async Task CopyFileAsync(
{
long writeStreamLength = updateInfo.Entry.SourceSize;
- if (this.relationship.EncryptionIsEnabled)
+ if (this.relationship.EncryptionMode == EncryptionMode.Encrypt)
{
short padding;
writeStreamLength = EncryptionManager.CalculateEncryptedFileSize(
updateInfo.Entry.SourceSize,
out padding);
}
+ else if (this.relationship.EncryptionMode == EncryptionMode.Decrypt)
+ {
+ short padding;
+ writeStreamLength = EncryptionManager.CalculateDecryptedFileSize(
+ updateInfo.Entry.SourceSize,
+ out padding);
+ }
fromStream = fromAdapter.GetReadStreamForEntry(updateInfo.Entry);
toStream = toAdapter.GetWriteStreamForEntry(updateInfo.Entry, writeStreamLength);
- if (this.relationship.EncryptionIsEnabled)
+ if (this.relationship.EncryptionMode != EncryptionMode.None)
{
// Create a copy of the certificate from the original cert's handle. A unique copy is required
// because the encryption manager will dispose of the RSA CSP derived from the cert, and will
diff --git a/SyncPro.Core/SyncPro.Core.csproj b/SyncPro.Core/SyncPro.Core.csproj
index 3075a94..804f0e0 100644
--- a/SyncPro.Core/SyncPro.Core.csproj
+++ b/SyncPro.Core/SyncPro.Core.csproj
@@ -121,6 +121,10 @@
+
+ {11febc64-ee05-4095-bd49-9fc7fabcc1df}
+ SyncPro.Certificates
+
{CE6A7780-BC06-4818-B15F-8DAD91032A71}
SyncPro.Tracing
diff --git a/SyncPro.UI/Controls/EncryptionSettingsDialog.xaml b/SyncPro.UI/Controls/EncryptionSettingsDialog.xaml
index c0859d1..cdeb57f 100644
--- a/SyncPro.UI/Controls/EncryptionSettingsDialog.xaml
+++ b/SyncPro.UI/Controls/EncryptionSettingsDialog.xaml
@@ -44,7 +44,8 @@
Text="Files can be encrypted/decrypted when synchronized. Select the way that encryption should be performed below. Encryption cannot be enabled or disabled once a relationship is created." />
-
+
diff --git a/SyncPro.UI/RelationshipEditor/Sections/SyncOptionsSection.xaml b/SyncPro.UI/RelationshipEditor/Sections/SyncOptionsSection.xaml
index e23015e..4616369 100644
--- a/SyncPro.UI/RelationshipEditor/Sections/SyncOptionsSection.xaml
+++ b/SyncPro.UI/RelationshipEditor/Sections/SyncOptionsSection.xaml
@@ -98,7 +98,18 @@
+ Text="{Binding Path=EncryptedSettingsStatus}">
+
+
+
+
diff --git a/SyncPro.UI/RelationshipEditor/SyncOptionsPageViewModel.cs b/SyncPro.UI/RelationshipEditor/SyncOptionsPageViewModel.cs
index a85f719..e102c40 100644
--- a/SyncPro.UI/RelationshipEditor/SyncOptionsPageViewModel.cs
+++ b/SyncPro.UI/RelationshipEditor/SyncOptionsPageViewModel.cs
@@ -3,6 +3,7 @@ namespace SyncPro.UI.RelationshipEditor
using System.Diagnostics;
using System.Windows.Input;
+ using SyncPro.Configuration;
using SyncPro.UI.Controls;
using SyncPro.UI.Framework.MVVM;
using SyncPro.UI.ViewModels;
@@ -28,7 +29,12 @@ private bool CanShowEncryptionSettingsDialog(object obj)
private void ShowEncryptionSettingsDialog(object obj)
{
- EncryptionSettingsDialogViewModel dialogViewModel = new EncryptionSettingsDialogViewModel();
+ EncryptionSettingsDialogViewModel dialogViewModel = new EncryptionSettingsDialogViewModel
+ {
+ IsCreateMode = this.EditorViewModel.IsCreateMode,
+ IsEncryptionEnabled = this.EditorViewModel.Relationship.EncryptionMode != EncryptionMode.None,
+ CreateNewCertificate = this.EditorViewModel.Relationship.EncryptionCreateCertificate
+ };
EncryptionSettingsDialog dialog = new EncryptionSettingsDialog
{
@@ -47,20 +53,44 @@ private void ShowEncryptionSettingsDialog(object obj)
private void SetEncryptedSettingsStatus()
{
+ if (this.EditorViewModel.IsEditMode)
+ {
+ switch (this.EditorViewModel.Relationship.EncryptionMode)
+ {
+ case EncryptionMode.None:
+ this.EncryptedSettingsStatus = "Encryption is disabled";
+ this.EncryptedSettingsStatusImportant = false;
+ break;
+ case EncryptionMode.Encrypt:
+ this.EncryptedSettingsStatus = "File encryption is enabled";
+ this.EncryptedSettingsStatusImportant = true;
+ break;
+ case EncryptionMode.Decrypt:
+ this.EncryptedSettingsStatus = "File decryption is enabled";
+ this.EncryptedSettingsStatusImportant = true;
+ break;
+ }
+
+ return;
+ }
+
if (this.IsEncryptionEnabled)
{
if (this.CreateNewEncryptionCertificate)
{
this.EncryptedSettingsStatus = "File encryption will be enabled using a new certificate.";
+ this.EncryptedSettingsStatusImportant = true;
}
else
{
this.EncryptedSettingsStatus = "File encryption will be enabled using an existing certificate.";
+ this.EncryptedSettingsStatusImportant = true;
}
}
else
{
this.EncryptedSettingsStatus = "Files will not be encrypted before copying.";
+ this.EncryptedSettingsStatusImportant = false;
}
}
@@ -73,12 +103,23 @@ public override void LoadContext()
this.SelectedScopeType = this.EditorViewModel.Relationship.Scope;
}
+ this.IsEncryptionEnabled =
+ this.EditorViewModel.Relationship.EncryptionMode != EncryptionMode.None;
+
this.SetEncryptedSettingsStatus();
}
public override void SaveContext()
{
this.EditorViewModel.Relationship.Scope = this.SelectedScopeType;
+
+ if (this.EditorViewModel.IsCreateMode)
+ {
+ this.EditorViewModel.Relationship.EncryptionMode =
+ this.IsEncryptionEnabled ? EncryptionMode.Encrypt : EncryptionMode.None;
+ this.EditorViewModel.Relationship.EncryptionCreateCertificate =
+ this.CreateNewEncryptionCertificate;
+ }
}
public override string NavTitle => "Options";
@@ -142,5 +183,14 @@ public string EncryptedSettingsStatus
get { return this.encryptedSettingsStatus; }
set { this.SetProperty(ref this.encryptedSettingsStatus, value); }
}
+
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private bool encryptedSettingsStatusImportant;
+
+ public bool EncryptedSettingsStatusImportant
+ {
+ get { return this.encryptedSettingsStatusImportant; }
+ set { this.SetProperty(ref this.encryptedSettingsStatusImportant, value); }
+ }
}
}
\ No newline at end of file
diff --git a/SyncPro.UI/ViewModels/EncryptionSettingsDialogViewModel.cs b/SyncPro.UI/ViewModels/EncryptionSettingsDialogViewModel.cs
index f6ca827..2ba3d19 100644
--- a/SyncPro.UI/ViewModels/EncryptionSettingsDialogViewModel.cs
+++ b/SyncPro.UI/ViewModels/EncryptionSettingsDialogViewModel.cs
@@ -8,13 +8,6 @@
using SyncPro.UI.Framework;
using SyncPro.UI.Framework.MVVM;
- public enum EncryptionType
- {
- None,
- Encrypt,
- Decrypt
- }
-
public class EncryptionSettingsDialogViewModel : ViewModelBase, IRequestClose
{
public ICommand OKCommand { get; }
@@ -37,6 +30,15 @@ public EncryptionSettingsDialogViewModel()
this.CreateNewCertificate = true;
}
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private bool isCreateMode;
+
+ public bool IsCreateMode
+ {
+ get { return this.isCreateMode; }
+ set { this.SetProperty(ref this.isCreateMode, value); }
+ }
+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private bool isEncryptionEnabled;
diff --git a/SyncPro.UI/ViewModels/SyncRelationshipViewModel.cs b/SyncPro.UI/ViewModels/SyncRelationshipViewModel.cs
index 753e41e..f7fe4ae 100644
--- a/SyncPro.UI/ViewModels/SyncRelationshipViewModel.cs
+++ b/SyncPro.UI/ViewModels/SyncRelationshipViewModel.cs
@@ -10,6 +10,7 @@
using System.Windows.Input;
using SyncPro.Adapters;
+ using SyncPro.Configuration;
using SyncPro.Data;
using SyncPro.Runtime;
using SyncPro.Tracing;
@@ -99,6 +100,20 @@ public int ThrottlingScaleFactor
set { this.BaseModel.ThrottlingScaleFactor = value; }
}
+ [BaseModelProperty(NotifyOnPropertyChange = true)]
+ public EncryptionMode EncryptionMode
+ {
+ get { return this.BaseModel.EncryptionMode; }
+ set { this.BaseModel.EncryptionMode = value; }
+ }
+
+ [BaseModelProperty(NotifyOnPropertyChange = true)]
+ public bool EncryptionCreateCertificate
+ {
+ get { return this.BaseModel.EncryptionCreateCertificate; }
+ set { this.BaseModel.EncryptionCreateCertificate = value; }
+ }
+
#endregion
#region ViewModel properties
diff --git a/SyncPro.UnitTests/EncryptionTests.cs b/SyncPro.UnitTests/EncryptionTests.cs
index d540656..8045f85 100644
--- a/SyncPro.UnitTests/EncryptionTests.cs
+++ b/SyncPro.UnitTests/EncryptionTests.cs
@@ -8,6 +8,7 @@ namespace SyncPro.UnitTests
using Microsoft.VisualStudio.TestTools.UnitTesting;
+ using SyncPro.Configuration;
using SyncPro.Runtime;
[TestClass]
@@ -42,7 +43,7 @@ public void SyncLocalFilesWithEncryption()
.SaveRelationship()
.CreateSimpleSourceStructure();
- wrapper.Relationship.EncryptionIsEnabled = true;
+ wrapper.Relationship.EncryptionMode = EncryptionMode.Encrypt;
wrapper.Relationship.EncryptionCertificateThumbprint = "420fe8033179cfb0ef21862d24bf6a1ec7df6c6d";
wrapper.CreateSyncRun()
diff --git a/SyncPro.sln b/SyncPro.sln
index 9172149..33db5cd 100644
--- a/SyncPro.sln
+++ b/SyncPro.sln
@@ -34,6 +34,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyncPro.Adapters.GoogleDriv
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyncPro.Adapters.WindowsFileSystem", "SyncPro.Adapters.WindowsFileSystem\SyncPro.Adapters.WindowsFileSystem.csproj", "{3475A78F-4AEB-4070-BBBB-4A8EFEC03E64}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SyncPro.Certificates", "SyncPro.Certificates\SyncPro.Certificates.csproj", "{11FEBC64-EE05-4095-BD49-9FC7FABCC1DF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -92,6 +94,10 @@ Global
{3475A78F-4AEB-4070-BBBB-4A8EFEC03E64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3475A78F-4AEB-4070-BBBB-4A8EFEC03E64}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3475A78F-4AEB-4070-BBBB-4A8EFEC03E64}.Release|Any CPU.Build.0 = Release|Any CPU
+ {11FEBC64-EE05-4095-BD49-9FC7FABCC1DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {11FEBC64-EE05-4095-BD49-9FC7FABCC1DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {11FEBC64-EE05-4095-BD49-9FC7FABCC1DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {11FEBC64-EE05-4095-BD49-9FC7FABCC1DF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE