Milán Major

netscape-revival

1 branch
Code

sun-java/classsrc/netscape/security/CapPrincipal.java

//
// CapPrincipal.java -- Copyright 1996, Netscape Communications Corp.
// Dan Wallach <dwallach@netscape.com>
// 16 July 1996
//

// $Id: CapPrincipal.java,v 1.5 1996/07/31 05:39:49 dwallach Exp $

package netscape.security;

import java.lang.*;
import java.util.*;

/**
 * This class represents a principal.  Ultimately, the user is assigning
 * their trust to a real person or company, and this holds their crypto
 * public key or other identifying information.
 * <p>
 * Related documentation here:
 * <ul>
 * <a href="http://iapp15/java/signing/authorsigning.html">http://iapp15/java/signing/authorsigning.html</a>
 * </ul>
 *
 * @version 	$Id: CapPrincipal.java,v 1.5 1996/07/31 05:39:49 dwallach Exp $
 * @author	Dan Wallach
 */
public final
class CapPrincipal {
    public static final int CODEBASE_EXACT=0;
    public static final int CODEBASE_REGEXP=1;
    public static final int CERT=2;
    public static final int CERT_FINGERPRINT=3;

    private int itsType;
    private int itsHashCode;
    private String itsStringRep;
    private byte itsBinaryRep[];
    private byte itsFingerprint[];

    /**
     * This constructor allows you to specify a principal based on an ASCII
     * string.  This is the typical way CODEBASE Principals are created.
     * CERT_FINGERPRINT's can also be made this way, using an ASCII coded
     * representation like so:  ##:##:##, where each ## represents one byte
     * (two hex digits).
     */
    public CapPrincipal(int type, String value) {
	itsType=type;

	switch(type) {
	case CODEBASE_EXACT:
	case CODEBASE_REGEXP:
	    itsStringRep = value;
	    break;

	case CERT:
	case CERT_FINGERPRINT:
	    //
	    // we need to convert from string to binary rep
	    //
	    StringTokenizer st;

	    st = new StringTokenizer(value, ":;, \t");
	    itsBinaryRep = new byte[st.countTokens()];

	    for(int i=0; st.hasMoreElements(); i++)
		itsBinaryRep[i] = (byte)Integer.parseInt(st.nextToken(), 16);

	    if(type == CERT) {
		certToFingerprint();
	    } else {
		itsFingerprint = itsBinaryRep;
	    }

	    break;

	default:
	    throw new IllegalArgumentException("unknown principal type");
	}
	computeHashCode();
    }

    /**
     * This constructor allows you to specify CERT's and CERT_FINGERPRINT's
     * using a more compact input representation.  It's not appropriate for
     * CODEBASE principals.
     */
    public CapPrincipal(int type, byte[] value) {
	itsType=type;

	switch(type) {
	case CERT:
	    itsBinaryRep = value;
	    certToFingerprint();
	    break;
	case CERT_FINGERPRINT:
	    itsBinaryRep = value;
	    itsFingerprint = itsBinaryRep;
	    break;

	case CODEBASE_EXACT:
	case CODEBASE_REGEXP:
	    //
	    // they must have fed us a string as a byte array... *sigh*
	    //
	    itsStringRep = new String(value, 0);
	    break;

	default:
	    throw new IllegalArgumentException("unknown principal type");
	}
	computeHashCode();
    }

    /**
     * Compares with the given CapPrincipal and returns if
     * they <i>represent the same actual principal</i>.
     */
    public boolean equals(Object obj) {
        if(obj == this) return true;
        if(obj == null || this == null) return false;
        if(!(obj instanceof CapPrincipal)) return false;

        CapPrincipal prin = (CapPrincipal) obj;

	switch(itsType) {
	case CERT:
	case CERT_FINGERPRINT:
	    switch(prin.itsType) {
	    case CERT:
	    case CERT_FINGERPRINT:
		if(prin.itsFingerprint.length != itsFingerprint.length)
		    return false;

		for(int i=0; i<itsFingerprint.length; i++)
		    if(prin.itsFingerprint[i] != itsFingerprint[i])
			return false;
		return true;

		//
		// codebases and certs are never equal
		//
	    case CODEBASE_EXACT:
	    case CODEBASE_REGEXP:
		return false;
	    }
	case CODEBASE_EXACT:
	case CODEBASE_REGEXP:
	    switch(prin.itsType) {
	    case CERT:
	    case CERT_FINGERPRINT:
	        return false;
	    case CODEBASE_EXACT:
	    case CODEBASE_REGEXP:
	        // TODO: regular expression handling -- for now,
	        // regular expressions are the same as bare strings
	        return itsStringRep.equals(prin.itsStringRep);
	    }
	}
	// if control gets here, we've got a serious bug
	return false;
    }

    /**
     * optimized hashCode() for fast dictionary lookups
     */
    public int hashCode() {
	return itsHashCode;
    }

    private void computeHashCode() {
	switch(itsType) {
	case CERT:
	case CERT_FINGERPRINT:
	    itsHashCode = 0;
	    //
	    // Same basic hash algorithm as used in java.lang.String --
	    // no security relevance, only a performance optimization.
	    // The security comes from the equals() method.
	    //
	    for(int i=0; i<itsFingerprint.length; i++)
		itsHashCode = itsHashCode * 37 + itsFingerprint[i];
	    break;

	case CODEBASE_EXACT:
	case CODEBASE_REGEXP:
	    itsHashCode = itsStringRep.hashCode();
	    break;
	}
    }

    public int getType() {
	return itsType;
    }

    public boolean isCodebaseExact() {
	return itsType == CODEBASE_EXACT;
    }

    public boolean isCodebaseRegexp() {
	return itsType == CODEBASE_REGEXP;
    }

    public boolean isCert() {
	return itsType == CERT;
    }

    public boolean isCertFingerprint() {
	return itsType == CERT_FINGERPRINT;
    }

    public String toString() {
	switch(itsType) {
	case CERT:
	case CERT_FINGERPRINT:
	    StringBuffer resultBuf = new StringBuffer();
	    for(int i=0; i<itsBinaryRep.length; i++) {
		if(i>0) resultBuf.append(":");

		//
		// *sigh* -- Java tries to sign-extend the byte when
		// converting to an int, and the language doesn't have
		// unsigned types.
		//
		if(itsBinaryRep[i] < 0)
		    resultBuf.append(Integer.toString((int)
						      (itsBinaryRep[i]&0x7f) +
						      0x80, 16));
		else
		    resultBuf.append(Integer.toString(itsBinaryRep[i], 16));
	    }
	    return resultBuf.toString();

	case CODEBASE_EXACT:
	case CODEBASE_REGEXP:
	    return itsStringRep;  // security: copying not necessary any more?
	}

	// control never actually reaches here, but the compiler complains
	return itsStringRep;
    }

    private void certToFingerprint() {
	// TODO: call builtin SHA, MD5, or something
	itsFingerprint = itsBinaryRep;
    }
}