File: Crypt.cs

package info (click to toggle)
mysql-connector-net 6.4.3-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 6,160 kB
  • ctags: 8,552
  • sloc: cs: 63,689; xml: 7,505; sql: 345; makefile: 50; ansic: 40
file content (340 lines) | stat: -rw-r--r-- 13,423 bytes parent folder | download | duplicates (2)
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
// Copyright (c) 2004-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
//
// MySQL Connector/NET is licensed under the terms of the GPLv2
// <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most 
// MySQL Connectors. There are special exceptions to the terms and 
// conditions of the GPLv2 as it is applied to this software, see the 
// FLOSS License Exception
// <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
//
// This program is free software; you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published 
// by the Free Software Foundation; version 2 of the License.
//
// This program is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
//
// You should have received a copy of the GNU General Public License along 
// with this program; if not, write to the Free Software Foundation, Inc., 
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA

using System;
using System.Globalization;
using System.Text;
using MySql.Data.Common;
using MySql.Data.MySqlClient.Properties;
//using System.Security.Cryptography;
//#if CF
//using OpenNETCF.Security.Cryptography;
//#endif

namespace MySql.Data.MySqlClient
{
    /// <summary>
    /// Summary description for Crypt.
    /// </summary>
    internal class Crypt
    {
        // private ctor to prevent creating a default one
        private Crypt()
        {
        }

/*		private void Create41Password( string password )
		{
			SHA1 sha = new SHA1CryptoServiceProvider(); 
			byte[] firstPassBytes = sha.ComputeHash( System.Text.Encoding.Default.GetBytes( password ));

			byte[] salt = packet.GetBuffer();
			byte[] input = new byte[ firstPassBytes.Length + 4 ];
			salt.CopyTo( input, 0 );
			firstPassBytes.CopyTo( input, 4 );
			byte[] outPass = new byte[100];
			byte[] secondPassBytes = sha.ComputeHash( input );

			byte[] cryptSalt = new byte[20];
			Security.ArrayCrypt( salt, 4, cryptSalt, 0, secondPassBytes, 20 );

			Security.ArrayCrypt( cryptSalt, 0, firstPassBytes, 0, firstPassBytes, 20 );

			// send the packet
			packet = CreatePacket(null);
			packet.Write( firstPassBytes, 0, 20 );
			SendPacket(packet);
		}
*/

        /// <summary>
        /// Simple XOR scramble
        /// </summary>
        /// <param name="from">Source array</param>
        /// <param name="fromIndex">Index inside source array</param>
        /// <param name="to">Destination array</param>
        /// <param name="toIndex">Index inside destination array</param>
        /// <param name="password">Password used to xor the bits</param>
        /// <param name="length">Number of bytes to scramble</param>
        private static void XorScramble(byte[] from, int fromIndex, byte[] to, int toIndex,
                                        byte[] password, int length)
        {
            // make sure we were called properly
            if (fromIndex < 0 || fromIndex >= from.Length)
                throw new ArgumentException(Resources.IndexMustBeValid, "fromIndex");
            if ((fromIndex + length) > from.Length)
                throw new ArgumentException(Resources.FromAndLengthTooBig, "fromIndex");
            if (from == null)
                throw new ArgumentException(Resources.BufferCannotBeNull, "from");
            if (to == null)
                throw new ArgumentException(Resources.BufferCannotBeNull, "to");
            if (toIndex < 0 || toIndex >= to.Length)
                throw new ArgumentException(Resources.IndexMustBeValid, "toIndex");
            if ((toIndex + length) > to.Length)
                throw new ArgumentException(Resources.IndexAndLengthTooBig, "toIndex");
            if (password == null || password.Length < length)
                throw new ArgumentException(Resources.PasswordMustHaveLegalChars, "password");
            if (length < 0)
                throw new ArgumentException(Resources.ParameterCannotBeNegative, "count");

            // now perform the work
            for (int i = 0; i < length; i++)
                to[toIndex++] = (byte) (from[fromIndex++] ^ password[i]);
        }

