root/crypto/dist/ipsec-tools/src/racoon/algorithm.c
/*      $NetBSD: algorithm.c,v 1.12 2025/03/08 16:39:08 christos Exp $  */

/* Id: algorithm.c,v 1.15 2006/05/23 20:23:09 manubsd Exp */

/*
 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name of the project 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 PROJECT 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 THE PROJECT OR CONTRIBUTORS 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.
 */

#include "config.h"

#include <sys/param.h>
#include <sys/types.h>
#include <stdlib.h>

#include "var.h"
#include "misc.h"
#include "vmbuf.h"
#include "plog.h"
#include "debug.h"

#include "crypto_openssl.h"
#include "dhgroup.h"
#include "algorithm.h"
#include "oakley.h"
#include "isakmp_var.h"
#include "isakmp.h"
#include "ipsec_doi.h"
#include "gcmalloc.h"

static struct hash_algorithm oakley_hashdef[] = {
{ "md5",        algtype_md5,            OAKLEY_ATTR_HASH_ALG_MD5,
                eay_md5_init,           eay_md5_update,
                eay_md5_final,          eay_md5_hashlen,
                eay_md5_one, },
{ "sha1",       algtype_sha1,           OAKLEY_ATTR_HASH_ALG_SHA,
                eay_sha1_init,          eay_sha1_update,
                eay_sha1_final,         eay_sha1_hashlen,
                eay_sha1_one, },
#ifdef WITH_SHA2
{ "sha2_256",   algtype_sha2_256,       OAKLEY_ATTR_HASH_ALG_SHA2_256,
                eay_sha2_256_init,      eay_sha2_256_update,
                eay_sha2_256_final,     eay_sha2_256_hashlen,
                eay_sha2_256_one, },
{ "sha2_384",   algtype_sha2_384,       OAKLEY_ATTR_HASH_ALG_SHA2_384,
                eay_sha2_384_init,      eay_sha2_384_update,
                eay_sha2_384_final,     eay_sha2_384_hashlen,
                eay_sha2_384_one, },
{ "sha2_512",   algtype_sha2_512,       OAKLEY_ATTR_HASH_ALG_SHA2_512,
                eay_sha2_512_init,      eay_sha2_512_update,
                eay_sha2_512_final,     eay_sha2_512_hashlen,
                eay_sha2_512_one, },
#endif
};

static struct hmac_algorithm oakley_hmacdef[] = {
{ "hmac_md5",   algtype_md5,            OAKLEY_ATTR_HASH_ALG_MD5,
                eay_hmacmd5_init,       eay_hmacmd5_update,
                eay_hmacmd5_final,      NULL,
                eay_hmacmd5_one, },
{ "hmac_sha1",  algtype_sha1,           OAKLEY_ATTR_HASH_ALG_SHA,
                eay_hmacsha1_init,      eay_hmacsha1_update,
                eay_hmacsha1_final,     NULL,
                eay_hmacsha1_one, },
#ifdef WITH_SHA2
{ "hmac_sha2_256",      algtype_sha2_256,       OAKLEY_ATTR_HASH_ALG_SHA2_256,
                eay_hmacsha2_256_init,  eay_hmacsha2_256_update,
                eay_hmacsha2_256_final, NULL,
                eay_hmacsha2_256_one, },
{ "hmac_sha2_384",      algtype_sha2_384,       OAKLEY_ATTR_HASH_ALG_SHA2_384,
                eay_hmacsha2_384_init,  eay_hmacsha2_384_update,
                eay_hmacsha2_384_final, NULL,
                eay_hmacsha2_384_one, },
{ "hmac_sha2_512",      algtype_sha2_512,       OAKLEY_ATTR_HASH_ALG_SHA2_512,
                eay_hmacsha2_512_init,  eay_hmacsha2_512_update,
                eay_hmacsha2_512_final, NULL,
                eay_hmacsha2_512_one, },
#endif
};

