1+ using System . Buffers ;
2+ using System . Buffers . Binary ;
3+ using System . Security . Cryptography ;
4+ using System . Text ;
5+ using Intersect . Logging ;
6+
7+ namespace Intersect . Network . LiteNetLib ;
8+
9+ public sealed class AesAlgorithm : SymmetricAlgorithm
10+ {
11+ // Encode the plaindata length (1 int), the nonce length (1 byte), and the tag length (1 byte)
12+ private const int HeaderSize = sizeof ( int ) + sizeof ( byte ) * 2 ;
13+
14+ private static KeySizes [ ] LegalBlockSizes ;
15+ private static KeySizes [ ] LegalKeySizes ;
16+
17+ static AesAlgorithm ( )
18+ {
19+ using var aes = Aes . Create ( ) ;
20+ LegalBlockSizes = aes . LegalBlockSizes ;
21+ LegalKeySizes = aes . LegalKeySizes ;
22+ }
23+
24+ private readonly Memory < byte > _key ;
25+
26+ private MemoryHandle _keyHandle ;
27+
28+ private static new bool IsValidSize ( KeySizes sizes , int size )
29+ {
30+ return SymmetricAlgorithm . IsValidSize (
31+ new KeySizes ( sizes . MinSize / 8 , sizes . MaxSize / 8 , sizes . SkipSize / 8 ) ,
32+ size
33+ ) ;
34+ }
35+
36+ public AesAlgorithm ( ReadOnlySpan < byte > key = default )
37+ {
38+ var buffer = RandomNumberGenerator . GetBytes ( 32 ) ;
39+ if ( key != default )
40+ {
41+ var keyLength = key . Length ;
42+ if ( ! LegalKeySizes . Any ( keySizes => IsValidSize ( keySizes , keyLength ) ) )
43+ {
44+ throw new ArgumentException (
45+ $ "{ key . Length } is not a supported key length in bytes.",
46+ nameof ( key )
47+ ) ;
48+ }
49+
50+ if ( ! key . TryCopyTo ( buffer ) )
51+ {
52+ throw new ArgumentException (
53+ "The provided key could not be copied to the internal buffer." ,
54+ nameof ( key )
55+ ) ;
56+ }
57+ }
58+
59+ _key = new Memory < byte > ( buffer ) ;
60+ _keyHandle = _key . Pin ( ) ;
61+ }
62+
63+ public override void Dispose ( )
64+ {
65+ _keyHandle . Dispose ( ) ;
66+ }
67+
68+ public override bool SetKey ( ReadOnlySpan < byte > key ) => key . TryCopyTo ( _key . Span ) ;
69+
70+ public override EncryptionResult TryDecrypt ( ReadOnlySpan < byte > cipherdata , out ReadOnlySpan < byte > plaindata )
71+ {
72+ plaindata = default ;
73+
74+ try
75+ {
76+ var version = cipherdata [ 0 ] ;
77+ cipherdata = cipherdata [ 1 ..] ;
78+
79+ if ( version != 0 )
80+ {
81+ return EncryptionResult . InvalidVersion ;
82+ }
83+
84+ using var aes = Aes . Create ( ) ;
85+ aes . Key = _key . ToArray ( ) ;
86+
87+ var plaindataLength = BinaryPrimitives . ReadInt32LittleEndian ( cipherdata ) ;
88+ cipherdata = cipherdata [ sizeof ( int ) ..] ;
89+
90+ var nonceLength = cipherdata [ 0 ] ;
91+ cipherdata = cipherdata [ 1 ..] ;
92+
93+ var nonce = cipherdata [ ..nonceLength ] ;
94+ cipherdata = cipherdata [ nonceLength ..] ;
95+
96+ aes . IV = nonce . ToArray ( ) ;
97+
98+ var cryptoTransform = aes . CreateDecryptor ( aes . Key , aes . IV ) ;
99+
100+ using MemoryStream memoryStream = new ( cipherdata . ToArray ( ) ) ;
101+ using CryptoStream cryptoStream = new ( memoryStream , cryptoTransform , CryptoStreamMode . Read ) ;
102+ var plaindataBuffer = new byte [ plaindataLength ] ;
103+ cryptoStream . ReadExactly ( plaindataBuffer ) ;
104+ plaindata = plaindataBuffer ;
105+
106+ #if DIAGNOSTIC
107+ Log . Debug ( $ "Decrypting { plaindata . Length } bytes:\n Key={ Convert . ToHexString ( aes . Key ) } Nonce={ Convert . ToHexString ( aes . IV ) } \n plaindata={ Convert . ToHexString ( plaindata ) } \n cipherdata={ Convert . ToHexString ( cipherdata ) } ") ;
108+ #endif
109+
110+ return EncryptionResult . Success ;
111+ }
112+ catch ( Exception exception )
113+ {
114+ Log . Debug ( exception ) ;
115+ return EncryptionResult . Error ;
116+ }
117+ }
118+
119+ public override EncryptionResult TryDecrypt ( ReadOnlySpan < byte > cipherdata , int offset , int length , out ReadOnlySpan < byte > plaindata ) =>
120+ TryDecrypt ( cipherdata [ offset ..( offset + length ) ] , out plaindata ) ;
121+
122+ public override EncryptionResult TryEncrypt ( ReadOnlySpan < byte > plaindata , out ReadOnlySpan < byte > cipherdata )
123+ {
124+ cipherdata = default ;
125+
126+ try
127+ {
128+ using var aes = Aes . Create ( ) ;
129+ aes . Key = _key . ToArray ( ) ;
130+
131+ var nonce = RandomNumberGenerator . GetBytes ( aes . BlockSize / 8 ) ;
132+ aes . IV = nonce ;
133+
134+ using MemoryStream memoryStream = new ( ) ;
135+
136+ // Version
137+ memoryStream . WriteByte ( 0 ) ;
138+
139+ var plaindataLengthBytes = new byte [ sizeof ( int ) ] ;
140+ BinaryPrimitives . WriteInt32LittleEndian ( plaindataLengthBytes , plaindata . Length ) ;
141+ memoryStream . Write ( plaindataLengthBytes ) ;
142+
143+ memoryStream . WriteByte ( ( byte ) nonce . Length ) ;
144+ memoryStream . Write ( nonce ) ;
145+
146+ var cryptoTransform = aes . CreateEncryptor ( aes . Key , aes . IV ) ;
147+ using CryptoStream cryptoStream = new ( memoryStream , cryptoTransform , CryptoStreamMode . Write , true ) ;
148+ cryptoStream . Write ( plaindata ) ;
149+
150+ cryptoStream . Close ( ) ;
151+ memoryStream . Close ( ) ;
152+
153+ cipherdata = memoryStream . ToArray ( ) ;
154+
155+ #if DIAGNOSTIC
156+ Log . Debug ( $ "Encrypting { plaindata . Length } bytes:\n Key={ Convert . ToHexString ( aes . Key ) } Nonce={ Convert . ToHexString ( aes . IV ) } \n plaindata={ Convert . ToHexString ( plaindata ) } \n cipherdata={ Convert . ToHexString ( cipherdata [ ^ plaindata . Length ..] ) } ") ;
157+ #endif
158+
159+ return EncryptionResult . Success ;
160+ }
161+ catch ( Exception exception )
162+ {
163+ Log . Debug ( exception ) ;
164+ return EncryptionResult . Error ;
165+ }
166+ }
167+
168+ public override EncryptionResult TryEncrypt ( ReadOnlySpan < byte > plaindata , int offset , int length , out ReadOnlySpan < byte > cipherdata ) =>
169+ TryEncrypt ( plaindata [ offset ..( offset + length ) ] , out cipherdata ) ;
170+ }
0 commit comments