]>
Commit | Line | Data |
---|---|---|
8d76481b SS |
1 | /* |
2 | * Copyright 2018-2018 The OpenSSL Project Authors. All Rights Reserved. | |
3 | * | |
4 | * Licensed under the OpenSSL license (the "License"). You may not use | |
5 | * this file except in compliance with the License. You can obtain a copy | |
6 | * in the file LICENSE in the source distribution or at | |
7 | * https://www.openssl.org/source/license.html | |
8 | */ | |
9 | ||
10 | #include <stdlib.h> | |
11 | #include <stdarg.h> | |
12 | #include <string.h> | |
13 | #include <openssl/evp.h> | |
14 | #include <openssl/kdf.h> | |
15 | #include "internal/cryptlib.h" | |
cee719c2 | 16 | #include "internal/numbers.h" |
8d76481b SS |
17 | #include "internal/evp_int.h" |
18 | #include "kdf_local.h" | |
19 | ||
20 | /* See RFC 4253, Section 7.2 */ | |
21 | ||
22 | static void kdf_sshkdf_reset(EVP_KDF_IMPL *impl); | |
23 | static int SSHKDF(const EVP_MD *evp_md, | |
24 | const unsigned char *key, size_t key_len, | |
25 | const unsigned char *xcghash, size_t xcghash_len, | |
26 | const unsigned char *session_id, size_t session_id_len, | |
27 | char type, unsigned char *okey, size_t okey_len); | |
28 | ||
29 | struct evp_kdf_impl_st { | |
30 | const EVP_MD *md; | |
31 | unsigned char *key; /* K */ | |
32 | size_t key_len; | |
33 | unsigned char *xcghash; /* H */ | |
34 | size_t xcghash_len; | |
35 | char type; /* X */ | |
36 | unsigned char *session_id; | |
37 | size_t session_id_len; | |
38 | }; | |
39 | ||
40 | static EVP_KDF_IMPL *kdf_sshkdf_new(void) | |
41 | { | |
42 | EVP_KDF_IMPL *impl; | |
43 | ||
44 | if ((impl = OPENSSL_zalloc(sizeof(*impl))) == NULL) | |
45 | KDFerr(KDF_F_KDF_SSHKDF_NEW, ERR_R_MALLOC_FAILURE); | |
46 | return impl; | |
47 | } | |
48 | ||
49 | static void kdf_sshkdf_free(EVP_KDF_IMPL *impl) | |
50 | { | |
51 | kdf_sshkdf_reset(impl); | |
52 | OPENSSL_free(impl); | |
53 | } | |
54 | ||
55 | static void kdf_sshkdf_reset(EVP_KDF_IMPL *impl) | |
56 | { | |
57 | OPENSSL_clear_free(impl->key, impl->key_len); | |
58 | OPENSSL_clear_free(impl->xcghash, impl->xcghash_len); | |
59 | OPENSSL_clear_free(impl->session_id, impl->session_id_len); | |
60 | memset(impl, 0, sizeof(*impl)); | |
61 | } | |
62 | ||
63 | static int kdf_sshkdf_parse_buffer_arg(unsigned char **dst, size_t *dst_len, | |
64 | va_list args) | |
65 | { | |
66 | const unsigned char *p; | |
67 | size_t len; | |
68 | ||
69 | p = va_arg(args, const unsigned char *); | |
70 | len = va_arg(args, size_t); | |
71 | OPENSSL_clear_free(*dst, *dst_len); | |
72 | *dst = OPENSSL_memdup(p, len); | |
73 | if (*dst == NULL) | |
74 | return 0; | |
75 | ||
76 | *dst_len = len; | |
77 | return 1; | |
78 | } | |
79 | ||
80 | static int kdf_sshkdf_ctrl(EVP_KDF_IMPL *impl, int cmd, va_list args) | |
81 | { | |
82 | int t; | |
83 | ||
84 | switch (cmd) { | |
85 | case EVP_KDF_CTRL_SET_MD: | |
86 | impl->md = va_arg(args, const EVP_MD *); | |
87 | if (impl->md == NULL) | |
88 | return 0; | |
89 | ||
90 | return 1; | |
91 | ||
92 | case EVP_KDF_CTRL_SET_KEY: | |
93 | return kdf_sshkdf_parse_buffer_arg(&impl->key, | |
94 | &impl->key_len, args); | |
95 | ||
96 | case EVP_KDF_CTRL_SET_SSHKDF_XCGHASH: | |
97 | return kdf_sshkdf_parse_buffer_arg(&impl->xcghash, | |
98 | &impl->xcghash_len, args); | |
99 | ||
100 | case EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID: | |
101 | return kdf_sshkdf_parse_buffer_arg(&impl->session_id, | |
102 | &impl->session_id_len, args); | |
103 | ||
104 | case EVP_KDF_CTRL_SET_SSHKDF_TYPE: | |
105 | t = va_arg(args, int); | |
106 | if (t < 65 || t > 70) { | |
107 | KDFerr(KDF_F_KDF_SSHKDF_CTRL, KDF_R_VALUE_ERROR); | |
108 | return 0; | |
109 | } | |
110 | ||
111 | impl->type = (char)t; | |
112 | return 1; | |
113 | ||
114 | default: | |
115 | return -2; | |
116 | ||
117 | } | |
118 | } | |
119 | ||
120 | static int kdf_sshkdf_ctrl_str(EVP_KDF_IMPL *impl, const char *type, | |
121 | const char *value) | |
122 | { | |
123 | if (value == NULL) { | |
124 | KDFerr(KDF_F_KDF_SSHKDF_CTRL_STR, KDF_R_VALUE_MISSING); | |
125 | return 0; | |
126 | } | |
127 | ||
46793451 SS |
128 | if (strcmp(type, "digest") == 0) |
129 | return kdf_md2ctrl(impl, kdf_sshkdf_ctrl, EVP_KDF_CTRL_SET_MD, value); | |
130 | /* alias, for historical reasons */ | |
8d76481b SS |
131 | if (strcmp(type, "md") == 0) |
132 | return kdf_md2ctrl(impl, kdf_sshkdf_ctrl, EVP_KDF_CTRL_SET_MD, value); | |
133 | ||
134 | if (strcmp(type, "key") == 0) | |
135 | return kdf_str2ctrl(impl, kdf_sshkdf_ctrl, | |
136 | EVP_KDF_CTRL_SET_KEY, value); | |
137 | ||
138 | if (strcmp(type, "hexkey") == 0) | |
139 | return kdf_hex2ctrl(impl, kdf_sshkdf_ctrl, | |
140 | EVP_KDF_CTRL_SET_KEY, value); | |
141 | ||
142 | if (strcmp(type, "xcghash") == 0) | |
143 | return kdf_str2ctrl(impl, kdf_sshkdf_ctrl, | |
144 | EVP_KDF_CTRL_SET_SSHKDF_XCGHASH, value); | |
145 | ||
146 | if (strcmp(type, "hexxcghash") == 0) | |
147 | return kdf_hex2ctrl(impl, kdf_sshkdf_ctrl, | |
148 | EVP_KDF_CTRL_SET_SSHKDF_XCGHASH, value); | |
149 | ||
150 | if (strcmp(type, "session_id") == 0) | |
151 | return kdf_str2ctrl(impl, kdf_sshkdf_ctrl, | |
152 | EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID, value); | |
153 | ||
154 | if (strcmp(type, "hexsession_id") == 0) | |
155 | return kdf_hex2ctrl(impl, kdf_sshkdf_ctrl, | |
156 | EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID, value); | |
157 | ||
158 | if (strcmp(type, "type") == 0) { | |
159 | if (strlen(value) != 1) { | |
160 | KDFerr(KDF_F_KDF_SSHKDF_CTRL_STR, KDF_R_VALUE_ERROR); | |
161 | return 0; | |
162 | } | |
163 | ||
164 | return call_ctrl(kdf_sshkdf_ctrl, impl, EVP_KDF_CTRL_SET_SSHKDF_TYPE, | |
165 | (int)value[0]); | |
166 | } | |
167 | ||
168 | KDFerr(KDF_F_KDF_SSHKDF_CTRL_STR, KDF_R_UNKNOWN_PARAMETER_TYPE); | |
169 | return -2; | |
170 | } | |
171 | ||
172 | static size_t kdf_sshkdf_size(EVP_KDF_IMPL *impl) | |
173 | { | |
174 | return SIZE_MAX; | |
175 | } | |
176 | ||
177 | static int kdf_sshkdf_derive(EVP_KDF_IMPL *impl, unsigned char *key, | |
178 | size_t keylen) | |
179 | { | |
180 | if (impl->md == NULL) { | |
181 | KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_MESSAGE_DIGEST); | |
182 | return 0; | |
183 | } | |
184 | if (impl->key == NULL) { | |
185 | KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_KEY); | |
186 | return 0; | |
187 | } | |
188 | if (impl->xcghash == NULL) { | |
189 | KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_XCGHASH); | |
190 | return 0; | |
191 | } | |
192 | if (impl->session_id == NULL) { | |
193 | KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_SESSION_ID); | |
194 | return 0; | |
195 | } | |
196 | if (impl->type == 0) { | |
197 | KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_TYPE); | |
198 | return 0; | |
199 | } | |
200 | return SSHKDF(impl->md, impl->key, impl->key_len, | |
201 | impl->xcghash, impl->xcghash_len, | |
202 | impl->session_id, impl->session_id_len, | |
203 | impl->type, key, keylen); | |
204 | } | |
205 | ||
d2ba8123 | 206 | const EVP_KDF sshkdf_kdf_meth = { |
8d76481b SS |
207 | EVP_KDF_SSHKDF, |
208 | kdf_sshkdf_new, | |
209 | kdf_sshkdf_free, | |
210 | kdf_sshkdf_reset, | |
211 | kdf_sshkdf_ctrl, | |
212 | kdf_sshkdf_ctrl_str, | |
213 | kdf_sshkdf_size, | |
214 | kdf_sshkdf_derive, | |
215 | }; | |
216 | ||
217 | static int SSHKDF(const EVP_MD *evp_md, | |
218 | const unsigned char *key, size_t key_len, | |
219 | const unsigned char *xcghash, size_t xcghash_len, | |
220 | const unsigned char *session_id, size_t session_id_len, | |
221 | char type, unsigned char *okey, size_t okey_len) | |
222 | { | |
223 | EVP_MD_CTX *md = NULL; | |
224 | unsigned char digest[EVP_MAX_MD_SIZE]; | |
225 | unsigned int dsize = 0; | |
226 | size_t cursize = 0; | |
227 | int ret = 0; | |
228 | ||
229 | md = EVP_MD_CTX_new(); | |
230 | if (md == NULL) | |
231 | return 0; | |
232 | ||
233 | if (!EVP_DigestInit_ex(md, evp_md, NULL)) | |
234 | goto out; | |
235 | ||
236 | if (!EVP_DigestUpdate(md, key, key_len)) | |
237 | goto out; | |
238 | ||
239 | if (!EVP_DigestUpdate(md, xcghash, xcghash_len)) | |
240 | goto out; | |
241 | ||
242 | if (!EVP_DigestUpdate(md, &type, 1)) | |
243 | goto out; | |
244 | ||
245 | if (!EVP_DigestUpdate(md, session_id, session_id_len)) | |
246 | goto out; | |
247 | ||
248 | if (!EVP_DigestFinal_ex(md, digest, &dsize)) | |
249 | goto out; | |
250 | ||
251 | if (okey_len < dsize) { | |
252 | memcpy(okey, digest, okey_len); | |
253 | ret = 1; | |
254 | goto out; | |
255 | } | |
256 | ||
257 | memcpy(okey, digest, dsize); | |
258 | ||
259 | for (cursize = dsize; cursize < okey_len; cursize += dsize) { | |
260 | ||
261 | if (!EVP_DigestInit_ex(md, evp_md, NULL)) | |
262 | goto out; | |
263 | ||
264 | if (!EVP_DigestUpdate(md, key, key_len)) | |
265 | goto out; | |
266 | ||
267 | if (!EVP_DigestUpdate(md, xcghash, xcghash_len)) | |
268 | goto out; | |
269 | ||
270 | if (!EVP_DigestUpdate(md, okey, cursize)) | |
271 | goto out; | |
272 | ||
273 | if (!EVP_DigestFinal_ex(md, digest, &dsize)) | |
274 | goto out; | |
275 | ||
276 | if (okey_len < cursize + dsize) { | |
277 | memcpy(okey + cursize, digest, okey_len - cursize); | |
278 | ret = 1; | |
279 | goto out; | |
280 | } | |
281 | ||
282 | memcpy(okey + cursize, digest, dsize); | |
283 | } | |
284 | ||
285 | ret = 1; | |
286 | ||
287 | out: | |
288 | EVP_MD_CTX_free(md); | |
289 | OPENSSL_cleanse(digest, EVP_MAX_MD_SIZE); | |
290 | return ret; | |
291 | } | |
292 |