xref: /aosp_15_r20/external/obex/src/com/android/obex/ObexSession.java (revision ff33b0cc4d4457a0bf8b25e5c2b4726ad6df5d1d)
1*ff33b0ccSKyunglyul Hyun /*
2*ff33b0ccSKyunglyul Hyun  * Copyright (c) 2008-2009, Motorola, Inc.
3*ff33b0ccSKyunglyul Hyun  *
4*ff33b0ccSKyunglyul Hyun  * All rights reserved.
5*ff33b0ccSKyunglyul Hyun  *
6*ff33b0ccSKyunglyul Hyun  * Redistribution and use in source and binary forms, with or without
7*ff33b0ccSKyunglyul Hyun  * modification, are permitted provided that the following conditions are met:
8*ff33b0ccSKyunglyul Hyun  *
9*ff33b0ccSKyunglyul Hyun  * - Redistributions of source code must retain the above copyright notice,
10*ff33b0ccSKyunglyul Hyun  * this list of conditions and the following disclaimer.
11*ff33b0ccSKyunglyul Hyun  *
12*ff33b0ccSKyunglyul Hyun  * - Redistributions in binary form must reproduce the above copyright notice,
13*ff33b0ccSKyunglyul Hyun  * this list of conditions and the following disclaimer in the documentation
14*ff33b0ccSKyunglyul Hyun  * and/or other materials provided with the distribution.
15*ff33b0ccSKyunglyul Hyun  *
16*ff33b0ccSKyunglyul Hyun  * - Neither the name of the Motorola, Inc. nor the names of its contributors
17*ff33b0ccSKyunglyul Hyun  * may be used to endorse or promote products derived from this software
18*ff33b0ccSKyunglyul Hyun  * without specific prior written permission.
19*ff33b0ccSKyunglyul Hyun  *
20*ff33b0ccSKyunglyul Hyun  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21*ff33b0ccSKyunglyul Hyun  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22*ff33b0ccSKyunglyul Hyun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23*ff33b0ccSKyunglyul Hyun  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24*ff33b0ccSKyunglyul Hyun  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25*ff33b0ccSKyunglyul Hyun  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26*ff33b0ccSKyunglyul Hyun  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27*ff33b0ccSKyunglyul Hyun  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28*ff33b0ccSKyunglyul Hyun  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29*ff33b0ccSKyunglyul Hyun  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30*ff33b0ccSKyunglyul Hyun  * POSSIBILITY OF SUCH DAMAGE.
31*ff33b0ccSKyunglyul Hyun  */
32*ff33b0ccSKyunglyul Hyun 
33*ff33b0ccSKyunglyul Hyun package com.android.obex;
34*ff33b0ccSKyunglyul Hyun 
35*ff33b0ccSKyunglyul Hyun import android.util.Log;
36*ff33b0ccSKyunglyul Hyun 
37*ff33b0ccSKyunglyul Hyun import java.io.IOException;
38*ff33b0ccSKyunglyul Hyun 
39*ff33b0ccSKyunglyul Hyun /**
40*ff33b0ccSKyunglyul Hyun  * The <code>ObexSession</code> interface characterizes the term
41*ff33b0ccSKyunglyul Hyun  * "OBEX Connection" as defined in the IrDA Object Exchange Protocol v1.2, which
42*ff33b0ccSKyunglyul Hyun  * could be the server-side view of an OBEX connection, or the client-side view
43*ff33b0ccSKyunglyul Hyun  * of the same connection, which is established by server's accepting of a
44*ff33b0ccSKyunglyul Hyun  * client issued "CONNECT".
45*ff33b0ccSKyunglyul Hyun  * <P>
46*ff33b0ccSKyunglyul Hyun  * This interface serves as the common super class for
47*ff33b0ccSKyunglyul Hyun  * <CODE>ClientSession</CODE> and <CODE>ServerSession</CODE>.
48*ff33b0ccSKyunglyul Hyun  */
49*ff33b0ccSKyunglyul Hyun public class ObexSession {
50*ff33b0ccSKyunglyul Hyun 
51*ff33b0ccSKyunglyul Hyun     private static final String TAG = "ObexSession";
52*ff33b0ccSKyunglyul Hyun     private static final boolean V = ObexHelper.VDBG;
53*ff33b0ccSKyunglyul Hyun 
54*ff33b0ccSKyunglyul Hyun     protected Authenticator mAuthenticator;
55*ff33b0ccSKyunglyul Hyun 
56*ff33b0ccSKyunglyul Hyun     protected byte[] mChallengeDigest;
57*ff33b0ccSKyunglyul Hyun 
58*ff33b0ccSKyunglyul Hyun     /**
59*ff33b0ccSKyunglyul Hyun      * Called when the server received an authentication challenge header. This
60*ff33b0ccSKyunglyul Hyun      * will cause the authenticator to handle the authentication challenge.
61*ff33b0ccSKyunglyul Hyun      * @param header the header with the authentication challenge
62*ff33b0ccSKyunglyul Hyun      * @return <code>true</code> if the last request should be resent;
63*ff33b0ccSKyunglyul Hyun      *         <code>false</code> if the last request should not be resent
64*ff33b0ccSKyunglyul Hyun      * @throws IOException
65*ff33b0ccSKyunglyul Hyun      */
handleAuthChall(HeaderSet header)66*ff33b0ccSKyunglyul Hyun     public boolean handleAuthChall(HeaderSet header) throws IOException {
67*ff33b0ccSKyunglyul Hyun         if (mAuthenticator == null) {
68*ff33b0ccSKyunglyul Hyun             return false;
69*ff33b0ccSKyunglyul Hyun         }
70*ff33b0ccSKyunglyul Hyun 
71*ff33b0ccSKyunglyul Hyun         /*
72*ff33b0ccSKyunglyul Hyun          * An authentication challenge is made up of one required and two
73*ff33b0ccSKyunglyul Hyun          * optional tag length value triplets. The tag 0x00 is required to be in
74*ff33b0ccSKyunglyul Hyun          * the authentication challenge and it represents the challenge digest
75*ff33b0ccSKyunglyul Hyun          * that was received. The tag 0x01 is the options tag. This tag tracks
76*ff33b0ccSKyunglyul Hyun          * if user ID is required and if full access will be granted. The tag
77*ff33b0ccSKyunglyul Hyun          * 0x02 is the realm, which provides a description of which user name
78*ff33b0ccSKyunglyul Hyun          * and password to use.
79*ff33b0ccSKyunglyul Hyun          */
80*ff33b0ccSKyunglyul Hyun         byte[] challenge = ObexHelper.getTagValue((byte)0x00, header.mAuthChall);
81*ff33b0ccSKyunglyul Hyun         byte[] option = ObexHelper.getTagValue((byte)0x01, header.mAuthChall);
82*ff33b0ccSKyunglyul Hyun         byte[] description = ObexHelper.getTagValue((byte)0x02, header.mAuthChall);
83*ff33b0ccSKyunglyul Hyun 
84*ff33b0ccSKyunglyul Hyun         String realm = null;
85*ff33b0ccSKyunglyul Hyun         if (description != null) {
86*ff33b0ccSKyunglyul Hyun             byte[] realmString = new byte[description.length - 1];
87*ff33b0ccSKyunglyul Hyun             System.arraycopy(description, 1, realmString, 0, realmString.length);
88*ff33b0ccSKyunglyul Hyun 
89*ff33b0ccSKyunglyul Hyun             switch (description[0] & 0xFF) {
90*ff33b0ccSKyunglyul Hyun 
91*ff33b0ccSKyunglyul Hyun                 case ObexHelper.OBEX_AUTH_REALM_CHARSET_ASCII:
92*ff33b0ccSKyunglyul Hyun                     // ASCII encoding
93*ff33b0ccSKyunglyul Hyun                     // Fall through
94*ff33b0ccSKyunglyul Hyun                 case ObexHelper.OBEX_AUTH_REALM_CHARSET_ISO_8859_1:
95*ff33b0ccSKyunglyul Hyun                     // ISO-8859-1 encoding
96*ff33b0ccSKyunglyul Hyun                     try {
97*ff33b0ccSKyunglyul Hyun                         realm = new String(realmString, "ISO8859_1");
98*ff33b0ccSKyunglyul Hyun                     } catch (Exception e) {
99*ff33b0ccSKyunglyul Hyun                         throw new IOException("Unsupported Encoding Scheme");
100*ff33b0ccSKyunglyul Hyun                     }
101*ff33b0ccSKyunglyul Hyun                     break;
102*ff33b0ccSKyunglyul Hyun 
103*ff33b0ccSKyunglyul Hyun                 case ObexHelper.OBEX_AUTH_REALM_CHARSET_UNICODE:
104*ff33b0ccSKyunglyul Hyun                     // UNICODE Encoding
105*ff33b0ccSKyunglyul Hyun                     realm = ObexHelper.convertToUnicode(realmString, false);
106*ff33b0ccSKyunglyul Hyun                     break;
107*ff33b0ccSKyunglyul Hyun 
108*ff33b0ccSKyunglyul Hyun                 default:
109*ff33b0ccSKyunglyul Hyun                     throw new IOException("Unsupported Encoding Scheme");
110*ff33b0ccSKyunglyul Hyun             }
111*ff33b0ccSKyunglyul Hyun         }
112*ff33b0ccSKyunglyul Hyun 
113*ff33b0ccSKyunglyul Hyun         boolean isUserIDRequired = false;
114*ff33b0ccSKyunglyul Hyun         boolean isFullAccess = true;
115*ff33b0ccSKyunglyul Hyun         if (option != null) {
116*ff33b0ccSKyunglyul Hyun             if ((option[0] & 0x01) != 0) {
117*ff33b0ccSKyunglyul Hyun                 isUserIDRequired = true;
118*ff33b0ccSKyunglyul Hyun             }
119*ff33b0ccSKyunglyul Hyun 
120*ff33b0ccSKyunglyul Hyun             if ((option[0] & 0x02) != 0) {
121*ff33b0ccSKyunglyul Hyun                 isFullAccess = false;
122*ff33b0ccSKyunglyul Hyun             }
123*ff33b0ccSKyunglyul Hyun         }
124*ff33b0ccSKyunglyul Hyun 
125*ff33b0ccSKyunglyul Hyun         PasswordAuthentication result = null;
126*ff33b0ccSKyunglyul Hyun         header.mAuthChall = null;
127*ff33b0ccSKyunglyul Hyun 
128*ff33b0ccSKyunglyul Hyun         try {
129*ff33b0ccSKyunglyul Hyun             result = mAuthenticator
130*ff33b0ccSKyunglyul Hyun                     .onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess);
131*ff33b0ccSKyunglyul Hyun         } catch (Exception e) {
132*ff33b0ccSKyunglyul Hyun             if (V) Log.d(TAG, "Exception occurred - returning false", e);
133*ff33b0ccSKyunglyul Hyun             return false;
134*ff33b0ccSKyunglyul Hyun         }
135*ff33b0ccSKyunglyul Hyun 
136*ff33b0ccSKyunglyul Hyun         /*
137*ff33b0ccSKyunglyul Hyun          * If no password is provided then we not resent the request
138*ff33b0ccSKyunglyul Hyun          */
139*ff33b0ccSKyunglyul Hyun         if (result == null) {
140*ff33b0ccSKyunglyul Hyun             return false;
141*ff33b0ccSKyunglyul Hyun         }
142*ff33b0ccSKyunglyul Hyun 
143*ff33b0ccSKyunglyul Hyun         byte[] password = result.getPassword();
144*ff33b0ccSKyunglyul Hyun         if (password == null) {
145*ff33b0ccSKyunglyul Hyun             return false;
146*ff33b0ccSKyunglyul Hyun         }
147*ff33b0ccSKyunglyul Hyun 
148*ff33b0ccSKyunglyul Hyun         byte[] userName = result.getUserName();
149*ff33b0ccSKyunglyul Hyun 
150*ff33b0ccSKyunglyul Hyun         /*
151*ff33b0ccSKyunglyul Hyun          * Create the authentication response header. It includes 1 required and
152*ff33b0ccSKyunglyul Hyun          * 2 option tag length value triples. The required triple has a tag of
153*ff33b0ccSKyunglyul Hyun          * 0x00 and is the response digest. The first optional tag is 0x01 and
154*ff33b0ccSKyunglyul Hyun          * represents the user ID. If no user ID is provided, then no user ID
155*ff33b0ccSKyunglyul Hyun          * will be sent. The second optional tag is 0x02 and is the challenge
156*ff33b0ccSKyunglyul Hyun          * that was received. This will always be sent
157*ff33b0ccSKyunglyul Hyun          */
158*ff33b0ccSKyunglyul Hyun         if (userName != null) {
159*ff33b0ccSKyunglyul Hyun             header.mAuthResp = new byte[38 + userName.length];
160*ff33b0ccSKyunglyul Hyun             header.mAuthResp[36] = (byte)0x01;
161*ff33b0ccSKyunglyul Hyun             header.mAuthResp[37] = (byte)userName.length;
162*ff33b0ccSKyunglyul Hyun             System.arraycopy(userName, 0, header.mAuthResp, 38, userName.length);
163*ff33b0ccSKyunglyul Hyun         } else {
164*ff33b0ccSKyunglyul Hyun             header.mAuthResp = new byte[36];
165*ff33b0ccSKyunglyul Hyun         }
166*ff33b0ccSKyunglyul Hyun 
167*ff33b0ccSKyunglyul Hyun         // Create the secret String
168*ff33b0ccSKyunglyul Hyun         byte[] digest = new byte[challenge.length + password.length + 1];
169*ff33b0ccSKyunglyul Hyun         System.arraycopy(challenge, 0, digest, 0, challenge.length);
170*ff33b0ccSKyunglyul Hyun         // Insert colon between challenge and password
171*ff33b0ccSKyunglyul Hyun         digest[challenge.length] = (byte)0x3A;
172*ff33b0ccSKyunglyul Hyun         System.arraycopy(password, 0, digest, challenge.length + 1, password.length);
173*ff33b0ccSKyunglyul Hyun 
174*ff33b0ccSKyunglyul Hyun         // Add the Response Digest
175*ff33b0ccSKyunglyul Hyun         header.mAuthResp[0] = (byte)0x00;
176*ff33b0ccSKyunglyul Hyun         header.mAuthResp[1] = (byte)0x10;
177*ff33b0ccSKyunglyul Hyun 
178*ff33b0ccSKyunglyul Hyun         System.arraycopy(ObexHelper.computeMd5Hash(digest), 0, header.mAuthResp, 2, 16);
179*ff33b0ccSKyunglyul Hyun 
180*ff33b0ccSKyunglyul Hyun         // Add the challenge
181*ff33b0ccSKyunglyul Hyun         header.mAuthResp[18] = (byte)0x02;
182*ff33b0ccSKyunglyul Hyun         header.mAuthResp[19] = (byte)0x10;
183*ff33b0ccSKyunglyul Hyun         System.arraycopy(challenge, 0, header.mAuthResp, 20, 16);
184*ff33b0ccSKyunglyul Hyun 
185*ff33b0ccSKyunglyul Hyun         return true;
186*ff33b0ccSKyunglyul Hyun     }
187*ff33b0ccSKyunglyul Hyun 
188*ff33b0ccSKyunglyul Hyun     /**
189*ff33b0ccSKyunglyul Hyun      * Called when the server received an authentication response header. This
190*ff33b0ccSKyunglyul Hyun      * will cause the authenticator to handle the authentication response.
191*ff33b0ccSKyunglyul Hyun      * @param authResp the authentication response
192*ff33b0ccSKyunglyul Hyun      * @return <code>true</code> if the response passed; <code>false</code> if
193*ff33b0ccSKyunglyul Hyun      *         the response failed
194*ff33b0ccSKyunglyul Hyun      */
handleAuthResp(byte[] authResp)195*ff33b0ccSKyunglyul Hyun     public boolean handleAuthResp(byte[] authResp) {
196*ff33b0ccSKyunglyul Hyun         if (mAuthenticator == null) {
197*ff33b0ccSKyunglyul Hyun             return false;
198*ff33b0ccSKyunglyul Hyun         }
199*ff33b0ccSKyunglyul Hyun         // get the correct password from the application
200*ff33b0ccSKyunglyul Hyun         byte[] correctPassword = mAuthenticator.onAuthenticationResponse(ObexHelper.getTagValue(
201*ff33b0ccSKyunglyul Hyun                 (byte)0x01, authResp));
202*ff33b0ccSKyunglyul Hyun         if (correctPassword == null) {
203*ff33b0ccSKyunglyul Hyun             return false;
204*ff33b0ccSKyunglyul Hyun         }
205*ff33b0ccSKyunglyul Hyun 
206*ff33b0ccSKyunglyul Hyun         byte[] temp = new byte[correctPassword.length + 16];
207*ff33b0ccSKyunglyul Hyun 
208*ff33b0ccSKyunglyul Hyun         System.arraycopy(mChallengeDigest, 0, temp, 0, 16);
209*ff33b0ccSKyunglyul Hyun         System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length);
210*ff33b0ccSKyunglyul Hyun 
211*ff33b0ccSKyunglyul Hyun         byte[] correctResponse = ObexHelper.computeMd5Hash(temp);
212*ff33b0ccSKyunglyul Hyun         byte[] actualResponse = ObexHelper.getTagValue((byte)0x00, authResp);
213*ff33b0ccSKyunglyul Hyun 
214*ff33b0ccSKyunglyul Hyun         // compare the MD5 hash array .
215*ff33b0ccSKyunglyul Hyun         for (int i = 0; i < 16; i++) {
216*ff33b0ccSKyunglyul Hyun             if (correctResponse[i] != actualResponse[i]) {
217*ff33b0ccSKyunglyul Hyun                 return false;
218*ff33b0ccSKyunglyul Hyun             }
219*ff33b0ccSKyunglyul Hyun         }
220*ff33b0ccSKyunglyul Hyun 
221*ff33b0ccSKyunglyul Hyun         return true;
222*ff33b0ccSKyunglyul Hyun     }
223*ff33b0ccSKyunglyul Hyun }
224