/*
 * Definitions likely to be helpful to multiple AES implementations.
 */

/*
 * The 'extra' structure used by AES implementations is used to
 * include information about how to check if a given implementation is
 * available at run time, and whether we've already checked.
 */
struct aes_extra_mutable;
struct aes_extra {
    /* Function to check availability. Might be expensive, so we don't
     * want to call it more than once. */
    bool (*check_available)(void);

    /* Point to a writable substructure. */
    struct aes_extra_mutable *mut;

    /* Extra API function specific to AES, to encrypt a single block
     * in ECB mode without touching the IV. Used by AES-GCM MAC
     * setup. */
    void (*encrypt_ecb_block)(ssh_cipher *, void *);
};
struct aes_extra_mutable {
    bool checked_availability;
    bool is_available;
};
static inline bool check_availability(const struct aes_extra *extra)
{
    if (!extra->mut->checked_availability) {
        extra->mut->is_available = extra->check_available();
        extra->mut->checked_availability = true;
    }

    return extra->mut->is_available;
}

/* Shared stub function for all the AES-GCM vtables. */
void aesgcm_cipher_crypt_length(
    ssh_cipher *cipher, void *blk, int len, unsigned long seq);

/* External entry point for the encrypt_ecb_block function. */
static inline void aes_encrypt_ecb_block(ssh_cipher *ciph, void *blk)
{
    const struct aes_extra *extra = ciph->vt->extra;
    extra->encrypt_ecb_block(ciph, blk);
}

/*
 * Macros to define vtables for AES variants. There are a lot of
 * these, because of the cross product between cipher modes, key
 * sizes, and assorted HW/SW implementations, so it's worth spending
 * some effort here to reduce the boilerplate in the sub-files.
 */

#define AES_EXTRA_BITS(impl_c, bits)                                    \
    static struct aes_extra_mutable aes ## impl_c ## _extra_mut;        \
    static const struct aes_extra aes ## bits ## impl_c ## _extra = {   \
        /* WINSCP */ \
        /*.check_available =*/ aes ## impl_c ## _available,                 \
        /*.mut =*/ &aes ## impl_c ## _extra_mut,                            \
        /*.encrypt_ecb_block =*/ &aes ## bits ## impl_c ## _encrypt_ecb_block, \
    }

#define AES_EXTRA(impl_c)                       \
    AES_EXTRA_BITS(impl_c, 128);                \
    AES_EXTRA_BITS(impl_c, 192);                \
    AES_EXTRA_BITS(impl_c, 256)

// WINSCP string constants are for avoiding 
// Warning 1060: Different alignments specified for same segment, %s. Using highest alignment.rdata
// in objconv

#define AES_CBC_VTABLE(impl_c, impl_display, bits)                      \
    const char ssh_aes ## bits ## _cbc ## impl_c ## ssh2_id[] = "aes" #bits "-cbc"; /*WINSCP*/ \
    const char ssh_aes ## bits ## _cbc ## impl_c ## text_name[] = "AES-" #bits " CBC (" impl_display ")"; /*WINSCP*/ \
    const ssh_cipheralg ssh_aes ## bits ## _cbc ## impl_c = {           \
        /*WINSCP*/ \
        /*.new =*/ aes ## impl_c ## _new,                                   \
        /*.free =*/ aes ## impl_c ## _free,                                 \
        /*.setiv =*/ aes ## impl_c ## _setiv_cbc,                           \
        /*.setkey =*/ aes ## impl_c ## _setkey,                             \
        /*.encrypt =*/ aes ## bits ## impl_c ## _cbc_encrypt,               \
        /*.decrypt =*/ aes ## bits ## impl_c ## _cbc_decrypt,               \
        NULL, \
        NULL, \
        /*.next_message =*/ nullcipher_next_message,                        \
        /*.ssh2_id =*/ ssh_aes ## bits ## _cbc ## impl_c ## ssh2_id, /*WINSCP*/ \
        /*.blksize =*/ 16,                                                  \
        /*.real_keybits =*/ bits,                                           \
        /*.padded_keybytes =*/ bits/8,                                      \
        /*.flags =*/ SSH_CIPHER_IS_CBC,                                     \
        /*.text_name =*/ ssh_aes ## bits ## _cbc ## impl_c ## text_name, /*WINSCP*/ \
        NULL, \
        /*.extra =*/ &aes ## bits ## impl_c ## _extra,                              \
    }

