/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* lib/krb5/asn.1/asn1_k_encode.c */
/*
 * Copyright 1994, 2008 by the Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * Export of this software from the United States of America may
 *   require a specific license from the United States Government.
 *   It is the responsibility of any person or organization contemplating
 *   export to obtain such a license before exporting.
 *
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of M.I.T. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  Furthermore if you modify this software you must label
 * your software as modified software and not distribute it in such a
 * fashion that it might be confused with the original M.I.T. software.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 */

#include "asn1_k_encode.h"
#include "asn1_make.h"
#include "asn1_encode.h"
#include <assert.h>

/*
 * helper macros
 *
 * These are mostly only needed for PKINIT, but there are three
 * basic-krb5 encoders not converted yet.
 */

/*
 * setup() -- create and initialize bookkeeping variables
 *   retval: stores error codes returned from subroutines
 *   length: length of the most-recently produced encoding
 *   sum: cumulative length of the entire encoding
 */
#define asn1_setup()                            \
    asn1_error_code retval;                     \
    unsigned int sum=0

/* form a sequence (by adding a sequence header to the current encoding) */
#define asn1_makeseq()                                  \
    { unsigned int length;                              \
        retval = asn1_make_sequence(buf,sum,&length);   \
        if (retval) {                                   \
            return retval; }                            \
        sum += length; }

/* produce the final output and clean up the workspace */
#define asn1_cleanup()                          \
    *retlen = sum;                              \
    return 0

/* asn1_addfield -- add a field, or component, to the encoding */
#define asn1_addfield(value,tag,encoder)                                \
    { unsigned int length;                                              \
        retval = encoder(buf,value,&length);                            \
        if (retval) {                                                   \
            return retval; }                                            \
        sum += length;                                                  \
        retval = asn1_make_etag(buf,CONTEXT_SPECIFIC,tag,length,&length); \
        if (retval) {                                                   \
            return retval; }                                            \
        sum += length; }

DEFINTTYPE(int32, krb5_int32);
DEFPTRTYPE(int32_ptr, int32);

DEFUINTTYPE(uint, unsigned int);
DEFUINTTYPE(octet, krb5_octet);
DEFUINTTYPE(ui_4, krb5_ui_4);

DEFFNLENTYPE(octetstring, unsigned char *, asn1_encode_octetstring);
DEFFNLENTYPE(s_octetstring, char *, asn1_encode_octetstring);
DEFFNLENTYPE(charstring, char *, asn1_encode_charstring);
DEFFNLENTYPE(generalstring, char *, asn1_encode_generalstring);
DEFFNLENTYPE(u_generalstring, unsigned char *, asn1_encode_generalstring);
DEFFNLENTYPE(opaque, char *, asn1_encode_opaque);

DEFFIELDTYPE(gstring_data, krb5_data,
             FIELDOF_STRING(krb5_data, generalstring, data, length, -1));
DEFPTRTYPE(gstring_data_ptr,gstring_data);

DEFFIELDTYPE(ostring_data, krb5_data,
             FIELDOF_STRING(krb5_data, s_octetstring, data, length, -1));
DEFPTRTYPE(ostring_data_ptr,ostring_data);

DEFFIELDTYPE(opaque_data, krb5_data,
             FIELDOF_STRING(krb5_data, opaque, data, length, -1));

DEFFIELDTYPE(realm_of_principal_data, krb5_principal_data,
             FIELDOF_NORM(krb5_principal_data, gstring_data, realm, -1));
DEFPTRTYPE(realm_of_principal, realm_of_principal_data);

static const struct field_info princname_fields[] = {
    FIELDOF_NORM(krb5_principal_data, int32, type, 0),
    FIELDOF_SEQOF_INT32(krb5_principal_data, gstring_data_ptr, data, length, 1),
};
/*
 * krb5_principal is a typedef for krb5_principal_data*, so this is
 * effectively "encode_principal_data_at" with an address arg.
 */
DEFSEQTYPE(principal_data, krb5_principal_data, princname_fields, 0);
DEFPTRTYPE(principal, principal_data);

static asn1_error_code
asn1_encode_kerberos_time_at(asn1buf *buf, const krb5_timestamp *val,
                             unsigned int *retlen)
{
    /* Range checking for time_t vs krb5_timestamp?  */
    time_t tval = *val;
    return asn1_encode_generaltime(buf, tval, retlen);
}
DEFFNXTYPE(kerberos_time, krb5_timestamp, asn1_encode_kerberos_time_at);

const static struct field_info address_fields[] = {
    FIELDOF_NORM(krb5_address, int32, addrtype, 0),
    FIELDOF_STRING(krb5_address, octetstring, contents, length, 1),
};
DEFSEQTYPE(address, krb5_address, address_fields, 0);
DEFPTRTYPE(address_ptr, address);

DEFNULLTERMSEQOFTYPE(seq_of_host_addresses, address_ptr);
DEFPTRTYPE(ptr_seqof_host_addresses, seq_of_host_addresses);

static unsigned int
optional_encrypted_data (const void *vptr)
{
    const krb5_enc_data *val = vptr;
    unsigned int optional = 0;

    if (val->kvno != 0)
        optional |= (1u << 1);

    return optional;
}

/*
 * Encode krb5_kvno as signed 32-bit for Windows RODC interop.  (This is an
 * inelegant backport; it's an alteration of the expansion of DEFINTTYPE(kvno,
 * krb5_kvno).)
 */
typedef krb5_kvno aux_typedefname_kvno;
static asn1_intmax loadint_kvno(const void *p)
{
    return (krb5_int32)*(krb5_kvno *)p;
}
const struct atype_info krb5int_asn1type_kvno = {
    atype_int, sizeof(krb5_kvno), 0, 0, 0, 0, 0, 0, 0, 0, 0,
    loadint_kvno, 0,
};
static const struct field_info encrypted_data_fields[] = {
    FIELDOF_NORM(krb5_enc_data, int32, enctype, 0),
    FIELDOF_OPT(krb5_enc_data, kvno, kvno, 1, 1),
    FIELDOF_NORM(krb5_enc_data, ostring_data, ciphertext, 2),
};
DEFSEQTYPE(encrypted_data, krb5_enc_data, encrypted_data_fields,
           optional_encrypted_data);

/*
 * The encode_bitstring function wants an array of bytes (since PKINIT
 * may provide something that isn't 32 bits), but krb5_flags is stored
 * as a 32-bit integer in host order.
 */
static asn1_error_code
asn1_encode_krb5_flags_at(asn1buf *buf, const krb5_flags *val,
                          unsigned int *retlen)
{
    unsigned char cbuf[4];
    store_32_be((krb5_ui_4) *val, cbuf);
    return asn1_encode_bitstring(buf, 4, cbuf, retlen);
}
DEFFNXTYPE(krb5_flags, krb5_flags, asn1_encode_krb5_flags_at);

const static struct field_info authdata_elt_fields[] = {
    /* ad-type[0]               INTEGER */
    FIELDOF_NORM(krb5_authdata, int32, ad_type, 0),
    /* ad-data[1]               OCTET STRING */
    FIELDOF_STRING(krb5_authdata, octetstring, contents, length, 1),
};
DEFSEQTYPE(authdata_elt, krb5_authdata, authdata_elt_fields, 0);
DEFPTRTYPE(authdata_elt_ptr, authdata_elt);
DEFNONEMPTYNULLTERMSEQOFTYPE(auth_data, authdata_elt_ptr);
DEFPTRTYPE(auth_data_ptr, auth_data);

static const struct field_info encryption_key_fields[] = {
    FIELDOF_NORM(krb5_keyblock, int32, enctype, 0),
    FIELDOF_STRING(krb5_keyblock, octetstring, contents, length, 1),
};
DEFSEQTYPE(encryption_key, krb5_keyblock, encryption_key_fields, 0);
DEFPTRTYPE(ptr_encryption_key, encryption_key);

static const struct field_info checksum_fields[] = {
    FIELDOF_NORM(krb5_checksum, int32, checksum_type, 0),
    FIELDOF_STRING(krb5_checksum, octetstring, contents, length, 1),
};
DEFSEQTYPE(checksum, krb5_checksum, checksum_fields, 0);
DEFPTRTYPE(checksum_ptr, checksum);
DEFNULLTERMSEQOFTYPE(seq_of_checksum, checksum_ptr);
DEFPTRTYPE(ptr_seqof_checksum, seq_of_checksum);

static const struct field_info lr_fields[] = {
    FIELDOF_NORM(krb5_last_req_entry, int32, lr_type, 0),
    FIELDOF_NORM(krb5_last_req_entry, kerberos_time, value, 1),
};
DEFSEQTYPE(last_req_ent, krb5_last_req_entry, lr_fields, 0);

DEFPTRTYPE(last_req_ent_ptr, last_req_ent);
DEFNONEMPTYNULLTERMSEQOFTYPE(last_req, last_req_ent_ptr);
DEFPTRTYPE(last_req_ptr, last_req);

static const struct field_info ticket_fields[] = {
    FIELD_INT_IMM(KVNO, 0),
    FIELDOF_NORM(krb5_ticket, realm_of_principal, server, 1),
    FIELDOF_NORM(krb5_ticket, principal, server, 2),
    FIELDOF_NORM(krb5_ticket, encrypted_data, enc_part, 3),
};
DEFSEQTYPE(untagged_ticket, krb5_ticket, ticket_fields, 0);
DEFAPPTAGGEDTYPE(ticket, 1, untagged_ticket);

static const struct field_info pa_data_fields[] = {
    FIELDOF_NORM(krb5_pa_data, int32, pa_type, 1),
    FIELDOF_STRING(krb5_pa_data, octetstring, contents, length, 2),
};
DEFSEQTYPE(pa_data, krb5_pa_data, pa_data_fields, 0);
DEFPTRTYPE(pa_data_ptr, pa_data);

DEFNULLTERMSEQOFTYPE(seq_of_pa_data, pa_data_ptr);
DEFPTRTYPE(ptr_seqof_pa_data, seq_of_pa_data);

DEFPTRTYPE(ticket_ptr, ticket);
DEFNONEMPTYNULLTERMSEQOFTYPE(seq_of_ticket,ticket_ptr);
DEFPTRTYPE(ptr_seqof_ticket, seq_of_ticket);

/* EncKDCRepPart ::= SEQUENCE */
static const struct field_info enc_kdc_rep_part_fields[] = {
    /* key[0]           EncryptionKey */
    FIELDOF_NORM(krb5_enc_kdc_rep_part, ptr_encryption_key, session, 0),
    /* last-req[1]      LastReq */
    FIELDOF_NORM(krb5_enc_kdc_rep_part, last_req_ptr, last_req, 1),
    /* nonce[2]         INTEGER */
    FIELDOF_NORM(krb5_enc_kdc_rep_part, int32, nonce, 2),
    /* key-expiration[3]        KerberosTime OPTIONAL */
    FIELDOF_OPT(krb5_enc_kdc_rep_part, kerberos_time, key_exp, 3, 3),
    /* flags[4]         TicketFlags */
    FIELDOF_NORM(krb5_enc_kdc_rep_part, krb5_flags, flags, 4),
    /* authtime[5]      KerberosTime */
    FIELDOF_NORM(krb5_enc_kdc_rep_part, kerberos_time, times.authtime, 5),
    /* starttime[6]     KerberosTime OPTIONAL */
    FIELDOF_OPT(krb5_enc_kdc_rep_part, kerberos_time, times.starttime, 6, 6),
    /* endtime[7]               KerberosTime */
    FIELDOF_NORM(krb5_enc_kdc_rep_part, kerberos_time, times.endtime, 7),
    /* renew-till[8]    KerberosTime OPTIONAL */
    FIELDOF_OPT(krb5_enc_kdc_rep_part, kerberos_time, times.renew_till, 8, 8),
    /* srealm[9]                Realm */
    FIELDOF_NORM(krb5_enc_kdc_rep_part, realm_of_principal, server, 9),
    /* sname[10]                PrincipalName */
    FIELDOF_NORM(krb5_enc_kdc_rep_part, principal, server, 10),
    /* caddr[11]                HostAddresses OPTIONAL */
    FIELDOF_OPT(krb5_enc_kdc_rep_part, ptr_seqof_host_addresses, caddrs,
                11, 11),
    /* encrypted-pa-data[12]    SEQUENCE OF PA-DATA OPTIONAL */
    FIELDOF_OPT(krb5_enc_kdc_rep_part, ptr_seqof_pa_data, enc_padata, 12, 12),
};
static unsigned int
optional_enc_kdc_rep_part(const void *p)
{
    const krb5_enc_kdc_rep_part *val = p;
    unsigned int optional = 0;

    if (val->key_exp)
        optional |= (1u << 3);
    if (val->times.starttime)
        optional |= (1u << 6);
    if (val->flags & TKT_FLG_RENEWABLE)
        optional |= (1u << 8);
    if (val->caddrs != NULL && val->caddrs[0] != NULL)
        optional |= (1u << 11);
    if (val->enc_padata != NULL)
        optional |= (1u << 12);

    return optional;
}
DEFSEQTYPE(enc_kdc_rep_part, krb5_enc_kdc_rep_part, enc_kdc_rep_part_fields,
           optional_enc_kdc_rep_part);

