error handling
[oweals/gnunet.git] / src / fs / fs_publish_ublock.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2010, 2012, 2013 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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
48                               *iv,
49                               const char *label,
50                               const struct GNUNET_CRYPTO_EcdsaPublicKey *pub)
51 {
52   struct GNUNET_HashCode key;
53
54   /* derive key from 'label' and public key of the namespace */
55   GNUNET_assert (GNUNET_YES ==
56                  GNUNET_CRYPTO_kdf (&key, sizeof(key),
57                                     "UBLOCK-ENC", strlen ("UBLOCK-ENC"),
58                                     label, strlen (label),
59                                     pub, sizeof(*pub),
60                                     NULL, 0));
61   GNUNET_CRYPTO_hash_to_aes_key (&key, skey, iv);
62 }
63
64
65 /**
66  * Decrypt the given UBlock, storing the result in output.
67  *
68  * @param input input data
69  * @param input_len number of bytes in @a input
70  * @param ns public key under which the UBlock was stored
71  * @param label label under which the UBlock was stored
72  * @param output where to write the result, has input_len bytes
73  */
74 void
75 GNUNET_FS_ublock_decrypt_ (const void *input,
76                            size_t input_len,
77                            const struct GNUNET_CRYPTO_EcdsaPublicKey *ns,
78                            const char *label,
79                            void *output)
80 {
81   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
82   struct GNUNET_CRYPTO_SymmetricSessionKey skey;
83
84   derive_ublock_encryption_key (&skey, &iv,
85                                 label, ns);
86   GNUNET_CRYPTO_symmetric_decrypt (input, input_len,
87                                    &skey, &iv,
88                                    output);
89 }
90
91
92 /**
93  * Context for 'ublock_put_cont'.
94  */
95 struct GNUNET_FS_PublishUblockContext
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  * Continuation of #GNUNET_FS_publish_ublock_().
121  *
122  * @param cls closure of type "struct GNUNET_FS_PublishUblockContext*"
123  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop)
124  *                #GNUNET_NO if content was already there
125  *                #GNUNET_YES (or other positive value) on success
126  * @param min_expiration minimum expiration time required for 0-priority content to be stored
127  *                by the datacache at this time, zero for unknown, forever if we have no
128  *                space for 0-priority content
129  * @param msg NULL on success, otherwise an error message
130  */
131 static void
132 ublock_put_cont (void *cls,
133                  int32_t success,
134                  struct GNUNET_TIME_Absolute min_expiration,
135                  const char *msg)
136 {
137   struct GNUNET_FS_PublishUblockContext *uc = cls;
138
139   uc->qre = NULL;
140   uc->cont (uc->cont_cls, msg);
141   GNUNET_free (uc);
142 }
143
144
145 /**
146  * Run the continuation.
147  *
148  * @param cls the `struct GNUNET_FS_PublishUblockContext *`
149  */
150 static void
151 run_cont (void *cls)
152 {
153   struct GNUNET_FS_PublishUblockContext *uc = cls;
154
155   uc->task = NULL;
156   uc->cont (uc->cont_cls, NULL);
157   GNUNET_free (uc);
158 }
159
160
161 /**
162  * Publish a UBlock.
163  *
164  * @param h handle to the file sharing subsystem
165  * @param dsh datastore handle to use for storage operation
166  * @param label identifier to use
167  * @param ulabel update label to use, may be an empty string for none
168  * @param ns namespace to publish in
169  * @param meta metadata to use
170  * @param uri URI to refer to in the UBlock
171  * @param bo per-block options
172  * @param options publication options
173  * @param cont continuation
174  * @param cont_cls closure for @a cont
175  * @return NULL on error (@a cont will still be called)
176  */
177 struct GNUNET_FS_PublishUblockContext *
178 GNUNET_FS_publish_ublock_ (struct GNUNET_FS_Handle *h,
179                            struct GNUNET_DATASTORE_Handle *dsh,
180                            const char *label,
181                            const char *ulabel,
182                            const struct GNUNET_CRYPTO_EcdsaPrivateKey *ns,
183                            const struct GNUNET_CONTAINER_MetaData *meta,
184                            const struct GNUNET_FS_Uri *uri,
185                            const struct GNUNET_FS_BlockOptions *bo,
186                            enum GNUNET_FS_PublishOptions options,
187                            GNUNET_FS_UBlockContinuation cont, void *cont_cls)
188 {
189   struct GNUNET_FS_PublishUblockContext *uc;
190   struct GNUNET_HashCode query;
191   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
192   struct GNUNET_CRYPTO_SymmetricSessionKey skey;
193   struct GNUNET_CRYPTO_EcdsaPrivateKey *nsd;
194   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
195   char *uris;
196   size_t size;
197   char *kbe;
198   char *sptr;
199   ssize_t mdsize;
200   size_t slen;
201   size_t ulen;
202   struct UBlock *ub_plain;
203   struct UBlock *ub_enc;
204
205   /* compute ublock to publish */
206   if (NULL == meta)
207     mdsize = 0;
208   else
209     mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
210   GNUNET_assert (mdsize >= 0);
211   uris = GNUNET_FS_uri_to_string (uri);
212   slen = strlen (uris) + 1;
213   if (NULL == ulabel)
214     ulen = 1;
215   else
216     ulen = strlen (ulabel) + 1;
217   size = mdsize + sizeof(struct UBlock) + slen + ulen;
218   if (size > MAX_UBLOCK_SIZE)
219   {
220     size = MAX_UBLOCK_SIZE;
221     mdsize = size - sizeof(struct UBlock) - (slen + ulen);
222   }
223   ub_plain = GNUNET_malloc (size);
224   kbe = (char *) &ub_plain[1];
225   if (NULL != ulabel)
226     GNUNET_memcpy (kbe, ulabel, ulen);
227   kbe += ulen;
228   GNUNET_memcpy (kbe, uris, slen);
229   kbe += slen;
230   GNUNET_free (uris);
231   sptr = kbe;
232   if (NULL != meta)
233     mdsize =
234       GNUNET_CONTAINER_meta_data_serialize (meta, &sptr, mdsize,
235                                             GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
236   if (-1 == mdsize)
237   {
238     GNUNET_break (0);
239     GNUNET_free (ub_plain);
240     cont (cont_cls, _ ("Internal error."));
241     return NULL;
242   }
243   size = sizeof(struct UBlock) + slen + mdsize + ulen;
244
245   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
246               "Publishing under identifier `%s'\n",
247               label);
248   /* get public key of the namespace */
249   GNUNET_CRYPTO_ecdsa_key_get_public (ns,
250                                       &pub);
251   derive_ublock_encryption_key (&skey, &iv,
252                                 label, &pub);
253
254   /* encrypt ublock */
255   ub_enc = GNUNET_malloc (size);
256   GNUNET_CRYPTO_symmetric_encrypt (&ub_plain[1],
257                                    ulen + slen + mdsize,
258                                    &skey, &iv,
259                                    &ub_enc[1]);
260   GNUNET_free (ub_plain);
261   ub_enc->purpose.size = htonl (ulen + slen + mdsize
262                                 + sizeof(struct UBlock)
263                                 - sizeof(struct GNUNET_CRYPTO_EcdsaSignature));
264   ub_enc->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_UBLOCK);
265
266   /* derive signing-key from 'label' and public key of the namespace */
267   nsd = GNUNET_CRYPTO_ecdsa_private_key_derive (ns, label, "fs-ublock");
268   GNUNET_CRYPTO_ecdsa_key_get_public (nsd,
269                                       &ub_enc->verification_key);
270   GNUNET_assert (GNUNET_OK ==
271                  GNUNET_CRYPTO_ecdsa_sign (nsd,
272                                            &ub_enc->purpose,
273                                            &ub_enc->signature));
274   GNUNET_CRYPTO_hash (&ub_enc->verification_key,
275                       sizeof(ub_enc->verification_key),
276                       &query);
277   GNUNET_free (nsd);
278
279   uc = GNUNET_new (struct GNUNET_FS_PublishUblockContext);
280   uc->cont = cont;
281   uc->cont_cls = cont_cls;
282   if (NULL != dsh)
283   {
284     uc->qre =
285       GNUNET_DATASTORE_put (dsh,
286                             0,
287                             &query,
288                             ulen + slen + mdsize + sizeof(struct UBlock),
289                             ub_enc,
290                             GNUNET_BLOCK_TYPE_FS_UBLOCK,
291                             bo->content_priority,
292                             bo->anonymity_level,
293                             bo->replication_level,
294                             bo->expiration_time,
295                             -2, 1,
296                             &ublock_put_cont, uc);
297   }
298   else
299   {
300     uc->task = GNUNET_SCHEDULER_add_now (&run_cont,
301                                          uc);
302   }
303   GNUNET_free (ub_enc);
304   return uc;
305 }
306
307
308 /**
309  * Abort UBlock publishing operation.
310  *
311  * @param uc operation to abort.
312  */
313 void
314 GNUNET_FS_publish_ublock_cancel_ (struct GNUNET_FS_PublishUblockContext *uc)
315 {
316   if (NULL != uc->qre)
317     GNUNET_DATASTORE_cancel (uc->qre);
318   if (NULL != uc->task)
319     GNUNET_SCHEDULER_cancel (uc->task);
320   GNUNET_free (uc);
321 }
322
323
324 /* end of fs_publish_ublock.c */