From 12a7a9e4887b20cd3baeaa0596d1a3ff05cc0d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Love=20H=C3=B6rnquist=20=C3=85strand?= Date: Sat, 7 Feb 2009 04:06:43 +0000 Subject: [PATCH] Prepare for ECDH. git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@24638 ec53bebd-3082-4978-b11e-865c3cabbd6b --- lib/krb5/pkinit.c | 227 ++++++++++++++++++++++++++++------------------ 1 file changed, 141 insertions(+), 86 deletions(-) diff --git a/lib/krb5/pkinit.c b/lib/krb5/pkinit.c index 4dfb8abbc..39bdc4cd6 100644 --- a/lib/krb5/pkinit.c +++ b/lib/krb5/pkinit.c @@ -60,7 +60,13 @@ struct krb5_pk_cert { struct krb5_pk_init_ctx_data { struct krb5_pk_identity *id; - DH *dh; + enum { USE_RSA, USE_DH, USE_ECDH } keyex; + union { + DH *dh; +#ifdef HAVE_OPENSSL + EC_KEY *eckey; +#endif + } u; krb5_data *clientDHNonce; struct krb5_dh_moduli **m; hx509_peer_info peer; @@ -325,7 +331,6 @@ static krb5_error_code build_auth_pack(krb5_context context, unsigned nonce, krb5_pk_init_ctx ctx, - DH *dh, const KDC_REQ_BODY *body, AuthPack *a) { @@ -374,12 +379,12 @@ build_auth_pack(krb5_context context, if (ret) return ret; - if (dh) { - DomainParameters dp; - heim_integer dh_pub_key; + if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) { krb5_data dhbuf; size_t size; + krb5_data_zero(&dhbuf); + if (1 /* support_cached_dh */) { ALLOC(a->clientDHNonce, 1); if (a->clientDHNonce == NULL) { @@ -401,60 +406,89 @@ build_auth_pack(krb5_context context, ALLOC(a->clientPublicValue, 1); if (a->clientPublicValue == NULL) return ENOMEM; - ret = der_copy_oid(oid_id_dhpublicnumber(), - &a->clientPublicValue->algorithm.algorithm); - if (ret) - return ret; - - memset(&dp, 0, sizeof(dp)); - ret = BN_to_integer(context, dh->p, &dp.p); - if (ret) { + if (ctx->keyex == USE_DH) { + DH *dh = ctx->u.dh; + DomainParameters dp; + heim_integer dh_pub_key; + + ret = der_copy_oid(oid_id_dhpublicnumber(), + &a->clientPublicValue->algorithm.algorithm); + if (ret) + return ret; + + memset(&dp, 0, sizeof(dp)); + + ret = BN_to_integer(context, dh->p, &dp.p); + if (ret) { + free_DomainParameters(&dp); + return ret; + } + ret = BN_to_integer(context, dh->g, &dp.g); + if (ret) { + free_DomainParameters(&dp); + return ret; + } + ret = BN_to_integer(context, dh->q, &dp.q); + if (ret) { + free_DomainParameters(&dp); + return ret; + } + dp.j = NULL; + dp.validationParms = NULL; + + a->clientPublicValue->algorithm.parameters = + malloc(sizeof(*a->clientPublicValue->algorithm.parameters)); + if (a->clientPublicValue->algorithm.parameters == NULL) { + free_DomainParameters(&dp); + return ret; + } + + ASN1_MALLOC_ENCODE(DomainParameters, + a->clientPublicValue->algorithm.parameters->data, + a->clientPublicValue->algorithm.parameters->length, + &dp, &size, ret); free_DomainParameters(&dp); - return ret; - } - ret = BN_to_integer(context, dh->g, &dp.g); - if (ret) { - free_DomainParameters(&dp); - return ret; - } - ret = BN_to_integer(context, dh->q, &dp.q); - if (ret) { - free_DomainParameters(&dp); - return ret; - } - dp.j = NULL; - dp.validationParms = NULL; + if (ret) + return ret; + if (size != a->clientPublicValue->algorithm.parameters->length) + krb5_abortx(context, "Internal ASN1 encoder error"); + + ret = BN_to_integer(context, dh->pub_key, &dh_pub_key); + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length, + &dh_pub_key, &size, ret); + der_free_heim_integer(&dh_pub_key); + if (ret) + return ret; + if (size != dhbuf.length) + krb5_abortx(context, "asn1 internal error"); + } else if (ctx->keyex == USE_ECDH) { +#ifdef HAVE_OPENSSL + ret = der_copy_oid(oid_id_ecPublicKey(), + &a->clientPublicValue->algorithm.algorithm); + if (ret) + return ret; - a->clientPublicValue->algorithm.parameters = - malloc(sizeof(*a->clientPublicValue->algorithm.parameters)); - if (a->clientPublicValue->algorithm.parameters == NULL) { - free_DomainParameters(&dp); - return ret; - } + ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (ctx->u.eckey == NULL) + return ENOMEM; - ASN1_MALLOC_ENCODE(DomainParameters, - a->clientPublicValue->algorithm.parameters->data, - a->clientPublicValue->algorithm.parameters->length, - &dp, &size, ret); - free_DomainParameters(&dp); - if (ret) - return ret; - if (size != a->clientPublicValue->algorithm.parameters->length) - krb5_abortx(context, "Internal ASN1 encoder error"); + ret = EC_KEY_generate_key(ctx->u.eckey); + if (ret != 1) + return EINVAL; - ret = BN_to_integer(context, dh->pub_key, &dh_pub_key); - if (ret) - return ret; - - ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length, - &dh_pub_key, &size, ret); - der_free_heim_integer(&dh_pub_key); - if (ret) - return ret; - if (size != dhbuf.length) - krb5_abortx(context, "asn1 internal error"); + /* XXX stuff domain and public key into dhbuf */ + dhbuf.data = NULL; + dhbuf.length = 0; +#else + return EINVAL; +#endif + } else + krb5_abortx(context, "internal error"); a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8; a->clientPublicValue->subjectPublicKey.data = dhbuf.data; } @@ -558,7 +592,7 @@ pk_mk_padata(krb5_context context, memset(&ap, 0, sizeof(ap)); - ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap); + ret = build_auth_pack(context, nonce, ctx, req_body, &ap); if (ret) { free_AuthPack(&ap); goto out; @@ -1228,7 +1262,7 @@ pk_rd_pa_reply_dh(krb5_context context, p = kdc_dh_info.subjectPublicKey.data; size = (kdc_dh_info.subjectPublicKey.length + 7) / 8; - { + if (ctx->keyex == USE_DH) { DHPublicKey k; ret = decode_DHPublicKey(p, size, &k, NULL); if (ret) { @@ -1244,30 +1278,38 @@ pk_rd_pa_reply_dh(krb5_context context, ret = ENOMEM; goto out; } - } - dh_gen_keylen = DH_size(ctx->dh); - size = BN_num_bytes(ctx->dh->p); - if (size < dh_gen_keylen) - size = dh_gen_keylen; - dh_gen_key = malloc(size); - if (dh_gen_key == NULL) { - ret = ENOMEM; - krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); + dh_gen_keylen = DH_size(ctx->u.dh); + size = BN_num_bytes(ctx->u.dh->p); + if (size < dh_gen_keylen) + size = dh_gen_keylen; + + dh_gen_key = malloc(size); + if (dh_gen_key == NULL) { + ret = ENOMEM; + krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); + goto out; + } + memset(dh_gen_key, 0, size - dh_gen_keylen); + + dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen), + kdc_dh_pubkey, ctx->u.dh); + if (dh_gen_keylen == -1) { + ret = KRB5KRB_ERR_GENERIC; + krb5_set_error_message(context, ret, + N_("PKINIT: Can't compute Diffie-Hellman key", "")); + goto out; + } + } else { +#ifdef HAVE_OPENSSL + krb5_abortx(context, "implement ECDH"); +#else + ret = EINVAL; +#endif goto out; } - memset(dh_gen_key, 0, size - dh_gen_keylen); - - dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen), - kdc_dh_pubkey, ctx->dh); - if (dh_gen_keylen == -1) { - ret = KRB5KRB_ERR_GENERIC; - krb5_set_error_message(context, ret, - N_("PKINIT: Can't compute Diffie-Hellman key", "")); - goto out; - } - + *key = malloc (sizeof (**key)); if (*key == NULL) { ret = ENOMEM; @@ -1293,7 +1335,7 @@ pk_rd_pa_reply_dh(krb5_context context, if (kdc_dh_pubkey) BN_free(kdc_dh_pubkey); if (dh_gen_key) { - memset(dh_gen_key, 0, DH_size(ctx->dh)); + memset(dh_gen_key, 0, DH_size(ctx->u.dh)); free(dh_gen_key); } if (host) @@ -2047,9 +2089,18 @@ _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt) if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL) return; ctx = opt->opt_private->pk_init_ctx; - if (ctx->dh) - DH_free(ctx->dh); - ctx->dh = NULL; + switch (ctx->keyex) { + case USE_DH: + DH_free(ctx->u.dh); + break; + case USE_RSA: + break; + case USE_ECDH: +#ifdef HAVE_OPENSSL + EC_KEY_free(ctx->u.eckey); +#endif + break; + } if (ctx->id) { hx509_verify_destroy_ctx(ctx->id->verify_ctx); hx509_certs_free(&ctx->id->certs); @@ -2101,9 +2152,6 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context, N_("malloc: out of memory", "")); return ENOMEM; } - opt->opt_private->pk_init_ctx->dh = NULL; - opt->opt_private->pk_init_ctx->id = NULL; - opt->opt_private->pk_init_ctx->clientDHNonce = NULL; opt->opt_private->pk_init_ctx->require_binding = 0; opt->opt_private->pk_init_ctx->require_eku = 1; opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1; @@ -2145,10 +2193,15 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context, return ret; } + /* XXX select cert here so we know if we will use ECDH or DH */ + if ((flags & 2) == 0) { const char *moduli_file; unsigned long dh_min_bits; + opt->opt_private->pk_init_ctx->keyex = USE_DH; + + moduli_file = krb5_config_get_string(context, NULL, "libdefaults", "moduli", @@ -2167,15 +2220,15 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context, return ret; } - opt->opt_private->pk_init_ctx->dh = DH_new(); - if (opt->opt_private->pk_init_ctx->dh == NULL) { + opt->opt_private->pk_init_ctx->u.dh = DH_new(); + if (opt->opt_private->pk_init_ctx->u.dh == NULL) { _krb5_get_init_creds_opt_free_pkinit(opt); krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } - ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh, + ret = select_dh_group(context, opt->opt_private->pk_init_ctx->u.dh, dh_min_bits, opt->opt_private->pk_init_ctx->m); if (ret) { @@ -2183,12 +2236,14 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context, return ret; } - if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) { + if (DH_generate_key(opt->opt_private->pk_init_ctx->u.dh) != 1) { _krb5_get_init_creds_opt_free_pkinit(opt); krb5_set_error_message(context, ENOMEM, N_("pkinit: failed to generate DH key", "")); return ENOMEM; } + } else { + opt->opt_private->pk_init_ctx->keyex = USE_RSA; } return 0;