xref: /freebsd/crypto/krb5/src/plugins/preauth/spake/groups.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* plugins/preauth/spake/groups.c - SPAKE group interfaces */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert  * Copyright (C) 2015 by the Massachusetts Institute of Technology.
5*7f2fe78bSCy Schubert  * All rights reserved.
6*7f2fe78bSCy Schubert  *
7*7f2fe78bSCy Schubert  * Redistribution and use in source and binary forms, with or without
8*7f2fe78bSCy Schubert  * modification, are permitted provided that the following conditions
9*7f2fe78bSCy Schubert  * are met:
10*7f2fe78bSCy Schubert  *
11*7f2fe78bSCy Schubert  * * Redistributions of source code must retain the above copyright
12*7f2fe78bSCy Schubert  *   notice, this list of conditions and the following disclaimer.
13*7f2fe78bSCy Schubert  *
14*7f2fe78bSCy Schubert  * * Redistributions in binary form must reproduce the above copyright
15*7f2fe78bSCy Schubert  *   notice, this list of conditions and the following disclaimer in
16*7f2fe78bSCy Schubert  *   the documentation and/or other materials provided with the
17*7f2fe78bSCy Schubert  *   distribution.
18*7f2fe78bSCy Schubert  *
19*7f2fe78bSCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20*7f2fe78bSCy Schubert  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21*7f2fe78bSCy Schubert  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22*7f2fe78bSCy Schubert  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23*7f2fe78bSCy Schubert  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24*7f2fe78bSCy Schubert  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25*7f2fe78bSCy Schubert  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26*7f2fe78bSCy Schubert  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*7f2fe78bSCy Schubert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28*7f2fe78bSCy Schubert  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29*7f2fe78bSCy Schubert  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30*7f2fe78bSCy Schubert  * OF THE POSSIBILITY OF SUCH DAMAGE.
31*7f2fe78bSCy Schubert  */
32*7f2fe78bSCy Schubert 
33*7f2fe78bSCy Schubert /*
34*7f2fe78bSCy Schubert  * The SPAKE2 algorithm works as follows:
35*7f2fe78bSCy Schubert  *
36*7f2fe78bSCy Schubert  * 1. The parties agree on a group, a base element G, and constant elements M
37*7f2fe78bSCy Schubert  *    and N.  In this mechanism, these parameters are determined by the
38*7f2fe78bSCy Schubert  *    registered group number.
39*7f2fe78bSCy Schubert  * 2. Both parties derive a scalar value w from the initial key.
40*7f2fe78bSCy Schubert  * 3. The first party (the KDC, in this mechanism) chooses a random secret
41*7f2fe78bSCy Schubert  *    scalar x and sends T=xG+wM.
42*7f2fe78bSCy Schubert  * 4. The second party (the client, in this mechanism) chooses a random
43*7f2fe78bSCy Schubert  *    secret scalar y and sends S=yG+wN.
44*7f2fe78bSCy Schubert  * 5. The first party computes K=x(S-wN).
45*7f2fe78bSCy Schubert  * 6. The second party computes the same value as K=y(T-wM).
46*7f2fe78bSCy Schubert  * 7. Both parties derive a key from a random oracle whose input incorporates
47*7f2fe78bSCy Schubert  *    the party identities, w, T, S, and K.
48*7f2fe78bSCy Schubert  *
49*7f2fe78bSCy Schubert  * We implement the algorithm using a vtable for each group, where the primary
50*7f2fe78bSCy Schubert  * vtable methods are "keygen" (corresponding to step 3 or 4) and "result"
51*7f2fe78bSCy Schubert  * (corresponding to step 5 or 6).  We use the term "private scalar" to refer
52*7f2fe78bSCy Schubert  * to x or y, and "public element" to refer to S or T.
53*7f2fe78bSCy Schubert  */
54*7f2fe78bSCy Schubert 
55*7f2fe78bSCy Schubert #include "iana.h"
56*7f2fe78bSCy Schubert #include "trace.h"
57*7f2fe78bSCy Schubert #include "groups.h"
58*7f2fe78bSCy Schubert 
59*7f2fe78bSCy Schubert #define DEFAULT_GROUPS_CLIENT "edwards25519"
60*7f2fe78bSCy Schubert #define DEFAULT_GROUPS_KDC ""
61*7f2fe78bSCy Schubert 
62*7f2fe78bSCy Schubert typedef struct groupent_st {
63*7f2fe78bSCy Schubert     const groupdef *gdef;
64*7f2fe78bSCy Schubert     groupdata *gdata;
65*7f2fe78bSCy Schubert } groupent;
66*7f2fe78bSCy Schubert 
67*7f2fe78bSCy Schubert struct groupstate_st {
68*7f2fe78bSCy Schubert     krb5_boolean is_kdc;
69*7f2fe78bSCy Schubert 
70*7f2fe78bSCy Schubert     /* Permitted and groups, from configuration */
71*7f2fe78bSCy Schubert     int32_t *permitted;
72*7f2fe78bSCy Schubert     size_t npermitted;
73*7f2fe78bSCy Schubert 
74*7f2fe78bSCy Schubert     /* Optimistic challenge group, from configuration */
75*7f2fe78bSCy Schubert     int32_t challenge_group;
76*7f2fe78bSCy Schubert 
77*7f2fe78bSCy Schubert     /* Lazily-initialized list of gdata objects. */
78*7f2fe78bSCy Schubert     groupent *data;
79*7f2fe78bSCy Schubert     size_t ndata;
80*7f2fe78bSCy Schubert };
81*7f2fe78bSCy Schubert 
82*7f2fe78bSCy Schubert extern groupdef builtin_edwards25519;
83*7f2fe78bSCy Schubert #ifdef SPAKE_OPENSSL
84*7f2fe78bSCy Schubert extern groupdef ossl_P256;
85*7f2fe78bSCy Schubert extern groupdef ossl_P384;
86*7f2fe78bSCy Schubert extern groupdef ossl_P521;
87*7f2fe78bSCy Schubert #endif
88*7f2fe78bSCy Schubert 
89*7f2fe78bSCy Schubert static const groupdef *groupdefs[] = {
90*7f2fe78bSCy Schubert     &builtin_edwards25519,
91*7f2fe78bSCy Schubert #ifdef SPAKE_OPENSSL
92*7f2fe78bSCy Schubert     &ossl_P256,
93*7f2fe78bSCy Schubert     &ossl_P384,
94*7f2fe78bSCy Schubert     &ossl_P521,
95*7f2fe78bSCy Schubert #endif
96*7f2fe78bSCy Schubert     NULL
97*7f2fe78bSCy Schubert };
98*7f2fe78bSCy Schubert 
99*7f2fe78bSCy Schubert /* Find a groupdef structure by group number.  Return NULL on failure. */
100*7f2fe78bSCy Schubert static const groupdef *
find_gdef(int32_t group)101*7f2fe78bSCy Schubert find_gdef(int32_t group)
102*7f2fe78bSCy Schubert {
103*7f2fe78bSCy Schubert     size_t i;
104*7f2fe78bSCy Schubert 
105*7f2fe78bSCy Schubert     for (i = 0; groupdefs[i] != NULL; i++) {
106*7f2fe78bSCy Schubert         if (groupdefs[i]->reg->id == group)
107*7f2fe78bSCy Schubert             return groupdefs[i];
108*7f2fe78bSCy Schubert     }
109*7f2fe78bSCy Schubert 
110*7f2fe78bSCy Schubert     return NULL;
111*7f2fe78bSCy Schubert }
112*7f2fe78bSCy Schubert 
113*7f2fe78bSCy Schubert /* Find a group number by name.  Return 0 on failure. */
114*7f2fe78bSCy Schubert static int32_t
find_gnum(const char * name)115*7f2fe78bSCy Schubert find_gnum(const char *name)
116*7f2fe78bSCy Schubert {
117*7f2fe78bSCy Schubert     size_t i;
118*7f2fe78bSCy Schubert 
119*7f2fe78bSCy Schubert     for (i = 0; groupdefs[i] != NULL; i++) {
120*7f2fe78bSCy Schubert         if (strcasecmp(name, groupdefs[i]->reg->name) == 0)
121*7f2fe78bSCy Schubert             return groupdefs[i]->reg->id;
122*7f2fe78bSCy Schubert     }
123*7f2fe78bSCy Schubert     return 0;
124*7f2fe78bSCy Schubert }
125*7f2fe78bSCy Schubert 
126*7f2fe78bSCy Schubert static krb5_boolean
in_grouplist(const int32_t * list,size_t count,int32_t group)127*7f2fe78bSCy Schubert in_grouplist(const int32_t *list, size_t count, int32_t group)
128*7f2fe78bSCy Schubert {
129*7f2fe78bSCy Schubert     size_t i;
130*7f2fe78bSCy Schubert 
131*7f2fe78bSCy Schubert     for (i = 0; i < count; i++) {
132*7f2fe78bSCy Schubert         if (list[i] == group)
133*7f2fe78bSCy Schubert             return TRUE;
134*7f2fe78bSCy Schubert     }
135*7f2fe78bSCy Schubert 
136*7f2fe78bSCy Schubert     return FALSE;
137*7f2fe78bSCy Schubert }
138*7f2fe78bSCy Schubert 
139*7f2fe78bSCy Schubert /* Retrieve a group data object for group within gstate, lazily initializing it
140*7f2fe78bSCy Schubert  * if necessary. */
141*7f2fe78bSCy Schubert static krb5_error_code
get_gdata(krb5_context context,groupstate * gstate,const groupdef * gdef,groupdata ** gdata_out)142*7f2fe78bSCy Schubert get_gdata(krb5_context context, groupstate *gstate, const groupdef *gdef,
143*7f2fe78bSCy Schubert           groupdata **gdata_out)
144*7f2fe78bSCy Schubert {
145*7f2fe78bSCy Schubert     krb5_error_code ret;
146*7f2fe78bSCy Schubert     groupent *ent, *newptr;
147*7f2fe78bSCy Schubert 
148*7f2fe78bSCy Schubert     *gdata_out = NULL;
149*7f2fe78bSCy Schubert 
150*7f2fe78bSCy Schubert     /* Look for an existing entry. */
151*7f2fe78bSCy Schubert     for (ent = gstate->data; ent < gstate->data + gstate->ndata; ent++) {
152*7f2fe78bSCy Schubert         if (ent->gdef == gdef) {
153*7f2fe78bSCy Schubert             *gdata_out = ent->gdata;
154*7f2fe78bSCy Schubert             return 0;
155*7f2fe78bSCy Schubert         }
156*7f2fe78bSCy Schubert     }
157*7f2fe78bSCy Schubert 
158*7f2fe78bSCy Schubert     /* Make a new entry. */
159*7f2fe78bSCy Schubert     newptr = realloc(gstate->data, (gstate->ndata + 1) * sizeof(groupent));
160*7f2fe78bSCy Schubert     if (newptr == NULL)
161*7f2fe78bSCy Schubert         return ENOMEM;
162*7f2fe78bSCy Schubert     gstate->data = newptr;
163*7f2fe78bSCy Schubert     ent = &gstate->data[gstate->ndata];
164*7f2fe78bSCy Schubert     ent->gdef = gdef;
165*7f2fe78bSCy Schubert     ent->gdata = NULL;
166*7f2fe78bSCy Schubert     if (gdef->init != NULL) {
167*7f2fe78bSCy Schubert         ret = gdef->init(context, gdef, &ent->gdata);
168*7f2fe78bSCy Schubert         if (ret)
169*7f2fe78bSCy Schubert             return ret;
170*7f2fe78bSCy Schubert     }
171*7f2fe78bSCy Schubert     gstate->ndata++;
172*7f2fe78bSCy Schubert     *gdata_out = ent->gdata;
173*7f2fe78bSCy Schubert     return 0;
174*7f2fe78bSCy Schubert }
175*7f2fe78bSCy Schubert 
176*7f2fe78bSCy Schubert /* Destructively parse str into a list of group numbers. */
177*7f2fe78bSCy Schubert static krb5_error_code
parse_groups(krb5_context context,char * str,int32_t ** list_out,size_t * count_out)178*7f2fe78bSCy Schubert parse_groups(krb5_context context, char *str, int32_t **list_out,
179*7f2fe78bSCy Schubert              size_t *count_out)
180*7f2fe78bSCy Schubert {
181*7f2fe78bSCy Schubert     const char *const delim = " \t\r\n,";
182*7f2fe78bSCy Schubert     char *token, *save = NULL;
183*7f2fe78bSCy Schubert     int32_t group, *newptr, *list = NULL;
184*7f2fe78bSCy Schubert     size_t count = 0;
185*7f2fe78bSCy Schubert 
186*7f2fe78bSCy Schubert     *list_out = NULL;
187*7f2fe78bSCy Schubert     *count_out = 0;
188*7f2fe78bSCy Schubert 
189*7f2fe78bSCy Schubert     /* Walk through the words in profstr. */
190*7f2fe78bSCy Schubert     for (token = strtok_r(str, delim, &save); token != NULL;
191*7f2fe78bSCy Schubert          token = strtok_r(NULL, delim, &save)) {
192*7f2fe78bSCy Schubert         group = find_gnum(token);
193*7f2fe78bSCy Schubert         if (!group) {
194*7f2fe78bSCy Schubert             TRACE_SPAKE_UNKNOWN_GROUP(context, token);
195*7f2fe78bSCy Schubert             continue;
196*7f2fe78bSCy Schubert         }
197*7f2fe78bSCy Schubert         if (in_grouplist(list, count, group))
198*7f2fe78bSCy Schubert             continue;
199*7f2fe78bSCy Schubert         newptr = realloc(list, (count + 1) * sizeof(*list));
200*7f2fe78bSCy Schubert         if (newptr == NULL) {
201*7f2fe78bSCy Schubert             free(list);
202*7f2fe78bSCy Schubert             return ENOMEM;
203*7f2fe78bSCy Schubert         }
204*7f2fe78bSCy Schubert         list = newptr;
205*7f2fe78bSCy Schubert         list[count++] = group;
206*7f2fe78bSCy Schubert     }
207*7f2fe78bSCy Schubert 
208*7f2fe78bSCy Schubert     *list_out = list;
209*7f2fe78bSCy Schubert     *count_out = count;
210*7f2fe78bSCy Schubert     return 0;
211*7f2fe78bSCy Schubert }
212*7f2fe78bSCy Schubert 
213*7f2fe78bSCy Schubert krb5_error_code
group_init_state(krb5_context context,krb5_boolean is_kdc,groupstate ** gstate_out)214*7f2fe78bSCy Schubert group_init_state(krb5_context context, krb5_boolean is_kdc,
215*7f2fe78bSCy Schubert                  groupstate **gstate_out)
216*7f2fe78bSCy Schubert {
217*7f2fe78bSCy Schubert     krb5_error_code ret;
218*7f2fe78bSCy Schubert     groupstate *gstate;
219*7f2fe78bSCy Schubert     const char *defgroups;
220*7f2fe78bSCy Schubert     char *profstr1 = NULL, *profstr2 = NULL;
221*7f2fe78bSCy Schubert     int32_t *permitted = NULL, challenge_group = 0;
222*7f2fe78bSCy Schubert     size_t npermitted;
223*7f2fe78bSCy Schubert 
224*7f2fe78bSCy Schubert     *gstate_out = NULL;
225*7f2fe78bSCy Schubert 
226*7f2fe78bSCy Schubert     defgroups = is_kdc ? DEFAULT_GROUPS_KDC : DEFAULT_GROUPS_CLIENT;
227*7f2fe78bSCy Schubert     ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
228*7f2fe78bSCy Schubert                              KRB5_CONF_SPAKE_PREAUTH_GROUPS, NULL, defgroups,
229*7f2fe78bSCy Schubert                              &profstr1);
230*7f2fe78bSCy Schubert     if (ret)
231*7f2fe78bSCy Schubert         goto cleanup;
232*7f2fe78bSCy Schubert     ret = parse_groups(context, profstr1, &permitted, &npermitted);
233*7f2fe78bSCy Schubert     if (ret)
234*7f2fe78bSCy Schubert         goto cleanup;
235*7f2fe78bSCy Schubert     if (npermitted == 0) {
236*7f2fe78bSCy Schubert         ret = KRB5_PLUGIN_OP_NOTSUPP;
237*7f2fe78bSCy Schubert         k5_setmsg(context, ret, _("No SPAKE preauth groups configured"));
238*7f2fe78bSCy Schubert         goto cleanup;
239*7f2fe78bSCy Schubert     }
240*7f2fe78bSCy Schubert 
241*7f2fe78bSCy Schubert     if (is_kdc) {
242*7f2fe78bSCy Schubert         /*
243*7f2fe78bSCy Schubert          * Check for a configured optimistic challenge group.  If one is set,
244*7f2fe78bSCy Schubert          * the KDC will send a challenge in the PREAUTH_REQUIRED method data,
245*7f2fe78bSCy Schubert          * before receiving the list of supported groups.
246*7f2fe78bSCy Schubert          */
247*7f2fe78bSCy Schubert         ret = profile_get_string(context->profile, KRB5_CONF_KDCDEFAULTS,
248*7f2fe78bSCy Schubert                                  KRB5_CONF_SPAKE_PREAUTH_KDC_CHALLENGE, NULL,
249*7f2fe78bSCy Schubert                                  NULL, &profstr2);
250*7f2fe78bSCy Schubert         if (ret)
251*7f2fe78bSCy Schubert             goto cleanup;
252*7f2fe78bSCy Schubert         if (profstr2 != NULL) {
253*7f2fe78bSCy Schubert             challenge_group = find_gnum(profstr2);
254*7f2fe78bSCy Schubert             if (!in_grouplist(permitted, npermitted, challenge_group)) {
255*7f2fe78bSCy Schubert                 ret = KRB5_PLUGIN_OP_NOTSUPP;
256*7f2fe78bSCy Schubert                 k5_setmsg(context, ret,
257*7f2fe78bSCy Schubert                           _("SPAKE challenge group not a permitted group: %s"),
258*7f2fe78bSCy Schubert                           profstr2);
259*7f2fe78bSCy Schubert                 goto cleanup;
260*7f2fe78bSCy Schubert             }
261*7f2fe78bSCy Schubert         }
262*7f2fe78bSCy Schubert     }
263*7f2fe78bSCy Schubert 
264*7f2fe78bSCy Schubert     gstate = k5alloc(sizeof(*gstate), &ret);
265*7f2fe78bSCy Schubert     if (gstate == NULL)
266*7f2fe78bSCy Schubert         goto cleanup;
267*7f2fe78bSCy Schubert     gstate->is_kdc = is_kdc;
268*7f2fe78bSCy Schubert     gstate->permitted = permitted;
269*7f2fe78bSCy Schubert     gstate->npermitted = npermitted;
270*7f2fe78bSCy Schubert     gstate->challenge_group = challenge_group;
271*7f2fe78bSCy Schubert     permitted = NULL;
272*7f2fe78bSCy Schubert     gstate->data = NULL;
273*7f2fe78bSCy Schubert     gstate->ndata = 0;
274*7f2fe78bSCy Schubert     *gstate_out = gstate;
275*7f2fe78bSCy Schubert 
276*7f2fe78bSCy Schubert cleanup:
277*7f2fe78bSCy Schubert     profile_release_string(profstr1);
278*7f2fe78bSCy Schubert     profile_release_string(profstr2);
279*7f2fe78bSCy Schubert     free(permitted);
280*7f2fe78bSCy Schubert     return ret;
281*7f2fe78bSCy Schubert }
282*7f2fe78bSCy Schubert 
283*7f2fe78bSCy Schubert 
284*7f2fe78bSCy Schubert void
group_free_state(groupstate * gstate)285*7f2fe78bSCy Schubert group_free_state(groupstate *gstate)
286*7f2fe78bSCy Schubert {
287*7f2fe78bSCy Schubert     groupent *ent;
288*7f2fe78bSCy Schubert 
289*7f2fe78bSCy Schubert     for (ent = gstate->data; ent < gstate->data + gstate->ndata; ent++) {
290*7f2fe78bSCy Schubert         if (ent->gdata != NULL && ent->gdef->fini != NULL)
291*7f2fe78bSCy Schubert             ent->gdef->fini(ent->gdata);
292*7f2fe78bSCy Schubert     }
293*7f2fe78bSCy Schubert 
294*7f2fe78bSCy Schubert     free(gstate->permitted);
295*7f2fe78bSCy Schubert     free(gstate->data);
296*7f2fe78bSCy Schubert     free(gstate);
297*7f2fe78bSCy Schubert }
298*7f2fe78bSCy Schubert 
299*7f2fe78bSCy Schubert krb5_boolean
group_is_permitted(groupstate * gstate,int32_t group)300*7f2fe78bSCy Schubert group_is_permitted(groupstate *gstate, int32_t group)
301*7f2fe78bSCy Schubert {
302*7f2fe78bSCy Schubert     return in_grouplist(gstate->permitted, gstate->npermitted, group);
303*7f2fe78bSCy Schubert }
304*7f2fe78bSCy Schubert 
305*7f2fe78bSCy Schubert void
group_get_permitted(groupstate * gstate,int32_t ** list_out,int32_t * count_out)306*7f2fe78bSCy Schubert group_get_permitted(groupstate *gstate, int32_t **list_out, int32_t *count_out)
307*7f2fe78bSCy Schubert {
308*7f2fe78bSCy Schubert     *list_out = gstate->permitted;
309*7f2fe78bSCy Schubert     *count_out = gstate->npermitted;
310*7f2fe78bSCy Schubert }
311*7f2fe78bSCy Schubert 
312*7f2fe78bSCy Schubert krb5_int32
group_optimistic_challenge(groupstate * gstate)313*7f2fe78bSCy Schubert group_optimistic_challenge(groupstate *gstate)
314*7f2fe78bSCy Schubert {
315*7f2fe78bSCy Schubert     assert(gstate->is_kdc);
316*7f2fe78bSCy Schubert     return gstate->challenge_group;
317*7f2fe78bSCy Schubert }
318*7f2fe78bSCy Schubert 
319*7f2fe78bSCy Schubert krb5_error_code
group_mult_len(int32_t group,size_t * len_out)320*7f2fe78bSCy Schubert group_mult_len(int32_t group, size_t *len_out)
321*7f2fe78bSCy Schubert {
322*7f2fe78bSCy Schubert     const groupdef *gdef;
323*7f2fe78bSCy Schubert 
324*7f2fe78bSCy Schubert     *len_out = 0;
325*7f2fe78bSCy Schubert     gdef = find_gdef(group);
326*7f2fe78bSCy Schubert     if (gdef == NULL)
327*7f2fe78bSCy Schubert         return EINVAL;
328*7f2fe78bSCy Schubert     *len_out = gdef->reg->mult_len;
329*7f2fe78bSCy Schubert     return 0;
330*7f2fe78bSCy Schubert }
331*7f2fe78bSCy Schubert 
332*7f2fe78bSCy Schubert krb5_error_code
group_keygen(krb5_context context,groupstate * gstate,int32_t group,const krb5_data * wbytes,krb5_data * priv_out,krb5_data * pub_out)333*7f2fe78bSCy Schubert group_keygen(krb5_context context, groupstate *gstate, int32_t group,
334*7f2fe78bSCy Schubert              const krb5_data *wbytes, krb5_data *priv_out, krb5_data *pub_out)
335*7f2fe78bSCy Schubert {
336*7f2fe78bSCy Schubert     krb5_error_code ret;
337*7f2fe78bSCy Schubert     const groupdef *gdef;
338*7f2fe78bSCy Schubert     groupdata *gdata;
339*7f2fe78bSCy Schubert     uint8_t *priv = NULL, *pub = NULL;
340*7f2fe78bSCy Schubert 
341*7f2fe78bSCy Schubert     *priv_out = empty_data();
342*7f2fe78bSCy Schubert     *pub_out = empty_data();
343*7f2fe78bSCy Schubert     gdef = find_gdef(group);
344*7f2fe78bSCy Schubert     if (gdef == NULL || wbytes->length != gdef->reg->mult_len)
345*7f2fe78bSCy Schubert         return EINVAL;
346*7f2fe78bSCy Schubert     ret = get_gdata(context, gstate, gdef, &gdata);
347*7f2fe78bSCy Schubert     if (ret)
348*7f2fe78bSCy Schubert         return ret;
349*7f2fe78bSCy Schubert 
350*7f2fe78bSCy Schubert     priv = k5alloc(gdef->reg->mult_len, &ret);
351*7f2fe78bSCy Schubert     if (priv == NULL)
352*7f2fe78bSCy Schubert         goto cleanup;
353*7f2fe78bSCy Schubert     pub = k5alloc(gdef->reg->elem_len, &ret);
354*7f2fe78bSCy Schubert     if (pub == NULL)
355*7f2fe78bSCy Schubert         goto cleanup;
356*7f2fe78bSCy Schubert 
357*7f2fe78bSCy Schubert     ret = gdef->keygen(context, gdata, (uint8_t *)wbytes->data, gstate->is_kdc,
358*7f2fe78bSCy Schubert                        priv, pub);
359*7f2fe78bSCy Schubert     if (ret)
360*7f2fe78bSCy Schubert         goto cleanup;
361*7f2fe78bSCy Schubert 
362*7f2fe78bSCy Schubert     *priv_out = make_data(priv, gdef->reg->mult_len);
363*7f2fe78bSCy Schubert     *pub_out = make_data(pub, gdef->reg->elem_len);
364*7f2fe78bSCy Schubert     priv = pub = NULL;
365*7f2fe78bSCy Schubert     TRACE_SPAKE_KEYGEN(context, pub_out);
366*7f2fe78bSCy Schubert 
367*7f2fe78bSCy Schubert cleanup:
368*7f2fe78bSCy Schubert     zapfree(priv, gdef->reg->mult_len);
369*7f2fe78bSCy Schubert     free(pub);
370*7f2fe78bSCy Schubert     return ret;
371*7f2fe78bSCy Schubert }
372*7f2fe78bSCy Schubert 
373*7f2fe78bSCy Schubert krb5_error_code
group_result(krb5_context context,groupstate * gstate,int32_t group,const krb5_data * wbytes,const krb5_data * ourpriv,const krb5_data * theirpub,krb5_data * spakeresult_out)374*7f2fe78bSCy Schubert group_result(krb5_context context, groupstate *gstate, int32_t group,
375*7f2fe78bSCy Schubert              const krb5_data *wbytes, const krb5_data *ourpriv,
376*7f2fe78bSCy Schubert              const krb5_data *theirpub, krb5_data *spakeresult_out)
377*7f2fe78bSCy Schubert {
378*7f2fe78bSCy Schubert     krb5_error_code ret;
379*7f2fe78bSCy Schubert     const groupdef *gdef;
380*7f2fe78bSCy Schubert     groupdata *gdata;
381*7f2fe78bSCy Schubert     uint8_t *spakeresult = NULL;
382*7f2fe78bSCy Schubert 
383*7f2fe78bSCy Schubert     *spakeresult_out = empty_data();
384*7f2fe78bSCy Schubert     gdef = find_gdef(group);
385*7f2fe78bSCy Schubert     if (gdef == NULL || wbytes->length != gdef->reg->mult_len)
386*7f2fe78bSCy Schubert         return EINVAL;
387*7f2fe78bSCy Schubert     if (ourpriv->length != gdef->reg->mult_len ||
388*7f2fe78bSCy Schubert         theirpub->length != gdef->reg->elem_len)
389*7f2fe78bSCy Schubert         return EINVAL;
390*7f2fe78bSCy Schubert     ret = get_gdata(context, gstate, gdef, &gdata);
391*7f2fe78bSCy Schubert     if (ret)
392*7f2fe78bSCy Schubert         return ret;
393*7f2fe78bSCy Schubert 
394*7f2fe78bSCy Schubert     spakeresult = k5alloc(gdef->reg->elem_len, &ret);
395*7f2fe78bSCy Schubert     if (spakeresult == NULL)
396*7f2fe78bSCy Schubert         goto cleanup;
397*7f2fe78bSCy Schubert 
398*7f2fe78bSCy Schubert     /* Invert is_kdc here to use the other party's constant. */
399*7f2fe78bSCy Schubert     ret = gdef->result(context, gdata, (uint8_t *)wbytes->data,
400*7f2fe78bSCy Schubert                        (uint8_t *)ourpriv->data, (uint8_t *)theirpub->data,
401*7f2fe78bSCy Schubert                        !gstate->is_kdc, spakeresult);
402*7f2fe78bSCy Schubert     if (ret)
403*7f2fe78bSCy Schubert         goto cleanup;
404*7f2fe78bSCy Schubert 
405*7f2fe78bSCy Schubert     *spakeresult_out = make_data(spakeresult, gdef->reg->elem_len);
406*7f2fe78bSCy Schubert     spakeresult = NULL;
407*7f2fe78bSCy Schubert     TRACE_SPAKE_RESULT(context, spakeresult_out);
408*7f2fe78bSCy Schubert 
409*7f2fe78bSCy Schubert cleanup:
410*7f2fe78bSCy Schubert     zapfree(spakeresult, gdef->reg->elem_len);
411*7f2fe78bSCy Schubert     return ret;
412*7f2fe78bSCy Schubert }
413*7f2fe78bSCy Schubert 
414*7f2fe78bSCy Schubert krb5_error_code
group_hash_len(int32_t group,size_t * len_out)415*7f2fe78bSCy Schubert group_hash_len(int32_t group, size_t *len_out)
416*7f2fe78bSCy Schubert {
417*7f2fe78bSCy Schubert     const groupdef *gdef;
418*7f2fe78bSCy Schubert 
419*7f2fe78bSCy Schubert     *len_out = 0;
420*7f2fe78bSCy Schubert     gdef = find_gdef(group);
421*7f2fe78bSCy Schubert     if (gdef == NULL)
422*7f2fe78bSCy Schubert         return EINVAL;
423*7f2fe78bSCy Schubert     *len_out = gdef->reg->hash_len;
424*7f2fe78bSCy Schubert     return 0;
425*7f2fe78bSCy Schubert }
426*7f2fe78bSCy Schubert 
427*7f2fe78bSCy Schubert krb5_error_code
group_hash(krb5_context context,groupstate * gstate,int32_t group,const krb5_data * dlist,size_t ndata,uint8_t * result_out)428*7f2fe78bSCy Schubert group_hash(krb5_context context, groupstate *gstate, int32_t group,
429*7f2fe78bSCy Schubert            const krb5_data *dlist, size_t ndata, uint8_t *result_out)
430*7f2fe78bSCy Schubert {
431*7f2fe78bSCy Schubert     krb5_error_code ret;
432*7f2fe78bSCy Schubert     const groupdef *gdef;
433*7f2fe78bSCy Schubert     groupdata *gdata;
434*7f2fe78bSCy Schubert 
435*7f2fe78bSCy Schubert     gdef = find_gdef(group);
436*7f2fe78bSCy Schubert     if (gdef == NULL)
437*7f2fe78bSCy Schubert         return EINVAL;
438*7f2fe78bSCy Schubert     ret = get_gdata(context, gstate, gdef, &gdata);
439*7f2fe78bSCy Schubert     if (ret)
440*7f2fe78bSCy Schubert         return ret;
441*7f2fe78bSCy Schubert     return gdef->hash(context, gdata, dlist, ndata, result_out);
442*7f2fe78bSCy Schubert }
443