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 @SuppressWarnings("JavaUtilDate") 082 public static RRSIG parse(DataInputStream dis, byte[] data, int length) throws IOException { 083 TYPE typeCovered = TYPE.getType(dis.readUnsignedShort()); 084 byte algorithm = dis.readByte(); 085 byte labels = dis.readByte(); 086 long originalTtl = dis.readInt() & 0xFFFFFFFFL; 087 Date signatureExpiration = new Date((dis.readInt() & 0xFFFFFFFFL) * 1000); 088 Date signatureInception = new Date((dis.readInt() & 0xFFFFFFFFL) * 1000); 089 int keyTag = dis.readUnsignedShort(); 090 DnsName signerName = DnsName.parse(dis, data); 091 int sigSize = length - signerName.size() - 18; 092 byte[] signature = new byte[sigSize]; 093 if (dis.read(signature) != signature.length) throw new IOException(); 094 return new RRSIG(typeCovered, null, algorithm, labels, originalTtl, signatureExpiration, signatureInception, keyTag, signerName, 095 signature); 096 } 097 098 private RRSIG(TYPE typeCovered, SignatureAlgorithm algorithm, byte algorithmByte, byte labels, long originalTtl, Date signatureExpiration, 099 Date signatureInception, int keyTag, DnsName signerName, byte[] signature) { 100 this.typeCovered = typeCovered; 101 102 assert algorithmByte == (algorithm != null ? algorithm.number : algorithmByte); 103 this.algorithmByte = algorithmByte; 104 this.algorithm = algorithm != null ? algorithm : SignatureAlgorithm.forByte(algorithmByte); 105 106 this.labels = labels; 107 this.originalTtl = originalTtl; 108 this.signatureExpiration = signatureExpiration; 109 this.signatureInception = signatureInception; 110 this.keyTag = keyTag; 111 this.signerName = signerName; 112 this.signature = signature; 113 } 114 115 public RRSIG(TYPE typeCovered, int algorithm, byte labels, long originalTtl, Date signatureExpiration, 116 Date signatureInception, int keyTag, DnsName signerName, byte[] signature) { 117 this(typeCovered, null, (byte) algorithm, labels, originalTtl, signatureExpiration, signatureInception, keyTag, signerName, signature); 118 } 119 120 public RRSIG(TYPE typeCovered, int algorithm, byte labels, long originalTtl, Date signatureExpiration, 121 Date signatureInception, int keyTag, String signerName, byte[] signature) { 122 this(typeCovered, null, (byte) algorithm, labels, originalTtl, signatureExpiration, signatureInception, keyTag, DnsName.from(signerName), signature); 123 } 124 125 public RRSIG(TYPE typeCovered, SignatureAlgorithm algorithm, byte labels, 126 long originalTtl, Date signatureExpiration, Date signatureInception, 127 int keyTag, DnsName signerName, byte[] signature) { 128 this(typeCovered, algorithm.number, labels, originalTtl, signatureExpiration, signatureInception, 129 keyTag, signerName, signature); 130 } 131 132 public RRSIG(TYPE typeCovered, SignatureAlgorithm algorithm, byte labels, 133 long originalTtl, Date signatureExpiration, Date signatureInception, 134 int keyTag, String signerName, byte[] signature) { 135 this(typeCovered, algorithm.number, labels, originalTtl, signatureExpiration, signatureInception, 136 keyTag, DnsName.from(signerName), signature); 137 } 138 139 public byte[] getSignature() { 140 return signature.clone(); 141 } 142 143 public DataInputStream getSignatureAsDataInputStream() { 144 return new DataInputStream(new ByteArrayInputStream(signature)); 145 } 146 147 public int getSignatureLength() { 148 return signature.length; 149 } 150 151 private transient String base64SignatureCache; 152 153 public String getSignatureBase64() { 154 if (base64SignatureCache == null) { 155 base64SignatureCache = Base64.encodeToString(signature); 156 } 157 return base64SignatureCache; 158 } 159 160 @Override 161 public TYPE getType() { 162 return TYPE.RRSIG; 163 } 164 165 @Override 166 public void serialize(DataOutputStream dos) throws IOException { 167 writePartialSignature(dos); 168 dos.write(signature); 169 } 170 171 @SuppressWarnings("JavaUtilDate") 172 public void writePartialSignature(DataOutputStream dos) throws IOException { 173 dos.writeShort(typeCovered.getValue()); 174 dos.writeByte(algorithmByte); 175 dos.writeByte(labels); 176 dos.writeInt((int) originalTtl); 177 dos.writeInt((int) (signatureExpiration.getTime() / 1000)); 178 dos.writeInt((int) (signatureInception.getTime() / 1000)); 179 dos.writeShort(keyTag); 180 signerName.writeToStream(dos); 181 } 182 183 @Override 184 public String toString() { 185 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); 186 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 187 StringBuilder sb = new StringBuilder() 188 .append(typeCovered).append(' ') 189 .append(algorithm).append(' ') 190 .append(labels).append(' ') 191 .append(originalTtl).append(' ') 192 .append(dateFormat.format(signatureExpiration)).append(' ') 193 .append(dateFormat.format(signatureInception)).append(' ') 194 .append(keyTag).append(' ') 195 .append(signerName).append(". ") 196 .append(getSignatureBase64()); 197 return sb.toString(); 198 } 199}