// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)

package org.xbill.DNS;

import java.io.*;
import java.security.PublicKey;
import java.util.*;

/**
 * Key - contains a cryptographic public key.  The data can be converted
 * to objects implementing java.security.interfaces.PublicKey
 *
 * @author Brian Wellington
 * @see DNSSEC
 */

public class KEYRecord extends KEYBase {

    private static final long serialVersionUID = 6385613447571488906L;

    public static class Protocol {
        /**
         * KEY protocol identifiers.
         */

        private Protocol() {
        }

        /**
         * No defined protocol.
         */
        public static final int NONE = 0;

        /**
         * Transaction Level Security
         */
        public static final int TLS = 1;

        /**
         * Email
         */
        public static final int EMAIL = 2;

        /**
         * DNSSEC
         */
        public static final int DNSSEC = 3;

        /**
         * IPSEC Control
         */
        public static final int IPSEC = 4;

        /**
         * Any protocol
         */
        public static final int ANY = 255;

        private static Mnemonic protocols = new Mnemonic("KEY protocol",
                Mnemonic.CASE_UPPER);

        static {
            protocols.setMaximum(0xFF);
            protocols.setNumericAllowed(true);

            protocols.add(NONE, "NONE");
            protocols.add(TLS, "TLS");
            protocols.add(EMAIL, "EMAIL");
            protocols.add(DNSSEC, "DNSSEC");
            protocols.add(IPSEC, "IPSEC");
            protocols.add(ANY, "ANY");
        }

        /**
         * Converts an KEY protocol value into its textual representation
         */
        public static String
        string(int type) {
            return protocols.getText(type);
        }

        /**
         * Converts a textual representation of a KEY protocol into its
         * numeric code.  Integers in the range 0..255 are also accepted.
         *
         * @param s The textual representation of the protocol
         * @return The protocol code, or -1 on error.
         */
        public static int
        value(String s) {
            return protocols.getValue(s);
        }
    }

    public static class Flags {
        /**
         * KEY flags identifiers.
         */

        private Flags() {
        }

        /**
         * KEY cannot be used for confidentiality
         */
        public static final int NOCONF = 0x4000;

        /**
         * KEY cannot be used for authentication
         */
        public static final int NOAUTH = 0x8000;

        /**
         * No key present
         */
        public static final int NOKEY = 0xC000;

        /**
         * Bitmask of the use fields
         */
        public static final int USE_MASK = 0xC000;

        /**
         * Flag 2 (unused)
         */
        public static final int FLAG2 = 0x2000;

        /**
         * Flags extension
         */
        public static final int EXTEND = 0x1000;

        /**
         * Flag 4 (unused)
         */
        public static final int FLAG4 = 0x0800;

        /**
         * Flag 5 (unused)
         */
        public static final int FLAG5 = 0x0400;

        /**
         * Key is owned by a user.
         */
        public static final int USER = 0x0000;

        /**
         * Key is owned by a zone.
         */
        public static final int ZONE = 0x0100;

        /**
         * Key is owned by a host.
         */
        public static final int HOST = 0x0200;

        /**
         * Key owner type 3 (reserved).
         */
        public static final int NTYP3 = 0x0300;

        /**
         * Key owner bitmask.
         */
        public static final int OWNER_MASK = 0x0300;

        /**
         * Flag 8 (unused)
         */
        public static final int FLAG8 = 0x0080;

        /**
         * Flag 9 (unused)
         */
        public static final int FLAG9 = 0x0040;

        /**
         * Flag 10 (unused)
         */
        public static final int FLAG10 = 0x0020;

        /**
         * Flag 11 (unused)
         */
        public static final int FLAG11 = 0x0010;

        /**
         * Signatory value 0
         */
        public static final int SIG0 = 0;

        /**
         * Signatory value 1
         */
        public static final int SIG1 = 1;

        /**
         * Signatory value 2
         */
        public static final int SIG2 = 2;

        /**
         * Signatory value 3
         */
        public static final int SIG3 = 3;

        /**
         * Signatory value 4
         */
        public static final int SIG4 = 4;

        /**
         * Signatory value 5
         */
        public static final int SIG5 = 5;

        /**
         * Signatory value 6
         */
        public static final int SIG6 = 6;

        /**
         * Signatory value 7
         */
        public static final int SIG7 = 7;

        /**
         * Signatory value 8
         */
        public static final int SIG8 = 8;

        /**
         * Signatory value 9
         */
        public static final int SIG9 = 9;

        /**
         * Signatory value 10
         */
        public static final int SIG10 = 10;

        /**
         * Signatory value 11
         */
        public static final int SIG11 = 11;

        /**
         * Signatory value 12
         */
        public static final int SIG12 = 12;

        /**
         * Signatory value 13
         */
        public static final int SIG13 = 13;

        /**
         * Signatory value 14
         */
        public static final int SIG14 = 14;

        /**
         * Signatory value 15
         */
        public static final int SIG15 = 15;

        private static Mnemonic flags = new Mnemonic("KEY flags",
                Mnemonic.CASE_UPPER);

