001/* 002 * Copyright 2015-2020 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 public enum CertUsage { 026 027 caConstraint((byte) 0), 028 serviceCertificateConstraint((byte) 1), 029 trustAnchorAssertion((byte) 2), 030 domainIssuedCertificate((byte) 3), 031 ; 032 033 public final byte byteValue; 034 035 CertUsage(byte byteValue) { 036 this.byteValue = byteValue; 037 CERT_USAGE_LUT.put(byteValue, this); 038 } 039 } 040 041 private static final Map<Byte, Selector> SELECTOR_LUT = new HashMap<>(); 042 043 public enum Selector { 044 fullCertificate((byte) 0), 045 subjectPublicKeyInfo((byte) 1), 046 ; 047 048 public final byte byteValue; 049 050 Selector(byte byteValue) { 051 this.byteValue = byteValue; 052 SELECTOR_LUT.put(byteValue, this); 053 } 054 } 055 056 private static final Map<Byte, MatchingType> MATCHING_TYPE_LUT = new HashMap<>(); 057 058 public enum MatchingType { 059 noHash((byte) 0), 060 sha256((byte) 1), 061 sha512((byte) 2), 062 ; 063 064 public final byte byteValue; 065 066 MatchingType(byte byteValue) { 067 this.byteValue = byteValue; 068 MATCHING_TYPE_LUT.put(byteValue, this); 069 } 070 } 071 072 static { 073 // Ensure that the LUTs are initialized. 074 CertUsage.values(); 075 Selector.values(); 076 MatchingType.values(); 077 } 078 079 /** 080 * The provided association that will be used to match the certificate presented in 081 * the TLS handshake. 082 */ 083 public final byte certUsageByte; 084 085 public final CertUsage certUsage; 086 087 /** 088 * Which part of the TLS certificate presented by the server will be matched against the 089 * association data. 090 */ 091 public final byte selectorByte; 092 093 public final Selector selector; 094 095 /** 096 * How the certificate association is presented. 097 */ 098 public final byte matchingTypeByte; 099 100 public final MatchingType matchingType; 101 102 /** 103 * The "certificate association data" to be matched. 104 */ 105 private final byte[] certificateAssociation; 106 107 public static TLSA parse(DataInputStream dis, int length) throws IOException { 108 byte certUsage = dis.readByte(); 109 byte selector = dis.readByte(); 110 byte matchingType = dis.readByte(); 111 byte[] certificateAssociation = new byte[length - 3]; 112 if (dis.read(certificateAssociation) != certificateAssociation.length) throw new IOException(); 113 return new TLSA(certUsage, selector, matchingType, certificateAssociation); 114 } 115 116 TLSA(byte certUsageByte, byte selectorByte, byte matchingTypeByte, byte[] certificateAssociation) { 117 this.certUsageByte = certUsageByte; 118 this.certUsage = CERT_USAGE_LUT.get(certUsageByte); 119 120 this.selectorByte = selectorByte; 121 this.selector = SELECTOR_LUT.get(selectorByte); 122 123 this.matchingTypeByte = matchingTypeByte; 124 this.matchingType = MATCHING_TYPE_LUT.get(matchingTypeByte); 125 126 this.certificateAssociation = certificateAssociation; 127 } 128 129 @Override 130 public Record.TYPE getType() { 131 return Record.TYPE.TLSA; 132 } 133 134 @Override 135 public void serialize(DataOutputStream dos) throws IOException { 136 dos.writeByte(certUsageByte); 137 dos.writeByte(selectorByte); 138 dos.writeByte(matchingTypeByte); 139 dos.write(certificateAssociation); 140 } 141 142 @Override 143 public String toString() { 144 return new StringBuilder() 145 .append(certUsageByte).append(' ') 146 .append(selectorByte).append(' ') 147 .append(matchingTypeByte).append(' ') 148 .append(new BigInteger(1, certificateAssociation).toString(16)).toString(); 149 } 150 151 public byte[] getCertificateAssociation() { 152 return certificateAssociation.clone(); 153 } 154 155 public boolean certificateAssociationEquals(byte[] otherCertificateAssociation) { 156 return Arrays.equals(certificateAssociation, otherCertificateAssociation); 157 } 158}