/*******************************************************************************
 * Copyright (c) 2026, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#pragma once

#include <string>
#include <vector>
#include <map>
#include <set>
#include <cstdint>
#include <ctime>
#include <mutex>
#include <memory>

#include <uuidp.h>
#include <netplus/socket.h>
#include <netplus/connection.h>
#include <netplus/eventapi.h>
#include <netplus/crypto/aes.h>
#include <netplus/crypto/sha.h>

#include "backend.h"
#include "exception.h"
#include "plugin.h"

#ifdef Windows
#define VISIBILITY __declspec(dllexport)
#else
#define VISIBILITY __attribute__((visibility("default")))
#endif

namespace authdb {

    /***************************************************************************
     * Kerberos 5 Message Types (RFC 4120)
     **************************************************************************/
    enum Krb5MsgType : uint8_t {
        KRB_AS_REQ    = 10,  /* Authentication Service Request  */
        KRB_AS_REP    = 11,  /* Authentication Service Reply    */
        KRB_TGS_REQ   = 12,  /* Ticket-Granting Service Request */
        KRB_TGS_REP   = 13,  /* Ticket-Granting Service Reply   */
        KRB_AP_REQ    = 14,  /* Application Request             */
        KRB_AP_REP    = 15,  /* Application Reply               */
        KRB_ERROR     = 30   /* Kerberos Error                  */
    };

    /***************************************************************************
     * Kerberos 5 Error Codes (subset of RFC 4120 §7.5.9)
     **************************************************************************/
    enum Krb5ErrorCode : uint32_t {
        KDC_ERR_NONE                  = 0,
        KDC_ERR_NAME_EXP             = 1,
        KDC_ERR_SERVICE_EXP          = 2,
        KDC_ERR_BAD_PVNO             = 3,
        KDC_ERR_C_OLD_MAST_KVNO     = 4,
        KDC_ERR_S_OLD_MAST_KVNO     = 5,
        KDC_ERR_C_PRINCIPAL_UNKNOWN  = 6,
        KDC_ERR_S_PRINCIPAL_UNKNOWN  = 7,
        KDC_ERR_PRINCIPAL_NOT_UNIQUE = 8,
        KDC_ERR_NULL_KEY             = 9,
        KDC_ERR_CANNOT_POSTDATE     = 10,
        KDC_ERR_NEVER_VALID         = 11,
        KDC_ERR_POLICY              = 12,
        KDC_ERR_BADOPTION           = 13,
        KDC_ERR_PREAUTH_FAILED      = 24,
        KDC_ERR_PREAUTH_REQUIRED    = 25,
        KDC_ERR_WRONG_REALM         = 68,
        KRB_AP_ERR_BAD_INTEGRITY    = 31,
        KRB_AP_ERR_TKT_EXPIRED      = 32,
        KRB_AP_ERR_TKT_NYV          = 33,
        KRB_AP_ERR_REPEAT           = 34,
        KRB_AP_ERR_SKEW             = 37,
        KRB_ERR_GENERIC             = 60
    };

    /***************************************************************************
     * Kerberos 5 Encryption Types (RFC 3962, RFC 8009)
     * MIT Kerberos / Heimdal compatible
     **************************************************************************/
    enum Krb5EncType : int32_t {
        KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1   = 17,  /* aes128-cts-hmac-sha1-96  */
        KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1   = 18,  /* aes256-cts-hmac-sha1-96  */
        KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256 = 19,  /* aes128-cts-hmac-sha256-128 */
        KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384 = 20   /* aes256-cts-hmac-sha384-192 */
    };

    /***************************************************************************
     * Kerberos 5 Checksum Types
     **************************************************************************/
    enum Krb5CksumType : int32_t {
        KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128  = 15,
        KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256  = 16,
        KRB5_CKSUMTYPE_HMAC_SHA256_128      = 19,
        KRB5_CKSUMTYPE_HMAC_SHA384_192      = 20
    };

    /***************************************************************************
     * PA-DATA Types (RFC 4120 §7.5.2)
     **************************************************************************/
    enum Krb5PaDataType : int32_t {
        KRB5_PADATA_ENC_TIMESTAMP   = 2,   /* PA-ENC-TIMESTAMP          */
        KRB5_PADATA_ETYPE_INFO      = 11,  /* PA-ETYPE-INFO             */
        KRB5_PADATA_ETYPE_INFO2     = 19,  /* PA-ETYPE-INFO2            */
        KRB5_PADATA_TGS_REQ         = 1    /* PA-TGS-REQ (AP-REQ in padata) */
    };

    /***************************************************************************
     * Kerberos 5 Key Usage Numbers (RFC 3961 §7)
     **************************************************************************/
    enum Krb5KeyUsage : int32_t {
        KRB5_KU_AS_REQ_PA_ENC_TS       = 1,   /* PA-ENC-TIMESTAMP encrypt    */
        KRB5_KU_TGS_REQ_AUTH           = 7,   /* TGS-REQ authenticator       */
        KRB5_KU_TGS_REQ_AUTH_CKSUM     = 6,   /* TGS-REQ authenticator cksum */
        KRB5_KU_AP_REQ_AUTH             = 11,  /* AP-REQ authenticator        */
        KRB5_KU_AP_REQ_AUTH_CKSUM       = 10,  /* AP-REQ authenticator cksum  */
        KRB5_KU_AS_REP_ENC_PART        = 3,   /* AS-REP encrypted part       */
        KRB5_KU_TGS_REP_ENC_PART       = 8,   /* TGS-REP encrypted part      */
        KRB5_KU_TGS_REP_ENC_PART_SUB   = 9,   /* TGS-REP enc part (subkey)   */
        KRB5_KU_AP_REP_ENC_PART        = 12,  /* AP-REP encrypted part       */
        KRB5_KU_TICKET                  = 2    /* Ticket encrypted part       */
    };

    /***************************************************************************
     * Kerberos 5 Principal Types
     **************************************************************************/
    enum Krb5PrincipalType : uint8_t {
        KRB5_NT_UNKNOWN   = 0,
        KRB5_NT_PRINCIPAL = 1,   /* User principal           */
        KRB5_NT_SRV_INST  = 2,   /* Service and host         */
        KRB5_NT_SRV_HST   = 3,   /* Service with host as instance */
        KRB5_NT_ENTERPRISE = 10  /* Enterprise principal     */
    };

    /***************************************************************************
     * Kerberos 5 Ticket Flags (RFC 4120 §5.3)
     **************************************************************************/
    enum Krb5TicketFlags : uint32_t {
        KRB5_TF_FORWARDABLE    = 0x40000000,
        KRB5_TF_FORWARDED      = 0x20000000,
        KRB5_TF_PROXIABLE      = 0x10000000,
        KRB5_TF_PROXY          = 0x08000000,
        KRB5_TF_MAY_POSTDATE   = 0x04000000,
        KRB5_TF_POSTDATED      = 0x02000000,
        KRB5_TF_INVALID        = 0x01000000,
        KRB5_TF_RENEWABLE      = 0x00800000,
        KRB5_TF_INITIAL        = 0x00400000,
        KRB5_TF_PRE_AUTHENT    = 0x00200000,
        KRB5_TF_HW_AUTHENT     = 0x00100000,
        KRB5_TF_ENC_PA_REP     = 0x00010000
    };

    /* Forward declarations for Asn1Der */
    struct Krb5Principal;
    struct Krb5SessionKey;
    struct Krb5Ticket;
    struct Krb5EncTicketPart;
    struct Krb5Authenticator;
    struct Krb5KdcReq;
    struct Krb5KdcRep;
    struct Krb5EncKdcRepPart;
    struct Krb5ApReq;
    struct Krb5ApRep;
    struct Krb5Error;

    /***************************************************************************
     * ASN.1 DER Encoder / Decoder (RFC 4120 wire format)
     *
     * Minimal but complete implementation for Kerberos 5 messages.
     * Compatible with MIT Kerberos and Heimdal ASN.1 encoding.
     **************************************************************************/
    class VISIBILITY Asn1Der {
    public:
        /* ASN.1 tag classes and types */
        static constexpr uint8_t TAG_BOOLEAN          = 0x01;
        static constexpr uint8_t TAG_INTEGER          = 0x02;
        static constexpr uint8_t TAG_BIT_STRING       = 0x03;
        static constexpr uint8_t TAG_OCTET_STRING     = 0x04;
        static constexpr uint8_t TAG_NULL             = 0x05;
        static constexpr uint8_t TAG_OID              = 0x06;
        static constexpr uint8_t TAG_SEQUENCE         = 0x30;
        static constexpr uint8_t TAG_SET              = 0x31;
        static constexpr uint8_t TAG_GENERALIZED_TIME = 0x18;
        static constexpr uint8_t TAG_GENERAL_STRING   = 0x1B;

        /* ASN.1 class bits */
        static constexpr uint8_t CLASS_CONTEXT        = 0x80;
        static constexpr uint8_t CLASS_APPLICATION    = 0x40;
        static constexpr uint8_t CONSTRUCTED          = 0x20;

        /* Application tags (constructed) for Kerberos messages */
        static constexpr uint8_t APP_TICKET           = 0x61; /* [APPLICATION 1]  */
        static constexpr uint8_t APP_AUTHENTICATOR    = 0x62; /* [APPLICATION 2]  */
        static constexpr uint8_t APP_ENC_TICKET_PART  = 0x63; /* [APPLICATION 3]  */
        static constexpr uint8_t APP_AS_REQ           = 0x6A; /* [APPLICATION 10] */
        static constexpr uint8_t APP_AS_REP           = 0x6B; /* [APPLICATION 11] */
        static constexpr uint8_t APP_TGS_REQ          = 0x6C; /* [APPLICATION 12] */
        static constexpr uint8_t APP_TGS_REP          = 0x6D; /* [APPLICATION 13] */
        static constexpr uint8_t APP_AP_REQ           = 0x6E; /* [APPLICATION 14] */
        static constexpr uint8_t APP_AP_REP           = 0x6F; /* [APPLICATION 15] */
        static constexpr uint8_t APP_ENC_AS_REP_PART  = 0x79; /* [APPLICATION 25] */
        static constexpr uint8_t APP_ENC_TGS_REP_PART = 0x7A; /* [APPLICATION 26] */
        static constexpr uint8_t APP_KRB_ERROR        = 0x7E; /* [APPLICATION 30] */

        /* ---- Encoding ---- */

        /* Encode a DER length */
        static void encodeLength(std::vector<uint8_t> &buf, size_t len);

        /* Encode a TLV (tag-length-value) */
        static void encodeTLV(std::vector<uint8_t> &buf, uint8_t tag,
                              const std::vector<uint8_t> &value);

        /* Encode a context-tagged constructed field: [n] EXPLICIT */
        static void encodeContextTag(std::vector<uint8_t> &buf, uint8_t tagnum,
                                     const std::vector<uint8_t> &value);

        /* Encode an INTEGER */
        static std::vector<uint8_t> encodeInteger(int32_t val);

        /* Encode an unsigned INTEGER (for UInt32) */
        static std::vector<uint8_t> encodeUInteger(uint32_t val);

        /* Encode an OCTET STRING */
        static std::vector<uint8_t> encodeOctetString(const std::vector<uint8_t> &data);

        /* Encode a GeneralString */
        static std::vector<uint8_t> encodeGeneralString(const std::string &s);

        /* Encode a GeneralizedTime (YYYYMMDDHHmmSSZ) */
        static std::vector<uint8_t> encodeGeneralizedTime(time_t t);

        /* Encode a BIT STRING (flags, 4 bytes padded) */
        static std::vector<uint8_t> encodeBitString(uint32_t flags);

        /* Encode a SEQUENCE wrapping the given contents */
        static std::vector<uint8_t> encodeSequence(const std::vector<uint8_t> &contents);

        /* Encode a PrincipalName (RFC 4120 §5.2.2) */
        static std::vector<uint8_t> encodePrincipalName(const Krb5Principal &princ);

        /* Encode an EncryptedData (RFC 4120 §5.2.9) */
        static std::vector<uint8_t> encodeEncryptedData(Krb5EncType etype, uint32_t kvno,
                                                         const std::vector<uint8_t> &cipher);

        /* Encode an EncryptionKey (RFC 4120 §5.2.9) */
        static std::vector<uint8_t> encodeEncryptionKey(const Krb5SessionKey &key);

        /* Encode a full Ticket (RFC 4120 §5.3) */
        static std::vector<uint8_t> encodeTicket(const Krb5Ticket &ticket);

        /* Encode a KDC-REQ (AS-REQ or TGS-REQ) (RFC 4120 §5.4.1) */
        static std::vector<uint8_t> encodeKdcReq(const Krb5KdcReq &req);

        /* Encode a KDC-REP (AS-REP or TGS-REP) (RFC 4120 §5.4.2) */
        static std::vector<uint8_t> encodeKdcRep(const Krb5KdcRep &rep);

        /* Encode an EncTicketPart (RFC 4120 §5.3) */
        static std::vector<uint8_t> encodeEncTicketPart(const Krb5EncTicketPart &etp);

        /* Encode an EncKDCRepPart (RFC 4120 §5.4.2) */
        static std::vector<uint8_t> encodeEncKdcRepPart(const Krb5EncKdcRepPart &erp,
                                                         bool isAsRep);

        /* Encode an Authenticator (RFC 4120 §5.5.1) */
        static std::vector<uint8_t> encodeAuthenticator(const Krb5Authenticator &auth);

        /* Encode an AP-REQ (RFC 4120 §5.5.1) */
        static std::vector<uint8_t> encodeApReq(const Krb5ApReq &apReq);

        /* Encode an AP-REP (RFC 4120 §5.5.2) */
        static std::vector<uint8_t> encodeApRep(const Krb5ApRep &apRep);

        /* Encode a KRB-ERROR (RFC 4120 §5.9.1) */
        static std::vector<uint8_t> encodeKrbError(const Krb5Error &err);

        /* Encode a PA-DATA (RFC 4120 §5.2.7) */
        static std::vector<uint8_t> encodePaData(int32_t paType,
                                                  const std::vector<uint8_t> &paValue);

        /* Encode PA-ENC-TIMESTAMP (RFC 4120 §5.2.7.2) */
        static std::vector<uint8_t> encodePaEncTimestamp(time_t timestamp, uint32_t usec);

        /* Encode ETYPE-INFO2 (RFC 4120 §5.2.7.5) */
        static std::vector<uint8_t> encodeEtypeInfo2(Krb5EncType etype,
                                                      const std::string &salt);

        /* ---- Decoding ---- */

        /* Parse a DER tag + length, return pointer to content and content length */
        static bool parseTLV(const uint8_t *data, size_t len, size_t &off,
                             uint8_t &tag, const uint8_t *&content, size_t &content_len);

        /* Parse an INTEGER */
        static bool parseInteger(const uint8_t *data, size_t len, int32_t &val);

        /* Parse an unsigned INTEGER (UInt32) */
        static bool parseUInteger(const uint8_t *data, size_t len, uint32_t &val);

        /* Parse an OCTET STRING (just returns the content bytes) */
        static bool parseOctetString(const uint8_t *data, size_t len,
                                     std::vector<uint8_t> &out);

        /* Parse a GeneralString */
        static bool parseGeneralString(const uint8_t *data, size_t len, std::string &out);

        /* Parse a GeneralizedTime */
        static bool parseGeneralizedTime(const uint8_t *data, size_t len, time_t &t);

        /* Parse a BIT STRING into flags (uint32_t) */
        static bool parseBitString(const uint8_t *data, size_t len, uint32_t &flags);

        /* Parse a PrincipalName (returns type + name components) */
        static bool parsePrincipalName(const uint8_t *data, size_t len,
                                       Krb5PrincipalType &type,
                                       std::vector<std::string> &components);

        /* Parse an EncryptedData */
        static bool parseEncryptedData(const uint8_t *data, size_t len,
                                       Krb5EncType &etype, uint32_t &kvno,
                                       std::vector<uint8_t> &cipher);

        /* Parse an EncryptionKey */
        static bool parseEncryptionKey(const uint8_t *data, size_t len,
                                       Krb5SessionKey &key);

        /* Parse a Ticket (including APPLICATION 1 wrapper) */
        static bool parseTicket(const uint8_t *data, size_t len, Krb5Ticket &ticket);

        /* Parse a KDC-REQ body */
        static bool parseKdcReq(const uint8_t *data, size_t len, Krb5KdcReq &req);

        /* Parse a KDC-REP */
        static bool parseKdcRep(const uint8_t *data, size_t len, Krb5KdcRep &rep);

        /* Parse an EncTicketPart */
        static bool parseEncTicketPart(const uint8_t *data, size_t len,
                                       Krb5EncTicketPart &etp);

        /* Parse an EncKDCRepPart */
        static bool parseEncKdcRepPart(const uint8_t *data, size_t len,
                                       Krb5EncKdcRepPart &erp);

        /* Parse an Authenticator */
        static bool parseAuthenticator(const uint8_t *data, size_t len,
                                       Krb5Authenticator &auth);

        /* Parse an AP-REQ */
        static bool parseApReq(const uint8_t *data, size_t len, Krb5ApReq &apReq);

        /* Parse an AP-REP */
        static bool parseApRep(const uint8_t *data, size_t len, Krb5ApRep &apRep);

        /* Parse a KRB-ERROR */
        static bool parseKrbError(const uint8_t *data, size_t len, Krb5Error &err);

        /* Parse a PA-ENC-TIMESTAMP */
        static bool parsePaEncTimestamp(const uint8_t *data, size_t len,
                                        time_t &ts, uint32_t &usec);

        /* Extract context-tagged field from within a SEQUENCE */
        static bool findContextTag(const uint8_t *data, size_t len,
                                   uint8_t tagnum,
                                   const uint8_t *&content, size_t &content_len);
    };

    /***************************************************************************
     * Kerberos Principal Name
     **************************************************************************/
    struct VISIBILITY Krb5Principal {
        Krb5PrincipalType  type;
        std::string        realm;       /* e.g. "EXAMPLE.COM"    */
        std::string        name;        /* e.g. "user" or "host" */
        std::string        instance;    /* e.g. "server.example.com" (for SRV) */

        Krb5Principal();
        Krb5Principal(Krb5PrincipalType t, const std::string &realm,
                      const std::string &name, const std::string &instance = "");

        /* ASN.1 DER serialization (standard wire format) */
        std::vector<uint8_t> serialize() const;
        bool                 deserialize(const uint8_t *data, size_t len, size_t &consumed);

        /* Name components for ASN.1 PrincipalName encoding */
        std::vector<std::string> nameComponents() const;

        std::string          toString() const;
    };

    /***************************************************************************
     * Kerberos Session Key
     **************************************************************************/
    struct VISIBILITY Krb5SessionKey {
        Krb5EncType            enctype;
        std::vector<uint8_t>   keydata;   /* 16 or 32 bytes depending on enctype */

        Krb5SessionKey();

        std::vector<uint8_t> serialize() const;
        bool                 deserialize(const uint8_t *data, size_t len, size_t &consumed);
    };

    /***************************************************************************
     * Kerberos Ticket
     **************************************************************************/
    struct VISIBILITY Krb5Ticket {
        uint8_t              tkt_vno;    /* always 5                        */
        Krb5Principal        sname;      /* service principal               */
        Krb5EncType          enc_etype;  /* encryption type used            */
        uint32_t             enc_kvno;   /* key version number              */
        std::vector<uint8_t> enc_part;   /* encrypted EncTicketPart         */

        Krb5Ticket();

        std::vector<uint8_t> serialize() const;
        bool                 deserialize(const uint8_t *data, size_t len, size_t &consumed);
    };

    /***************************************************************************
     * Encrypted Ticket Part (plaintext before encryption)
     **************************************************************************/
    struct VISIBILITY Krb5EncTicketPart {
        uint32_t         flags;        /* ticket flags                  */
        Krb5SessionKey   session_key;
        Krb5Principal    cname;        /* client principal              */
        std::string      transited;    /* transited realms encoding     */
        time_t           authtime;     /* time of initial auth          */
        time_t           starttime;    /* ticket valid from             */
        time_t           endtime;      /* ticket expires                */
        time_t           renew_till;   /* renewable until               */
        uuid::uuid       uid;         /* authdb user/client UUID       */

        Krb5EncTicketPart();

        std::vector<uint8_t> serialize() const;
        bool                 deserialize(const uint8_t *data, size_t len, size_t &consumed);
    };

    /***************************************************************************
     * Kerberos Authenticator
     **************************************************************************/
    struct VISIBILITY Krb5Authenticator {
        uint8_t          authenticator_vno;   /* always 5              */
        Krb5Principal    cname;               /* client principal      */
        time_t           ctime;               /* client time           */
        uint32_t         cusec;               /* microseconds          */
        uint32_t         seq_number;          /* sequence number       */

        Krb5Authenticator();

        std::vector<uint8_t> serialize() const;
        bool                 deserialize(const uint8_t *data, size_t len, size_t &consumed);
    };

    /***************************************************************************
     * Kerberos AS-REQ / TGS-REQ
     **************************************************************************/
    struct VISIBILITY Krb5KdcReq {
        uint8_t              pvno;          /* protocol version (5)       */
        Krb5MsgType          msg_type;      /* KRB_AS_REQ or KRB_TGS_REQ */
        uint32_t             kdc_options;   /* KDC options flags          */
        Krb5Principal        cname;         /* client principal           */
        Krb5Principal        sname;         /* requested service          */
        time_t               from;          /* requested start time       */
        time_t               till;          /* requested expiration       */
        time_t               rtime;         /* requested renew until      */
        uint32_t             nonce;         /* random nonce               */
        std::vector<Krb5EncType> etypes;    /* requested enc types        */
        std::vector<uint8_t> padata;        /* pre-auth data (PA-DATA)    */
        Krb5Ticket           tgt;           /* TGT (only for TGS-REQ)    */
        std::vector<uint8_t> authenticator; /* AP-REQ in padata (TGS-REQ)*/

        Krb5KdcReq();

        std::vector<uint8_t> serialize() const;
        bool                 deserialize(const uint8_t *data, size_t len, size_t &consumed);
    };

    /***************************************************************************
     * Kerberos AS-REP / TGS-REP
     **************************************************************************/
    struct VISIBILITY Krb5KdcRep {
        uint8_t              pvno;
        Krb5MsgType          msg_type;       /* KRB_AS_REP or KRB_TGS_REP */
        Krb5Principal        cname;
        Krb5Ticket           ticket;         /* the ticket (TGT or service) */
        Krb5EncType          enc_etype;      /* enc-part encryption type    */
        uint32_t             enc_kvno;       /* enc-part key version        */
        std::vector<uint8_t> enc_part;       /* encrypted with client key   */

        Krb5KdcRep();

        std::vector<uint8_t> serialize() const;
        bool                 deserialize(const uint8_t *data, size_t len, size_t &consumed);
    };

    /***************************************************************************
     * Encrypted KDC-REP Part (session key + ticket info for client)
     **************************************************************************/
    struct VISIBILITY Krb5EncKdcRepPart {
        Krb5SessionKey   session_key;
        uint32_t         nonce;
        uint32_t         flags;
        time_t           authtime;
        time_t           starttime;
        time_t           endtime;
        time_t           renew_till;
        Krb5Principal    sname;

        Krb5EncKdcRepPart();

        std::vector<uint8_t> serialize(bool isAsRep) const;
        bool                 deserialize(const uint8_t *data, size_t len, size_t &consumed);
    };

    /***************************************************************************
     * Kerberos AP-REQ
     **************************************************************************/
    struct VISIBILITY Krb5ApReq {
        uint8_t              pvno;
        Krb5MsgType          msg_type;      /* KRB_AP_REQ */
        uint32_t             ap_options;    /* AP options flags */
        Krb5Ticket           ticket;
        Krb5EncType          auth_etype;    /* authenticator enc type */
        uint32_t             auth_kvno;     /* authenticator key version */
        std::vector<uint8_t> authenticator; /* encrypted with session key */

        Krb5ApReq();

        std::vector<uint8_t> serialize() const;
        bool                 deserialize(const uint8_t *data, size_t len, size_t &consumed);
    };

    /***************************************************************************
     * Kerberos AP-REP
     **************************************************************************/
    struct VISIBILITY Krb5ApRep {
        uint8_t              pvno;
        Krb5MsgType          msg_type;      /* KRB_AP_REP */
        Krb5EncType          enc_etype;     /* encrypted data enc type */
        uint32_t             enc_kvno;      /* encrypted data key version */
        std::vector<uint8_t> enc_part;      /* encrypted with session key */

        Krb5ApRep();

        std::vector<uint8_t> serialize() const;
        bool                 deserialize(const uint8_t *data, size_t len, size_t &consumed);
    };

    /***************************************************************************
     * Kerberos Error Message
     **************************************************************************/
    struct VISIBILITY Krb5Error {
        uint8_t         pvno;
        Krb5MsgType     msg_type;           /* KRB_ERROR */
        time_t          stime;
        uint32_t        susec;
        Krb5ErrorCode   error_code;
        Krb5Principal   sname;
        std::string     e_text;
        std::vector<uint8_t> e_data;        /* optional error data */

        Krb5Error();

        std::vector<uint8_t> serialize() const;
        bool                 deserialize(const uint8_t *data, size_t len, size_t &consumed);
    };

    /***************************************************************************
     * Kerberos 5 Crypto Operations (using netplus)
     *
     * Supports standard key derivation per RFC 3961/3962 for MIT/Heimdal
     * compatibility.  Uses PBKDF2-HMAC-SHA1, AES-CTS, and HMAC-SHA1-96.
     **************************************************************************/
    class VISIBILITY Krb5Crypto {
    public:
        /* Derive a Kerberos key from a password + salt (RFC 3962 PBKDF2) */
        static std::vector<uint8_t> string2key(const std::string &password,
                                                const std::string &salt,
                                                Krb5EncType etype);

        /* Derive a key for a specific usage (RFC 3961 key derivation) */
        static std::vector<uint8_t> deriveKey(const std::vector<uint8_t> &baseKey,
                                               Krb5KeyUsage usage,
                                               Krb5EncType etype);

        /* Generate a random session key */
        static Krb5SessionKey generateSessionKey(Krb5EncType etype);

        /* Encrypt data with key and usage number */
        static std::vector<uint8_t> encrypt(const std::vector<uint8_t> &key,
                                             const std::vector<uint8_t> &plaintext,
                                             Krb5KeyUsage usage = KRB5_KU_TICKET);

        /* Decrypt data with key and usage number */
        static std::vector<uint8_t> decrypt(const std::vector<uint8_t> &key,
                                             const std::vector<uint8_t> &ciphertext,
                                             Krb5KeyUsage usage = KRB5_KU_TICKET);

        /* HMAC integrity check */
        static std::vector<uint8_t> checksum(const std::vector<uint8_t> &key,
                                              const std::vector<uint8_t> &data,
                                              Krb5KeyUsage usage = KRB5_KU_TICKET);

        /* Verify HMAC checksum */
        static bool verifyChecksum(const std::vector<uint8_t> &key,
                                    const std::vector<uint8_t> &data,
                                    const std::vector<uint8_t> &mac,
                                    Krb5KeyUsage usage = KRB5_KU_TICKET);

        /* PBKDF2-HMAC-SHA1 (RFC 3962 string-to-key) */
        static std::vector<uint8_t> pbkdf2HmacSha1(const std::string &password,
                                                     const std::vector<uint8_t> &salt,
                                                     uint32_t iterations,
                                                     size_t keyLen);

        /* n-fold operation (RFC 3961 §5.1) */
        static std::vector<uint8_t> nfold(const std::vector<uint8_t> &input,
                                           size_t nbytes);

        /* DK key derivation (RFC 3961) */
        static std::vector<uint8_t> dk(const std::vector<uint8_t> &key,
                                        const std::vector<uint8_t> &constant,
                                        size_t keyLen);

        /* Generate random bytes */
        static std::vector<uint8_t> randomBytes(size_t count);

        /* Generate a random nonce */
        static uint32_t randomNonce();

        /* Key length for a given encryption type */
        static size_t keyLength(Krb5EncType etype);

        /* Select the best supported encryption type from a list */
        static Krb5EncType selectEncType(const std::vector<Krb5EncType> &etypes);
    };

    /***************************************************************************
     * Per-realm data: backend reference + TGS master key
     **************************************************************************/
    struct VISIBILITY RealmInfo {
        AuthBackend         *backend;    /* backend for this realm           */
        bool                 owned;      /* true if KDC owns the backend ptr */
        std::vector<uint8_t> tgsKey;     /* per-realm TGS master key         */
        std::string          domainName; /* original domain name             */

        RealmInfo();
        RealmInfo(AuthBackend *b, bool own, const std::string &domain = "");
    };

    /***************************************************************************
     * Cross-Realm Trust Key
     *
     * Holds shared secret between two realms for cross-realm TGT issuance.
     * Key for krbtgt/REMOTE_REALM@LOCAL_REALM
     **************************************************************************/
    struct VISIBILITY CrossRealmKey {
        std::string          localRealm;
        std::string          remoteRealm;
        std::vector<uint8_t> key;

        CrossRealmKey();
        CrossRealmKey(const std::string &local, const std::string &remote,
                      const std::vector<uint8_t> &k);
    };

    /***************************************************************************
     * Kerberos 5 KDC (Key Distribution Center)
     *
     * Implements the AS (Authentication Service) and TGS (Ticket-Granting
     * Service) functionality.  Supports multiple realms: one realm for the
     * admin database and one realm for every domain stored in the admin
     * backend.  Realm names are the upper-cased domain names.
     *
     * MIT Kerberos / Heimdal compatible:
     *   - ASN.1 DER wire format (RFC 4120)
     *   - Standard encryption types (aes128/256-cts-hmac-sha1-96)
     *   - Cross-realm referrals for multi-realm support
     *   - Standard PA-DATA handling (PA-ENC-TIMESTAMP, ETYPE-INFO2)
     *   - Standard key derivation (PBKDF2, RFC 3962)
     **************************************************************************/
    class VISIBILITY Krb5KDC : public netplus::eventapi, public KdcPlugin {
    public:
        /* Construct KDC; enumerates all domains and creates one realm each */
        Krb5KDC(AuthBackend &adminBackend,
                const std::string &bindAddr = "0.0.0.0",
                int port = 88, int maxconn = 128);
        ~Krb5KDC() override;

        /* Start the KDC event loop (blocking) */
        void runKdc(ULONG_PTR args = 0);

        /* Plugin interface */
        const char *name() const override { return "krb5"; }
        void init() override;
        void run() override { runKdc(0); }

        /* KdcPlugin interface */
        std::vector<uint8_t> string2key(const std::string &password,
                                         const std::string &salt,
                                         int etype) override;

        /* (Re-)load realms from the admin backend (called in ctor) */
        void loadRealms();

        /* Establish cross-realm trust between all known realms */
        void generateCrossRealmKeys();

        /* Get the list of known realm names */
        std::vector<std::string> getRealmNames() const;

        /* Maximum clock skew allowed in seconds (default: 300 = 5 min) */
        void setMaxClockSkew(int seconds);

        /* Default ticket lifetime in seconds (default: 36000 = 10 hours) */
        void setTicketLifetime(int seconds);

        /* Default renewable lifetime in seconds (default: 604800 = 7 days) */
        void setRenewableLifetime(int seconds);

        /* netplus::eventapi overrides (public so internal proxy can call them) */
        void RequestEvent(netplus::con &curcon, const int tid, ULONG_PTR args) override;
        void ResponseEvent(netplus::con &curcon, const int tid, ULONG_PTR args) override;
        void ConnectEvent(netplus::con &curcon, const int tid, ULONG_PTR args) override;
        void DisconnectEvent(netplus::con &curcon, const int tid, ULONG_PTR args) override;
        void CreateConnection(std::shared_ptr<netplus::con> &res) override;

    private:
        /* Resolve the realm from a request and return the RealmInfo, or nullptr */
        RealmInfo *resolveRealm(const std::string &realm);

        /* Standard Kerberos protocol (ASN.1 DER / RFC 4120) handlers */
        std::vector<uint8_t> handleStandardRequest(const uint8_t *data, size_t len);
        std::vector<uint8_t> handleStandardAsReq(const uint8_t *data, size_t len);
        std::vector<uint8_t> handleStandardTgsReq(const uint8_t *data, size_t len);
        std::vector<uint8_t> buildStandardError(Krb5ErrorCode code,
                                                 const std::string &text,
                                                 const std::string &realm,
                                                 const std::string &sname_name,
                                                 const std::string &sname_inst = "");

        /* Look up cross-realm key for krbtgt/REMOTE@LOCAL */
        bool getCrossRealmKey(const std::string &localRealm,
                              const std::string &remoteRealm,
                              std::vector<uint8_t> &key);

        /* Issue a cross-realm referral TGT */
        std::vector<uint8_t> buildCrossRealmReferral(const Krb5KdcReq &req,
                                                      const Krb5EncTicketPart &tgtEnc,
                                                      const std::string &targetRealm,
                                                      Krb5EncType etype);

        /* Look up a user principal in a specific backend */
        bool lookupUser(AuthBackend &backend, const Krb5Principal &principal,
                        uuid::uuid &uid, std::vector<uint8_t> &key);

        /* Look up a client (computer) principal in a specific backend */
        bool lookupClient(AuthBackend &backend, const Krb5Principal &principal,
                          uuid::uuid &uid, std::vector<uint8_t> &key);

        /* Look up a service principal (e.g. HTTP/host) in a specific backend */
        bool lookupService(AuthBackend &backend, const Krb5Principal &principal,
                           uuid::uuid &uid, std::vector<uint8_t> &key);

        AuthBackend                      &_AdminBackend;
        std::map<std::string, RealmInfo>  _Realms;       /* realm-name → info      */
        std::vector<CrossRealmKey>        _CrossRealmKeys;/* cross-realm trust keys */
        int                               _MaxClockSkew;
        int                               _TicketLifetime;
        int                               _RenewableLifetime;
        std::mutex                        _Lock;

        std::vector<netplus::socket*>     _ServerSockets;
        void                             *_Event;
    };

    /***************************************************************************
     * Kerberos 5 Client Library
     *
     * Used by authdb clients to authenticate users and computers against
     * the KDC.  Uses standard ASN.1 DER wire format for MIT/Heimdal
     * compatibility.  Supports cross-realm referrals for multi-realm.
     **************************************************************************/
    class VISIBILITY Krb5Client {
    public:
        Krb5Client(const std::string &kdcHost, int kdcPort = 88);
        ~Krb5Client();

        /* AS exchange: authenticate user with password, get TGT */
        bool authenticateUser(const std::string &username,
                              const std::string &password,
                              const std::string &realm);

        /* AS exchange: authenticate computer/client with key */
        bool authenticateClient(const std::string &clientname,
                                const std::string &clientkey,
                                const std::string &realm);

        /* TGS exchange: request a service ticket using existing TGT
         * Follows cross-realm referrals automatically */
        bool requestServiceTicket(const std::string &serviceName,
                                  const std::string &serviceInstance,
                                  const std::string &serviceRealm = "");

        /* Build AP-REQ for service authentication */
        std::vector<uint8_t> buildApReq();

        /* Get the current TGT */
        const Krb5Ticket &getTGT() const;

        /* Get the current service ticket */
        const Krb5Ticket &getServiceTicket() const;

        /* Get the session key for the current ticket */
        const Krb5SessionKey &getSessionKey() const;

        /* Check if we have a valid (non-expired) TGT */
        bool hasValidTGT() const;

        /* Set preferred encryption types */
        void setPreferredEtypes(const std::vector<Krb5EncType> &etypes);

    private:
        /* Send a KDC request and receive the response (standard wire format) */
        std::vector<uint8_t> sendKdcRequest(const std::vector<uint8_t> &request);

        /* Follow cross-realm referral for TGS */
        bool followReferral(const std::string &targetRealm,
                            const std::string &serviceName,
                            const std::string &serviceInstance);

        std::string          _KdcHost;
        int                  _KdcPort;
        std::string          _Realm;

        /* Preferred encryption types (ordered by preference) */
        std::vector<Krb5EncType> _PreferredEtypes;

        /* Cached credentials */
        Krb5Ticket           _TGT;
        Krb5Ticket           _ServiceTicket;
        Krb5SessionKey       _TGTSessionKey;
        Krb5SessionKey       _ServiceSessionKey;
        Krb5Principal        _ClientPrincipal;
        std::vector<uint8_t> _ClientKey;
        time_t               _TGTExpiry;
        uint32_t             _TGTFlags;
    };

    /***************************************************************************
     * Kerberos 5 Service Verifier
     *
     * Used by application servers to verify AP-REQ messages from clients.
     * Supports standard ASN.1 DER wire format for MIT/Heimdal interop.
     **************************************************************************/
    class VISIBILITY Krb5Service {
    public:
        Krb5Service(const std::string &serviceName,
                    const std::string &hostname,
                    const std::string &realm,
                    const std::vector<uint8_t> &serviceKey);
        ~Krb5Service();

        /* Verify an AP-REQ and extract the client principal + session key */
        bool verifyApReq(const std::vector<uint8_t> &apReqData,
                         Krb5Principal &clientPrincipal,
                         Krb5SessionKey &sessionKey);

        /* Build an AP-REP response */
        std::vector<uint8_t> buildApRep(const Krb5Authenticator &auth,
                                         const Krb5SessionKey &sessionKey);

    private:
        Krb5Principal        _ServicePrincipal;
        std::vector<uint8_t> _ServiceKey;
    };

};
