- test and log for bug #0003423
[oweals/gnunet.git] / src / gnsrecord / gnsrecord_crypto.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009-2013 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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 expire block expiration
76  * @param label the name for the records
77  * @param rd record data
78  * @param rd_count number of records
79  * @return NULL on error (block too large)
80  */
81 struct GNUNET_GNSRECORD_Block *
82 GNUNET_GNSRECORD_block_create (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
83                                struct GNUNET_TIME_Absolute expire,
84                                const char *label,
85                                const struct GNUNET_GNSRECORD_Data *rd,
86                                unsigned int rd_count)
87 {
88   size_t payload_len = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
89   char payload[sizeof (uint32_t) + payload_len];
90   struct GNUNET_GNSRECORD_Block *block;
91   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
92   struct GNUNET_CRYPTO_EcdsaPrivateKey *dkey;
93   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
94   struct GNUNET_CRYPTO_SymmetricSessionKey skey;
95   struct GNUNET_GNSRECORD_Data rdc[rd_count];
96   uint32_t rd_count_nbo;
97   unsigned int i;
98   struct GNUNET_TIME_Absolute now;
99
100   if (payload_len > GNUNET_GNSRECORD_MAX_BLOCK_SIZE)
101     return NULL;
102   /* convert relative to absolute times */
103   now = GNUNET_TIME_absolute_get ();
104   for (i=0;i<rd_count;i++)
105   {
106     rdc[i] = rd[i];
107     if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
108     {
109       struct GNUNET_TIME_Relative t;
110
111       /* encrypted blocks must never have relative expiration times, convert! */
112       rdc[i].flags &= ~GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
113       t.rel_value_us = rdc[i].expiration_time;
114       rdc[i].expiration_time = GNUNET_TIME_absolute_add (now, t).abs_value_us;
115     }
116   }
117   /* serialize */
118   rd_count_nbo = htonl (rd_count);
119   memcpy (payload, &rd_count_nbo, sizeof (uint32_t));
120   GNUNET_assert (payload_len ==
121                  GNUNET_GNSRECORD_records_serialize (rd_count, rdc,
122                                                      payload_len, &payload[sizeof (uint32_t)]));
123   block = GNUNET_malloc (sizeof (struct GNUNET_GNSRECORD_Block) +
124                          sizeof (uint32_t) + payload_len);
125   block->purpose.size = htonl (sizeof (uint32_t) + payload_len +
126                                sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
127                                sizeof (struct GNUNET_TIME_AbsoluteNBO));
128   block->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN);
129   block->expiration_time = GNUNET_TIME_absolute_hton (expire);
130   /* encrypt and sign */
131   dkey = GNUNET_CRYPTO_ecdsa_private_key_derive (key,
132                                                  label,
133                                                  "gns");
134   GNUNET_CRYPTO_ecdsa_key_get_public (dkey,
135                                     &block->derived_key);
136   GNUNET_CRYPTO_ecdsa_key_get_public (key,
137                                     &pkey);
138   derive_block_aes_key (&iv, &skey, label, &pkey);
139   GNUNET_break (payload_len + sizeof (uint32_t) ==
140                 GNUNET_CRYPTO_symmetric_encrypt (payload, payload_len + sizeof (uint32_t),
141                                                  &skey, &iv,
142                                                  &block[1]));
143   if (GNUNET_OK !=
144       GNUNET_CRYPTO_ecdsa_sign (dkey,
145                               &block->purpose,
146                               &block->signature))
147   {
148     GNUNET_break (0);
149     GNUNET_free (dkey);
150     GNUNET_free (block);
151     return NULL;
152   }
153   GNUNET_free (dkey);
154   return block;
155 }
156
157
158 /**
159  * Check if a signature is valid.  This API is used by the GNS Block
160  * to validate signatures received from the network.
161  *
162  * @param block block to verify
163  * @return #GNUNET_OK if the signature is valid
164  */
165 int
166 GNUNET_GNSRECORD_block_verify (const struct GNUNET_GNSRECORD_Block *block)
167 {
168   return GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN,
169                                    &block->purpose,
170                                    &block->signature,
171                                    &block->derived_key);
172 }
173
174
175 /**
176  * Decrypt block.
177  *
178  * @param block block to decrypt
179  * @param zone_key public key of the zone
180  * @param label the name for the records
181  * @param proc function to call with the result
182  * @param proc_cls closure for proc
183  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the block was
184  *        not well-formed
185  */
186 int
187 GNUNET_GNSRECORD_block_decrypt (const struct GNUNET_GNSRECORD_Block *block,
188                                 const struct GNUNET_CRYPTO_EcdsaPublicKey *zone_key,
189                                 const char *label,
190                                 GNUNET_GNSRECORD_RecordCallback proc,
191                                 void *proc_cls)
192 {
193   size_t payload_len = ntohl (block->purpose.size) -
194     sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) -
195     sizeof (struct GNUNET_TIME_AbsoluteNBO);
196   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
197   struct GNUNET_CRYPTO_SymmetricSessionKey skey;
198
199   if (ntohl (block->purpose.size) <
200       sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
201       sizeof (struct GNUNET_TIME_AbsoluteNBO))
202   {
203     GNUNET_break_op (0);
204     return GNUNET_SYSERR;
205   }
206   derive_block_aes_key (&iv, &skey, label, zone_key);
207   {
208     char payload[payload_len];
209     uint32_t rd_count;
210
211     GNUNET_break (payload_len ==
212                   GNUNET_CRYPTO_symmetric_decrypt (&block[1], payload_len,
213                                              &skey, &iv,
214                                              payload));
215     memcpy (&rd_count,
216             payload,
217             sizeof (uint32_t));
218     rd_count = ntohl (rd_count);
219     if (rd_count > 2048)
220     {
221       /* limit to sane value */
222       GNUNET_break_op (0);
223       return GNUNET_SYSERR;
224     }
225     {
226       struct GNUNET_GNSRECORD_Data rd[rd_count];
227       unsigned int i;
228       unsigned int j;
229       unsigned int k;
230       struct GNUNET_TIME_Absolute now;
231
232       if (GNUNET_OK !=
233           GNUNET_GNSRECORD_records_deserialize (payload_len - sizeof (uint32_t),
234                                                 &payload[sizeof (uint32_t)],
235                                                 rd_count,
236                                                 rd))
237       {
238         GNUNET_break_op (0);
239         return GNUNET_SYSERR;
240       }
241       /* hide expired records */
242       now = GNUNET_TIME_absolute_get ();
243       j = 0;
244       for (i=0;i<rd_count;i++)
245       {
246         if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_PENDING))
247           continue; /* PENDING should never be used */
248         if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
249         {
250           /* encrypted blocks must never have relative expiration times, skip! */
251           GNUNET_break_op (0);
252           continue;
253         }
254
255         if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW_RECORD))
256         {
257           int include_record = GNUNET_YES;
258           /* Shadow record, figure out if we have a not expired active record */
259           for (k=0;k<rd_count;k++)
260           {
261             if (k == i)
262               continue;
263             if (rd[i].expiration_time < now.abs_value_us)
264               include_record = GNUNET_NO; /* Shadow record is expired */
265             if ((rd[k].record_type == rd[i].record_type)
266                 && (rd[k].expiration_time >= now.abs_value_us)
267                 && (0 == (rd[k].flags & GNUNET_GNSRECORD_RF_SHADOW_RECORD)))
268               include_record = GNUNET_NO; /* We have a non-expired, non-shadow record of the same type */
269           }
270           if (GNUNET_YES == include_record)
271           {
272             rd[i].flags ^= GNUNET_GNSRECORD_RF_SHADOW_RECORD; /* Remove Flag */
273             if (j != i)
274               rd[j] = rd[i];
275             j++;
276           }
277         }
278         else if (rd[i].expiration_time >= now.abs_value_us)
279         {
280           /* Include this record */
281           if (j != i)
282             rd[j] = rd[i];
283           j++;
284         }
285       }
286       rd_count = j;
287       if (NULL != proc)
288         proc (proc_cls, rd_count, (0 != rd_count) ? rd : NULL);
289     }
290   }
291   return GNUNET_OK;
292 }
293
294
295 /**
296  * Calculate the DHT query for a given @a label in a given @a zone.
297  *
298  * @param zone private key of the zone
299  * @param label label of the record
300  * @param query hash to use for the query
301  */
302 void
303 GNUNET_GNSRECORD_query_from_private_key (const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
304                                          const char *label,
305                                          struct GNUNET_HashCode *query)
306 {
307   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
308
309   GNUNET_CRYPTO_ecdsa_key_get_public (zone, &pub);
310   GNUNET_GNSRECORD_query_from_public_key (&pub, label, query);
311 }
312
313
314 /**
315  * Calculate the DHT query for a given @a label in a given @a zone.
316  *
317  * @param pub public key of the zone
318  * @param label label of the record
319  * @param query hash to use for the query
320  */
321 void
322 GNUNET_GNSRECORD_query_from_public_key (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub,
323                                         const char *label,
324                                         struct GNUNET_HashCode *query)
325 {
326   struct GNUNET_CRYPTO_EcdsaPublicKey pd;
327
328   GNUNET_CRYPTO_ecdsa_public_key_derive (pub, label, "gns", &pd);
329   GNUNET_CRYPTO_hash (&pd, sizeof (pd), query);
330 }
331
332
333 /* end of gnsrecord_crypto.c */