diff options
author | Jeff Burdges <burdges@gnunet.org> | 2016-03-20 14:44:36 +0000 |
---|---|---|
committer | Jeff Burdges <burdges@gnunet.org> | 2016-03-20 14:44:36 +0000 |
commit | 60de5f48cbfc3868570284e91415ca7e06c390e1 (patch) | |
tree | 3e75dfd8c6a3400b1641655d0e96104420535d42 /src/util/crypto_rsa.c | |
parent | 157f9a2bc96a0b1594effe78158894e59e03a033 (diff) | |
download | gnunet-60de5f48cbfc3868570284e91415ca7e06c390e1.tar.gz gnunet-60de5f48cbfc3868570284e91415ca7e06c390e1.zip |
Implement a Full Domain Hash (FDH) for RSA signatures and blind signatures
This gives a measure of provable security to the Taler exchange/mint
against hypothetical one-more forgery attacks. See:
https://eprint.iacr.org/2001/002.pdf
http://www.di.ens.fr/~pointche/Documents/Papers/2001_fcA.pdf
We seed the FDH with the denomination keys as as a homage to RSA-PSS.
This may slightly improves the exchanges's resistance to a violation
of RSA-KTI and against insiders who can influence the choice of RSA
keys but cannot actually exfiltrate them.
Adopting FDH fixes a bug when using 512 bit RSA keys as well.
Diffstat (limited to 'src/util/crypto_rsa.c')
-rw-r--r-- | src/util/crypto_rsa.c | 248 |
1 files changed, 189 insertions, 59 deletions
diff --git a/src/util/crypto_rsa.c b/src/util/crypto_rsa.c index 6c58757d8..a14eff407 100644 --- a/src/util/crypto_rsa.c +++ b/src/util/crypto_rsa.c | |||
@@ -566,16 +566,15 @@ GNUNET_CRYPTO_rsa_blinding_key_free (struct GNUNET_CRYPTO_rsa_BlindingKey *bkey) | |||
566 | 566 | ||
567 | 567 | ||
568 | /** | 568 | /** |
569 | * Encode the blinding key in a format suitable for | 569 | * Print an MPI to a newly created buffer |
570 | * storing it into a file. | ||
571 | * | 570 | * |
572 | * @param bkey the blinding key | 571 | * @param v MPI to print. |
573 | * @param[out] buffer set to a buffer with the encoded key | 572 | * @param[out] buffer set to a buffer with the result |
574 | * @return size of memory allocated in @a buffer | 573 | * @return number of bytes stored in @a buffer |
575 | */ | 574 | */ |
576 | size_t | 575 | size_t |
577 | GNUNET_CRYPTO_rsa_blinding_key_encode (const struct GNUNET_CRYPTO_rsa_BlindingKey *bkey, | 576 | GNUNET_CRYPTO_mpi_print (gcry_mpi_t v, |
578 | char **buffer) | 577 | char **buffer) |
579 | { | 578 | { |
580 | size_t n; | 579 | size_t n; |
581 | char *b; | 580 | char *b; |
@@ -585,20 +584,36 @@ GNUNET_CRYPTO_rsa_blinding_key_encode (const struct GNUNET_CRYPTO_rsa_BlindingKe | |||
585 | NULL, | 584 | NULL, |
586 | 0, | 585 | 0, |
587 | &n, | 586 | &n, |
588 | bkey->r); | 587 | v); |
589 | b = GNUNET_malloc (n); | 588 | b = GNUNET_malloc (n); |
590 | GNUNET_assert (0 == | 589 | GNUNET_assert (0 == |
591 | gcry_mpi_print (GCRYMPI_FMT_USG, | 590 | gcry_mpi_print (GCRYMPI_FMT_USG, |
592 | (unsigned char *) b, | 591 | (unsigned char *) b, |
593 | n, | 592 | n, |
594 | &rsize, | 593 | &rsize, |
595 | bkey->r)); | 594 | v)); |
596 | *buffer = b; | 595 | *buffer = b; |
597 | return n; | 596 | return n; |
598 | } | 597 | } |
599 | 598 | ||
600 | 599 | ||
601 | /** | 600 | /** |
601 | * Encode the blinding key in a format suitable for | ||
602 | * storing it into a file. | ||
603 | * | ||
604 | * @param bkey the blinding key | ||
605 | * @param[out] buffer set to a buffer with the encoded key | ||
606 | * @return size of memory allocated in @a buffer | ||
607 | */ | ||
608 | size_t | ||
609 | GNUNET_CRYPTO_rsa_blinding_key_encode (const struct GNUNET_CRYPTO_rsa_BlindingKey *bkey, | ||
610 | char **buffer) | ||
611 | { | ||
612 | return GNUNET_CRYPTO_mpi_print (bkey->r, buffer); | ||
613 | } | ||
614 | |||
615 | |||
616 | /** | ||
602 | * Decode the blinding key from the data-format back | 617 | * Decode the blinding key from the data-format back |
603 | * to the "normal", internal format. | 618 | * to the "normal", internal format. |
604 | * | 619 | * |
@@ -630,6 +645,95 @@ GNUNET_CRYPTO_rsa_blinding_key_decode (const char *buf, | |||
630 | 645 | ||
631 | 646 | ||
632 | /** | 647 | /** |
648 | * Computes a full domain hash seeded by the given public key. | ||
649 | * This gives a measure of provable security to the Taler exchange | ||
650 | * against one-more forgery attacks. See: | ||
651 | * https://eprint.iacr.org/2001/002.pdf | ||
652 | * http://www.di.ens.fr/~pointche/Documents/Papers/2001_fcA.pdf | ||
653 | * | ||
654 | * @param hash initial hash of the message to sign | ||
655 | * @param pkey the public key of the signer | ||
656 | * @return libgcrypt error that to represent an allocation failure | ||
657 | */ | ||
658 | gcry_error_t | ||
659 | rsa_full_domain_hash (gcry_mpi_t *r, | ||
660 | const struct GNUNET_HashCode *hash, | ||
661 | const struct GNUNET_CRYPTO_rsa_PublicKey *pkey, | ||
662 | size_t *rsize) | ||
663 | { | ||
664 | int i,nbits,nhashes; | ||
665 | gcry_error_t rc; | ||
666 | char *buf; | ||
667 | size_t buf_len; | ||
668 | gcry_md_hd_t h,h0; | ||
669 | struct GNUNET_HashCode *hs; | ||
670 | |||
671 | /* Uncomment the following to debug without using the full domain hash */ | ||
672 | /* | ||
673 | rc = gcry_mpi_scan (r, | ||
674 | GCRYMPI_FMT_USG, | ||
675 | (const unsigned char *)hash, | ||
676 | sizeof(struct GNUNET_HashCode), | ||
677 | rsize); | ||
678 | return rc; | ||
679 | */ | ||
680 | |||
681 | nbits = GNUNET_CRYPTO_rsa_public_key_len (pkey); | ||
682 | // calls gcry_mpi_get_nbits(.. pkey->sexp ..) | ||
683 | if (nbits < 512) | ||
684 | nbits = 512; | ||
685 | |||
686 | // Already almost an HMAC since we consume a hash, so no GCRY_MD_FLAG_HMAC. | ||
687 | rc = gcry_md_open (&h,GCRY_MD_SHA512,0); | ||
688 | if (0 != rc) return rc; | ||
689 | |||
690 | // We seed with the public denomination key as a homage to RSA-PSS by | ||
691 | // Mihir Bellare and Phillip Rogaway. Doing this lowers the degree | ||
692 | // of the hypothetical polyomial-time attack on RSA-KTI created by a | ||
693 | // polynomial-time one-more forgary attack. Yey seeding! | ||
694 | buf_len = GNUNET_CRYPTO_rsa_public_key_encode (pkey, &buf); | ||
695 | gcry_md_write (h, buf,buf_len); | ||
696 | GNUNET_free (buf); | ||
697 | |||
698 | nhashes = (nbits-1) / (8 * sizeof(struct GNUNET_HashCode)) + 1; | ||
699 | hs = (struct GNUNET_HashCode *)GNUNET_malloc (nhashes * sizeof(struct GNUNET_HashCode)); | ||
700 | for (i=0; i<nhashes; i++) | ||
701 | { | ||
702 | gcry_md_write (h, hash, sizeof(struct GNUNET_HashCode)); | ||
703 | rc = gcry_md_copy (&h0, h); | ||
704 | if (0 != rc) break; | ||
705 | gcry_md_putc (h0, i % 256); | ||
706 | // gcry_md_final (&h0); | ||
707 | memcpy (&hs[i], | ||
708 | gcry_md_read (h0,GCRY_MD_SHA512), | ||
709 | sizeof(struct GNUNET_HashCode)); | ||
710 | gcry_md_close (h0); | ||
711 | } | ||
712 | gcry_md_close (h); | ||
713 | if (0 != rc) { | ||
714 | GNUNET_free (hs); | ||
715 | return rc; | ||
716 | } | ||
717 | |||
718 | rc = gcry_mpi_scan (r, | ||
719 | GCRYMPI_FMT_USG, | ||
720 | (const unsigned char *)hs, | ||
721 | nhashes * sizeof(struct GNUNET_HashCode), | ||
722 | rsize); | ||
723 | GNUNET_free (hs); | ||
724 | if (0 != rc) return rc; | ||
725 | |||
726 | // Do not allow *r to exceed n or signatures fail to verify unpredictably. | ||
727 | // This happening with gcry_mpi_clear_highbit (*r, nbits-1) so maybe | ||
728 | // gcry_mpi_clear_highbit is broken, but setting the highbit sounds good. | ||
729 | // (void) fprintf (stderr, "%d %d %d",nbits,nhashes, gcry_mpi_get_nbits(*r)); | ||
730 | gcry_mpi_set_highbit (*r, nbits-2); | ||
731 | // (void) fprintf (stderr, " %d\n",gcry_mpi_get_nbits(*r)); | ||
732 | return rc; | ||
733 | } | ||
734 | |||
735 | |||
736 | /** | ||
633 | * Blinds the given message with the given blinding key | 737 | * Blinds the given message with the given blinding key |
634 | * | 738 | * |
635 | * @param hash hash of the message to sign | 739 | * @param hash hash of the message to sign |
@@ -651,7 +755,6 @@ GNUNET_CRYPTO_rsa_blind (const struct GNUNET_HashCode *hash, | |||
651 | size_t rsize; | 755 | size_t rsize; |
652 | size_t n; | 756 | size_t n; |
653 | gcry_error_t rc; | 757 | gcry_error_t rc; |
654 | char *b; | ||
655 | int ret; | 758 | int ret; |
656 | 759 | ||
657 | ret = key_from_sexp (ne, pkey->sexp, "public-key", "ne"); | 760 | ret = key_from_sexp (ne, pkey->sexp, "public-key", "ne"); |
@@ -663,11 +766,9 @@ GNUNET_CRYPTO_rsa_blind (const struct GNUNET_HashCode *hash, | |||
663 | *buffer = NULL; | 766 | *buffer = NULL; |
664 | return 0; | 767 | return 0; |
665 | } | 768 | } |
666 | if (0 != (rc = gcry_mpi_scan (&data, | 769 | |
667 | GCRYMPI_FMT_USG, | 770 | rc = rsa_full_domain_hash(&data, hash, pkey, &rsize); |
668 | (const unsigned char *) hash, | 771 | if (0 != rc) // Allocation error in libgcrypt |
669 | sizeof (struct GNUNET_HashCode), | ||
670 | &rsize))) | ||
671 | { | 772 | { |
672 | GNUNET_break (0); | 773 | GNUNET_break (0); |
673 | gcry_mpi_release (ne[0]); | 774 | gcry_mpi_release (ne[0]); |
@@ -690,75 +791,50 @@ GNUNET_CRYPTO_rsa_blind (const struct GNUNET_HashCode *hash, | |||
690 | gcry_mpi_release (ne[1]); | 791 | gcry_mpi_release (ne[1]); |
691 | gcry_mpi_release (r_e); | 792 | gcry_mpi_release (r_e); |
692 | 793 | ||
693 | gcry_mpi_print (GCRYMPI_FMT_USG, | 794 | n = GNUNET_CRYPTO_mpi_print (data_r_e, buffer); |
694 | NULL, | ||
695 | 0, | ||
696 | &n, | ||
697 | data_r_e); | ||
698 | b = GNUNET_malloc (n); | ||
699 | rc = gcry_mpi_print (GCRYMPI_FMT_USG, | ||
700 | (unsigned char *) b, | ||
701 | n, | ||
702 | &rsize, | ||
703 | data_r_e); | ||
704 | gcry_mpi_release (data_r_e); | 795 | gcry_mpi_release (data_r_e); |
705 | *buffer = b; | ||
706 | return n; | 796 | return n; |
707 | } | 797 | } |
708 | 798 | ||
709 | 799 | ||
710 | /** | 800 | /** |
711 | * Convert the data specified in the given purpose argument to an | 801 | * Convert an MPI to an S-expression suitable for signature operations. |
712 | * S-expression suitable for signature operations. | ||
713 | * | 802 | * |
714 | * @param ptr pointer to the data to convert | 803 | * @param value pointer to the data to convert |
715 | * @param size the size of the data | ||
716 | * @return converted s-expression | 804 | * @return converted s-expression |
717 | */ | 805 | */ |
718 | static gcry_sexp_t | 806 | static gcry_sexp_t |
719 | data_to_sexp (const void *ptr, size_t size) | 807 | mpi_to_sexp (gcry_mpi_t value) |
720 | { | 808 | { |
721 | gcry_mpi_t value; | 809 | gcry_sexp_t data = NULL; |
722 | gcry_sexp_t data; | ||
723 | 810 | ||
724 | value = NULL; | ||
725 | data = NULL; | ||
726 | GNUNET_assert (0 == | ||
727 | gcry_mpi_scan (&value, | ||
728 | GCRYMPI_FMT_USG, | ||
729 | ptr, | ||
730 | size, | ||
731 | NULL)); | ||
732 | GNUNET_assert (0 == | 811 | GNUNET_assert (0 == |
733 | gcry_sexp_build (&data, | 812 | gcry_sexp_build (&data, |
734 | NULL, | 813 | NULL, |
735 | "(data (flags raw) (value %M))", | 814 | "(data (flags raw) (value %M))", |
736 | value)); | 815 | value)); |
737 | gcry_mpi_release (value); | ||
738 | return data; | 816 | return data; |
739 | } | 817 | } |
740 | 818 | ||
741 | 819 | ||
742 | /** | 820 | /** |
743 | * Sign the given message. | 821 | * Sign and release the given MPI. |
744 | * | 822 | * |
745 | * @param key private key to use for the signing | 823 | * @param key private key to use for the signing |
746 | * @param msg the message to sign | 824 | * @param value the MPI to sign |
747 | * @param msg_len number of bytes in @a msg to sign | ||
748 | * @return NULL on error, signature on success | 825 | * @return NULL on error, signature on success |
749 | */ | 826 | */ |
750 | struct GNUNET_CRYPTO_rsa_Signature * | 827 | struct GNUNET_CRYPTO_rsa_Signature * |
751 | GNUNET_CRYPTO_rsa_sign (const struct GNUNET_CRYPTO_rsa_PrivateKey *key, | 828 | rsa_sign_mpi (const struct GNUNET_CRYPTO_rsa_PrivateKey *key, |
752 | const void *msg, | 829 | gcry_mpi_t value) |
753 | size_t msg_len) | ||
754 | { | 830 | { |
755 | struct GNUNET_CRYPTO_rsa_Signature *sig; | 831 | struct GNUNET_CRYPTO_rsa_Signature *sig; |
756 | struct GNUNET_CRYPTO_rsa_PublicKey *public_key; | 832 | struct GNUNET_CRYPTO_rsa_PublicKey *public_key; |
757 | gcry_sexp_t result; | 833 | gcry_sexp_t data,result; |
758 | gcry_sexp_t data; | 834 | |
835 | data = mpi_to_sexp (value); | ||
836 | gcry_mpi_release (value); | ||
759 | 837 | ||
760 | data = data_to_sexp (msg, | ||
761 | msg_len); | ||
762 | if (0 != | 838 | if (0 != |
763 | gcry_pk_sign (&result, | 839 | gcry_pk_sign (&result, |
764 | data, | 840 | data, |
@@ -775,7 +851,7 @@ GNUNET_CRYPTO_rsa_sign (const struct GNUNET_CRYPTO_rsa_PrivateKey *key, | |||
775 | data, | 851 | data, |
776 | public_key->sexp)) | 852 | public_key->sexp)) |
777 | { | 853 | { |
778 | GNUNET_break (0); | 854 | GNUNET_break (0); |
779 | GNUNET_CRYPTO_rsa_public_key_free (public_key); | 855 | GNUNET_CRYPTO_rsa_public_key_free (public_key); |
780 | gcry_sexp_release (data); | 856 | gcry_sexp_release (data); |
781 | gcry_sexp_release (result); | 857 | gcry_sexp_release (result); |
@@ -792,6 +868,56 @@ GNUNET_CRYPTO_rsa_sign (const struct GNUNET_CRYPTO_rsa_PrivateKey *key, | |||
792 | 868 | ||
793 | 869 | ||
794 | /** | 870 | /** |
871 | * Sign a blinded value, which must be a full domain hash of a message. | ||
872 | * | ||
873 | * @param key private key to use for the signing | ||
874 | * @param msg the message to sign | ||
875 | * @param msg_len number of bytes in @a msg to sign | ||
876 | * @return NULL on error, signature on success | ||
877 | */ | ||
878 | struct GNUNET_CRYPTO_rsa_Signature * | ||
879 | GNUNET_CRYPTO_rsa_sign_blinded (const struct GNUNET_CRYPTO_rsa_PrivateKey *key, | ||
880 | const void *msg, | ||
881 | size_t msg_len) | ||
882 | { | ||
883 | gcry_mpi_t v = NULL; | ||
884 | |||
885 | GNUNET_assert (0 == | ||
886 | gcry_mpi_scan (&v, | ||
887 | GCRYMPI_FMT_USG, | ||
888 | msg, | ||
889 | msg_len, | ||
890 | NULL)); | ||
891 | |||
892 | return rsa_sign_mpi (key,v); | ||
893 | } | ||
894 | |||
895 | |||
896 | /** | ||
897 | * Create and sign a full domain hash of a message. | ||
898 | * | ||
899 | * @param key private key to use for the signing | ||
900 | * @param hash the hash of the message to sign | ||
901 | * @return NULL on error, signature on success | ||
902 | */ | ||
903 | struct GNUNET_CRYPTO_rsa_Signature * | ||
904 | GNUNET_CRYPTO_rsa_sign_fdh (const struct GNUNET_CRYPTO_rsa_PrivateKey *key, | ||
905 | const struct GNUNET_HashCode *hash) | ||
906 | { | ||
907 | struct GNUNET_CRYPTO_rsa_PublicKey *pkey; | ||
908 | gcry_mpi_t v = NULL; | ||
909 | gcry_error_t rc; | ||
910 | |||
911 | pkey = GNUNET_CRYPTO_rsa_private_key_get_public (key); | ||
912 | rc = rsa_full_domain_hash (&v, hash, pkey, NULL); | ||
913 | GNUNET_CRYPTO_rsa_public_key_free (pkey); | ||
914 | GNUNET_assert (0 == rc); | ||
915 | |||
916 | return rsa_sign_mpi (key,v); | ||
917 | } | ||
918 | |||
919 | |||
920 | /** | ||
795 | * Free memory occupied by signature. | 921 | * Free memory occupied by signature. |
796 | * | 922 | * |
797 | * @param sig memory to freee | 923 | * @param sig memory to freee |
@@ -976,22 +1102,26 @@ GNUNET_CRYPTO_rsa_unblind (struct GNUNET_CRYPTO_rsa_Signature *sig, | |||
976 | * | 1102 | * |
977 | * @param hash hash of the message to verify to match the @a sig | 1103 | * @param hash hash of the message to verify to match the @a sig |
978 | * @param sig signature that is being validated | 1104 | * @param sig signature that is being validated |
979 | * @param public_key public key of the signer | 1105 | * @param pkey public key of the signer |
980 | * @returns #GNUNET_OK if ok, #GNUNET_SYSERR if invalid | 1106 | * @returns #GNUNET_OK if ok, #GNUNET_SYSERR if invalid |
981 | */ | 1107 | */ |
982 | int | 1108 | int |
983 | GNUNET_CRYPTO_rsa_verify (const struct GNUNET_HashCode *hash, | 1109 | GNUNET_CRYPTO_rsa_verify (const struct GNUNET_HashCode *hash, |
984 | const struct GNUNET_CRYPTO_rsa_Signature *sig, | 1110 | const struct GNUNET_CRYPTO_rsa_Signature *sig, |
985 | const struct GNUNET_CRYPTO_rsa_PublicKey *public_key) | 1111 | const struct GNUNET_CRYPTO_rsa_PublicKey *pkey) |
986 | { | 1112 | { |
987 | gcry_sexp_t data; | 1113 | gcry_sexp_t data; |
1114 | gcry_mpi_t r; | ||
988 | int rc; | 1115 | int rc; |
989 | 1116 | ||
990 | data = data_to_sexp (hash, | 1117 | rc = rsa_full_domain_hash (&r, hash, pkey, NULL); |
991 | sizeof (struct GNUNET_HashCode)); | 1118 | GNUNET_assert (0 == rc); // Allocation error in libgcrypt |
1119 | data = mpi_to_sexp(r); | ||
1120 | gcry_mpi_release (r); | ||
1121 | |||
992 | rc = gcry_pk_verify (sig->sexp, | 1122 | rc = gcry_pk_verify (sig->sexp, |
993 | data, | 1123 | data, |
994 | public_key->sexp); | 1124 | pkey->sexp); |
995 | gcry_sexp_release (data); | 1125 | gcry_sexp_release (data); |
996 | if (0 != rc) | 1126 | if (0 != rc) |
997 | { | 1127 | { |