add support for KDC side of DH PKINIT

git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@13158 ec53bebd-3082-4978-b11e-865c3cabbd6b
This commit is contained in:
Love Hörnquist Åstrand
2003-11-26 00:44:54 +00:00
parent b3b050fa31
commit 8e42e1a3f4

View File

@@ -217,6 +217,215 @@ pk_free_client_param(krb5_context context, pk_client_params *client_params)
free(client_params);
}
static krb5_error_code
check_dh_params(DH *dh)
{
/* XXX check the DH parameters come from 1st or 2nd Oeakley Group */
return 0;
}
static krb5_error_code
generate_dh_keyblock(krb5_context context, pk_client_params *client_params,
krb5_keyblock *reply_key)
{
unsigned char *dh_gen_key = NULL;
krb5_keyblock key;
int dh_gen_keylen;
krb5_error_code ret;
/* XXX don't hardcode the keytype */
krb5_enctype enctype = ETYPE_DES3_CBC_SHA1;
memset(&key, 0, sizeof(key));
dh_gen_key = malloc(DH_size(client_params->dh));
if (dh_gen_key == NULL) {
krb5_set_error_string(context, "malloc: out of memory");
ret = ENOMEM;
goto out;
}
if (!DH_generate_key(client_params->dh)) {
krb5_set_error_string(context, "Can't generate Diffie-Hellman "
"keys (%s)",
ERR_error_string(ERR_get_error(), NULL));
ret = KRB5KRB_ERR_GENERIC;
goto out;
}
if (client_params->dh_public_key == NULL) {
krb5_set_error_string(context, "dh_public_key");
ret = KRB5KRB_ERR_GENERIC;
goto out;
}
dh_gen_keylen = DH_compute_key(dh_gen_key,
client_params->dh_public_key,
client_params->dh);
if (dh_gen_keylen == -1) {
krb5_set_error_string(context, "Can't compute Diffie-Hellman key (%s)",
ERR_error_string(ERR_get_error(), NULL));
ret = KRB5KRB_ERR_GENERIC;
goto out;
}
switch (enctype) {
case ETYPE_DES_CBC_CRC:
case ETYPE_DES_CBC_MD4:
case ETYPE_DES_CBC_MD5:
case ETYPE_DES3_CBC_SHA1:
case ETYPE_OLD_DES3_CBC_SHA1: {
DES_cblock *k;
ret = krb5_generate_random_keyblock(context, enctype, &key);
if (ret)
goto out;
memset(key.keyvalue.data, 0, key.keyvalue.length);
if (dh_gen_keylen < key.keyvalue.length) {
krb5_set_error_string(context, "Too small key generated by "
"Diffie-Hellman mechanism");
ret = KRB5KRB_ERR_GENERIC;
goto out;
}
memcpy(key.keyvalue.data, dh_gen_key, key.keyvalue.length);
k = key.keyvalue.data;
DES_set_odd_parity(&k[0]);
switch (enctype) {
case ETYPE_DES3_CBC_SHA1:
case ETYPE_OLD_DES3_CBC_SHA1:
DES_set_odd_parity(&k[1]);
DES_set_odd_parity(&k[2]);
break;
default:
break;
}
break;
}
default:
krb5_set_error_string(context, "PKINIT DH, unsupported enctype: %d",
(int)enctype);
ret = KDC_ERROR_KEY_TOO_WEAK;
break;
}
ret = krb5_copy_keyblock_contents(context, &key, reply_key);
out:
if (dh_gen_key)
free(dh_gen_key);
if (key.keyvalue.data)
krb5_free_keyblock_contents(context, &key);
return ret;
}
static BIGNUM *
any_to_BN(krb5_context context, const char *field, heim_any *f)
{
unsigned char *p = f->data;
ASN1_INTEGER *i;
BIGNUM *bn;
i = d2i_ASN1_INTEGER(NULL, &p, f->length);
if (i == NULL) {
krb5_set_error_string(context, "PKINIT: failed to decode %s", field);
return NULL;
}
bn = ASN1_INTEGER_to_BN(i, NULL);
ASN1_INTEGER_free(i);
if (bn == NULL) {
krb5_set_error_string(context,
"PKINIT: failed to convert %s to bignum",
field);
return NULL;
}
return bn;
}
static krb5_error_code
get_dh_param(krb5_context context, SubjectPublicKeyInfo *dh_key_info,
pk_client_params *client_params)
{
DomainParameters dhparam;
DH *dh = NULL;
krb5_error_code ret;
int dhret;
memset(&dhparam, 0, sizeof(dhparam));
if (oid_cmp(&dh_key_info->algorithm.algorithm, &heim_dhpublicnumber_oid)) {
krb5_set_error_string(context,
"PKINIT invalid oid in clientPublicValue");
return KRB5_BADMSGTYPE;
}
#if 0
if (dh_key_info->algorithm.parameters == NULL) {
krb5_set_error_string(context, "PKINIT missing algorithm parameter "
"in clientPublicValue");
return KRB5_BADMSGTYPE;
}
#endif
ret = decode_DomainParameters(dh_key_info->algorithm.parameters.data,
dh_key_info->algorithm.parameters.length,
&dhparam,
NULL);
if (ret) {
krb5_set_error_string(context, "Can't decode algorithm "
"parameters in clientPublicValue");
goto out;
}
dh = DH_new();
if (dh == NULL) {
krb5_set_error_string(context, "Cannot create DH structure (%s)",
ERR_error_string(ERR_get_error(), NULL));
ret = ENOMEM;
goto out;
}
ret = KRB5_BADMSGTYPE;
dh->p = any_to_BN(context, "DH prime", &dhparam.p);
if (dh->p == NULL)
goto out;
dh->g = any_to_BN(context, "DH base", &dhparam.g);
if (dh->g == NULL)
goto out;
dh->q = any_to_BN(context, "DH p-1 factor", &dhparam.q);
if (dh->g == NULL)
goto out;
{
heim_any glue;
glue.data = dh_key_info->subjectPublicKey.data;
glue.length = dh_key_info->subjectPublicKey.length;
client_params->dh_public_key = any_to_BN(context,
"subjectPublicKey",
&glue);
if (client_params->dh_public_key == NULL) {
krb5_clear_error_string(context);
goto out;
}
}
if (DH_check(dh, &dhret) != 1) {
krb5_set_error_string(context, "PKINIT DH data not ok: %s",
ERR_error_string(ERR_get_error(), NULL));
ret = KDC_ERROR_KEY_TOO_WEAK;
goto out;
}
client_params->dh = dh;
dh = NULL;
ret = 0;
out:
if (dh)
DH_free(dh);
free_DomainParameters(&dhparam);
return ret;
}
krb5_error_code
pk_rd_padata(krb5_context context,
KDC_REQ *req,
@@ -312,9 +521,9 @@ pk_rd_padata(krb5_context context,
client_params->nonce = ap.pkAuthenticator.nonce;
if (ap.clientPublicValue) {
ret = KRB5KRB_ERR_GENERIC;
krb5_set_error_string(context, "PKINIT DH, no support for DH");
goto out;
ret = get_dh_param(context, ap.clientPublicValue, client_params);
if (ret)
goto out;
}
/*
@@ -388,6 +597,7 @@ pk_rd_padata(krb5_context context,
static krb5_error_code
pk_mk_pa_reply_enckey(krb5_context context,
pk_client_params *client_params,
const KDC_REQ *req,
krb5_keyblock *reply_key,
ContentInfo *content_info)
{
@@ -400,9 +610,9 @@ pk_mk_pa_reply_enckey(krb5_context context,
krb5_data buf, sd_data, enc_sd_data;
krb5_keyblock tmp_key;
krb5_enctype enctype = ENCTYPE_DES3_CBC_NONE;
krb5_enctype enctype = ETYPE_DES3_CBC_NONE;
heim_oid *enc_type_oid = NULL;
X509_NAME *issuer_name;
heim_oid *enc_key_oid;
size_t size;
@@ -412,6 +622,15 @@ pk_mk_pa_reply_enckey(krb5_context context,
memset(&tmp_key, 0, sizeof(tmp_key));
memset(&ed, 0, sizeof(ed));
switch (enctype) {
case ETYPE_DES3_CBC_NONE:
enc_type_oid = &heim_des_ede3_cbc_oid;
break;
default:
krb5_set_error_string(context, "not support for enctype %d", enctype);
return KRB5_PROG_KEYTYPE_NOSUPP;
}
{
ReplyKeyPack kp;
memset(&kp, 0, sizeof(kp));
@@ -489,19 +708,20 @@ pk_mk_pa_reply_enckey(krb5_context context,
ri->rid.u.issuerAndSerialNumber.issuer.length = buf.length;
{
heim_oid *pk_enc_key_oid;
krb5_data enc_tmp_key;
ret = pk_encrypt_key(context, &tmp_key,
X509_get_pubkey(client_params->certificate->cert),
&enc_tmp_key,
&enc_key_oid);
&pk_enc_key_oid);
if (ret)
goto out;
ri->encryptedKey.length = enc_tmp_key.length;
ri->encryptedKey.data = enc_tmp_key.data;
ret = copy_oid(enc_key_oid, &ri->keyEncryptionAlgorithm.algorithm);
ret = copy_oid(pk_enc_key_oid, &ri->keyEncryptionAlgorithm.algorithm);
if (ret)
goto out;
}
@@ -518,7 +738,7 @@ pk_mk_pa_reply_enckey(krb5_context context,
krb5_clear_error_string(context);
goto out;
}
ret = copy_oid(&heim_des_ede3_cbc_oid,
ret = copy_oid(enc_type_oid,
&ed.encryptedContentInfo.contentEncryptionAlgorithm.algorithm);
if (ret) {
krb5_clear_error_string(context);
@@ -562,9 +782,93 @@ pk_mk_pa_reply_enckey(krb5_context context,
return ret;
}
/*
*
*/
static krb5_error_code
pk_mk_pa_reply_dh(krb5_context context,
DH *kdc_dh,
pk_client_params *client_params,
krb5_keyblock *reply_key,
ContentInfo *content_info)
{
ASN1_INTEGER *dh_pub_key = NULL;
KDCDHKeyInfo dh_info;
krb5_error_code ret;
SignedData sd;
krb5_data buf, sd_buf;
size_t size;
memset(&dh_info, 0, sizeof(dh_info));
memset(&sd, 0, sizeof(sd));
krb5_data_zero(&buf);
krb5_data_zero(&sd_buf);
dh_pub_key = BN_to_ASN1_INTEGER(kdc_dh->pub_key, NULL);
if (dh_pub_key == NULL) {
krb5_set_error_string(context, "BN_to_ASN1_INTEGER() failed (%s)",
ERR_error_string(ERR_get_error(), NULL));
ret = KRB5_AP_ERR_OPENSSL;
goto out;
}
OPENSSL_ASN1_MALLOC_ENCODE(ASN1_INTEGER, buf.data, buf.length, dh_pub_key,
ret);
ASN1_INTEGER_free(dh_pub_key);
if (ret) {
krb5_set_error_string(context, "Encoding of ASN1_INTEGER failed (%s)",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
dh_info.subjectPublicKey.length = buf.length * 8;
dh_info.subjectPublicKey.data = buf.data;
dh_info.nonce = client_params->nonce;
ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
ret);
if (ret) {
krb5_set_error_string(context, "ASN.1 encoding of "
"KdcDHKeyInfo failed (%d)", ret);
goto out;
}
if (buf.length != size)
krb5_abortx(context, "Internal ASN.1 encoder error");
/*
* Create the SignedData structure and sign the KdcDHKeyInfo
* filled in above
*/
ret = _krb5_pk_create_sign(context,
&heim_pkdhkeydata_oid,
&buf,
kdc_identity,
&sd_buf);
krb5_data_free(&buf);
if (ret)
goto out;
ret = _krb5_pk_mk_ContentInfo(context, &sd_buf, &pkcs7_signed_oid,
content_info);
krb5_data_free(&sd_buf);
out:
free_KDCDHKeyInfo(&dh_info);
return ret;
}
/*
*
*/
krb5_error_code
pk_mk_pa_reply(krb5_context context,
pk_client_params *client_params,
const KDC_REQ *req,
krb5_keyblock **reply_key,
METHOD_DATA *md)
{
@@ -572,6 +876,7 @@ pk_mk_pa_reply(krb5_context context,
PA_PK_AS_REP rep;
void *buf;
size_t len, size;
krb5_enctype enctype;
if (!enable_pkinit) {
krb5_clear_error_string(context);
@@ -580,19 +885,43 @@ pk_mk_pa_reply(krb5_context context,
memset(&rep, 0, sizeof(rep));
if (req->req_body.etype.len < 1) {
ret = KRB5KRB_ERR_GENERIC;
krb5_set_error_string(context,
"No valid enctype available from client");
goto out;
}
enctype = req->req_body.etype.val[0];
enctype = ETYPE_DES3_CBC_SHA1;
if (client_params->dh == NULL) {
rep.element = choice_PA_PK_AS_REP_encKeyPack;
krb5_generate_random_keyblock(context, ETYPE_DES3_CBC_SHA1,
krb5_generate_random_keyblock(context, enctype,
&client_params->reply_key);
ret = pk_mk_pa_reply_enckey(context,
client_params,
req,
&client_params->reply_key,
&rep.u.encKeyPack);
} else {
ret = KRB5KRB_ERR_GENERIC;
krb5_set_error_string(context, "DH support not compiled in");
rep.element = choice_PA_PK_AS_REP_dhSignedData;
ret = check_dh_params(client_params->dh);
if (ret)
return ret;
ret = generate_dh_keyblock(context, client_params,
&client_params->reply_key);
if (ret)
return ret;
ret = pk_mk_pa_reply_dh(context, client_params->dh,
client_params,
&client_params->reply_key,
&rep.u.dhSignedData);
}
if (ret)
goto out;