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.record.Record.TYPE; 015import org.minidns.util.Base64; 016 017import java.io.ByteArrayInputStream; 018import java.io.DataInputStream; 019import java.io.DataOutputStream; 020import java.io.IOException; 021import java.math.BigInteger; 022import java.util.Arrays; 023 024/** 025 * DNSKEY record payload. 026 */ 027public class DNSKEY extends Data { 028 /** 029 * Whether the key should be used as a secure entry point key. 030 * 031 * see RFC 3757 032 */ 033 public static final short FLAG_SECURE_ENTRY_POINT = 0x1; 034 035 /** 036 * Whether the record holds a revoked key. 037 */ 038 public static final short FLAG_REVOKE = 0x80; 039 040 /** 041 * Whether the record holds a DNS zone key. 042 */ 043 public static final short FLAG_ZONE = 0x100; 044 045 /** 046 * Use the protocol defined in RFC 4034. 047 */ 048 public static final byte PROTOCOL_RFC4034 = 3; 049 050 /** 051 * Bitmap of flags: {@link #FLAG_SECURE_ENTRY_POINT}, {@link #FLAG_REVOKE}, {@link #FLAG_ZONE}. 052 * 053 * @see <a href="https://www.iana.org/assignments/dnskey-flags/dnskey-flags.xhtml">IANA - DNSKEY RR Flags</a> 054 */ 055 public final short flags; 056 057 /** 058 * Must be {@link #PROTOCOL_RFC4034}. 059 */ 060 public final byte protocol; 061 062 /** 063 * The public key's cryptographic algorithm used. 064 * 065 */ 066 public final SignatureAlgorithm algorithm; 067 068 /** 069 * The byte value of the public key's cryptographic algorithm used. 070 * 071 */ 072 public final byte algorithmByte; 073 074 /** 075 * The public key material. The format depends on the algorithm of the key being stored. 076 */ 077 private final byte[] key; 078 079 /** 080 * This DNSKEY's key tag. Calculated just-in-time when using {@link #getKeyTag()} 081 */ 082 private transient Integer keyTag; 083 084 public static DNSKEY parse(DataInputStream dis, int length) throws IOException { 085 short flags = dis.readShort(); 086 byte protocol = dis.readByte(); 087 byte algorithm = dis.readByte(); 088 byte[] key = new byte[length - 4]; 089 dis.readFully(key); 090 return new DNSKEY(flags, protocol, algorithm, key); 091 } 092 093 private DNSKEY(short flags, byte protocol, SignatureAlgorithm algorithm, byte algorithmByte, byte[] key) { 094 this.flags = flags; 095 this.protocol = protocol; 096 097 assert algorithmByte == (algorithm != null ? algorithm.number : algorithmByte); 098 this.algorithmByte = algorithmByte; 099 this.algorithm = algorithm != null ? algorithm : SignatureAlgorithm.forByte(algorithmByte); 100 101 this.key = key; 102 } 103 104 public DNSKEY(short flags, byte protocol, byte algorithm, byte[] key) { 105 this(flags, protocol, SignatureAlgorithm.forByte(algorithm), algorithm, key); 106 } 107 108 public DNSKEY(short flags, byte protocol, SignatureAlgorithm algorithm, byte[] key) { 109 this(flags, protocol, algorithm, algorithm.number, key); 110 } 111 112 @Override 113 public TYPE getType() { 114 return TYPE.DNSKEY; 115 } 116 117 /** 118 * Retrieve the key tag identifying this DNSKEY. 119 * The key tag is used within the DS and RRSIG record to distinguish multiple keys for the same name. 120 * 121 * This implementation is based on the reference implementation shown in RFC 4034 Appendix B. 122 * 123 * @return this DNSKEY's key tag 124 */ 125 public /* unsigned short */ int getKeyTag() { 126 if (keyTag == null) { 127 byte[] recordBytes = toByteArray(); 128 long ac = 0; 129 130 for (int i = 0; i < recordBytes.length; ++i) { 131 ac += ((i & 1) > 0) ? recordBytes[i] & 0xFFL : ((recordBytes[i] & 0xFFL) << 8); 132 } 133 ac += (ac >> 16) & 0xFFFF; 134 keyTag = (int) (ac & 0xFFFF); 135 } 136 return keyTag; 137 } 138 139 @Override 140 public void serialize(DataOutputStream dos) throws IOException { 141 dos.writeShort(flags); 142 dos.writeByte(protocol); 143 dos.writeByte(algorithmByte); 144 dos.write(key); 145 } 146 147 @Override 148 public String toString() { 149 StringBuilder sb = new StringBuilder() 150 .append(flags).append(' ') 151 .append(protocol).append(' ') 152 .append(algorithm).append(' ') 153 .append(Base64.encodeToString(key)); 154 return sb.toString(); 155 } 156 157 public int getKeyLength() { 158 return key.length; 159 } 160 161 public byte[] getKey() { 162 return key.clone(); 163 } 164 165 public DataInputStream getKeyAsDataInputStream() { 166 return new DataInputStream(new ByteArrayInputStream(key)); 167 } 168 169 private transient String keyBase64Cache; 170 171 public String getKeyBase64() { 172 if (keyBase64Cache == null) { 173 keyBase64Cache = Base64.encodeToString(key); 174 } 175 return keyBase64Cache; 176 } 177 178 private transient BigInteger keyBigIntegerCache; 179 180 public BigInteger getKeyBigInteger() { 181 if (keyBigIntegerCache == null) { 182 keyBigIntegerCache = new BigInteger(key); 183 } 184 return keyBigIntegerCache; 185 } 186 187 public boolean keyEquals(byte[] otherKey) { 188 return Arrays.equals(key, otherKey); 189 } 190 191 public boolean isSecureEntryPoint() { 192 return (flags & FLAG_SECURE_ENTRY_POINT) == 1; 193 } 194}