package org.xbill.DNS.security;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.DSAKey;
import java.util.Date;
import org.xbill.DNS.*;
import org.xbill.DNS.utils.DataByteOutputStream;

/**
 * Creates SIG(0) transaction signatures.
 *
 * @author Pasi Eronen
 * @author Brian Wellington
 */

public class SIG0Signer {

/**
 * The default validity period for outgoing SIG(0) signed messages.
 * Can be overriden by the sig0validity option.
 */
private static final short VALIDITY = 300;
    
private int algorithm;
private PrivateKey privateKey;
private Name name;
private int footprint;

/** 
 * Creates a new SIG(0) signer object.
 * @param algorithm usually DNSSEC.RSAMD5, DNSSEC.DSA, or DNSSEC.RSASHA1
 * @param privateKey signing key (must match algorithm)
 * @param name the name of the key
 * @param keyFootprint the key tag
 */
public
SIG0Signer(int algorithm, PrivateKey privateKey, Name name, int keyFootprint) {
	this.algorithm = (byte) algorithm;
	this.privateKey = privateKey;
	this.name = name;
	this.footprint = keyFootprint;
}

/**
 * Creates a new SIG(0) signer object. This is the same as the
 * other constructor, except that the key tag is calculated automatically
 * from the given public key.
 */
public
SIG0Signer(int algorithm, PrivateKey privateKey, Name name,
	   PublicKey publicKey)
{
    this.algorithm = (byte) algorithm;
    this.privateKey = privateKey;
    this.name = name;
    KEYRecord keyRecord = KEYConverter.buildRecord(name, DClass.IN, 0,
						   KEYRecord.OWNER_USER,
						   KEYRecord.PROTOCOL_ANY,
						   publicKey);
    this.footprint = keyRecord.getFootprint();
}

/**
 * Appends a SIG(0) signature to the message.
 * @param m the message
 * @param old if this message is a response, the original message
 */
public void apply(Message m, byte old[])
throws IOException, SignatureException, InvalidKeyException,
       NoSuchAlgorithmException
{
	
	int validity = VALIDITY;
	if (Options.check("sig0validity")) {
		String s = Options.value("sig0validity");
		try {
			validity = Short.parseShort(s);
		}
		catch (NumberFormatException e) {
		}
	}
	
	long now = System.currentTimeMillis();
	Date timeSigned = new Date(now);
	Date timeExpires = new Date(now + validity * 1000);
	
	String algorithmName;
	if (algorithm == DNSSEC.DSA) {
		algorithmName = "SHA1withDSA";
	} else if (algorithm == DNSSEC.RSAMD5) {
		algorithmName = "MD5withRSA";
	} else if (algorithm == DNSSEC.RSASHA1) {
		algorithmName = "SHA1withRSA";
	} else {
		throw new NoSuchAlgorithmException("Unknown algorithm");
	}
	
	DataByteOutputStream out = new DataByteOutputStream();
	
	out.writeShort(0); // type covered
	out.writeByte(algorithm); // algorithm
	out.writeByte(0); // labels
	out.writeInt(0); // original TTL
	out.writeInt((int)(timeExpires.getTime() / 1000));
	out.writeInt((int)(timeSigned.getTime() / 1000));
	out.writeShort(footprint); // key tag
	name.toWireCanonical(out); // name
	
	if (old != null)
		out.write(old);
	out.write(m.toWire());
	
	byte[] outBytes = out.toByteArray();
	
	Signature signer = Signature.getInstance(algorithmName);
	signer.initSign(privateKey);
	signer.update(outBytes);
	byte[] signature = signer.sign();

	/*
	 * RSA signatures are already in correct format, but Java DSA
	 * routines use ASN.1; convert this to SIG format.
	 */
	if (algorithm == DNSSEC.DSA) {
		DSAKey dsakey = (DSAKey) privateKey;
		signature = DSASignature.create(dsakey.getParams(), signature);
	}
	
	SIGRecord r = new SIGRecord(Name.root, DClass.ANY, 0,
				    0, algorithm,
				    0, timeExpires, timeSigned,
				    footprint,
				    name,
				    signature);
	m.addRecord(r, Section.ADDITIONAL);
}

}

@@ -2025,14 +2025,15 @@
 byte
+ %5B%5D
  old
-%5B%5D
 )%0Ath
@@ -2770,444 +2770,144 @@
 %0A%09%7D%0A
-%09
 %0A%09
-DataByteOutputStream out = new DataByteOutputStream();%0A%09%0A%09out.writeShort(0); // type covered%0A%09out.writeByte(algorithm); // algorithm%0A%09out.writeByte(0); // labels%0A%09out.writeInt(0); // original TTL%0A%09out.writeInt((int)(timeExpires.getTime() / 1000));%0A%09out.writeInt((int)(timeSigned.getTime() / 1000));%0A%09out.writeShort(footprint); // key tag%0A%09name.toWireCanonical(out); // name%0A%09%0A%09if (old != null)%0A%09%09out.write(old);%0A%09out.write(m.toWire()
+SIGRecord tmpsig = new SIGRecord(Name.root, DClass.ANY, 0, 0,%0A%09%09%09%09%09 algorithm, 0, timeExpires, timeSigned,%0A%09%09%09%09%09 footprint, name, null
 );%0A%09
@@ -2912,16 +2912,17 @@
 %0A%09%0A%09byte
+ 
 %5B%5D outBy
@@ -2931,24 +2931,43 @@
 s = 
-out.toByteArray(
+DNSSEC.digestMessage(tmpsig, m, old
 );%0A%09
@@ -3086,16 +3086,17 @@
 );%0A%09byte
+ 
 %5B%5D signa
@@ -3395,17 +3395,19 @@
 GRecord 
-r
+sig
  = new S
@@ -3440,24 +3440,16 @@
 .ANY, 0,
-%0A%09%09%09%09   
  0, algo
@@ -3463,16 +3463,18 @@
 %09%09%09%09    
+  
 0, timeE
@@ -3492,24 +3492,16 @@
 eSigned,
-%0A%09%09%09%09   
  footpri
@@ -3516,21 +3516,15 @@
     
+  
 name,
-%0A%09%09%09%09   
  sig
@@ -3545,17 +3545,19 @@
 dRecord(
-r
+sig
 , Sectio

call DNSSEC.digestMessage()