// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package org.xbill.DNS;

import java.io.*;
import java.security.PublicKey;

import org.xbill.DNS.utils.*;

/**
 * The base class for KEY/DNSKEY records, which have identical formats
 * @author Brian Wellington
 */
abstract class KEYBase extends Record {
	private static final long serialVersionUID = 3469321722693285454L;
	protected int flags, proto, alg;
	protected byte[] key;
	protected int footprint = -1;
	protected PublicKey publicKey = null;

	protected KEYBase() {
	}
	public KEYBase(Name name, int type, int dclass, long ttl, int flags,
			int proto, int alg, byte[] key) {
		super(name, type, dclass, ttl);
		this.flags = checkU16("flags", flags);
		this.proto = checkU8("proto", proto);
		this.alg = checkU8("alg", alg);
		this.key = key;
	}
	@Override
	void rrFromWire(DNSInput in) throws IOException {
		flags = in.readU16();
		proto = in.readU8();
		alg = in.readU8();
		if (in.remaining() > 0)
			key = in.readByteArray();
	}
	/** Converts the DNSKEY/KEY Record to a String */
	@Override
	String rrToString() {
		StringBuffer sb = new StringBuffer();
		sb.append(flags);
		sb.append(" ");
		sb.append(proto);
		sb.append(" ");
		sb.append(alg);
		if (key != null) {
			if (Options.check("multiline")) {
				sb.append(" (\n");
				sb.append(base64.formatString(key, 64, "\t", true));
				sb.append(" ; key_tag = ");
				sb.append(getFootprint());
			} else {
				sb.append(" ");
				sb.append(base64.toString(key));
			}
		}
		return sb.toString();
	}
	/**
	 * Returns the flags describing the key's properties
	 */
	public int getFlags() {
		return flags;
	}
	/**
	 * Returns the protocol that the key was created for
	 */
	public int getProtocol() {
		return proto;
	}
	/**
	 * Returns the key's algorithm
	 */
	public int getAlgorithm() {
		return alg;
	}
	/**
	 * Returns the binary data representing the key
	 */
	public byte[] getKey() {
		return key;
	}
	/**
	 * Returns the key's footprint (after computing it)
	 */
	public int getFootprint() {
		if (footprint >= 0)
			return footprint;
		int foot = 0;
		DNSOutput out = new DNSOutput();
		rrToWire(out, null, false);
		byte[] rdata = out.toByteArray();
		if (alg == DNSSEC.Algorithm.RSAMD5) {
			int d1 = rdata[rdata.length - 3] & 0xFF;
			int d2 = rdata[rdata.length - 2] & 0xFF;
			foot = (d1 << 8) + d2;
		} else {
			int i;
			for (i = 0; i < rdata.length - 1; i += 2) {
				int d1 = rdata[i] & 0xFF;
				int d2 = rdata[i + 1] & 0xFF;
				foot += ((d1 << 8) + d2);
			}
			if (i < rdata.length) {
				int d1 = rdata[i] & 0xFF;
				foot += (d1 << 8);
			}
			foot += ((foot >> 16) & 0xFFFF);
		}
		footprint = (foot & 0xFFFF);
		return footprint;
	}
	/**
	 * Returns a PublicKey corresponding to the data in this key.
	 * @throws DNSSEC.DNSSECException The key could not be converted.
	 */
	public PublicKey getPublicKey() throws DNSSEC.DNSSECException {
		if (publicKey != null)
			return publicKey;
		publicKey = DNSSEC.toPublicKey(this);
		return publicKey;
	}
	@Override
	void rrToWire(DNSOutput out, Compression c, boolean canonical) {
		out.writeU16(flags);
		out.writeU8(proto);
		out.writeU8(alg);
		if (key != null)
			out.writeByteArray(key);
	}
}
