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}