static struct enc_algorithm oakley_encdef[] = {
{ "des",        algtype_des,            OAKLEY_ATTR_ENC_ALG_DES,        8,
                eay_des_encrypt,        eay_des_decrypt,
                eay_des_weakkey,        eay_des_keylen, },
#ifdef HAVE_OPENSSL_IDEA_H
{ "idea",       algtype_idea,           OAKLEY_ATTR_ENC_ALG_IDEA,       8,
                eay_idea_encrypt,       eay_idea_decrypt,
                eay_idea_weakkey,       eay_idea_keylen, },
#endif
{ "blowfish",   algtype_blowfish,       OAKLEY_ATTR_ENC_ALG_BLOWFISH,   8,
                eay_bf_encrypt,         eay_bf_decrypt,
                eay_bf_weakkey,         eay_bf_keylen, },
#ifdef HAVE_OPENSSL_RC5_H
{ "rc5",        algtype_rc5,            OAKLEY_ATTR_ENC_ALG_RC5,        8,
                eay_rc5_encrypt,        eay_rc5_decrypt,
                eay_rc5_weakkey,        eay_rc5_keylen, },
#endif
{ "3des",       algtype_3des,           OAKLEY_ATTR_ENC_ALG_3DES,       8,
                eay_3des_encrypt,       eay_3des_decrypt,
                eay_3des_weakkey,       eay_3des_keylen, },
{ "cast",       algtype_cast128,        OAKLEY_ATTR_ENC_ALG_CAST,       8,
                eay_cast_encrypt,       eay_cast_decrypt,
                eay_cast_weakkey,       eay_cast_keylen, },
{ "aes",        algtype_aes,    OAKLEY_ATTR_ENC_ALG_AES,        16,
                eay_aes_encrypt,        eay_aes_decrypt,
                eay_aes_weakkey,        eay_aes_keylen, },
#ifdef HAVE_OPENSSL_CAMELLIA_H
{ "camellia",   algtype_camellia,       OAKLEY_ATTR_ENC_ALG_CAMELLIA,   16,
                eay_camellia_encrypt,   eay_camellia_decrypt,
                eay_camellia_weakkey,   eay_camellia_keylen, },
#endif
};

static struct enc_algorithm ipsec_encdef[] = {
{ "des-iv64",   algtype_des_iv64,       IPSECDOI_ESP_DES_IV64,          8,
                NULL,                   NULL,
                NULL,                   eay_des_keylen, },
{ "des",        algtype_des,            IPSECDOI_ESP_DES,               8,
                NULL,                   NULL,
                NULL,                   eay_des_keylen, },
{ "3des",       algtype_3des,           IPSECDOI_ESP_3DES,              8,
                NULL,                   NULL,
                NULL,                   eay_3des_keylen, },
#ifdef HAVE_OPENSSL_RC5_H
{ "rc5",        algtype_rc5,            IPSECDOI_ESP_RC5,               8,
                NULL,                   NULL,
                NULL,                   eay_rc5_keylen, },
#endif
{ "cast",       algtype_cast128,        IPSECDOI_ESP_CAST,              8,
                NULL,                   NULL,
                NULL,                   eay_cast_keylen, },
{ "blowfish",   algtype_blowfish,       IPSECDOI_ESP_BLOWFISH,          8,
                NULL,                   NULL,
                NULL,                   eay_bf_keylen, },
{ "des-iv32",   algtype_des_iv32,       IPSECDOI_ESP_DES_IV32,          8,
                NULL,                   NULL,
                NULL,                   eay_des_keylen, },
{ "null",       algtype_null_enc,       IPSECDOI_ESP_NULL,              8,
                NULL,                   NULL,
                NULL,                   eay_null_keylen, },
{ "aes",        algtype_aes,            IPSECDOI_ESP_AES,               16,
                NULL,                   NULL,
                NULL,                   eay_aes_keylen, },
{ "aes_gcm_16", algtype_aesgcm16,               IPSECDOI_ESP_AESGCM16,          16,
                NULL,                   NULL,
                NULL,                   eay_aesgcm_keylen, },
{ "twofish",    algtype_twofish,        IPSECDOI_ESP_TWOFISH,           16,
                NULL,                   NULL,
                NULL,                   eay_twofish_keylen, },
#ifdef HAVE_OPENSSL_IDEA_H
{ "3idea",      algtype_3idea,          IPSECDOI_ESP_3IDEA,             8,
                NULL,                   NULL,
                NULL,                   NULL, },
{ "idea",       algtype_idea,           IPSECDOI_ESP_IDEA,              8,
                NULL,                   NULL,
                NULL,                   NULL, },
#endif
{ "rc4",        algtype_rc4,            IPSECDOI_ESP_RC4,               8,
                NULL,                   NULL,
                NULL,                   NULL, },
#ifdef HAVE_OPENSSL_CAMELLIA_H
{ "camellia",   algtype_camellia,       IPSECDOI_ESP_CAMELLIA,          16,
                NULL,                   NULL,
                NULL,                   eay_camellia_keylen, },
#endif
};

