Skip to content

Commit 61e37d6

Browse files
committed
feat(Core): add encryption & use it by default
Uses AES-256-GCM and z85 for content encoding. FreeBSD is not supported yet, so it doesn't use Encryption.
1 parent 7866eb4 commit 61e37d6

26 files changed

Lines changed: 617 additions & 149 deletions

.github/Progress.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
- [x] Package PaddleOCR for Linux properly.
1111
- [ ] Add charts for statistics like uploading, avg image size, most used image extension, and error rate for uploading
1212
- [ ] Ensure SnapX is the default screenshot program when installed on a new Windows installation.
13-
- [ ] Package for popular technologies (Flatpak, RPM (Fedora/OpenSUSE), Snap, Homebrew)
13+
- [x] Package for popular technologies (Flatpak, RPM (Fedora/OpenSUSE), Snap, Homebrew)
1414
- [ ] Expose the entire Core in UI (Avalonia) (Missing direct upload function & drag and drop)
1515
- [x] Add telemetry & Aptabase is a work in progress, PR pending https://github.com/aptabase/aptabase-maui/pull/12
1616
- [ ] Create MSI installer with [WixSharp](https://github.com/oleg-shilo/wixsharp)
@@ -31,7 +31,7 @@
3131
- [ ] Add database viewer as an built in SQLite database viewer that is able to complete operations like mass path replacement ie `C:\Users/Brycen` -> `/home/brycen`
3232
- [ ] Make CLI UNIX friendly while keeping ShareX's CLI valid. The CLI should be able to print its usage and autocomplete.
3333
- [ ] Add first tool, the FFMPEG 100MB video transcoder
34-
- [ ] Port `go-keyring` to C# (Needed for not saving auth creds in plaintext, big no no )
34+
- [x] Port `go-keyring` to C# (Needed for not saving auth creds in plaintext, big no no )
3535
- [ ] Search for missing files button in main window and locate missing file to allow users to fix broken entries.
3636
- [x] Bring in XCap library in .NET and other cross-platform screen capture libraries. (This will make the port take much longer)
3737
- [x] Remove SnapX as a fork of ShareX that can be merged into upstream. *Completed at 233 commits ahead of upstream*
@@ -41,7 +41,7 @@
4141
- [ ] Add Custom Uploader List to SnapX via a build-time HTTP Fetch, or if the file is there already, use that. Can be disabled by packagers as they need offline builds. Or they could fetch the list, or rather, JSON, as part of their build script that isn't done during packaging time. That list is then embedded into the binary, and then at runtime it is checked *again* for any new entries to said list. Thus, the functionality keeps working even in an environment where SnapX cannot access the internet.
4242
- [ ] ~~Add automatic region detection that suggests to users in Egypt, Russia, Ukraine, and possibly more to switch their default image uploader from Imgur to ImgBB. According to my tests, ImgBB doesn't have such region blocks for Ukraine & Egypt. Russia is untested because PIA doesn't have any servers there.~~ Imgur continually gets less reliable. I am investigating making ImgBB the default uploader for now until a more suitable alternative is found.
4343
- [ ] Add instructions to generate API keys for those interested
44-
- [ ] Add editor functionality (Crop, Annotate, etc) using [SkiaSharp](https://github.com/mono/SkiaSharp)
44+
- [ ] Add editor functionality (Crop, Annotate, etc) using ImageSharp
4545

4646
## Studying ShareX's behavior on Windows 11 24H2
4747

SnapX.Avalonia/Converters/HeaderSecurityBlurConverter.cs

Lines changed: 3 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,20 @@
1+
using SnapX.Core.Upload.Custom;
2+
13
namespace SnapX.Avalonia.Converters;
24

35
using System.Globalization;
46
using global::Avalonia.Data.Converters;
57

68
public class HeaderSecurityBlurConverter : IValueConverter
79
{
8-
private static readonly string[] SensitiveKeys =
9-
{
10-
"password",
11-
"upload_password",
12-
"upload-password",
13-
"passwd",
14-
"pass",
15-
"pwd",
16-
"api",
17-
"apikey",
18-
"api-key",
19-
"api_key",
20-
"x-api-key",
21-
"api key",
22-
"key",
23-
"keyid",
24-
"key-id",
25-
"email",
26-
"user",
27-
"k", // puush.me uses 'k' as their API key header
28-
"p", // short for password
29-
"username",
30-
"user-name",
31-
"user name",
32-
"credential",
33-
"creds",
34-
"cred",
35-
"token",
36-
"secret",
37-
"auth",
38-
"authorization",
39-
"x-authorization",
40-
"x-auth-token",
41-
"access-token",
42-
"access token",
43-
"bearer",
44-
"session",
45-
"jwt",
46-
"cookie",
47-
"priv",
48-
"sid",
49-
"uuid",
50-
"guid",
51-
"salt",
52-
"nonce",
53-
};
5410

5511
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
5612
{
5713
string? key = value as string;
5814
if (string.IsNullOrWhiteSpace(key))
5915
return 0.0;
6016

61-
bool isSensitive = SensitiveKeys.Any(s =>
17+
bool isSensitive = CustomUploaderItem.SensitiveKeys.Any(s =>
6218
key.Equals(s, StringComparison.OrdinalIgnoreCase)
6319
);
6420

SnapX.Avalonia/Views/Settings/Views/FileUploaders/FTPSettingsView.axaml.cs

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -303,74 +303,74 @@ private async void TestFTPAccountClick(object? Sender, RoutedEventArgs E)
303303
await Task.Run(() =>
304304
{
305305

306-
try
307-
{
308-
switch (account.Protocol)
306+
try
309307
{
310-
case FTPProtocol.FTP or FTPProtocol.FTPS:
311-
{
312-
using var ftp = new FTP(account);
313-
if (ftp.Connect())
308+
switch (account.Protocol)
309+
{
310+
case FTPProtocol.FTP or FTPProtocol.FTPS:
314311
{
315-
if (!ftp.DirectoryExists(remotePath))
312+
using var ftp = new FTP(account);
313+
if (ftp.Connect())
316314
{
317-
directories = ftp.CreateMultiDirectory(remotePath);
318-
}
315+
if (!ftp.DirectoryExists(remotePath))
316+
{
317+
directories = ftp.CreateMultiDirectory(remotePath);
318+
}
319319

320-
if (ftp.IsConnected)
321-
{
322-
if (directories.Count > 0)
320+
if (ftp.IsConnected)
323321
{
324-
msg = "Account connected, created folders" + "\r\n" + string.Join("\r\n", directories);
322+
if (directories.Count > 0)
323+
{
324+
msg = "Account connected, created folders" + "\r\n" + string.Join("\r\n", directories);
325+
}
326+
else
327+
{
328+
msg = "Account connected";
329+
}
325330
}
326331
else
327332
{
328-
msg = "Account connected";
333+
msg = "Account not connected";
329334
}
330335
}
331-
else
332-
{
333-
msg = "Account not connected";
334-
}
335-
}
336336

337-
break;
338-
}
339-
case FTPProtocol.SFTP:
340-
{
341-
using var sftp = new SFTP(account);
342-
if (sftp.Connect())
337+
break;
338+
}
339+
case FTPProtocol.SFTP:
343340
{
344-
if (!sftp.DirectoryExists(remotePath))
341+
using var sftp = new SFTP(account);
342+
if (sftp.Connect())
345343
{
346-
directories = sftp.CreateMultiDirectory(remotePath);
347-
}
344+
if (!sftp.DirectoryExists(remotePath))
345+
{
346+
directories = sftp.CreateMultiDirectory(remotePath);
347+
}
348348

349-
if (sftp.IsConnected)
350-
{
351-
if (directories.Count > 0)
349+
if (sftp.IsConnected)
352350
{
353-
msg = "Account connected, created folders" + "\r\n" + string.Join("\r\n", directories);
351+
if (directories.Count > 0)
352+
{
353+
msg = "Account connected, created folders" + "\r\n" + string.Join("\r\n", directories);
354+
}
355+
else
356+
{
357+
msg = "Account connected";
358+
}
354359
}
355360
else
356361
{
357-
msg = "Account connected";
362+
msg = "Account not connected";
358363
}
359364
}
360-
else
361-
{
362-
msg = "Account not connected";
363-
}
364-
}
365365

366-
break;
367-
}
366+
break;
367+
}
368+
}
369+
}
370+
catch (Exception e)
371+
{
372+
msg = e.Message;
368373
}
369-
}
370-
catch (Exception e)
371-
{
372-
msg = e.Message;
373-
}
374374
});
375375

376376
var dialog = new ContentDialog

SnapX.Core/MasterkeyManager.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System.Security.Cryptography;
2+
using GnomeStack.Standard;
3+
using SimpleBase;
4+
5+
namespace SnapX.Core;
6+
7+
public static class MasterKeyManager
8+
{
9+
private const string ServiceName = SnapXL.AppName;
10+
private const string AccountName = "MasterEncryptionKey";
11+
private const int KeySize = 32; // 256-bit
12+
13+
public static byte[] GetOrGenerateKey()
14+
{
15+
var z85Key = OsSecretVault.GetSecret(ServiceName, AccountName);
16+
17+
if (string.IsNullOrEmpty(z85Key)) return GenerateAndStoreKey();
18+
try
19+
{
20+
var key = Base85.Z85.Decode(z85Key);
21+
if (key.Length == KeySize)
22+
{
23+
// DebugHelper.WriteLine($"[Vault] Key successfully recovered via Z85. Hash: {BitConverter.ToString(key[..4])}...");
24+
return key;
25+
}
26+
}
27+
catch (Exception ex)
28+
{
29+
DebugHelper.WriteLine($"[Vault] Secret was not valid Z85: {ex.Message}");
30+
}
31+
32+
return GenerateAndStoreKey();
33+
}
34+
35+
private static byte[] GenerateAndStoreKey()
36+
{
37+
var newKey = new byte[KeySize];
38+
using (var rng = RandomNumberGenerator.Create())
39+
{
40+
rng.GetBytes(newKey);
41+
}
42+
var z85Key = Base85.Z85.Encode(newKey);
43+
44+
OsSecretVault.SetSecret(ServiceName, AccountName, z85Key);
45+
46+
return newKey;
47+
}
48+
49+
public static void ResetKey() => OsSecretVault.DeleteSecret(ServiceName, AccountName);
50+
}

SnapX.Core/SettingManager.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Reflection;
88
using System.Text.Json;
99
using System.Text.Json.Serialization;
10+
using System.Text.Json.Serialization.Metadata;
1011
using Dapper;
1112
#if WINDOWS
1213
using Esatto.Win32.Registry;
@@ -216,7 +217,7 @@ public static void LoadUploadersConfig(bool fallbackSupport = true)
216217
}
217218
UploadersConfig.CreateBackup = true;
218219
UploadersConfig.CreateWeeklyBackup = true;
219-
UploadersConfig.SupportDPAPIEncryption = true;
220+
UploadersConfig.UseEncryption = true;
220221
BuiltConfig.Bind(UploadersConfig, Options => Options.BindNonPublicProperties = true);
221222
UploadersConfigBackwardCompatibilityTasks();
222223
}
@@ -390,8 +391,9 @@ private static T MigrateJsonConfig<T>(string path)
390391
new JsonSerializerOptions
391392
{
392393
PropertyNameCaseInsensitive = true,
393-
TypeInfoResolver = SettingsContext.Default,
394+
TypeInfoResolver = SettingsContext.Default.WithAddedModifier(typeInfo => JsonEncryptionResolver.CreateModifier(typeInfo, SecurePropertyStore.Instance)),
394395
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
396+
AllowTrailingCommas = true,
395397
Converters =
396398
{
397399
new JsonRectangleConverter(),

SnapX.Core/SnapX.Core.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@
9898
while these are only useful for Linux,
9999
hiding them behind a compiler conditional makes debugging more difficult. -->
100100
<PackageReference Include="Tmds.DBus.Protocol" Version="0.90.3" />
101-
<!-- <PackageReference Include="WaylandSharper" Version="0.1.3">-->
101+
<PackageReference Include="GnomeStack.Secrets.OperatingSystem" Version="0.1.6" />
102+
<PackageReference Include="SimpleBase" Version="5.6.0" />
103+
<!-- <PackageReference Include="WaylandSharper" Version="0.1.3">-->
102104
<!-- <IncludeAssets>build; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
103105
<!-- <PrivateAssets>all</PrivateAssets>-->
104106
<!-- </PackageReference>-->

SnapX.Core/Upload/Custom/CustomUploaderItem.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using SnapX.Core.Utils.Extensions;
1515
using SnapX.Core.Utils.Miscellaneous;
1616
using SnapX.Core.Utils.Parsers;
17+
using YamlDotNet.Serialization;
1718

1819
namespace SnapX.Core.Upload.Custom;
1920

@@ -136,6 +137,55 @@ public bool ShouldSerializeData() =>
136137

137138
[DefaultValue("")]
138139
public string? ErrorMessage { get; set; }
140+
[Browsable(false)]
141+
[YamlIgnore]
142+
[JsonIgnore]
143+
public static readonly string[] SensitiveKeys =
144+
{
145+
"password",
146+
"upload_password",
147+
"upload-password",
148+
"passwd",
149+
"pass",
150+
"pwd",
151+
"api",
152+
"apikey",
153+
"api-key",
154+
"api_key",
155+
"x-api-key",
156+
"api key",
157+
"key",
158+
"keyid",
159+
"key-id",
160+
"email",
161+
"user",
162+
"k", // puush.me uses 'k' as their API key header
163+
"p", // short for password
164+
"username",
165+
"user-name",
166+
"user name",
167+
"credential",
168+
"creds",
169+
"cred",
170+
"token",
171+
"secret",
172+
"auth",
173+
"authorization",
174+
"x-authorization",
175+
"x-auth-token",
176+
"access-token",
177+
"access token",
178+
"bearer",
179+
"session",
180+
"jwt",
181+
"cookie",
182+
"priv",
183+
"sid",
184+
"uuid",
185+
"guid",
186+
"salt",
187+
"nonce",
188+
};
139189

140190
public CustomUploaderItem()
141191
{

SnapX.Core/Upload/File/AmazonS3Settings.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
// SPDX-License-Identifier: GPL-3.0-or-later
33

44

5+
using SnapX.Core.Utils;
56

67
namespace SnapX.Core.Upload.File;
78

89
public class AmazonS3Settings
910
{
1011
public string? AccessKeyID { get; set; }
12+
[JsonEncrypt]
13+
[YamlEncrypt]
1114
public string SecretAccessKey { get; set; }
1215
public string? Endpoint { get; set; }
1316
public string Region { get; set; }

0 commit comments

Comments
 (0)