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