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