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 binary value of the type of RRset covered by this signature. In contrast to {@link #typeCovered}, this also 038 * holds the actual value if the type is unknown to MiniDNS. 039 */ 040 public final int typeCoveredValue; 041 042 /** 043 * The cryptographic algorithm used to create the signature. 044 */ 045 public final SignatureAlgorithm algorithm; 046 047 /** 048 * The cryptographic algorithm used to create the signature. 049 */ 050 public final byte algorithmByte; 051 052 /** 053 * The number of labels in the original RRSIG RR owner name. 054 */ 055 public final byte labels; 056 057 /** 058 * The TTL of the covered RRset. 059 */ 060 public final long /* unsigned int */ originalTtl; 061 062 /** 063 * The date and time this RRSIG records expires. 064 */ 065 public final Date signatureExpiration; 066 067 /** 068 * The date and time this RRSIG records starts to be valid. 069 */ 070 public final Date signatureInception; 071 072 /** 073 * The key tag value of the DNSKEY RR that validates this signature. 074 */ 075 public final int /* unsigned short */ keyTag; 076 077 /** 078 * The owner name of the DNSKEY RR that a validator is supposed to use. 079 */ 080 public final DnsName signerName; 081 082 /** 083 * Signature that covers RRSIG RDATA (excluding the signature field) and RRset data. 084 */ 085 private final byte[] signature; 086 087 @SuppressWarnings("JavaUtilDate") 088 public static RRSIG parse(DataInputStream dis, byte[] data, int length) throws IOException { 089 int typeCoveredValue = dis.readUnsignedShort(); 090 TYPE typeCovered = TYPE.getType(typeCoveredValue); 091 byte algorithm = dis.readByte(); 092 byte labels = dis.readByte(); 093 long originalTtl = dis.readInt() & 0xFFFFFFFFL; 094 Date signatureExpiration = new Date((dis.readInt() & 0xFFFFFFFFL) * 1000); 095 Date signatureInception = new Date((dis.readInt() & 0xFFFFFFFFL) * 1000); 096 int keyTag = dis.readUnsignedShort(); 097 DnsName signerName = DnsName.parse(dis, data); 098 int sigSize = length - signerName.size() - 18; 099 byte[] signature = new byte[sigSize]; 100 if (dis.read(signature) != signature.length) throw new IOException(); 101 return new RRSIG(typeCovered, typeCoveredValue, null, algorithm, labels, originalTtl, signatureExpiration, signatureInception, keyTag, 102 signerName, signature); 103 } 104 105 private RRSIG(TYPE typeCovered, int typeCoveredValue, SignatureAlgorithm algorithm, byte algorithmByte, byte labels, long originalTtl, 106 Date signatureExpiration, Date signatureInception, int keyTag, DnsName signerName, byte[] signature) { 107 this.typeCovered = typeCovered; 108 this.typeCoveredValue = typeCoveredValue; 109 110 assert algorithmByte == (algorithm != null ? algorithm.number : algorithmByte); 111 this.algorithmByte = algorithmByte; 112 this.algorithm = algorithm != null ? algorithm : SignatureAlgorithm.forByte(algorithmByte); 113 114 this.labels = labels; 115 this.originalTtl = originalTtl; 116 this.signatureExpiration = signatureExpiration; 117 this.signatureInception = signatureInception; 118 this.keyTag = keyTag; 119 this.signerName = signerName; 120 this.signature = signature; 121 } 122 123 public RRSIG(TYPE typeCovered, int algorithm, byte labels, long originalTtl, Date signatureExpiration, 124 Date signatureInception, int keyTag, DnsName signerName, byte[] signature) { 125 this(typeCovered, typeValueOf(typeCovered), null, (byte) algorithm, labels, originalTtl, signatureExpiration, signatureInception, keyTag, 126 signerName, signature); 127 } 128 129 public RRSIG(TYPE typeCovered, int algorithm, byte labels, long originalTtl, Date signatureExpiration, 130 Date signatureInception, int keyTag, String signerName, byte[] signature) { 131 this(typeCovered, typeValueOf(typeCovered), null, (byte) algorithm, labels, originalTtl, signatureExpiration, signatureInception, keyTag, 132 DnsName.from(signerName), signature); 133 } 134 135 public RRSIG(TYPE typeCovered, SignatureAlgorithm algorithm, byte labels, 136 long originalTtl, Date signatureExpiration, Date signatureInception, 137 int keyTag, DnsName signerName, byte[] signature) { 138 this(typeCovered, algorithm.number, labels, originalTtl, signatureExpiration, signatureInception, 139 keyTag, signerName, signature); 140 } 141 142 public RRSIG(TYPE typeCovered, SignatureAlgorithm algorithm, byte labels, 143 long originalTtl, Date signatureExpiration, Date signatureInception, 144 int keyTag, String signerName, byte[] signature) { 145 this(typeCovered, algorithm.number, labels, originalTtl, signatureExpiration, signatureInception, 146 keyTag, DnsName.from(signerName), signature); 147 } 148 149 private static int typeValueOf(TYPE typeCovered) { 150 // Some users construct RRSIGs without a covered type, e.g. if they are only interested in the signature. 151 if (typeCovered == null) { 152 return TYPE.UNKNOWN.getValue(); 153 } 154 return typeCovered.getValue(); 155 } 156 157 public byte[] getSignature() { 158 return signature.clone(); 159 } 160 161 public DataInputStream getSignatureAsDataInputStream() { 162 return new DataInputStream(new ByteArrayInputStream(signature)); 163 } 164 165 public int getSignatureLength() { 166 return signature.length; 167 } 168 169 private transient String base64SignatureCache; 170 171 public String getSignatureBase64() { 172 if (base64SignatureCache == null) { 173 base64SignatureCache = Base64.encodeToString(signature); 174 } 175 return base64SignatureCache; 176 } 177 178 @Override 179 public TYPE getType() { 180 return TYPE.RRSIG; 181 } 182 183 @Override 184 public void serialize(DataOutputStream dos) throws IOException { 185 writePartialSignature(dos); 186 dos.write(signature); 187 } 188 189 @SuppressWarnings("JavaUtilDate") 190 public void writePartialSignature(DataOutputStream dos) throws IOException { 191 dos.writeShort(typeCoveredValue); 192 dos.writeByte(algorithmByte); 193 dos.writeByte(labels); 194 dos.writeInt((int) originalTtl); 195 dos.writeInt((int) (signatureExpiration.getTime() / 1000)); 196 dos.writeInt((int) (signatureInception.getTime() / 1000)); 197 dos.writeShort(keyTag); 198 signerName.writeToStream(dos); 199 } 200 201 @Override 202 public String toString() { 203 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); 204 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 205 return TYPE.asString(typeCoveredValue) + " " 206 + algorithm + " " 207 + labels + " " 208 + originalTtl + " " 209 + dateFormat.format(signatureExpiration) + " " 210 + dateFormat.format(signatureInception) + " " 211 + keyTag + " " 212 + signerName + ". " 213 + getSignatureBase64(); 214 } 215}