/*
 * Yuck!  Eventually push this *up* above the encoder API and make the
 * rest of the library put the realm name in one consistent place.  At
 * the same time, might as well add the msg-type field and encode both
 * AS-REQ and TGS-REQ through the same descriptor.
 */
struct kdc_req_hack {
    krb5_kdc_req v;
    krb5_data *server_realm;
};
static const struct field_info kdc_req_hack_fields[] = {
    FIELDOF_NORM(struct kdc_req_hack, krb5_flags, v.kdc_options, 0),
    FIELDOF_OPT(struct kdc_req_hack, principal, v.client, 1, 1),
    FIELDOF_NORM(struct kdc_req_hack, gstring_data_ptr, server_realm, 2),
    FIELDOF_OPT(struct kdc_req_hack, principal, v.server, 3, 3),
    FIELDOF_OPT(struct kdc_req_hack, kerberos_time, v.from, 4, 4),
    FIELDOF_NORM(struct kdc_req_hack, kerberos_time, v.till, 5),
    FIELDOF_OPT(struct kdc_req_hack, kerberos_time, v.rtime, 6, 6),
    FIELDOF_NORM(struct kdc_req_hack, int32, v.nonce, 7),
    FIELDOF_SEQOF_INT32(struct kdc_req_hack, int32_ptr, v.ktype, v.nktypes, 8),
    FIELDOF_OPT(struct kdc_req_hack, ptr_seqof_host_addresses, v.addresses, 9, 9),
    FIELDOF_OPT(struct kdc_req_hack, encrypted_data, v.authorization_data, 10, 10),
    FIELDOF_OPT(struct kdc_req_hack, ptr_seqof_ticket, v.second_ticket, 11, 11),
};
static unsigned int
optional_kdc_req_hack(const void *p)
{
    const struct kdc_req_hack *val2 = p;
    const krb5_kdc_req *val = &val2->v;
    unsigned int optional = 0;

    if (val->second_ticket != NULL && val->second_ticket[0] != NULL)
        optional |= (1u << 11);
    if (val->authorization_data.ciphertext.data != NULL)
        optional |= (1u << 10);
    if (val->addresses != NULL && val->addresses[0] != NULL)
        optional |= (1u << 9);
    if (val->rtime)
        optional |= (1u << 6);
    if (val->from)
        optional |= (1u << 4);
    if (val->server != NULL)
        optional |= (1u << 3);
    if (val->client != NULL)
        optional |= (1u << 1);

    return optional;
}
DEFSEQTYPE(kdc_req_body_hack, struct kdc_req_hack, kdc_req_hack_fields,
           optional_kdc_req_hack);
static asn1_error_code
asn1_encode_kdc_req_hack(asn1buf *, const struct kdc_req_hack *,
                         unsigned int *);
MAKE_ENCFN(asn1_encode_kdc_req_hack, kdc_req_body_hack);
static asn1_error_code
asn1_encode_kdc_req_body(asn1buf *buf, const krb5_kdc_req *val,
                         unsigned int *retlen)
{
    struct kdc_req_hack val2;
    val2.v = *val;
    if (val->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) {
        if (val->second_ticket != NULL && val->second_ticket[0] != NULL) {
            val2.server_realm = &val->second_ticket[0]->server->realm;
        } else return ASN1_MISSING_FIELD;
    } else if (val->server != NULL) {
        val2.server_realm = &val->server->realm;
    } else return ASN1_MISSING_FIELD;
    return asn1_encode_kdc_req_hack(buf, &val2, retlen);
}
DEFFNXTYPE(kdc_req_body, krb5_kdc_req, asn1_encode_kdc_req_body);
/* end ugly hack */

DEFPTRTYPE(ptr_kdc_req_body,kdc_req_body);

static const struct field_info transited_fields[] = {
    FIELDOF_NORM(krb5_transited, octet, tr_type, 0),
    FIELDOF_NORM(krb5_transited, ostring_data, tr_contents, 1),
};
DEFSEQTYPE(transited, krb5_transited, transited_fields, 0);

static const struct field_info krb_safe_body_fields[] = {
    FIELDOF_NORM(krb5_safe, ostring_data, user_data, 0),
    FIELDOF_OPT(krb5_safe, kerberos_time, timestamp, 1, 1),
    FIELDOF_OPT(krb5_safe, int32, usec, 2, 2),
    FIELDOF_OPT(krb5_safe, uint, seq_number, 3, 3),
    FIELDOF_NORM(krb5_safe, address_ptr, s_address, 4),
    FIELDOF_OPT(krb5_safe, address_ptr, r_address, 5, 5),
};
static unsigned int
optional_krb_safe_body(const void *p)
{
    const krb5_safe *val = p;
    unsigned int optional = 0;

    if (val->timestamp) {
        optional |= (1u << 1);
        optional |= (1u << 2);
    }
    if (val->seq_number)
        optional |= (1u << 3);
    if (val->r_address != NULL)
        optional |= (1u << 5);

    return optional;
}
DEFSEQTYPE(krb_safe_body, krb5_safe, krb_safe_body_fields,
           optional_krb_safe_body);

static const struct field_info krb_cred_info_fields[] = {
    FIELDOF_NORM(krb5_cred_info, ptr_encryption_key, session, 0),
    FIELDOF_OPT(krb5_cred_info, realm_of_principal, client, 1, 1),
    FIELDOF_OPT(krb5_cred_info, principal, client, 2, 2),
    FIELDOF_OPT(krb5_cred_info, krb5_flags, flags, 3, 3),
    FIELDOF_OPT(krb5_cred_info, kerberos_time, times.authtime, 4, 4),
    FIELDOF_OPT(krb5_cred_info, kerberos_time, times.starttime, 5, 5),
    FIELDOF_OPT(krb5_cred_info, kerberos_time, times.endtime, 6, 6),
    FIELDOF_OPT(krb5_cred_info, kerberos_time, times.renew_till, 7, 7),
    FIELDOF_OPT(krb5_cred_info, realm_of_principal, server, 8, 8),
    FIELDOF_OPT(krb5_cred_info, principal, server, 9, 9),
    FIELDOF_OPT(krb5_cred_info, ptr_seqof_host_addresses, caddrs, 10, 10),
};
static unsigned int
optional_krb_cred_info(const void *p)
{
    const krb5_cred_info *val = p;
    unsigned int optional = 0;

    if (val->caddrs != NULL && val->caddrs[0] != NULL)
        optional |= (1u << 10);
    if (val->server != NULL) {
        optional |= (1u << 9);
        optional |= (1u << 8);
    }
    if (val->times.renew_till)
        optional |= (1u << 7);
    if (val->times.endtime)
        optional |= (1u << 6);
    if (val->times.starttime)
        optional |= (1u << 5);
    if (val->times.authtime)
        optional |= (1u << 4);
    if (val->flags)
        optional |= (1u << 3);
    if (val->client != NULL) {
        optional |= (1u << 2);
        optional |= (1u << 1);
    }

    return optional;
}
DEFSEQTYPE(cred_info, krb5_cred_info, krb_cred_info_fields,
           optional_krb_cred_info);
DEFPTRTYPE(cred_info_ptr, cred_info);
DEFNULLTERMSEQOFTYPE(seq_of_cred_info, cred_info_ptr);

DEFPTRTYPE(ptrseqof_cred_info, seq_of_cred_info);



static unsigned int
optional_etype_info_entry(const void *vptr)
{
    const krb5_etype_info_entry *val = vptr;
    unsigned int optional = 0;

    if (val->length != KRB5_ETYPE_NO_SALT)
        optional |= (1u << 1);

    return optional;
}
static const struct field_info etype_info_entry_fields[] = {
    FIELDOF_NORM(krb5_etype_info_entry, int32, etype, 0),
    FIELDOF_OPTSTRING(krb5_etype_info_entry, octetstring, salt, length, 1, 1),
};
DEFSEQTYPE(etype_info_entry, krb5_etype_info_entry, etype_info_entry_fields,
           optional_etype_info_entry);

static unsigned int
optional_etype_info2_entry(const void *vptr)
{
    const krb5_etype_info_entry *val = vptr;
    unsigned int optional = 0;

    if (val->length != KRB5_ETYPE_NO_SALT)
        optional |= (1u << 1);
    if (val->s2kparams.data)
        optional |= (1u << 2);

    return optional;
}

static const struct field_info etype_info2_entry_fields[] = {
    FIELDOF_NORM(krb5_etype_info_entry, int32, etype, 0),
    FIELDOF_OPTSTRING(krb5_etype_info_entry, u_generalstring, salt, length,
                      1, 1),
    FIELDOF_OPT(krb5_etype_info_entry, ostring_data, s2kparams, 2, 2),
};
DEFSEQTYPE(etype_info2_entry, krb5_etype_info_entry, etype_info2_entry_fields,
           optional_etype_info2_entry);

DEFPTRTYPE(etype_info_entry_ptr, etype_info_entry);
DEFNULLTERMSEQOFTYPE(etype_info, etype_info_entry_ptr);

DEFPTRTYPE(etype_info2_entry_ptr, etype_info2_entry);
DEFNULLTERMSEQOFTYPE(etype_info2, etype_info2_entry_ptr);

static const struct field_info passwdsequence_fields[] = {
    FIELDOF_NORM(passwd_phrase_element, ostring_data_ptr, passwd, 0),
    FIELDOF_NORM(passwd_phrase_element, ostring_data_ptr, phrase, 1),
};
DEFSEQTYPE(passwdsequence, passwd_phrase_element, passwdsequence_fields, 0);

DEFPTRTYPE(passwdsequence_ptr, passwdsequence);
DEFNONEMPTYNULLTERMSEQOFTYPE(seqof_passwdsequence, passwdsequence_ptr);
DEFPTRTYPE(ptr_seqof_passwdsequence, seqof_passwdsequence);


