Mercurial > dropbear
comparison common-algo.c @ 1683:41bf8f216644
merge rsa-sha256
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Tue, 26 May 2020 00:24:02 +0800 |
parents | e0871128e61f 435cfb9ec96e |
children | c2c0f43ff827 |
comparison
equal
deleted
inserted
replaced
1673:e0871128e61f | 1683:41bf8f216644 |
---|---|
30 #include "dh_groups.h" | 30 #include "dh_groups.h" |
31 #include "ltc_prng.h" | 31 #include "ltc_prng.h" |
32 #include "ecc.h" | 32 #include "ecc.h" |
33 #include "gcm.h" | 33 #include "gcm.h" |
34 #include "chachapoly.h" | 34 #include "chachapoly.h" |
35 #include "ssh.h" | |
35 | 36 |
36 /* This file (algo.c) organises the ciphers which can be used, and is used to | 37 /* This file (algo.c) organises the ciphers which can be used, and is used to |
37 * decide which ciphers/hashes/compression/signing to use during key exchange*/ | 38 * decide which ciphers/hashes/compression/signing to use during key exchange*/ |
38 | 39 |
39 static int void_cipher(const unsigned char* in, unsigned char* out, | 40 static int void_cipher(const unsigned char* in, unsigned char* out, |
240 algo_type ssh_nocompress[] = { | 241 algo_type ssh_nocompress[] = { |
241 {"none", DROPBEAR_COMP_NONE, NULL, 1, NULL}, | 242 {"none", DROPBEAR_COMP_NONE, NULL, 1, NULL}, |
242 {NULL, 0, NULL, 0, NULL} | 243 {NULL, 0, NULL, 0, NULL} |
243 }; | 244 }; |
244 | 245 |
245 algo_type sshhostkey[] = { | 246 algo_type sigalgs[] = { |
246 #if DROPBEAR_ED25519 | 247 #if DROPBEAR_ED25519 |
247 {"ssh-ed25519", DROPBEAR_SIGNKEY_ED25519, NULL, 1, NULL}, | 248 {"ssh-ed25519", DROPBEAR_SIGNATURE_ED25519, NULL, 1, NULL}, |
248 #endif | 249 #endif |
249 #if DROPBEAR_ECDSA | 250 #if DROPBEAR_ECDSA |
250 #if DROPBEAR_ECC_256 | 251 #if DROPBEAR_ECC_256 |
251 {"ecdsa-sha2-nistp256", DROPBEAR_SIGNKEY_ECDSA_NISTP256, NULL, 1, NULL}, | 252 {"ecdsa-sha2-nistp256", DROPBEAR_SIGNATURE_ECDSA_NISTP256, NULL, 1, NULL}, |
252 #endif | 253 #endif |
253 #if DROPBEAR_ECC_384 | 254 #if DROPBEAR_ECC_384 |
254 {"ecdsa-sha2-nistp384", DROPBEAR_SIGNKEY_ECDSA_NISTP384, NULL, 1, NULL}, | 255 {"ecdsa-sha2-nistp384", DROPBEAR_SIGNATURE_ECDSA_NISTP384, NULL, 1, NULL}, |
255 #endif | 256 #endif |
256 #if DROPBEAR_ECC_521 | 257 #if DROPBEAR_ECC_521 |
257 {"ecdsa-sha2-nistp521", DROPBEAR_SIGNKEY_ECDSA_NISTP521, NULL, 1, NULL}, | 258 {"ecdsa-sha2-nistp521", DROPBEAR_SIGNATURE_ECDSA_NISTP521, NULL, 1, NULL}, |
258 #endif | 259 #endif |
259 #endif | 260 #endif |
260 #if DROPBEAR_RSA | 261 #if DROPBEAR_RSA |
261 {"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1, NULL}, | 262 #if DROPBEAR_RSA_SHA256 |
263 {"rsa-sha2-256", DROPBEAR_SIGNATURE_RSA_SHA256, NULL, 1, NULL}, | |
264 #endif | |
265 #if DROPBEAR_RSA_SHA1 | |
266 {"ssh-rsa", DROPBEAR_SIGNATURE_RSA_SHA1, NULL, 1, NULL}, | |
267 #endif | |
262 #endif | 268 #endif |
263 #if DROPBEAR_DSS | 269 #if DROPBEAR_DSS |
264 {"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1, NULL}, | 270 {"ssh-dss", DROPBEAR_SIGNATURE_DSS, NULL, 1, NULL}, |
265 #endif | 271 #endif |
266 {NULL, 0, NULL, 0, NULL} | 272 {NULL, 0, NULL, 0, NULL} |
267 }; | 273 }; |
268 | 274 |
269 #if DROPBEAR_DH_GROUP1 | 275 #if DROPBEAR_DH_GROUP1 |
277 #endif | 283 #endif |
278 #if DROPBEAR_DH_GROUP16 | 284 #if DROPBEAR_DH_GROUP16 |
279 static const struct dropbear_kex kex_dh_group16_sha512 = {DROPBEAR_KEX_NORMAL_DH, dh_p_16, DH_P_16_LEN, NULL, &sha512_desc }; | 285 static const struct dropbear_kex kex_dh_group16_sha512 = {DROPBEAR_KEX_NORMAL_DH, dh_p_16, DH_P_16_LEN, NULL, &sha512_desc }; |
280 #endif | 286 #endif |
281 | 287 |
282 /* These can't be const since dropbear_ecc_fill_dp() fills out | |
283 ecc_curve at runtime */ | |
284 #if DROPBEAR_ECDH | 288 #if DROPBEAR_ECDH |
285 #if DROPBEAR_ECC_256 | 289 #if DROPBEAR_ECC_256 |
286 static const struct dropbear_kex kex_ecdh_nistp256 = {DROPBEAR_KEX_ECDH, NULL, 0, &ecc_curve_nistp256, &sha256_desc }; | 290 static const struct dropbear_kex kex_ecdh_nistp256 = {DROPBEAR_KEX_ECDH, NULL, 0, &ecc_curve_nistp256, &sha256_desc }; |
287 #endif | 291 #endif |
288 #if DROPBEAR_ECC_384 | 292 #if DROPBEAR_ECC_384 |
296 #if DROPBEAR_CURVE25519 | 300 #if DROPBEAR_CURVE25519 |
297 /* Referred to directly */ | 301 /* Referred to directly */ |
298 static const struct dropbear_kex kex_curve25519 = {DROPBEAR_KEX_CURVE25519, NULL, 0, NULL, &sha256_desc }; | 302 static const struct dropbear_kex kex_curve25519 = {DROPBEAR_KEX_CURVE25519, NULL, 0, NULL, &sha256_desc }; |
299 #endif | 303 #endif |
300 | 304 |
305 /* data == NULL for non-kex algorithm identifiers */ | |
301 algo_type sshkex[] = { | 306 algo_type sshkex[] = { |
302 #if DROPBEAR_CURVE25519 | 307 #if DROPBEAR_CURVE25519 |
303 {"curve25519-sha256", 0, &kex_curve25519, 1, NULL}, | 308 {"curve25519-sha256", 0, &kex_curve25519, 1, NULL}, |
304 {"[email protected]", 0, &kex_curve25519, 1, NULL}, | 309 {"[email protected]", 0, &kex_curve25519, 1, NULL}, |
305 #endif | 310 #endif |
325 #endif | 330 #endif |
326 #if DROPBEAR_DH_GROUP16 | 331 #if DROPBEAR_DH_GROUP16 |
327 {"diffie-hellman-group16-sha512", 0, &kex_dh_group16_sha512, 1, NULL}, | 332 {"diffie-hellman-group16-sha512", 0, &kex_dh_group16_sha512, 1, NULL}, |
328 #endif | 333 #endif |
329 #if DROPBEAR_KEXGUESS2 | 334 #if DROPBEAR_KEXGUESS2 |
330 {KEXGUESS2_ALGO_NAME, KEXGUESS2_ALGO_ID, NULL, 1, NULL}, | 335 {KEXGUESS2_ALGO_NAME, 0, NULL, 1, NULL}, |
336 #endif | |
337 #if DROPBEAR_EXT_INFO | |
338 #if DROPBEAR_CLIENT | |
339 /* Set unusable by svr_algos_initialise() */ | |
340 {SSH_EXT_INFO_C, 0, NULL, 1, NULL}, | |
341 #endif | |
331 #endif | 342 #endif |
332 {NULL, 0, NULL, 0, NULL} | 343 {NULL, 0, NULL, 0, NULL} |
333 }; | 344 }; |
334 | 345 |
335 /* algolen specifies the length of algo, algos is our local list to match | |
336 * against. | |
337 * Returns DROPBEAR_SUCCESS if we have a match for algo, DROPBEAR_FAILURE | |
338 * otherwise */ | |
339 int have_algo(const char* algo, size_t algolen, const algo_type algos[]) { | |
340 | |
341 int i; | |
342 | |
343 for (i = 0; algos[i].name != NULL; i++) { | |
344 if (strlen(algos[i].name) == algolen | |
345 && (strncmp(algos[i].name, algo, algolen) == 0)) { | |
346 return DROPBEAR_SUCCESS; | |
347 } | |
348 } | |
349 | |
350 return DROPBEAR_FAILURE; | |
351 } | |
352 | |
353 /* Output a comma separated list of algorithms to a buffer */ | 346 /* Output a comma separated list of algorithms to a buffer */ |
354 void buf_put_algolist(buffer * buf, const algo_type localalgos[]) { | 347 void buf_put_algolist_all(buffer * buf, const algo_type localalgos[], int useall) { |
355 | |
356 unsigned int i, len; | 348 unsigned int i, len; |
357 unsigned int donefirst = 0; | 349 unsigned int donefirst = 0; |
358 buffer *algolist = NULL; | 350 unsigned int startpos; |
359 | 351 |
360 algolist = buf_new(300); | 352 startpos = buf->pos; |
353 /* Placeholder for length */ | |
354 buf_putint(buf, 0); | |
361 for (i = 0; localalgos[i].name != NULL; i++) { | 355 for (i = 0; localalgos[i].name != NULL; i++) { |
362 if (localalgos[i].usable) { | 356 if (localalgos[i].usable || useall) { |
363 if (donefirst) | 357 if (donefirst) { |
364 buf_putbyte(algolist, ','); | 358 buf_putbyte(buf, ','); |
359 } | |
365 donefirst = 1; | 360 donefirst = 1; |
366 len = strlen(localalgos[i].name); | 361 len = strlen(localalgos[i].name); |
367 buf_putbytes(algolist, (const unsigned char *) localalgos[i].name, len); | 362 buf_putbytes(buf, (const unsigned char *) localalgos[i].name, len); |
368 } | 363 } |
369 } | 364 } |
370 buf_putstring(buf, (const char*)algolist->data, algolist->len); | 365 /* Fill out the length */ |
371 TRACE(("algolist add '%*s'", algolist->len, algolist->data)) | 366 len = buf->pos - startpos - 4; |
372 buf_free(algolist); | 367 buf_setpos(buf, startpos); |
368 buf_putint(buf, len); | |
369 TRACE(("algolist add %d '%*s'", len, len, buf_getptr(buf, len))) | |
370 buf_incrwritepos(buf, len); | |
371 } | |
372 | |
373 void buf_put_algolist(buffer * buf, const algo_type localalgos[]) { | |
374 buf_put_algolist_all(buf, localalgos, 0); | |
375 } | |
376 | |
377 /* returns a list of pointers into algolist, of null-terminated names. | |
378 ret_list should be passed in with space for *ret_count elements, | |
379 on return *ret_count has the number of names filled. | |
380 algolist is modified. */ | |
381 static void get_algolist(char* algolist, unsigned int algolist_len, | |
382 const char* *ret_list, unsigned int *ret_count) { | |
383 unsigned int max_count = *ret_count; | |
384 unsigned int i; | |
385 | |
386 if (*ret_count == 0) { | |
387 return; | |
388 } | |
389 if (algolist_len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) { | |
390 *ret_count = 0; | |
391 } | |
392 | |
393 /* ret_list will contain a list of the strings parsed out. | |
394 We will have at least one string (even if it's just "") */ | |
395 ret_list[0] = algolist; | |
396 *ret_count = 1; | |
397 for (i = 0; i < algolist_len; i++) { | |
398 if (algolist[i] == '\0') { | |
399 /* someone is trying something strange */ | |
400 *ret_count = 0; | |
401 return; | |
402 } | |
403 | |
404 if (algolist[i] == ',') { | |
405 if (*ret_count >= max_count) { | |
406 /* Too many */ | |
407 *ret_count = 0; | |
408 return; | |
409 } | |
410 algolist[i] = '\0'; | |
411 ret_list[*ret_count] = &algolist[i+1]; | |
412 (*ret_count)++; | |
413 } | |
414 } | |
415 } | |
416 | |
417 /* Return DROPBEAR_SUCCESS if the namelist contains algo, | |
418 DROPBEAR_FAILURE otherwise. buf position is not incremented. */ | |
419 int buf_has_algo(buffer *buf, const char *algo) { | |
420 unsigned char* algolist = NULL; | |
421 unsigned int orig_pos = buf->pos; | |
422 unsigned int len, remotecount, i; | |
423 const char *remotenames[MAX_PROPOSED_ALGO]; | |
424 int ret = DROPBEAR_FAILURE; | |
425 | |
426 algolist = buf_getstring(buf, &len); | |
427 remotecount = MAX_PROPOSED_ALGO; | |
428 get_algolist(algolist, len, remotenames, &remotecount); | |
429 for (i = 0; i < remotecount; i++) | |
430 { | |
431 if (strcmp(remotenames[i], algo) == 0) { | |
432 ret = DROPBEAR_SUCCESS; | |
433 break; | |
434 } | |
435 } | |
436 if (algolist) { | |
437 m_free(algolist); | |
438 } | |
439 buf_setpos(buf, orig_pos); | |
440 return ret; | |
441 } | |
442 | |
443 algo_type * first_usable_algo(algo_type algos[]) { | |
444 int i; | |
445 for (i = 0; algos[i].name != NULL; i++) { | |
446 if (algos[i].usable) { | |
447 return &algos[i]; | |
448 } | |
449 } | |
450 return NULL; | |
373 } | 451 } |
374 | 452 |
375 /* match the first algorithm in the comma-separated list in buf which is | 453 /* match the first algorithm in the comma-separated list in buf which is |
376 * also in localalgos[], or return NULL on failure. | 454 * also in localalgos[], or return NULL on failure. |
377 * (*goodguess) is set to 1 if the preferred client/server algos match, | 455 * (*goodguess) is set to 1 if the preferred client/server algos match, |
378 * 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are | 456 * 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are |
379 * guessed correctly */ | 457 * guessed correctly */ |
380 algo_type * buf_match_algo(buffer* buf, algo_type localalgos[], | 458 algo_type * buf_match_algo(buffer* buf, algo_type localalgos[], |
381 enum kexguess2_used *kexguess2, int *goodguess) | 459 int kexguess2, int *goodguess) { |
382 { | |
383 | |
384 char * algolist = NULL; | 460 char * algolist = NULL; |
385 const char *remotenames[MAX_PROPOSED_ALGO], *localnames[MAX_PROPOSED_ALGO]; | 461 const char *remotenames[MAX_PROPOSED_ALGO], *localnames[MAX_PROPOSED_ALGO]; |
386 unsigned int len; | 462 unsigned int len; |
387 unsigned int remotecount, localcount, clicount, servcount, i, j; | 463 unsigned int remotecount, localcount, clicount, servcount, i, j; |
388 algo_type * ret = NULL; | 464 algo_type * ret = NULL; |
393 } | 469 } |
394 | 470 |
395 /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */ | 471 /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */ |
396 algolist = buf_getstring(buf, &len); | 472 algolist = buf_getstring(buf, &len); |
397 TRACE(("buf_match_algo: %s", algolist)) | 473 TRACE(("buf_match_algo: %s", algolist)) |
398 if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) { | 474 remotecount = MAX_PROPOSED_ALGO; |
399 goto out; | 475 get_algolist(algolist, len, remotenames, &remotecount); |
400 } | |
401 | |
402 /* remotenames will contain a list of the strings parsed out */ | |
403 /* We will have at least one string (even if it's just "") */ | |
404 remotenames[0] = algolist; | |
405 remotecount = 1; | |
406 for (i = 0; i < len; i++) { | |
407 if (algolist[i] == '\0') { | |
408 /* someone is trying something strange */ | |
409 goto out; | |
410 } | |
411 if (algolist[i] == ',') { | |
412 algolist[i] = '\0'; | |
413 remotenames[remotecount] = &algolist[i+1]; | |
414 remotecount++; | |
415 } | |
416 if (remotecount >= MAX_PROPOSED_ALGO) { | |
417 break; | |
418 } | |
419 } | |
420 if (kexguess2 && *kexguess2 == KEXGUESS2_LOOK) { | |
421 for (i = 0; i < remotecount; i++) | |
422 { | |
423 if (strcmp(remotenames[i], KEXGUESS2_ALGO_NAME) == 0) { | |
424 *kexguess2 = KEXGUESS2_YES; | |
425 break; | |
426 } | |
427 } | |
428 if (*kexguess2 == KEXGUESS2_LOOK) { | |
429 *kexguess2 = KEXGUESS2_NO; | |
430 } | |
431 } | |
432 | 476 |
433 for (i = 0; localalgos[i].name != NULL; i++) { | 477 for (i = 0; localalgos[i].name != NULL; i++) { |
434 if (localalgos[i].usable) { | 478 if (localalgos[i].usable) { |
435 localnames[i] = localalgos[i].name; | 479 localnames[i] = localalgos[i].name; |
436 } else { | 480 } else { |
458 /* unusable algos are NULL */ | 502 /* unusable algos are NULL */ |
459 continue; | 503 continue; |
460 } | 504 } |
461 if (strcmp(servnames[j], clinames[i]) == 0) { | 505 if (strcmp(servnames[j], clinames[i]) == 0) { |
462 /* set if it was a good guess */ | 506 /* set if it was a good guess */ |
463 if (goodguess && kexguess2) { | 507 if (goodguess != NULL) { |
464 if (*kexguess2 == KEXGUESS2_YES) { | 508 if (kexguess2) { |
465 if (i == 0) { | 509 if (i == 0) { |
466 *goodguess = 1; | 510 *goodguess = 1; |
467 } | 511 } |
468 | |
469 } else { | 512 } else { |
470 if (i == 0 && j == 0) { | 513 if (i == 0 && j == 0) { |
471 *goodguess = 1; | 514 *goodguess = 1; |
472 } | 515 } |
473 } | 516 } |