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.HashMap; 019import java.util.Map; 020 021public class TLSA extends Data { 022 023 private static final Map<Byte, CertUsage> CERT_USAGE_LUT = new HashMap<>(); 024 025 /** 026 * The certificate usage field. 027 * 028 * @see <a href="https://tools.ietf.org/html/rfc6698#section-2.1.1">RFC 6698 ยง 2.1.1</a> 029 * 030 */ 031 public enum CertUsage { 032 033 /** 034 * The given <b>CA</b> certificate (or its public key) MUST be found in at least 035 * one PKIX path to the end entity certificate. 036 * 037 * <p> 038 * PKIX-TA(0) 039 * </p> 040 */ 041 caConstraint((byte) 0), 042 043 /** 044 * The given certificate (or its public key) MUST match the end entity 045 * certificate and MUST pass PKIX validation. Note that the requirement to pass 046 * PKIX validation is what makes this different from 047 * {@link #domainIssuedCertificate}. 048 * 049 * <p> 050 * PKIX-EE(1) 051 * </p> 052 */ 053 serviceCertificateConstraint((byte) 1), 054 055 /** 056 * The given certificate (or its public key) MUST be used as trust anchor when 057 * validating the end entity certificate. 058 * 059 * <p> 060 * DANE-TA(2) 061 * </p> 062 */ 063 trustAnchorAssertion((byte) 2), 064 065 /** 066 * The given certificate (or its public key) MUST match the end entity 067 * certificate. Unlike {@link #serviceCertificateConstraint}, this does not 068 * require PKIX validation. 069 * 070 * <p> 071 * DANE-EE(3) 072 * </p> 073 */ 074 domainIssuedCertificate((byte) 3), 075 ; 076 077 public final byte byteValue; 078 079 CertUsage(byte byteValue) { 080 this.byteValue = byteValue; 081 CERT_USAGE_LUT.put(byteValue, this); 082 } 083 } 084 085 private static final Map<Byte, Selector> SELECTOR_LUT = new HashMap<>(); 086 087 public enum Selector { 088 fullCertificate((byte) 0), 089 subjectPublicKeyInfo((byte) 1), 090 ; 091 092 public final byte byteValue; 093 094 Selector(byte byteValue) { 095 this.byteValue = byteValue; 096 SELECTOR_LUT.put(byteValue, this); 097 } 098 } 099 100 private static final Map<Byte, MatchingType> MATCHING_TYPE_LUT = new HashMap<>(); 101 102 public enum MatchingType { 103 noHash((byte) 0), 104 sha256((byte) 1), 105 sha512((byte) 2), 106 ; 107 108 public final byte byteValue; 109 110 MatchingType(byte byteValue) { 111 this.byteValue = byteValue; 112 MATCHING_TYPE_LUT.put(byteValue, this); 113 } 114 } 115 116 static { 117 // Ensure that the LUTs are initialized. 118 CertUsage.values(); 119 Selector.values(); 120 MatchingType.values(); 121 } 122 123 /** 124 * The provided association that will be used to match the certificate presented in 125 * the TLS handshake. 126 */ 127 public final byte certUsageByte; 128 129 public final CertUsage certUsage; 130 131 /** 132 * Which part of the TLS certificate presented by the server will be matched against the 133 * association data. 134 */ 135 public final byte selectorByte; 136 137 public final Selector selector; 138 139 /** 140 * How the certificate association is presented. 141 */ 142 public final byte matchingTypeByte; 143 144 public final MatchingType matchingType; 145 146 /** 147 * The "certificate association data" to be matched. 148 */ 149 private final byte[] certificateAssociation; 150 151 public static TLSA parse(DataInputStream dis, int length) throws IOException { 152 byte certUsage = dis.readByte(); 153 byte selector = dis.readByte(); 154 byte matchingType = dis.readByte(); 155 byte[] certificateAssociation = new byte[length - 3]; 156 if (dis.read(certificateAssociation) != certificateAssociation.length) throw new IOException(); 157 return new TLSA(certUsage, selector, matchingType, certificateAssociation); 158 } 159 160 TLSA(byte certUsageByte, byte selectorByte, byte matchingTypeByte, byte[] certificateAssociation) { 161 this.certUsageByte = certUsageByte; 162 this.certUsage = CERT_USAGE_LUT.get(certUsageByte); 163 164 this.selectorByte = selectorByte; 165 this.selector = SELECTOR_LUT.get(selectorByte); 166 167 this.matchingTypeByte = matchingTypeByte; 168 this.matchingType = MATCHING_TYPE_LUT.get(matchingTypeByte); 169 170 this.certificateAssociation = certificateAssociation; 171 } 172 173 @Override 174 public Record.TYPE getType() { 175 return Record.TYPE.TLSA; 176 } 177 178 @Override 179 public void serialize(DataOutputStream dos) throws IOException { 180 dos.writeByte(certUsageByte); 181 dos.writeByte(selectorByte); 182 dos.writeByte(matchingTypeByte); 183 dos.write(certificateAssociation); 184 } 185 186 @Override 187 public String toString() { 188 return new StringBuilder() 189 .append(certUsageByte).append(' ') 190 .append(selectorByte).append(' ') 191 .append(matchingTypeByte).append(' ') 192 .append(new BigInteger(1, certificateAssociation).toString(16)).toString(); 193 } 194 195 public byte[] getCertificateAssociation() { 196 return certificateAssociation.clone(); 197 } 198 199 public boolean certificateAssociationEquals(byte[] otherCertificateAssociation) { 200 return Arrays.equals(certificateAssociation, otherCertificateAssociation); 201 } 202}