-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathAesCtrHmacContentCrypt.cs
More file actions
148 lines (125 loc) · 5.74 KB
/
AesCtrHmacContentCrypt.cs
File metadata and controls
148 lines (125 loc) · 5.74 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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
using SecureFolderFS.Core.Cryptography.Cipher;
using SecureFolderFS.Core.Cryptography.SecureStore;
using System;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using SecureFolderFS.Shared.ComponentModel;
using static SecureFolderFS.Core.Cryptography.Constants.Crypto.Chunks.AesCtrHmac;
using static SecureFolderFS.Core.Cryptography.Extensions.ContentCryptExtensions.AesCtrHmacContentExtensions;
using static SecureFolderFS.Core.Cryptography.Extensions.HeaderCryptExtensions.AesCtrHmacHeaderExtensions;
namespace SecureFolderFS.Core.Cryptography.ContentCrypt
{
/// <inheritdoc cref="IContentCrypt"/>
internal sealed class AesCtrHmacContentCrypt : BaseContentCrypt
{
private readonly IKeyUsage _macKey;
/// <inheritdoc/>
public override int ChunkPlaintextSize { get; } = CHUNK_PLAINTEXT_SIZE;
/// <inheritdoc/>
public override int ChunkCiphertextSize { get; } = CHUNK_CIPHERTEXT_SIZE;
/// <inheritdoc/>
public override int ChunkFirstReservedSize { get; } = CHUNK_NONCE_SIZE;
public AesCtrHmacContentCrypt(IKeyUsage macKey)
{
_macKey = macKey;
}
/// <inheritdoc/>
[SkipLocalsInit]
public override unsafe void EncryptChunk(ReadOnlySpan<byte> plaintextChunk, long chunkNumber, ReadOnlySpan<byte> header, Span<byte> ciphertextChunk)
{
// Chunk nonce
RandomNumberGenerator.Fill(ciphertextChunk.Slice(0, CHUNK_NONCE_SIZE));
// Encrypt
AesCtr256.Encrypt(
plaintextChunk,
header.GetHeaderContentKey(),
ciphertextChunk.Slice(0, CHUNK_NONCE_SIZE),
ciphertextChunk.Slice(CHUNK_NONCE_SIZE, plaintextChunk.Length));
// Calculate MAC using UseKey pattern
fixed (byte* headerPtr = header)
fixed (byte* ciphertextPtr = ciphertextChunk)
{
var state = (
headerPtr: (nint)headerPtr,
headerLen: header.Length,
ctPtr: (nint)ciphertextPtr,
ctLen: ciphertextChunk.Length,
ptLen: plaintextChunk.Length,
chunkNumber
);
_macKey.UseKey(state, static (macKey, s) =>
{
var hdr = new ReadOnlySpan<byte>((byte*)s.headerPtr, s.headerLen);
var ct = new Span<byte>((byte*)s.ctPtr, s.ctLen);
CalculateChunkMacStatic(
macKey,
hdr.GetHeaderNonce(),
ct.Slice(0, CHUNK_NONCE_SIZE),
ct.Slice(CHUNK_NONCE_SIZE, s.ptLen),
s.chunkNumber,
ct.Slice(CHUNK_NONCE_SIZE + s.ptLen, CHUNK_MAC_SIZE));
});
}
}
/// <inheritdoc/>
[SkipLocalsInit]
public override unsafe bool DecryptChunk(ReadOnlySpan<byte> ciphertextChunk, long chunkNumber, ReadOnlySpan<byte> header, Span<byte> plaintextChunk)
{
// Verify MAC using UseKey pattern
fixed (byte* headerPtr = header)
fixed (byte* ciphertextPtr = ciphertextChunk)
{
var state = (
headerPtr: (nint)headerPtr,
headerLen: header.Length,
ctPtr: (nint)ciphertextPtr,
ctLen: ciphertextChunk.Length,
chunkNumber
);
var macValid = _macKey.UseKey(state, static (macKey, s) =>
{
var hdr = new ReadOnlySpan<byte>((byte*)s.headerPtr, s.headerLen);
var ct = new ReadOnlySpan<byte>((byte*)s.ctPtr, s.ctLen);
// Allocate byte* for MAC
Span<byte> mac = stackalloc byte[CHUNK_MAC_SIZE];
// Calculate MAC
CalculateChunkMacStatic(
macKey,
hdr.GetHeaderNonce(),
ct.GetChunkNonce(),
ct.GetChunkPayload(),
s.chunkNumber,
mac);
// Check MAC using constant-time comparison to prevent timing attacks
return CryptographicOperations.FixedTimeEquals(mac, ct.GetChunkMac());
});
if (!macValid)
return false;
}
// Decrypt
AesCtr256.Decrypt(
ciphertextChunk.GetChunkPayload(),
header.GetHeaderContentKey(),
ciphertextChunk.GetChunkNonce(),
plaintextChunk);
return true;
}
[SkipLocalsInit]
private static void CalculateChunkMacStatic(ReadOnlySpan<byte> macKey, ReadOnlySpan<byte> headerNonce, ReadOnlySpan<byte> chunkNonce, ReadOnlySpan<byte> ciphertextPayload, long chunkNumber, Span<byte> chunkMac)
{
// Convert long to byte array
Span<byte> beChunkNumber = stackalloc byte[sizeof(long)];
Unsafe.As<byte, long>(ref beChunkNumber[0]) = chunkNumber;
// Reverse byte order as needed
if (BitConverter.IsLittleEndian)
beChunkNumber.Reverse();
// Initialize HMAC
using var hmacSha256 = IncrementalHash.CreateHMAC(HashAlgorithmName.SHA256, macKey);
hmacSha256.AppendData(headerNonce); // headerNonce
hmacSha256.AppendData(beChunkNumber); // beChunkNumber
hmacSha256.AppendData(chunkNonce); // chunkNonce
hmacSha256.AppendData(ciphertextPayload); // ciphertextPayload
hmacSha256.GetCurrentHash(chunkMac);
}
}
}