2 This file is part of GNUnet
3 (C) 2003, 2004, 2005, 2006, 2007, 2008, 2013 Christian Grothoff (and other contributing authors)
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 2, or (at your
8 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 General Public License for more details.
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.
21 * @file fs/fs_pseudonym.c
22 * @brief pseudonym functions
23 * @author Christian Grothoff
26 * - all cryptographic operations are currently NOT implemented and
27 * provided by stubs that merely pretend to work!
30 #include "gnunet_util_lib.h"
31 #include "gnunet_fs_service.h"
35 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
37 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
39 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
42 * Log an error message at log-level 'level' that indicates
43 * a failure of the command 'cmd' with the message given
44 * by gcry_strerror(rc).
46 #define LOG_GCRY(level, cmd, rc) do { LOG(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0);
49 * Name of the directory which stores meta data for pseudonym
51 #define PS_METADATA_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonym" DIR_SEPARATOR_STR "metadata" DIR_SEPARATOR_STR
54 * Name of the directory which stores names for pseudonyms
56 #define PS_NAMES_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonym" DIR_SEPARATOR_STR "names" DIR_SEPARATOR_STR
60 * Configuration section we use.
62 #define GNUNET_CLIENT_SERVICE_NAME "fs"
65 /* ************************* Disk operations (pseudonym data mgmt) **************** */
68 * Registered callbacks for discovery of pseudonyms.
70 struct GNUNET_FS_pseudonym_DiscoveryHandle
73 * This is a doubly linked list.
75 struct GNUNET_FS_pseudonym_DiscoveryHandle *next;
78 * This is a doubly linked list.
80 struct GNUNET_FS_pseudonym_DiscoveryHandle *prev;
83 * Function to call each time a pseudonym is discovered.
85 GNUNET_FS_PseudonymIterator callback;
88 * Closure for callback.
95 * Head of the linked list of functions to call when
96 * new pseudonyms are added.
98 static struct GNUNET_FS_pseudonym_DiscoveryHandle *disco_head;
101 * Tail of the linked list of functions to call when
102 * new pseudonyms are added.
104 static struct GNUNET_FS_pseudonym_DiscoveryHandle *disco_tail;
108 * Internal notification about new tracked URI.
110 * @param pseudonym public key of the pseudonym
111 * @param md meta data to be written
112 * @param rating rating of pseudonym
115 internal_notify (const struct GNUNET_FS_PseudonymIdentifier *pseudonym,
116 const struct GNUNET_CONTAINER_MetaData *md, int rating)
118 struct GNUNET_FS_pseudonym_DiscoveryHandle *pos;
120 for (pos = disco_head; NULL != pos; pos = pos->next)
121 pos->callback (pos->callback_cls, pseudonym, NULL, NULL, md, rating);
126 * Register callback to be invoked whenever we discover
128 * Will immediately call provided iterator callback for all
129 * already discovered pseudonyms.
131 * @param cfg configuration to use
132 * @param iterator iterator over pseudonym
133 * @param iterator_cls point to a closure
134 * @return registration handle
136 struct GNUNET_FS_pseudonym_DiscoveryHandle *
137 GNUNET_FS_pseudonym_discovery_callback_register (const struct
138 GNUNET_CONFIGURATION_Handle *cfg,
139 GNUNET_FS_PseudonymIterator iterator,
142 struct GNUNET_FS_pseudonym_DiscoveryHandle *dh;
144 dh = GNUNET_malloc (sizeof (struct GNUNET_FS_pseudonym_DiscoveryHandle));
145 dh->callback = iterator;
146 dh->callback_cls = iterator_cls;
147 GNUNET_CONTAINER_DLL_insert (disco_head, disco_tail, dh);
148 GNUNET_FS_pseudonym_list_all (cfg, iterator, iterator_cls);
154 * Unregister pseudonym discovery callback.
156 * @param dh registration to unregister
159 GNUNET_FS_pseudonym_discovery_callback_unregister (struct GNUNET_FS_pseudonym_DiscoveryHandle *dh)
161 GNUNET_CONTAINER_DLL_remove (disco_head, disco_tail, dh);
167 * Get the filename (or directory name) for the given
168 * pseudonym identifier and directory prefix.
170 * @param cfg configuration to use
171 * @param prefix path components to append to the private directory name
172 * @param pseudonym the pseudonym, can be NULL
173 * @return filename of the pseudonym (if pseudonym != NULL) or directory with the data (if pseudonym == NULL)
176 get_data_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
178 const struct GNUNET_FS_PseudonymIdentifier *pseudonym)
180 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
181 struct GNUNET_HashCode psid;
183 if (NULL != pseudonym)
185 GNUNET_CRYPTO_hash (pseudonym,
186 sizeof (struct GNUNET_FS_PseudonymIdentifier),
188 GNUNET_CRYPTO_hash_to_enc (&psid, &enc);
190 return GNUNET_DISK_get_home_filename (cfg,
191 GNUNET_CLIENT_SERVICE_NAME, prefix,
194 : (const char *) &enc,
200 * Get the filename (or directory name) for the given
201 * hash code and directory prefix.
203 * @param cfg configuration to use
204 * @param prefix path components to append to the private directory name
205 * @param hc some hash code
206 * @return filename of the pseudonym (if hc != NULL) or directory with the data (if hc == NULL)
209 get_data_filename_hash (const struct GNUNET_CONFIGURATION_Handle *cfg,
211 const struct GNUNET_HashCode *hc)
213 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
216 GNUNET_CRYPTO_hash_to_enc (hc, &enc);
217 return GNUNET_DISK_get_home_filename (cfg,
218 GNUNET_CLIENT_SERVICE_NAME, prefix,
221 : (const char *) &enc,
227 * Set the pseudonym metadata, rank and name.
228 * Writes the pseudonym infomation into a file
230 * @param cfg overall configuration
231 * @param pseudonym id of the pseudonym
232 * @param name name to set. Must be the non-unique version of it.
233 * May be NULL, in which case it erases pseudonym's name!
234 * @param md metadata to set
235 * May be NULL, in which case it erases pseudonym's metadata!
236 * @param rank rank to assign
237 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
240 GNUNET_FS_pseudonym_set_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
241 const struct GNUNET_FS_PseudonymIdentifier *pseudonym,
243 const struct GNUNET_CONTAINER_MetaData *md,
247 struct GNUNET_BIO_WriteHandle *fileW;
249 fn = get_data_filename (cfg, PS_METADATA_DIR, pseudonym);
250 if (NULL == (fileW = GNUNET_BIO_write_open (fn)))
253 return GNUNET_SYSERR;
255 if ((GNUNET_OK != GNUNET_BIO_write (fileW, pseudonym,
256 sizeof (struct GNUNET_FS_PseudonymIdentifier))) ||
257 (GNUNET_OK != GNUNET_BIO_write_int32 (fileW, rank)) ||
258 (GNUNET_OK != GNUNET_BIO_write_string (fileW, name)) ||
259 (GNUNET_OK != GNUNET_BIO_write_meta_data (fileW, md)))
261 (void) GNUNET_BIO_write_close (fileW);
262 GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
264 return GNUNET_SYSERR;
266 if (GNUNET_OK != GNUNET_BIO_write_close (fileW))
268 GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
270 return GNUNET_SYSERR;
273 /* create entry for pseudonym name in names */
275 GNUNET_free_non_null (GNUNET_FS_pseudonym_name_uniquify (cfg, pseudonym,
282 * Read pseudonym infomation from a file
284 * @param cfg configuration to use
285 * @param pseudonym hash code of a pseudonym
286 * @param meta meta data to be read from a file
287 * @param rank rank of a pseudonym
288 * @param ns_name name of a pseudonym
289 * @return GNUNET_OK on success, GNUNET_SYSERR on error
292 read_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
293 const struct GNUNET_FS_PseudonymIdentifier *pseudonym,
294 struct GNUNET_CONTAINER_MetaData **meta,
298 struct GNUNET_FS_PseudonymIdentifier pd;
301 struct GNUNET_BIO_ReadHandle *fileR;
303 fn = get_data_filename (cfg, PS_METADATA_DIR, pseudonym);
305 GNUNET_DISK_file_test (fn))
308 return GNUNET_SYSERR;
310 if (NULL == (fileR = GNUNET_BIO_read_open (fn)))
313 return GNUNET_SYSERR;
317 if ( (GNUNET_OK != GNUNET_BIO_read (fileR, "pseudonym", &pd, sizeof (pd))) ||
318 (0 != memcmp (&pd, pseudonym, sizeof (pd))) ||
319 (GNUNET_OK != GNUNET_BIO_read_int32 (fileR, rank)) ||
321 GNUNET_BIO_read_string (fileR, "Read string error!", ns_name, 200)) ||
323 GNUNET_BIO_read_meta_data (fileR, "Read meta data error!", meta)) )
325 (void) GNUNET_BIO_read_close (fileR, &emsg);
326 GNUNET_free_non_null (emsg);
327 GNUNET_free_non_null (*ns_name);
329 GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
331 return GNUNET_SYSERR;
333 if (GNUNET_OK != GNUNET_BIO_read_close (fileR, &emsg))
335 LOG (GNUNET_ERROR_TYPE_WARNING,
336 _("Failed to parse metadata about pseudonym from file `%s': %s\n"), fn,
338 GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
339 GNUNET_CONTAINER_meta_data_destroy (*meta);
341 GNUNET_free_non_null (*ns_name);
343 GNUNET_free_non_null (emsg);
345 return GNUNET_SYSERR;
353 * Return unique variant of the namespace name. Use it after
354 * GNUNET_FS_pseudonym_get_info() to make sure that name is unique.
356 * @param cfg configuration
357 * @param pseudonym public key of the pseudonym
358 * @param name name to uniquify
359 * @param suffix if not NULL, filled with the suffix value
360 * @return NULL on failure (should never happen), name on success.
361 * Free the name with GNUNET_free().
364 GNUNET_FS_pseudonym_name_uniquify (const struct GNUNET_CONFIGURATION_Handle *cfg,
365 const struct GNUNET_FS_PseudonymIdentifier *pseudonym,
367 unsigned int *suffix)
369 struct GNUNET_HashCode nh;
370 struct GNUNET_FS_PseudonymIdentifier pi;
373 struct GNUNET_DISK_FileHandle *fh;
379 GNUNET_CRYPTO_hash (name, strlen (name), &nh);
380 fn = get_data_filename_hash (cfg, PS_NAMES_DIR, &nh);
382 if (0 == STAT (fn, &sbuf))
383 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_size (fn, &len, GNUNET_YES, GNUNET_YES));
384 fh = GNUNET_DISK_file_open (fn,
385 GNUNET_DISK_OPEN_CREATE |
386 GNUNET_DISK_OPEN_READWRITE,
387 GNUNET_DISK_PERM_USER_READ |
388 GNUNET_DISK_PERM_USER_WRITE);
391 while ((len >= sizeof (struct GNUNET_FS_PseudonymIdentifier)) &&
392 (sizeof (struct GNUNET_FS_PseudonymIdentifier) ==
393 GNUNET_DISK_file_read (fh, &pi, sizeof (struct GNUNET_FS_PseudonymIdentifier))))
395 if (0 == memcmp (&pi, pseudonym, sizeof (struct GNUNET_FS_PseudonymIdentifier)))
401 len -= sizeof (struct GNUNET_HashCode);
406 if (sizeof (struct GNUNET_FS_PseudonymIdentifier) !=
407 GNUNET_DISK_file_write (fh, pseudonym, sizeof (struct GNUNET_FS_PseudonymIdentifier)))
408 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "write", fn);
410 GNUNET_DISK_file_close (fh);
411 ret = GNUNET_malloc (strlen (name) + 32);
412 GNUNET_snprintf (ret, strlen (name) + 32, "%s-%u", name, idx);
421 * Get namespace name, metadata and rank
422 * This is a wrapper around internal read_info() call, and ensures that
423 * returned data is not invalid (not NULL).
425 * @param cfg configuration
426 * @param pseudonym public key of the pseudonym
427 * @param ret_meta a location to store metadata pointer. NULL, if metadata
428 * is not needed. Destroy with GNUNET_CONTAINER_meta_data_destroy().
429 * @param ret_rank a location to store rank. NULL, if rank not needed.
430 * @param ret_name a location to store human-readable name. Name is not unique.
431 * NULL, if name is not needed. Free with GNUNET_free().
432 * @param name_is_a_dup is set to GNUNET_YES, if ret_name was filled with
433 * a duplicate of a "no-name" placeholder
434 * @return GNUNET_OK on success. GNUENT_SYSERR if the data was
435 * unobtainable (in that case ret_* are filled with placeholders -
436 * empty metadata container, rank -1 and a "no-name" name).
439 GNUNET_FS_pseudonym_get_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
440 const struct GNUNET_FS_PseudonymIdentifier *pseudonym,
441 struct GNUNET_CONTAINER_MetaData **ret_meta,
446 struct GNUNET_CONTAINER_MetaData *meta;
452 if (GNUNET_OK == read_info (cfg, pseudonym, &meta, &rank, &name))
454 if ((meta != NULL) && (name == NULL))
456 GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
457 EXTRACTOR_METATYPE_TITLE,
458 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
459 EXTRACTOR_METATYPE_FILENAME,
460 EXTRACTOR_METATYPE_DESCRIPTION,
461 EXTRACTOR_METATYPE_SUBJECT,
462 EXTRACTOR_METATYPE_PUBLISHER,
463 EXTRACTOR_METATYPE_AUTHOR_NAME,
464 EXTRACTOR_METATYPE_COMMENT,
465 EXTRACTOR_METATYPE_SUMMARY,
467 if (ret_name != NULL)
471 name = GNUNET_strdup (_("no-name"));
472 if (name_is_a_dup != NULL)
473 *name_is_a_dup = GNUNET_YES;
475 else if (name_is_a_dup != NULL)
476 *name_is_a_dup = GNUNET_NO;
479 else if (name != NULL)
482 if (ret_meta != NULL)
485 meta = GNUNET_CONTAINER_meta_data_create ();
488 else if (meta != NULL)
489 GNUNET_CONTAINER_meta_data_destroy (meta);
491 if (ret_rank != NULL)
496 if (ret_name != NULL)
497 *ret_name = GNUNET_strdup (_("no-name"));
498 if (ret_meta != NULL)
499 *ret_meta = GNUNET_CONTAINER_meta_data_create ();
500 if (ret_rank != NULL)
502 if (name_is_a_dup != NULL)
503 *name_is_a_dup = GNUNET_YES;
504 return GNUNET_SYSERR;
509 * Get the namespace ID belonging to the given namespace name.
511 * @param cfg configuration to use
512 * @param ns_uname unique (!) human-readable name for the namespace
513 * @param pseudonym set to public key of pseudonym based on 'ns_uname'
514 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
517 GNUNET_FS_pseudonym_name_to_id (const struct GNUNET_CONFIGURATION_Handle *cfg,
518 const char *ns_uname,
519 struct GNUNET_FS_PseudonymIdentifier *pseudonym)
525 struct GNUNET_HashCode nh;
527 struct GNUNET_DISK_FileHandle *fh;
530 slen = strlen (ns_uname);
531 while ((slen > 0) && (1 != SSCANF (&ns_uname[slen - 1], "-%u", &idx)))
534 return GNUNET_SYSERR;
535 name = GNUNET_strdup (ns_uname);
536 name[slen - 1] = '\0';
538 GNUNET_CRYPTO_hash (name, strlen (name), &nh);
540 fn = get_data_filename_hash (cfg, PS_NAMES_DIR, &nh);
542 if ((GNUNET_OK != GNUNET_DISK_file_test (fn) ||
543 (GNUNET_OK != GNUNET_DISK_file_size (fn, &len, GNUNET_YES, GNUNET_YES))) ||
544 ((idx + 1) * sizeof (struct GNUNET_FS_PseudonymIdentifier) > len))
547 return GNUNET_SYSERR;
549 fh = GNUNET_DISK_file_open (fn,
550 GNUNET_DISK_OPEN_CREATE |
551 GNUNET_DISK_OPEN_READWRITE,
552 GNUNET_DISK_PERM_USER_READ |
553 GNUNET_DISK_PERM_USER_WRITE);
556 GNUNET_DISK_file_seek (fh, idx * sizeof (struct GNUNET_FS_PseudonymIdentifier),
557 GNUNET_DISK_SEEK_SET))
559 GNUNET_DISK_file_close (fh);
560 return GNUNET_SYSERR;
562 if (sizeof (struct GNUNET_FS_PseudonymIdentifier) !=
563 GNUNET_DISK_file_read (fh, pseudonym, sizeof (struct GNUNET_FS_PseudonymIdentifier)))
565 GNUNET_DISK_file_close (fh);
566 return GNUNET_SYSERR;
568 GNUNET_DISK_file_close (fh);
575 * struct used to list the pseudonym
577 struct ListPseudonymClosure
581 * iterator over pseudonym
583 GNUNET_FS_PseudonymIterator iterator;
586 * Closure for iterator.
591 * Configuration to use.
593 const struct GNUNET_CONFIGURATION_Handle *cfg;
599 * Helper function to list all available pseudonyms
601 * @param cls point to a struct ListPseudonymClosure
602 * @param fullname name of pseudonym
605 list_pseudonym_helper (void *cls, const char *fullname)
607 struct ListPseudonymClosure *lpc = cls;
608 struct GNUNET_FS_PseudonymIdentifier pd;
610 struct GNUNET_BIO_ReadHandle *fileR;
613 struct GNUNET_CONTAINER_MetaData *meta;
617 if (NULL == (fileR = GNUNET_BIO_read_open (fullname)))
618 return GNUNET_SYSERR;
621 if ( (GNUNET_OK != GNUNET_BIO_read (fileR, "pseudonym", &pd, sizeof (pd))) ||
622 (GNUNET_OK != GNUNET_BIO_read_int32 (fileR, &rank)) ||
624 GNUNET_BIO_read_string (fileR, "Read string error!", &ns_name, 200)) ||
626 GNUNET_BIO_read_meta_data (fileR, "Read meta data error!", &meta)) )
628 (void) GNUNET_BIO_read_close (fileR, &emsg);
629 GNUNET_free_non_null (emsg);
630 GNUNET_free_non_null (ns_name);
631 GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fullname));
632 return GNUNET_SYSERR;
635 ns_name = GNUNET_strdup (_("no-name"));
636 if (GNUNET_OK != GNUNET_BIO_read_close (fileR, &emsg))
638 LOG (GNUNET_ERROR_TYPE_WARNING,
639 _("Failed to parse metadata about pseudonym from file `%s': %s\n"), fullname,
641 GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fullname));
642 GNUNET_CONTAINER_meta_data_destroy (meta);
643 GNUNET_free (ns_name);
644 GNUNET_free_non_null (emsg);
645 return GNUNET_SYSERR;
648 name_unique = GNUNET_FS_pseudonym_name_uniquify (lpc->cfg, &pd, ns_name, NULL);
649 if (NULL != lpc->iterator)
650 ret = lpc->iterator (lpc->iterator_cls, &pd, ns_name, name_unique, meta, rank);
651 GNUNET_free (ns_name);
652 GNUNET_free_non_null (name_unique);
653 GNUNET_CONTAINER_meta_data_destroy (meta);
659 * List all available pseudonyms.
661 * @param cfg overall configuration
662 * @param iterator function to call for each pseudonym
663 * @param iterator_cls closure for iterator
664 * @return number of pseudonyms found
667 GNUNET_FS_pseudonym_list_all (const struct GNUNET_CONFIGURATION_Handle *cfg,
668 GNUNET_FS_PseudonymIterator iterator,
671 struct ListPseudonymClosure cls;
675 cls.iterator = iterator;
676 cls.iterator_cls = iterator_cls;
678 fn = get_data_filename (cfg, PS_METADATA_DIR, NULL);
679 GNUNET_assert (fn != NULL);
680 GNUNET_DISK_directory_create (fn);
681 ret = GNUNET_DISK_directory_scan (fn, &list_pseudonym_helper, &cls);
688 * Change the rank of a pseudonym.
690 * @param cfg overall configuration
691 * @param pseudonym the pseudonym
692 * @param delta by how much should the rating be changed?
693 * @return new rating of the pseudonym
696 GNUNET_FS_pseudonym_rank (const struct GNUNET_CONFIGURATION_Handle *cfg,
697 const struct GNUNET_FS_PseudonymIdentifier *pseudonym,
700 struct GNUNET_CONTAINER_MetaData *meta;
706 ret = read_info (cfg, pseudonym, &meta, &rank, &name);
707 if (ret == GNUNET_SYSERR)
710 meta = GNUNET_CONTAINER_meta_data_create ();
713 GNUNET_FS_pseudonym_set_info (cfg, pseudonym, name, meta, rank);
714 GNUNET_CONTAINER_meta_data_destroy (meta);
715 GNUNET_free_non_null (name);
721 * Add a pseudonym to the set of known pseudonyms.
722 * For all pseudonym advertisements that we discover
723 * FS should automatically call this function.
725 * @param cfg overall configuration
726 * @param pseudonym the pseudonym to add
727 * @param meta metadata for the pseudonym
728 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
731 GNUNET_FS_pseudonym_add (const struct GNUNET_CONFIGURATION_Handle *cfg,
732 const struct GNUNET_FS_PseudonymIdentifier *pseudonym,
733 const struct GNUNET_CONTAINER_MetaData *meta)
737 struct GNUNET_CONTAINER_MetaData *old;
743 fn = get_data_filename (cfg, PS_METADATA_DIR, pseudonym);
744 GNUNET_assert (fn != NULL);
746 if ((0 == STAT (fn, &sbuf)) &&
747 (GNUNET_OK == read_info (cfg, pseudonym, &old, &rank, &name)))
749 GNUNET_CONTAINER_meta_data_merge (old, meta);
750 ret = GNUNET_FS_pseudonym_set_info (cfg, pseudonym, name, old, rank);
751 GNUNET_CONTAINER_meta_data_destroy (old);
752 GNUNET_free_non_null (name);
756 ret = GNUNET_FS_pseudonym_set_info (cfg, pseudonym, NULL, meta, rank);
759 internal_notify (pseudonym, meta, rank);
764 /* ***************************** cryptographic operations ************************* */
767 * Handle for a pseudonym (private key).
769 struct GNUNET_FS_PseudonymHandle
772 * 256-bit 'd' secret value (mod 'n', where n is 256-bit for NIST P-256).
774 unsigned char d[256 / 8];
777 * Public key corresponding to the private key.
779 struct GNUNET_FS_PseudonymIdentifier public_key;
784 * If target != size, move target bytes to the end of the size-sized
785 * buffer and zero out the first target-size bytes.
787 * @param buf original buffer
788 * @param size number of bytes in the buffer
789 * @param target target size of the buffer
792 adjust (unsigned char *buf, size_t size, size_t target)
796 memmove (&buf[target - size], buf, size);
797 memset (buf, 0, target - size);
803 * Extract values from an S-expression.
805 * @param array where to store the result(s)
806 * @param sexp S-expression to parse
807 * @param topname top-level name in the S-expression that is of interest
808 * @param elems names of the elements to extract
809 * @return 0 on success
812 key_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname,
821 if (! (list = gcry_sexp_find_token (sexp, topname, 0)))
823 l2 = gcry_sexp_cadr (list);
824 gcry_sexp_release (list);
829 for (s = elems; *s; s++, idx++)
831 if (! (l2 = gcry_sexp_find_token (list, s, 1)))
833 for (i = 0; i < idx; i++)
835 gcry_free (array[i]);
838 gcry_sexp_release (list);
839 return 3; /* required parameter not found */
841 array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
842 gcry_sexp_release (l2);
845 for (i = 0; i < idx; i++)
847 gcry_free (array[i]);
850 gcry_sexp_release (list);
851 return 4; /* required parameter is invalid */
854 gcry_sexp_release (list);
860 * Create a pseudonym.
862 * @param filename name of the file to use for storage, NULL for in-memory only
863 * @return handle to the private key of the pseudonym
865 struct GNUNET_FS_PseudonymHandle *
866 GNUNET_FS_pseudonym_create (const char *filename)
868 struct GNUNET_FS_PseudonymHandle *ph;
880 ph = GNUNET_malloc (sizeof (struct GNUNET_FS_PseudonymHandle));
881 if ( (NULL != filename) &&
882 (GNUNET_YES == GNUNET_DISK_file_test (filename)) )
884 ret = GNUNET_DISK_fn_read (filename, ph,
885 sizeof (struct GNUNET_FS_PseudonymHandle));
886 /* Note: we don't do any validation here, maybe we should? */
887 if (sizeof (struct GNUNET_FS_PseudonymHandle) == ret)
890 if (0 != (rc = gcry_sexp_build (¶ms, NULL,
891 "(genkey(ecdsa(curve \"NIST P-256\")))")))
893 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
896 if (0 != (rc = gcry_pk_genkey (&r_key, params)))
898 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_genkey", rc);
899 gcry_sexp_release (r_key);
902 /* extract "d" (secret key) from r_key */
903 rc = key_from_sexp (&d, r_key, "private-key", "d");
905 rc = key_from_sexp (&d, r_key, "private-key", "d");
907 rc = key_from_sexp (&d, r_key, "ecc", "d");
910 gcry_sexp_release (r_key);
911 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "key_from_sexp", rc);
914 size = sizeof (ph->d);
916 gcry_mpi_print (GCRYMPI_FMT_USG, ph->d, size, &size,
918 gcry_mpi_release (d);
919 adjust (ph->d, size, sizeof (ph->d));
921 /* extract 'q' (public key) from r_key */
922 if (0 != (rc = gcry_mpi_ec_new (&ctx, r_key, NULL)))
924 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_ec_new", rc); /* erroff gives more info */
925 gcry_sexp_release (r_key);
928 gcry_sexp_release (r_key);
929 q = gcry_mpi_ec_get_point ("q", ctx, 0);
930 q_x = gcry_mpi_new (256);
931 q_y = gcry_mpi_new (256);
932 gcry_mpi_ec_get_affine (q_x, q_y, q, ctx);
933 gcry_mpi_point_release (q);
935 /* store q_x/q_y in public key */
936 size = sizeof (ph->public_key.q_x);
938 gcry_mpi_print (GCRYMPI_FMT_USG, ph->public_key.q_x, size, &size,
941 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_print", rc);
942 gcry_mpi_release (q_x);
943 gcry_mpi_release (q_y);
947 adjust (ph->public_key.q_x, size, sizeof (ph->public_key.q_x));
948 gcry_mpi_release (q_x);
950 size = sizeof (ph->public_key.q_y);
952 gcry_mpi_print (GCRYMPI_FMT_USG, ph->public_key.q_y, size, &size,
955 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_print", rc);
956 gcry_mpi_release (q_y);
959 adjust (ph->public_key.q_y, size, sizeof (ph->public_key.q_y));
960 gcry_mpi_release (q_y);
963 if (NULL != filename)
965 ret = GNUNET_DISK_fn_write (filename, ph, sizeof (struct GNUNET_FS_PseudonymHandle),
966 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
967 if (sizeof (struct GNUNET_FS_PseudonymHandle) != ret)
978 * Create a pseudonym, from a file that must already exist.
980 * @param filename name of the file to use for storage, NULL for in-memory only
981 * @return handle to the private key of the pseudonym
983 struct GNUNET_FS_PseudonymHandle *
984 GNUNET_FS_pseudonym_create_from_existing_file (const char *filename)
986 struct GNUNET_FS_PseudonymHandle *ph;
989 ph = GNUNET_malloc (sizeof (struct GNUNET_FS_PseudonymHandle));
990 ret = GNUNET_DISK_fn_read (filename, ph,
991 sizeof (struct GNUNET_FS_PseudonymHandle));
992 if (sizeof (struct GNUNET_FS_PseudonymHandle) != ret)
997 /* Note: we don't do any validation here; maybe we should? */
1003 * Get the handle for the 'anonymous' pseudonym shared by all users.
1004 * That pseudonym uses a fixed 'secret' for the private key; this
1005 * construction is useful to make anonymous and pseudonymous APIs
1006 * (and packets) indistinguishable on the network. See #2564.
1008 * @return handle to the (non-secret) private key of the 'anonymous' pseudonym
1010 struct GNUNET_FS_PseudonymHandle *
1011 GNUNET_FS_pseudonym_get_anonymous_pseudonym_handle ()
1013 struct GNUNET_FS_PseudonymHandle *ph;
1015 ph = GNUNET_malloc (sizeof (struct GNUNET_FS_PseudonymHandle));
1016 /* Note if we use 'd=0' for the anonymous handle (as per#2564),
1017 then I believe the public key should be also zero, as Q=0P=0;
1018 so setting everything to all-zeros (as per GNUNET_malloc)
1019 should be all that is needed here).
1026 * Destroy a pseudonym handle. Does NOT remove the private key from
1029 * @param ph pseudonym handle to destroy
1032 GNUNET_FS_pseudonym_destroy (struct GNUNET_FS_PseudonymHandle *ph)
1039 * Convert the data specified in the given purpose argument to an
1040 * S-expression suitable for signature operations.
1042 * @param purpose data to convert
1043 * @return converted s-expression
1046 data_to_pkcs1 (const struct GNUNET_FS_PseudonymSignaturePurpose *purpose)
1048 struct GNUNET_CRYPTO_ShortHashCode hc;
1052 GNUNET_CRYPTO_short_hash (purpose, ntohl (purpose->size), &hc);
1053 #define FORMATSTRING "(4:data(5:flags3:raw)(5:value32:01234567890123456789012345678901))"
1054 bufSize = strlen (FORMATSTRING) + 1;
1058 memcpy (buff, FORMATSTRING, bufSize);
1062 ("01234567890123456789012345678901))")
1063 - 1], &hc, sizeof (struct GNUNET_CRYPTO_ShortHashCode));
1064 GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0));
1072 * Cryptographically sign some data with the pseudonym.
1074 * @param ph private key 'd' used for signing (corresponds to 'x' in #2564)
1075 * @param purpose data to sign
1076 * @param seed hash of the plaintext of the data that we are signing,
1077 * used for deterministic PRNG for anonymous signing;
1078 * corresponds to 'k' in section 2.7 of #2564
1079 * @param signing_key modifier to apply to the private key for signing ('h');
1080 * see section 2.3 of #2564.
1081 * @param signature where to store the signature
1082 * @return GNUNET_SYSERR on failure
1085 GNUNET_FS_pseudonym_sign (struct GNUNET_FS_PseudonymHandle *ph,
1086 const struct GNUNET_FS_PseudonymSignaturePurpose *purpose,
1087 const struct GNUNET_HashCode *seed,
1088 const struct GNUNET_HashCode *signing_key,
1089 struct GNUNET_FS_PseudonymSignature *signature)
1097 gcry_mpi_t n; /* n from P-256 */
1104 /* get private key 'd' from pseudonym */
1105 size = sizeof (ph->d);
1106 if (0 != (rc = gcry_mpi_scan (&d, GCRYMPI_FMT_USG,
1110 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
1111 return GNUNET_SYSERR;
1113 /* get 'x' value from signing key */
1114 size = sizeof (struct GNUNET_HashCode);
1115 if (0 != (rc = gcry_mpi_scan (&h, GCRYMPI_FMT_USG,
1119 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
1120 gcry_mpi_release (d);
1121 return GNUNET_SYSERR;
1124 /* initialize 'n' from P-256; hex copied from libgcrypt code */
1125 if (0 != (rc = gcry_mpi_scan (&n, GCRYMPI_FMT_HEX,
1126 "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 0, NULL)))
1128 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
1129 gcry_mpi_release (d);
1130 gcry_mpi_release (h);
1131 return GNUNET_SYSERR;
1134 /* calculate dx = d + h mod n */
1135 dh = gcry_mpi_new (256);
1136 gcry_mpi_addm (dh, d, h, n);
1137 gcry_mpi_release (d);
1138 gcry_mpi_release (h);
1139 gcry_mpi_release (n);
1141 /* now build sexpression with the signing key */
1142 if (0 != (rc = gcry_sexp_build (&spriv, &erroff,
1143 "(private-key(ecdsa(curve \"NIST P-256\")(d %m)))",
1146 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
1147 gcry_mpi_release (dh);
1148 return GNUNET_SYSERR;
1150 gcry_mpi_release (dh);
1151 /* prepare data for signing */
1152 data = data_to_pkcs1 (purpose);
1154 /* get 'k' value from seed, if available */
1157 size = sizeof (struct GNUNET_HashCode);
1158 if (0 != (rc = gcry_mpi_scan (&k, GCRYMPI_FMT_USG,
1162 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
1163 return GNUNET_SYSERR;
1167 /* actually create signature */
1168 /* FIXME: need API to pass 'k' if 'seed' was non-NULL! */
1169 if (0 != (rc = gcry_pk_sign (&result, data, spriv)))
1171 LOG (GNUNET_ERROR_TYPE_WARNING,
1172 _("ECC signing failed at %s:%d: %s\n"), __FILE__,
1173 __LINE__, gcry_strerror (rc));
1174 gcry_sexp_release (data);
1175 gcry_sexp_release (spriv);
1177 gcry_mpi_release (k);
1178 memset (signature, 0, sizeof (struct GNUNET_FS_PseudonymSignature));
1179 return GNUNET_SYSERR;
1182 gcry_mpi_release (k);
1183 gcry_sexp_release (data);
1184 gcry_sexp_release (spriv);
1187 /* extract 'r' and 's' values from sexpression 'result' and store in 'signature' */
1188 if (0 != (rc = key_from_sexp (rs, result, "sig-val", "rs")))
1191 gcry_sexp_release (result);
1192 return GNUNET_SYSERR;
1194 gcry_sexp_release (result);
1195 size = sizeof (signature->sig_r);
1196 if (0 != (rc = gcry_mpi_print (GCRYMPI_FMT_USG, signature->sig_r, size,
1199 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_print", rc);
1200 gcry_mpi_release (rs[0]);
1201 gcry_mpi_release (rs[1]);
1202 return GNUNET_SYSERR;
1205 gcry_mpi_release (rs[0]);
1206 size = sizeof (signature->sig_s);
1207 if (0 != (rc = gcry_mpi_print (GCRYMPI_FMT_USG, signature->sig_s, size,
1210 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_print", rc);
1211 gcry_mpi_release (rs[1]);
1212 return GNUNET_SYSERR;
1214 gcry_mpi_release (rs[1]);
1220 * Get an ECC context (with Q set to the respective public key) from
1223 * @param pseudonym with information on 'q'
1224 * @return curve context
1227 get_context_from_pseudonym (struct GNUNET_FS_PseudonymIdentifier *pseudonym)
1236 /* extract 'q' from pseudonym */
1237 size = sizeof (pseudonym->q_x);
1238 if (0 != (rc = gcry_mpi_scan (&q_x, GCRYMPI_FMT_USG, pseudonym->q_x, size, &size)))
1240 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
1243 size = sizeof (pseudonym->q_y);
1244 if (0 != (rc = gcry_mpi_scan (&q_y, GCRYMPI_FMT_USG, pseudonym->q_y, size, &size)))
1246 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
1247 gcry_mpi_release (q_x);
1250 q = gcry_mpi_point_new (256);
1251 gcry_mpi_point_set (q, q_x, q_y, GCRYMPI_CONST_ONE);
1252 gcry_mpi_release (q_x);
1253 gcry_mpi_release (q_y);
1255 /* create basic ECC context */
1256 if (0 != (rc = gcry_mpi_ec_new (&ctx, NULL, "NIST P-256")))
1258 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_ec_new", rc); /* erroff gives more info */
1259 gcry_mpi_point_release (q);
1262 /* initialize 'ctx' with 'q' */
1263 gcry_mpi_ec_set_point ("q", q, ctx);
1264 gcry_mpi_point_release (q);
1270 * Given a pseudonym and a signing key, derive the corresponding public
1271 * key that would be used to verify the resulting signature.
1273 * @param pseudonym the public key (dQ in ECDSA)
1274 * @param signing_key input to derive 'h' (see section 2.4 of #2564)
1275 * @param verification_key resulting public key to verify the signature
1276 * created from the '(d+h)' of 'pseudonym' and the 'signing_key';
1277 * the value stored here can then be given to GNUNET_FS_pseudonym_verify.
1278 * @return GNUNET_OK on success, GNUNET_SYSERR on error
1281 GNUNET_FS_pseudonym_derive_verification_key (struct GNUNET_FS_PseudonymIdentifier *pseudonym,
1282 const struct GNUNET_HashCode *signing_key,
1283 struct GNUNET_FS_PseudonymIdentifier *verification_key)
1291 gcry_mpi_point_t hg;
1296 /* get 'h' value from signing key */
1297 size = sizeof (struct GNUNET_HashCode);
1298 if (0 != (rc = gcry_mpi_scan (&h, GCRYMPI_FMT_USG,
1302 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
1303 return GNUNET_SYSERR;
1305 /* create ECC context based on Q from pseudonym */
1306 if (NULL == (ctx = get_context_from_pseudonym (pseudonym)))
1308 gcry_mpi_release (h);
1309 return GNUNET_SYSERR;
1312 g = gcry_mpi_ec_get_point ("g", ctx, 0);
1314 /* then call the 'multiply' function, to compute the product hG */
1315 hg = gcry_mpi_point_new (0);
1316 gcry_mpi_ec_mul (hg, h, g, ctx);
1317 gcry_mpi_release (h);
1319 /* get Q = dG from 'pseudonym' */
1320 q = gcry_mpi_ec_get_point ("q", ctx, 0);
1321 /* calculate V = Q + hG = dG + hG = (d + h)G*/
1322 v = gcry_mpi_point_new (0);
1323 gcry_mpi_ec_add (v, q, hg, ctx);
1324 /* FIXME: free 'hg'? */
1326 /* store 'v' point in "verification_key" */
1327 v_x = gcry_mpi_new (256);
1328 v_y = gcry_mpi_new (256);
1329 gcry_mpi_ec_get_affine (v_x, v_y, v, ctx);
1331 gcry_mpi_point_release (v);
1332 gcry_ctx_release (ctx);
1334 size = sizeof (verification_key->q_x);
1335 if (0 != (rc = gcry_mpi_print (GCRYMPI_FMT_USG, verification_key->q_x, size,
1338 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_print", rc);
1339 gcry_mpi_release (v_x);
1340 gcry_mpi_release (v_y);
1341 return GNUNET_SYSERR;
1343 gcry_mpi_release (v_x);
1344 size = sizeof (verification_key->q_y);
1345 if (0 != (rc = gcry_mpi_print (GCRYMPI_FMT_USG, verification_key->q_y, size,
1348 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_print", rc);
1349 gcry_mpi_release (v_y);
1350 return GNUNET_SYSERR;
1352 gcry_mpi_release (v_y);
1358 * Verify a signature made with a pseudonym.
1360 * @param purpose data that was signed
1361 * @param signature signature to verify
1362 * @param verification_key public key to use for checking the signature;
1363 * corresponds to 'g^(x+h)' in section 2.4 of #2564.
1364 * @return GNUNET_OK on success (signature valid, 'pseudonym' set),
1365 * GNUNET_SYSERR if the signature is invalid
1368 GNUNET_FS_pseudonym_verify (const struct GNUNET_FS_PseudonymSignaturePurpose *purpose,
1369 const struct GNUNET_FS_PseudonymSignature *signature,
1370 const struct GNUNET_FS_PseudonymIdentifier *verification_key)
1373 gcry_sexp_t sig_sexpr;
1374 gcry_sexp_t pk_sexpr;
1385 /* build s-expression for signature */
1386 size = sizeof (signature->sig_r);
1387 if (0 != (rc = gcry_mpi_scan (&r, GCRYMPI_FMT_USG,
1388 signature->sig_r, size, &size)))
1390 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
1391 return GNUNET_SYSERR;
1393 size = sizeof (signature->sig_s);
1394 if (0 != (rc = gcry_mpi_scan (&s, GCRYMPI_FMT_USG,
1395 signature->sig_s, size, &size)))
1397 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
1398 gcry_mpi_release (r);
1399 return GNUNET_SYSERR;
1401 if (0 != (rc = gcry_sexp_build (&sig_sexpr, &erroff, "(sig-val(ecdsa(r %m)(s %m)))",
1404 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
1405 gcry_mpi_release (r);
1406 gcry_mpi_release (s);
1407 return GNUNET_SYSERR;
1409 gcry_mpi_release (r);
1410 gcry_mpi_release (s);
1413 /* build s-expression for data that was signed */
1414 data = data_to_pkcs1 (purpose);
1416 /* create context of public key and initialize Q */
1417 size = sizeof (verification_key->q_x);
1418 if (0 != (rc = gcry_mpi_scan (&q_x, GCRYMPI_FMT_USG,
1419 verification_key->q_x, size, &size)))
1421 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
1422 gcry_sexp_release (data);
1423 gcry_sexp_release (sig_sexpr);
1424 return GNUNET_SYSERR;
1426 size = sizeof (verification_key->q_y);
1427 if (0 != (rc = gcry_mpi_scan (&q_y, GCRYMPI_FMT_USG,
1428 verification_key->q_y, size, &size)))
1430 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc);
1431 gcry_sexp_release (data);
1432 gcry_sexp_release (sig_sexpr);
1433 gcry_mpi_release (q_x);
1434 return GNUNET_SYSERR;
1436 q = gcry_mpi_point_new (256);
1437 gcry_mpi_point_set (q, q_x, q_y, GCRYMPI_CONST_ONE);
1438 gcry_mpi_release (q_x);
1439 gcry_mpi_release (q_y);
1441 /* create basic ECC context */
1442 if (0 != (rc = gcry_mpi_ec_new (&ctx, NULL, "NIST P-256")))
1444 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_ec_new", rc); /* erroff gives more info */
1445 gcry_sexp_release (data);
1446 gcry_sexp_release (sig_sexpr);
1447 gcry_mpi_point_release (q);
1448 return GNUNET_SYSERR;
1450 /* initialize 'ctx' with 'q' */
1451 gcry_mpi_ec_set_point ("q", q, ctx);
1452 gcry_mpi_point_release (q);
1454 /* convert 'ctx' to 'sexp' */
1455 if (0 != (rc = gcry_pubkey_get_sexp (&pk_sexpr, GCRY_PK_GET_PUBKEY, ctx)))
1457 LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_from_context", rc);
1458 gcry_ctx_release (ctx);
1459 gcry_sexp_release (data);
1460 gcry_sexp_release (sig_sexpr);
1461 return GNUNET_SYSERR;
1463 gcry_ctx_release (ctx);
1465 /* finally, verify the signature */
1466 rc = gcry_pk_verify (sig_sexpr, data, pk_sexpr);
1467 gcry_sexp_release (sig_sexpr);
1468 gcry_sexp_release (data);
1469 gcry_sexp_release (pk_sexpr);
1472 LOG (GNUNET_ERROR_TYPE_WARNING,
1473 _("ECDSA signature verification failed at %s:%d: %s\n"), __FILE__,
1474 __LINE__, gcry_strerror (rc));
1475 return GNUNET_SYSERR;
1482 * Get the identifier (public key) of a pseudonym.
1484 * @param ph pseudonym handle with the private key
1485 * @param pseudonym pseudonym identifier (set based on 'ph')
1488 GNUNET_FS_pseudonym_get_identifier (struct GNUNET_FS_PseudonymHandle *ph,
1489 struct GNUNET_FS_PseudonymIdentifier *pseudonym)
1491 memcpy (pseudonym, &ph->public_key,
1492 sizeof (struct GNUNET_FS_PseudonymIdentifier));
1497 * Remove pseudonym from the set of known pseudonyms.
1499 * @param cfg overall configuration
1500 * @param id the pseudonym identifier
1501 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1504 GNUNET_FS_pseudonym_remove (const struct GNUNET_CONFIGURATION_Handle *cfg,
1505 const struct GNUNET_FS_PseudonymIdentifier *id)
1510 fn = get_data_filename (cfg, PS_METADATA_DIR, id);
1512 return GNUNET_SYSERR;
1513 result = UNLINK (fn);
1515 return (0 == result) ? GNUNET_OK : GNUNET_SYSERR;
1518 /* end of pseudonym.c */