static struct hmac_algorithm ipsec_hmacdef[] = {
{ "md5",        algtype_hmac_md5,       IPSECDOI_ATTR_AUTH_HMAC_MD5,
                NULL,                   NULL,
                NULL,                   eay_md5_hashlen,
                NULL, },
{ "sha1",       algtype_hmac_sha1,      IPSECDOI_ATTR_AUTH_HMAC_SHA1,
                NULL,                   NULL,
                NULL,                   eay_sha1_hashlen,
                NULL, },
{ "kpdk",       algtype_kpdk,           IPSECDOI_ATTR_AUTH_KPDK,
                NULL,                   NULL,
                NULL,                   eay_kpdk_hashlen,
                NULL, },
{ "null",       algtype_non_auth,       IPSECDOI_ATTR_AUTH_NONE,
                NULL,                   NULL,
                NULL,                   eay_null_hashlen,
                NULL, },
#ifdef WITH_SHA2
{ "hmac_sha2_256",      algtype_hmac_sha2_256,IPSECDOI_ATTR_AUTH_HMAC_SHA2_256,
                NULL,                   NULL,
                NULL,                   eay_sha2_256_hashlen,
                NULL, },
{ "hmac_sha2_384",      algtype_hmac_sha2_384,IPSECDOI_ATTR_AUTH_HMAC_SHA2_384,
                NULL,                   NULL,
                NULL,                   eay_sha2_384_hashlen,
                NULL, },
{ "hmac_sha2_512",      algtype_hmac_sha2_512,IPSECDOI_ATTR_AUTH_HMAC_SHA2_512,
                NULL,                   NULL,
                NULL,                   eay_sha2_512_hashlen,
                NULL, },
#endif
};

static struct misc_algorithm ipsec_compdef[] = {
{ "oui",        algtype_oui,            IPSECDOI_IPCOMP_OUI, },
{ "deflate",    algtype_deflate,        IPSECDOI_IPCOMP_DEFLATE, },
{ "lzs",        algtype_lzs,            IPSECDOI_IPCOMP_LZS, },
};

/*
 * In case of asymmetric modes (hybrid xauth), what's racoon mode of
 * operations ; it seems that the proposal should always use the
 * initiator half (unless a server initiates a connection, which is
 * not handled, and probably not useful).
 */
static struct misc_algorithm oakley_authdef[] = {
{ "pre_shared_key",     algtype_psk,    OAKLEY_ATTR_AUTH_METHOD_PSKEY, },
{ "dsssig",             algtype_dsssig, OAKLEY_ATTR_AUTH_METHOD_DSSSIG, },
{ "rsasig",             algtype_rsasig, OAKLEY_ATTR_AUTH_METHOD_RSASIG, },
{ "rsaenc",             algtype_rsaenc, OAKLEY_ATTR_AUTH_METHOD_RSAENC, },
{ "rsarev",             algtype_rsarev, OAKLEY_ATTR_AUTH_METHOD_RSAREV, },

{ "gssapi_krb",         algtype_gssapikrb,
    OAKLEY_ATTR_AUTH_METHOD_GSSAPI_KRB, },

#ifdef ENABLE_HYBRID
{ "hybrid_rsa_server",  algtype_hybrid_rsa_s,   
    OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R, },

{ "hybrid_dss_server",  algtype_hybrid_dss_s,   
    OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R, },

{ "xauth_psk_server",   algtype_xauth_psk_s,    
    OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R, },

{ "xauth_rsa_server",   algtype_xauth_rsa_s,    
    OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R, },

{ "hybrid_rsa_client",  algtype_hybrid_rsa_c,   
    OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I, },

{ "hybrid_dss_client",  algtype_hybrid_dss_c,   
    OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I, },

{ "xauth_psk_client",   algtype_xauth_psk_c,    
    OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_I, },

{ "xauth_rsa_client",   algtype_xauth_rsa_c,    
    OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_I, },
#endif
};

