Preparation for Reference Type
authorMarkus Voggenreiter <Markus.Voggenreiter@tum.de>
Wed, 23 Oct 2019 19:50:46 +0000 (21:50 +0200)
committerMarkus Voggenreiter <Markus.Voggenreiter@tum.de>
Wed, 23 Oct 2019 19:50:46 +0000 (21:50 +0200)
src/include/gnunet_gnsrecord_lib.h
src/include/gnunet_protocols.h
src/include/gnunet_reclaim_attribute_lib.h
src/include/gnunet_reclaim_service.h
src/reclaim-attribute/reclaim_attribute.c
src/reclaim-attribute/reclaim_attribute.h
src/reclaim/json_reclaim.c
src/reclaim/json_reclaim.h
src/reclaim/plugin_gnsrecord_reclaim.c
src/reclaim/plugin_rest_reclaim.c
src/reclaim/reclaim_api.c

index 41e23e1aba0cfa791856430bf6f0052415b7c89f..9e430c2aa3183a8731da3b16670e920300993a38 100644 (file)
@@ -145,6 +145,11 @@ extern "C" {
  */
 #define GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_ATTR 65554
 
+/**
+ * Record type for reclaim attestation references
+ */
+#define GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_REF 65555
+
 /**
  * Flags that can be set for a record.
  */
index f1dc74462653ed7475d80a6e253407b89ad74a38..f8d4243591ad30cb4b767eb48344d1efda8f8514 100644 (file)
@@ -2720,6 +2720,8 @@ extern "C" {
 
 #define GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_RESULT 979
 
+#define GNUNET_MESSAGE_TYPE_RECLAIM_REFERENCE_STORE 980
+
 /**************************************************
  *
  * ABD MESSAGE TYPES
index 8476e77fc2ff9c645b993822b4e4bbc68558657a..cfdecae7924b3f3d8c5f2d18eae796f2c0a2a451 100644 (file)
@@ -137,6 +137,34 @@ struct GNUNET_RECLAIM_ATTESTATION_Claim
   const void *data;
 };
 
+/**
+ * A reference to an Attestatiom.
+ */
+struct GNUNET_RECLAIM_ATTESTATION_REFERENCE
+{
+  /**
+   * ID
+   */
+  uint64_t id;
+
+  /**
+   * Referenced ID of Attestation
+   */
+  uint64_t id_attest;
+
+  /**
+   * The name of the attribute/attestation reference value. Note "name" must never be individually
+   * free'd
+   */
+  const char *name;
+
+  /**
+   * The name of the attribute/attestation reference value. Note "name" must never be individually
+   * free'd
+   */
+  const char *reference_value;
+};
+
 /**
  * A list of GNUNET_RECLAIM_ATTRIBUTE_Claim structures.
  */
@@ -450,6 +478,51 @@ GNUNET_RECLAIM_ATTESTATION_number_to_typename (uint32_t type);
 uint32_t
 GNUNET_RECLAIM_ATTESTATION_typename_to_number (const char *typename);
 
+/**
+ * Create a new attestation reference.
+ *
+ * @param attr_name the referenced claim name
+ * @param ref_value the claim name in the attestation
+ * @return the new reference
+ */
+struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *
+GNUNET_RECLAIM_ATTESTATION_reference_new (const char *attr_name,
+                                          const char *ref_value);
+
+
+/**
+ * Get required size for serialization buffer
+ *
+ * @param attr the reference to serialize
+ * @return the required buffer size
+ */
+size_t
+GNUNET_RECLAIM_ATTESTATION_REF_serialize_get_size (
+  const struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *attr);
+
+/**
+ * Serialize a reference
+ *
+ * @param attr the reference to serialize
+ * @param result the serialized reference
+ * @return length of serialized data
+ */
+size_t
+GNUNET_RECLAIM_ATTESTATION_REF_serialize (
+  const struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *attr,
+  char *result);
+
+/**
+ * Deserialize a reference
+ *
+ * @param data the serialized reference
+ * @param data_size the length of the serialized data
+ *
+ * @return a GNUNET_IDENTITY_PROVIDER_Attribute, must be free'd by caller
+ */
+struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *
+GNUNET_RECLAIM_ATTESTATION_REF_deserialize (const char *data, size_t data_size);
+
 #if 0 /* keep Emacsens' auto-indent happy */
 {
 #endif
index e7384fd0625d13b3a807bfa93b7b8b075000aa1a..eb6c1bc9e2d961b4032d6e457e97300d83699ffd 100644 (file)
@@ -117,7 +117,7 @@ typedef void (*GNUNET_RECLAIM_ContinuationWithStatus) (void *cls,
  */
 typedef void (*GNUNET_RECLAIM_AttributeResult) (
   void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
-  const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr, 
+  const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr,
   const struct GNUNET_RECLAIM_ATTESTATION_Claim *attest);
 
 
@@ -242,6 +242,26 @@ GNUNET_RECLAIM_get_attributes_start (
   GNUNET_RECLAIM_AttributeResult proc, void *proc_cls,
   GNUNET_SCHEDULER_TaskCallback finish_cb, void *finish_cb_cls);
 
+/**
+   * Store an attestation reference.  If the reference is already present,
+   * it is replaced with the new reference.
+   *
+   * @param h handle to the re:claimID service
+   * @param pkey private key of the identity
+   * @param attr the reference value
+   * @param exp_interval the relative expiration interval for the reference
+   * @param cont continuation to call when done
+   * @param cont_cls closure for @a cont
+   * @return handle to abort the request
+   */
+struct GNUNET_RECLAIM_Operation *
+GNUNET_RECLAIM_attestation_reference_store (
+  struct GNUNET_RECLAIM_Handle *h,
+  const struct GNUNET_CRYPTO_EcdsaPrivateKey *pkey,
+  const struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *attr,
+  const struct GNUNET_TIME_Relative *exp_interval,
+  GNUNET_RECLAIM_ContinuationWithStatus cont,
+  void *cont_cls);
 
 /**
  * Calls the record processor specified in #GNUNET_RECLAIM_get_attributes_start
index 2cc0827a77171d68564e60ab3c708f7119658ee5..0083ac53e9a2d1ad08edc5dbdd20b13b377ffddc 100644 (file)
@@ -400,6 +400,41 @@ GNUNET_RECLAIM_ATTESTATION_claim_new (const char *attr_name,
   return attr;
 }
 
+/**
+ * Create a new attestation reference.
+ *
+ * @param attr_name the referenced claim name
+ * @param ref_value the claim name in the attestation
+ * @return the new reference
+ */
+struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *
+GNUNET_RECLAIM_ATTESTATION_reference_new (const char *attr_name,
+                                          const char *ref_value)
+{
+  struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *attr;
+  char *write_ptr;
+  char *attr_name_tmp = GNUNET_strdup (attr_name);
+  char *ref_value_tmp = GNUNET_strdup (ref_value);
+
+  GNUNET_STRINGS_utf8_tolower (attr_name, attr_name_tmp);
+  GNUNET_STRINGS_utf8_tolower (ref_value, ref_value_tmp);
+
+  attr = GNUNET_malloc (sizeof(struct GNUNET_RECLAIM_ATTESTATION_REFERENCE)
+                        + strlen (attr_name_tmp) + strlen (ref_value_tmp) + 2);
+
+  write_ptr = (char *) &attr[1];
+  GNUNET_memcpy (write_ptr, attr_name_tmp, strlen (attr_name_tmp) + 1);
+  attr->name = write_ptr;
+
+  write_ptr = (char *) &attr[1];
+  GNUNET_memcpy (write_ptr, ref_value_tmp, strlen (ref_value_tmp) + 1);
+  attr->reference_value = write_ptr;
+
+  GNUNET_free (attr_name_tmp);
+  GNUNET_free (ref_value_tmp);
+  return attr;
+}
+
 /**
  * Add a new attribute to a claim list
  *
@@ -762,5 +797,97 @@ GNUNET_RECLAIM_ATTESTATION_deserialize (const char *data, size_t data_size)
   return attr;
 }
 
+/**
+ * Get required size for serialization buffer
+ *
+ * @param attr the reference to serialize
+ * @return the required buffer size
+ */
+size_t
+GNUNET_RECLAIM_ATTESTATION_REF_serialize_get_size (
+  const struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *attr)
+{
+  return sizeof(struct Attestation_Reference) + strlen (attr->name) + strlen (
+    attr->reference_value);
+}
+
+
+/**
+ * Serialize a reference
+ *
+ * @param attr the reference to serialize
+ * @param result the serialized reference
+ * @return length of serialized data
+ */
+size_t
+GNUNET_RECLAIM_ATTESTATION_REF_serialize (
+  const struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *attr,
+  char *result)
+{
+  size_t name_len;
+  size_t refval_len;
+  struct Attestation_Reference *attr_ser;
+  char *write_ptr;
+  attr_ser = (struct Attestation_Reference *) result;
+  attr_ser->reference_id = GNUNET_htonll (attr->id);
+  attr_ser->attestation_id = GNUNET_htonll (attr->id_attest);
+  name_len = strlen (attr->name);
+  refval_len = strlen (attr->reference_value);
+  attr_ser->name_len = htons (name_len);
+  attr_ser->ref_value_len = htons (refval_len);
+  write_ptr = (char *) &attr_ser[1];
+  GNUNET_memcpy (write_ptr, attr->name, name_len);
+  write_ptr += name_len;
+  GNUNET_memcpy (write_ptr, attr->reference_value, refval_len);
+
+  return sizeof(struct Attestation_Reference) + strlen (attr->name) + strlen (
+    attr->reference_value);
+}
+
 
+/**
+ * Deserialize a reference
+ *
+ * @param data the serialized reference
+ * @param data_size the length of the serialized data
+ *
+ * @return a GNUNET_IDENTITY_PROVIDER_Attribute, must be free'd by caller
+ */
+struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *
+GNUNET_RECLAIM_ATTESTATION_REF_deserialize (const char *data, size_t data_size)
+{
+  struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *attr;
+  struct Attestation_Reference *attr_ser;
+  size_t name_len;
+  size_t refval_len;
+  char *write_ptr;
+
+  if (data_size < sizeof(struct Attestation_Reference))
+    return NULL;
+  attr_ser = (struct Attestation_Reference *) data;
+  name_len = ntohs (attr_ser->name_len);
+  refval_len = ntohs (attr_ser->ref_value_len);
+  if (data_size < sizeof(struct Attestation_Reference) + refval_len + name_len)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Buffer too small to deserialize\n");
+    return NULL;
+  }
+  attr = GNUNET_malloc (sizeof(struct GNUNET_RECLAIM_ATTESTATION_REFERENCE)
+                        + refval_len + name_len + 2);
+
+  attr->id = GNUNET_ntohll (attr_ser->reference_id);
+  attr->id_attest = GNUNET_ntohll (attr_ser->attestation_id);
+
+  write_ptr = (char *) &attr[1];
+  GNUNET_memcpy (write_ptr, &attr_ser[1], name_len);
+  write_ptr[name_len] = '\0';
+  attr->name = write_ptr;
+
+  write_ptr += name_len + 1;
+  GNUNET_memcpy (write_ptr, (char *) &attr_ser[1] + name_len, refval_len);
+  write_ptr[refval_len] = '\0';
+  attr->reference_value = write_ptr;
+  return attr;
+}
 /* end of reclaim_attribute.c */
index 750afc479603ea2078fe53a24a505903596143b8..80f1e5aacfdc5c291f23cad21e24033ab6e2876e 100644 (file)
@@ -94,4 +94,33 @@ struct Attestation
   // followed by data_size Attestation value data
 };
 
+/**
+ * Serialized attestation reference
+ */
+struct Attestation_Reference
+{
+  /**
+   * Reference ID
+   */
+  uint64_t reference_id;
+
+  /**
+   * The ID of the referenced attestation
+   */
+  uint64_t attestation_id;
+
+  /**
+   * Claim Name length
+   */
+  uint32_t name_len;
+
+    /**
+   * Length of the referenced value
+   */
+  uint32_t ref_value_len;
+
+
+  // followed by the name and referenced value
+};
+
 #endif
index 6de5fca8675be2c2c14edcf84acb19552340dbed..209b7f5bc85d9809378f502fb9b7135fde227704 100644 (file)
@@ -368,3 +368,107 @@ GNUNET_RECLAIM_JSON_spec_claim_attest (struct
   *attr = NULL;
   return ret;
 }
+
+/**
+   * Parse given JSON object to an attestation claim
+   *
+   * @param cls closure, NULL
+   * @param root the json object representing data
+   * @param spec where to write the data
+   * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
+   */
+static int
+parse_attest_ref (void *cls, json_t *root, struct
+                  GNUNET_JSON_Specification *spec)
+{
+  struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *attr;
+  const char *name_str = NULL;
+  const char *ref_val_str = NULL;
+  const char *ref_id_str = NULL;
+  const char *id_str = NULL;
+  int unpack_state;
+
+  GNUNET_assert (NULL != root);
+
+  if (! json_is_object (root))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Error json is not array nor object!\n");
+    return GNUNET_SYSERR;
+  }
+  // interpret single reference
+  unpack_state = json_unpack (root,
+                              "{s:s, s?s, s:s, s:s!}",
+                              "name",
+                              &name_str,
+                              "id",
+                              &id_str,
+                              "ref_id",
+                              &ref_id_str,
+                              "ref_value",
+                              &ref_val_str);
+  if ((0 != unpack_state) || (NULL == name_str) || (NULL == ref_val_str) ||
+      (NULL == ref_id_str))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Error json object has a wrong format!\n");
+    return GNUNET_SYSERR;
+  }
+
+  attr = GNUNET_RECLAIM_ATTESTATION_reference_new (name_str, ref_val_str);
+
+  attr->id = 0;
+
+  if ((NULL == ref_id_str) || (0 == strlen (ref_id_str)))
+    attr->id_attest = 0;
+  else
+    GNUNET_STRINGS_string_to_data (ref_id_str,
+                                   strlen (ref_id_str),
+                                   &attr->id_attest,
+                                   sizeof(uint64_t));
+
+  *(struct GNUNET_RECLAIM_ATTESTATION_REFERENCE **) spec->ptr = attr;
+  return GNUNET_OK;
+}
+
+/**
+ * Cleanup data left from parsing RSA public key.
+ *
+ * @param cls closure, NULL
+ * @param[out] spec where to free the data
+ */
+static void
+clean_attest_ref (void *cls, struct GNUNET_JSON_Specification *spec)
+{
+  struct GNUNET_RECLAIM_ATTESTATION_REFERENCE **attr;
+
+  attr = (struct GNUNET_RECLAIM_ATTESTATION_REFERENCE **) spec->ptr;
+  if (NULL != *attr)
+  {
+    GNUNET_free (*attr);
+    *attr = NULL;
+  }
+}
+
+/**
+ * JSON Specification for Reclaim attestation references.
+ *
+ * @param ticket struct of GNUNET_RECLAIM_ATTESTATION_REFERENCE to fill
+ * @return JSON Specification
+ */
+struct GNUNET_JSON_Specification
+GNUNET_RECLAIM_JSON_spec_claim_attest_ref (struct
+                                           GNUNET_RECLAIM_ATTESTATION_REFERENCE
+                                           **attr)
+{
+  struct GNUNET_JSON_Specification ret = { .parser = &parse_attest_ref,
+                                           .cleaner = &clean_attest_ref,
+                                           .cls = NULL,
+                                           .field = NULL,
+                                           .ptr = attr,
+                                           .ptr_size = 0,
+                                           .size_ptr = NULL };
+
+  *attr = NULL;
+  return ret;
+}
\ No newline at end of file
index 4280cce486078b28d76089ae42de08d6974672bf..9e6479e5ed6862220685feea9986d04fb6b1f01b 100644 (file)
@@ -56,3 +56,12 @@ GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket);
 struct GNUNET_JSON_Specification
 GNUNET_RECLAIM_JSON_spec_claim_attest (struct
                                        GNUNET_RECLAIM_ATTESTATION_Claim **attr);