static const struct field_info sam_challenge_fields[] = {
    FIELDOF_NORM(krb5_sam_challenge, int32, sam_type, 0),
    FIELDOF_NORM(krb5_sam_challenge, krb5_flags, sam_flags, 1),
    FIELDOF_OPT(krb5_sam_challenge, ostring_data, sam_type_name, 2, 2),
    FIELDOF_OPT(krb5_sam_challenge, ostring_data, sam_track_id,3, 3),
    FIELDOF_OPT(krb5_sam_challenge, ostring_data, sam_challenge_label,4, 4),
    FIELDOF_OPT(krb5_sam_challenge, ostring_data, sam_challenge,5, 5),
    FIELDOF_OPT(krb5_sam_challenge, ostring_data, sam_response_prompt,6, 6),
    FIELDOF_OPT(krb5_sam_challenge, ostring_data, sam_pk_for_sad,7, 7),
    FIELDOF_OPT(krb5_sam_challenge, int32, sam_nonce, 8, 8),
    FIELDOF_OPT(krb5_sam_challenge, checksum, sam_cksum, 9, 9),
};
static unsigned int
optional_sam_challenge(const void *p)
{
    const krb5_sam_challenge *val = p;
    unsigned int optional = 0;

    if (val->sam_cksum.length)
        optional |= (1u << 9);

    if (val->sam_nonce)
        optional |= (1u << 8);

    if (val->sam_pk_for_sad.length > 0) optional |= (1u << 7);
    if (val->sam_response_prompt.length > 0) optional |= (1u << 6);
    if (val->sam_challenge.length > 0) optional |= (1u << 5);
    if (val->sam_challenge_label.length > 0) optional |= (1u << 4);
    if (val->sam_track_id.length > 0) optional |= (1u << 3);
    if (val->sam_type_name.length > 0) optional |= (1u << 2);

    return optional;
}
DEFSEQTYPE(sam_challenge,krb5_sam_challenge,sam_challenge_fields,
           optional_sam_challenge);

MAKE_ENCFN(asn1_encode_sequence_of_checksum, seq_of_checksum);
static asn1_error_code
asn1_encode_sam_challenge_2(asn1buf *buf, const krb5_sam_challenge_2 *val,
                            unsigned int *retlen)
{
    asn1_setup();
    if ( (!val) || (!val->sam_cksum) || (!val->sam_cksum[0]))
        return ASN1_MISSING_FIELD;

    asn1_addfield(val->sam_cksum, 1, asn1_encode_sequence_of_checksum);

    {
        unsigned int length;

        retval = asn1buf_insert_octetstring(buf, val->sam_challenge_2_body.length,
                                            (unsigned char *)val->sam_challenge_2_body.data);
        if (retval) {
            return retval;
        }
        sum += val->sam_challenge_2_body.length;
        retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0,
                                val->sam_challenge_2_body.length, &length);
        if (retval) {
            return retval;
        }
        sum += length;
    }

    asn1_makeseq();
    asn1_cleanup();
}
DEFFNXTYPE(sam_challenge_2, krb5_sam_challenge_2, asn1_encode_sam_challenge_2);

static const struct field_info sam_challenge_2_body_fields[] = {
    FIELDOF_NORM(krb5_sam_challenge_2_body, int32, sam_type, 0),
    FIELDOF_NORM(krb5_sam_challenge_2_body, krb5_flags, sam_flags, 1),
    FIELDOF_OPT(krb5_sam_challenge_2_body, ostring_data, sam_type_name, 2, 2),
    FIELDOF_OPT(krb5_sam_challenge_2_body, ostring_data, sam_track_id,3, 3),
    FIELDOF_OPT(krb5_sam_challenge_2_body, ostring_data, sam_challenge_label,4, 4),
    FIELDOF_OPT(krb5_sam_challenge_2_body, ostring_data, sam_challenge,5, 5),
    FIELDOF_OPT(krb5_sam_challenge_2_body, ostring_data, sam_response_prompt,6, 6),
    FIELDOF_OPT(krb5_sam_challenge_2_body, ostring_data, sam_pk_for_sad,7, 7),
    FIELDOF_NORM(krb5_sam_challenge_2_body, int32, sam_nonce, 8),
    FIELDOF_NORM(krb5_sam_challenge_2_body, int32, sam_etype, 9),
};
static unsigned int
optional_sam_challenge_2_body(const void *p)
{
    const krb5_sam_challenge_2_body *val = p;
    unsigned int optional = 0;

    if (val->sam_pk_for_sad.length > 0) optional |= (1u << 7);
    if (val->sam_response_prompt.length > 0) optional |= (1u << 6);
    if (val->sam_challenge.length > 0) optional |= (1u << 5);
    if (val->sam_challenge_label.length > 0) optional |= (1u << 4);
    if (val->sam_track_id.length > 0) optional |= (1u << 3);
    if (val->sam_type_name.length > 0) optional |= (1u << 2);

    return optional;
}
DEFSEQTYPE(sam_challenge_2_body,krb5_sam_challenge_2_body,sam_challenge_2_body_fields,
           optional_sam_challenge_2_body);


static const struct field_info sam_key_fields[] = {
    FIELDOF_NORM(krb5_sam_key, encryption_key, sam_key, 0),
};
DEFSEQTYPE(sam_key, krb5_sam_key, sam_key_fields, 0);

static const struct field_info enc_sam_response_enc_fields[] = {
    FIELDOF_NORM(krb5_enc_sam_response_enc, int32, sam_nonce, 0),
    FIELDOF_NORM(krb5_enc_sam_response_enc, kerberos_time, sam_timestamp, 1),
    FIELDOF_NORM(krb5_enc_sam_response_enc, int32, sam_usec, 2),
    FIELDOF_OPT(krb5_enc_sam_response_enc, ostring_data, sam_sad, 3, 3),
};
static unsigned int
optional_enc_sam_response_enc(const void *p)
{
    const krb5_enc_sam_response_enc *val = p;
    unsigned int optional = 0;

    if (val->sam_sad.length > 0) optional |= (1u << 3);

    return optional;
}
DEFSEQTYPE(enc_sam_response_enc, krb5_enc_sam_response_enc,
           enc_sam_response_enc_fields, optional_enc_sam_response_enc);

static const struct field_info enc_sam_response_enc_2_fields[] = {
    FIELDOF_NORM(krb5_enc_sam_response_enc_2, int32, sam_nonce, 0),
    FIELDOF_OPT(krb5_enc_sam_response_enc_2, ostring_data, sam_sad, 1, 1),
};
static unsigned int
optional_enc_sam_response_enc_2(const void *p)
{
    const krb5_enc_sam_response_enc_2 *val = p;
    unsigned int optional = 0;

    if (val->sam_sad.length > 0) optional |= (1u << 1);

    return optional;
}
DEFSEQTYPE(enc_sam_response_enc_2, krb5_enc_sam_response_enc_2,
           enc_sam_response_enc_2_fields, optional_enc_sam_response_enc_2);

static const struct field_info sam_response_fields[] = {
    FIELDOF_NORM(krb5_sam_response, int32, sam_type, 0),
    FIELDOF_NORM(krb5_sam_response, krb5_flags, sam_flags, 1),
    FIELDOF_OPT(krb5_sam_response, ostring_data, sam_track_id, 2, 2),
    FIELDOF_OPT(krb5_sam_response, encrypted_data, sam_enc_key, 3, 3),
    FIELDOF_NORM(krb5_sam_response, encrypted_data, sam_enc_nonce_or_ts, 4),
    FIELDOF_OPT(krb5_sam_response, int32, sam_nonce, 5, 5),
    FIELDOF_OPT(krb5_sam_response, kerberos_time, sam_patimestamp, 6, 6),
};
static unsigned int
optional_sam_response(const void *p)
{
    const krb5_sam_response *val = p;
    unsigned int optional = 0;

    if (val->sam_patimestamp)
        optional |= (1u << 6);
    if (val->sam_nonce)
        optional |= (1u << 5);
    if (val->sam_enc_key.ciphertext.length)
        optional |= (1u << 3);
    if (val->sam_track_id.length > 0) optional |= (1u << 2);

    return optional;
}
DEFSEQTYPE(sam_response, krb5_sam_response, sam_response_fields,
           optional_sam_response);

static const struct field_info sam_response_2_fields[] = {
    FIELDOF_NORM(krb5_sam_response_2, int32, sam_type, 0),
    FIELDOF_NORM(krb5_sam_response_2, krb5_flags, sam_flags, 1),
    FIELDOF_OPT(krb5_sam_response_2, ostring_data, sam_track_id, 2, 2),
    FIELDOF_NORM(krb5_sam_response_2, encrypted_data, sam_enc_nonce_or_sad, 3),
    FIELDOF_NORM(krb5_sam_response_2, int32, sam_nonce, 4),
};
static unsigned int
optional_sam_response_2(const void *p)
{
    const krb5_sam_response_2 *val = p;
    unsigned int optional = 0;

    if (val->sam_track_id.length > 0) optional |= (1u << 2);

    return optional;
}
DEFSEQTYPE(sam_response_2, krb5_sam_response_2, sam_response_2_fields,
           optional_sam_response_2);

static const struct field_info predicted_sam_response_fields[] = {
    FIELDOF_NORM(krb5_predicted_sam_response, encryption_key, sam_key, 0),
    FIELDOF_NORM(krb5_predicted_sam_response, krb5_flags, sam_flags, 1),
    FIELDOF_NORM(krb5_predicted_sam_response, kerberos_time, stime, 2),
    FIELDOF_NORM(krb5_predicted_sam_response, int32, susec, 3),
    FIELDOF_NORM(krb5_predicted_sam_response, realm_of_principal, client, 4),
    FIELDOF_NORM(krb5_predicted_sam_response, principal, client, 5),
    FIELDOF_OPT(krb5_predicted_sam_response, ostring_data, msd, 6, 6),
};
static unsigned int
optional_predicted_sam_response(const void *p)
{
    const krb5_predicted_sam_response *val = p;
    unsigned int optional = 0;

    if (val->msd.length > 0) optional |= (1u << 6);

    return optional;
}
DEFSEQTYPE(predicted_sam_response, krb5_predicted_sam_response,
           predicted_sam_response_fields,
           optional_predicted_sam_response);

static const struct field_info krb5_authenticator_fields[] = {
    /* Authenticator ::= [APPLICATION 2] SEQUENCE */
    /* authenticator-vno[0]     INTEGER */
    FIELD_INT_IMM(KVNO, 0),
    /* crealm[1]                        Realm */
    FIELDOF_NORM(krb5_authenticator, realm_of_principal, client, 1),
    /* cname[2]                 PrincipalName */
    FIELDOF_NORM(krb5_authenticator, principal, client, 2),
    /* cksum[3]                 Checksum OPTIONAL */
    FIELDOF_OPT(krb5_authenticator, checksum_ptr, checksum, 3, 3),
    /* cusec[4]                 INTEGER */
    FIELDOF_NORM(krb5_authenticator, int32, cusec, 4),
    /* ctime[5]                 KerberosTime */
    FIELDOF_NORM(krb5_authenticator, kerberos_time, ctime, 5),
    /* subkey[6]                        EncryptionKey OPTIONAL */
    FIELDOF_OPT(krb5_authenticator, ptr_encryption_key, subkey, 6, 6),
    /* seq-number[7]            INTEGER OPTIONAL */
    FIELDOF_OPT(krb5_authenticator, uint, seq_number, 7, 7),
    /* authorization-data[8]    AuthorizationData OPTIONAL */
    FIELDOF_OPT(krb5_authenticator, auth_data_ptr, authorization_data, 8, 8),
};
static unsigned int
optional_krb5_authenticator(const void *p)
{
    const krb5_authenticator *val = p;
    unsigned int optional = 0;

    if (val->authorization_data != NULL && val->authorization_data[0] != NULL)
        optional |= (1u << 8);

    if (val->seq_number != 0)
        optional |= (1u << 7);

    if (val->subkey != NULL)
        optional |= (1u << 6);

    if (val->checksum != NULL)
        optional |= (1u << 3);

    return optional;
}
DEFSEQTYPE(untagged_krb5_authenticator, krb5_authenticator, krb5_authenticator_fields,
           optional_krb5_authenticator);
DEFAPPTAGGEDTYPE(krb5_authenticator, 2, untagged_krb5_authenticator);

