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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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
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"
38 #define LOG(kind, ...) GNUNET_log_from (kind, "gnsrecord", __VA_ARGS__)
42 * Derive session key and iv from label and public key.
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
50 derive_block_aes_key (struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
51 struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
53 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub)
55 static const char ctx_key[] = "gns-aes-ctx-key";
56 static const char ctx_iv[] = "gns-aes-ctx-iv";
58 GNUNET_CRYPTO_kdf (skey, sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey),
59 ctx_key, strlen (ctx_key),
60 pub, sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
61 label, strlen (label),
63 GNUNET_CRYPTO_kdf (iv, sizeof(struct
64 GNUNET_CRYPTO_SymmetricInitializationVector),
65 ctx_iv, strlen (ctx_iv),
66 pub, sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
67 label, strlen (label),
73 * Sign name and records
75 * @param key the private key
76 * @param pkey associated public key
77 * @param expire block expiration
78 * @param label the name for the records
79 * @param rd record data
80 * @param rd_count number of records
81 * @return NULL on error (block too large)
83 static struct GNUNET_GNSRECORD_Block *
84 block_create (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
85 const struct GNUNET_CRYPTO_EcdsaPublicKey *pkey,
86 struct GNUNET_TIME_Absolute expire,
88 const struct GNUNET_GNSRECORD_Data *rd,
89 unsigned int rd_count)
91 ssize_t payload_len = GNUNET_GNSRECORD_records_get_size (rd_count,
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;
106 if (payload_len > GNUNET_GNSRECORD_MAX_BLOCK_SIZE)
111 /* convert relative to absolute times */
112 now = GNUNET_TIME_absolute_get ();
113 for (unsigned int i = 0; i < rd_count; i++)
116 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
118 struct GNUNET_TIME_Relative t;
120 /* encrypted blocks must never have relative expiration times, convert! */
121 rdc[i].flags &= ~GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
122 t.rel_value_us = rdc[i].expiration_time;
123 rdc[i].expiration_time = GNUNET_TIME_absolute_add (now, t).abs_value_us;
127 rd_count_nbo = htonl (rd_count);
129 char payload[sizeof(uint32_t) + payload_len];
131 GNUNET_memcpy (payload,
134 GNUNET_assert (payload_len ==
135 GNUNET_GNSRECORD_records_serialize (rd_count,
138 &payload[sizeof(uint32_t)
140 block = GNUNET_malloc (sizeof(struct GNUNET_GNSRECORD_Block)
143 block->purpose.size = htonl (sizeof(uint32_t)
146 GNUNET_CRYPTO_EccSignaturePurpose)
147 + sizeof(struct GNUNET_TIME_AbsoluteNBO));
148 block->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN);
149 block->expiration_time = GNUNET_TIME_absolute_hton (expire);
150 /* encrypt and sign */
151 dkey = GNUNET_CRYPTO_ecdsa_private_key_derive (key,
154 GNUNET_CRYPTO_ecdsa_key_get_public (dkey,
155 &block->derived_key);
156 derive_block_aes_key (&iv,
160 GNUNET_break (payload_len + sizeof(uint32_t) ==
161 GNUNET_CRYPTO_symmetric_encrypt (payload,
169 GNUNET_CRYPTO_ecdsa_sign_ (dkey,
184 * Sign name and records
186 * @param key the private key
187 * @param expire block expiration
188 * @param label the name for the records
189 * @param rd record data
190 * @param rd_count number of records
191 * @return NULL on error (block too large)
193 struct GNUNET_GNSRECORD_Block *
194 GNUNET_GNSRECORD_block_create (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
195 struct GNUNET_TIME_Absolute expire,
197 const struct GNUNET_GNSRECORD_Data *rd,
198 unsigned int rd_count)
200 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
202 GNUNET_CRYPTO_ecdsa_key_get_public (key,
204 return block_create (key,
214 * Line in cache mapping private keys to public keys.
221 struct GNUNET_CRYPTO_EcdsaPrivateKey key;
224 * Associated public key.
226 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
231 * Sign name and records, cache derived public key (also keeps the
232 * private key in static memory, so do not use this function if
233 * keeping the private key in the process'es RAM is a major issue).
235 * @param key the private key
236 * @param expire block expiration
237 * @param label the name for the records
238 * @param rd record data
239 * @param rd_count number of records
240 * @return NULL on error (block too large)
242 struct GNUNET_GNSRECORD_Block *
243 GNUNET_GNSRECORD_block_create2 (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
244 struct GNUNET_TIME_Absolute expire,
246 const struct GNUNET_GNSRECORD_Data *rd,
247 unsigned int rd_count)
250 static struct KeyCacheLine cache[CSIZE];
251 struct KeyCacheLine *line;
253 line = &cache[(*(unsigned int *) key) % CSIZE];
254 if (0 != memcmp (&line->key,
258 /* cache miss, recompute */
260 GNUNET_CRYPTO_ecdsa_key_get_public (key,
264 return block_create (key,
274 * Check if a signature is valid. This API is used by the GNS Block
275 * to validate signatures received from the network.
277 * @param block block to verify
278 * @return #GNUNET_OK if the signature is valid
281 GNUNET_GNSRECORD_block_verify (const struct GNUNET_GNSRECORD_Block *block)
283 return GNUNET_CRYPTO_ecdsa_verify_ (GNUNET_SIGNATURE_PURPOSE_GNS_RECORD_SIGN,
286 &block->derived_key);
293 * @param block block to decrypt
294 * @param zone_key public key of the zone
295 * @param label the name for the records
296 * @param proc function to call with the result
297 * @param proc_cls closure for proc
298 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the block was
302 GNUNET_GNSRECORD_block_decrypt (const struct GNUNET_GNSRECORD_Block *block,
304 GNUNET_CRYPTO_EcdsaPublicKey *zone_key,
306 GNUNET_GNSRECORD_RecordCallback proc,
309 size_t payload_len = ntohl (block->purpose.size)
310 - sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
311 - sizeof(struct GNUNET_TIME_AbsoluteNBO);
312 struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
313 struct GNUNET_CRYPTO_SymmetricSessionKey skey;
315 if (ntohl (block->purpose.size) <
316 sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
317 + sizeof(struct GNUNET_TIME_AbsoluteNBO))
320 return GNUNET_SYSERR;
322 derive_block_aes_key (&iv,
327 char payload[payload_len];
330 GNUNET_break (payload_len ==
331 GNUNET_CRYPTO_symmetric_decrypt (&block[1], payload_len,
334 GNUNET_memcpy (&rd_count,
337 rd_count = ntohl (rd_count);
340 /* limit to sane value */
342 return GNUNET_SYSERR;
345 struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)];
347 struct GNUNET_TIME_Absolute now;
350 GNUNET_GNSRECORD_records_deserialize (payload_len - sizeof(uint32_t),
351 &payload[sizeof(uint32_t)],
356 return GNUNET_SYSERR;
358 /* hide expired records */
359 now = GNUNET_TIME_absolute_get ();
361 for (unsigned int i = 0; i < rd_count; i++)
363 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
365 /* encrypted blocks must never have relative expiration times, skip! */
370 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW_RECORD))
372 int include_record = GNUNET_YES;
373 /* Shadow record, figure out if we have a not expired active record */
374 for (unsigned int k = 0; k < rd_count; k++)
378 if (rd[i].expiration_time < now.abs_value_us)
379 include_record = GNUNET_NO; /* Shadow record is expired */
380 if ((rd[k].record_type == rd[i].record_type) &&
381 (rd[k].expiration_time >= now.abs_value_us) &&
382 (0 == (rd[k].flags & GNUNET_GNSRECORD_RF_SHADOW_RECORD)))
384 include_record = GNUNET_NO; /* We have a non-expired, non-shadow record of the same type */
385 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
386 "Ignoring shadow record\n");
390 if (GNUNET_YES == include_record)
392 rd[i].flags ^= GNUNET_GNSRECORD_RF_SHADOW_RECORD; /* Remove Flag */
398 else if (rd[i].expiration_time >= now.abs_value_us)
400 /* Include this record */
407 struct GNUNET_TIME_Absolute at;
409 at.abs_value_us = rd[i].expiration_time;
410 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
411 "Excluding record that expired %s (%llu ago)\n",
412 GNUNET_STRINGS_absolute_time_to_string (at),
413 (unsigned long long) rd[i].expiration_time
421 (0 != rd_count) ? rd : NULL);
429 * Calculate the DHT query for a given @a label in a given @a zone.
431 * @param zone private key of the zone
432 * @param label label of the record
433 * @param query hash to use for the query
436 GNUNET_GNSRECORD_query_from_private_key (const struct
437 GNUNET_CRYPTO_EcdsaPrivateKey *zone,
439 struct GNUNET_HashCode *query)
441 struct GNUNET_CRYPTO_EcdsaPublicKey pub;
443 GNUNET_CRYPTO_ecdsa_key_get_public (zone,
445 GNUNET_GNSRECORD_query_from_public_key (&pub,
452 * Calculate the DHT query for a given @a label in a given @a zone.
454 * @param pub public key of the zone
455 * @param label label of the record
456 * @param query hash to use for the query
459 GNUNET_GNSRECORD_query_from_public_key (const struct
460 GNUNET_CRYPTO_EcdsaPublicKey *pub,
462 struct GNUNET_HashCode *query)
464 struct GNUNET_CRYPTO_EcdsaPublicKey pd;
465 GNUNET_CRYPTO_ecdsa_public_key_derive (pub,
469 GNUNET_CRYPTO_hash (&pd,
475 /* end of gnsrecord_crypto.c */