+
+  /**
+   * JSON Specification for Reclaim attestation references.
+   *
+   * @param ticket struct of GNUNET_RECLAIM_ATTESTATION_REFERENCE to fill
+   * @return JSON Specification
+   */
+  struct GNUNET_JSON_Specification
+  GNUNET_RECLAIM_JSON_spec_claim_attest_ref(struct GNUNET_RECLAIM_ATTESTATION_REFERENCE **attr);
index 62d7902a5120afb91276b9e1afa2c0ec46046242..e6938feb57ad850a1f76339469f91f0fb252fc6c 100644 (file)
@@ -55,6 +55,7 @@ value_to_string (void *cls, uint32_t type, const void *data, size_t data_size)
   case GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET:
   case GNUNET_GNSRECORD_TYPE_RECLAIM_MASTER:
   case GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_ATTR:
+  case GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_REF:
     return GNUNET_STRINGS_data_to_string_alloc (data, data_size);
 
   default:
@@ -95,6 +96,7 @@ string_to_value (void *cls, uint32_t type, const char *s, void **data,
   case GNUNET_GNSRECORD_TYPE_RECLAIM_MASTER:
   case GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET:
   case GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_ATTR:
+  case GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_REF:
     return GNUNET_STRINGS_string_to_data (s, strlen (s), *data, *data_size);
 
   default:
@@ -119,6 +121,7 @@ static struct
   { "RECLAIM_OIDC_CLIENT", GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_CLIENT },
   { "RECLAIM_OIDC_REDIRECT", GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT },
   { "RECLAIM_TICKET", GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET },
+  { "RECLAIM_ATTEST_REF", GNUNET_GNSRECORD_TYPE_RECLAIM_ATTEST_REF },
   { NULL, UINT32_MAX }
 };
 
