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;
018
019import org.minidns.constants.DnssecConstants.DigestAlgorithm;
020import org.minidns.constants.DnssecConstants.SignatureAlgorithm;
021
022/**
023 * DS (Delegation Signer) record payload.
024 *
025 * @see <a href="https://tools.ietf.org/html/rfc4034#section-5">RFC 4034 ยง 5</a>
026 */
027public abstract class DelegatingDnssecRR extends Data {
028
029    /**
030     * The key tag value of the DNSKEY RR that validates this signature.
031     */
032    public final int /* unsigned short */ keyTag;
033
034    /**
035     * The cryptographic algorithm used to create the signature. If MiniDNS
036     * isn't aware of the signature algorithm, then this field will be
037     * <code>null</code>.
038     * 
039     * @see #algorithmByte
040     */
041    public final SignatureAlgorithm algorithm;
042
043    /**
044     * The byte value of the cryptographic algorithm used to create the signature.
045     */
046    public final byte algorithmByte;
047
048    /**
049     * The algorithm used to construct the digest. If MiniDNS
050     * isn't aware of the digest algorithm, then this field will be
051     * <code>null</code>.
052     * 
053     * @see #digestTypeByte
054     */
055    public final DigestAlgorithm digestType;
056
057    /**
058     * The byte value of algorithm used to construct the digest.
059     */
060    public final byte digestTypeByte;
061
062    /**
063     * The digest build from a DNSKEY.
064     */
065    protected final byte[] digest;
066
067    protected static SharedData parseSharedData(DataInputStream dis, int length) throws IOException {
068        int keyTag = dis.readUnsignedShort();
069        byte algorithm = dis.readByte();
070        byte digestType = dis.readByte();
071        byte[] digest = new byte[length - 4];
072        if (dis.read(digest) != digest.length) throw new IOException();
073        return new SharedData(keyTag, algorithm, digestType, digest);
074    }
075
076    protected static final class SharedData {
077        protected final int keyTag;
078        protected final byte algorithm;
079        protected final byte digestType;
080        protected final byte[] digest;
081
082        private SharedData(int keyTag, byte algorithm, byte digestType, byte[] digest) {
083            this.keyTag = keyTag;
084            this.algorithm = algorithm;
085            this.digestType = digestType;
086            this.digest = digest;
087        }
088    }
089
090    protected DelegatingDnssecRR(int keyTag, SignatureAlgorithm algorithm, byte algorithmByte, DigestAlgorithm digestType, byte digestTypeByte, byte[] digest) {
091        this.keyTag = keyTag;
092
093        assert algorithmByte == (algorithm != null ? algorithm.number : algorithmByte);
094        this.algorithmByte = algorithmByte;
095        this.algorithm = algorithm != null ? algorithm : SignatureAlgorithm.forByte(algorithmByte);
096
097        assert digestTypeByte == (digestType != null ? digestType.value : digestTypeByte);
098        this.digestTypeByte = digestTypeByte;
099        this.digestType = digestType != null ? digestType : DigestAlgorithm.forByte(digestTypeByte);
100
101        assert digest != null;
102        this.digest = digest;
103    }
104
105    protected DelegatingDnssecRR(int keyTag, byte algorithm, byte digestType, byte[] digest) {
106        this(keyTag, null, algorithm, null, digestType, digest);
107    }
108
109    protected DelegatingDnssecRR(int keyTag, SignatureAlgorithm algorithm, DigestAlgorithm digestType, byte[] digest) {
110        this(keyTag, algorithm, algorithm.number, digestType, digestType.value, digest);
111    }
112
113    protected DelegatingDnssecRR(int keyTag, SignatureAlgorithm algorithm, byte digestType, byte[] digest) {
114        this(keyTag, algorithm, algorithm.number, null, digestType, digest);
115    }
116
117    @Override
118    public void serialize(DataOutputStream dos) throws IOException {
119        dos.writeShort(keyTag);
120        dos.writeByte(algorithmByte);
121        dos.writeByte(digestTypeByte);
122        dos.write(digest);
123    }
124
125    @Override
126    public String toString() {
127        StringBuilder sb = new StringBuilder()
128                .append(keyTag).append(' ')
129                .append(algorithm).append(' ')
130                .append(digestType).append(' ')
131                .append(new BigInteger(1, digest).toString(16).toUpperCase());
132        return sb.toString();
133    }
134
135    private transient BigInteger digestBigIntCache;
136
137    public BigInteger getDigestBigInteger() {
138        if (digestBigIntCache == null) {
139            digestBigIntCache = new BigInteger(1, digest);
140        }
141        return digestBigIntCache;
142    }
143
144    private transient String digestHexCache;
145
146    public String getDigestHex() {
147        if (digestHexCache == null) {
148            digestHexCache = getDigestBigInteger().toString(16).toUpperCase();
149        }
150        return digestHexCache;
151    }
152
153    public boolean digestEquals(byte[] otherDigest) {
154        return Arrays.equals(digest, otherDigest);
155    }
156}