package org.verisign.joid.server;
import java.util.HashMap;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.json.JSONObject;
import edu.stanford.prpl.junction.api.activity.JunctionActor;
import edu.stanford.prpl.junction.api.messaging.MessageHandler;
import edu.stanford.prpl.junction.api.messaging.MessageHeader;
import edu.stanford.prpl.phoneIdp.server.api.PhoneIdp;
public class ProviderActor extends JunctionActor {
//private PhoneIdp mPhoneIdp;
private HttpSession mSession;
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
private static HashMap<String,String>randomnessForSessions = new HashMap<String,String>();
public static boolean setSessionData(HttpServletRequest request, String data) {
if (randomnessForSessions.containsKey(data)) return false;
randomnessForSessions.put(data,request.getSession().getId());
return true;
}
public static boolean sessionOwnsData(HttpServletRequest request, String data) {
return (randomnessForSessions.containsKey(data) &&
request.getSession().getId().equals(randomnessForSessions.get(data)));
}
private static boolean clearSessionData(HttpServletRequest request, String data) {
if (sessionOwnsData(request,data)) {
/*
* Note that the ID<=>session map is server-generated.
* A browser is not capable of setting the data associated with a session.
* So we don't need to worry about a replay attack.
*
* The whole mapping from data to session is actually not needed,
* and is more of a sanity check.
*
* BJD
*/
randomnessForSessions.remove(data);
return true;
}
return false;
}
public ProviderActor(/*PhoneIdp idp,*/ HttpServletRequest request) {
super("provider");
//mPhoneIdp=idp;
mSession = request.getSession(true);
}
public static User createUser(String username) {
try {
if (null != ((MemoryUserManager)OpenIdServlet.getUserManager()).getUser(username)) {
System.out.println("User " + username + " already exists.");
return null;
}
KeyGenerator keyGen = KeyGenerator.getInstance("HmacSHA1");
SecretKey key = keyGen.generateKey();
String b64key = new String(Base64Coder.encode(key.getEncoded()));
User user = new User(username,b64key);
((MemoryUserManager)OpenIdServlet.getUserManager()).save(user);
return user;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static User createUser() {
String username = "user_"+UUID.randomUUID().toString().substring(30, 36);
return createUser(username);
}
// I hate this, and will soon change it to
// onMessageReceied() { ... }
public MessageHandler getMessageHandler() {
return new MessageHandler() {
public void onMessageReceived(MessageHeader header, JSONObject json) {
try {
if (json.has("action")
&& json.getString("action").equals("authenticate")
&& json.has("authkey")
&& json.has("username")) {
// fields :: username, authtoken?
// have: json.optString("authkey");
// Using DSG's stuff:
/*
if (mPhoneIdp.verifyResponse("signedText")) {
// login successful
} else {
// login failed
}
*/
User user = ((MemoryUserManager)OpenIdServlet.getUserManager()).getUser(json.getString("username"));
if (user == null) {
System.out.println("could not find user " + json.getString("username") + " for logging in.");
}
// We'll wait for the browser redirect;
// code reenters in login.jsp
/*
String computedResult = computeBase64_HMAC(
getJunction().getSessionID(),
user.getPassword());
System.out.println("computed result: " + computedResult);
JSONObject response = new JSONObject();
response.put("result", computedResult);
response.put("from","provider");
response.put("matches", json.getString("authkey").equals(computedResult)?"true":"false");
getJunction().sendMessageToSession(response);
if (json.getString("authkey").equals(computedResult)) {
// send valid response to browser
mSession.setAttribute(OpenIdServlet.VERIFY_RESULT, true);
}
*/
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
}
public static boolean validateUserResponse(String username, HttpServletRequest httpRequest) {
MemoryUserManager manager = (MemoryUserManager)OpenIdServlet.getUserManager();
User user = manager.getUser(username);
if (user == null) return false;
String key = user.getPassword(); // look up from user
String data = httpRequest.getParameter("data");
String response = httpRequest.getParameter("authkey");
// make sure the random data is associated with the browser session.
if (!sessionOwnsData(httpRequest,data)) {
return false;
}
String result = computeBase64_HMAC(data,key);
boolean answer = (result.equals(response));
if (answer) {
clearSessionData(httpRequest, data);
}
return answer;
}
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));
System.out.println("b64: " + ans);
return ans;
} catch (Exception e) {
e.printStackTrace();
return "badanswer";
}
}
}