index f126c0fb10dd483eb61a93de0ee939e45f3a4d87..693a84650171180aadaa83a2d0feb986d265e503 100644 (file)
@@ -451,14 +451,106 @@ ticket_collect (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
 }
 
 
+static void
+add_attestation_ref_cont (struct GNUNET_REST_RequestHandle *con_handle,
+                          const char *url,
+                          void *cls)
+{
+  struct RequestHandle *handle = cls;
+  const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
+  const char *identity;
+  struct EgoEntry *ego_entry;
+  struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *attribute;
+  struct GNUNET_TIME_Relative exp;
+  char term_data[handle->rest_handle->data_size + 1];
+  json_t *data_json;
+  json_error_t err;
+  struct GNUNET_JSON_Specification attrspec[] =
+  { GNUNET_RECLAIM_JSON_spec_claim_attest_ref (&attribute),
+    GNUNET_JSON_spec_end () };
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Adding an attestation reference for %s.\n",
+              handle->url);
+  if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTESTATION_REFERENCE) + strlen (
+        "reference/") + 1 >= strlen (
+        handle->url))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
+    GNUNET_SCHEDULER_add_now (&do_error, handle);
+    return;
+  }
+  identity = handle->url + strlen (
+    GNUNET_REST_API_NS_RECLAIM_ATTESTATION_REFERENCE) + strlen ("reference/") + 1;
+  for (ego_entry = handle->ego_head; NULL != ego_entry;
+       ego_entry = ego_entry->next)
+    if (0 == strcmp (identity, ego_entry->identifier))
+      break;
+  if (NULL == ego_entry)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Identity unknown (%s)\n", identity);
+    return;
+  }
+  identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
+  if (0 >= handle->rest_handle->data_size)
+  {
+    GNUNET_SCHEDULER_add_now (&do_error, handle);
+    return;
+  }
+
+  term_data[handle->rest_handle->data_size] = '\0';
+  GNUNET_memcpy (term_data,
+                 handle->rest_handle->data,
+                 handle->rest_handle->data_size);
+  data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_JSON_parse (data_json, attrspec, NULL, NULL));
+  json_decref (data_json);
+  if (NULL == attribute)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unable to parse attestation reference from %s\n",
+                term_data);
+    GNUNET_SCHEDULER_add_now (&do_error, handle);
+    return;
+  }
+  /**
+   * New ID for attribute
+   */
+  if (0 == attribute->id)
+    attribute->id = attribute->id_attest;
+  handle->idp = GNUNET_RECLAIM_connect (cfg);
+  exp = GNUNET_TIME_UNIT_HOURS;
+  handle->idp_op = GNUNET_RECLAIM_attestation_reference_store (handle->idp,
+                                                               identity_priv,
+                                                               attribute,
+                                                               &exp,
+                                                               &finished_cont,
+                                                               handle);
+  GNUNET_JSON_parse_free (attrspec);
+}
+
+
 static void
 add_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle,
                       const char *url,
                       void *cls)
 {
+  struct RequestHandle *handle = cls;
+  /* Check for substring "reference" */
+  if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTESTATION_REFERENCE) < strlen (
+        handle->url))
+  {
+    if ( strncmp ("reference/", (handle->url + strlen (
+                                   GNUNET_REST_API_NS_RECLAIM_ATTESTATION_REFERENCE)
+                                 + 1), strlen (
+                    "reference/")) == 0)
+    {
+      add_attestation_ref_cont (con_handle,url,cls);
+      return;
+    }
+  }
   const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
   const char *identity;
