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