static const struct field_info enc_tkt_part_fields[] = {
    /* EncTicketPart ::= [APPLICATION 3] SEQUENCE */
    /* flags[0]                 TicketFlags */
    FIELDOF_NORM(krb5_enc_tkt_part, krb5_flags, flags, 0),
    /* key[1]                   EncryptionKey */
    FIELDOF_NORM(krb5_enc_tkt_part, ptr_encryption_key, session, 1),
    /* crealm[2]                        Realm */
    FIELDOF_NORM(krb5_enc_tkt_part, realm_of_principal, client, 2),
    /* cname[3]                 PrincipalName */
    FIELDOF_NORM(krb5_enc_tkt_part, principal, client, 3),
    /* transited[4]             TransitedEncoding */
    FIELDOF_NORM(krb5_enc_tkt_part, transited, transited, 4),
    /* authtime[5]              KerberosTime */
    FIELDOF_NORM(krb5_enc_tkt_part, kerberos_time, times.authtime, 5),
    /* starttime[6]             KerberosTime OPTIONAL */
    FIELDOF_OPT(krb5_enc_tkt_part, kerberos_time, times.starttime, 6, 6),
    /* endtime[7]                       KerberosTime */
    FIELDOF_NORM(krb5_enc_tkt_part, kerberos_time, times.endtime, 7),
    /* renew-till[8]            KerberosTime OPTIONAL */
    FIELDOF_OPT(krb5_enc_tkt_part, kerberos_time, times.renew_till, 8, 8),
    /* caddr[9]                 HostAddresses OPTIONAL */
    FIELDOF_OPT(krb5_enc_tkt_part, ptr_seqof_host_addresses, caddrs, 9, 9),
    /* authorization-data[10]   AuthorizationData OPTIONAL */
    FIELDOF_OPT(krb5_enc_tkt_part, auth_data_ptr, authorization_data, 10, 10),
};
static unsigned int
optional_enc_tkt_part(const void *p)
{
    const krb5_enc_tkt_part *val = p;
    unsigned int optional = 0;

    if (val->authorization_data != NULL && val->authorization_data[0] != NULL)
        optional |= (1u << 10);
    if (val->caddrs != NULL && val->caddrs[0] != NULL)
        optional |= (1u << 9);
    if (val->times.renew_till)
        optional |= (1u << 8);
    if (val->times.starttime)
        optional |= (1u << 6);

    return optional;
}
DEFSEQTYPE(untagged_enc_tkt_part, krb5_enc_tkt_part, enc_tkt_part_fields,
           optional_enc_tkt_part);
DEFAPPTAGGEDTYPE(enc_tkt_part, 3, untagged_enc_tkt_part);

DEFAPPTAGGEDTYPE(enc_tgs_rep_part, 26, enc_kdc_rep_part);

static const struct field_info as_rep_fields[] = {
    /* AS-REP ::= [APPLICATION 11] KDC-REP */
    /* But KDC-REP needs to know what type it's being encapsulated
       in, so expand each version.  */
    FIELD_INT_IMM(KVNO, 0),
    FIELD_INT_IMM(KRB5_AS_REP, 1),
    FIELDOF_OPT(krb5_kdc_rep, ptr_seqof_pa_data, padata, 2, 2),
    FIELDOF_NORM(krb5_kdc_rep, realm_of_principal, client, 3),
    FIELDOF_NORM(krb5_kdc_rep, principal, client, 4),
    FIELDOF_NORM(krb5_kdc_rep, ticket_ptr, ticket, 5),
    FIELDOF_NORM(krb5_kdc_rep, encrypted_data, enc_part, 6),
};
static unsigned int
optional_as_rep(const void *p)
{
    const krb5_kdc_rep *val = p;
    unsigned int optional = 0;

    if (val->padata != NULL && val->padata[0] != NULL)
        optional |= (1u << 2);

    return optional;
}
DEFSEQTYPE(untagged_as_rep, krb5_kdc_rep, as_rep_fields, optional_as_rep);
DEFAPPTAGGEDTYPE(as_rep, 11, untagged_as_rep);

static const struct field_info tgs_rep_fields[] = {
    /* TGS-REP ::= [APPLICATION 13] KDC-REP */
    /* But KDC-REP needs to know what type it's being encapsulated
       in, so expand each version.  */
    FIELD_INT_IMM(KVNO, 0),
    FIELD_INT_IMM(KRB5_TGS_REP, 1),
    FIELDOF_OPT(krb5_kdc_rep, ptr_seqof_pa_data, padata, 2, 2),
    FIELDOF_NORM(krb5_kdc_rep, realm_of_principal, client, 3),
    FIELDOF_NORM(krb5_kdc_rep, principal, client, 4),
    FIELDOF_NORM(krb5_kdc_rep, ticket_ptr, ticket, 5),
    FIELDOF_NORM(krb5_kdc_rep, encrypted_data, enc_part, 6),
};
static unsigned int
optional_tgs_rep(const void *p)
{
    const krb5_kdc_rep *val = p;
    unsigned int optional = 0;

    if (val->padata != NULL && val->padata[0] != NULL)
        optional |= (1u << 2);

    return optional;
}
DEFSEQTYPE(untagged_tgs_rep, krb5_kdc_rep, tgs_rep_fields, optional_tgs_rep);
DEFAPPTAGGEDTYPE(tgs_rep, 13, untagged_tgs_rep);

static const struct field_info ap_req_fields[] = {
    /* AP-REQ ::=       [APPLICATION 14] SEQUENCE */
    /* pvno[0]          INTEGER */
    FIELD_INT_IMM(KVNO, 0),
    /* msg-type[1]      INTEGER */
    FIELD_INT_IMM(ASN1_KRB_AP_REQ, 1),
    /* ap-options[2]    APOptions */
    FIELDOF_NORM(krb5_ap_req, krb5_flags, ap_options, 2),
    /* ticket[3]                Ticket */
    FIELDOF_NORM(krb5_ap_req, ticket_ptr, ticket, 3),
    /* authenticator[4] EncryptedData */
    FIELDOF_NORM(krb5_ap_req, encrypted_data, authenticator, 4),
};
DEFSEQTYPE(untagged_ap_req, krb5_ap_req, ap_req_fields, 0);
DEFAPPTAGGEDTYPE(ap_req, 14, untagged_ap_req);

static const struct field_info ap_rep_fields[] = {
    /* AP-REP ::=       [APPLICATION 15] SEQUENCE */
    /* pvno[0]          INTEGER */
    FIELD_INT_IMM(KVNO, 0),
    /* msg-type[1]      INTEGER */
    FIELD_INT_IMM(ASN1_KRB_AP_REP, 1),
    /* enc-part[2]      EncryptedData */
    FIELDOF_NORM(krb5_ap_rep, encrypted_data, enc_part, 2),
};
DEFSEQTYPE(untagged_ap_rep, krb5_ap_rep, ap_rep_fields, 0);
DEFAPPTAGGEDTYPE(ap_rep, 15, untagged_ap_rep);

static const struct field_info ap_rep_enc_part_fields[] = {
    /* EncAPRepPart ::= [APPLICATION 27] SEQUENCE */
    /* ctime[0]         KerberosTime */
    FIELDOF_NORM(krb5_ap_rep_enc_part, kerberos_time, ctime, 0),
    /* cusec[1]         INTEGER */
    FIELDOF_NORM(krb5_ap_rep_enc_part, int32, cusec, 1),
    /* subkey[2]                EncryptionKey OPTIONAL */
    FIELDOF_OPT(krb5_ap_rep_enc_part, ptr_encryption_key, subkey, 2, 2),
    /* seq-number[3]    INTEGER OPTIONAL */
    FIELDOF_OPT(krb5_ap_rep_enc_part, uint, seq_number, 3, 3),
};
static unsigned int
optional_ap_rep_enc_part(const void *p)
{
    const krb5_ap_rep_enc_part *val = p;
    unsigned int optional = 0;

    if (val->seq_number)
        optional |= (1u << 3);
    if (val->subkey != NULL)
        optional |= (1u << 2);

    return optional;
}
DEFSEQTYPE(untagged_ap_rep_enc_part, krb5_ap_rep_enc_part,
           ap_rep_enc_part_fields, optional_ap_rep_enc_part);
DEFAPPTAGGEDTYPE(ap_rep_enc_part, 27, untagged_ap_rep_enc_part);

static const struct field_info as_req_fields[] = {
    /* AS-REQ ::= [APPLICATION 10] KDC-REQ */
    FIELD_INT_IMM(KVNO, 1),
    FIELD_INT_IMM(KRB5_AS_REQ, 2),
    FIELDOF_OPT(krb5_kdc_req, ptr_seqof_pa_data, padata, 3, 3),
    FIELDOF_ENCODEAS(krb5_kdc_req, kdc_req_body, 4),
};
static unsigned int
optional_as_req(const void *p)
{
    const krb5_kdc_req *val = p;
    unsigned int optional = 0;

    if (val->padata != NULL && val->padata[0] != NULL)
        optional |= (1u << 3);

    return optional;
}
DEFSEQTYPE(untagged_as_req, krb5_kdc_req, as_req_fields, optional_as_req);
DEFAPPTAGGEDTYPE(as_req, 10, untagged_as_req);

static const struct field_info tgs_req_fields[] = {
    /* TGS-REQ ::= [APPLICATION 12] KDC-REQ */
    FIELD_INT_IMM(KVNO, 1),
    FIELD_INT_IMM(KRB5_TGS_REQ, 2),
    FIELDOF_OPT(krb5_kdc_req, ptr_seqof_pa_data, padata, 3, 3),
    FIELDOF_ENCODEAS(krb5_kdc_req, kdc_req_body, 4),
};
static unsigned int
optional_tgs_req(const void *p)
{
    const krb5_kdc_req *val = p;
    unsigned int optional = 0;

    if (val->padata != NULL && val->padata[0] != NULL)
        optional |= (1u << 3);

    return optional;
}
DEFSEQTYPE(untagged_tgs_req, krb5_kdc_req, tgs_req_fields,
           optional_tgs_req);
DEFAPPTAGGEDTYPE(tgs_req, 12, untagged_tgs_req);

static const struct field_info krb5_safe_fields[] = {
    FIELD_INT_IMM(KVNO, 0),
    FIELD_INT_IMM(ASN1_KRB_SAFE,1),
    FIELD_SELF(krb_safe_body, 2),
    FIELDOF_NORM(krb5_safe, checksum_ptr, checksum, 3),
};
DEFSEQTYPE(untagged_krb5_safe, krb5_safe, krb5_safe_fields, 0);
DEFAPPTAGGEDTYPE(krb5_safe, 20, untagged_krb5_safe);

DEFPTRTYPE(krb_saved_safe_body_ptr, opaque_data);
DEFFIELDTYPE(krb5_safe_checksum_only, krb5_safe,
             FIELDOF_NORM(krb5_safe, checksum_ptr, checksum, -1));
DEFPTRTYPE(krb5_safe_checksum_only_ptr, krb5_safe_checksum_only);
static const struct field_info krb5_safe_with_body_fields[] = {
    FIELD_INT_IMM(KVNO, 0),
    FIELD_INT_IMM(ASN1_KRB_SAFE,1),
    FIELDOF_NORM(struct krb5_safe_with_body, krb_saved_safe_body_ptr, body, 2),
    FIELDOF_NORM(struct krb5_safe_with_body, krb5_safe_checksum_only_ptr, safe, 3),
};
DEFSEQTYPE(untagged_krb5_safe_with_body, struct krb5_safe_with_body,
           krb5_safe_with_body_fields, 0);
DEFAPPTAGGEDTYPE(krb5_safe_with_body, 20, untagged_krb5_safe_with_body);

static const struct field_info priv_fields[] = {
    FIELD_INT_IMM(KVNO, 0),
    FIELD_INT_IMM(ASN1_KRB_PRIV, 1),
    FIELDOF_NORM(krb5_priv, encrypted_data, enc_part, 3),
};
DEFSEQTYPE(untagged_priv, krb5_priv, priv_fields, 0);
DEFAPPTAGGEDTYPE(krb5_priv, 21, untagged_priv);

