/* This value is shared with KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */
/* case KRB5KDC_ERR_KEY_TOO_WEAK: */
case KRB5KDC_ERR_DISCARD:
+ /* pkinit alg-agility */
+ case KRB5KDC_ERR_NO_ACCEPTABLE_KDF:
return retval;
default:
return KRB5KDC_ERR_PREAUTH_FAILED;
error_code KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED, "Checksum must be included"
error_code KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED, "Digest in signed-data not accepted"
error_code KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED, "Public key encryption not supported"
-error_code KRB5PLACEHOLD_82, "KRB5 error code 82"
+error_code KRB5KDC_ERR_NO_ACCEPTABLE_KDF, "No acceptable KDF offered"
error_code KRB5PLACEHOLD_83, "KRB5 error code 83"
error_code KRB5PLACEHOLD_84, "KRB5 error code 84"
error_code KRB5KRB_AP_ERR_IAKERB_KDC_NOT_FOUND, "The IAKERB proxy could not find a KDC"
unsigned int client_key_len = 0;
krb5_checksum cksum = {0, 0, 0, NULL};
krb5_data k5data;
+ krb5_octet_data secret;
int valid_san = 0;
int valid_eku = 0;
int need_eku_checking = 1;
goto cleanup;
}
- retval = pkinit_octetstring2key(context, etype, client_key,
+ /* If we have a KDF algorithm ID, call the algorithm agility KDF... */
+ if (kdc_reply->u.dh_Info.kdfID) {
+ secret.length = client_key_len;
+ secret.data = client_key;
+
+ retval = pkinit_alg_agility_kdf(context, &secret,
+ kdc_reply->u.dh_Info.kdfID,
+ request->client,
+ request->server, etype,
+ (krb5_octet_data *)encoded_request,
+ (krb5_octet_data *)as_rep,
+ key_block);
+
+ if (retval) {
+ pkiDebug("failed to create key pkinit_alg_agility_kdf %s\n",
+ error_message(retval));
+ goto cleanup;
+ }
+
+ /* ...otherwise, use the older octetstring2key function. */
+ } else {
+
+ retval = pkinit_octetstring2key(context, etype, client_key,
client_key_len, key_block);
- if (retval) {
- pkiDebug("failed to create key pkinit_octetstring2key %s\n",
- error_message(retval));
- goto cleanup;
+ if (retval) {
+ pkiDebug("failed to create key pkinit_octetstring2key %s\n",
+ error_message(retval));
+ goto cleanup;
+ }
}
break;
extern const size_t krb5_pkinit_sha256_oid_len;
extern const krb5_octet krb5_pkinit_sha512_oid[];
extern const size_t krb5_pkinit_sha512_oid_len;
+/**
+ * An ordered set of OIDs, stored as krb5_octet_data of KDF algorithms
+ * supported by this implementation. The order of this array controls
+ * the order in which the server will pick.
+ */
+extern const krb5_octet_data const *supported_kdf_alg_ids[] ;
+extern const krb5_octet_data const sha1_id;
+extern const krb5_octet_data const sha256_id;
+extern const krb5_octet_data const sha512_id;
#endif /* _PKINIT_CRYPTO_H */
}
memset (key_block->contents, 0, key_block->length);
+ /* If this is anonymous pkinit, use the anonymous principle for party_u_info */
+ if (party_u_info && krb5_principal_compare_any_realm(context, party_u_info,
+ krb5_anonymous_principal()))
+ party_u_info = krb5_anonymous_principal();
+
if (0 != (retval = pkinit_alg_values(context, alg_oid, &hash_len, &EVP_func)))
goto cleanup;
#include "pkinit_crypto.h"
/* statically declare OID constants for all three algorithms */
-const krb5_octet krb5_pkinit_sha1_oid[10] =
+const krb5_octet krb5_pkinit_sha1_oid[8] =
{0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x01};
const size_t krb5_pkinit_sha1_oid_len = 8;
-const krb5_octet krb5_pkinit_sha256_oid[10] =
+const krb5_octet krb5_pkinit_sha256_oid[8] =
{0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x02};
const size_t krb5_pkinit_sha256_oid_len = 8;
-const krb5_octet krb5_pkinit_sha512_oid [10] =
+const krb5_octet krb5_pkinit_sha512_oid [8] =
{0x2B,0x06,0x01,0x05,0x02,0x03,0x06,0x03};
const size_t krb5_pkinit_sha512_oid_len = 8;
+
+#define oid_as_data(var, oid_base) \
+ const krb5_octet_data var = \
+ {0, sizeof oid_base, (krb5_octet *) oid_base}
+oid_as_data(sha1_id, krb5_pkinit_sha1_oid);
+oid_as_data(sha256_id, krb5_pkinit_sha256_oid);
+oid_as_data(sha512_id, krb5_pkinit_sha512_oid);
+#undef oid_as_data
+
+const krb5_octet_data const *supported_kdf_alg_ids[] = {
+ &sha256_id,
+ &sha1_id,
+ &sha512_id,
+ NULL
+};
#include <errno.h>
#include <string.h>
+#include <k5-int.h>
#include "pkinit.h"
/* Remove when FAST PKINIT is settled. */
return ret;
}
+static krb5_error_code
+pkinit_pick_kdf_alg(krb5_context context,
+ krb5_octet_data **kdf_list,
+ krb5_octet_data **alg_oid)
+{
+ krb5_error_code retval = 0;
+ krb5_octet_data *req_oid = NULL;
+ const krb5_octet_data *supp_oid = NULL;
+ krb5_octet_data *tmp_oid = NULL;
+ int i, j = 0;
+
+ *alg_oid = NULL;
+
+ /* for each of the OIDs in the client's request... */
+ for (i = 0; NULL != (req_oid = kdf_list[i]); i++) {
+ /* if the requested OID is supported, use it. */
+ for (j = 0; NULL != (supp_oid = supported_kdf_alg_ids[j]); j++) {
+ if ((req_oid->length == supp_oid->length) &&
+ (0 == memcmp(req_oid->data, supp_oid->data, req_oid->length))) {
+ tmp_oid = k5alloc(sizeof(krb5_octet_data), &retval);
+ if (retval)
+ goto cleanup;
+ tmp_oid->data = k5alloc(supp_oid->length, &retval);
+ if (retval)
+ goto cleanup;
+ tmp_oid->length = supp_oid->length;
+ memcpy(tmp_oid->data, supp_oid->data, supp_oid->length);
+ *alg_oid = tmp_oid;
+ tmp_oid = NULL;
+ goto cleanup;
+ }
+ }
+ retval = KRB5KDC_ERR_NO_ACCEPTABLE_KDF;
+ }
+cleanup:
+ if (tmp_oid)
+ krb5_free_octet_data(context, tmp_oid);
+ return retval;
+}
+
static krb5_error_code
pkinit_server_return_padata(krb5_context context,
krb5_pa_data * padata,
krb5_pa_pk_as_rep *rep = NULL;
krb5_pa_pk_as_rep_draft9 *rep9 = NULL;
krb5_data *out_data = NULL;
+ krb5_octet_data secret;
krb5_enctype enctype = -1;
#endif
}
+ if ((rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo) &&
+ ((reqctx->rcv_auth_pack != NULL &&
+ reqctx->rcv_auth_pack->supportedKDFs != NULL))) {
+
+ /* If using the alg-agility KDF, put the algorithm in the reply
+ * before encoding it.
+ */
+ if (reqctx->rcv_auth_pack != NULL &&
+ reqctx->rcv_auth_pack->supportedKDFs != NULL) {
+ retval = pkinit_pick_kdf_alg(context, reqctx->rcv_auth_pack->supportedKDFs,
+ &(rep->u.dh_Info.kdfID));
+ if (retval) {
+ pkiDebug("pkinit_pick_kdf_alg failed: %s\n",
+ error_message(retval));
+ goto cleanup;
+ }
+ }
+ }
+
switch ((int)padata->pa_type) {
case KRB5_PADATA_PK_AS_REQ:
retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data);
if ((rep9 != NULL &&
rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) ||
(rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) {
- retval = pkinit_octetstring2key(context, enctype, server_key,
- server_key_len, encrypting_key);
- if (retval) {
- pkiDebug("pkinit_octetstring2key failed: %s\n",
- error_message(retval));
- goto cleanup;
+
+ /* If supported KDFs are specified, use the alg agility KDF */
+ if ((reqctx->rcv_auth_pack != NULL &&
+ reqctx->rcv_auth_pack->supportedKDFs != NULL)) {
+
+ secret.data = server_key;
+ secret.length = server_key_len;
+
+ retval = pkinit_alg_agility_kdf(context, &secret,
+ rep->u.dh_Info.kdfID,
+ request->client, request->server,
+ enctype,
+ (krb5_octet_data *)req_pkt,
+ (krb5_octet_data *)out_data,
+ encrypting_key);
+ if (retval) {
+ pkiDebug("pkinit_alg_agility_kdf failed: %s\n",
+ error_message(retval));
+ goto cleanup;
+ }
+
+ /* Otherwise, use the older octetstring2key() function */
+ } else {
+ retval = pkinit_octetstring2key(context, enctype, server_key,
+ server_key_len, encrypting_key);
+ if (retval) {
+ pkiDebug("pkinit_octetstring2key failed: %s\n",
+ error_message(retval));
+ goto cleanup;
+ }
}
}
(*send_pa)->length = out_data->length;
(*send_pa)->contents = (krb5_octet *) out_data->data;
-
cleanup:
pkinit_fini_kdc_req_context(context, reqctx);
free(scratch.data);