The use of the likes of UINT32_MAX requires internal/numbers.h
[openssl.git] / crypto / kdf / sshkdf.c
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"
16 #include "internal/numbers.h"
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
128     if (strcmp(type, "md") == 0)
129         return kdf_md2ctrl(impl, kdf_sshkdf_ctrl, EVP_KDF_CTRL_SET_MD, value);
130
131     if (strcmp(type, "key") == 0)
132         return kdf_str2ctrl(impl, kdf_sshkdf_ctrl,
133                             EVP_KDF_CTRL_SET_KEY, value);
134
135     if (strcmp(type, "hexkey") == 0)
136         return kdf_hex2ctrl(impl, kdf_sshkdf_ctrl,
137                             EVP_KDF_CTRL_SET_KEY, value);
138
139     if (strcmp(type, "xcghash") == 0)
140         return kdf_str2ctrl(impl, kdf_sshkdf_ctrl,
141                             EVP_KDF_CTRL_SET_SSHKDF_XCGHASH, value);
142
143     if (strcmp(type, "hexxcghash") == 0)
144         return kdf_hex2ctrl(impl, kdf_sshkdf_ctrl,
145                             EVP_KDF_CTRL_SET_SSHKDF_XCGHASH, value);
146
147     if (strcmp(type, "session_id") == 0)
148         return kdf_str2ctrl(impl, kdf_sshkdf_ctrl,
149                             EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID, value);
150
151     if (strcmp(type, "hexsession_id") == 0)
152         return kdf_hex2ctrl(impl, kdf_sshkdf_ctrl,
153                             EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID, value);
154
155     if (strcmp(type, "type") == 0) {
156         if (strlen(value) != 1) {
157             KDFerr(KDF_F_KDF_SSHKDF_CTRL_STR, KDF_R_VALUE_ERROR);
158             return 0;
159         }
160
161         return call_ctrl(kdf_sshkdf_ctrl, impl, EVP_KDF_CTRL_SET_SSHKDF_TYPE,
162                          (int)value[0]);
163     }
164
165     KDFerr(KDF_F_KDF_SSHKDF_CTRL_STR, KDF_R_UNKNOWN_PARAMETER_TYPE);
166     return -2;
167 }
168
169 static size_t kdf_sshkdf_size(EVP_KDF_IMPL *impl)
170 {
171     return SIZE_MAX;
172 }
173
174 static int kdf_sshkdf_derive(EVP_KDF_IMPL *impl, unsigned char *key,
175                              size_t keylen)
176 {
177     if (impl->md == NULL) {
178         KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_MESSAGE_DIGEST);
179         return 0;
180     }
181     if (impl->key == NULL) {
182         KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_KEY);
183         return 0;
184     }
185     if (impl->xcghash == NULL) {
186         KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_XCGHASH);
187         return 0;
188     }
189     if (impl->session_id == NULL) {
190         KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_SESSION_ID);
191         return 0;
192     }
193     if (impl->type == 0) {
194         KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_TYPE);
195         return 0;
196     }
197     return SSHKDF(impl->md, impl->key, impl->key_len,
198                   impl->xcghash, impl->xcghash_len,
199                   impl->session_id, impl->session_id_len,
200                   impl->type, key, keylen);
201 }
202
203 const EVP_KDF_METHOD sshkdf_kdf_meth = {
204     EVP_KDF_SSHKDF,
205     kdf_sshkdf_new,
206     kdf_sshkdf_free,
207     kdf_sshkdf_reset,
208     kdf_sshkdf_ctrl,
209     kdf_sshkdf_ctrl_str,
210     kdf_sshkdf_size,
211     kdf_sshkdf_derive,
212 };
213
214 static int SSHKDF(const EVP_MD *evp_md,
215                   const unsigned char *key, size_t key_len,
216                   const unsigned char *xcghash, size_t xcghash_len,
217                   const unsigned char *session_id, size_t session_id_len,
218                   char type, unsigned char *okey, size_t okey_len)
219 {
220     EVP_MD_CTX *md = NULL;
221     unsigned char digest[EVP_MAX_MD_SIZE];
222     unsigned int dsize = 0;
223     size_t cursize = 0;
224     int ret = 0;
225
226     md = EVP_MD_CTX_new();
227     if (md == NULL)
228         return 0;
229
230     if (!EVP_DigestInit_ex(md, evp_md, NULL))
231         goto out;
232
233     if (!EVP_DigestUpdate(md, key, key_len))
234         goto out;
235
236     if (!EVP_DigestUpdate(md, xcghash, xcghash_len))
237         goto out;
238
239     if (!EVP_DigestUpdate(md, &type, 1))
240         goto out;
241
242     if (!EVP_DigestUpdate(md, session_id, session_id_len))
243         goto out;
244
245     if (!EVP_DigestFinal_ex(md, digest, &dsize))
246         goto out;
247
248     if (okey_len < dsize) {
249         memcpy(okey, digest, okey_len);
250         ret = 1;
251         goto out;
252     }
253
254     memcpy(okey, digest, dsize);
255
256     for (cursize = dsize; cursize < okey_len; cursize += dsize) {
257
258         if (!EVP_DigestInit_ex(md, evp_md, NULL))
259             goto out;
260
261         if (!EVP_DigestUpdate(md, key, key_len))
262             goto out;
263
264         if (!EVP_DigestUpdate(md, xcghash, xcghash_len))
265             goto out;
266
267         if (!EVP_DigestUpdate(md, okey, cursize))
268             goto out;
269
270         if (!EVP_DigestFinal_ex(md, digest, &dsize))
271             goto out;
272
273         if (okey_len < cursize + dsize) {
274             memcpy(okey + cursize, digest, okey_len - cursize);
275             ret = 1;
276             goto out;
277         }
278
279         memcpy(okey + cursize, digest, dsize);
280     }
281
282     ret = 1;
283
284 out:
285     EVP_MD_CTX_free(md);
286     OPENSSL_cleanse(digest, EVP_MAX_MD_SIZE);
287     return ret;
288 }
289