Skip to content

Commit 612d403

Browse files
committed
Partially fix Base4K encoding
1 parent 575d54e commit 612d403

6 files changed

Lines changed: 31 additions & 29 deletions

File tree

src/Core/SecureFolderFS.Core.FileSystem/Helpers/Paths/Abstract/AbstractPathHelpers.Cipher.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.IO;
7+
using System.Text;
78
using System.Threading;
89
using System.Threading.Tasks;
910

@@ -161,7 +162,22 @@ public static async Task<string> EncryptNameAsync(string plaintextName, IFolder
161162
var directoryId = AllocateDirectoryId(specifics.Security, ciphertextName);
162163
var result = await GetDirectoryIdAsync(ciphertextParentFolder, specifics, directoryId, cancellationToken);
163164

164-
return specifics.Security.NameCrypt.DecryptName(Path.GetFileNameWithoutExtension(ciphertextName), result ? directoryId : ReadOnlySpan<byte>.Empty);
165+
// Do NOT use Path.GetFileNameWithoutExtension - after APFS or ContentProvider NFD-decomposes Base4K
166+
// codepoints, the string may contain spurious dot-like characters that confuse the
167+
// path parser, causing it to truncate mid-ciphertext.
168+
// Strip the known extension manually instead.
169+
var nameWithoutExtension = ciphertextName.EndsWith(Constants.Names.ENCRYPTED_FILE_EXTENSION, StringComparison.Ordinal)
170+
? ciphertextName.AsSpan(0, ciphertextName.Length - Constants.Names.ENCRYPTED_FILE_EXTENSION.Length)
171+
: ciphertextName.AsSpan();
172+
173+
var normalizedLength = nameWithoutExtension.GetNormalizedLength(NormalizationForm.FormC);
174+
var normalized = new char[normalizedLength];
175+
176+
// NFC-normalize before decoding
177+
if (nameWithoutExtension.TryNormalize(normalized, out var written, NormalizationForm.FormC))
178+
return specifics.Security.NameCrypt.DecryptName(normalized.AsSpan(0, written), result ? directoryId : ReadOnlySpan<byte>.Empty);
179+
180+
return specifics.Security.NameCrypt.DecryptName(nameWithoutExtension, result ? directoryId : ReadOnlySpan<byte>.Empty);
165181
}
166182
catch (Exception)
167183
{

src/Core/SecureFolderFS.Core.FileSystem/Validators/BaseFileSystemValidator.cs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,15 @@ protected async Task<IResult> ValidateNameResultAsync(IStorableChild storable, C
5757
if (parentFolder is null)
5858
return null;
5959

60-
var ciphertextName = storable.Name;
61-
var directoryId = AbstractPathHelpers.AllocateDirectoryId(specifics.Security, ciphertextName);
60+
var decryptedName = await AbstractPathHelpers.DecryptNameAsync(storable.Name, parentFolder, specifics, cancellationToken);
61+
if (!string.IsNullOrEmpty(decryptedName))
62+
return decryptedName;
6263

63-
try
64-
{
65-
var result = await AbstractPathHelpers.GetDirectoryIdAsync(parentFolder, specifics, directoryId, cancellationToken).ConfigureAwait(false);
66-
return specifics.Security.NameCrypt.DecryptName(Path.GetFileNameWithoutExtension(ciphertextName), result ? directoryId : ReadOnlySpan<byte>.Empty);
67-
}
68-
catch (FileNotFoundException)
69-
{
70-
// We want to suppress FileNotFoundException that might be raised when the DirectoryID file is not found.
71-
// This case should be already handled in the folder validator
64+
// We want to suppress failures that might be raised when the Directory ID file is not found.
65+
// This case should be already handled in the folder validator
7266

73-
// Return an empty string to prevent raising exceptions due to the name being null
74-
return string.Empty;
75-
}
67+
// Return an empty string to prevent raising exceptions due to the name being null
68+
return string.Empty;
7669
}
7770
}
7871
}

