001/*
002 * Copyright 2015-2018 the original author or authors
003 *
004 * This software is licensed under the Apache License, Version 2.0,
005 * the GNU Lesser General Public License version 2 or later ("LGPL")
006 * and the WTFPL.
007 * You may choose either license to govern your use of this software only
008 * upon the condition that you accept all of the terms of either
009 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
010 */
011package org.minidns.record;
012
013import org.minidns.constants.DnssecConstants.SignatureAlgorithm;
014import org.minidns.dnsname.DnsName;
015import org.minidns.record.Record.TYPE;
016import org.minidns.util.Base64;
017
018import java.io.DataInputStream;
019import java.io.DataOutputStream;
020import java.io.IOException;
021import java.text.SimpleDateFormat;
022import java.util.Date;
023import java.util.TimeZone;
024
025/**
026 * RRSIG record payload.
027 */
028public class RRSIG extends Data {
029
030    /**
031     * The type of RRset covered by this signature.
032     */
033    public final TYPE typeCovered;
034
035    /**
036     * The cryptographic algorithm used to create the signature.
037     */
038    public final SignatureAlgorithm algorithm;
039
040    /**
041     * The cryptographic algorithm used to create the signature.
042     */
043    public final byte algorithmByte;
044
045    /**
046     * The number of labels in the original RRSIG RR owner name.
047     */
048    public final byte labels;
049
050    /**
051     * The TTL of the covered RRset.
052     */
053    public final long /* unsigned int */ originalTtl;
054
055    /**
056     * The date and time this RRSIG records expires.
057     */
058    public final Date signatureExpiration;
059
060    /**
061     * The date and time this RRSIG records starts to be valid.
062     */
063    public final Date signatureInception;
064
065    /**
066     * The key tag value of the DNSKEY RR that validates this signature.
067     */
068    public final int /* unsigned short */  keyTag;
069
070    /**
071     * The owner name of the DNSKEY RR that a validator is supposed to use.
072     */
073    public final DnsName signerName;
074
075    /**
076     * Signature that covers RRSIG RDATA (excluding the signature field) and RRset data.
077     */
078    public final byte[] signature;
079
080    public static RRSIG parse(DataInputStream dis, byte[] data, int length) throws IOException {
081        TYPE typeCovered = TYPE.getType(dis.readUnsignedShort());
082        byte algorithm = dis.readByte();
083        byte labels = dis.readByte();
084        long originalTtl = dis.readInt() & 0xFFFFFFFFL;
085        Date signatureExpiration = new Date((dis.readInt() & 0xFFFFFFFFL) * 1000);
086        Date signatureInception = new Date((dis.readInt() & 0xFFFFFFFFL) * 1000);
087        int keyTag = dis.readUnsignedShort();
088        DnsName signerName = DnsName.parse(dis, data);
089        int sigSize = length - signerName.size() - 18;
090        byte[] signature = new byte[sigSize];
091        if (dis.read(signature) != signature.length) throw new IOException();
092        return new RRSIG(typeCovered, null, algorithm, labels, originalTtl, signatureExpiration, signatureInception, keyTag, signerName,
093                signature);
094    }
095
096    private  RRSIG(TYPE typeCovered, SignatureAlgorithm algorithm, byte algorithmByte, byte labels, long originalTtl, Date signatureExpiration, 
097            Date signatureInception, int keyTag, DnsName signerName, byte[] signature) {
098        this.typeCovered = typeCovered;
099
100        assert algorithmByte == (algorithm != null ? algorithm.number : algorithmByte);
101        this.algorithmByte = algorithmByte;
102        this.algorithm = algorithm != null ? algorithm : SignatureAlgorithm.forByte(algorithmByte);
103
104        this.labels = labels;
105        this.originalTtl = originalTtl;
106        this.signatureExpiration = signatureExpiration;
107        this.signatureInception = signatureInception;
108        this.keyTag = keyTag;
109        this.signerName = signerName;
110        this.signature = signature;
111    }
112
113    public RRSIG(TYPE typeCovered, int algorithm, byte labels, long originalTtl, Date signatureExpiration, 
114            Date signatureInception, int keyTag, DnsName signerName, byte[] signature) {
115            this(typeCovered, null, (byte) algorithm, labels, originalTtl, signatureExpiration, signatureInception, keyTag, signerName, signature);
116    }
117
118    public RRSIG(TYPE typeCovered, int algorithm, byte labels, long originalTtl, Date signatureExpiration, 
119            Date signatureInception, int keyTag, String signerName, byte[] signature) {
120            this(typeCovered, null, (byte) algorithm, labels, originalTtl, signatureExpiration, signatureInception, keyTag, DnsName.from(signerName), signature);
121    }
122
123    public RRSIG(TYPE typeCovered, SignatureAlgorithm algorithm, byte labels,
124            long originalTtl, Date signatureExpiration, Date signatureInception,
125            int keyTag, DnsName signerName, byte[] signature) {
126        this(typeCovered,algorithm.number, labels, originalTtl, signatureExpiration, signatureInception, keyTag, signerName, signature);
127    }
128
129    public RRSIG(TYPE typeCovered, SignatureAlgorithm algorithm, byte labels,
130            long originalTtl, Date signatureExpiration, Date signatureInception,
131            int keyTag, String signerName, byte[] signature) {
132        this(typeCovered,algorithm.number, labels, originalTtl, signatureExpiration, signatureInception, keyTag, DnsName.from(signerName), signature);
133    }
134
135    @Override
136    public TYPE getType() {
137        return TYPE.RRSIG;
138    }
139
140    @Override
141    public void serialize(DataOutputStream dos) throws IOException {
142        writePartialSignature(dos);
143        dos.write(signature);
144    }
145
146    public void writePartialSignature(DataOutputStream dos) throws IOException {
147        dos.writeShort(typeCovered.getValue());
148        dos.writeByte(algorithmByte);
149        dos.writeByte(labels);
150        dos.writeInt((int) originalTtl);
151        dos.writeInt((int) (signatureExpiration.getTime()/1000));
152        dos.writeInt((int) (signatureInception.getTime()/1000));
153        dos.writeShort(keyTag);
154        signerName.writeToStream(dos);
155    }
156
157    @Override
158    public String toString() {
159        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
160        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
161        StringBuilder sb = new StringBuilder()
162                .append(typeCovered).append(' ')
163                .append(algorithm).append(' ')
164                .append(labels).append(' ')
165                .append(originalTtl).append(' ')
166                .append(dateFormat.format(signatureExpiration)).append(' ')
167                .append(dateFormat.format(signatureInception)).append(' ')
168                .append(keyTag).append(' ')
169                .append(signerName).append(". ")
170                .append(Base64.encodeToString(signature));
171        return sb.toString();
172    }
173}