Path: blob/main/crypto/krb5/src/plugins/preauth/spake/util.c
34890 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* plugins/preauth/spake/util.c - Utility functions for SPAKE preauth module */2/*3* Copyright (C) 2015 by the Massachusetts Institute of Technology.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9*10* * Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in15* the documentation and/or other materials provided with the16* distribution.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS19* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT20* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS21* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE22* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,23* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES24* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR25* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,27* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)28* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED29* OF THE POSSIBILITY OF SUCH DAMAGE.30*/3132#include "k5-int.h"33#include "trace.h"34#include "util.h"35#include "groups.h"3637/* Use data to construct a single-element pa-data list of type38* KRB5_PADATA_SPAKE. Claim data's memory on success or failure. */39krb5_error_code40convert_to_padata(krb5_data *data, krb5_pa_data ***pa_out)41{42krb5_pa_data *pa = NULL, **list = NULL;4344list = calloc(2, sizeof(*list));45if (list == NULL)46goto fail;47pa = calloc(1, sizeof(*pa));48if (pa == NULL)49goto fail;50pa->magic = KV5M_PA_DATA;51pa->pa_type = KRB5_PADATA_SPAKE;52pa->length = data->length;53pa->contents = (uint8_t *)data->data;54list[0] = pa;55list[1] = NULL;56*pa_out = list;57free(data);58return 0;5960fail:61free(list);62free(pa);63free(data->data);64free(data);65return ENOMEM;66}6768/*69* Update the transcript hash thash with its current value and the70* concatenation of data1 and data2, using the hash function for group. Either71* data1 or data2 may be NULL to omit it. Allocate thash if it is empty.72*/73krb5_error_code74update_thash(krb5_context context, groupstate *gstate, int32_t group,75krb5_data *thash, const krb5_data *data1, const krb5_data *data2)76{77krb5_error_code ret;78size_t hashlen;79krb5_data dlist[3];80const krb5_data empty = empty_data();8182if (thash->length == 0) {83/* Initialize the transcript hash to all zeros. */84ret = group_hash_len(group, &hashlen);85if (ret)86return ret;87ret = alloc_data(thash, hashlen);88if (ret)89return ret;90}9192/* Set up the data array and hash it with the group's hash function. */93dlist[0] = *thash;94dlist[1] = (data1 != NULL) ? *data1 : empty;95dlist[2] = (data2 != NULL) ? *data2 : empty;96return group_hash(context, gstate, group, dlist, 3,97(uint8_t *)thash->data);98}99100/* Derive a byte vector for the SPAKE w multiplier input from ikey. Place101* result in allocated storage in *wbytes_out. */102krb5_error_code103derive_wbytes(krb5_context context, int32_t group, const krb5_keyblock *ikey,104krb5_data *wbytes_out)105{106krb5_error_code ret;107const char prefix[] = "SPAKEsecret";108size_t mult_len, prefix_len = sizeof(prefix) - 1;109krb5_data prf_input = empty_data(), wbytes = empty_data();110111*wbytes_out = empty_data();112113/* Allocate space for a multiplier. */114ret = group_mult_len(group, &mult_len);115if (ret)116goto cleanup;117ret = alloc_data(&wbytes, mult_len);118if (ret)119goto cleanup;120121/* Compose the PRF input string. */122ret = alloc_data(&prf_input, prefix_len + 4);123if (ret)124goto cleanup;125memcpy(prf_input.data, prefix, prefix_len);126store_32_be(group, prf_input.data + prefix_len);127128/* Derive the SPAKE input from the initial reply key with PRF+. */129ret = krb5_c_prfplus(context, ikey, &prf_input, &wbytes);130if (ret)131goto cleanup;132133*wbytes_out = wbytes;134wbytes = empty_data();135136cleanup:137free(prf_input.data);138zapfree(wbytes.data, wbytes.length);139return ret;140}141142/*143* Derive K'[n] from the group number, the initial key enctype, the initial144* multiplier, the SPAKE result, the transcript hash, and the encoded145* KDC-REQ-BODY. Place the result in allocated storage in *out.146*/147krb5_error_code148derive_key(krb5_context context, groupstate *gstate, int32_t group,149const krb5_keyblock *ikey, const krb5_data *wbytes,150const krb5_data *spakeresult, const krb5_data *thash,151const krb5_data *der_req, uint32_t n, krb5_keyblock **out)152{153krb5_error_code ret;154krb5_data dlist[9], seed = empty_data(), d;155uint8_t groupnbuf[4], etypenbuf[4], nbuf[4], bcount;156size_t hashlen, seedlen, keylen, nblocks, i;157size_t ndata = sizeof(dlist) / sizeof(*dlist);158krb5_keyblock *hkey = NULL;159160*out = NULL;161162store_32_be(group, groupnbuf);163store_32_be(n, nbuf);164store_32_be(ikey->enctype, etypenbuf);165dlist[0] = string2data("SPAKEkey");166dlist[1] = make_data(groupnbuf, sizeof(groupnbuf));167dlist[2] = make_data(etypenbuf, sizeof(etypenbuf));168dlist[3] = *wbytes;169dlist[4] = *spakeresult;170dlist[5] = *thash;171dlist[6] = *der_req;172dlist[7] = make_data(nbuf, sizeof(nbuf));173dlist[8] = make_data(&bcount, 1);174175/* Count the number of hash blocks required (should be 1 for all current176* scenarios) and allocate space. */177ret = group_hash_len(group, &hashlen);178if (ret)179goto cleanup;180ret = krb5_c_keylengths(context, ikey->enctype, &seedlen, &keylen);181if (ret)182goto cleanup;183nblocks = (seedlen + hashlen - 1) / hashlen;184ret = alloc_data(&seed, nblocks * hashlen);185if (ret)186goto cleanup;187188/* Compute and concatenate hash blocks to fill the seed buffer. */189for (i = 0; i < nblocks; i++) {190bcount = i + 1;191ret = group_hash(context, gstate, group, dlist, ndata,192(uint8_t *)seed.data + i * hashlen);193if (ret)194goto cleanup;195}196197ret = krb5_init_keyblock(context, ikey->enctype, keylen, &hkey);198if (ret)199goto cleanup;200d = make_data(seed.data, seedlen);201ret = krb5_c_random_to_key(context, ikey->enctype, &d, hkey);202if (ret)203goto cleanup;204205ret = krb5_c_fx_cf2_simple(context, ikey, "SPAKE", hkey, "keyderiv", out);206207cleanup:208zapfree(seed.data, seed.length);209krb5_free_keyblock(context, hkey);210return ret;211}212213214