diff options
author | Christian Grothoff <christian@grothoff.org> | 2012-07-14 21:19:22 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2012-07-14 21:19:22 +0000 |
commit | a299afa8af5593c2114ca8242099d8f7971c428e (patch) | |
tree | d6474062280de16f92cb7f2239393094dcf472cd /src/util/crypto_rsa.c | |
parent | 7958f822ccba4fe2dc372c62aaaa2b2d8fe56acf (diff) | |
download | gnunet-a299afa8af5593c2114ca8242099d8f7971c428e.tar.gz gnunet-a299afa8af5593c2114ca8242099d8f7971c428e.zip |
-new API for asyncronous generation of private RSA keys
Diffstat (limited to 'src/util/crypto_rsa.c')
-rw-r--r-- | src/util/crypto_rsa.c | 358 |
1 files changed, 326 insertions, 32 deletions
diff --git a/src/util/crypto_rsa.c b/src/util/crypto_rsa.c index bbd73083d..6e92f0753 100644 --- a/src/util/crypto_rsa.c +++ b/src/util/crypto_rsa.c | |||
@@ -34,9 +34,7 @@ | |||
34 | #include "platform.h" | 34 | #include "platform.h" |
35 | #include <gcrypt.h> | 35 | #include <gcrypt.h> |
36 | #include "gnunet_common.h" | 36 | #include "gnunet_common.h" |
37 | #include "gnunet_crypto_lib.h" | 37 | #include "gnunet_util_lib.h" |
38 | #include "gnunet_disk_lib.h" | ||
39 | #include "gnunet_strings_lib.h" | ||
40 | 38 | ||
41 | #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) | 39 | #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) |
42 | 40 | ||
@@ -44,9 +42,10 @@ | |||
44 | 42 | ||
45 | #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename) | 43 | #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename) |
46 | 44 | ||
45 | |||
47 | /** | 46 | /** |
48 | * The private information of an RSA key pair. | 47 | * The private information of an RSA key pair. |
49 | * NOTE: this must match the definition in crypto_ksk.c | 48 | * NOTE: this must match the definition in crypto_ksk.c and gnunet-rsa.c! |
50 | */ | 49 | */ |
51 | struct GNUNET_CRYPTO_RsaPrivateKey | 50 | struct GNUNET_CRYPTO_RsaPrivateKey |
52 | { | 51 | { |
@@ -81,31 +80,6 @@ adjust (unsigned char *buf, size_t size, size_t target) | |||
81 | } | 80 | } |
82 | } | 81 | } |
83 | 82 | ||
84 | /** | ||
85 | * Create a new private key. Caller must free return value. | ||
86 | * | ||
87 | * @return fresh private key | ||
88 | */ | ||
89 | struct GNUNET_CRYPTO_RsaPrivateKey * | ||
90 | GNUNET_CRYPTO_rsa_key_create () | ||
91 | { | ||
92 | struct GNUNET_CRYPTO_RsaPrivateKey *ret; | ||
93 | gcry_sexp_t s_key; | ||
94 | gcry_sexp_t s_keyparam; | ||
95 | |||
96 | GNUNET_assert (0 == | ||
97 | gcry_sexp_build (&s_keyparam, NULL, | ||
98 | "(genkey(rsa(nbits %d)(rsa-use-e 3:257)))", | ||
99 | HOSTKEY_LEN)); | ||
100 | GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam)); | ||
101 | gcry_sexp_release (s_keyparam); | ||
102 | #if EXTRA_CHECKS | ||
103 | GNUNET_assert (0 == gcry_pk_testkey (s_key)); | ||
104 | #endif | ||
105 | ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey)); | ||
106 | ret->sexp = s_key; | ||
107 | return ret; | ||
108 | } | ||
109 | 83 | ||
110 | /** | 84 | /** |
111 | * Free memory occupied by hostkey | 85 | * Free memory occupied by hostkey |
@@ -118,6 +92,10 @@ GNUNET_CRYPTO_rsa_key_free (struct GNUNET_CRYPTO_RsaPrivateKey *hostkey) | |||
118 | GNUNET_free (hostkey); | 92 | GNUNET_free (hostkey); |
119 | } | 93 | } |
120 | 94 | ||
95 | |||
96 | /** | ||
97 | * FIXME: document! | ||
98 | */ | ||
121 | static int | 99 | static int |
122 | key_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname, | 100 | key_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname, |
123 | const char *elems) | 101 | const char *elems) |
@@ -594,6 +572,98 @@ GNUNET_CRYPTO_rsa_decode_key (const char *buf, uint16_t len) | |||
594 | 572 | ||
595 | 573 | ||
596 | /** | 574 | /** |
575 | * Create a new private key. Caller must free return value. | ||
576 | * | ||
577 | * @return fresh private key | ||
578 | */ | ||
579 | static struct GNUNET_CRYPTO_RsaPrivateKey * | ||
580 | rsa_key_create () | ||
581 | { | ||
582 | struct GNUNET_CRYPTO_RsaPrivateKey *ret; | ||
583 | gcry_sexp_t s_key; | ||
584 | gcry_sexp_t s_keyparam; | ||
585 | |||
586 | GNUNET_assert (0 == | ||
587 | gcry_sexp_build (&s_keyparam, NULL, | ||
588 | "(genkey(rsa(nbits %d)(rsa-use-e 3:257)))", | ||
589 | HOSTKEY_LEN)); | ||
590 | GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam)); | ||
591 | gcry_sexp_release (s_keyparam); | ||
592 | #if EXTRA_CHECKS | ||
593 | GNUNET_assert (0 == gcry_pk_testkey (s_key)); | ||
594 | #endif | ||
595 | ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPrivateKey)); | ||
596 | ret->sexp = s_key; | ||
597 | return ret; | ||
598 | } | ||
599 | |||
600 | |||
601 | /** | ||
602 | * Try to read the private key from the given file. | ||
603 | * | ||
604 | * @param filename file to read the key from | ||
605 | * @return NULL on error | ||
606 | */ | ||
607 | static struct GNUNET_CRYPTO_RsaPrivateKey * | ||
608 | try_read_key (const char *filename) | ||
609 | { | ||
610 | struct GNUNET_CRYPTO_RsaPrivateKey *ret; | ||
611 | struct GNUNET_CRYPTO_RsaPrivateKeyBinaryEncoded *enc; | ||
612 | struct GNUNET_DISK_FileHandle *fd; | ||
613 | OFF_T fs; | ||
614 | uint16_t len; | ||
615 | |||
616 | if (GNUNET_YES != GNUNET_DISK_file_test (filename)) | ||
617 | return NULL; | ||
618 | |||
619 | /* hostkey file exists already, read it! */ | ||
620 | if (NULL == (fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, | ||
621 | GNUNET_DISK_PERM_NONE))) | ||
622 | { | ||
623 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename); | ||
624 | return NULL; | ||
625 | } | ||
626 | if (GNUNET_OK != (GNUNET_DISK_file_handle_size (fd, &fs))) | ||
627 | { | ||
628 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "stat", filename); | ||
629 | (void) GNUNET_DISK_file_close (fd); | ||
630 | return NULL; | ||
631 | } | ||
632 | if (fs > UINT16_MAX) | ||
633 | { | ||
634 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
635 | _("File `%s' does not contain a valid private key. Deleting it.\n"), | ||
636 | filename); | ||
637 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); | ||
638 | if (0 != UNLINK (filename)) | ||
639 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); | ||
640 | return NULL; | ||
641 | } | ||
642 | |||
643 | enc = GNUNET_malloc (fs); | ||
644 | GNUNET_break (fs == GNUNET_DISK_file_read (fd, enc, fs)); | ||
645 | len = ntohs (enc->len); | ||
646 | ret = NULL; | ||
647 | if ((len != fs) || | ||
648 | (NULL == (ret = GNUNET_CRYPTO_rsa_decode_key ((char *) enc, len)))) | ||
649 | { | ||
650 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
651 | _("File `%s' does not contain a valid private key. Deleting it.\n"), | ||
652 | filename); | ||
653 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); | ||
654 | if (0 != UNLINK (filename)) | ||
655 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); | ||
656 | GNUNET_free (enc); | ||
657 | return NULL; | ||
658 | } | ||
659 | GNUNET_free (enc); | ||
660 | |||
661 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); | ||
662 | return ret; | ||
663 | } | ||
664 | |||
665 | |||
666 | /** | ||
597 | * Create a new private key by reading it from a file. If the | 667 | * Create a new private key by reading it from a file. If the |
598 | * files does not exist, create a new key and write it to the | 668 | * files does not exist, create a new key and write it to the |
599 | * file. Caller must free return value. Note that this function | 669 | * file. Caller must free return value. Note that this function |
@@ -658,13 +728,13 @@ GNUNET_CRYPTO_rsa_key_create_from_file (const char *filename) | |||
658 | { | 728 | { |
659 | ec = errno; | 729 | ec = errno; |
660 | LOG (GNUNET_ERROR_TYPE_ERROR, | 730 | LOG (GNUNET_ERROR_TYPE_ERROR, |
661 | _("Could not aquire lock on file `%s': %s...\n"), filename, | 731 | _("Could not acquire lock on file `%s': %s...\n"), filename, |
662 | STRERROR (ec)); | 732 | STRERROR (ec)); |
663 | } | 733 | } |
664 | } | 734 | } |
665 | LOG (GNUNET_ERROR_TYPE_INFO, | 735 | LOG (GNUNET_ERROR_TYPE_INFO, |
666 | _("Creating a new private key. This may take a while.\n")); | 736 | _("Creating a new private key. This may take a while.\n")); |
667 | ret = GNUNET_CRYPTO_rsa_key_create (); | 737 | ret = rsa_key_create (); |
668 | GNUNET_assert (ret != NULL); | 738 | GNUNET_assert (ret != NULL); |
669 | enc = GNUNET_CRYPTO_rsa_encode_key (ret); | 739 | enc = GNUNET_CRYPTO_rsa_encode_key (ret); |
670 | GNUNET_assert (enc != NULL); | 740 | GNUNET_assert (enc != NULL); |
@@ -705,7 +775,7 @@ GNUNET_CRYPTO_rsa_key_create_from_file (const char *filename) | |||
705 | { | 775 | { |
706 | ec = errno; | 776 | ec = errno; |
707 | LOG (GNUNET_ERROR_TYPE_ERROR, | 777 | LOG (GNUNET_ERROR_TYPE_ERROR, |
708 | _("Could not aquire lock on file `%s': %s...\n"), filename, | 778 | _("Could not acquire lock on file `%s': %s...\n"), filename, |
709 | STRERROR (ec)); | 779 | STRERROR (ec)); |
710 | LOG (GNUNET_ERROR_TYPE_ERROR, | 780 | LOG (GNUNET_ERROR_TYPE_ERROR, |
711 | _ | 781 | _ |
@@ -786,6 +856,230 @@ GNUNET_CRYPTO_rsa_key_create_from_file (const char *filename) | |||
786 | 856 | ||
787 | 857 | ||
788 | /** | 858 | /** |
859 | * Handle to cancel private key generation and state for the | ||
860 | * key generation operation. | ||
861 | */ | ||
862 | struct GNUNET_CRYPTO_RsaKeyGenerationContext | ||
863 | { | ||
864 | |||
865 | /** | ||
866 | * Continuation to call upon completion. | ||
867 | */ | ||
868 | GNUNET_CRYPTO_RsaKeyCallback cont; | ||
869 | |||
870 | /** | ||
871 | * Closure for 'cont'. | ||
872 | */ | ||
873 | void *cont_cls; | ||
874 | |||
875 | /** | ||
876 | * Name of the file. | ||
877 | */ | ||
878 | char *filename; | ||
879 | |||
880 | /** | ||
881 | * Handle to the helper process which does the key generation. | ||
882 | */ | ||
883 | struct GNUNET_OS_Process *gnunet_rsa; | ||
884 | |||
885 | /** | ||
886 | * Handle to 'stdout' of gnunet-rsa. We 'read' on stdout to detect | ||
887 | * process termination (instead of messing with SIGCHLD). | ||
888 | */ | ||
889 | struct GNUNET_DISK_PipeHandle *gnunet_rsa_out; | ||
890 | |||
891 | /** | ||
892 | * Location where we store the private key if it already existed. | ||
893 | * (if this is used, 'filename', 'gnunet_rsa' and 'gnunet_rsa_out' will | ||
894 | * not be used). | ||
895 | */ | ||
896 | struct GNUNET_CRYPTO_RsaPrivateKey *pk; | ||
897 | |||
898 | /** | ||
899 | * Task reading from 'gnunet_rsa_out' to wait for process termination. | ||
900 | */ | ||
901 | GNUNET_SCHEDULER_TaskIdentifier read_task; | ||
902 | |||
903 | }; | ||
904 | |||
905 | |||
906 | /** | ||
907 | * Task called upon shutdown or process termination of 'gnunet-rsa' during | ||
908 | * RSA key generation. Check where we are and perform the appropriate | ||
909 | * action. | ||
910 | * | ||
911 | * @param cls the 'struct GNUNET_CRYPTO_RsaKeyGenerationContext' | ||
912 | * @param tc scheduler context | ||
913 | */ | ||
914 | static void | ||
915 | check_key_generation_completion (void *cls, | ||
916 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
917 | { | ||
918 | struct GNUNET_CRYPTO_RsaKeyGenerationContext *gc = cls; | ||
919 | enum GNUNET_OS_ProcessStatusType type; | ||
920 | unsigned long code; | ||
921 | struct GNUNET_CRYPTO_RsaPrivateKey *pk; | ||
922 | |||
923 | gc->read_task = GNUNET_SCHEDULER_NO_TASK; | ||
924 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | ||
925 | { | ||
926 | gc->cont (gc->cont_cls, NULL, _("interrupted by shutdown")); | ||
927 | GNUNET_CRYPTO_rsa_key_create_stop (gc); | ||
928 | return; | ||
929 | } | ||
930 | if (GNUNET_OK != | ||
931 | GNUNET_OS_process_status (gc->gnunet_rsa, | ||
932 | &type, &code)) | ||
933 | { | ||
934 | GNUNET_break (0); | ||
935 | gc->cont (gc->cont_cls, NULL, _("internal error")); | ||
936 | GNUNET_CRYPTO_rsa_key_create_stop (gc); | ||
937 | return; | ||
938 | } | ||
939 | GNUNET_OS_process_destroy (gc->gnunet_rsa); | ||
940 | gc->gnunet_rsa = NULL; | ||
941 | if ( (GNUNET_OS_PROCESS_EXITED != type) || | ||
942 | (0 != code) ) | ||
943 | { | ||
944 | gc->cont (gc->cont_cls, NULL, _("gnunet-rsa failed")); | ||
945 | GNUNET_CRYPTO_rsa_key_create_stop (gc); | ||
946 | return; | ||
947 | } | ||
948 | if (NULL == (pk = try_read_key (gc->filename))) | ||
949 | { | ||
950 | GNUNET_break (0); | ||
951 | gc->cont (gc->cont_cls, NULL, _("gnunet-rsa failed")); | ||
952 | GNUNET_CRYPTO_rsa_key_create_stop (gc); | ||
953 | return; | ||
954 | } | ||
955 | gc->cont (gc->cont_cls, pk, NULL); | ||
956 | GNUNET_free (gc->filename); | ||
957 | GNUNET_free (gc); | ||
958 | } | ||
959 | |||
960 | |||
961 | /** | ||
962 | * Return the private RSA key which already existed on disk | ||
963 | * (asynchronously) to the caller. | ||
964 | * | ||
965 | * @param cls the 'struct GNUNET_CRYPTO_RsaKeyGenerationContext' | ||
966 | * @param tc scheduler context (unused) | ||
967 | */ | ||
968 | static void | ||
969 | async_return_key (void *cls, | ||
970 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
971 | { | ||
972 | struct GNUNET_CRYPTO_RsaKeyGenerationContext *gc = cls; | ||
973 | |||
974 | gc->cont (gc->cont_cls, | ||
975 | gc->pk, | ||
976 | NULL); | ||
977 | GNUNET_free (gc); | ||
978 | } | ||
979 | |||
980 | |||
981 | /** | ||
982 | * Create a new private key by reading it from a file. If the files | ||
983 | * does not exist, create a new key and write it to the file. If the | ||
984 | * contents of the file are invalid the old file is deleted and a | ||
985 | * fresh key is created. | ||
986 | * | ||
987 | * @param filename name of file to use for storage | ||
988 | * @param cont function to call when done (or on errors) | ||
989 | * @param cont_cls closure for 'cont' | ||
990 | * @return handle to abort operation, NULL on fatal errors (cont will not be called if NULL is returned) | ||
991 | */ | ||
992 | struct GNUNET_CRYPTO_RsaKeyGenerationContext * | ||
993 | GNUNET_CRYPTO_rsa_key_create_start (const char *filename, | ||
994 | GNUNET_CRYPTO_RsaKeyCallback cont, | ||
995 | void *cont_cls) | ||
996 | { | ||
997 | struct GNUNET_CRYPTO_RsaKeyGenerationContext *gc; | ||
998 | struct GNUNET_CRYPTO_RsaPrivateKey *pk; | ||
999 | |||
1000 | if (NULL != (pk = try_read_key (filename))) | ||
1001 | { | ||
1002 | /* quick happy ending: key already exists! */ | ||
1003 | gc = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaKeyGenerationContext)); | ||
1004 | gc->pk = pk; | ||
1005 | gc->cont = cont; | ||
1006 | gc->cont_cls = cont_cls; | ||
1007 | gc->read_task = GNUNET_SCHEDULER_add_now (&async_return_key, | ||
1008 | gc); | ||
1009 | return gc; | ||
1010 | } | ||
1011 | gc = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaKeyGenerationContext)); | ||
1012 | gc->filename = GNUNET_strdup (filename); | ||
1013 | gc->cont = cont; | ||
1014 | gc->cont_cls = cont_cls; | ||
1015 | gc->gnunet_rsa_out = GNUNET_DISK_pipe (GNUNET_NO, | ||
1016 | GNUNET_NO, | ||
1017 | GNUNET_NO, | ||
1018 | GNUNET_YES); | ||
1019 | if (NULL == gc->gnunet_rsa_out) | ||
1020 | { | ||
1021 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "pipe"); | ||
1022 | GNUNET_free (gc->filename); | ||
1023 | GNUNET_free (gc); | ||
1024 | return NULL; | ||
1025 | } | ||
1026 | gc->gnunet_rsa = GNUNET_OS_start_process (GNUNET_YES, | ||
1027 | GNUNET_OS_INHERIT_STD_ERR, | ||
1028 | NULL, | ||
1029 | gc->gnunet_rsa_out, | ||
1030 | "gnunet-rsa", | ||
1031 | "gnunet-rsa", | ||
1032 | gc->filename, | ||
1033 | NULL); | ||
1034 | if (NULL == gc->gnunet_rsa) | ||
1035 | { | ||
1036 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fork"); | ||
1037 | GNUNET_DISK_pipe_close (gc->gnunet_rsa_out); | ||
1038 | GNUNET_free (gc->filename); | ||
1039 | GNUNET_free (gc); | ||
1040 | return NULL; | ||
1041 | } | ||
1042 | GNUNET_assert (GNUNET_OK == | ||
1043 | GNUNET_DISK_pipe_close_end (gc->gnunet_rsa_out, | ||
1044 | GNUNET_DISK_PIPE_END_WRITE)); | ||
1045 | gc->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, | ||
1046 | GNUNET_DISK_pipe_handle (gc->gnunet_rsa_out, | ||
1047 | GNUNET_DISK_PIPE_END_READ), | ||
1048 | &check_key_generation_completion, | ||
1049 | gc); | ||
1050 | return gc; | ||
1051 | } | ||
1052 | |||
1053 | |||
1054 | /** | ||
1055 | * Abort RSA key generation. | ||
1056 | * | ||
1057 | * @param gc key generation context to abort | ||
1058 | */ | ||
1059 | void | ||
1060 | GNUNET_CRYPTO_rsa_key_create_stop (struct GNUNET_CRYPTO_RsaKeyGenerationContext *gc) | ||
1061 | { | ||
1062 | GNUNET_SCHEDULER_cancel (gc->read_task); | ||
1063 | if (NULL != gc->gnunet_rsa) | ||
1064 | { | ||
1065 | (void) GNUNET_OS_process_kill (gc->gnunet_rsa, SIGKILL); | ||
1066 | GNUNET_break (GNUNET_OK == | ||
1067 | GNUNET_OS_process_wait (gc->gnunet_rsa)); | ||
1068 | GNUNET_OS_process_destroy (gc->gnunet_rsa); | ||
1069 | } | ||
1070 | if (NULL != gc->filename) | ||
1071 | { | ||
1072 | if (0 != UNLINK (gc->filename)) | ||
1073 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", gc->filename); | ||
1074 | GNUNET_free (gc->filename); | ||
1075 | } | ||
1076 | if (NULL != gc->pk) | ||
1077 | GNUNET_CRYPTO_rsa_key_free (gc->pk); | ||
1078 | GNUNET_free (gc); | ||
1079 | } | ||
1080 | |||
1081 | |||
1082 | /** | ||
789 | * Setup a hostkey file for a peer given the name of the | 1083 | * Setup a hostkey file for a peer given the name of the |
790 | * configuration file (!). This function is used so that | 1084 | * configuration file (!). This function is used so that |
791 | * at a later point code can be certain that reading a | 1085 | * at a later point code can be certain that reading a |