summaryrefslogtreecommitdiff
path: root/nacl/curvecp/curvecpclient.c
diff options
context:
space:
mode:
Diffstat (limited to 'nacl/curvecp/curvecpclient.c')
-rw-r--r--nacl/curvecp/curvecpclient.c476
1 files changed, 476 insertions, 0 deletions
diff --git a/nacl/curvecp/curvecpclient.c b/nacl/curvecp/curvecpclient.c
new file mode 100644
index 00000000..00793f00
--- /dev/null
+++ b/nacl/curvecp/curvecpclient.c
@@ -0,0 +1,476 @@
1#include <signal.h>
2#include <sys/types.h>
3#include <sys/stat.h>
4#include <sys/wait.h>
5#include <fcntl.h>
6#include <poll.h>
7#include <unistd.h>
8#include "e.h"
9#include "die.h"
10#include "load.h"
11#include "open.h"
12#include "byte.h"
13#include "socket.h"
14#include "uint64_pack.h"
15#include "uint64_unpack.h"
16#include "nanoseconds.h"
17#include "hexparse.h"
18#include "nameparse.h"
19#include "portparse.h"
20#include "writeall.h"
21#include "safenonce.h"
22#include "randommod.h"
23
24long long recent = 0;
25
26#define NUMIP 8
27long long hellowait[NUMIP] = {
28 1000000000
29, 1500000000
30, 2250000000
31, 3375000000
32, 5062500000
33, 7593750000
34, 11390625000
35, 17085937500
36} ;
37
38#include "crypto_box.h"
39#include "randombytes.h"
40#if crypto_box_PUBLICKEYBYTES != 32
41error!
42#endif
43#if crypto_box_NONCEBYTES != 24
44error!
45#endif
46#if crypto_box_BOXZEROBYTES != 16
47error!
48#endif
49#if crypto_box_ZEROBYTES != 32
50error!
51#endif
52#if crypto_box_BEFORENMBYTES != 32
53error!
54#endif
55
56int flagverbose = 1;
57
58#define USAGE "\
59curvecpclient: how to use:\n\
60curvecpclient: -q (optional): no error messages\n\
61curvecpclient: -Q (optional): print error messages (default)\n\
62curvecpclient: -v (optional): print extra information\n\
63curvecpclient: -c keydir (optional): use this public-key directory\n\
64curvecpclient: sname: server's name\n\
65curvecpclient: pk: server's public key\n\
66curvecpclient: ip: server's IP address\n\
67curvecpclient: port: server's UDP port\n\
68curvecpclient: ext: server's extension\n\
69curvecpclient: prog: run this client\n\
70"
71
72void die_usage(const char *s)
73{
74 if (s) die_4(100,USAGE,"curvecpclient: fatal: ",s,"\n");
75 die_1(100,USAGE);
76}
77
78void die_fatal(const char *trouble,const char *d,const char *fn)
79{
80 /* XXX: clean up? OS can do it much more reliably */
81 if (!flagverbose) die_0(111);
82 if (d) {
83 if (fn) die_9(111,"curvecpclient: fatal: ",trouble," ",d,"/",fn,": ",e_str(errno),"\n");
84 die_7(111,"curvecpclient: fatal: ",trouble," ",d,": ",e_str(errno),"\n");
85 }
86 if (errno) die_5(111,"curvecpclient: fatal: ",trouble,": ",e_str(errno),"\n");
87 die_3(111,"curvecpclient: fatal: ",trouble,"\n");
88}
89
90int multiipparse(unsigned char *y,const char *x)
91{
92 long long pos;
93 long long pos2;
94 long long ynum;
95 long long ypos;
96 long long j;
97 long long k;
98 long long d;
99 for (j = 0;j < 4 * NUMIP;++j) y[j] = 0;
100 ynum = 0;
101 while (ynum < 1000) {
102 ++ynum;
103 ypos = randommod(ynum);
104 for (k = 0;k < 4;++k) {
105 pos = ypos * 4 + k;
106 pos2 = (ynum - 1) * 4 + k;
107 if (pos >= 0 && pos < 4 * NUMIP && pos2 >= 0 && pos2 < 4 * NUMIP) y[pos2] = y[pos];
108 d = 0;
109 for (j = 0;j < 3 && x[j] >= '0' && x[j] <= '9';++j) d = d * 10 + (x[j] - '0');
110 if (j == 0) return 0;
111 x += j;
112 if (pos >= 0 && pos < 4 * NUMIP) y[pos] = d;
113 if (k < 3) {
114 if (*x != '.') return 0;
115 ++x;
116 }
117 }
118 if (!*x) break;
119 if (*x != ',') return 0;
120 ++x;
121 }
122 /* if fewer than 8 IP addresses, cycle through them: */
123 pos = 0;
124 pos2 = ynum * 4;
125 while (pos2 < 4 * NUMIP) {
126 if (pos >= 0 && pos < 4 * NUMIP && pos2 >= 0 && pos2 < 4 * NUMIP) y[pos2] = y[pos];
127 ++pos2;
128 ++pos;
129 }
130 return 1;
131}
132
133
134/* routing to the client: */
135unsigned char clientextension[16];
136long long clientextensionloadtime = 0;
137int udpfd = -1;
138
139void clientextension_init(void)
140{
141 if (recent >= clientextensionloadtime) {
142 clientextensionloadtime = recent + 30000000000LL;
143 if (load("/etc/curvecpextension",clientextension,16) == -1)
144 if (errno == ENOENT || errno == ENAMETOOLONG)
145 byte_zero(clientextension,16);
146 }
147}
148
149
150/* client security: */
151char *keydir = 0;
152unsigned char clientlongtermpk[32];
153unsigned char clientlongtermsk[32];
154unsigned char clientshorttermpk[32];
155unsigned char clientshorttermsk[32];
156crypto_uint64 clientshorttermnonce;
157unsigned char vouch[64];
158
159void clientshorttermnonce_update(void)
160{
161 ++clientshorttermnonce;
162 if (clientshorttermnonce) return;
163 errno = EPROTO;
164 die_fatal("nonce space expired",0,0);
165}
166
167/* routing to the server: */
168unsigned char serverip[4 * NUMIP];
169unsigned char serverport[2];
170unsigned char serverextension[16];
171
172/* server security: */
173unsigned char servername[256];
174unsigned char serverlongtermpk[32];
175unsigned char servershorttermpk[32];
176unsigned char servercookie[96];
177
178/* shared secrets: */
179unsigned char clientshortserverlong[32];
180unsigned char clientshortservershort[32];
181unsigned char clientlongserverlong[32];
182
183unsigned char allzero[128] = {0};
184
185unsigned char nonce[24];
186unsigned char text[2048];
187
188unsigned char packet[4096];
189unsigned char packetip[4];
190unsigned char packetport[2];
191crypto_uint64 packetnonce;
192int flagreceivedmessage = 0;
193crypto_uint64 receivednonce = 0;
194
195struct pollfd p[3];
196
197int fdwd = -1;
198
199int tochild[2] = {-1,-1};
200int fromchild[2] = {-1,-1};
201pid_t child = -1;
202int childstatus = 0;
203
204unsigned char childbuf[4096];
205long long childbuflen = 0;
206unsigned char childmessage[2048];
207long long childmessagelen = 0;
208
209int main(int argc,char **argv)
210{
211 long long hellopackets;
212 long long r;
213 long long nextaction;
214
215 signal(SIGPIPE,SIG_IGN);
216
217 if (!argv[0]) die_usage(0);
218 for (;;) {
219 char *x;
220 if (!argv[1]) break;
221 if (argv[1][0] != '-') break;
222 x = *++argv;
223 if (x[0] == '-' && x[1] == 0) break;
224 if (x[0] == '-' && x[1] == '-' && x[2] == 0) break;
225 while (*++x) {
226 if (*x == 'q') { flagverbose = 0; continue; }
227 if (*x == 'Q') { flagverbose = 1; continue; }
228 if (*x == 'v') { if (flagverbose == 2) flagverbose = 3; else flagverbose = 2; continue; }
229 if (*x == 'c') {
230 if (x[1]) { keydir = x + 1; break; }
231 if (argv[1]) { keydir = *++argv; break; }
232 }
233 die_usage(0);
234 }
235 }
236 if (!nameparse(servername,*++argv)) die_usage("sname must be at most 255 bytes, at most 63 bytes between dots");
237 if (!hexparse(serverlongtermpk,32,*++argv)) die_usage("pk must be exactly 64 hex characters");
238 if (!multiipparse(serverip,*++argv)) die_usage("ip must be a comma-separated series of IPv4 addresses");
239 if (!portparse(serverport,*++argv)) die_usage("port must be an integer between 0 and 65535");
240 if (!hexparse(serverextension,16,*++argv)) die_usage("ext must be exactly 32 hex characters");
241 if (!*++argv) die_usage("missing prog");
242
243 for (;;) {
244 r = open_read("/dev/null");
245 if (r == -1) die_fatal("unable to open /dev/null",0,0);
246 if (r > 9) { close(r); break; }
247 }
248
249 if (keydir) {
250 fdwd = open_cwd();
251 if (fdwd == -1) die_fatal("unable to open current working directory",0,0);
252 if (chdir(keydir) == -1) die_fatal("unable to change to directory",keydir,0);
253 if (load("publickey",clientlongtermpk,sizeof clientlongtermpk) == -1) die_fatal("unable to read public key from",keydir,0);
254 if (load(".expertsonly/secretkey",clientlongtermsk,sizeof clientlongtermsk) == -1) die_fatal("unable to read secret key from",keydir,0);
255 } else {
256 crypto_box_keypair(clientlongtermpk,clientlongtermsk);
257 }
258
259 crypto_box_keypair(clientshorttermpk,clientshorttermsk);
260 clientshorttermnonce = randommod(281474976710656LL);
261 crypto_box_beforenm(clientshortserverlong,serverlongtermpk,clientshorttermsk);
262 crypto_box_beforenm(clientlongserverlong,serverlongtermpk,clientlongtermsk);
263
264 udpfd = socket_udp();
265 if (udpfd == -1) die_fatal("unable to create socket",0,0);
266
267 for (hellopackets = 0;hellopackets < NUMIP;++hellopackets) {
268 recent = nanoseconds();
269
270 /* send a Hello packet: */
271
272 clientextension_init();
273
274 clientshorttermnonce_update();
275 byte_copy(nonce,16,"CurveCP-client-H");
276 uint64_pack(nonce + 16,clientshorttermnonce);
277
278 byte_copy(packet,8,"QvnQ5XlH");
279 byte_copy(packet + 8,16,serverextension);
280 byte_copy(packet + 24,16,clientextension);
281 byte_copy(packet + 40,32,clientshorttermpk);
282 byte_copy(packet + 72,64,allzero);
283 byte_copy(packet + 136,8,nonce + 16);
284 crypto_box_afternm(text,allzero,96,nonce,clientshortserverlong);
285 byte_copy(packet + 144,80,text + 16);
286
287 socket_send(udpfd,packet,224,serverip + 4 * hellopackets,serverport);
288
289 nextaction = recent + hellowait[hellopackets] + randommod(hellowait[hellopackets]);
290
291 for (;;) {
292 long long timeout = nextaction - recent;
293 if (timeout <= 0) break;
294 p[0].fd = udpfd;
295 p[0].events = POLLIN;
296 if (poll(p,1,timeout / 1000000 + 1) < 0) p[0].revents = 0;
297
298 do { /* try receiving a Cookie packet: */
299 if (!p[0].revents) break;
300 r = socket_recv(udpfd,packet,sizeof packet,packetip,packetport);
301 if (r != 200) break;
302 if (!(byte_isequal(packetip,4,serverip + 4 * hellopackets) &
303 byte_isequal(packetport,2,serverport) &
304 byte_isequal(packet,8,"RL3aNMXK") &
305 byte_isequal(packet + 8,16,clientextension) &
306 byte_isequal(packet + 24,16,serverextension)
307 )) break;
308 byte_copy(nonce,8,"CurveCPK");
309 byte_copy(nonce + 8,16,packet + 40);
310 byte_zero(text,16);
311 byte_copy(text + 16,144,packet + 56);
312 if (crypto_box_open_afternm(text,text,160,nonce,clientshortserverlong)) break;
313 byte_copy(servershorttermpk,32,text + 32);
314 byte_copy(servercookie,96,text + 64);
315 byte_copy(serverip,4,serverip + 4 * hellopackets);
316 goto receivedcookie;
317 } while (0);
318
319 recent = nanoseconds();
320 }
321 }
322
323 errno = ETIMEDOUT; die_fatal("no response from server",0,0);
324
325 receivedcookie:
326
327 crypto_box_beforenm(clientshortservershort,servershorttermpk,clientshorttermsk);
328
329 byte_copy(nonce,8,"CurveCPV");
330 if (keydir) {
331 if (safenonce(nonce + 8,0) == -1) die_fatal("nonce-generation disaster",0,0);
332 } else {
333 randombytes(nonce + 8,16);
334 }
335
336 byte_zero(text,32);
337 byte_copy(text + 32,32,clientshorttermpk);
338 crypto_box_afternm(text,text,64,nonce,clientlongserverlong);
339 byte_copy(vouch,16,nonce + 8);
340 byte_copy(vouch + 16,48,text + 16);
341
342 /* server is responding, so start child: */
343
344 if (open_pipe(tochild) == -1) die_fatal("unable to create pipe",0,0);
345 if (open_pipe(fromchild) == -1) die_fatal("unable to create pipe",0,0);
346
347 child = fork();
348 if (child == -1) die_fatal("unable to fork",0,0);
349 if (child == 0) {
350 if (keydir) if (fchdir(fdwd) == -1) die_fatal("unable to chdir to original directory",0,0);
351 close(8);
352 if (dup(tochild[0]) != 8) die_fatal("unable to dup",0,0);
353 close(9);
354 if (dup(fromchild[1]) != 9) die_fatal("unable to dup",0,0);
355 /* XXX: set up environment variables */
356 signal(SIGPIPE,SIG_DFL);
357 execvp(*argv,argv);
358 die_fatal("unable to run",*argv,0);
359 }
360
361 close(fromchild[1]);
362 close(tochild[0]);
363
364
365 for (;;) {
366 p[0].fd = udpfd;
367 p[0].events = POLLIN;
368 p[1].fd = fromchild[0];
369 p[1].events = POLLIN;
370
371 if (poll(p,2,-1) < 0) {
372 p[0].revents = 0;
373 p[1].revents = 0;
374 }
375
376 do { /* try receiving a Message packet: */
377 if (!p[0].revents) break;
378 r = socket_recv(udpfd,packet,sizeof packet,packetip,packetport);
379 if (r < 80) break;
380 if (r > 1152) break;
381 if (r & 15) break;
382 packetnonce = uint64_unpack(packet + 40);
383 if (flagreceivedmessage && packetnonce <= receivednonce) break;
384 if (!(byte_isequal(packetip,4,serverip + 4 * hellopackets) &
385 byte_isequal(packetport,2,serverport) &
386 byte_isequal(packet,8,"RL3aNMXM") &
387 byte_isequal(packet + 8,16,clientextension) &
388 byte_isequal(packet + 24,16,serverextension)
389 )) break;
390 byte_copy(nonce,16,"CurveCP-server-M");
391 byte_copy(nonce + 16,8,packet + 40);
392 byte_zero(text,16);
393 byte_copy(text + 16,r - 48,packet + 48);
394 if (crypto_box_open_afternm(text,text,r - 32,nonce,clientshortservershort)) break;
395
396 if (!flagreceivedmessage) {
397 flagreceivedmessage = 1;
398 randombytes(clientlongtermpk,sizeof clientlongtermpk);
399 randombytes(vouch,sizeof vouch);
400 randombytes(servername,sizeof servername);
401 randombytes(servercookie,sizeof servercookie);
402 }
403
404 receivednonce = packetnonce;
405 text[31] = (r - 64) >> 4;
406 /* child is responsible for reading all data immediately, so we won't block: */
407 if (writeall(tochild[1],text + 31,r - 63) == -1) goto done;
408 } while (0);
409
410 do { /* try receiving data from child: */
411 long long i;
412 if (!p[1].revents) break;
413 r = read(fromchild[0],childbuf,sizeof childbuf);
414 if (r == -1) if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) break;
415 if (r <= 0) goto done;
416 childbuflen = r;
417 for (i = 0;i < childbuflen;++i) {
418 if (childmessagelen < 0) goto done;
419 if (childmessagelen >= sizeof childmessage) goto done;
420 childmessage[childmessagelen++] = childbuf[i];
421 if (childmessage[0] & 128) goto done;
422 if (childmessagelen == 1 + 16 * (unsigned long long) childmessage[0]) {
423 clientextension_init();
424 clientshorttermnonce_update();
425 uint64_pack(nonce + 16,clientshorttermnonce);
426 if (flagreceivedmessage) {
427 r = childmessagelen - 1;
428 if (r < 16) goto done;
429 if (r > 1088) goto done;
430 byte_copy(nonce,16,"CurveCP-client-M");
431 byte_zero(text,32);
432 byte_copy(text + 32,r,childmessage + 1);
433 crypto_box_afternm(text,text,r + 32,nonce,clientshortservershort);
434 byte_copy(packet,8,"QvnQ5XlM");
435 byte_copy(packet + 8,16,serverextension);
436 byte_copy(packet + 24,16,clientextension);
437 byte_copy(packet + 40,32,clientshorttermpk);
438 byte_copy(packet + 72,8,nonce + 16);
439 byte_copy(packet + 80,r + 16,text + 16);
440 socket_send(udpfd,packet,r + 96,serverip,serverport);
441 } else {
442 r = childmessagelen - 1;
443 if (r < 16) goto done;
444 if (r > 640) goto done;
445 byte_copy(nonce,16,"CurveCP-client-I");
446 byte_zero(text,32);
447 byte_copy(text + 32,32,clientlongtermpk);
448 byte_copy(text + 64,64,vouch);
449 byte_copy(text + 128,256,servername);
450 byte_copy(text + 384,r,childmessage + 1);
451 crypto_box_afternm(text,text,r + 384,nonce,clientshortservershort);
452 byte_copy(packet,8,"QvnQ5XlI");
453 byte_copy(packet + 8,16,serverextension);
454 byte_copy(packet + 24,16,clientextension);
455 byte_copy(packet + 40,32,clientshorttermpk);
456 byte_copy(packet + 72,96,servercookie);
457 byte_copy(packet + 168,8,nonce + 16);
458 byte_copy(packet + 176,r + 368,text + 16);
459 socket_send(udpfd,packet,r + 544,serverip,serverport);
460 }
461 childmessagelen = 0;
462 }
463 }
464 } while (0);
465 }
466
467
468 done:
469
470 do {
471 r = waitpid(child,&childstatus,0);
472 } while (r == -1 && errno == EINTR);
473
474 if (!WIFEXITED(childstatus)) { errno = 0; die_fatal("process killed by signal",0,0); }
475 return WEXITSTATUS(childstatus);
476}