001/*
002 * Copyright 2015-2024 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.ByteArrayInputStream;
019import java.io.DataInputStream;
020import java.io.DataOutputStream;
021import java.io.IOException;
022import java.text.SimpleDateFormat;
023import java.util.Date;
024import java.util.TimeZone;
025
026/**
027 * RRSIG record payload.
028 */
029public class RRSIG extends Data {
030
031    /**
032     * The type of RRset covered by this signature.
033     */
034    public final TYPE typeCovered;
035
036    /**
037     * The cryptographic algorithm used to create the signature.
038     */
039    public final SignatureAlgorithm algorithm;
040
041    /**
042     * The cryptographic algorithm used to create the signature.
043     */
044    public final byte algorithmByte;
045
046    /**
047     * The number of labels in the original RRSIG RR owner name.
048     */
049    public final byte labels;
050
051    /**
052     * The TTL of the covered RRset.
053     */
054    public final long /* unsigned int */ originalTtl;
055
056    /**
057     * The date and time this RRSIG records expires.
058     */
059    public final Date signatureExpiration;
060
061    /**
062     * The date and time this RRSIG records starts to be valid.
063     */
064    public final Date signatureInception;
065
066    /**
067     * The key tag value of the DNSKEY RR that validates this signature.
068     */
069    public final int /* unsigned short */  keyTag;
070
071    /**
072     * The owner name of the DNSKEY RR that a validator is supposed to use.
073     */
074    public final DnsName signerName;
075
076    /**
077     * Signature that covers RRSIG RDATA (excluding the signature field) and RRset data.
078     */
079    private final byte[] signature;
080
081    public static RRSIG parse(DataInputStream dis, byte[] data, int length) throws IOException {
082        TYPE typeCovered = TYPE.getType(dis.readUnsignedShort());
083        byte algorithm = dis.readByte();
084        byte labels = dis.readByte();
085        long originalTtl = dis.readInt() & 0xFFFFFFFFL;
086        Date signatureExpiration = new Date((dis.readInt() & 0xFFFFFFFFL) * 1000);
087        Date signatureInception = new Date((dis.readInt() & 0xFFFFFFFFL) * 1000);
088        int keyTag = dis.readUnsignedShort();
089        DnsName signerName = DnsName.parse(dis, data);
090        int sigSize = length - signerName.size() - 18;
091        byte[] signature = new byte[sigSize];
092        if (dis.read(signature) != signature.length) throw new IOException();
093        return new RRSIG(typeCovered, null, algorithm, labels, originalTtl, signatureExpiration, signatureInception, keyTag, signerName,
094                signature);
095    }
096
097    private  RRSIG(TYPE typeCovered, SignatureAlgorithm algorithm, byte algorithmByte, byte labels, long originalTtl, Date signatureExpiration, 
098            Date signatureInception, int keyTag, DnsName signerName, byte[] signature) {
099        this.typeCovered = typeCovered;
100
101        assert algorithmByte == (algorithm != null ? algorithm.number : algorithmByte);
102        this.algorithmByte = algorithmByte;
103        this.algorithm = algorithm != null ? algorithm : SignatureAlgorithm.forByte(algorithmByte);
104
105        this.labels = labels;
106        this.originalTtl = originalTtl;
107        this.signatureExpiration = signatureExpiration;
108        this.signatureInception = signatureInception;
109        this.keyTag = keyTag;
110        this.signerName = signerName;
111        this.signature = signature;
112    }
113
114    public RRSIG(TYPE typeCovered, int algorithm, byte labels, long originalTtl, Date signatureExpiration, 
115            Date signatureInception, int keyTag, DnsName signerName, byte[] signature) {
116            this(typeCovered, null, (byte) algorithm, labels, originalTtl, signatureExpiration, signatureInception, keyTag, signerName, signature);
117    }
118
119    public RRSIG(TYPE typeCovered, int algorithm, byte labels, long originalTtl, Date signatureExpiration, 
120            Date signatureInception, int keyTag, String signerName, byte[] signature) {
121            this(typeCovered, null, (byte) algorithm, labels, originalTtl, signatureExpiration, signatureInception, keyTag, DnsName.from(signerName), signature);
122    }
123
124    public RRSIG(TYPE typeCovered, SignatureAlgorithm algorithm, byte labels,
125            long originalTtl, Date signatureExpiration, Date signatureInception,
126            int keyTag, DnsName signerName, byte[] signature) {
127        this(typeCovered, algorithm.number, labels, originalTtl, signatureExpiration, signatureInception,
128                keyTag, signerName, signature);
129    }
130
131    public RRSIG(TYPE typeCovered, SignatureAlgorithm algorithm, byte labels,
132            long originalTtl, Date signatureExpiration, Date signatureInception,
133            int keyTag, String signerName, byte[] signature) {
134        this(typeCovered, algorithm.number, labels, originalTtl, signatureExpiration, signatureInception,
135                keyTag, DnsName.from(signerName), signature);
136    }
137
138    public byte[] getSignature() {
139        return signature.clone();
140    }
141
142    public DataInputStream getSignatureAsDataInputStream() {
143        return new DataInputStream(new ByteArrayInputStream(signature));
144    }
145
146    public int getSignatureLength() {
147        return signature.length;
148    }
149
150    private transient String base64SignatureCache;
151
152    public String getSignatureBase64() {
153        if (base64SignatureCache == null) {
154            base64SignatureCache = Base64.encodeToString(signature);
155        }
156        return base64SignatureCache;
157    }
158
159    @Override
160    public TYPE getType() {
161        return TYPE.RRSIG;
162    }
163
164    @Override
165    public void serialize(DataOutputStream dos) throws IOException {
166        writePartialSignature(dos);
167        dos.write(signature);
168    }
169
170    public void writePartialSignature(DataOutputStream dos) throws IOException {
171        dos.writeShort(typeCovered.getValue());
172        dos.writeByte(algorithmByte);
173        dos.writeByte(labels);
174        dos.writeInt((int) originalTtl);
175        dos.writeInt((int) (signatureExpiration.getTime() / 1000));
176        dos.writeInt((int) (signatureInception.getTime() / 1000));
177        dos.writeShort(keyTag);
178        signerName.writeToStream(dos);
179    }
180
181    @Override
182    public String toString() {
183        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
184        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
185        StringBuilder sb = new StringBuilder()
186                .append(typeCovered).append(' ')
187                .append(algorithm).append(' ')
188                .append(labels).append(' ')
189                .append(originalTtl).append(' ')
190                .append(dateFormat.format(signatureExpiration)).append(' ')
191                .append(dateFormat.format(signatureInception)).append(' ')
192                .append(keyTag).append(' ')
193                .append(signerName).append(". ")
194                .append(getSignatureBase64());
195        return sb.toString();
196    }
197}