@@ -93,16 +93,65 @@ private void DeriveKeys(out byte[] encKey, out byte[] macKey)
9393 byte [ ] master = LoadOrCreate ( Path . Combine ( _dir , "secret.bin" ) , 32 ) ;
9494 byte [ ] salt = LoadOrCreate ( Path . Combine ( _dir , "salt.bin" ) , 16 ) ;
9595 string password = Convert . ToBase64String ( master ) + "|" + MachineId ( ) ;
96- using ( var kdf = new Rfc2898DeriveBytes ( password , salt , Iterations ) )
96+ #if UNITY_2021_2_OR_NEWER
97+ using ( var kdf = new Rfc2898DeriveBytes ( password , salt , Iterations , HashAlgorithmName . SHA256 ) )
9798 {
9899 byte [ ] material = kdf . GetBytes ( 64 ) ;
99100 encKey = new byte [ 32 ] ;
100101 macKey = new byte [ 32 ] ;
101102 Buffer . BlockCopy ( material , 0 , encKey , 0 , 32 ) ;
102103 Buffer . BlockCopy ( material , 32 , macKey , 0 , 32 ) ;
103104 }
105+ #else
106+ // Unity 2020.3 (netstandard2.1) has no 4-arg Rfc2898DeriveBytes overload, so
107+ // implement PBKDF2-HMAC-SHA256 (RFC 2898) manually to keep key derivation
108+ // byte-identical with the 2021.2+ path (a 3-arg SHA1 derivation would make
109+ // existing ciphertext fail MAC validation).
110+ byte [ ] material = Pbkdf2Sha256 ( password , salt , Iterations , 64 ) ;
111+ encKey = new byte [ 32 ] ;
112+ macKey = new byte [ 32 ] ;
113+ Buffer . BlockCopy ( material , 0 , encKey , 0 , 32 ) ;
114+ Buffer . BlockCopy ( material , 32 , macKey , 0 , 32 ) ;
115+ #endif
104116 }
105117
118+ #if ! UNITY_2021_2_OR_NEWER
119+ /// <summary>PBKDF2 with HMAC-SHA256 (RFC 2898), matching the .NET 4-arg Rfc2898DeriveBytes output.</summary>
120+ private static byte [ ] Pbkdf2Sha256 ( string password , byte [ ] salt , int iterations , int numBytes )
121+ {
122+ var prf = new System . Security . Cryptography . HMACSHA256 (
123+ System . Text . Encoding . UTF8 . GetBytes ( password ) ) ;
124+ int hLen = prf . HashSize / 8 ;
125+ int blocks = ( numBytes + hLen - 1 ) / hLen ;
126+
127+ var output = new byte [ blocks * hLen ] ;
128+ var saltPlusOne = new byte [ salt . Length + 4 ] ;
129+
130+ for ( int block = 1 ; block <= blocks ; block ++ )
131+ {
132+ Buffer . BlockCopy ( salt , 0 , saltPlusOne , 0 , salt . Length ) ;
133+ saltPlusOne [ salt . Length ] = ( byte ) ( ( block >> 24 ) & 0xFF ) ;
134+ saltPlusOne [ salt . Length + 1 ] = ( byte ) ( ( block >> 16 ) & 0xFF ) ;
135+ saltPlusOne [ salt . Length + 2 ] = ( byte ) ( ( block >> 8 ) & 0xFF ) ;
136+ saltPlusOne [ salt . Length + 3 ] = ( byte ) ( block & 0xFF ) ;
137+
138+ byte [ ] u = prf . ComputeHash ( saltPlusOne ) ;
139+ byte [ ] t = ( byte [ ] ) u . Clone ( ) ;
140+ for ( int i = 1 ; i < iterations ; i ++ )
141+ {
142+ u = prf . ComputeHash ( u ) ;
143+ for ( int j = 0 ; j < hLen ; j ++ ) t [ j ] ^= u [ j ] ;
144+ }
145+ Buffer . BlockCopy ( t , 0 , output , ( block - 1 ) * hLen , hLen ) ;
146+ }
147+
148+ prf . Dispose ( ) ;
149+ var result = new byte [ numBytes ] ;
150+ Buffer . BlockCopy ( output , 0 , result , 0 , numBytes ) ;
151+ return result ;
152+ }
153+ #endif
154+
106155 private byte [ ] Encrypt ( byte [ ] plaintext )
107156 {
108157 DeriveKeys ( out byte [ ] encKey , out byte [ ] macKey ) ;
0 commit comments