-  struct RequestHandle *handle = cls;
   struct EgoEntry *ego_entry;
   struct GNUNET_RECLAIM_ATTESTATION_Claim *attribute;
   struct GNUNET_TIME_Relative exp;
index bb0a5e3a8936c3e185f7fd28412aaf9aedddd64a..fb9ef855758b6b14f07ba76f9d04a2a82131fe97 100644 (file)
@@ -1115,6 +1115,53 @@ GNUNET_RECLAIM_attestation_delete (
   return op;
 }
 
+/**
+   * Store an attestation reference.  If the reference is already present,
+   * it is replaced with the new reference.
+   *
+   * @param h handle to the re:claimID service
+   * @param pkey private key of the identity
+   * @param attr the reference value
+   * @param exp_interval the relative expiration interval for the reference
+   * @param cont continuation to call when done
+   * @param cont_cls closure for @a cont
+   * @return handle to abort the request
+   */
+struct GNUNET_RECLAIM_Operation *
+GNUNET_RECLAIM_attestation_reference_store (
+  struct GNUNET_RECLAIM_Handle *h,
+  const struct GNUNET_CRYPTO_EcdsaPrivateKey *pkey,
+  const struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *attr,
+  const struct GNUNET_TIME_Relative *exp_interval,
+  GNUNET_RECLAIM_ContinuationWithStatus cont,
+  void *cont_cls)
+{
+  struct GNUNET_RECLAIM_Operation *op;
+  struct AttributeStoreMessage *sam;
+  size_t attr_len;
+  op = GNUNET_new (struct GNUNET_RECLAIM_Operation);
+  op->h = h;
+  op->as_cb = cont;
+  op->cls = cont_cls;
+  op->r_id = h->r_id_gen++;
+  GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op);
+  attr_len = GNUNET_RECLAIM_ATTESTATION_REF_serialize_get_size (attr);
+  op->env = GNUNET_MQ_msg_extra (sam,
+                                 attr_len,
+                                 GNUNET_MESSAGE_TYPE_RECLAIM_REFERENCE_STORE);
+  sam->identity = *pkey;
+  sam->id = htonl (op->r_id);
+  sam->exp = GNUNET_htonll (exp_interval->rel_value_us);
+
+  GNUNET_RECLAIM_ATTESTATION_REF_serialize (attr, (char *) &sam[1]);
+
+  sam->attr_len = htons (attr_len);
+  if (NULL != h->mq)
+    GNUNET_MQ_send_copy (h->mq, op->env);
+  return op;
+}
+
+
 /**
  * List all attributes for a local identity.
  * This MUST lock the `struct GNUNET_RECLAIM_Handle`