xref: /freebsd/crypto/krb5/src/lib/crypto/krb/derive.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright (C) 1998 by the FundsXpress, INC.
4  *
5  * All rights reserved.
6  *
7  * Export of this software from the United States of America may require
8  * a specific license from the United States Government.  It is the
9  * responsibility of any person or organization contemplating export to
10  * obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of FundsXpress. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  FundsXpress makes no representations about the suitability of
20  * this software for any purpose.  It is provided "as is" without express
21  * or implied warranty.
22  *
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26  */
27 
28 #include "crypto_int.h"
29 
30 static krb5_key
find_cached_dkey(struct derived_key * list,const krb5_data * constant)31 find_cached_dkey(struct derived_key *list, const krb5_data *constant)
32 {
33     for (; list; list = list->next) {
34         if (data_eq(list->constant, *constant)) {
35             krb5_k_reference_key(NULL, list->dkey);
36             return list->dkey;
37         }
38     }
39     return NULL;
40 }
41 
42 static krb5_error_code
add_cached_dkey(krb5_key key,const krb5_data * constant,const krb5_keyblock * dkeyblock,krb5_key * cached_dkey)43 add_cached_dkey(krb5_key key, const krb5_data *constant,
44                 const krb5_keyblock *dkeyblock, krb5_key *cached_dkey)
45 {
46     krb5_key dkey;
47     krb5_error_code ret;
48     struct derived_key *dkent = NULL;
49     char *data = NULL;
50 
51     /* Allocate fields for the new entry. */
52     dkent = malloc(sizeof(*dkent));
53     if (dkent == NULL)
54         goto cleanup;
55     data = k5memdup(constant->data, constant->length, &ret);
56     if (data == NULL)
57         goto cleanup;
58     ret = krb5_k_create_key(NULL, dkeyblock, &dkey);
59     if (ret != 0)
60         goto cleanup;
61 
62     /* Add the new entry to the list. */
63     dkent->dkey = dkey;
64     dkent->constant.data = data;
65     dkent->constant.length = constant->length;
66     dkent->next = key->derived;
67     key->derived = dkent;
68 
69     /* Return a "copy" of the cached key. */
70     krb5_k_reference_key(NULL, dkey);
71     *cached_dkey = dkey;
72     return 0;
73 
74 cleanup:
75     free(dkent);
76     free(data);
77     return ENOMEM;
78 }
79 
80 krb5_error_code
krb5int_derive_random(const struct krb5_enc_provider * enc,const struct krb5_hash_provider * hash,krb5_key inkey,krb5_data * outrnd,const krb5_data * in_constant,enum deriv_alg alg)81 krb5int_derive_random(const struct krb5_enc_provider *enc,
82                       const struct krb5_hash_provider *hash,
83                       krb5_key inkey, krb5_data *outrnd,
84                       const krb5_data *in_constant, enum deriv_alg alg)
85 {
86     krb5_data empty = empty_data();
87 
88     switch (alg) {
89     case DERIVE_RFC3961:
90         return k5_derive_random_rfc3961(enc, inkey, in_constant, outrnd);
91     case DERIVE_SP800_108_CMAC:
92         return k5_sp800_108_feedback_cmac(enc, inkey, in_constant, outrnd);
93     case DERIVE_SP800_108_HMAC:
94         return k5_sp800_108_counter_hmac(hash, inkey, in_constant, &empty,
95                                          outrnd);
96     default:
97         return EINVAL;
98     }
99 }
100 
101 /*
102  * Compute a derived key into the keyblock outkey.  This variation on
103  * krb5int_derive_key does not cache the result, as it is only used
104  * directly in situations which are not expected to be repeated with
105  * the same inkey and constant.
106  */
107 krb5_error_code
krb5int_derive_keyblock(const struct krb5_enc_provider * enc,const struct krb5_hash_provider * hash,krb5_key inkey,krb5_keyblock * outkey,const krb5_data * in_constant,enum deriv_alg alg)108 krb5int_derive_keyblock(const struct krb5_enc_provider *enc,
109                         const struct krb5_hash_provider *hash,
110                         krb5_key inkey, krb5_keyblock *outkey,
111                         const krb5_data *in_constant, enum deriv_alg alg)
112 {
113     krb5_error_code ret;
114     krb5_data rawkey = empty_data();
115 
116     /* Allocate a buffer for the raw key bytes. */
117     ret = alloc_data(&rawkey, enc->keybytes);
118     if (ret)
119         goto cleanup;
120 
121     /* Derive pseudo-random data for the key bytes. */
122     ret = krb5int_derive_random(enc, hash, inkey, &rawkey, in_constant, alg);
123     if (ret)
124         goto cleanup;
125 
126     /* Postprocess the key. */
127     ret = krb5_c_random_to_key(NULL, inkey->keyblock.enctype, &rawkey, outkey);
128 
129 cleanup:
130     zapfree(rawkey.data, enc->keybytes);
131     return ret;
132 }
133 
134 krb5_error_code
krb5int_derive_key(const struct krb5_enc_provider * enc,const struct krb5_hash_provider * hash,krb5_key inkey,krb5_key * outkey,const krb5_data * in_constant,enum deriv_alg alg)135 krb5int_derive_key(const struct krb5_enc_provider *enc,
136                    const struct krb5_hash_provider *hash,
137                    krb5_key inkey, krb5_key *outkey,
138                    const krb5_data *in_constant, enum deriv_alg alg)
139 {
140     krb5_keyblock keyblock;
141     krb5_error_code ret;
142     krb5_key dkey;
143 
144     *outkey = NULL;
145 
146     /* Check for a cached result. */
147     dkey = find_cached_dkey(inkey->derived, in_constant);
148     if (dkey != NULL) {
149         *outkey = dkey;
150         return 0;
151     }
152 
153     /* Derive into a temporary keyblock. */
154     keyblock.length = enc->keylength;
155     keyblock.contents = malloc(keyblock.length);
156     keyblock.enctype = inkey->keyblock.enctype;
157     if (keyblock.contents == NULL)
158         return ENOMEM;
159     ret = krb5int_derive_keyblock(enc, hash, inkey, &keyblock, in_constant,
160                                   alg);
161     if (ret)
162         goto cleanup;
163 
164     /* Cache the derived key. */
165     ret = add_cached_dkey(inkey, in_constant, &keyblock, &dkey);
166     if (ret != 0)
167         goto cleanup;
168 
169     *outkey = dkey;
170 
171 cleanup:
172     zapfree(keyblock.contents, keyblock.length);
173     return ret;
174 }
175