-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathBaseNameCrypt.cs
More file actions
99 lines (82 loc) · 3.75 KB
/
BaseNameCrypt.cs
File metadata and controls
99 lines (82 loc) · 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
using System;
using System.Buffers.Text;
using System.Runtime.CompilerServices;
using System.Text;
using SecureFolderFS.Core.Cryptography.Cipher;
namespace SecureFolderFS.Core.Cryptography.NameCrypt
{
/// <inheritdoc cref="INameCrypt"/>
internal abstract class BaseNameCrypt : INameCrypt
{
protected const NormalizationForm NORMALIZATION = NormalizationForm.FormC;
/// <inheritdoc/>
public virtual string EncodingId { get; }
protected BaseNameCrypt(string fileNameEncodingId)
{
EncodingId = fileNameEncodingId;
}
/// <inheritdoc/>
[SkipLocalsInit]
public virtual string EncryptName(ReadOnlySpan<char> plaintextName, ReadOnlySpan<byte> directoryId)
{
// Allocate byte* for encoding
var length = Encoding.UTF8.GetByteCount(plaintextName);
var bytes = length < 256 ? stackalloc byte[length] : new byte[length];
// Get bytes from plaintext name
var count = Encoding.UTF8.GetBytes(plaintextName, bytes);
// Encrypt
var ciphertextNameBuffer = EncryptFileName(bytes.Slice(0, count), directoryId);
// Encode string
return EncodingId switch
{
Constants.CipherId.ENCODING_BASE64URL => Base64Url.EncodeToString(ciphertextNameBuffer),
Constants.CipherId.ENCODING_BASE4K => SecombaBase4K.Encode(ciphertextNameBuffer).Normalize(NORMALIZATION),
_ => throw new ArgumentOutOfRangeException(nameof(EncodingId))
};
}
/// <inheritdoc/>
[SkipLocalsInit]
public virtual string? DecryptName(ReadOnlySpan<char> ciphertextName, ReadOnlySpan<byte> directoryId)
{
try
{
if (EncodingId == Constants.CipherId.ENCODING_BASE4K && !ciphertextName.IsNormalized(NORMALIZATION))
{
var normalizedLength = ciphertextName.GetNormalizedLength(NORMALIZATION);
var destination = normalizedLength < 256 ? stackalloc char[normalizedLength] : new char[normalizedLength];
// Try to normalize
if (!ciphertextName.TryNormalize(destination, out var written, NORMALIZATION))
return null;
// Decode
return Decode(destination.Slice(0, written), directoryId);
}
// Skip normalization and decode directly
return Decode(ciphertextName, directoryId);
}
catch (Exception)
{
return null;
}
string? Decode(ReadOnlySpan<char> name, ReadOnlySpan<byte> associatedData)
{
// Decode buffer
var decoded = EncodingId switch
{
Constants.CipherId.ENCODING_BASE64URL => Base64Url.DecodeFromChars(name),
Constants.CipherId.ENCODING_BASE4K => SecombaBase4K.Decode(name),
_ => throw new ArgumentOutOfRangeException(nameof(EncodingId))
};
// Decrypt
var plaintextNameBuffer = DecryptFileName(decoded, associatedData);
if (plaintextNameBuffer is null)
return null;
// Get string from plaintext buffer
return Encoding.UTF8.GetString(plaintextNameBuffer);
}
}
protected abstract byte[] EncryptFileName(ReadOnlySpan<byte> plaintextFileNameBuffer, ReadOnlySpan<byte> directoryId);
protected abstract byte[]? DecryptFileName(ReadOnlySpan<byte> ciphertextFileNameBuffer, ReadOnlySpan<byte> directoryId);
/// <inheritdoc/>
public abstract void Dispose();
}
}