#define AES_SDCTR_VTABLE(impl_c, impl_display, bits)                    \
    const char ssh_aes ## bits ## _sdctr ## impl_c ## ssh2_id[] = "aes" #bits "-ctr"; /*WINSCP*/ \
    const char ssh_aes ## bits ## _sdctr ## impl_c ## text_name[] = "AES-" #bits " SDCTR (" impl_display ")"; /*WINSCP*/ \
    const ssh_cipheralg ssh_aes ## bits ## _sdctr ## impl_c = {         \
        /*WINSCP*/ \
        /*.new =*/ aes ## impl_c ## _new,                                   \
        /*.free =*/ aes ## impl_c ## _free,                                 \
        /*.setiv =*/ aes ## impl_c ## _setiv_sdctr,                         \
        /*.setkey =*/ aes ## impl_c ## _setkey,                             \
        /*.encrypt =*/ aes ## bits ## impl_c ## _sdctr,                     \
        /*.decrypt =*/ aes ## bits ## impl_c ## _sdctr,                     \
        NULL, \
        NULL, \
        /*.next_message =*/ nullcipher_next_message,                        \
        /*.ssh2_id =*/ ssh_aes ## bits ## _sdctr ## impl_c ## ssh2_id, /*WINSCP*/ \
        /*.blksize =*/ 16,                                                  \
        /*.real_keybits =*/ bits,                                           \
        /*.padded_keybytes =*/ bits/8,                                      \
        /*.flags =*/ 0,                                                     \
        /*.text_name =*/ ssh_aes ## bits ## _sdctr ## impl_c ## text_name, /*WINSCP*/ \
        NULL, \
        /*.extra =*/ &aes ## bits ## impl_c ## _extra,                      \
    }

#define AES_GCM_VTABLE(impl_c, impl_display, bits)                      \
    const char ssh_aes ## bits ## _gcm ## impl_c ## ssh2_id[] = "aes" #bits "-gcm@openssh.com"; /*WINSCP*/ \
    const char ssh_aes ## bits ## _gcm ## impl_c ## text_name[] = "AES-" #bits " GCM (" impl_display ")"; /*WINSCP*/ \
    const ssh_cipheralg ssh_aes ## bits ## _gcm ## impl_c = {           \
        /*.new =*/ aes ## impl_c ## _new,                                   \
        /*.free =*/ aes ## impl_c ## _free,                                 \
        /*.setiv =*/ aes ## impl_c ## _setiv_gcm,                           \
        /*.setkey =*/ aes ## impl_c ## _setkey,                             \
        /*.encrypt =*/ aes ## bits ## impl_c ## _gcm,                       \
        /*.decrypt =*/ aes ## bits ## impl_c ## _gcm,                       \
        /*.encrypt_length =*/ aesgcm_cipher_crypt_length,                   \
        /*.decrypt_length =*/ aesgcm_cipher_crypt_length,                   \
        /*.next_message =*/ aes ## impl_c ## _next_message_gcm,             \
        /* 192-bit AES-GCM is included only so that testcrypt can run   \
         * standard test vectors against it. OpenSSH doesn't define a   \
         * protocol id for it. So we set its ssh2_id to NULL. */        \
        /*.ssh2_id =*/ bits==192 ? NULL : ssh_aes ## bits ## _gcm ## impl_c ## ssh2_id,   \
        /*.blksize =*/ 16,                                                  \
        /*.real_keybits =*/ bits,                                           \
        /*.padded_keybytes =*/ bits/8,                                      \
        /*.flags =*/ SSH_CIPHER_SEPARATE_LENGTH,                            \
        /*.text_name =*/ ssh_aes ## bits ## _gcm ## impl_c ## text_name,            \
        /*.required_mac =*/ &ssh2_aesgcm_mac,                               \
        /*.extra =*/ &aes ## bits ## impl_c ## _extra,                      \
    }

#define AES_ALL_VTABLES(impl_c, impl_display)           \
    AES_CBC_VTABLE(impl_c, impl_display, 128);          \
    AES_CBC_VTABLE(impl_c, impl_display, 192);          \
    AES_CBC_VTABLE(impl_c, impl_display, 256);          \
    AES_SDCTR_VTABLE(impl_c, impl_display, 128);        \
    AES_SDCTR_VTABLE(impl_c, impl_display, 192);        \
    AES_SDCTR_VTABLE(impl_c, impl_display, 256);        \
    AES_GCM_VTABLE(impl_c, impl_display, 128);          \
    AES_GCM_VTABLE(impl_c, impl_display, 192);          \
    AES_GCM_VTABLE(impl_c, impl_display, 256)

/*
 * Macros to repeat a piece of code particular numbers of times that
 * correspond to 1 fewer than the number of AES rounds. (Because the
 * last round is different.)
 */
#define REP2(x) x x
#define REP4(x) REP2(REP2(x))
#define REP8(x) REP2(REP4(x))
#define REP9(x) REP8(x) x
#define REP11(x) REP8(x) REP2(x) x
#define REP13(x) REP8(x) REP4(x) x

/*
 * The round constants used in key schedule expansion.
 */
extern const uint8_t aes_key_setup_round_constants[10];

/*
 * The largest number of round keys ever needed.
 */
#define MAXROUNDKEYS 15