src/Platforms/SecureFolderFS.Maui/Views/Modals/DeviceLink/DeviceLinkCredentialsPage.xaml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
Background="{StaticResource ThemePrimaryColorBrush}">
3434
<Border
3535
Padding="20"
36-
Background="{StaticResource ThemeSecondaryColorBrush}"
36+
Background="{StaticResource ThemeElevatedFillPrimaryColorBrush}"
3737
StrokeThickness="0">
3838
<Border.StrokeShape>
3939
<RoundRectangle CornerRadius="16" />
@@ -140,14 +140,12 @@
140140
<Label
141141
FontAttributes="Bold"
142142
FontSize="18"
143-
Text="{l:ResourceString Rid=DeviceLink}"
144-
TextColor="{StaticResource PrimaryContrastingLightColor}" />
143+
Text="{l:ResourceString Rid=DeviceLink}" />
145144
<Label
146145
FontSize="13"
147146
LineBreakMode="WordWrap"
148147
Opacity="0.8"
149-
Text="{l:ResourceString Rid=DeviceLinkDescription}"
150-
TextColor="{StaticResource PrimaryContrastingLightColor}" />
148+
Text="{l:ResourceString Rid=DeviceLinkDescription}" />
151149
</VerticalStackLayout>
152150
</Grid>
153151
</Border>

src/Platforms/SecureFolderFS.Maui/Views/Modals/Settings/SettingsPage.xaml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
Background="{StaticResource ThemePrimaryColorBrush}">
3131
<Border
3232
Padding="20,20"
33-
Background="{StaticResource ThemeSecondaryColorBrush}"
33+
Background="{StaticResource ThemeElevatedFillPrimaryColorBrush}"
3434
StrokeThickness="0">
3535
<Border.StrokeShape>
3636
<RoundRectangle CornerRadius="16" />
@@ -66,15 +66,13 @@
6666
FontAttributes="Bold"
6767
FontSize="18"
6868
HorizontalOptions="Center"
69-
Text="{l:ResourceString Rid=Settings}"
70-
TextColor="{StaticResource PrimaryContrastingLightColor}" />
69+
Text="{l:ResourceString Rid=Settings}" />
7170
<Label
7271
FontSize="13"
7372
HorizontalOptions="Center"
7473
HorizontalTextAlignment="Center"
7574
Opacity="0.7"
76-
Text="Manage your preferences, appearance, and privacy options for SecureFolderFS."
77-
TextColor="{StaticResource PrimaryContrastingLightColor}" />
75+
Text="Manage your preferences, appearance, and privacy options for SecureFolderFS." />
7876
</VerticalStackLayout>
7977
</Border>
8078
</Grid>

src/Platforms/SecureFolderFS.UI/ServiceImplementation/BaseVaultCredentialsService.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ public abstract class BaseVaultCredentialsService : IVaultCredentialsService
1313
/// <inheritdoc/>
1414
public virtual IEnumerable<string> GetEncodingOptions()
1515
{
16-
if (!OperatingSystem.IsIOS() && !OperatingSystem.IsMacOS() && !OperatingSystem.IsMacCatalyst() && !OperatingSystem.IsAndroid())
17-
yield return Core.Cryptography.Constants.CipherId.ENCODING_BASE4K;
18-
16+
yield return Core.Cryptography.Constants.CipherId.ENCODING_BASE4K;
1917
yield return Core.Cryptography.Constants.CipherId.ENCODING_BASE64URL;
2018
}
2119

src/Sdk/SecureFolderFS.Sdk/AppModels/ThumbnailCacheModel.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ public static async Task<string> GetCacheKeyAsync(IFile file, CancellationToken
118118
if (dateModified is null)
119119
return pathHash;
120120

121-
122121
// Combine path hash with modification date for the cache key
123122
// Format: {pathHash}_{dateModifiedTicks}
124123
return $"{pathHash}_{dateModified.Value.Ticks}";

0 commit comments

Comments
 (0)