static const struct field_info priv_enc_part_fields[] = {
    FIELDOF_NORM(krb5_priv_enc_part, ostring_data, user_data, 0),
    FIELDOF_OPT(krb5_priv_enc_part, kerberos_time, timestamp, 1, 1),
    FIELDOF_OPT(krb5_priv_enc_part, int32, usec, 2, 2),
    FIELDOF_OPT(krb5_priv_enc_part, uint, seq_number, 3, 3),
    FIELDOF_NORM(krb5_priv_enc_part, address_ptr, s_address, 4),
    FIELDOF_OPT(krb5_priv_enc_part, address_ptr, r_address, 5, 5),
};
static unsigned int
optional_priv_enc_part(const void *p)
{
    const krb5_priv_enc_part *val = p;
    unsigned int optional = 0;

    if (val->timestamp) {
        optional |= (1u << 2);
        optional |= (1u << 1);
    }
    if (val->seq_number)
        optional |= (1u << 3);
    if (val->r_address)
        optional |= (1u << 5);

    return optional;
}
DEFSEQTYPE(untagged_priv_enc_part, krb5_priv_enc_part, priv_enc_part_fields,
           optional_priv_enc_part);
DEFAPPTAGGEDTYPE(priv_enc_part, 28, untagged_priv_enc_part);

static const struct field_info cred_fields[] = {
    /* KRB-CRED ::= [APPLICATION 22] SEQUENCE */
    /* pvno[0]          INTEGER */
    FIELD_INT_IMM(KVNO, 0),
    /* msg-type[1]      INTEGER, -- KRB_CRED */
    FIELD_INT_IMM(ASN1_KRB_CRED, 1),
    /* tickets[2]       SEQUENCE OF Ticket */
    FIELDOF_NORM(krb5_cred, ptr_seqof_ticket, tickets, 2),
    /* enc-part[3]      EncryptedData */
    FIELDOF_NORM(krb5_cred, encrypted_data, enc_part, 3),
};
DEFSEQTYPE(untagged_cred, krb5_cred, cred_fields, 0);
DEFAPPTAGGEDTYPE(krb5_cred, 22, untagged_cred);

static const struct field_info enc_cred_part_fields[] = {
    /* EncKrbCredPart ::= [APPLICATION 29] SEQUENCE */
    /* ticket-info[0]   SEQUENCE OF KrbCredInfo */
    FIELDOF_NORM(krb5_cred_enc_part, ptrseqof_cred_info, ticket_info, 0),
    /* nonce[1]         INTEGER OPTIONAL */
    FIELDOF_OPT(krb5_cred_enc_part, int32, nonce, 1, 1),
    /* timestamp[2]     KerberosTime OPTIONAL */
    FIELDOF_OPT(krb5_cred_enc_part, kerberos_time, timestamp, 2, 2),
    /* usec[3]          INTEGER OPTIONAL */
    FIELDOF_OPT(krb5_cred_enc_part, int32, usec, 3, 3),
    /* s-address[4]     HostAddress OPTIONAL */
    FIELDOF_OPT(krb5_cred_enc_part, address_ptr, s_address, 4, 4),
    /* r-address[5]     HostAddress OPTIONAL */
    FIELDOF_OPT(krb5_cred_enc_part, address_ptr, r_address, 5, 5),
};
static unsigned int
optional_enc_cred_part(const void *p)
{
    const krb5_cred_enc_part *val = p;
    unsigned int optional = 0;

    if (val->r_address != NULL)
        optional |= (1u << 5);

    if (val->s_address != NULL)
        optional |= (1u << 4);

    if (val->timestamp) {
        optional |= (1u << 2);
        optional |= (1u << 3);
    }

    if (val->nonce)
        optional |= (1u << 1);

    return optional;
}
DEFSEQTYPE(untagged_enc_cred_part, krb5_cred_enc_part, enc_cred_part_fields,
           optional_enc_cred_part);
DEFAPPTAGGEDTYPE(enc_cred_part, 29, untagged_enc_cred_part);

static const struct field_info error_fields[] = {
    /* KRB-ERROR ::= [APPLICATION 30] SEQUENCE */
    /* pvno[0]          INTEGER */
    FIELD_INT_IMM(KVNO, 0),
    /* msg-type[1]      INTEGER */
    FIELD_INT_IMM(ASN1_KRB_ERROR, 1),
    /* ctime[2]         KerberosTime OPTIONAL */
    FIELDOF_OPT(krb5_error, kerberos_time, ctime, 2, 2),
    /* cusec[3]         INTEGER OPTIONAL */
    FIELDOF_OPT(krb5_error, int32, cusec, 3, 3),
    /* stime[4]         KerberosTime */
    FIELDOF_NORM(krb5_error, kerberos_time, stime, 4),
    /* susec[5]         INTEGER */
    FIELDOF_NORM(krb5_error, int32, susec, 5),
    /* error-code[6]    INTEGER */
    FIELDOF_NORM(krb5_error, ui_4, error, 6),
    /* crealm[7]        Realm OPTIONAL */
    FIELDOF_OPT(krb5_error, realm_of_principal, client, 7, 7),
    /* cname[8]         PrincipalName OPTIONAL */
    FIELDOF_OPT(krb5_error, principal, client, 8, 8),
    /* realm[9]         Realm -- Correct realm */
    FIELDOF_NORM(krb5_error, realm_of_principal, server, 9),
    /* sname[10]        PrincipalName -- Correct name */
    FIELDOF_NORM(krb5_error, principal, server, 10),
    /* e-text[11]       GeneralString OPTIONAL */
    FIELDOF_OPT(krb5_error, gstring_data, text, 11, 11),
    /* e-data[12]       OCTET STRING OPTIONAL */
    FIELDOF_OPT(krb5_error, ostring_data, e_data, 12, 12),
};
static unsigned int
optional_error(const void *p)
{
    const krb5_error *val = p;
    unsigned int optional = 0;

    if (val->ctime)
        optional |= (1u << 2);
    if (val->cusec)
        optional |= (1u << 3);
    if (val->client) {
        optional |= (1u << 7);
        optional |= (1u << 8);
    }
    if (val->text.data != NULL && val->text.length > 0)
        optional |= (1u << 11);
    if (val->e_data.data != NULL && val->e_data.length > 0)
        optional |= (1u << 12);

    return optional;
}
DEFSEQTYPE(untagged_krb5_error, krb5_error, error_fields, optional_error);
DEFAPPTAGGEDTYPE(krb5_error, 30, untagged_krb5_error);

static const struct field_info alt_method_fields[] = {
    FIELDOF_NORM(krb5_alt_method, int32, method, 0),
    FIELDOF_OPTSTRING(krb5_alt_method, octetstring, data, length, 1, 1),
};
static unsigned int
optional_alt_method(const void *p)
{
    const krb5_alt_method *a = p;
    unsigned int optional = 0;

    if (a->data != NULL && a->length > 0)
        optional |= (1u << 1);

    return optional;
}
DEFSEQTYPE(alt_method, krb5_alt_method, alt_method_fields, optional_alt_method);

static const struct field_info pa_enc_ts_fields[] = {
    FIELDOF_NORM(krb5_pa_enc_ts, kerberos_time, patimestamp, 0),
    FIELDOF_OPT(krb5_pa_enc_ts, int32, pausec, 1, 1),
};
static unsigned int
optional_pa_enc_ts(const void *p)
{
    const krb5_pa_enc_ts *val = p;
    unsigned int optional = 0;

    if (val->pausec)
        optional |= (1u << 1);

    return optional;
}
DEFSEQTYPE(pa_enc_ts, krb5_pa_enc_ts, pa_enc_ts_fields, optional_pa_enc_ts);

static const struct field_info pwd_data_fields[] = {
    FIELDOF_NORM(krb5_pwd_data, int32, sequence_count, 0),
    FIELDOF_NORM(krb5_pwd_data, ptr_seqof_passwdsequence, element, 1),
};
DEFSEQTYPE(pwd_data, krb5_pwd_data, pwd_data_fields, 0);

static const struct field_info setpw_req_fields[] = {
    FIELDOF_NORM(struct krb5_setpw_req, ostring_data, password, 0),
    FIELDOF_NORM(struct krb5_setpw_req, principal, target, 1),
    FIELDOF_NORM(struct krb5_setpw_req, realm_of_principal, target, 2),
};

DEFSEQTYPE(setpw_req, struct krb5_setpw_req, setpw_req_fields, 0);

/* [MS-SFU] Section 2.2.1. */
static const struct field_info pa_for_user_fields[] = {
    FIELDOF_NORM(krb5_pa_for_user, principal, user, 0),
    FIELDOF_NORM(krb5_pa_for_user, realm_of_principal, user, 1),
    FIELDOF_NORM(krb5_pa_for_user, checksum, cksum, 2),
    FIELDOF_NORM(krb5_pa_for_user, gstring_data, auth_package, 3),
};

DEFSEQTYPE(pa_for_user, krb5_pa_for_user, pa_for_user_fields, 0);

/* [MS-SFU] Section 2.2.2. */
static const struct field_info s4u_userid_fields[] = {
    FIELDOF_NORM(krb5_s4u_userid, int32, nonce, 0),
    FIELDOF_OPT(krb5_s4u_userid, principal, user, 1, 1),
    FIELDOF_NORM(krb5_s4u_userid, realm_of_principal, user, 2),
    FIELDOF_OPT(krb5_s4u_userid, ostring_data, subject_cert, 3, 3),
    FIELDOF_OPT(krb5_s4u_userid, krb5_flags, options, 4, 4),
};

static unsigned int s4u_userid_optional (const void *p) {
    const krb5_s4u_userid *val = p;
    unsigned int optional = 0;
    if (val->user != NULL && val->user->length != 0)
        optional |= (1u)<<1;
    if (val->subject_cert.length != 0)
        optional |= (1u)<<3;
    if (val->options != 0)
        optional |= (1u)<<4;
    return optional;
}

DEFSEQTYPE(s4u_userid, krb5_s4u_userid, s4u_userid_fields, s4u_userid_optional);

static const struct field_info pa_s4u_x509_user_fields[] = {
    FIELDOF_NORM(krb5_pa_s4u_x509_user, s4u_userid, user_id, 0),
    FIELDOF_NORM(krb5_pa_s4u_x509_user, checksum, cksum, 1),
};

DEFSEQTYPE(pa_s4u_x509_user, krb5_pa_s4u_x509_user, pa_s4u_x509_user_fields, 0);

/* draft-ietf-krb-wg-kerberos-referrals Appendix A. */
static const struct field_info pa_svr_referral_data_fields[] = {
    FIELDOF_NORM(krb5_pa_svr_referral_data, realm_of_principal, principal, 0),
    FIELDOF_OPT(krb5_pa_svr_referral_data, principal, principal, 1, 1),
};

DEFSEQTYPE(pa_svr_referral_data, krb5_pa_svr_referral_data, pa_svr_referral_data_fields, 0);

/* draft-ietf-krb-wg-kerberos-referrals Section 8. */
static const struct field_info pa_server_referral_data_fields[] = {
    FIELDOF_OPT(krb5_pa_server_referral_data, gstring_data_ptr, referred_realm, 0, 0),
    FIELDOF_OPT(krb5_pa_server_referral_data, principal, true_principal_name, 1, 1),
    FIELDOF_OPT(krb5_pa_server_referral_data, principal, requested_principal_name, 2, 2),
    FIELDOF_OPT(krb5_pa_server_referral_data, kerberos_time, referral_valid_until, 3, 3),
    FIELDOF_NORM(krb5_pa_server_referral_data, checksum, rep_cksum, 4),
};

DEFSEQTYPE(pa_server_referral_data, krb5_pa_server_referral_data, pa_server_referral_data_fields, 0);

#if 0
/* draft-brezak-win2k-krb-authz Section 6. */
static const struct field_info pa_pac_request_fields[] = {
    FIELDOF_NORM(krb5_pa_pac_req, boolean, include_pac, 0),
};

DEFSEQTYPE(pa_pac_request, krb5_pa_pac_req, pa_pac_request_fields, 0);
#endif

/* RFC 4537 */
DEFFIELDTYPE(etype_list, krb5_etype_list,
             FIELDOF_SEQOF_INT32(krb5_etype_list, int32_ptr, etypes, length, -1));

