2 This file is part of GNUnet.
3 Copyright (C) 2009-2013, 2018 GNUnet e.V.
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.
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.
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
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"
33 #define LOG(kind,...) GNUNET_log_from (kind, "gnsrecord",__VA_ARGS__)
37 * Derive session key and iv from label and public key.
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
45 derive_block_aes_key (struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
46 struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
48 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub)
50 static const char ctx_key[] = "gns-aes-ctx-key";
51 static const char ctx_iv[] = "gns-aes-ctx-iv";
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),
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),
67 * Sign name and records
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)
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,
82 const struct GNUNET_GNSRECORD_Data *rd,
83 unsigned int rd_count)
85 ssize_t payload_len = GNUNET_GNSRECORD_records_get_size (rd_count,
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;
100 if (payload_len > GNUNET_GNSRECORD_MAX_BLOCK_SIZE)
105 /* convert relative to absolute times */
106 now = GNUNET_TIME_absolute_get ();
107 for (unsigned int i=0;i<rd_count;i++)
110 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
112 struct GNUNET_TIME_Relative t;
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;
121 rd_count_nbo = htonl (rd_count);
123 char payload[sizeof (uint32_t) + payload_len];
125 GNUNET_memcpy (payload,
128 GNUNET_assert (payload_len ==
129 GNUNET_GNSRECORD_records_serialize (rd_count,
132 &payload[sizeof (uint32_t)]));
133 block = GNUNET_malloc (sizeof (struct GNUNET_GNSRECORD_Block) +
136 block->purpose.size = htonl (sizeof (uint32_t) +
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,
146 GNUNET_CRYPTO_ecdsa_key_get_public (dkey,
147 &block->derived_key);
148 derive_block_aes_key (&iv,
152 GNUNET_break (payload_len + sizeof (uint32_t) ==
153 GNUNET_CRYPTO_symmetric_encrypt (payload,
154 payload_len + sizeof (uint32_t),
160 GNUNET_CRYPTO_ecdsa_sign (dkey,
175 * Sign name and records
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)
184 struct GNUNET_GNSRECORD_Block *
185 GNUNET_GNSRECORD_block_create (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
186 struct GNUNET_TIME_Absolute expire,
188 const struct GNUNET_GNSRECORD_Data *rd,
189 unsigned int rd_count)
191 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
193 GNUNET_CRYPTO_ecdsa_key_get_public (key,
195 return block_create (key,
205 * Line in cache mapping private keys to public keys.
212 struct GNUNET_CRYPTO_EcdsaPrivateKey key;
215 * Associated public key.
217 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
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).
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)
234 struct GNUNET_GNSRECORD_Block *
235 GNUNET_GNSRECORD_block_create2 (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
236 struct GNUNET_TIME_Absolute expire,
238 const struct GNUNET_GNSRECORD_Data *rd,
239 unsigned int rd_count)
242 static struct KeyCacheLine cache[CSIZE];
243 struct KeyCacheLine *line;
245 line = &cache[(*(unsigned int *) key) % CSIZE];
246 if (0 != memcmp (&line->key,
250 /* cache miss, recompute */
252 GNUNET_CRYPTO_ecdsa_key_get_public (key,
256 return block_create (key,
267 * Check if a signature is valid. This API is used by the GNS Block
268 * to validate signatures received from the network.
270 * @param block block to verify
271 * @return #GNUNET_OK if the signature is valid
274 GNUNET_GNSRECORD_block_verify (const struct GNUNET_GNSRECORD_Block *block)
276 return GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN,
279 &block->derived_key);
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
295 GNUNET_GNSRECORD_block_decrypt (const struct GNUNET_GNSRECORD_Block *block,
296 const struct GNUNET_CRYPTO_EcdsaPublicKey *zone_key,
298 GNUNET_GNSRECORD_RecordCallback proc,
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;
307 if (ntohl (block->purpose.size) <
308 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
309 sizeof (struct GNUNET_TIME_AbsoluteNBO))
312 return GNUNET_SYSERR;
314 derive_block_aes_key (&iv,
319 char payload[payload_len];
322 GNUNET_break (payload_len ==
323 GNUNET_CRYPTO_symmetric_decrypt (&block[1], payload_len,
326 GNUNET_memcpy (&rd_count,
329 rd_count = ntohl (rd_count);
332 /* limit to sane value */
334 return GNUNET_SYSERR;
337 struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL(rd_count)];
339 struct GNUNET_TIME_Absolute now;
342 GNUNET_GNSRECORD_records_deserialize (payload_len - sizeof (uint32_t),
343 &payload[sizeof (uint32_t)],
348 return GNUNET_SYSERR;
350 /* hide expired records */
351 now = GNUNET_TIME_absolute_get ();
353 for (unsigned int i=0;i<rd_count;i++)
355 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
357 /* encrypted blocks must never have relative expiration times, skip! */
362 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW_RECORD))
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++)
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)) )
376 include_record = GNUNET_NO; /* We have a non-expired, non-shadow record of the same type */
380 if (GNUNET_YES == include_record)
382 rd[i].flags ^= GNUNET_GNSRECORD_RF_SHADOW_RECORD; /* Remove Flag */
388 else if (rd[i].expiration_time >= now.abs_value_us)
390 /* Include this record */
400 (0 != rd_count) ? rd : NULL);
408 * Calculate the DHT query for a given @a label in a given @a zone.
410 * @param zone private key of the zone
411 * @param label label of the record
412 * @param query hash to use for the query
415 GNUNET_GNSRECORD_query_from_private_key (const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
417 struct GNUNET_HashCode *query)
419 struct GNUNET_CRYPTO_EcdsaPublicKey pub;
421 GNUNET_CRYPTO_ecdsa_key_get_public (zone,
423 GNUNET_GNSRECORD_query_from_public_key (&pub,
430 * Calculate the DHT query for a given @a label in a given @a zone.
432 * @param pub public key of the zone
433 * @param label label of the record
434 * @param query hash to use for the query
437 GNUNET_GNSRECORD_query_from_public_key (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub,
439 struct GNUNET_HashCode *query)
441 struct GNUNET_CRYPTO_EcdsaPublicKey pd;
443 GNUNET_CRYPTO_ecdsa_public_key_derive (pub,
447 GNUNET_CRYPTO_hash (&pd,
453 /* end of gnsrecord_crypto.c */