        static {
            flags.setMaximum(0xFFFF);
            flags.setNumericAllowed(false);

            flags.add(NOCONF, "NOCONF");
            flags.add(NOAUTH, "NOAUTH");
            flags.add(NOKEY, "NOKEY");
            flags.add(FLAG2, "FLAG2");
            flags.add(EXTEND, "EXTEND");
            flags.add(FLAG4, "FLAG4");
            flags.add(FLAG5, "FLAG5");
            flags.add(USER, "USER");
            flags.add(ZONE, "ZONE");
            flags.add(HOST, "HOST");
            flags.add(NTYP3, "NTYP3");
            flags.add(FLAG8, "FLAG8");
            flags.add(FLAG9, "FLAG9");
            flags.add(FLAG10, "FLAG10");
            flags.add(FLAG11, "FLAG11");
            flags.add(SIG0, "SIG0");
            flags.add(SIG1, "SIG1");
            flags.add(SIG2, "SIG2");
            flags.add(SIG3, "SIG3");
            flags.add(SIG4, "SIG4");
            flags.add(SIG5, "SIG5");
            flags.add(SIG6, "SIG6");
            flags.add(SIG7, "SIG7");
            flags.add(SIG8, "SIG8");
            flags.add(SIG9, "SIG9");
            flags.add(SIG10, "SIG10");
            flags.add(SIG11, "SIG11");
            flags.add(SIG12, "SIG12");
            flags.add(SIG13, "SIG13");
            flags.add(SIG14, "SIG14");
            flags.add(SIG15, "SIG15");
        }

        /**
         * Converts a textual representation of KEY flags into its
         * numeric code.  Integers in the range 0..65535 are also accepted.
         *
         * @param s The textual representation of the protocol
         * @return The protocol code, or -1 on error.
         */
        public static int
        value(String s) {
            int value;
            try {
                value = Integer.parseInt(s);
                if (value >= 0 && value <= 0xFFFF) {
                    return value;
                }
                return -1;
            } catch (NumberFormatException e) {
            }
            StringTokenizer st = new StringTokenizer(s, "|");
            value = 0;
            while (st.hasMoreTokens()) {
                int val = flags.getValue(st.nextToken());
                if (val < 0) {
                    return -1;
                }
                value |= val;
            }
            return value;
        }
    }

/* flags */
    /**
     * This key cannot be used for confidentiality (encryption)
     */
    public static final int FLAG_NOCONF = Flags.NOCONF;

    /**
     * This key cannot be used for authentication
     */
    public static final int FLAG_NOAUTH = Flags.NOAUTH;

    /**
     * This key cannot be used for authentication or confidentiality
     */
    public static final int FLAG_NOKEY = Flags.NOKEY;

    /**
     * A zone key
     */
    public static final int OWNER_ZONE = Flags.ZONE;

    /**
     * A host/end entity key
     */
    public static final int OWNER_HOST = Flags.HOST;

    /**
     * A user key
     */
    public static final int OWNER_USER = Flags.USER;

/* protocols */
    /**
     * Key was created for use with transaction level security
     */
    public static final int PROTOCOL_TLS = Protocol.TLS;

    /**
     * Key was created for use with email
     */
    public static final int PROTOCOL_EMAIL = Protocol.EMAIL;

    /**
     * Key was created for use with DNSSEC
     */
    public static final int PROTOCOL_DNSSEC = Protocol.DNSSEC;

    /**
     * Key was created for use with IPSEC
     */
    public static final int PROTOCOL_IPSEC = Protocol.IPSEC;

    /**
     * Key was created for use with any protocol
     */
    public static final int PROTOCOL_ANY = Protocol.ANY;

    KEYRecord() {
    }

    Record
    getObject() {
        return new KEYRecord();
    }

    /**
     * Creates a KEY Record from the given data
     *
     * @param flags Flags describing the key's properties
     * @param proto The protocol that the key was created for
     * @param alg   The key's algorithm
     * @param key   Binary data representing the key
     */
    public KEYRecord(Name name, int dclass, long ttl, int flags, int proto, int alg,
                     byte[] key) {
        super(name, Type.KEY, dclass, ttl, flags, proto, alg, key);
    }

    /**
     * Creates a KEY Record from the given data
     *
     * @param flags Flags describing the key's properties
     * @param proto The protocol that the key was created for
     * @param alg   The key's algorithm
     * @param key   The key as a PublicKey
     * @throws DNSSEC.DNSSECException The PublicKey could not be converted into DNS
     *                                format.
     */
    public KEYRecord(Name name, int dclass, long ttl, int flags, int proto, int alg,
                     PublicKey key) throws DNSSEC.DNSSECException {
        super(name, Type.KEY, dclass, ttl, flags, proto, alg,
                DNSSEC.fromPublicKey(key, alg));
        publicKey = key;
    }

    void
    rdataFromString(Tokenizer st, Name origin) throws IOException {
        String flagString = st.getIdentifier();
        flags = Flags.value(flagString);
        if (flags < 0)
            throw st.exception("Invalid flags: " + flagString);
        String protoString = st.getIdentifier();
        proto = Protocol.value(protoString);
        if (proto < 0)
            throw st.exception("Invalid protocol: " + protoString);
        String algString = st.getIdentifier();
        alg = DNSSEC.Algorithm.value(algString);
        if (alg < 0)
            throw st.exception("Invalid algorithm: " + algString);
    /* If this is a null KEY, there's no key data */
        if ((flags & Flags.USE_MASK) == Flags.NOKEY)
            key = null;
        else
            key = st.getBase64();
    }

}