/* draft-ietf-krb-wg-preauth-framework-09 */
static const struct field_info fast_armor_fields[] = {
    FIELDOF_NORM(krb5_fast_armor, int32, armor_type, 0),
    FIELDOF_NORM( krb5_fast_armor, ostring_data, armor_value, 1),
};

DEFSEQTYPE( fast_armor, krb5_fast_armor, fast_armor_fields, 0);
DEFPTRTYPE( ptr_fast_armor, fast_armor);

static const struct field_info fast_armored_req_fields[] = {
    FIELDOF_OPT( krb5_fast_armored_req, ptr_fast_armor, armor, 0, 0),
    FIELDOF_NORM( krb5_fast_armored_req, checksum, req_checksum, 1),
    FIELDOF_NORM( krb5_fast_armored_req, encrypted_data, enc_part, 2),
};

static unsigned int fast_armored_req_optional (const void *p) {
    const krb5_fast_armored_req *val = p;
    unsigned int optional = 0;
    if (val->armor)
        optional |= (1u)<<0;
    return optional;
}

DEFSEQTYPE( fast_armored_req, krb5_fast_armored_req, fast_armored_req_fields, fast_armored_req_optional);
DEFFIELDTYPE( pa_fx_fast_request, krb5_fast_armored_req,
              FIELDOF_ENCODEAS( krb5_fast_armored_req, fast_armored_req, 0));

DEFFIELDTYPE(fast_req_padata, krb5_kdc_req,
             FIELDOF_NORM(krb5_kdc_req, ptr_seqof_pa_data, padata, -1));
DEFPTRTYPE(ptr_fast_req_padata, fast_req_padata);

static const struct field_info fast_req_fields[] = {
    FIELDOF_NORM(krb5_fast_req, krb5_flags, fast_options, 0),
    FIELDOF_NORM( krb5_fast_req, ptr_fast_req_padata, req_body, 1),
    FIELDOF_NORM( krb5_fast_req, ptr_kdc_req_body, req_body, 2),
};

DEFSEQTYPE(fast_req, krb5_fast_req, fast_req_fields, 0);


static const struct field_info fast_finished_fields[] = {
    FIELDOF_NORM( krb5_fast_finished, kerberos_time, timestamp, 0),
    FIELDOF_NORM( krb5_fast_finished, int32, usec, 1),
    FIELDOF_NORM( krb5_fast_finished, realm_of_principal, client, 2),
    FIELDOF_NORM(krb5_fast_finished, principal, client, 3),
    FIELDOF_NORM( krb5_fast_finished, checksum, ticket_checksum, 4),
};

DEFSEQTYPE( fast_finished, krb5_fast_finished, fast_finished_fields, 0);

DEFPTRTYPE( ptr_fast_finished, fast_finished);

static const struct field_info fast_response_fields[] = {
    FIELDOF_NORM(krb5_fast_response, ptr_seqof_pa_data, padata, 0),
    FIELDOF_OPT( krb5_fast_response, ptr_encryption_key, strengthen_key, 1, 1),
    FIELDOF_OPT( krb5_fast_response, ptr_fast_finished, finished, 2, 2),
    FIELDOF_NORM(krb5_fast_response, int32, nonce, 3),
};

static unsigned int
fast_response_optional (const void *p)
{
    unsigned int optional = 0;
    const krb5_fast_response *val = p;
    if (val->strengthen_key)
        optional |= (1u <<1);
    if (val->finished)
        optional |= (1u<<2);
    return optional;
}
DEFSEQTYPE( fast_response, krb5_fast_response, fast_response_fields, fast_response_optional);

static const struct field_info fast_rep_fields[] = {
    FIELDOF_ENCODEAS(krb5_enc_data, encrypted_data, 0),
};
DEFSEQTYPE(fast_rep, krb5_enc_data, fast_rep_fields, 0);

DEFFIELDTYPE(pa_fx_fast_reply, krb5_enc_data,
             FIELDOF_ENCODEAS(krb5_enc_data, fast_rep, 0));

static const struct field_info ad_kdcissued_fields[] = {
    FIELDOF_NORM(krb5_ad_kdcissued, checksum, ad_checksum, 0),
    FIELDOF_OPT(krb5_ad_kdcissued, realm_of_principal, i_principal, 1, 1),
    FIELDOF_OPT(krb5_ad_kdcissued, principal, i_principal, 2, 1),
    FIELDOF_NORM(krb5_ad_kdcissued, auth_data_ptr, elements, 3),
};

static unsigned int
ad_kdcissued_optional(const void *p)
{
    unsigned int optional = 0;
    const krb5_ad_kdcissued *val = p;
    if (val->i_principal)
        optional |= (1u << 1);
    return optional;
}

DEFSEQTYPE(ad_kdc_issued, krb5_ad_kdcissued, ad_kdcissued_fields, ad_kdcissued_optional);

static const struct field_info princ_plus_realm_fields[] = {
    FIELDOF_ENCODEAS(krb5_principal_data, principal_data, 0),
    FIELDOF_ENCODEAS(krb5_principal_data, realm_of_principal_data, 1),
};

DEFSEQTYPE(princ_plus_realm_data, krb5_principal_data, princ_plus_realm_fields, 0);
DEFPTRTYPE(princ_plus_realm, princ_plus_realm_data);

DEFNULLTERMSEQOFTYPE(seq_of_princ_plus_realm, princ_plus_realm);
DEFPTRTYPE(ptr_seq_of_princ_plus_realm, seq_of_princ_plus_realm);

static const struct field_info ad_signedpath_data_fields[] = {
    FIELDOF_NORM(krb5_ad_signedpath_data, princ_plus_realm, client, 0),
    FIELDOF_NORM(krb5_ad_signedpath_data, kerberos_time, authtime, 1),
    FIELDOF_OPT(krb5_ad_signedpath_data, ptr_seq_of_princ_plus_realm, delegated, 2, 2),
    FIELDOF_OPT(krb5_ad_signedpath_data, ptr_seqof_pa_data, method_data, 3, 3),
    FIELDOF_OPT(krb5_ad_signedpath_data, auth_data_ptr, authorization_data, 4, 4),
};

static unsigned int ad_signedpath_data_optional(const void *p)
{
    unsigned int optional = 0;
    const krb5_ad_signedpath_data *val = p;
    if (val->delegated && val->delegated[0])
        optional |= (1u << 2);
    if (val->method_data && val->method_data[0])
        optional |= (1u << 3);
    if (val->authorization_data && val->authorization_data[0])
        optional |= (1u << 4);
    return optional;
}

DEFSEQTYPE(ad_signedpath_data, krb5_ad_signedpath_data, ad_signedpath_data_fields, ad_signedpath_data_optional);

static const struct field_info ad_signedpath_fields[] = {
    FIELDOF_NORM(krb5_ad_signedpath, int32, enctype, 0),
    FIELDOF_NORM(krb5_ad_signedpath, checksum, checksum, 1),
    FIELDOF_OPT(krb5_ad_signedpath, ptr_seq_of_princ_plus_realm, delegated, 2, 2),
    FIELDOF_OPT(krb5_ad_signedpath, ptr_seqof_pa_data, method_data, 3, 3),
};

static unsigned int ad_signedpath_optional(const void *p)
{
    unsigned int optional = 0;
    const krb5_ad_signedpath *val = p;
    if (val->delegated && val->delegated[0])
        optional |= (1u << 2);
    if (val->method_data && val->method_data[0])
        optional |= (1u << 3);
    return optional;
}

DEFSEQTYPE(ad_signedpath, krb5_ad_signedpath, ad_signedpath_fields, ad_signedpath_optional);

static const struct field_info iakerb_header_fields[] = {
    FIELDOF_NORM(krb5_iakerb_header, ostring_data, target_realm, 1),
    FIELDOF_OPT(krb5_iakerb_header, ostring_data_ptr, cookie, 2, 2),
};

static unsigned int iakerb_header_optional(const void *p)
{
    unsigned int optional = 0;
    const krb5_iakerb_header *val = p;
    if (val->cookie && val->cookie->data)
        optional |= (1u << 2);
    return optional;
}

DEFSEQTYPE(iakerb_header, krb5_iakerb_header, iakerb_header_fields, iakerb_header_optional);

static const struct field_info iakerb_finished_fields[] = {
    FIELDOF_NORM(krb5_iakerb_finished, checksum, checksum, 1),
};

static unsigned int iakerb_finished_optional(const void *p)
{
    unsigned int optional = 0;
    return optional;
}

DEFSEQTYPE(iakerb_finished, krb5_iakerb_finished, iakerb_finished_fields,
           iakerb_finished_optional);

/* Exported complete encoders -- these produce a krb5_data with
   the encoding in the correct byte order.  */

MAKE_FULL_ENCODER(encode_krb5_authenticator, krb5_authenticator);
MAKE_FULL_ENCODER(encode_krb5_ticket, ticket);
MAKE_FULL_ENCODER(encode_krb5_encryption_key, encryption_key);
MAKE_FULL_ENCODER(encode_krb5_enc_tkt_part, enc_tkt_part);
/* XXX We currently (for backwards compatibility) encode both
   EncASRepPart and EncTGSRepPart with application tag 26.  */
MAKE_FULL_ENCODER(encode_krb5_enc_kdc_rep_part, enc_tgs_rep_part);
MAKE_FULL_ENCODER(encode_krb5_as_rep, as_rep);
MAKE_FULL_ENCODER(encode_krb5_tgs_rep, tgs_rep);
MAKE_FULL_ENCODER(encode_krb5_ap_req, ap_req);
MAKE_FULL_ENCODER(encode_krb5_ap_rep, ap_rep);
MAKE_FULL_ENCODER(encode_krb5_ap_rep_enc_part, ap_rep_enc_part);
MAKE_FULL_ENCODER(encode_krb5_as_req, as_req);
MAKE_FULL_ENCODER(encode_krb5_tgs_req, tgs_req);
MAKE_FULL_ENCODER(encode_krb5_kdc_req_body, kdc_req_body);
MAKE_FULL_ENCODER(encode_krb5_safe, krb5_safe);

/*
 * encode_krb5_safe_with_body
 *
 * Like encode_krb5_safe(), except takes a saved KRB-SAFE-BODY
 * encoding to avoid problems with re-encoding.
 */
MAKE_FULL_ENCODER(encode_krb5_safe_with_body, krb5_safe_with_body);

MAKE_FULL_ENCODER(encode_krb5_priv, krb5_priv);
MAKE_FULL_ENCODER(encode_krb5_enc_priv_part, priv_enc_part);
MAKE_FULL_ENCODER(encode_krb5_checksum, checksum);

MAKE_FULL_ENCODER(encode_krb5_cred, krb5_cred);
MAKE_FULL_ENCODER(encode_krb5_enc_cred_part, enc_cred_part);
MAKE_FULL_ENCODER(encode_krb5_error, krb5_error);
MAKE_FULL_ENCODER(encode_krb5_authdata, auth_data);
MAKE_FULL_ENCODER(encode_krb5_authdata_elt, authdata_elt);
MAKE_FULL_ENCODER(encode_krb5_alt_method, alt_method);
MAKE_FULL_ENCODER(encode_krb5_etype_info, etype_info);
MAKE_FULL_ENCODER(encode_krb5_etype_info2, etype_info2);
MAKE_FULL_ENCODER(encode_krb5_enc_data, encrypted_data);
MAKE_FULL_ENCODER(encode_krb5_pa_enc_ts, pa_enc_ts);
/* Sandia Additions */
MAKE_FULL_ENCODER(encode_krb5_pwd_sequence, passwdsequence);
MAKE_FULL_ENCODER(encode_krb5_pwd_data, pwd_data);
MAKE_FULL_ENCODER(encode_krb5_padata_sequence, seq_of_pa_data);
/* sam preauth additions */
MAKE_FULL_ENCODER(encode_krb5_sam_challenge, sam_challenge);
MAKE_FULL_ENCODER(encode_krb5_sam_challenge_2, sam_challenge_2);
MAKE_FULL_ENCODER(encode_krb5_sam_challenge_2_body,
                  sam_challenge_2_body);
