]> git.meshlink.io Git - meshlink/blob - src/protocol_key.c
Add PRF to derive key material from the ECDH shared secret.
[meshlink] / src / protocol_key.c
1 /*
2     protocol_key.c -- handle the meta-protocol, key exchange
3     Copyright (C) 1999-2005 Ivo Timmermans,
4                   2000-2011 Guus Sliepen <guus@tinc-vpn.org>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "system.h"
22
23 #include "splay_tree.h"
24 #include "cipher.h"
25 #include "connection.h"
26 #include "crypto.h"
27 #include "ecdh.h"
28 #include "logger.h"
29 #include "net.h"
30 #include "netutl.h"
31 #include "node.h"
32 #include "protocol.h"
33 #include "utils.h"
34 #include "xalloc.h"
35
36 static bool mykeyused = false;
37
38 void send_key_changed(void) {
39         splay_node_t *node;
40         connection_t *c;
41
42         send_request(broadcast, "%d %x %s", KEY_CHANGED, rand(), myself->name);
43
44         /* Immediately send new keys to directly connected nodes to keep UDP mappings alive */
45
46         for(node = connection_tree->head; node; node = node->next) {
47                 c = node->data;
48                 if(c->status.active && c->node && c->node->status.reachable)
49                         send_ans_key(c->node);
50         }
51 }
52
53 bool key_changed_h(connection_t *c, char *request) {
54         char name[MAX_STRING_SIZE];
55         node_t *n;
56
57         if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) {
58                 logger(LOG_ERR, "Got bad %s from %s (%s)", "KEY_CHANGED",
59                            c->name, c->hostname);
60                 return false;
61         }
62
63         if(seen_request(request))
64                 return true;
65
66         n = lookup_node(name);
67
68         if(!n) {
69                 logger(LOG_ERR, "Got %s from %s (%s) origin %s which does not exist",
70                            "KEY_CHANGED", c->name, c->hostname, name);
71                 return true;
72         }
73
74         n->status.validkey = false;
75         n->last_req_key = 0;
76
77         /* Tell the others */
78
79         if(!tunnelserver)
80                 forward_request(c, request);
81
82         return true;
83 }
84
85 bool send_req_key(node_t *to) {
86         return send_request(to->nexthop->connection, "%d %s %s 1", REQ_KEY, myself->name, to->name);
87 }
88
89 bool req_key_h(connection_t *c, char *request) {
90         char from_name[MAX_STRING_SIZE];
91         char to_name[MAX_STRING_SIZE];
92         node_t *from, *to;
93         int kx_version = 0;
94
95         if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING " %d", from_name, to_name, &kx_version) < 2) {
96                 logger(LOG_ERR, "Got bad %s from %s (%s)", "REQ_KEY", c->name,
97                            c->hostname);
98                 return false;
99         }
100
101         if(!check_id(from_name) || !check_id(to_name)) {
102                 logger(LOG_ERR, "Got bad %s from %s (%s): %s", "REQ_KEY", c->name, c->hostname, "invalid name");
103                 return false;
104         }
105
106         from = lookup_node(from_name);
107
108         if(!from) {
109                 logger(LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
110                            "REQ_KEY", c->name, c->hostname, from_name);
111                 return true;
112         }
113
114         to = lookup_node(to_name);
115
116         if(!to) {
117                 logger(LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
118                            "REQ_KEY", c->name, c->hostname, to_name);
119                 return true;
120         }
121
122         /* Check if this key request is for us */
123
124         if(to == myself) {                      /* Yes, send our own key back */
125                 if(kx_version > 0) {
126                         logger(LOG_DEBUG, "Got ECDH key request from %s", from->name);
127                         from->status.ecdh = true;
128                 }
129                 send_ans_key(from);
130         } else {
131                 if(tunnelserver)
132                         return true;
133
134                 if(!to->status.reachable) {
135                         logger(LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
136                                 "REQ_KEY", c->name, c->hostname, to_name);
137                         return true;
138                 }
139
140                 send_request(to->nexthop->connection, "%s", request);
141         }
142
143         return true;
144 }
145
146 bool send_ans_key_ecdh(node_t *to) {
147         char key[ECDH_SIZE * 2 + 1];
148
149         ecdh_generate_public(&to->ecdh, key);
150
151         bin2hex(key, key, ECDH_SIZE);
152         key[ECDH_SIZE * 2] = '\0';
153
154         return send_request(to->nexthop->connection, "%d %s %s ECDH:%s %d %d %zu %d", ANS_KEY,
155                                                 myself->name, to->name, key,
156                                                 cipher_get_nid(&myself->incipher),
157                                                 digest_get_nid(&myself->indigest),
158                                                 digest_length(&myself->indigest),
159                                                 myself->incompression);
160 }
161
162 bool send_ans_key(node_t *to) {
163         if(to->status.ecdh)
164                 return send_ans_key_ecdh(to);
165
166         size_t keylen = cipher_keylength(&myself->incipher);
167         char key[keylen * 2 + 1];
168
169         cipher_open_by_nid(&to->incipher, cipher_get_nid(&myself->incipher));
170         digest_open_by_nid(&to->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
171         to->incompression = myself->incompression;
172
173         randomize(key, keylen);
174         cipher_set_key(&to->incipher, key, true);
175         digest_set_key(&to->indigest, key, keylen);
176
177         bin2hex(key, key, keylen);
178         key[keylen * 2] = '\0';
179
180         // Reset sequence number and late packet window
181         mykeyused = true;
182         to->received_seqno = 0;
183         if(replaywin) memset(to->late, 0, replaywin);
184
185         return send_request(to->nexthop->connection, "%d %s %s %s %d %d %zu %d", ANS_KEY,
186                                                 myself->name, to->name, key,
187                                                 cipher_get_nid(&to->incipher),
188                                                 digest_get_nid(&to->indigest),
189                                                 digest_length(&to->indigest),
190                                                 to->incompression);
191 }
192
193 bool ans_key_h(connection_t *c, char *request) {
194         char from_name[MAX_STRING_SIZE];
195         char to_name[MAX_STRING_SIZE];
196         char key[MAX_STRING_SIZE];
197         char address[MAX_STRING_SIZE] = "";
198         char port[MAX_STRING_SIZE] = "";
199         int cipher, digest, maclength, compression, keylen;
200         node_t *from, *to;
201
202         if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d "MAX_STRING" "MAX_STRING" %d",
203                 from_name, to_name, key, &cipher, &digest, &maclength,
204                 &compression, address, port) < 7) {
205                 logger(LOG_ERR, "Got bad %s from %s (%s)", "ANS_KEY", c->name,
206                            c->hostname);
207                 return false;
208         }
209
210         if(!check_id(from_name) || !check_id(to_name)) {
211                 logger(LOG_ERR, "Got bad %s from %s (%s): %s", "ANS_KEY", c->name, c->hostname, "invalid name");
212                 return false;
213         }
214
215         from = lookup_node(from_name);
216
217         if(!from) {
218                 logger(LOG_ERR, "Got %s from %s (%s) origin %s which does not exist in our connection list",
219                            "ANS_KEY", c->name, c->hostname, from_name);
220                 return true;
221         }
222
223         to = lookup_node(to_name);
224
225         if(!to) {
226                 logger(LOG_ERR, "Got %s from %s (%s) destination %s which does not exist in our connection list",
227                            "ANS_KEY", c->name, c->hostname, to_name);
228                 return true;
229         }
230
231         /* Forward it if necessary */
232
233         if(to != myself) {
234                 if(tunnelserver)
235                         return true;
236
237                 if(!to->status.reachable) {
238                         logger(LOG_WARNING, "Got %s from %s (%s) destination %s which is not reachable",
239                                    "ANS_KEY", c->name, c->hostname, to_name);
240                         return true;
241                 }
242
243                 if(!*address && from->address.sa.sa_family != AF_UNSPEC) {
244                         char *address, *port;
245                         ifdebug(PROTOCOL) logger(LOG_DEBUG, "Appending reflexive UDP address to ANS_KEY from %s to %s", from->name, to->name);
246                         sockaddr2str(&from->address, &address, &port);
247                         send_request(to->nexthop->connection, "%s %s %s", request, address, port);
248                         free(address);
249                         free(port);
250                         return true;
251                 }
252
253                 return send_request(to->nexthop->connection, "%s", request);
254         }
255
256         /* Check and lookup cipher and digest algorithms */
257
258         if(!cipher_open_by_nid(&from->outcipher, cipher)) {
259                 logger(LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name, from->hostname);
260                 return false;
261         }
262
263         if(!digest_open_by_nid(&from->outdigest, digest, maclength)) {
264                 logger(LOG_ERR, "Node %s (%s) uses unknown digest!", from->name, from->hostname);
265                 return false;
266         }
267
268         if(maclength != digest_length(&from->outdigest)) {
269                 logger(LOG_ERR, "Node %s (%s) uses bogus MAC length!", from->name, from->hostname);
270                 return false;
271         }
272
273         if(compression < 0 || compression > 11) {
274                 logger(LOG_ERR, "Node %s (%s) uses bogus compression level!", from->name, from->hostname);
275                 return true;
276         }
277
278         from->outcompression = compression;
279
280         /* ECDH or old-style key exchange? */
281         /* TODO: look at SSH and TLS to see how they derive cipher and HMAC keys from shared secret properly */
282         
283         if(!strncmp(key, "ECDH:", 5)) {
284                 logger(LOG_DEBUG, "Got ECDH key from %s", from->name);
285
286                 keylen = (strlen(key) - 5) / 2;
287
288                 if(keylen != ECDH_SIZE) {
289                         logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
290                         return false;
291                 }
292
293                 if(ECDH_SHARED_SIZE < cipher_keylength(&from->outcipher)) {
294                         logger(LOG_ERR, "ECDH key too short for cipher of %s!", from->name);
295                         return false;
296                 }
297
298                 if(!from->ecdh) {
299                         logger(LOG_DEBUG, "Woops, we didn't generate our public key yet");
300                         from->status.ecdh = true;
301                         if(!send_ans_key(from))
302                                 return false;
303                 }
304
305                 char shared[ECDH_SHARED_SIZE * 2 + 1];
306                 char hex[ECDH_SHARED_SIZE * 2 + 1];
307                 hex2bin(key + 5, key + 5, keylen);
308
309                 if(!ecdh_compute_shared(&from->ecdh, key + 5, shared))
310                         return false;
311
312                 /* Update our crypto end */
313
314                 char *mykey;
315                 size_t mykeylen = cipher_keylength(&myself->incipher);
316                 keylen = cipher_keylength(&from->outcipher);
317
318                 if(ECDH_SHARED_SIZE < mykeylen) {
319                         logger(LOG_ERR, "ECDH key too short for cipher of MYSELF!");
320                         return false;
321                 }
322
323                 if(strcmp(myself->name, from->name) < 0) {
324                         logger(LOG_DEBUG, "Using left half of shared secret");
325                         mykey = shared;
326                         memcpy(key, shared + ECDH_SHARED_SIZE - keylen, keylen);
327                 } else {
328                         logger(LOG_DEBUG, "Using right half of shared secret");
329                         mykey = shared + ECDH_SHARED_SIZE - mykeylen;
330                         memcpy(key, shared, keylen);
331                 }
332
333                 cipher_open_by_nid(&from->incipher, cipher_get_nid(&myself->incipher));
334                 digest_open_by_nid(&from->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
335                 from->incompression = myself->incompression;
336
337                 cipher_set_key(&from->incipher, mykey, true);
338                 digest_set_key(&from->indigest, mykey, mykeylen);
339
340                 // Reset sequence number and late packet window
341                 mykeyused = true;
342                 from->received_seqno = 0;
343                 if(replaywin)
344                         memset(from->late, 0, replaywin);
345
346                 bin2hex(shared, hex, ECDH_SHARED_SIZE);
347                 hex[ECDH_SHARED_SIZE * 2] = 0;
348                 logger(LOG_DEBUG, "Shared secret was %s", hex);
349
350                 bin2hex(mykey, hex, mykeylen);
351                 hex[mykeylen * 2] = 0;
352                 logger(LOG_DEBUG, "My part is: %s (%d)", hex, mykeylen);
353
354                 bin2hex(key, hex, keylen);
355                 hex[keylen * 2] = 0;
356                 logger(LOG_DEBUG, "His part is: %s (%d)", hex, keylen);
357         } else {
358                 keylen = strlen(key) / 2;
359                 hex2bin(key, key, keylen);
360         }
361
362         /* Update our copy of the origin's packet key */
363
364         if(keylen != cipher_keylength(&from->outcipher)) {
365                 logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
366                 return false;
367         }
368
369         cipher_set_key(&from->outcipher, key, false);
370         digest_set_key(&from->outdigest, key, keylen);
371
372         from->status.validkey = true;
373         from->sent_seqno = 0;
374
375         if(*address && *port) {
376                 ifdebug(PROTOCOL) logger(LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
377                 sockaddr_t sa = str2sockaddr(address, port);
378                 update_node_udp(from, &sa);
379         }
380
381         if(from->options & OPTION_PMTU_DISCOVERY)
382                 send_mtu_probe(from);
383
384         return true;
385 }