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 java.io.DataInputStream; 014import java.io.DataOutputStream; 015import java.io.IOException; 016import java.math.BigInteger; 017import java.util.Arrays; 018import java.util.Locale; 019 020import org.minidns.constants.DnssecConstants.DigestAlgorithm; 021import org.minidns.constants.DnssecConstants.SignatureAlgorithm; 022 023/** 024 * DS (Delegation Signer) record payload. 025 * 026 * @see <a href="https://tools.ietf.org/html/rfc4034#section-5">RFC 4034 ยง 5</a> 027 */ 028public abstract class DelegatingDnssecRR extends Data { 029 030 /** 031 * The key tag value of the DNSKEY RR that validates this signature. 032 */ 033 public final int /* unsigned short */ keyTag; 034 035 /** 036 * The cryptographic algorithm used to create the signature. If MiniDNS 037 * isn't aware of the signature algorithm, then this field will be 038 * <code>null</code>. 039 * 040 * @see #algorithmByte 041 */ 042 public final SignatureAlgorithm algorithm; 043 044 /** 045 * The byte value of the cryptographic algorithm used to create the signature. 046 */ 047 public final byte algorithmByte; 048 049 /** 050 * The algorithm used to construct the digest. If MiniDNS 051 * isn't aware of the digest algorithm, then this field will be 052 * <code>null</code>. 053 * 054 * @see #digestTypeByte 055 */ 056 public final DigestAlgorithm digestType; 057 058 /** 059 * The byte value of algorithm used to construct the digest. 060 */ 061 public final byte digestTypeByte; 062 063 /** 064 * The digest build from a DNSKEY. 065 */ 066 protected final byte[] digest; 067 068 protected static SharedData parseSharedData(DataInputStream dis, int length) throws IOException { 069 int keyTag = dis.readUnsignedShort(); 070 byte algorithm = dis.readByte(); 071 byte digestType = dis.readByte(); 072 byte[] digest = new byte[length - 4]; 073 if (dis.read(digest) != digest.length) throw new IOException(); 074 return new SharedData(keyTag, algorithm, digestType, digest); 075 } 076 077 protected static final class SharedData { 078 final int keyTag; 079 final byte algorithm; 080 final byte digestType; 081 final byte[] digest; 082 083 private SharedData(int keyTag, byte algorithm, byte digestType, byte[] digest) { 084 this.keyTag = keyTag; 085 this.algorithm = algorithm; 086 this.digestType = digestType; 087 this.digest = digest; 088 } 089 } 090 091 protected DelegatingDnssecRR(int keyTag, SignatureAlgorithm algorithm, byte algorithmByte, DigestAlgorithm digestType, byte digestTypeByte, byte[] digest) { 092 this.keyTag = keyTag; 093 094 assert algorithmByte == (algorithm != null ? algorithm.number : algorithmByte); 095 this.algorithmByte = algorithmByte; 096 this.algorithm = algorithm != null ? algorithm : SignatureAlgorithm.forByte(algorithmByte); 097 098 assert digestTypeByte == (digestType != null ? digestType.value : digestTypeByte); 099 this.digestTypeByte = digestTypeByte; 100 this.digestType = digestType != null ? digestType : DigestAlgorithm.forByte(digestTypeByte); 101 102 assert digest != null; 103 this.digest = digest; 104 } 105 106 protected DelegatingDnssecRR(int keyTag, byte algorithm, byte digestType, byte[] digest) { 107 this(keyTag, null, algorithm, null, digestType, digest); 108 } 109 110 protected DelegatingDnssecRR(int keyTag, SignatureAlgorithm algorithm, DigestAlgorithm digestType, byte[] digest) { 111 this(keyTag, algorithm, algorithm.number, digestType, digestType.value, digest); 112 } 113 114 protected DelegatingDnssecRR(int keyTag, SignatureAlgorithm algorithm, byte digestType, byte[] digest) { 115 this(keyTag, algorithm, algorithm.number, null, digestType, digest); 116 } 117 118 @Override 119 public void serialize(DataOutputStream dos) throws IOException { 120 dos.writeShort(keyTag); 121 dos.writeByte(algorithmByte); 122 dos.writeByte(digestTypeByte); 123 dos.write(digest); 124 } 125 126 @Override 127 public String toString() { 128 StringBuilder sb = new StringBuilder() 129 .append(keyTag).append(' ') 130 .append(algorithm).append(' ') 131 .append(digestType).append(' ') 132 .append(new BigInteger(1, digest).toString(16).toUpperCase(Locale.ROOT)); 133 return sb.toString(); 134 } 135 136 private transient BigInteger digestBigIntCache; 137 138 public BigInteger getDigestBigInteger() { 139 if (digestBigIntCache == null) { 140 digestBigIntCache = new BigInteger(1, digest); 141 } 142 return digestBigIntCache; 143 } 144 145 private transient String digestHexCache; 146 147 public String getDigestHex() { 148 if (digestHexCache == null) { 149 digestHexCache = getDigestBigInteger().toString(16).toUpperCase(Locale.ROOT); 150 } 151 return digestHexCache; 152 } 153 154 public boolean digestEquals(byte[] otherDigest) { 155 return Arrays.equals(digest, otherDigest); 156 } 157}