19439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly/* 29439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Copyright (c) 2008-2009, Motorola, Inc. 39439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 49439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * All rights reserved. 59439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 69439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * Redistribution and use in source and binary forms, with or without 79439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * modification, are permitted provided that the following conditions are met: 89439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 99439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * - Redistributions of source code must retain the above copyright notice, 109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * this list of conditions and the following disclaimer. 119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * - Redistributions in binary form must reproduce the above copyright notice, 139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * this list of conditions and the following disclaimer in the documentation 149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * and/or other materials provided with the distribution. 159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * - Neither the name of the Motorola, Inc. nor the names of its contributors 179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * may be used to endorse or promote products derived from this software 189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * without specific prior written permission. 199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * 209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * POSSIBILITY OF SUCH DAMAGE. 319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellypackage javax.obex; 349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyimport java.io.IOException; 369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 379439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly/** 389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * The <code>ObexSession</code> interface characterizes the term 399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * "OBEX Connection" as defined in the IrDA Object Exchange Protocol v1.2, which 409439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * could be the server-side view of an OBEX connection, or the client-side view 419439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * of the same connection, which is established by server's accepting of a 429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * client issued "CONNECT". 439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * <P> 449439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * This interface serves as the common super class for 459439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly * <CODE>ClientSession</CODE> and <CODE>ServerSession</CODE>. 462e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly * @hide 479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly */ 483998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejunpublic class ObexSession { 499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 503998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun protected Authenticator mAuthenticator; 519439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 523998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun protected byte[] mChallengeDigest; 533998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 543998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun /** 553998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * Called when the server received an authentication challenge header. This 563998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * will cause the authenticator to handle the authentication challenge. 5705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun * @param header the header with the authentication challenge 583998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * @return <code>true</code> if the last request should be resent; 593998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * <code>false</code> if the last request should not be resent 603998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * @throws IOException 613998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun */ 623998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun public boolean handleAuthChall(HeaderSet header) throws IOException { 633998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun if (mAuthenticator == null) { 643998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun return false; 653998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 663998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 673998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun /* 683998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * An authentication challenge is made up of one required and two 693998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * optional tag length value triplets. The tag 0x00 is required to be in 703998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * the authentication challenge and it represents the challenge digest 713998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * that was received. The tag 0x01 is the options tag. This tag tracks 723998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * if user ID is required and if full access will be granted. The tag 733998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * 0x02 is the realm, which provides a description of which user name 743998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * and password to use. 753998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun */ 763998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun byte[] challenge = ObexHelper.getTagValue((byte)0x00, header.mAuthChall); 773998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun byte[] option = ObexHelper.getTagValue((byte)0x01, header.mAuthChall); 783998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun byte[] description = ObexHelper.getTagValue((byte)0x02, header.mAuthChall); 793998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 803998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun String realm = null; 813998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun if (description != null) { 823998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun byte[] realmString = new byte[description.length - 1]; 833998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun System.arraycopy(description, 1, realmString, 0, realmString.length); 843998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 853998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun switch (description[0] & 0xFF) { 863998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 873998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun case ObexHelper.OBEX_AUTH_REALM_CHARSET_ASCII: 883998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun // ASCII encoding 893998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun // Fall through 903998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun case ObexHelper.OBEX_AUTH_REALM_CHARSET_ISO_8859_1: 913998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun // ISO-8859-1 encoding 923998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun try { 933998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun realm = new String(realmString, "ISO8859_1"); 943998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } catch (Exception e) { 953998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun throw new IOException("Unsupported Encoding Scheme"); 963998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 973998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun break; 983998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 993998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun case ObexHelper.OBEX_AUTH_REALM_CHARSET_UNICODE: 1003998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun // UNICODE Encoding 1013998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun realm = ObexHelper.convertToUnicode(realmString, false); 1023998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun break; 1033998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1043998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun default: 1053998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun throw new IOException("Unsupported Encoding Scheme"); 1063998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 1073998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 1083998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1093998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun boolean isUserIDRequired = false; 1103998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun boolean isFullAccess = true; 1113998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun if (option != null) { 1123998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun if ((option[0] & 0x01) != 0) { 1133998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun isUserIDRequired = true; 1143998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 1153998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1163998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun if ((option[0] & 0x02) != 0) { 1173998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun isFullAccess = false; 1183998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 1193998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 1203998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1213998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun PasswordAuthentication result = null; 1223998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun header.mAuthChall = null; 1233998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1243998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun try { 1253998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun result = mAuthenticator 1263998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun .onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess); 1273998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } catch (Exception e) { 1283998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun return false; 1293998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 1303998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1313998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun /* 1323998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * If no password is provided then we not resent the request 1333998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun */ 1343998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun if (result == null) { 1353998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun return false; 1363998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 1373998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1383998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun byte[] password = result.getPassword(); 1393998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun if (password == null) { 1403998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun return false; 1413998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 1423998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1433998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun byte[] userName = result.getUserName(); 1443998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1453998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun /* 1463998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * Create the authentication response header. It includes 1 required and 1473998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * 2 option tag length value triples. The required triple has a tag of 1483998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * 0x00 and is the response digest. The first optional tag is 0x01 and 1493998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * represents the user ID. If no user ID is provided, then no user ID 1503998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * will be sent. The second optional tag is 0x02 and is the challenge 1513998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * that was received. This will always be sent 1523998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun */ 1533998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun if (userName != null) { 1543998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun header.mAuthResp = new byte[38 + userName.length]; 1553998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun header.mAuthResp[36] = (byte)0x01; 1563998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun header.mAuthResp[37] = (byte)userName.length; 1573998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun System.arraycopy(userName, 0, header.mAuthResp, 38, userName.length); 1583998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } else { 1593998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun header.mAuthResp = new byte[36]; 1603998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 1613998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1623998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun // Create the secret String 1633998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun byte[] digest = new byte[challenge.length + password.length + 1]; 1643998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun System.arraycopy(challenge, 0, digest, 0, challenge.length); 1653998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun // Insert colon between challenge and password 1663998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun digest[challenge.length] = (byte)0x3A; 1673998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun System.arraycopy(password, 0, digest, challenge.length + 1, password.length); 1683998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1693998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun // Add the Response Digest 1703998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun header.mAuthResp[0] = (byte)0x00; 1713998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun header.mAuthResp[1] = (byte)0x10; 1723998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1733998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun System.arraycopy(ObexHelper.computeMd5Hash(digest), 0, header.mAuthResp, 2, 16); 1743998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1753998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun // Add the challenge 1763998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun header.mAuthResp[18] = (byte)0x02; 1773998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun header.mAuthResp[19] = (byte)0x10; 1783998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun System.arraycopy(challenge, 0, header.mAuthResp, 20, 16); 1793998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1803998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun return true; 1813998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 1823998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 1833998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun /** 1843998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * Called when the server received an authentication response header. This 1853998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * will cause the authenticator to handle the authentication response. 18605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun * @param authResp the authentication response 1873998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * @return <code>true</code> if the response passed; <code>false</code> if 1883998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun * the response failed 1893998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun */ 1903998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun public boolean handleAuthResp(byte[] authResp) { 1913998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun if (mAuthenticator == null) { 1923998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun return false; 1933998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 1943998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun // get the correct password from the application 1953998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun byte[] correctPassword = mAuthenticator.onAuthenticationResponse(ObexHelper.getTagValue( 1963998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun (byte)0x01, authResp)); 1973998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun if (correctPassword == null) { 1983998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun return false; 1993998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 2003998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 2013998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun byte[] temp = new byte[correctPassword.length + 16]; 2023998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 2033998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun System.arraycopy(mChallengeDigest, 0, temp, 0, 16); 2043998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length); 2053998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 2063998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun byte[] correctResponse = ObexHelper.computeMd5Hash(temp); 2073998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun byte[] actualResponse = ObexHelper.getTagValue((byte)0x00, authResp); 2083998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 2093998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun // compare the MD5 hash array . 2103998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun for (int i = 0; i < 16; i++) { 2113998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun if (correctResponse[i] != actualResponse[i]) { 2123998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun return false; 2133998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 2143998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 2153998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun 2163998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun return true; 2173998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun } 2189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly} 219