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.dnslabel.DnsLabel; 014import org.minidns.record.Record.TYPE; 015import org.minidns.util.Base32; 016 017import java.io.DataInputStream; 018import java.io.DataOutputStream; 019import java.io.IOException; 020import java.math.BigInteger; 021import java.util.Arrays; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Locale; 025import java.util.Map; 026 027/** 028 * NSEC3 record payload. 029 */ 030public class NSEC3 extends Data { 031 032 /** 033 * This Flag indicates whether this NSEC3 RR may cover unsigned 034 * delegations. 035 */ 036 public static final byte FLAG_OPT_OUT = 0x1; 037 038 private static final Map<Byte, HashAlgorithm> HASH_ALGORITHM_LUT = new HashMap<>(); 039 040 /** 041 * DNSSEC NSEC3 Hash Algorithms. 042 * 043 * @see <a href= 044 * "https://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml#dnssec-nsec3-parameters-3"> 045 * IANA DNSSEC NSEC3 Hash Algorithms</a> 046 */ 047 public enum HashAlgorithm { 048 RESERVED(0, "Reserved"), 049 SHA1(1, "SHA-1"), 050 ; 051 052 HashAlgorithm(int value, String description) { 053 if (value < 0 || value > 255) { 054 throw new IllegalArgumentException(); 055 } 056 this.value = (byte) value; 057 this.description = description; 058 HASH_ALGORITHM_LUT.put(this.value, this); 059 } 060 061 public final byte value; 062 public final String description; 063 064 public static HashAlgorithm forByte(byte b) { 065 return HASH_ALGORITHM_LUT.get(b); 066 } 067 } 068 069 /** 070 * The cryptographic hash algorithm used. If MiniDNS 071 * isn't aware of the hash algorithm, then this field will be 072 * <code>null</code>. 073 * 074 * @see #hashAlgorithmByte 075 */ 076 public final HashAlgorithm hashAlgorithm; 077 078 /** 079 * The byte value of the cryptographic hash algorithm used. 080 */ 081 public final byte hashAlgorithmByte; 082 083 /** 084 * Bitmap of flags: {@link #FLAG_OPT_OUT}. 085 */ 086 public final byte flags; 087 088 /** 089 * The number of iterations the hash algorithm is applied. 090 */ 091 public final int /* unsigned short */ iterations; 092 093 /** 094 * The salt appended to the next owner name before hashing. 095 */ 096 private final byte[] salt; 097 098 /** 099 * The next hashed owner name in hash order. 100 */ 101 private final byte[] nextHashed; 102 103 private final byte[] typeBitmap; 104 105 /** 106 * The RR types existing at the original owner name. 107 */ 108 public final List<TYPE> types; 109 110 public static NSEC3 parse(DataInputStream dis, int length) throws IOException { 111 byte hashAlgorithm = dis.readByte(); 112 byte flags = dis.readByte(); 113 int iterations = dis.readUnsignedShort(); 114 int saltLength = dis.readUnsignedByte(); 115 byte[] salt = new byte[saltLength]; 116 if (dis.read(salt) != salt.length) throw new IOException(); 117 int hashLength = dis.readUnsignedByte(); 118 byte[] nextHashed = new byte[hashLength]; 119 if (dis.read(nextHashed) != nextHashed.length) throw new IOException(); 120 byte[] typeBitmap = new byte[length - (6 + saltLength + hashLength)]; 121 if (dis.read(typeBitmap) != typeBitmap.length) throw new IOException(); 122 List<TYPE> types = NSEC.readTypeBitMap(typeBitmap); 123 return new NSEC3(hashAlgorithm, flags, iterations, salt, nextHashed, types); 124 } 125 126 private NSEC3(HashAlgorithm hashAlgorithm, byte hashAlgorithmByte, byte flags, int iterations, byte[] salt, byte[] nextHashed, List<TYPE> types) { 127 assert hashAlgorithmByte == (hashAlgorithm != null ? hashAlgorithm.value : hashAlgorithmByte); 128 this.hashAlgorithmByte = hashAlgorithmByte; 129 this.hashAlgorithm = hashAlgorithm != null ? hashAlgorithm : HashAlgorithm.forByte(hashAlgorithmByte); 130 131 this.flags = flags; 132 this.iterations = iterations; 133 this.salt = salt; 134 this.nextHashed = nextHashed; 135 this.types = types; 136 this.typeBitmap = NSEC.createTypeBitMap(types); 137 } 138 139 public NSEC3(byte hashAlgorithm, byte flags, int iterations, byte[] salt, byte[] nextHashed, List<TYPE> types) { 140 this(null, hashAlgorithm, flags, iterations, salt, nextHashed, types); 141 } 142 143 public NSEC3(byte hashAlgorithm, byte flags, int iterations, byte[] salt, byte[] nextHashed, TYPE... types) { 144 this(null, hashAlgorithm, flags, iterations, salt, nextHashed, Arrays.asList(types)); 145 } 146 147 @Override 148 public TYPE getType() { 149 return TYPE.NSEC3; 150 } 151 152 @Override 153 public void serialize(DataOutputStream dos) throws IOException { 154 dos.writeByte(hashAlgorithmByte); 155 dos.writeByte(flags); 156 dos.writeShort(iterations); 157 dos.writeByte(salt.length); 158 dos.write(salt); 159 dos.writeByte(nextHashed.length); 160 dos.write(nextHashed); 161 dos.write(typeBitmap); 162 } 163 164 @Override 165 public String toString() { 166 StringBuilder sb = new StringBuilder() 167 .append(hashAlgorithm).append(' ') 168 .append(flags).append(' ') 169 .append(iterations).append(' ') 170 .append(salt.length == 0 ? "-" : new BigInteger(1, salt).toString(16).toUpperCase(Locale.ROOT)).append(' ') 171 .append(Base32.encodeToString(nextHashed)); 172 for (TYPE type : types) { 173 sb.append(' ').append(type); 174 } 175 return sb.toString(); 176 } 177 178 public byte[] getSalt() { 179 return salt.clone(); 180 } 181 182 public int getSaltLength() { 183 return salt.length; 184 } 185 186 public byte[] getNextHashed() { 187 return nextHashed.clone(); 188 } 189 190 private String nextHashedBase32Cache; 191 192 public String getNextHashedBase32() { 193 if (nextHashedBase32Cache == null) { 194 nextHashedBase32Cache = Base32.encodeToString(nextHashed); 195 } 196 return nextHashedBase32Cache; 197 } 198 199 private DnsLabel nextHashedDnsLabelCache; 200 201 public DnsLabel getNextHashedDnsLabel() { 202 if (nextHashedDnsLabelCache == null) { 203 String nextHashedBase32 = getNextHashedBase32(); 204 nextHashedDnsLabelCache = DnsLabel.from(nextHashedBase32); 205 } 206 return nextHashedDnsLabelCache; 207 } 208 209 public void copySaltInto(byte[] dest, int destPos) { 210 System.arraycopy(salt, 0, dest, destPos, salt.length); 211 } 212}