package edu.stanford.junction.snap2pass; import java.net.URI; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.json.JSONObject; import edu.stanford.junction.JunctionMaker; import edu.stanford.junction.android.AndroidJunctionMaker; import edu.stanford.junction.android.AndroidJunctionMaker.Intents; import edu.stanford.junction.api.activity.JunctionActor; import edu.stanford.junction.api.messaging.MessageHeader; import edu.stanford.junction.provider.xmpp.XMPPSwitchboardConfig; import android.app.Activity; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; public class LoginAgent extends Activity { ProgressDialog mProgress = null; LoginTask mLoginTask = null; @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mLoginTask = new LoginTask(); if (mProgress == null) { mProgress = new ProgressDialog(LoginAgent.this); mProgress.setTitle("Logging you in"); mProgress.setIndeterminate(true); mProgress.setOnCancelListener(new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface dialog) { mLoginTask.cancel(true); } }); } if (AndroidJunctionMaker.isJoinable(LoginAgent.this)) { mLoginTask.execute((Void)null); } else { Log.d("snap2pass","Could not join intent in LoginAgent"); finish(); } } class LoginTask extends AsyncTask<Void, String, Void> { private LoginActor mLoginActor=null; private String authResponse = null; @Override protected void onPreExecute() { mProgress.show(); } @Override protected void onCancelled() { Log.d("snap2pass","Cancelling login."); mLoginActor.cancelLogin(); } @Override protected Void doInBackground(Void... params) { publishProgress("Computing response ..."); Bundle bundle = LoginAgent.this.getIntent().getExtras(); try { URI uri = new URI(bundle.getString(Intents.EXTRA_ACTIVITY_SESSION_URI)); String sid = JunctionMaker.getSessionIDFromURI(uri); mLoginActor = new LoginActor(sid); authResponse = mLoginActor.getResponse(); publishProgress("Connecting ..."); } catch (Exception e) { Log.e("snap2pass","invalid session URI",e); return null; } XMPPSwitchboardConfig config = new XMPPSwitchboardConfig("prpl.stanford.edu"); AndroidJunctionMaker jm = AndroidJunctionMaker.getInstance(config); jm.newJunction(LoginAgent.this, mLoginActor); // A little silly that most of the work in the // AsyncTask is in another thread, but so it goes. synchronized(mLoginActor) { try { mLoginActor.wait(); } catch (InterruptedException e) { Log.e("snap2pass","Interrupt exception while logging in",e); } } return null; } @Override protected void onProgressUpdate(String... progress) { mProgress.setMessage(progress[0]); /*if (authResponse == null) { mProgress.setMessage(progress[0]); } else { String msg = progress[0] + "\n\n Your auth code is " + authResponse; mProgress.setMessage(msg); }*/ } @Override protected void onPostExecute(Void result) { mProgress.dismiss(); finish(); } public void setProgress(String progress){ super.publishProgress(progress); } }; /** * A JunctionActor for logging in to a Snap2Pass * session. A LoginActor can only be used with * one challenge, passed in to its constructor. * */ private class LoginActor extends JunctionActor { private String mChallenge = null; private String mResponse = null; private String mUsername = null; private String mKey = null; public LoginActor(String challenge) { super("mobile"); mChallenge=challenge; SharedPreferences prefs = getSharedPreferences("main", 0); mUsername = prefs.getString("username", null); mKey = prefs.getString("key", null); } public void cancelLogin() { leave(); } @Override public void onMessageReceived(MessageHeader header, JSONObject message) { if (getActorID().equals(header.getSender())) { // only send one auth message. this.leave(); // Finishes the AsyncTask synchronized(LoginActor.this) { LoginActor.this.notify(); } } } @Override public void onActivityJoin() { try { mLoginTask.setProgress("Logging in ..."); String response = getResponse(); JSONObject msg = new JSONObject(); msg.put("action","authenticate"); msg.put("username",mUsername); msg.put("authkey",response); this.sendMessageToSession(msg); } catch (Exception e) { Log.e("snap2pass","Failed to log in",e); } }; public String getResponse() { if (mResponse != null) return mResponse; mResponse = computeBase64_HMAC(mChallenge, mKey); return mResponse; } }; private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; private static String computeBase64_HMAC(String data, String key) { try { SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM); Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); mac.init(signingKey); // compute the hmac on input data bytes byte[] rawHmac = mac.doFinal(data.getBytes()); // base64-encode the hmac String ans = new String(Base64Coder.encode(rawHmac)); return ans; } catch (Exception e) { Log.d("snap2pass","Error computing HMAC",e); return null; } } }