fixing #3799: only unindex if DB operations succeeded previously in the first place...
[oweals/gnunet.git] / src / fs / fs_publish_ublock.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2010, 2012, 2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file fs/fs_publish_ublock.c
23  * @brief publish a UBLOCK in GNUnet
24  * @see https://gnunet.org/encoding and #2564
25  * @author Krista Bennett
26  * @author Christian Grothoff
27  */
28 #include "platform.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_signatures.h"
31 #include "fs_publish_ublock.h"
32 #include "fs_api.h"
33 #include "fs_tree.h"
34
35
36 /**
37  * Derive the key for symmetric encryption/decryption from
38  * the public key and the label.
39  *
40  * @param skey where to store symmetric key
41  * @param iv where to store the IV
42  * @param label label to use for key derivation
43  * @param pub public key to use for key derivation
44  */
45 static void
46 derive_ublock_encryption_key (struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
47                               struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
48                               const char *label,
49                               const struct GNUNET_CRYPTO_EcdsaPublicKey *pub)
50 {
51   struct GNUNET_HashCode key;
52
53   /* derive key from 'label' and public key of the namespace */
54   GNUNET_assert (GNUNET_YES ==
55                  GNUNET_CRYPTO_kdf (&key, sizeof (key),
56                                     "UBLOCK-ENC", strlen ("UBLOCK-ENC"),
57                                     label, strlen (label),
58                                     pub, sizeof (*pub),
59                                     NULL, 0));
60   GNUNET_CRYPTO_hash_to_aes_key (&key, skey, iv);
61 }
62
63
64 /**
65  * Decrypt the given UBlock, storing the result in output.
66  *
67  * @param input input data
68  * @param input_len number of bytes in @a input
69  * @param ns public key under which the UBlock was stored
70  * @param label label under which the UBlock was stored
71  * @param output where to write the result, has input_len bytes
72  */
73 void
74 GNUNET_FS_ublock_decrypt_ (const void *input,
75                            size_t input_len,
76                            const struct GNUNET_CRYPTO_EcdsaPublicKey *ns,
77                            const char *label,
78                            void *output)
79 {
80   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
81   struct GNUNET_CRYPTO_SymmetricSessionKey skey;
82
83   derive_ublock_encryption_key (&skey, &iv,
84                                 label, ns);
85   GNUNET_CRYPTO_symmetric_decrypt (input, input_len,
86                              &skey, &iv,
87                              output);
88 }
89
90
91 /**
92  * Context for 'ublock_put_cont'.
93  */
94 struct GNUNET_FS_PublishUblockContext
95 {
96
97   /**
98    * Function to call when done.
99    */
100   GNUNET_FS_UBlockContinuation cont;
101
102   /**
103    * Closure of 'cont'.
104    */
105   void *cont_cls;
106
107   /**
108    * Handle for active datastore operation.
109    */
110   struct GNUNET_DATASTORE_QueueEntry *qre;
111
112   /**
113    * Task to run continuation asynchronously.
114    */
115   struct GNUNET_SCHEDULER_Task * task;
116
117 };
118
119
120 /**
121  * Continuation of #GNUNET_FS_publish_ublock_().
122  *
123  * @param cls closure of type "struct GNUNET_FS_PublishUblockContext*"
124  * @param success GNUNET_SYSERR on failure (including timeout/queue drop)
125  *                GNUNET_NO if content was already there
126  *                GNUNET_YES (or other positive value) on success
127  * @param min_expiration minimum expiration time required for 0-priority content to be stored
128  *                by the datacache at this time, zero for unknown, forever if we have no
129  *                space for 0-priority content
130  * @param msg NULL on success, otherwise an error message
131  */
132 static void
133 ublock_put_cont (void *cls,
134                  int32_t success,
135                  struct GNUNET_TIME_Absolute min_expiration,
136                  const char *msg)
137 {
138   struct GNUNET_FS_PublishUblockContext *uc = cls;
139
140   uc->qre = NULL;
141   uc->cont (uc->cont_cls, msg);
142   GNUNET_free (uc);
143 }
144
145
146 /**
147  * Run the continuation.
148  *
149  * @param cls the `struct GNUNET_FS_PublishUblockContext *`
150  * @param tc scheduler context
151  */
152 static void
153 run_cont (void *cls,
154           const struct GNUNET_SCHEDULER_TaskContext *tc)
155 {
156   struct GNUNET_FS_PublishUblockContext *uc = cls;
157
158   uc->task = NULL;
159   uc->cont (uc->cont_cls, NULL);
160   GNUNET_free (uc);
161 }
162
163
164 /**
165  * Publish a UBlock.
166  *
167  * @param h handle to the file sharing subsystem
168  * @param dsh datastore handle to use for storage operation
169  * @param label identifier to use
170  * @param ulabel update label to use, may be an empty string for none
171  * @param ns namespace to publish in
172  * @param meta metadata to use
173  * @param uri URI to refer to in the UBlock
174  * @param bo per-block options
175  * @param options publication options
176  * @param cont continuation
177  * @param cont_cls closure for @a cont
178  * @return NULL on error (@a cont will still be called)
179  */
180 struct GNUNET_FS_PublishUblockContext *
181 GNUNET_FS_publish_ublock_ (struct GNUNET_FS_Handle *h,
182                            struct GNUNET_DATASTORE_Handle *dsh,
183                            const char *label,
184                            const char *ulabel,
185                            const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
186                            const struct GNUNET_CONTAINER_MetaData *meta,
187                            const struct GNUNET_FS_Uri *uri,
188                            const struct GNUNET_FS_BlockOptions *bo,
189                            enum GNUNET_FS_PublishOptions options,
190                            GNUNET_FS_UBlockContinuation cont, void *cont_cls)
191 {
192   struct GNUNET_FS_PublishUblockContext *uc;
193   struct GNUNET_HashCode query;
194   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
195   struct GNUNET_CRYPTO_SymmetricSessionKey skey;
196   struct GNUNET_CRYPTO_EcdsaPrivateKey *nsd;
197   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
198   char *uris;
199   size_t size;
200   char *kbe;
201   char *sptr;
202   ssize_t mdsize;
203   size_t slen;
204   size_t ulen;
205   struct UBlock *ub_plain;
206   struct UBlock *ub_enc;
207
208   /* compute ublock to publish */
209   if (NULL == meta)
210     mdsize = 0;
211   else
212     mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
213   GNUNET_assert (mdsize >= 0);
214   uris = GNUNET_FS_uri_to_string (uri);
215   slen = strlen (uris) + 1;
216   if (NULL == ulabel)
217     ulen = 1;
218   else
219     ulen = strlen (ulabel) + 1;
220   size = mdsize + sizeof (struct UBlock) + slen + ulen;
221   if (size > MAX_UBLOCK_SIZE)
222   {
223     size = MAX_UBLOCK_SIZE;
224     mdsize = size - sizeof (struct UBlock) - (slen + ulen);
225   }
226   ub_plain = GNUNET_malloc (size);
227   kbe = (char *) &ub_plain[1];
228   if (NULL != ulabel)
229     memcpy (kbe, ulabel, ulen);
230   kbe += ulen;
231   memcpy (kbe, uris, slen);
232   kbe += slen;
233   GNUNET_free (uris);
234   sptr = kbe;
235   if (NULL != meta)
236     mdsize =
237       GNUNET_CONTAINER_meta_data_serialize (meta, &sptr, mdsize,
238                                             GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
239   if (-1 == mdsize)
240   {
241     GNUNET_break (0);
242     GNUNET_free (ub_plain);
243     cont (cont_cls, _("Internal error."));
244     return NULL;
245   }
246   size = sizeof (struct UBlock) + slen + mdsize + ulen;
247
248   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
249               "Publishing under identifier `%s'\n",
250               label);
251   /* get public key of the namespace */
252   GNUNET_CRYPTO_ecdsa_key_get_public (ns,
253                                     &pub);
254   derive_ublock_encryption_key (&skey, &iv,
255                                 label, &pub);
256
257   /* encrypt ublock */
258   ub_enc = GNUNET_malloc (size);
259   GNUNET_CRYPTO_symmetric_encrypt (&ub_plain[1],
260                              ulen + slen + mdsize,
261                              &skey, &iv,
262                              &ub_enc[1]);
263   GNUNET_free (ub_plain);
264   ub_enc->purpose.size = htonl (ulen + slen + mdsize +
265                                 sizeof (struct UBlock)
266                                 - sizeof (struct GNUNET_CRYPTO_EcdsaSignature));
267   ub_enc->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_UBLOCK);
268
269   /* derive signing-key from 'label' and public key of the namespace */
270   nsd = GNUNET_CRYPTO_ecdsa_private_key_derive (ns, label, "fs-ublock");
271   GNUNET_CRYPTO_ecdsa_key_get_public (nsd,
272                                     &ub_enc->verification_key);
273   GNUNET_assert (GNUNET_OK ==
274                  GNUNET_CRYPTO_ecdsa_sign (nsd,
275                                          &ub_enc->purpose,
276                                          &ub_enc->signature));
277   GNUNET_CRYPTO_hash (&ub_enc->verification_key,
278                       sizeof (ub_enc->verification_key),
279                       &query);
280   GNUNET_free (nsd);
281
282   uc = GNUNET_new (struct GNUNET_FS_PublishUblockContext);
283   uc->cont = cont;
284   uc->cont_cls = cont_cls;
285   if (NULL != dsh)
286   {
287     uc->qre =
288       GNUNET_DATASTORE_put (dsh, 0, &query,
289                             ulen + slen + mdsize + sizeof (struct UBlock),
290                             ub_enc,
291                             GNUNET_BLOCK_TYPE_FS_UBLOCK,
292                             bo->content_priority,
293                             bo->anonymity_level,
294                             bo->replication_level,
295                             bo->expiration_time,
296                             -2, 1,
297                             GNUNET_CONSTANTS_SERVICE_TIMEOUT,
298                             &ublock_put_cont, uc);
299   }
300   else
301   {
302     uc->task = GNUNET_SCHEDULER_add_now (&run_cont,
303                                          uc);
304   }
305   return uc;
306 }
307
308
309 /**
310  * Abort UBlock publishing operation.
311  *
312  * @param uc operation to abort.
313  */
314 void
315 GNUNET_FS_publish_ublock_cancel_ (struct GNUNET_FS_PublishUblockContext *uc)
316 {
317   if (NULL != uc->qre)
318     GNUNET_DATASTORE_cancel (uc->qre);
319   if (NULL != uc->task)
320     GNUNET_SCHEDULER_cancel (uc->task);
321   GNUNET_free (uc);
322 }
323
324 /* end of fs_publish_ublock.c */