static struct dh_algorithm oakley_dhdef[] = {
{ "modp768",    algtype_modp768,        OAKLEY_ATTR_GRP_DESC_MODP768,
                &dh_modp768, },
{ "modp1024",   algtype_modp1024,       OAKLEY_ATTR_GRP_DESC_MODP1024,
                &dh_modp1024, },
{ "modp1536",   algtype_modp1536,       OAKLEY_ATTR_GRP_DESC_MODP1536,
                &dh_modp1536, },
{ "modp2048",   algtype_modp2048,       OAKLEY_ATTR_GRP_DESC_MODP2048,
                &dh_modp2048, },
{ "modp3072",   algtype_modp3072,       OAKLEY_ATTR_GRP_DESC_MODP3072,
                &dh_modp3072, },
{ "modp4096",   algtype_modp4096,       OAKLEY_ATTR_GRP_DESC_MODP4096,
                &dh_modp4096, },
{ "modp6144",   algtype_modp6144,       OAKLEY_ATTR_GRP_DESC_MODP6144,
                &dh_modp6144, },
{ "modp8192",   algtype_modp8192,       OAKLEY_ATTR_GRP_DESC_MODP8192,
                &dh_modp8192, },
};

static struct hash_algorithm *alg_oakley_hashdef(int);
static struct hmac_algorithm *alg_oakley_hmacdef(int);
static struct enc_algorithm *alg_oakley_encdef(int);
static struct enc_algorithm *alg_ipsec_encdef(int);
static struct hmac_algorithm *alg_ipsec_hmacdef(int);
static struct dh_algorithm *alg_oakley_dhdef(int);

/* oakley hash algorithm */
static struct hash_algorithm *
alg_oakley_hashdef(int doi)
{

        for (size_t i = 0; i < ARRAYLEN(oakley_hashdef); i++)
                if (doi == oakley_hashdef[i].doi) {
                        plog(LLV_DEBUG, LOCATION, NULL, "hash(%s)\n",
                                oakley_hashdef[i].name);
                        return &oakley_hashdef[i];
                }
        return NULL;
}

int
alg_oakley_hashdef_ok(int doi)
{
        struct hash_algorithm *f;

        f = alg_oakley_hashdef(doi);
        if (f == NULL)
                return 0;

        return 1;
}

int
alg_oakley_hashdef_doi(int type)
{
        int res = -1;

        for (size_t i = 0; i < ARRAYLEN(oakley_hashdef); i++)
                if (type == oakley_hashdef[i].type) {
                        res = oakley_hashdef[i].doi;
                        break;
                }
        return res;
}

int
alg_oakley_hashdef_hashlen(int doi)
{
        struct hash_algorithm *f;

        f = alg_oakley_hashdef(doi);
        if (f == NULL || f->hashlen == NULL)
                return 0;

        return (f->hashlen)();
}

const char *
alg_oakley_hashdef_name(int doi)
{
        struct hash_algorithm *f;

        f = alg_oakley_hashdef(doi);
        if (f == NULL)
                return "*UNKNOWN*";

        return f->name;
}

vchar_t *
alg_oakley_hashdef_one(int doi, vchar_t *buf)
{
        struct hash_algorithm *f;

        f = alg_oakley_hashdef(doi);
        if (f == NULL || f->hashlen == NULL)
                return NULL;

        return (f->one)(buf);
}

/* oakley hmac algorithm */
static struct hmac_algorithm *
alg_oakley_hmacdef(int doi)
{

        for (size_t i = 0; i < ARRAYLEN(oakley_hmacdef); i++)
                if (doi == oakley_hmacdef[i].doi) {
                        plog(LLV_DEBUG, LOCATION, NULL, "hmac(%s)\n",
                                oakley_hmacdef[i].name);
                        return &oakley_hmacdef[i];
                }
        return NULL;
}

int
alg_oakley_hmacdef_doi(int type)
{
        int res = -1;

        for (size_t i = 0; i < ARRAYLEN(oakley_hmacdef); i++)
                if (type == oakley_hmacdef[i].type) {
                        res = oakley_hmacdef[i].doi;
                        break;
                }
        return res;
}