        /// <summary>
        /// Generate a scrambled password for 4.1.0 using new passwords
        /// </summary>
        /// <param name="password">The password to scramble</param>
        /// <param name="seedBytes">The seedbytes used to scramble</param>
        /// <returns>Array of bytes containing the scrambled password</returns>
        public static byte[] Get410Password(string password, byte[] seedBytes)
        {
            SHA1Hash sha = new SHA1Hash();
            //SHA1 sha = new SHA1CryptoServiceProvider(); 

            // clean it and then digest it
            password = password.Replace(" ", "").Replace("\t", "");
            byte[] passBytes = Encoding.Default.GetBytes(password);
            byte[] firstPass = sha.ComputeHash(passBytes);

            byte[] input = new byte[24];
            Array.Copy(seedBytes, 0, input, 0, 4);
            Array.Copy(firstPass, 0, input, 4, 20);
            byte[] secondPass = sha.ComputeHash(input);

            byte[] scrambledBuff = new byte[20];
            XorScramble(seedBytes, 4, scrambledBuff, 0, secondPass, 20);

            byte[] finalBuff = new byte[20];
            XorScramble(scrambledBuff, 0, finalBuff, 0, firstPass, 20);

            return finalBuff;
        }

        /// <summary>
        /// Generates a proper hash for old style 4.1.0 passwords.  This would be used
        /// if a 4.1.0 server contained old 16 byte hashes.
        /// </summary>
        /// <param name="password">The password to hash</param>
        /// <param name="seedBytes">Seed bytes received from the server</param>
        /// <returns>Byte array containing the password hash</returns>
        public static byte[] GetOld410Password(string password, byte[] seedBytes)
        {
            long[] passwordHash = Hash(password);
            string passHex = String.Format(CultureInfo.InvariantCulture,
                                           "{0,8:X}{1,8:X}", passwordHash[0], passwordHash[1]);

            int[] salt = getSaltFromPassword(passHex);

            // compute binary password
            byte[] binaryPassword = new byte[20];
            int offset = 0;
            for (int i = 0; i < 2; i++)
            {
                int val = salt[i];

                for (int t = 3; t >= 0; t--)
                {
                    binaryPassword[t + offset] = (byte) (val%256);
                    val >>= 8; /* Scroll 8 bits to get next part*/
                }

                offset += 4;
            }

            //SHA1 sha = new SHA1CryptoServiceProvider(); 
            SHA1Hash sha = new SHA1Hash();
            byte[] temp = new byte[8];
            Buffer.BlockCopy(binaryPassword, 0, temp, 0, 8);
            byte[] binaryHash = sha.ComputeHash(temp);

            byte[] scrambledBuff = new byte[20];
            XorScramble(seedBytes, 4, scrambledBuff, 0, binaryHash, 20);

            string scrambleString = Encoding.Default.GetString(scrambledBuff, 0, scrambledBuff.Length).Substring(0, 8);

            long[] hashPass = Hash(password);
            long[] hashMessage = Hash(scrambleString);

            long max = 0x3FFFFFFFL;
            byte[] to = new byte[20];
            int msgPos = 0;
            int msgLength = scrambleString.Length;
            int toPos = 0;
            long seed1 = (hashPass[0] ^ hashMessage[0])%max;
            long seed2 = (hashPass[1] ^ hashMessage[1])%max;

            while (msgPos++ < msgLength)
                to[toPos++] = (byte) (Math.Floor(rand(ref seed1, ref seed2, max)*31) + 64);

            /* Make it harder to break */
            byte extra = (byte) (Math.Floor(rand(ref seed1, ref seed2, max)*31));

            for (int i = 0; i < 8; i++)
                to[i] ^= extra;

            return to;
        }

