Cache the worker encryption keys on disk for faster startup.

They are stored user-encrypted, since the relevant security concern is another user sniffing the TCP stream.
This commit is contained in:
Ben Olden-Cooligan 2019-09-12 18:56:27 -04:00
parent 0bbd5168ec
commit 586b9f4c1d
6 changed files with 53 additions and 8 deletions

View File

@ -33,7 +33,7 @@ namespace NAPS2.Sdk.Tests.Config
public void InternalDefaultsNotNullProps()
{
var config = InternalDefaults.GetCommonConfig();
AssertPropNullOrNotNull(config, false);
AssertPropNullOrNotNull(config, false, "");
}
[Fact]
@ -42,19 +42,19 @@ namespace NAPS2.Sdk.Tests.Config
var config = new CommonConfig();
Assert.NotNull(config.Version);
config.Version = null;
AssertPropNullOrNotNull(config, true);
AssertPropNullOrNotNull(config, true, "");
}
private static void AssertPropNullOrNotNull(object config, bool shouldBeNull)
private static void AssertPropNullOrNotNull(object config, bool shouldBeNull, string path)
{
Assert.NotNull(config);
Assert.True(config != null, path);
foreach (var prop in config.GetType().GetProperties())
{
var value = prop.GetValue(config);
if (prop.CustomAttributes.Any(x => typeof(ChildAttribute).IsAssignableFrom(x.AttributeType)))
{
// Child, so recurse
AssertPropNullOrNotNull(value, shouldBeNull);
AssertPropNullOrNotNull(value, shouldBeNull, $"{path}{prop.Name}.");
}
else
{

View File

@ -25,6 +25,7 @@ namespace NAPS2.Config
EmailSetup = new EmailSetup();
BatchSettings = new BatchSettings();
KeyboardShortcuts = new KeyboardShortcuts();
SslSetup = new SslSetup();
}
[Common]
@ -176,6 +177,10 @@ namespace NAPS2.Config
[Child]
[Common]
public KeyboardShortcuts KeyboardShortcuts { get; set; }
[Child]
[Common]
public SslSetup SslSetup { get; set; }
[Common]
public ScanProfile DefaultProfileSettings { get; set; }

View File

@ -16,7 +16,6 @@ namespace NAPS2.Config
{
public static class InternalDefaults
{
// TODO: Test that no properties are null
public static CommonConfig GetCommonConfig() =>
new CommonConfig
{
@ -184,6 +183,11 @@ namespace NAPS2.Config
ZoomIn = "Ctrl+Oemplus",
ZoomOut = "Ctrl+OemMinus"
},
SslSetup = new SslSetup
{
WorkerCert = "",
WorkerPrivateKey = ""
},
DefaultProfileSettings = new ScanProfile { Version = ScanProfile.CURRENT_VERSION }
};
}

View File

@ -0,0 +1,11 @@
using NAPS2.Serialization;
namespace NAPS2.Config
{
public class SslSetup
{
public SecureString WorkerCert { get; set; }
public SecureString WorkerPrivateKey { get; set; }
}
}

View File

@ -137,6 +137,7 @@
<DesignTime>True</DesignTime>
<DependentUpon>ClientCreds.resx</DependentUpon>
</Compile>
<Compile Include="Config\SslSetup.cs" />
<Compile Include="Images\HoughLineDeskewer.cs" />
<Compile Include="Images\Deskewer.cs" />
<Compile Include="Images\ImageListMutation.cs" />

View File

@ -5,6 +5,7 @@ using System.IO;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using NAPS2.Config;
using NAPS2.Images.Storage;
using NAPS2.Platform;
using NAPS2.Util;
@ -20,11 +21,12 @@ namespace NAPS2.Remoting.Worker
public static IWorkerFactory Default
{
get => _default ?? (_default = new WorkerFactory(ImageContext.Default));
get => _default ??= new WorkerFactory(ImageContext.Default);
set => _default = value ?? throw new ArgumentNullException(nameof(value));
}
public const string WORKER_EXE_NAME = "NAPS2.Worker.exe";
public static readonly string[] SearchDirs =
{
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
@ -32,6 +34,8 @@ namespace NAPS2.Remoting.Worker
};
private readonly ImageContext imageContext;
private readonly ConfigProvider<CommonConfig> configProvider;
private readonly ConfigScopes configScopes;
private string workerExePath;
private BlockingCollection<WorkerContext> workerQueue;
@ -41,6 +45,13 @@ namespace NAPS2.Remoting.Worker
this.imageContext = imageContext;
}
public WorkerFactory(ImageContext imageContext, ConfigProvider<CommonConfig> configProvider, ConfigScopes configScopes)
{
this.imageContext = imageContext;
this.configProvider = configProvider;
this.configScopes = configScopes;
}
private string WorkerExePath
{
get
@ -90,7 +101,7 @@ namespace NAPS2.Remoting.Worker
}
}
var (cert, privateKey) = SslHelper.GenerateRootCertificate();
var (cert, privateKey) = GetOrCreateCertAndPrivateKey();
WriteEncodedString(proc.StandardInput, cert);
WriteEncodedString(proc.StandardInput, privateKey);
var portStr = proc.StandardOutput.ReadLine();
@ -103,6 +114,19 @@ namespace NAPS2.Remoting.Worker
return (proc, port, cert, privateKey);
}
private (string cert, string privateKey) GetOrCreateCertAndPrivateKey()
{
string cert = configProvider?.Get(c => c.SslSetup.WorkerCert);
string privateKey = configProvider?.Get(c => c.SslSetup.WorkerPrivateKey);
if (string.IsNullOrEmpty(cert) || string.IsNullOrEmpty(privateKey))
{
(cert, privateKey) = SslHelper.GenerateRootCertificate();
configScopes?.User.Set(c => c.SslSetup.WorkerCert = cert);
configScopes?.User.Set(c => c.SslSetup.WorkerPrivateKey = privateKey);
}
return (cert, privateKey);
}
private void WriteEncodedString(StreamWriter streamWriter, string value)
{
streamWriter.WriteLine(Convert.ToBase64String(Encoding.UTF8.GetBytes(value)));