vchar_t *
alg_oakley_hmacdef_one(int doi, vchar_t *key, vchar_t *buf)
{
        struct hmac_algorithm *f;
        vchar_t *res;
#ifdef ENABLE_STATS
        struct timeval start, end;
#endif

        f = alg_oakley_hmacdef(doi);
        if (f == NULL || f->one == NULL)
                return NULL;

#ifdef ENABLE_STATS
        gettimeofday(&start, NULL);
#endif

        res = (f->one)(key, buf);

#ifdef ENABLE_STATS
        gettimeofday(&end, NULL);
        syslog(LOG_NOTICE, "%s(%s size=%zu): %8.6f", __func__,
                f->name, buf->l, timedelta(&start, &end));
#endif

        return res;
}

/* oakley encryption algorithm */
static struct enc_algorithm *
alg_oakley_encdef(int doi)
{

        for (size_t i = 0; i < ARRAYLEN(oakley_encdef); i++)
                if (doi == oakley_encdef[i].doi) {
                        plog(LLV_DEBUG, LOCATION, NULL, "encryption(%s)\n",
                                oakley_encdef[i].name);
                        return &oakley_encdef[i];
                }
        return NULL;
}

int
alg_oakley_encdef_ok(int doi)
{
        struct enc_algorithm *f;

        f = alg_oakley_encdef(doi);
        if (f == NULL)
                return 0;

        return 1;
}

int
alg_oakley_encdef_doi(int type)
{
        int res = -1;

        for (size_t i = 0; i < ARRAYLEN(oakley_encdef); i++)
                if (type == oakley_encdef[i].type) {
                        res = oakley_encdef[i].doi;
                        break;
                }
        return res;
}

int
alg_oakley_encdef_keylen(int doi, int len)
{
        struct enc_algorithm *f;

        f = alg_oakley_encdef(doi);
        if (f == NULL || f->keylen == NULL)
                return -1;

        return (f->keylen)(len);
}

int
alg_oakley_encdef_blocklen(int doi)
{
        struct enc_algorithm *f;

        f = alg_oakley_encdef(doi);
        if (f == NULL)
                return -1;

        return f->blocklen;
}

const char *
alg_oakley_encdef_name(int doi)
{
        struct enc_algorithm *f;

        f = alg_oakley_encdef(doi);
        if (f == NULL)
                return "*UNKNOWN*";

        return f->name;
}

vchar_t *
alg_oakley_encdef_decrypt(int doi, vchar_t *buf, vchar_t *key, vchar_t *iv)
{
        vchar_t *res;
        struct enc_algorithm *f;
#ifdef ENABLE_STATS
        struct timeval start, end;
#endif

        f = alg_oakley_encdef(doi);
        if (f == NULL || f->decrypt == NULL)
                return NULL;

#ifdef ENABLE_STATS
        gettimeofday(&start, NULL);
#endif

        res = (f->decrypt)(buf, key, iv);

#ifdef ENABLE_STATS
        gettimeofday(&end, NULL);
        syslog(LOG_NOTICE, "%s(%s klen=%zu size=%zu): %8.6f", __func__,
                f->name, key->l << 3, buf->l, timedelta(&start, &end));
#endif
        return res;
}

vchar_t *
alg_oakley_encdef_encrypt(int doi, vchar_t *buf, vchar_t *key, vchar_t *iv)
{
        vchar_t *res;
        struct enc_algorithm *f;
#ifdef ENABLE_STATS
        struct timeval start, end;
#endif

        f = alg_oakley_encdef(doi);
        if (f == NULL || f->encrypt == NULL)
                return NULL;

#ifdef ENABLE_STATS
        gettimeofday(&start, NULL);
#endif

        res = (f->encrypt)(buf, key, iv);

#ifdef ENABLE_STATS
        gettimeofday(&end, NULL);
        syslog(LOG_NOTICE, "%s(%s klen=%zu size=%zu): %8.6f", __func__,
                f->name, key->l << 3, buf->l, timedelta(&start, &end));
#endif
        return res;
}

/* ipsec encryption algorithm */
static struct enc_algorithm *
alg_ipsec_encdef(int doi)
{

        for (size_t i = 0; i < ARRAYLEN(ipsec_encdef); i++)
                if (doi == ipsec_encdef[i].doi) {
                        plog(LLV_DEBUG, LOCATION, NULL, "encryption(%s)\n",
                                ipsec_encdef[i].name);
                        return &ipsec_encdef[i];
                }
        return NULL;
}

