package com.duosecurity;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public final class DuoWeb {
private static final String DUO_PREFIX = "TX";
private static final String APP_PREFIX = "APP";
private static final String AUTH_PREFIX = "AUTH";
private static final int DUO_EXPIRE = 300;
private static final int APP_EXPIRE = 3600;
private static final int IKEY_LEN = 20;
private static final int SKEY_LEN = 40;
private static final int AKEY_LEN = 40;
public static final String ERR_USER = "ERR|The username passed to sign_request() is invalid.";
public static final String ERR_IKEY = "ERR|The Duo integration key passed to sign_request() is invalid.";
public static final String ERR_SKEY = "ERR|The Duo secret key passed to sign_request() is invalid.";
public static final String ERR_AKEY = "ERR|The application secret key passed to sign_request() must be at least " + AKEY_LEN + " characters.";
public static final String ERR_UNKNOWN = "ERR|An unknown error has occurred.";
public static final String INVALID_RESPONSE = "Invalid response";
private DuoWeb() {}
public static String signRequest(final String ikey, final String skey, final String akey, final String username) {
final String duoSig;
final String appSig;
if (username.equals("")) {
return ERR_USER;
}
if (username.indexOf('|') != -1) {
return ERR_USER;
}
if (ikey.equals("") || ikey.length() != IKEY_LEN) {
return ERR_IKEY;
}
if (skey.equals("") || skey.length() != SKEY_LEN) {
return ERR_SKEY;
}
if (akey.equals("") || akey.length() < AKEY_LEN) {
return ERR_AKEY;
}
try {
duoSig = signVals(skey, username, ikey, DUO_PREFIX, DUO_EXPIRE);
appSig = signVals(akey, username, ikey, APP_PREFIX, APP_EXPIRE);
} catch (final Exception e) {
return ERR_UNKNOWN;
}
return duoSig + ":" + appSig;
}
public static String verifyResponse(final String ikey, final String skey, final String akey, final String sigResponse)
throws DuoWebException, NoSuchAlgorithmException, InvalidKeyException, IOException {
String authUser;
String appUser;
final String[] sigs = sigResponse.split(":");
final String authSig = sigs[0];
final String appSig = sigs[1];
authUser = parseVals(skey, authSig, AUTH_PREFIX, ikey);
appUser = parseVals(akey, appSig, APP_PREFIX, ikey);
if (!authUser.equals(appUser)) {
throw new DuoWebException("Authentication failed.");
}
return authUser;
}
private static String signVals(final String key, final String username, final String ikey, final String prefix, final int expire)
throws InvalidKeyException, NoSuchAlgorithmException {
final long ts = System.currentTimeMillis() / 1000;
final long expireTs = ts + expire;
final String exp = Long.toString(expireTs);
final String val = username + "|" + ikey + "|" + exp;
final String cookie = prefix + "|" + Base64.encodeBytes(val.getBytes());
final String sig = Util.hmacSign(key, cookie);
return cookie + "|" + sig;
}
private static String parseVals(final String key, final String val, final String prefix, final String ikey)
throws InvalidKeyException, NoSuchAlgorithmException, IOException, DuoWebException {
final long ts = System.currentTimeMillis() / 1000;
final String[] parts = val.split("\\|");
if (parts.length != 3) {
throw new DuoWebException(INVALID_RESPONSE);
}
final String uPrefix = parts[0];
final String uB64 = parts[1];
final String uSig = parts[2];
final String sig = Util.hmacSign(key, uPrefix + "|" + uB64);
if (!Util.hmacSign(key, sig).equals(Util.hmacSign(key, uSig))) {
throw new DuoWebException("Invalid response");
}
if (!uPrefix.equals(prefix)) {
throw new DuoWebException("Invalid response");
}
final byte[] decoded = Base64.decode(uB64);
final String cookie = new String(decoded);
final String[] cookieParts = cookie.split("\\|");
if (cookieParts.length != 3) {
throw new DuoWebException("Invalid response");
}
final String username = cookieParts[0];
final String uIkey = cookieParts[1];
final String expire = cookieParts[2];
if (!uIkey.equals(ikey)) {
throw new DuoWebException("Invalid response");
}
final long expireTs = Long.parseLong(expire);
if (ts >= expireTs) {
throw new DuoWebException("Transaction has expired. Please check that the system time is correct.");
}
return username;
}
}