// DigestAuthPrincipal.java
// $Id: DigestAuthPrincipal.java,v 1.6 2000/08/16 21:37:33 ylafon Exp $
// (c) COPYRIGHT MIT, INRIA and Keio, 1999.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.jigsaw.acl;

import java.security.Principal;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.w3c.jigsaw.http.Request;
import org.w3c.www.http.HttpCredential;
import org.w3c.util.StringUtils;

/**
 * @version $Revision: 1.6 $
 * @author  Benoît Mahé (bmahe@w3.org)
 */
public class DigestAuthPrincipal extends HTTPPrincipal  {

    String dac_user      = null;
    String dac_realm     = null;
    String dac_nonce     = null;
    String dac_uri       = null;
    String dac_response  = null;
    String dac_algorithm = null;
    String dac_method    = null;
    String nonce         = null;
    String old_nonce     = null;
    String algo          = null;
    boolean stale        = false;
    boolean no_user      = false;

    public boolean isStale() {
	return stale;
    }

    public boolean equals(Object another) {
	if (no_user)
	    return false;
	if (another instanceof AclPrincipal) {
	    AclPrincipal aclp = (AclPrincipal) another;
	    String username   = aclp.getName();
	    String realm      = aclp.getRealm();
	    String passwd     = aclp.getPassword();

	    if (!dac_user.equals(username))
		return false;
	    if (!dac_realm.equals(realm))
		return false;
	    if (dac_algorithm != null && !dac_algorithm.equals(this.algo))
		return false;
	    if (!dac_nonce.equals(this.nonce)) {
		if (!dac_nonce.equals(this.old_nonce)) {
		    // check if the user knows the right passwd
		    String a1, a2, ha1, ha2;
		    a1 = username + ":" + realm + ":" + passwd;
		    a2 = dac_method + ":" + dac_uri;
		    MessageDigest md = null;
		    try {
			md = MessageDigest.getInstance(this.algo);
		    } catch (NoSuchAlgorithmException algex) {
			// fatal error, can't authenticate
			return false;
		    }
		    md.update(a1.getBytes());
		    ha1 = StringUtils.toHexString(md.digest());
		    md.reset();
		    md.update(a2.getBytes());
		    ha2 = StringUtils.toHexString(md.digest());
		    md.reset();
		    String kd, hkd;
		    // KD( H(A1), unq(nonce-value) ":" H(A2)
		    kd = ha1 + ":" + dac_nonce + ":" + ha2;
		    md.update(kd.getBytes());
		    hkd = StringUtils.toHexString(md.digest());
		    stale = hkd.equals(dac_response);
		    return false;
		} else {
		    stale = true;
		}
	    }
	    // basic things have been checked... now try the real thing
	    String a1, a2, ha1, ha2;
	    a1 = username + ":" + realm + ":" + passwd;
	    a2 = dac_method + ":" + dac_uri;
	    MessageDigest md = null;
	    try {
		md = MessageDigest.getInstance(this.algo);
	    } catch (NoSuchAlgorithmException algex) {
		// fatal error, can't authenticate
		return false;
	    }
	    md.update(a1.getBytes());
	    ha1 = StringUtils.toHexString(md.digest());
	    md.reset();
	    md.update(a2.getBytes());
	    ha2 = StringUtils.toHexString(md.digest());
	    md.reset();
	    String kd, hkd;
	    if (stale)   // KD( H(A1), unq(nonce-value) ":" H(A2)
		kd = ha1 + ":" + old_nonce + ":" + ha2;
	    else
		kd = ha1 + ":" + nonce + ":" + ha2;
	    md.update(kd.getBytes());
	    hkd = StringUtils.toHexString(md.digest());
	    if (!hkd.equals(dac_response))
		return false;
	    // yeah!!!
	    return true;
	} else if (another instanceof DigestAuthPrincipal) {
	    return false;
	}
	return false;
    }

    public String toString() {
	if (dac_user != null)
	    return dac_user;
	return "Digest";
    }

    public int hashCode() {
	if (dac_nonce != null)
	    return dac_nonce.hashCode();
	else return -1;
    }

    public String getName() {
	return dac_user;
    }

    public DigestAuthPrincipal(Request request, 
			       String nonce, 
			       String old_nonce,
			       String algo)
	throws InvalidAuthException
    {
	super(request);
	HttpCredential credential = (request.isProxy()
				     ? request.getProxyAuthorization()
				     : request.getAuthorization());
	if ((credential == null) ||
	    ( ! credential.getScheme().equalsIgnoreCase("Digest"))) {
	    no_user = true;
	} else {
	    no_user        = false;
	    dac_user       = credential.getAuthParameter("username");
	    dac_uri        = credential.getAuthParameter("uri");
	    dac_response   = credential.getAuthParameter("response");
	    dac_realm      = credential.getAuthParameter("realm");
	    dac_method     = request.getMethod();
	    dac_nonce      = credential.getAuthParameter("nonce");
	    this.nonce     = nonce;
	    this.old_nonce = old_nonce;
	    this.algo      = algo;
	    if (dac_user == null || dac_uri == null || dac_response == null ||
		dac_realm == null) {
		String msg = ("Invalid authentication header");
		throw new InvalidAuthException(msg);
	    }
	}
    }

    public DigestAuthPrincipal(Request request)
	throws InvalidAuthException
    {
	super(request);
	throw new InvalidAuthException("Bad call for authentification");
    }
}