int
alg_ipsec_encdef_doi(int type)
{
        int res = -1;

        for (size_t i = 0; i < ARRAYLEN(ipsec_encdef); i++)
                if (type == ipsec_encdef[i].type) {
                        res = ipsec_encdef[i].doi;
                        break;
                }
        return res;
}

int
alg_ipsec_encdef_keylen(int doi, int len)
{
        struct enc_algorithm *f;

        f = alg_ipsec_encdef(doi);
        if (f == NULL || f->keylen == NULL)
                return -1;

        return (f->keylen)(len);
}

/* ipsec hmac algorithm */
static struct hmac_algorithm *
alg_ipsec_hmacdef(int doi)
{

        for (size_t i = 0; i < ARRAYLEN(ipsec_hmacdef); i++)
                if (doi == ipsec_hmacdef[i].doi) {
                        plog(LLV_DEBUG, LOCATION, NULL, "hmac(%s)\n",
                                ipsec_hmacdef[i].name);
                        return &ipsec_hmacdef[i];
                }
        return NULL;
}

int
alg_ipsec_hmacdef_doi(int type)
{
        int res = -1;

        for (size_t i = 0; i < ARRAYLEN(ipsec_hmacdef); i++)
                if (type == ipsec_hmacdef[i].type) {
                        res = ipsec_hmacdef[i].doi;
                        break;
                }
        return res;
}

int
alg_ipsec_hmacdef_hashlen(int doi)
{
        struct hmac_algorithm *f;

        f = alg_ipsec_hmacdef(doi);
        if (f == NULL || f->hashlen == NULL)
                return -1;

        return (f->hashlen)();
}

/* ip compression */
int
alg_ipsec_compdef_doi(int type)
{
        int res = -1;

        for (size_t i = 0; i < ARRAYLEN(ipsec_compdef); i++)
                if (type == ipsec_compdef[i].type) {
                        res = ipsec_compdef[i].doi;
                        break;
                }
        return res;
}

/* dh algorithm */
static struct dh_algorithm *
alg_oakley_dhdef(int doi)
{

        for (size_t i = 0; i < ARRAYLEN(oakley_dhdef); i++)
                if (doi == oakley_dhdef[i].doi) {
                        plog(LLV_DEBUG, LOCATION, NULL, "hmac(%s)\n",
                                oakley_dhdef[i].name);
                        return &oakley_dhdef[i];
                }
        return NULL;
}

int
alg_oakley_dhdef_ok(int doi)
{
        struct dh_algorithm *f;

        f = alg_oakley_dhdef(doi);
        if (f == NULL)
                return 0;

        return 1;
}

int
alg_oakley_dhdef_doi(int type)
{
        int res = -1;

        for (size_t i = 0; i < ARRAYLEN(oakley_dhdef); i++)
                if (type == oakley_dhdef[i].type) {
                        res = oakley_dhdef[i].doi;
                        break;
                }
        return res;
}

struct dhgroup *
alg_oakley_dhdef_group(int doi)
{
        struct dh_algorithm *f;

        f = alg_oakley_dhdef(doi);
        if (f == NULL || f->dhgroup == NULL)
                return NULL;

        return f->dhgroup;
}

const char *
alg_oakley_dhdef_name(int doi)
{
        struct dh_algorithm *f;
        
        f = alg_oakley_dhdef(doi);
        if (f == NULL)
                return "*UNKNOWN*";
        return f->name;
}

/* authentication method */
int
alg_oakley_authdef_doi(int type)
{
        int res = -1;

        for (size_t i = 0; i < ARRAYLEN(oakley_authdef); i++)
                if (type == oakley_authdef[i].type) {
                        res = oakley_authdef[i].doi;
                        break;
                }
        return res;
}

const char *
alg_oakley_authdef_name(int doi)
{

        for (size_t i = 0; i < ARRAYLEN(oakley_authdef); i++)
                if (doi == oakley_authdef[i].doi) {
                        return oakley_authdef[i].name;
                }
        return "*UNKNOWN*";
}

/*
 * give the default key length
 * OUT: -1:             NG
 *      0:              fixed key cipher, key length not allowed
 *      positive:       default key length
 */
