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