1using System.Security.Cryptography;
3#pragma warning disable CA5358
8internal sealed class NdsModcryptTransform : IDisposable
11 private readonly Aes _aes;
13 private readonly ICryptoTransform _encryptor;
15 private readonly
byte[] _aesKey;
17 private readonly
byte[] _counter;
19 private readonly
byte[] _aesCounter =
new byte[NdsModcrypt.BlockSize];
21 private readonly
byte[] _keyStream =
new byte[NdsModcrypt.BlockSize];
23 private int _keyStreamIndex = NdsModcrypt.BlockSize;
25 private bool _disposed;
31 internal NdsModcryptTransform(ReadOnlySpan<byte> key, ReadOnlySpan<byte> initialCounter,
long byteOffset)
34 _aes.Mode = CipherMode.ECB;
35 _aes.Padding = PaddingMode.None;
36 _aesKey = key.ToArray();
37 Array.Reverse(_aesKey);
39 _encryptor = _aes.CreateEncryptor();
40 _counter = initialCounter.ToArray();
41 AddBlocks(_counter, (ulong)(byteOffset / NdsModcrypt.BlockSize));
42 int skip = (int)(byteOffset % NdsModcrypt.BlockSize);
46 _keyStreamIndex = skip;
53 internal void Transform(ReadOnlySpan<byte> source, Span<byte> destination)
55 ObjectDisposedException.ThrowIf(_disposed,
this);
56 if (destination.Length < source.Length)
58 throw new ArgumentException(
"The modcrypt destination is shorter than the source.", nameof(destination));
61 for (
int index = 0; index < source.Length; index++)
63 if (_keyStreamIndex == NdsModcrypt.BlockSize)
69 destination[index] = (byte)(source[index] ^ _keyStream[_keyStreamIndex++]);
83 CryptographicOperations.ZeroMemory(_aesKey);
84 CryptographicOperations.ZeroMemory(_counter);
85 CryptographicOperations.ZeroMemory(_aesCounter);
86 CryptographicOperations.ZeroMemory(_keyStream);
91 private void GenerateKeyStream()
93 for (
int index = 0; index < _counter.Length; index++)
95 _aesCounter[index] = _counter[^(index + 1)];
98 _ = _encryptor.TransformBlock(_aesCounter, 0, _aesCounter.Length, _keyStream, 0);
99 Array.Reverse(_keyStream);
106 private static void AddBlocks(Span<byte> counter, ulong blocks)
108 ulong carry = blocks;
109 for (
int index = 0; index < counter.Length && carry != 0; index++)
111 ulong sum = counter[index] + (carry & 0xFF);
112 counter[index] = (byte)sum;
113 carry = (carry >> 8) + (sum >> 8);
118 throw new ArgumentOutOfRangeException(nameof(blocks),
"The byte offset overflows the 128-bit modcrypt counter.");
124 private static void Increment(Span<byte> counter)
126 for (
int index = 0; index < counter.Length; index++)
129 if (counter[index] != 0)
137#pragma warning restore CA5358