fix ERR_add_error_vdata() for use with multiple args/calls
[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, "digest") == 0)
129         return kdf_md2ctrl(impl, kdf_sshkdf_ctrl, EVP_KDF_CTRL_SET_MD, value);
130     /* alias, for historical reasons */
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
206 const EVP_KDF sshkdf_kdf_meth = {
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