aboutsummaryrefslogtreecommitdiff
path: root/src/util/crypto_rsa.c
diff options
context:
space:
mode:
authorJeff Burdges <burdges@gnunet.org>2016-05-30 15:54:56 +0000
committerJeff Burdges <burdges@gnunet.org>2016-05-30 15:54:56 +0000
commitafb40a6d7a49d2608b709d6e8863675a6a301c99 (patch)
tree26c97c0217311d2313ecac5daa853d428ecf9025 /src/util/crypto_rsa.c
parent295a7ab56564369098a12e2cc39fac0d5225c465 (diff)
downloadgnunet-afb40a6d7a49d2608b709d6e8863675a6a301c99.tar.gz
gnunet-afb40a6d7a49d2608b709d6e8863675a6a301c99.zip
Use a uniform random number mod an RSA composites for both
the blinding factor and the full domain hash. This resolves an attack against the blinding factor in Taler: There was a call to GNUNET_CRYPTO_kdf in bkey = rsa_blinding_key_derive (len, bks); that gives exactly len bits where len = GNUNET_CRYPTO_rsa_public_key_len (pkey); Now r = 2^(len-1)/pkey.n is the probability that a set high bit being okay, meaning bkey < pkey.n. It follows that (1-r)/2 of the time bkey > pkey.n making the effective bkey be bkey mod pkey.n = bkey - pkey.n so the effective bkey has its high bit set with probability r/2. We expect r to be close to 1/2 if the exchange is honest, but the exchange can choose r otherwise. In blind signing, the exchange sees B = bkey * S mod pkey.n On deposit, the exchange sees S so they can compute bkey' = B/S mod pkey.n for all B they recorded to see if bkey' has it's high bit set. Also, note the exchange can compute 1/S efficiently since they know the factors of pkey.n. I suppose that happens with probability r/(1+r) if its the wrong B, not completely sure. If otoh we've the right B, then we've the probability r/2 of a set high bit in the effective bkey. Interestingly, r^2-r has a maximum at the default r=1/2 anyways, giving the wrong and right probabilities 1/3 and 1/4, respectively. I fear this gives the exchange a meaningful fraction of a bit of information per coin involved in the transaction. It sounds damaging if numerous coins were involved. And it could run across transactions in some scenarios. I suspect we need a more uniform deterministic pseudo-random number generator for blinding factors. Just fyi, our old call to gcry_mpi_randomize had this same problem. I do not believe this caused a problem for the full domain hash, but we can fix it easily enough anyways.
Diffstat (limited to 'src/util/crypto_rsa.c')
-rw-r--r--src/util/crypto_rsa.c173
1 files changed, 44 insertions, 129 deletions
diff --git a/src/util/crypto_rsa.c b/src/util/crypto_rsa.c
index ab3ce6fe7..4415f20f6 100644
--- a/src/util/crypto_rsa.c
+++ b/src/util/crypto_rsa.c
@@ -400,31 +400,24 @@ GNUNET_CRYPTO_rsa_public_key_decode (const char *buf,
400 * @return the newly created blinding key 400 * @return the newly created blinding key
401 */ 401 */
402static struct RsaBlindingKey * 402static struct RsaBlindingKey *
403rsa_blinding_key_derive (unsigned int len, 403rsa_blinding_key_derive (const struct GNUNET_CRYPTO_RsaPublicKey *pkey,
404 const struct GNUNET_CRYPTO_RsaBlindingKeySecret *bks) 404 const struct GNUNET_CRYPTO_RsaBlindingKeySecret *bks)
405{ 405{
406 char *xts = "Blinding KDF extrator HMAC key"; /* Trusts bks' randomness more */
406 struct RsaBlindingKey *blind; 407 struct RsaBlindingKey *blind;
407 uint8_t buf[len / 8]; 408 gcry_mpi_t n;
408 int rc;
409 size_t rsize;
410 409
411 blind = GNUNET_new (struct RsaBlindingKey); 410 blind = GNUNET_new (struct RsaBlindingKey);
412 /* FIXME: #4483: actually derive key from bks! - Jeff, 411
413 check that you're happy with this!*/ 412 /* Extract the composite n from the RSA public key */
414 GNUNET_assert (GNUNET_YES == 413 GNUNET_assert( 0 == key_from_sexp (&n, pkey->sexp, "rsa", "n") );
415 GNUNET_CRYPTO_kdf (buf, 414 GNUNET_assert( 0 == gcry_mpi_get_flag(n, GCRYMPI_FLAG_OPAQUE) );
416 sizeof (buf), 415
417 "blinding-kdf", 416 GNUNET_CRYPTO_kdf_mod_mpi (&blind->r,
418 strlen ("blinding-kdf"), 417 n,
419 bks, 418 xts, strlen(xts),
420 sizeof (*bks), 419 bks, sizeof(*bks),
421 NULL, 0)); 420 "Blinding KDF");
422 rc = gcry_mpi_scan (&blind->r,
423 GCRYMPI_FMT_USG,
424 (const unsigned char *) buf,
425 sizeof (buf),
426 &rsize);
427 GNUNET_assert (0 == rc);
428 return blind; 421 return blind;
429} 422}
430 423
@@ -538,13 +531,10 @@ unsigned int
538GNUNET_CRYPTO_rsa_public_key_len (const struct GNUNET_CRYPTO_RsaPublicKey *key) 531GNUNET_CRYPTO_rsa_public_key_len (const struct GNUNET_CRYPTO_RsaPublicKey *key)
539{ 532{
540 gcry_mpi_t n; 533 gcry_mpi_t n;
541 int ret;
542 unsigned int rval; 534 unsigned int rval;
543 535
544 ret = key_from_sexp (&n, key->sexp, "rsa", "n"); 536 if (0 != key_from_sexp (&n, key->sexp, "rsa", "n"))
545 if (0 != ret) 537 { /* Not an RSA public key */
546 {
547 /* this is no public RSA key */
548 GNUNET_break (0); 538 GNUNET_break (0);
549 return 0; 539 return 0;
550 } 540 }
@@ -610,91 +600,33 @@ numeric_mpi_alloc_n_print (gcry_mpi_t v,
610 * @param hash initial hash of the message to sign 600 * @param hash initial hash of the message to sign
611 * @param pkey the public key of the signer 601 * @param pkey the public key of the signer
612 * @param rsize If not NULL, the number of bytes actually stored in buffer 602 * @param rsize If not NULL, the number of bytes actually stored in buffer
613 * @return libgcrypt error that to represent an allocation failure
614 */ 603 */
615/* FIXME: exported symbol without proper prefix... */ 604static void
616gcry_error_t
617rsa_full_domain_hash (gcry_mpi_t *r, 605rsa_full_domain_hash (gcry_mpi_t *r,
618 const struct GNUNET_HashCode *hash, 606 const struct GNUNET_HashCode *hash,
619 const struct GNUNET_CRYPTO_RsaPublicKey *pkey, 607 const struct GNUNET_CRYPTO_RsaPublicKey *pkey)
620 size_t *rsize)
621{ 608{
622 unsigned int i; 609 gcry_mpi_t n;
623 unsigned int nbits; 610 char *xts;
624 unsigned int nhashes; 611 size_t xts_len;
625 gcry_error_t rc; 612
626 char *buf; 613 /* Extract the composite n from the RSA public key */
627 size_t buf_len; 614 GNUNET_assert( 0 == key_from_sexp (&n, pkey->sexp, "rsa", "n") );
628 gcry_md_hd_t h; 615 GNUNET_assert( 0 == gcry_mpi_get_flag(n, GCRYMPI_FLAG_OPAQUE) );
629 gcry_md_hd_t h0; 616
630 struct GNUNET_HashCode *hs; 617 /* We key with the public denomination key as a homage to RSA-PSS by *
631 618 * Mihir Bellare and Phillip Rogaway. Doing this lowers the degree *
632 /* Uncomment the following to debug without using the full domain hash */ 619 * of the hypothetical polyomial-time attack on RSA-KTI created by a *
633 /* 620 * polynomial-time one-more forgary attack. Yey seeding! */
634 rc = gcry_mpi_scan (r, 621 xts_len = GNUNET_CRYPTO_rsa_public_key_encode (pkey, &xts);
635 GCRYMPI_FMT_USG, 622
636 (const unsigned char *)hash, 623 GNUNET_CRYPTO_kdf_mod_mpi (r,
637 sizeof(struct GNUNET_HashCode), 624 n,
638 rsize); 625 xts, xts_len,
639 return rc; 626 hash, sizeof(*hash),
640 */ 627 "RSA-FDA FTpsW!");
641 628
642 nbits = GNUNET_CRYPTO_rsa_public_key_len (pkey); 629 GNUNET_free (xts);
643 if (nbits < 512)
644 nbits = 512;
645
646 /* Already almost an HMAC since we consume a hash, so no GCRY_MD_FLAG_HMAC. */
647 rc = gcry_md_open (&h, GCRY_MD_SHA512, 0);
648 if (0 != rc)
649 return rc;
650
651 /* We seed with the public denomination key as a homage to RSA-PSS by *
652 * Mihir Bellare and Phillip Rogaway. Doing this lowers the degree *
653 * of the hypothetical polyomial-time attack on RSA-KTI created by a *
654 * polynomial-time one-more forgary attack. Yey seeding! */
655 buf_len = GNUNET_CRYPTO_rsa_public_key_encode (pkey, &buf);
656 gcry_md_write (h, buf, buf_len);
657 GNUNET_free (buf);
658
659 nhashes = (nbits-1) / (8 * sizeof(struct GNUNET_HashCode)) + 1;
660 hs = GNUNET_new_array (nhashes,
661 struct GNUNET_HashCode);
662 for (i=0; i<nhashes; i++)
663 {
664 gcry_md_write (h, hash, sizeof(struct GNUNET_HashCode));
665 rc = gcry_md_copy (&h0, h);
666 if (0 != rc)
667 {
668 gcry_md_close (h0);
669 break;
670 }
671 gcry_md_putc (h0, i % 256);
672 memcpy (&hs[i],
673 gcry_md_read (h0, GCRY_MD_SHA512),
674 sizeof(struct GNUNET_HashCode));
675 gcry_md_close (h0);
676 }
677 gcry_md_close (h);
678 if (0 != rc)
679 {
680 GNUNET_free (hs);
681 return rc;
682 }
683
684 rc = gcry_mpi_scan (r,
685 GCRYMPI_FMT_USG,
686 (const unsigned char *) hs,
687 nhashes * sizeof(struct GNUNET_HashCode),
688 rsize);
689 GNUNET_free (hs);
690 if (0 != rc)
691 return rc;
692
693 /* Do not allow *r to exceed n or signatures fail to verify unpredictably. *
694 * This happening with gcry_mpi_clear_highbit (*r, nbits-1) so maybe *
695 * gcry_mpi_clear_highbit is broken, but setting the highbit sounds good. */
696 gcry_mpi_set_highbit (*r, nbits-2);
697 return rc;
698} 630}
699 631
700 632
@@ -718,11 +650,8 @@ GNUNET_CRYPTO_rsa_blind (const struct GNUNET_HashCode *hash,
718 gcry_mpi_t ne[2]; 650 gcry_mpi_t ne[2];
719 gcry_mpi_t r_e; 651 gcry_mpi_t r_e;
720 gcry_mpi_t data_r_e; 652 gcry_mpi_t data_r_e;
721 size_t rsize;
722 size_t n; 653 size_t n;
723 gcry_error_t rc;
724 int ret; 654 int ret;
725 unsigned int len;
726 655
727 ret = key_from_sexp (ne, pkey->sexp, "public-key", "ne"); 656 ret = key_from_sexp (ne, pkey->sexp, "public-key", "ne");
728 if (0 != ret) 657 if (0 != ret)
@@ -734,17 +663,8 @@ GNUNET_CRYPTO_rsa_blind (const struct GNUNET_HashCode *hash,
734 return 0; 663 return 0;
735 } 664 }
736 665
737 rc = rsa_full_domain_hash (&data, hash, pkey, &rsize); 666 rsa_full_domain_hash (&data, hash, pkey);
738 if (0 != rc) /* Allocation error in libgcrypt */ 667 bkey = rsa_blinding_key_derive (pkey,
739 {
740 GNUNET_break (0);
741 gcry_mpi_release (ne[0]);
742 gcry_mpi_release (ne[1]);
743 *buffer = NULL;
744 return 0;
745 }
746 len = GNUNET_CRYPTO_rsa_public_key_len (pkey);
747 bkey = rsa_blinding_key_derive (len,
748 bks); 668 bks);
749 r_e = gcry_mpi_new (0); 669 r_e = gcry_mpi_new (0);
750 gcry_mpi_powm (r_e, 670 gcry_mpi_powm (r_e,
@@ -886,13 +806,11 @@ GNUNET_CRYPTO_rsa_sign_fdh (const struct GNUNET_CRYPTO_RsaPrivateKey *key,
886{ 806{
887 struct GNUNET_CRYPTO_RsaPublicKey *pkey; 807 struct GNUNET_CRYPTO_RsaPublicKey *pkey;
888 gcry_mpi_t v = NULL; 808 gcry_mpi_t v = NULL;
889 gcry_error_t rc;
890 struct GNUNET_CRYPTO_RsaSignature *sig; 809 struct GNUNET_CRYPTO_RsaSignature *sig;
891 810
892 pkey = GNUNET_CRYPTO_rsa_private_key_get_public (key); 811 pkey = GNUNET_CRYPTO_rsa_private_key_get_public (key);
893 rc = rsa_full_domain_hash (&v, hash, pkey, NULL); 812 rsa_full_domain_hash (&v, hash, pkey);
894 GNUNET_CRYPTO_rsa_public_key_free (pkey); 813 GNUNET_CRYPTO_rsa_public_key_free (pkey);
895 GNUNET_assert (0 == rc);
896 814
897 sig = rsa_sign_mpi (key, v); 815 sig = rsa_sign_mpi (key, v);
898 gcry_mpi_release (v); 816 gcry_mpi_release (v);
@@ -1034,7 +952,6 @@ GNUNET_CRYPTO_rsa_unblind (struct GNUNET_CRYPTO_RsaSignature *sig,
1034 gcry_mpi_t ubsig; 952 gcry_mpi_t ubsig;
1035 int ret; 953 int ret;
1036 struct GNUNET_CRYPTO_RsaSignature *sret; 954 struct GNUNET_CRYPTO_RsaSignature *sret;
1037 unsigned int len;
1038 955
1039 ret = key_from_sexp (&n, pkey->sexp, "public-key", "n"); 956 ret = key_from_sexp (&n, pkey->sexp, "public-key", "n");
1040 if (0 != ret) 957 if (0 != ret)
@@ -1053,8 +970,7 @@ GNUNET_CRYPTO_rsa_unblind (struct GNUNET_CRYPTO_RsaSignature *sig,
1053 GNUNET_break_op (0); 970 GNUNET_break_op (0);
1054 return NULL; 971 return NULL;
1055 } 972 }
1056 len = GNUNET_CRYPTO_rsa_public_key_len (pkey); 973 bkey = rsa_blinding_key_derive (pkey,
1057 bkey = rsa_blinding_key_derive (len,
1058 bks); 974 bks);
1059 975
1060 r_inv = gcry_mpi_new (0); 976 r_inv = gcry_mpi_new (0);
@@ -1106,8 +1022,7 @@ GNUNET_CRYPTO_rsa_verify (const struct GNUNET_HashCode *hash,
1106 gcry_mpi_t r; 1022 gcry_mpi_t r;
1107 int rc; 1023 int rc;
1108 1024
1109 rc = rsa_full_domain_hash (&r, hash, pkey, NULL); 1025 rsa_full_domain_hash (&r, hash, pkey);
1110 GNUNET_assert (0 == rc); /* Allocation error in libgcrypt */
1111 data = mpi_to_sexp(r); 1026 data = mpi_to_sexp(r);
1112 gcry_mpi_release (r); 1027 gcry_mpi_release (r);
1113 1028