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