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