error handling
[oweals/gnunet.git] / src / gnsrecord / gnsrecord_crypto.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009-2013, 2018 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 gnsrecord/gnsrecord_crypto.c
23  * @brief API for GNS record-related crypto
24  * @author Martin Schanzenbach
25  * @author Matthias Wachs
26  * @author Christian Grothoff
27  */
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_signatures.h"
32 #include "gnunet_arm_service.h"
33 #include "gnunet_gnsrecord_lib.h"
34 #include "gnunet_dnsparser_lib.h"
35 #include "gnunet_tun_lib.h"
36
37
38 #define LOG(kind, ...) GNUNET_log_from (kind, "gnsrecord", __VA_ARGS__)
39
40
41 /**
42  * Derive session key and iv from label and public key.
43  *
44  * @param iv initialization vector to initialize
45  * @param skey session key to initialize
46  * @param label label to use for KDF
47  * @param pub public key to use for KDF
48  */
49 static void
50 derive_block_aes_key (struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
51                       struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
52                       const char *label,
53                       const struct GNUNET_CRYPTO_EcdsaPublicKey *pub)
54 {
55   static const char ctx_key[] = "gns-aes-ctx-key";
56   static const char ctx_iv[] = "gns-aes-ctx-iv";
57
58   GNUNET_CRYPTO_kdf (skey, sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey),
59                      ctx_key, strlen (ctx_key),
60                      pub, sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
61                      label, strlen (label),
62                      NULL, 0);
63   GNUNET_CRYPTO_kdf (iv, sizeof(struct
64                                 GNUNET_CRYPTO_SymmetricInitializationVector),
65                      ctx_iv, strlen (ctx_iv),
66                      pub, sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
67                      label, strlen (label),
68                      NULL, 0);
69 }
70
71
72 /**
73  * Sign name and records
74  *
75  * @param key the private key
76  * @param pkey associated public key
77  * @param expire block expiration
78  * @param label the name for the records
79  * @param rd record data
80  * @param rd_count number of records
81  * @return NULL on error (block too large)
82  */
83 static struct GNUNET_GNSRECORD_Block *
84 block_create (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
85               const struct GNUNET_CRYPTO_EcdsaPublicKey *pkey,
86               struct GNUNET_TIME_Absolute expire,
87               const char *label,
88               const struct GNUNET_GNSRECORD_Data *rd,
89               unsigned int rd_count)
90 {
91   ssize_t payload_len = GNUNET_GNSRECORD_records_get_size (rd_count,
92                                                            rd);
93   struct GNUNET_GNSRECORD_Block *block;
94   struct GNUNET_CRYPTO_EcdsaPrivateKey *dkey;
95   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
96   struct GNUNET_CRYPTO_SymmetricSessionKey skey;
97   struct GNUNET_GNSRECORD_Data rdc[GNUNET_NZL (rd_count)];
98   uint32_t rd_count_nbo;
99   struct GNUNET_TIME_Absolute now;
100
101   if (payload_len < 0)
102   {
103     GNUNET_break (0);
104     return NULL;
105   }
106   if (payload_len > GNUNET_GNSRECORD_MAX_BLOCK_SIZE)
107   {
108     GNUNET_break (0);
109     return NULL;
110   }
111   /* convert relative to absolute times */
112   now = GNUNET_TIME_absolute_get ();
113   for (unsigned int i = 0; i < rd_count; i++)
114   {
115     rdc[i] = rd[i];
116     if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
117     {
118       struct GNUNET_TIME_Relative t;
119
120       /* encrypted blocks must never have relative expiration times, convert! */
121       rdc[i].flags &= ~GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
122       t.rel_value_us = rdc[i].expiration_time;
123       rdc[i].expiration_time = GNUNET_TIME_absolute_add (now, t).abs_value_us;
124     }
125   }
126   /* serialize */
127   rd_count_nbo = htonl (rd_count);
128   {
129     char payload[sizeof(uint32_t) + payload_len];
130
131     GNUNET_memcpy (payload,
132                    &rd_count_nbo,
133                    sizeof(uint32_t));
134     GNUNET_assert (payload_len ==
135                    GNUNET_GNSRECORD_records_serialize (rd_count,
136                                                        rdc,
137                                                        payload_len,
138                                                        &payload[sizeof(uint32_t)
139                                                        ]));
140     block = GNUNET_malloc (sizeof(struct GNUNET_GNSRECORD_Block)
141                            + sizeof(uint32_t)
142                            + payload_len);
143     block->purpose.size = htonl (sizeof(uint32_t)
144                                  + payload_len
145                                  + sizeof(struct
146                                           GNUNET_CRYPTO_EccSignaturePurpose)
147                                  + sizeof(struct GNUNET_TIME_AbsoluteNBO));
148     block->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN);
149     block->expiration_time = GNUNET_TIME_absolute_hton (expire);
150     /* encrypt and sign */
151     dkey = GNUNET_CRYPTO_ecdsa_private_key_derive (key,
152                                                    label,
153                                                    "gns");
154     GNUNET_CRYPTO_ecdsa_key_get_public (dkey,
155                                         &block->derived_key);
156     derive_block_aes_key (&iv,
157                           &skey,
158                           label,
159                           pkey);
160     GNUNET_break (payload_len + sizeof(uint32_t) ==
161                   GNUNET_CRYPTO_symmetric_encrypt (payload,
162                                                    payload_len
163                                                    + sizeof(uint32_t),
164                                                    &skey,
165                                                    &iv,
166                                                    &block[1]));
167   }
168   if (GNUNET_OK !=
169       GNUNET_CRYPTO_ecdsa_sign (dkey,
170                                 &block->purpose,
171                                 &block->signature))
172   {
173     GNUNET_break (0);
174     GNUNET_free (dkey);
175     GNUNET_free (block);
176     return NULL;
177   }
178   GNUNET_free (dkey);
179   return block;
180 }
181
182
183 /**
184  * Sign name and records
185  *
186  * @param key the private key
187  * @param expire block expiration
188  * @param label the name for the records
189  * @param rd record data
190  * @param rd_count number of records
191  * @return NULL on error (block too large)
192  */
193 struct GNUNET_GNSRECORD_Block *
194 GNUNET_GNSRECORD_block_create (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
195                                struct GNUNET_TIME_Absolute expire,
196                                const char *label,
197                                const struct GNUNET_GNSRECORD_Data *rd,
198                                unsigned int rd_count)
199 {
200   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
201
202   GNUNET_CRYPTO_ecdsa_key_get_public (key,
203                                       &pkey);
204   return block_create (key,
205                        &pkey,
206                        expire,
207                        label,
208                        rd,
209                        rd_count);
210 }
211
212
213 /**
214  * Line in cache mapping private keys to public keys.
215  */
216 struct KeyCacheLine
217 {
218   /**
219    * A private key.
220    */
221   struct GNUNET_CRYPTO_EcdsaPrivateKey key;
222
223   /**
224    * Associated public key.
225    */
226   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
227 };
228
229
230 /**
231  * Sign name and records, cache derived public key (also keeps the
232  * private key in static memory, so do not use this function if
233  * keeping the private key in the process'es RAM is a major issue).
234  *
235  * @param key the private key
236  * @param expire block expiration
237  * @param label the name for the records
238  * @param rd record data
239  * @param rd_count number of records
240  * @return NULL on error (block too large)
241  */
242 struct GNUNET_GNSRECORD_Block *
243 GNUNET_GNSRECORD_block_create2 (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
244                                 struct GNUNET_TIME_Absolute expire,
245                                 const char *label,
246                                 const struct GNUNET_GNSRECORD_Data *rd,
247                                 unsigned int rd_count)
248 {
249 #define CSIZE 64
250   static struct KeyCacheLine cache[CSIZE];
251   struct KeyCacheLine *line;
252
253   line = &cache[(*(unsigned int *) key) % CSIZE];
254   if (0 != memcmp (&line->key,
255                    key,
256                    sizeof(*key)))
257   {
258     /* cache miss, recompute */
259     line->key = *key;
260     GNUNET_CRYPTO_ecdsa_key_get_public (key,
261                                         &line->pkey);
262   }
263 #undef CSIZE
264   return block_create (key,
265                        &line->pkey,
266                        expire,
267                        label,
268                        rd,
269                        rd_count);
270 }
271
272
273 /**
274  * Check if a signature is valid.  This API is used by the GNS Block
275  * to validate signatures received from the network.
276  *
277  * @param block block to verify
278  * @return #GNUNET_OK if the signature is valid
279  */
280 int
281 GNUNET_GNSRECORD_block_verify (const struct GNUNET_GNSRECORD_Block *block)
282 {
283   return GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN,
284                                      &block->purpose,
285                                      &block->signature,
286                                      &block->derived_key);
287 }
288
289
290 /**
291  * Decrypt block.
292  *
293  * @param block block to decrypt
294  * @param zone_key public key of the zone
295  * @param label the name for the records
296  * @param proc function to call with the result
297  * @param proc_cls closure for proc
298  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the block was
299  *        not well-formed
300  */
301 int
302 GNUNET_GNSRECORD_block_decrypt (const struct GNUNET_GNSRECORD_Block *block,
303                                 const struct
304                                 GNUNET_CRYPTO_EcdsaPublicKey *zone_key,
305                                 const char *label,
306                                 GNUNET_GNSRECORD_RecordCallback proc,
307                                 void *proc_cls)
308 {
309   size_t payload_len = ntohl (block->purpose.size)
310                        - sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
311                        - sizeof(struct GNUNET_TIME_AbsoluteNBO);
312   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
313   struct GNUNET_CRYPTO_SymmetricSessionKey skey;
314
315   if (ntohl (block->purpose.size) <
316       sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
317       + sizeof(struct GNUNET_TIME_AbsoluteNBO))
318   {
319     GNUNET_break_op (0);
320     return GNUNET_SYSERR;
321   }
322   derive_block_aes_key (&iv,
323                         &skey,
324                         label,
325                         zone_key);
326   {
327     char payload[payload_len];
328     uint32_t rd_count;
329
330     GNUNET_break (payload_len ==
331                   GNUNET_CRYPTO_symmetric_decrypt (&block[1], payload_len,
332                                                    &skey, &iv,
333                                                    payload));
334     GNUNET_memcpy (&rd_count,
335                    payload,
336                    sizeof(uint32_t));
337     rd_count = ntohl (rd_count);
338     if (rd_count > 2048)
339     {
340       /* limit to sane value */
341       GNUNET_break_op (0);
342       return GNUNET_SYSERR;
343     }
344     {
345       struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)];
346       unsigned int j;
347       struct GNUNET_TIME_Absolute now;
348
349       if (GNUNET_OK !=
350           GNUNET_GNSRECORD_records_deserialize (payload_len - sizeof(uint32_t),
351                                                 &payload[sizeof(uint32_t)],
352                                                 rd_count,
353                                                 rd))
354       {
355         GNUNET_break_op (0);
356         return GNUNET_SYSERR;
357       }
358       /* hide expired records */
359       now = GNUNET_TIME_absolute_get ();
360       j = 0;
361       for (unsigned int i = 0; i < rd_count; i++)
362       {
363         if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
364         {
365           /* encrypted blocks must never have relative expiration times, skip! */
366           GNUNET_break_op (0);
367           continue;
368         }
369
370         if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW_RECORD))
371         {
372           int include_record = GNUNET_YES;
373           /* Shadow record, figure out if we have a not expired active record */
374           for (unsigned int k = 0; k < rd_count; k++)
375           {
376             if (k == i)
377               continue;
378             if (rd[i].expiration_time < now.abs_value_us)
379               include_record = GNUNET_NO;       /* Shadow record is expired */
380             if ((rd[k].record_type == rd[i].record_type) &&
381                 (rd[k].expiration_time >= now.abs_value_us) &&
382                 (0 == (rd[k].flags & GNUNET_GNSRECORD_RF_SHADOW_RECORD)))
383             {
384               include_record = GNUNET_NO;         /* We have a non-expired, non-shadow record of the same type */
385               GNUNET_log (GNUNET_ERROR_TYPE_INFO,
386                           "Ignoring shadow record\n");
387               break;
388             }
389           }
390           if (GNUNET_YES == include_record)
391           {
392             rd[i].flags ^= GNUNET_GNSRECORD_RF_SHADOW_RECORD;       /* Remove Flag */
393             if (j != i)
394               rd[j] = rd[i];
395             j++;
396           }
397         }
398         else if (rd[i].expiration_time >= now.abs_value_us)
399         {
400           /* Include this record */
401           if (j != i)
402             rd[j] = rd[i];
403           j++;
404         }
405         else
406         {
407           struct GNUNET_TIME_Absolute at;
408
409           at.abs_value_us = rd[i].expiration_time;
410           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
411                       "Excluding record that expired %s (%llu ago)\n",
412                       GNUNET_STRINGS_absolute_time_to_string (at),
413                       (unsigned long long) rd[i].expiration_time
414                       - now.abs_value_us);
415         }
416       }
417       rd_count = j;
418       if (NULL != proc)
419         proc (proc_cls,
420               rd_count,
421               (0 != rd_count) ? rd : NULL);
422     }
423   }
424   return GNUNET_OK;
425 }
426
427
428 /**
429  * Calculate the DHT query for a given @a label in a given @a zone.
430  *
431  * @param zone private key of the zone
432  * @param label label of the record
433  * @param query hash to use for the query
434  */
435 void
436 GNUNET_GNSRECORD_query_from_private_key (const struct
437                                          GNUNET_CRYPTO_EcdsaPrivateKey *zone,
438                                          const char *label,
439                                          struct GNUNET_HashCode *query)
440 {
441   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
442
443   GNUNET_CRYPTO_ecdsa_key_get_public (zone,
444                                       &pub);
445   GNUNET_GNSRECORD_query_from_public_key (&pub,
446                                           label,
447                                           query);
448 }
449
450
451 /**
452  * Calculate the DHT query for a given @a label in a given @a zone.
453  *
454  * @param pub public key of the zone
455  * @param label label of the record
456  * @param query hash to use for the query
457  */
458 void
459 GNUNET_GNSRECORD_query_from_public_key (const struct
460                                         GNUNET_CRYPTO_EcdsaPublicKey *pub,
461                                         const char *label,
462                                         struct GNUNET_HashCode *query)
463 {
464   struct GNUNET_CRYPTO_EcdsaPublicKey pd;
465   GNUNET_CRYPTO_ecdsa_public_key_derive (pub,
466                                          label,
467                                          "gns",
468                                          &pd);
469   GNUNET_CRYPTO_hash (&pd,
470                       sizeof(pd),
471                       query);
472 }
473
474
475 /* end of gnsrecord_crypto.c */