MAKE_FULL_ENCODER(encode_krb5_sam_key, sam_key);
MAKE_FULL_ENCODER(encode_krb5_enc_sam_response_enc,
                  enc_sam_response_enc);
MAKE_FULL_ENCODER(encode_krb5_enc_sam_response_enc_2,
                  enc_sam_response_enc_2);
MAKE_FULL_ENCODER(encode_krb5_sam_response, sam_response);
MAKE_FULL_ENCODER(encode_krb5_sam_response_2, sam_response_2);
MAKE_FULL_ENCODER(encode_krb5_predicted_sam_response,
                  predicted_sam_response);
MAKE_FULL_ENCODER(encode_krb5_setpw_req, setpw_req);
MAKE_FULL_ENCODER(encode_krb5_pa_for_user, pa_for_user);
MAKE_FULL_ENCODER(encode_krb5_s4u_userid, s4u_userid);
MAKE_FULL_ENCODER(encode_krb5_pa_s4u_x509_user, pa_s4u_x509_user);
MAKE_FULL_ENCODER(encode_krb5_pa_svr_referral_data, pa_svr_referral_data);
MAKE_FULL_ENCODER(encode_krb5_pa_server_referral_data, pa_server_referral_data);
MAKE_FULL_ENCODER(encode_krb5_etype_list, etype_list);

MAKE_FULL_ENCODER(encode_krb5_pa_fx_fast_request, pa_fx_fast_request);
MAKE_FULL_ENCODER( encode_krb5_fast_req, fast_req);
MAKE_FULL_ENCODER( encode_krb5_pa_fx_fast_reply, pa_fx_fast_reply);
MAKE_FULL_ENCODER(encode_krb5_fast_response, fast_response);

MAKE_FULL_ENCODER(encode_krb5_ad_kdcissued, ad_kdc_issued);
MAKE_FULL_ENCODER(encode_krb5_ad_signedpath_data, ad_signedpath_data);
MAKE_FULL_ENCODER(encode_krb5_ad_signedpath, ad_signedpath);
MAKE_FULL_ENCODER(encode_krb5_iakerb_header, iakerb_header);
MAKE_FULL_ENCODER(encode_krb5_iakerb_finished, iakerb_finished);

/*
 * PKINIT
 */

/*
 * This code hasn't been converted to use the above framework yet,
 * because we currently have no test cases to validate the new
 * version.  It *also* appears that some of the encodings may disagree
 * with the specifications, but that's a separate problem.
 */

/**** asn1 macros ****/
#if 0
How to write an asn1 encoder function using these macros:

asn1_error_code asn1_encode_krb5_substructure(asn1buf *buf,
                                              const krb5_type *val,
                                              int *retlen)
{
    asn1_setup();

    asn1_addfield(val->last_field, n, asn1_type);
    asn1_addfield(rep->next_to_last_field, n-1, asn1_type);
    ...

        /* for OPTIONAL fields */
        if (rep->field_i == should_not_be_omitted)
            asn1_addfield(rep->field_i, i, asn1_type);

        /*
         * for string fields (these encoders take an additional argument,
         * the length of the string)
         */
        addlenfield(rep->field_length, rep->field, i-1, asn1_type);

        /* if you really have to do things yourself... */
        retval = asn1_encode_asn1_type(buf,rep->field,&length);
        if (retval) return retval;
        sum += length;
        retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, tag_number, length,
                                &length);
        if (retval) return retval;
        sum += length;

        ...
            asn1_addfield(rep->second_field, 1, asn1_type);
            asn1_addfield(rep->first_field, 0, asn1_type);
            asn1_makeseq();

            asn1_cleanup();
}
#endif

/* asn1_addlenfield -- add a field whose length must be separately specified */
#define asn1_addlenfield(len, value, tag, encoder)      \
    {                                                   \
        unsigned int length;                            \
        retval = encoder(buf, len, value, &length);     \
        if (retval) {                                   \
            return retval; }                            \
        sum += length;                                  \
        retval = asn1_make_etag(buf, CONTEXT_SPECIFIC,  \
                                tag, length, &length);  \
        if (retval) {                                   \
            return retval; }                            \
        sum += length;                                  \
    }

/*
 * asn1_addfield_implicit -- add an implicitly tagged field, or component,
 * to the encoding
 */
#define asn1_addfield_implicit(value,tag,encoder)               \
    { unsigned int length;                                      \
        retval = encoder(buf,value,&length);                    \
        if (retval) {                                           \
            return retval; }                                    \
        sum += length;                                          \
        retval = asn1_make_tag(buf, CONTEXT_SPECIFIC,PRIMITIVE, \
                               tag, length, &length);           \
        if (retval) {                                           \
            return retval; }                                    \
        sum += length; }

/*
 * asn1_insert_implicit_octetstring -- add an octet string with implicit
 * tagging
 */
#define asn1_insert_implicit_octetstring(len,value,tag)                 \
    { unsigned int length;                                              \
        retval = asn1buf_insert_octetstring(buf,len,value);             \
        if (retval) {                                                   \
            return retval; }                                            \
        sum += len;                                                     \
        retval = asn1_make_tag(buf, CONTEXT_SPECIFIC, PRIMITIVE,        \
                               tag, len, &length);                      \
        if (retval) {                                                   \
            return retval; }                                            \
        sum += length; }

/* asn1_insert_implicit_bitstring -- add a bitstring with implicit tagging */
/* needs "length" declared in enclosing context */
#define asn1_insert_implicit_bitstring(len, value, tag)         \
    {                                                           \
        retval = asn1buf_insert_octetstring(buf, len, value);   \
        if (retval) {                                           \
            return retval; }                                    \
        sum += len;                                             \
        retval = asn1buf_insert_octet(buf, 0);                  \
        if (retval) {                                           \
            return retval; }                                    \
        sum++;                                                  \
        retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE,       \
                               tag, len + 1, &length);          \
        if (retval) {                                           \
            return retval; }                                    \
        sum += length;                                          \
    }

#ifndef DISABLE_PKINIT

DEFFNXTYPE(algorithm_identifier, krb5_algorithm_identifier, asn1_encode_algorithm_identifier);
DEFFNLENTYPE(object_identifier, asn1_octet *, asn1_encode_oid);
DEFFIELDTYPE(oid_data, krb5_octet_data,
             FIELDOF_STRING(krb5_octet_data,object_identifier, data, length, -1));
DEFPTRTYPE(oid_data_ptr, oid_data);

static const struct field_info kdf_alg_id_fields[] = {
    FIELDOF_ENCODEAS(krb5_octet_data, oid_data, 0)
};
DEFSEQTYPE(kdf_alg_id, krb5_octet_data, kdf_alg_id_fields, NULL);
DEFPTRTYPE(kdf_alg_id_ptr, kdf_alg_id);
DEFNONEMPTYNULLTERMSEQOFTYPE(supported_kdfs, kdf_alg_id_ptr);
DEFPTRTYPE(supported_kdfs_ptr, supported_kdfs);
MAKE_ENCFN(asn1_encode_supported_kdfs,
           supported_kdfs);
MAKE_ENCFN(asn1_encode_kdf_alg_id, kdf_alg_id);


/* Krb5PrincipalName is defined in RFC 4556 and is *not* PrincipalName from RFC 4120*/
static const struct field_info pkinit_krb5_principal_name_fields[] = {
    FIELDOF_NORM(krb5_principal_data, gstring_data, realm, 0),
    FIELDOF_ENCODEAS(krb5_principal_data, principal_data, 1)
};


DEFSEQTYPE(pkinit_krb5_principal_name_data, krb5_principal_data, pkinit_krb5_principal_name_fields, NULL);
DEFPTRTYPE(pkinit_krb5_principal_name, pkinit_krb5_principal_name_data);
DEFOCTETWRAPTYPE(pkinit_krb5_principal_name_wrapped, pkinit_krb5_principal_name);


/* For SP80056A OtherInfo, for pkinit agility */
static const struct field_info sp80056a_other_info_fields[] = {
    FIELDOF_NORM(krb5_sp80056a_other_info, algorithm_identifier, algorithm_identifier, -1),
    FIELDOF_NORM(krb5_sp80056a_other_info, pkinit_krb5_principal_name_wrapped, party_u_info, 0),
    FIELDOF_NORM(krb5_sp80056a_other_info, pkinit_krb5_principal_name_wrapped, party_v_info, 1),
    FIELDOF_STRING(krb5_sp80056a_other_info, s_octetstring, supp_pub_info.data, supp_pub_info.length, 2),
};

DEFSEQTYPE(sp80056a_other_info, krb5_sp80056a_other_info, sp80056a_other_info_fields, NULL);

/* For PkinitSuppPubInfo, for pkinit agility */
static const struct field_info pkinit_supp_pub_info_fields[] = {
    FIELDOF_NORM(krb5_pkinit_supp_pub_info, int32, enctype, 0),
    FIELDOF_STRING(krb5_pkinit_supp_pub_info, octetstring, as_req.data, as_req.length, 1),
    FIELDOF_STRING(krb5_pkinit_supp_pub_info, octetstring, pk_as_rep.data, pk_as_rep.length, 2),
};

DEFSEQTYPE(pkinit_supp_pub_info, krb5_pkinit_supp_pub_info, pkinit_supp_pub_info_fields, NULL);

MAKE_FULL_ENCODER(encode_krb5_pkinit_supp_pub_info, pkinit_supp_pub_info);
MAKE_FULL_ENCODER(encode_krb5_sp80056a_other_info, sp80056a_other_info);

/* Callable encoders for the types defined above, until the PKINIT
   encoders get converted.  */
MAKE_ENCFN(asn1_encode_realm, realm_of_principal_data);
MAKE_ENCFN(asn1_encode_principal_name, principal_data);
MAKE_ENCFN(asn1_encode_encryption_key, encryption_key);
MAKE_ENCFN(asn1_encode_checksum, checksum);

static asn1_error_code
asn1_encode_kerberos_time(asn1buf *buf, const krb5_timestamp val,
                          unsigned int *retlen)
{
    return asn1_encode_kerberos_time_at(buf,&val,retlen);
}

