EMMA Coverage Report (generated Mon Jul 24 20:33:06 CDT 2006)
[all classes][com.mysql.jdbc]

COVERAGE SUMMARY FOR SOURCE FILE [Security.java]

nameclass, %method, %block, %line, %
Security.java100% (1/1)9%   (1/11)13%  (51/394)14%  (13/90)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Security100% (1/1)9%   (1/11)13%  (51/394)14%  (13/90)
Security (): void 0%   (0/1)0%   (0/3)0%   (0/2)
charVal (char): int 0%   (0/1)0%   (0/28)0%   (0/1)
createKeyFromOldPassword (String): byte [] 0%   (0/1)0%   (0/10)0%   (0/3)
getBinaryPassword (int [], boolean): byte [] 0%   (0/1)0%   (0/84)0%   (0/20)
getSaltFromPassword (String): int [] 0%   (0/1)0%   (0/77)0%   (0/18)
longToHex (long): String 0%   (0/1)0%   (0/40)0%   (0/10)
makeScrambledPassword (String): String 0%   (0/1)0%   (0/24)0%   (0/5)
passwordCrypt (byte [], byte [], byte [], int): void 0%   (0/1)0%   (0/23)0%   (0/5)
passwordHashStage1 (String): byte [] 0%   (0/1)0%   (0/38)0%   (0/9)
passwordHashStage2 (byte [], byte []): byte [] 0%   (0/1)0%   (0/16)0%   (0/4)
scramble411 (String, String): byte [] 100% (1/1)100% (51/51)100% (13/13)