int
default_keylen(int class, int type)
{

        switch (class) {
        case algclass_isakmp_enc:
        case algclass_ipsec_enc:
                break;
        default:
                return 0;
        }

        switch (type) {
        case algtype_blowfish:
        case algtype_rc5:
        case algtype_cast128:
        case algtype_aes:
        case algtype_aesgcm16:
        case algtype_twofish:
        case algtype_camellia:
                return 128;
        default:
                return 0;
        }
}

/*
 * check key length
 * OUT: -1:     NG
 *      0:      OK
 */
int
check_keylen(int class, int type, int len)
{
        int badrange;

        switch (class) {
        case algclass_isakmp_enc:
        case algclass_ipsec_enc:
                break;
        default:
                /* unknown class, punt */
                plog(LLV_ERROR, LOCATION, NULL,
                        "unknown algclass %d\n", class);
                return -1;
        }

        /* key length must be multiple of 8 bytes - RFC2451 2.2 */
        switch (type) {
        case algtype_blowfish:
        case algtype_rc5:
        case algtype_cast128:
        case algtype_aes:
        case algtype_aesgcm16:
        case algtype_twofish:
        case algtype_camellia:
                if (len % 8 != 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "key length %d is not multiple of 8\n", len);
                        return -1;
                }
                break;
        }

        /* key length range */
        badrange = 0;
        switch (type) {
        case algtype_blowfish:
                if (len < 40 || 448 < len)
                        badrange++;
                break;
        case algtype_rc5:
                if (len < 40 || 2040 < len)
                        badrange++;
                break;
        case algtype_cast128:
                if (len < 40 || 128 < len)
                        badrange++;
                break;
        case algtype_aes:
                if (!(len == 128 || len == 192 || len == 256))
                        badrange++;
                break;
        case algtype_aesgcm16:
                if (!(len == 128 || len == 192 || len == 256))
                        badrange++;
                break;
        case algtype_twofish:
                if (len < 40 || 256 < len)
                        badrange++;
                break;
        case algtype_camellia:
                if (!(len == 128 || len == 192 || len == 256))
                        badrange++;
                break;
        default:
                if (len) {
                        plog(LLV_ERROR, LOCATION, NULL,
                                "key length is not allowed");
                        return -1;
                }
                break;
        }
        if (badrange) {
                plog(LLV_ERROR, LOCATION, NULL,
                        "key length out of range\n");
                return -1;
        }

        return 0;
}

/*
 * convert algorithm type to DOI value.
 * OUT  -1   : NG
 *      other: converted.
 */
int
algtype2doi(int class, int type)
{
        int res = -1;

        switch (class) {
        case algclass_ipsec_enc:
                res = alg_ipsec_encdef_doi(type);
                break;
        case algclass_ipsec_auth:
                res = alg_ipsec_hmacdef_doi(type);
                break;
        case algclass_ipsec_comp:
                res = alg_ipsec_compdef_doi(type);
                break;
        case algclass_isakmp_enc:
                res =  alg_oakley_encdef_doi(type);
                break;
        case algclass_isakmp_hash:
                res = alg_oakley_hashdef_doi(type);
                break;
        case algclass_isakmp_dh:
                res = alg_oakley_dhdef_doi(type);
                break;
        case algclass_isakmp_ameth:
                res = alg_oakley_authdef_doi(type);
                break;
        }
        return res;
}

/*
 * convert algorithm class to DOI value.
 * OUT  -1   : NG
 *      other: converted.
 */
int
algclass2doi(int class)
{
        switch (class) {
        case algclass_ipsec_enc:
                return IPSECDOI_PROTO_IPSEC_ESP;
        case algclass_ipsec_auth:
                return IPSECDOI_ATTR_AUTH;
        case algclass_ipsec_comp:
                return IPSECDOI_PROTO_IPCOMP;
        case algclass_isakmp_enc:
                return OAKLEY_ATTR_ENC_ALG;
        case algclass_isakmp_hash:
                return OAKLEY_ATTR_HASH_ALG;
        case algclass_isakmp_dh:
                return OAKLEY_ATTR_GRP_DESC;
        case algclass_isakmp_ameth:
                return OAKLEY_ATTR_AUTH_METHOD;
        default:
                return -1;
        }
        /*NOTREACHED*/
        return -1;
}