/* Now the real PKINIT encoder functions.  */
asn1_error_code
asn1_encode_pk_authenticator(asn1buf *buf, const krb5_pk_authenticator *val,
                             unsigned int *retlen)
{
    asn1_setup();
    asn1_addlenfield(val->paChecksum.length, val->paChecksum.contents, 3, asn1_encode_octetstring);
    asn1_addfield(val->nonce, 2, asn1_encode_integer);
    asn1_addfield(val->ctime, 1, asn1_encode_kerberos_time);
    asn1_addfield(val->cusec, 0, asn1_encode_integer);

    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_pk_authenticator_draft9(asn1buf *buf,
                                    const krb5_pk_authenticator_draft9 *val,
                                    unsigned int *retlen)
{
    asn1_setup();

    asn1_addfield(val->nonce, 4, asn1_encode_integer);
    asn1_addfield(val->ctime, 3, asn1_encode_kerberos_time);
    asn1_addfield(val->cusec, 2, asn1_encode_integer);
    asn1_addfield(val->kdcName, 1, asn1_encode_realm);
    asn1_addfield(val->kdcName, 0, asn1_encode_principal_name);

    asn1_makeseq();
    asn1_cleanup();
}


asn1_error_code
asn1_encode_algorithm_identifier(asn1buf *buf,
                                 const krb5_algorithm_identifier *val,
                                 unsigned int *retlen)
{
    asn1_setup();

    if (val->parameters.length != 0) {
        retval = asn1buf_insert_octetstring(buf, val->parameters.length,
                                            val->parameters.data);
        if (retval)
            return retval;
        sum += val->parameters.length;
    }

    {
        unsigned int length;
        retval = asn1_encode_oid(buf, val->algorithm.length,
                                 val->algorithm.data,
                                 &length);

        if (retval)
            return retval;
        sum += length;
    }

    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_subject_pk_info(asn1buf *buf, const krb5_subject_pk_info *val,
                            unsigned int *retlen)
{
    asn1_setup();

    {
        unsigned int length;
        asn1_insert_implicit_bitstring(val->subjectPublicKey.length,val->subjectPublicKey.data,ASN1_BITSTRING);
    }

    if (val->algorithm.parameters.length != 0) {
        unsigned int length;

        retval = asn1buf_insert_octetstring(buf, val->algorithm.parameters.length,
                                            val->algorithm.parameters.data);
        if (retval)
            return retval;
        sum += val->algorithm.parameters.length;

        retval = asn1_encode_oid(buf, val->algorithm.algorithm.length,
                                 val->algorithm.algorithm.data,
                                 &length);

        if (retval)
            return retval;
        sum += length;


        retval = asn1_make_etag(buf, UNIVERSAL, ASN1_SEQUENCE,
                                val->algorithm.parameters.length + length,
                                &length);

        if (retval)
            return retval;
        sum += length;
    }

    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_sequence_of_algorithm_identifier(
    asn1buf *buf, const krb5_algorithm_identifier **val,
    unsigned int *retlen)
{
    asn1_setup();
    int i;

    if (val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;

    for (i=0; val[i] != NULL; i++);
    for (i--; i>=0; i--) {
        unsigned int length;
        retval = asn1_encode_algorithm_identifier(buf,val[i],&length);
        if (retval) return retval;
        sum += length;
    }
    asn1_makeseq();

    asn1_cleanup();
}

asn1_error_code
asn1_encode_auth_pack(asn1buf *buf, const krb5_auth_pack *val,
                      unsigned int *retlen)
{
    asn1_setup();

    if (val->supportedKDFs != NULL)
        asn1_addfield(val->supportedKDFs, 4, asn1_encode_supported_kdfs);
    if (val->clientDHNonce.length != 0)
        asn1_addlenfield(val->clientDHNonce.length, val->clientDHNonce.data, 3, asn1_encode_octetstring);
    if (val->supportedCMSTypes != NULL)
        asn1_addfield((const krb5_algorithm_identifier **)val->supportedCMSTypes,2,asn1_encode_sequence_of_algorithm_identifier);
    if (val->clientPublicValue != NULL)
        asn1_addfield(val->clientPublicValue,1,asn1_encode_subject_pk_info);
    asn1_addfield(&(val->pkAuthenticator),0,asn1_encode_pk_authenticator);

    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_auth_pack_draft9(asn1buf *buf, const krb5_auth_pack_draft9 *val,
                             unsigned int *retlen)
{
    asn1_setup();

    if (val->clientPublicValue != NULL)
        asn1_addfield(val->clientPublicValue, 1, asn1_encode_subject_pk_info);
    asn1_addfield(&(val->pkAuthenticator), 0, asn1_encode_pk_authenticator_draft9);

    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_external_principal_identifier(
    asn1buf *buf, const krb5_external_principal_identifier *val,
    unsigned int *retlen)
{
    asn1_setup();

    /* Verify there is something to encode */
    if (val->subjectKeyIdentifier.length == 0 && val->issuerAndSerialNumber.length == 0 && val->subjectName.length == 0)
        return ASN1_MISSING_FIELD;

    if (val->subjectKeyIdentifier.length != 0)
        asn1_insert_implicit_octetstring(val->subjectKeyIdentifier.length,val->subjectKeyIdentifier.data,2);

    if (val->issuerAndSerialNumber.length != 0)
        asn1_insert_implicit_octetstring(val->issuerAndSerialNumber.length,val->issuerAndSerialNumber.data,1);

    if (val->subjectName.length != 0)
        asn1_insert_implicit_octetstring(val->subjectName.length,val->subjectName.data,0);

    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_sequence_of_external_principal_identifier(
    asn1buf *buf,
    const krb5_external_principal_identifier **val,
    unsigned int *retlen)
{
    asn1_setup();
    int i;

    if (val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;

    for (i=0; val[i] != NULL; i++);
    for (i--; i>=0; i--) {
        unsigned int length;
        retval = asn1_encode_external_principal_identifier(buf,val[i],&length);
        if (retval) return retval;
        sum += length;
    }
    asn1_makeseq();

    asn1_cleanup();
}

asn1_error_code
asn1_encode_pa_pk_as_req(asn1buf *buf, const krb5_pa_pk_as_req *val,
                         unsigned int *retlen)
{
    asn1_setup();

    if (val->kdcPkId.length != 0)
        asn1_insert_implicit_octetstring(val->kdcPkId.length,val->kdcPkId.data,2);

    if (val->trustedCertifiers != NULL)
        asn1_addfield((const krb5_external_principal_identifier **)val->trustedCertifiers,1,asn1_encode_sequence_of_external_principal_identifier);

    asn1_insert_implicit_octetstring(val->signedAuthPack.length,val->signedAuthPack.data,0);

    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_trusted_ca(asn1buf *buf, const krb5_trusted_ca *val,
                       unsigned int *retlen)
{
    asn1_setup();

    switch (val->choice) {
    case choice_trusted_cas_issuerAndSerial:
        asn1_insert_implicit_octetstring(val->u.issuerAndSerial.length,val->u.issuerAndSerial.data,2);
        break;
    case choice_trusted_cas_caName:
        asn1_insert_implicit_octetstring(val->u.caName.length,val->u.caName.data,1);
        break;
    case choice_trusted_cas_principalName:
        asn1_addfield_implicit(val->u.principalName,0,asn1_encode_principal_name);
        break;
    default:
        return ASN1_MISSING_FIELD;
    }

    asn1_cleanup();
}

asn1_error_code
asn1_encode_sequence_of_trusted_ca(asn1buf *buf, const krb5_trusted_ca **val,
                                   unsigned int *retlen)
{
    asn1_setup();
    int i;

    if (val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;

    for (i=0; val[i] != NULL; i++);
    for (i--; i>=0; i--) {
        unsigned int length;
        retval = asn1_encode_trusted_ca(buf,val[i],&length);
        if (retval) return retval;
        sum += length;
    }
    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_pa_pk_as_req_draft9(asn1buf *buf,
                                const krb5_pa_pk_as_req_draft9 *val,
                                unsigned int *retlen)
{
    asn1_setup();

    if (val->encryptionCert.length != 0)
        asn1_insert_implicit_octetstring(val->encryptionCert.length,val->encryptionCert.data,3);

    if (val->kdcCert.length != 0)
        asn1_insert_implicit_octetstring(val->kdcCert.length,val->kdcCert.data,2);

    if (val->trustedCertifiers != NULL)
        asn1_addfield((const krb5_trusted_ca **)val->trustedCertifiers,1,asn1_encode_sequence_of_trusted_ca);

    asn1_insert_implicit_octetstring(val->signedAuthPack.length,val->signedAuthPack.data,0);

    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_dh_rep_info(asn1buf *buf, const krb5_dh_rep_info *val,
                        unsigned int *retlen)
{
    asn1_setup();

    if (val->kdfID)
        asn1_addfield(val->kdfID, 2, asn1_encode_kdf_alg_id);
    if (val->serverDHNonce.length != 0)
        asn1_insert_implicit_octetstring(val->serverDHNonce.length,val->serverDHNonce.data,1);

    asn1_insert_implicit_octetstring(val->dhSignedData.length,val->dhSignedData.data,0);

    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_kdc_dh_key_info(asn1buf *buf, const krb5_kdc_dh_key_info *val,
                            unsigned int *retlen)
{
    asn1_setup();

    if (val->dhKeyExpiration != 0)
        asn1_addfield(val->dhKeyExpiration, 2, asn1_encode_kerberos_time);
    asn1_addfield(val->nonce, 1, asn1_encode_integer);

    {
        unsigned int length;

        asn1_insert_implicit_bitstring(val->subjectPublicKey.length,val->subjectPublicKey.data,3);
        retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0,
                                val->subjectPublicKey.length + 1 + length,
                                &length);
        if (retval)
            return retval;
        sum += length;
    }

    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_reply_key_pack(asn1buf *buf, const krb5_reply_key_pack *val,
                           unsigned int *retlen)
{
    asn1_setup();

    asn1_addfield(&(val->asChecksum), 1, asn1_encode_checksum);
    asn1_addfield(&(val->replyKey), 0, asn1_encode_encryption_key);

    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_reply_key_pack_draft9(asn1buf *buf,
                                  const krb5_reply_key_pack_draft9 *val,
                                  unsigned int *retlen)
{
    asn1_setup();

    asn1_addfield(val->nonce, 1, asn1_encode_integer);
    asn1_addfield(&(val->replyKey), 0, asn1_encode_encryption_key);

    asn1_makeseq();
    asn1_cleanup();
}

asn1_error_code
asn1_encode_pa_pk_as_rep(asn1buf *buf, const krb5_pa_pk_as_rep *val,
                         unsigned int *retlen)
{
    asn1_setup();

    switch (val->choice)
    {
    case choice_pa_pk_as_rep_dhInfo:
        asn1_addfield(&(val->u.dh_Info), choice_pa_pk_as_rep_dhInfo, asn1_encode_dh_rep_info);
        break;
    case choice_pa_pk_as_rep_encKeyPack:
        asn1_insert_implicit_octetstring(val->u.encKeyPack.length,val->u.encKeyPack.data,1);
        break;
    default:
        return ASN1_MISSING_FIELD;
    }

    asn1_cleanup();
}

asn1_error_code
asn1_encode_pa_pk_as_rep_draft9(asn1buf *buf,
                                const krb5_pa_pk_as_rep_draft9 *val,
                                unsigned int *retlen)
{
    asn1_setup();

    switch (val->choice)
    {
    case choice_pa_pk_as_rep_draft9_dhSignedData:
        asn1_insert_implicit_octetstring(val->u.dhSignedData.length,val->u.dhSignedData.data,0);
        break;
    case choice_pa_pk_as_rep_encKeyPack:
        asn1_insert_implicit_octetstring(val->u.encKeyPack.length,val->u.encKeyPack.data,1);
        break;
    default:
        return ASN1_MISSING_FIELD;
    }

    asn1_cleanup();
}

asn1_error_code
asn1_encode_td_trusted_certifiers(
    asn1buf *buf, const krb5_external_principal_identifier **val,
    unsigned int *retlen)
{
    asn1_setup();
    {
        unsigned int length;
        retval = asn1_encode_sequence_of_external_principal_identifier(buf, val, &length);
        if (retval)
            return retval;
        /* length set but ignored?  sum not updated?  */
    }
    asn1_cleanup();
}

#else /* DISABLE_PKINIT */

/* Stubs for exported pkinit encoder functions. */

krb5_error_code
encode_krb5_sp80056a_other_info(const krb5_sp80056a_other_info *rep,
                                krb5_data **code)
{
    return EINVAL;
}

krb5_error_code
encode_krb5_pkinit_supp_pub_info(const krb5_pkinit_supp_pub_info *rep,
                                 krb5_data **code)
{
    return EINVAL;
}

#endif /* not DISABLE_PKINIT */

asn1_error_code
asn1_encode_sequence_of_typed_data(asn1buf *buf, const krb5_typed_data **val,
                                   unsigned int *retlen)
{
    asn1_setup();
    int i;

    if (val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;

    for (i=0; val[i] != NULL; i++);
    for (i--; i>=0; i--) {
        unsigned int length;

        retval = asn1_encode_typed_data(buf,val[i],&length);
        if (retval) return retval;
        sum += length;
    }
    asn1_makeseq();

    asn1_cleanup();
}

asn1_error_code
asn1_encode_typed_data(asn1buf *buf, const krb5_typed_data *val,
                       unsigned int *retlen)
{
    asn1_setup();
    asn1_addlenfield(val->length, val->data, 1, asn1_encode_octetstring);
    asn1_addfield(val->type, 0, asn1_encode_integer);
    asn1_makeseq();
    asn1_cleanup();
}