1/*
2 Copyright (C) 2002-2004 MySQL AB
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of version 2 of the GNU General Public License as 
6 published by the Free Software Foundation.
7 
8 There are special exceptions to the terms and conditions of the GPL 
9 as it is applied to this software. View the full text of the 
10 exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
11 software distribution.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 
22 
23 
24 */
25package com.mysql.jdbc;
26 
27import java.security.MessageDigest;
28import java.security.NoSuchAlgorithmException;
29 
30/**
31 * Methods for doing secure authentication with MySQL-4.1 and newer.
32 * 
33 * @author Mark Matthews
34 * 
35 * @version $Id: Security.java 3726 2005-05-19 15:52:24Z mmatthews $
36 */
37class Security {
38        private static final char PVERSION41_CHAR = '*';
39 
40        private static final int SHA1_HASH_SIZE = 20;
41 
42        /**
43         * Returns hex value for given char
44         */
45        private static int charVal(char c) {
46                return ((c >= '0') && (c <= '9')) ? (c - '0')
47                                : (((c >= 'A') && (c <= 'Z')) ? (c - 'A' + 10) : (c - 'a' + 10));
48        }
49 
50        /*
51         * Convert password in salted form to binary string password and hash-salt
52         * For old password this involes one more hashing
53         * 
54         * SYNOPSIS get_hash_and_password() salt IN Salt to convert from pversion IN
55         * Password version to use hash OUT Store zero ended hash here bin_password
56         * OUT Store binary password here (no zero at the end)
57         * 
58         * RETURN 0 for pre 4.1 passwords !0 password version char for newer
59         * passwords
60         */
61 
62        /**
63         * Creates key from old password to decode scramble Used in 4.1
64         * authentication with passwords stored pre-4.1 hashing.
65         * 
66         * @param passwd
67         *            the password to create the key from
68         * 
69         * @return 20 byte generated key
70         * 
71         * @throws NoSuchAlgorithmException
72         *             if the message digest 'SHA-1' is not available.
73         */
74        static byte[] createKeyFromOldPassword(String passwd)
75                        throws NoSuchAlgorithmException {
76                /* At first hash password to the string stored in password */
77                passwd = makeScrambledPassword(passwd);
78 
79                /* Now convert it to the salt form */
80                int[] salt = getSaltFromPassword(passwd);
81 
82                /* Finally get hash and bin password from salt */
83                return getBinaryPassword(salt, false);
84        }
85 
86        /**
87         * DOCUMENT ME!
88         * 
89         * @param salt
90         *            DOCUMENT ME!
91         * @param usingNewPasswords
92         *            DOCUMENT ME!
93         * 
94         * @return DOCUMENT ME!
95         * 
96         * @throws NoSuchAlgorithmException
97         *             if the message digest 'SHA-1' is not available.
98         */
99        static byte[] getBinaryPassword(int[] salt, boolean usingNewPasswords)
100                        throws NoSuchAlgorithmException {
101                int val = 0;
102 
103                byte[] binaryPassword = new byte[SHA1_HASH_SIZE]; /*
104                                                                                                                         * Binary password
105                                                                                                                         * loop pointer
106                                                                                                                         */
107 
108                if (usingNewPasswords) /* New password version assumed */{
109                        int pos = 0;
110 
111                        for (int i = 0; i < 4; i++) /* Iterate over these elements */{
112                                val = salt[i];
113 
114                                for (int t = 3; t >= 0; t--) {
115                                        binaryPassword[pos++] = (byte) (val & 255);
116                                        val >>= 8; /* Scroll 8 bits to get next part */
117                                }
118                        }
119 
120                        return binaryPassword;
121                }
122 
123                int offset = 0;
124 
125                for (int i = 0; i < 2; i++) /* Iterate over these elements */{
126                        val = salt[i];
127 
128                        for (int t = 3; t >= 0; t--) {
129                                binaryPassword[t + offset] = (byte) (val % 256);
130                                val >>= 8; /* Scroll 8 bits to get next part */
131                        }
132 
133                        offset += 4;
134                }
135 
136                MessageDigest md = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$
137 
138                md.update(binaryPassword, 0, 8);
139 
140                return md.digest();
141        }
142 
143        private static int[] getSaltFromPassword(String password) {
144                int[] result = new int[6];
145 
146                if ((password == null) || (password.length() == 0)) {
147                        return result;
148                }
149 
150                if (password.charAt(0) == PVERSION41_CHAR) {
151                        // new password
152                        String saltInHex = password.substring(1, 5);
153 
154                        int val = 0;
155 
156                        for (int i = 0; i < 4; i++) {
157                                val = (val << 4) + charVal(saltInHex.charAt(i));
158                        }
159 
160                        return result;
161                }
162 
163                int resultPos = 0;
164                int pos = 0;
165                int length = password.length();
166 
167                while (pos < length) {
168                        int val = 0;
169 
170                        for (int i = 0; i < 8; i++) {
171                                val = (val << 4) + charVal(password.charAt(pos++));
172                        }
173 
174                        result[resultPos++] = val;
175                }
176 
177                return result;
178        }
179 
180        private static String longToHex(long val) {
181                String longHex = Long.toHexString(val);
182 
183                int length = longHex.length();
184 
185                if (length < 8) {
186                        int padding = 8 - length;
187                        StringBuffer buf = new StringBuffer();
188 
189                        for (int i = 0; i < padding; i++) {
190                                buf.append("0"); //$NON-NLS-1$
191                        }
192 
193                        buf.append(longHex);
194 
195                        return buf.toString();
196                }
197 
198                return longHex.substring(0, 8);
199        }
200 
201        /**
202         * Creates password to be stored in user database from raw string.
203         * 
204         * Handles Pre-MySQL 4.1 passwords.
205         * 
206         * @param password
207         *            plaintext password
208         * 
209         * @return scrambled password
210         * 
211         * @throws NoSuchAlgorithmException
212         *             if the message digest 'SHA-1' is not available.
213         */
214        static String makeScrambledPassword(String password)
215                        throws NoSuchAlgorithmException {
216                long[] passwordHash = Util.newHash(password);
217                StringBuffer scramble = new StringBuffer();
218 
219                scramble.append(longToHex(passwordHash[0]));
220                scramble.append(longToHex(passwordHash[1]));
221 
222                return scramble.toString();
223        }
224 
225        /**
226         * Encrypt/Decrypt function used for password encryption in authentication
227         * 
228         * Simple XOR is used here but it is OK as we crypt random strings
229         * 
230         * @param from
231         *            IN Data for encryption
232         * @param to
233         *            OUT Encrypt data to the buffer (may be the same)
234         * @param password
235         *            IN Password used for encryption (same length)
236         * @param length
237         *            IN Length of data to encrypt
238         */
239        static void passwordCrypt(byte[] from, byte[] to, byte[] password,
240                        int length) {
241                int pos = 0;
242 
243                while ((pos < from.length) && (pos < length)) {
244                        to[pos] = (byte) (from[pos] ^ password[pos]);
245                        pos++;
246                }
247        }
248 
249        /**
250         * Stage one password hashing, used in MySQL 4.1 password handling
251         * 
252         * @param password
253         *            plaintext password
254         * 
255         * @return stage one hash of password
256         * 
257         * @throws NoSuchAlgorithmException
258         *             if the message digest 'SHA-1' is not available.
259         */
260        static byte[] passwordHashStage1(String password)
261                        throws NoSuchAlgorithmException {
262                MessageDigest md = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$
263                StringBuffer cleansedPassword = new StringBuffer();
264 
265                int passwordLength = password.length();
266 
267                for (int i = 0; i < passwordLength; i++) {
268                        char c = password.charAt(i);
269 
270                        if ((c == ' ') || (c == '\t')) {
271                                continue; /* skip space in password */
272                        }
273 
274                        cleansedPassword.append(c);
275                }
276 
277                return md.digest(cleansedPassword.toString().getBytes());
278        }
279 
280        /**
281         * Stage two password hashing used in MySQL 4.1 password handling
282         * 
283         * @param hash
284         *            from passwordHashStage1
285         * @param salt
286         *            salt used for stage two hashing
287         * 
288         * @return result of stage two password hash
289         * 
290         * @throws NoSuchAlgorithmException
291         *             if the message digest 'SHA-1' is not available.
292         */
293        static byte[] passwordHashStage2(byte[] hashedPassword, byte[] salt)
294                        throws NoSuchAlgorithmException {
295                MessageDigest md = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$
296 
297                // hash 4 bytes of salt
298                md.update(salt, 0, 4);
299 
300                md.update(hashedPassword, 0, SHA1_HASH_SIZE);
301 
302                return md.digest();
303        }
304 
305        // SERVER: public_seed=create_random_string()
306        // send(public_seed)
307        //
308        // CLIENT: recv(public_seed)
309        // hash_stage1=sha1("password")
310        // hash_stage2=sha1(hash_stage1)
311        // reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
312        //
313        // // this three steps are done in scramble()
314        //
315        // send(reply)
316        //
317        //
318        // SERVER: recv(reply)
319        // hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
320        // candidate_hash2=sha1(hash_stage1)
321        // check(candidate_hash2==hash_stage2)
322        static byte[] scramble411(String password, String seed)
323                        throws NoSuchAlgorithmException {
324                MessageDigest md = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$
325 
326                byte[] passwordHashStage1 = md.digest(password.getBytes());
327                md.reset();
328 
329                byte[] passwordHashStage2 = md.digest(passwordHashStage1);
330                md.reset();
331 
332                byte[] seedAsBytes = seed.getBytes(); // for debugging
333                md.update(seedAsBytes);
334                md.update(passwordHashStage2);
335 
336                byte[] toBeXord = md.digest();
337 
338                int numToXor = toBeXord.length;
339 
340                for (int i = 0; i < numToXor; i++) {
341                        toBeXord[i] = (byte) (toBeXord[i] ^ passwordHashStage1[i]);
342                }
343 
344                return toBeXord;
345        }
346 
347        /**
348         * Prevent construction.
349         */
350        private Security() {
351                super();
352        }
353}

[all classes][com.mysql.jdbc]
EMMA 2.0.4217 (C) Vladimir Roubtsov