        /// <summary>
        /// Returns a byte array containing the proper encryption of the 
        /// given password/seed according to the new 4.1.1 authentication scheme.
        /// </summary>
        /// <param name="password"></param>
        /// <param name="seed"></param>
        /// <returns></returns>
        public static byte[] Get411Password(string password, string seed)
        {
            // if we have no password, then we just return 1 zero byte
            if (password.Length == 0) return new byte[1];

            //SHA1 sha = new SHA1CryptoServiceProvider(); 
            SHA1Hash sha = new SHA1Hash();

            byte[] firstHash = sha.ComputeHash(Encoding.Default.GetBytes(password));
            byte[] secondHash = sha.ComputeHash(firstHash);
            byte[] seedBytes = Encoding.Default.GetBytes(seed);

            byte[] input = new byte[seedBytes.Length + secondHash.Length];
            Array.Copy(seedBytes, 0, input, 0, seedBytes.Length);
            Array.Copy(secondHash, 0, input, seedBytes.Length, secondHash.Length);
            byte[] thirdHash = sha.ComputeHash(input);

            byte[] finalHash = new byte[thirdHash.Length + 1];
            finalHash[0] = 0x14;
            Array.Copy(thirdHash, 0, finalHash, 1, thirdHash.Length);

            for (int i = 1; i < finalHash.Length; i++)
                finalHash[i] = (byte) (finalHash[i] ^ firstHash[i - 1]);
            return finalHash;
        }

        private static int[] getSaltFromPassword(String password)
        {
            int[] result = new int[6];

            if (password == null || password.Length == 0)
                return result;

            int resultPos = 0;
            int pos = 0;

            while (pos < password.Length)
            {
                int val = 0;

                for (int i = 0; i < 8; i++)
                    val = (val << 4) + HexValue(password[pos++]);

                result[resultPos++] = val;
            }

            return result;
        }

        private static int HexValue(char c)
        {
            if (c >= 'A' && c <= 'Z') return (c - 'A') + 10;
            if (c >= 'a' && c <= 'z') return (c - 'a') + 10;
            return c - '0';
        }


        private static double rand(ref long seed1, ref long seed2, long max)
        {
            seed1 = (seed1*3) + seed2;
            seed1 %= max;
            seed2 = (seed1 + seed2 + 33)%max;
            return (seed1/(double) max);
        }

        /// <summary>
        /// Encrypts a password using the MySql encryption scheme
        /// </summary>
        /// <param name="password">The password to encrypt</param>
        /// <param name="seed">The encryption seed the server gave us</param>
        /// <param name="new_ver">Indicates if we should use the old or new encryption scheme</param>
        /// <returns></returns>
        public static String EncryptPassword(String password, String seed, bool new_ver)
        {
            long max = 0x3fffffff;
            if (! new_ver)
                max = 0x01FFFFFF;
            if (password == null || password.Length == 0)
                return password;

            long[] hash_seed = Hash(seed);
            long[] hash_pass = Hash(password);

            long seed1 = (hash_seed[0] ^ hash_pass[0])%max;
            long seed2 = (hash_seed[1] ^ hash_pass[1])%max;
            if (! new_ver)
                seed2 = seed1/2;

            char[] scrambled = new char[seed.Length];
            for (int x = 0; x < seed.Length; x++)
            {
                double r = rand(ref seed1, ref seed2, max);
                scrambled[x] = (char) (Math.Floor(r*31) + 64);
            }

            if (new_ver)
            {
                /* Make it harder to break */
                char extra = (char) Math.Floor(rand(ref seed1, ref seed2, max)*31);
                for (int x = 0; x < scrambled.Length; x++)
                    scrambled[x] ^= extra;
            }

            return new string(scrambled);
        }

        /// <summary>
        /// Hashes a password using the algorithm from Monty's code.
        /// The first element in the return is the result of the "old" hash.
        /// The second element is the rest of the "new" hash.
        /// </summary>
        /// <param name="P">Password to be hashed</param>
        /// <returns>Two element array containing the hashed values</returns>
        private static long[] Hash(String P)
        {
            long val1 = 1345345333;
            long val2 = 0x12345671;
            long inc = 7;

            for (int i = 0; i < P.Length; i++)
            {
                if (P[i] == ' ' || P[i] == '\t') continue;
                long temp = (0xff & P[i]);
                val1 ^= (((val1 & 63) + inc)*temp) + (val1 << 8);
                val2 += (val2 << 8) ^ val1;
                inc += temp;
            }

            long[] hash = new long[2];
            hash[0] = val1 & 0x7fffffff;
            hash[1] = val2 & 0x7fffffff;
            return hash;
        }
    }
}