From: Markus Voggenreiter Date: Wed, 23 Oct 2019 19:50:46 +0000 (+0200) Subject: Preparation for Reference Type X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=32f7f80f0559c54777134cee1874e60b1f3d0f38;p=oweals%2Fgnunet.git Preparation for Reference Type --- diff --git a/src/include/gnunet_gnsrecord_lib.h b/src/include/gnunet_gnsrecord_lib.h index 41e23e1ab..9e430c2aa 100644 --- a/src/include/gnunet_gnsrecord_lib.h +++ b/src/include/gnunet_gnsrecord_lib.h @@ -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. */ diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index f1dc74462..f8d424359 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -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 diff --git a/src/include/gnunet_reclaim_attribute_lib.h b/src/include/gnunet_reclaim_attribute_lib.h index 8476e77fc..cfdecae79 100644 --- a/src/include/gnunet_reclaim_attribute_lib.h +++ b/src/include/gnunet_reclaim_attribute_lib.h @@ -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 diff --git a/src/include/gnunet_reclaim_service.h b/src/include/gnunet_reclaim_service.h index e7384fd06..eb6c1bc9e 100644 --- a/src/include/gnunet_reclaim_service.h +++ b/src/include/gnunet_reclaim_service.h @@ -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 diff --git a/src/reclaim-attribute/reclaim_attribute.c b/src/reclaim-attribute/reclaim_attribute.c index 9ac75dfba..0083ac53e 100644 --- a/src/reclaim-attribute/reclaim_attribute.c +++ b/src/reclaim-attribute/reclaim_attribute.c @@ -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,4 +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 */ diff --git a/src/reclaim-attribute/reclaim_attribute.h b/src/reclaim-attribute/reclaim_attribute.h index 750afc479..80f1e5aac 100644 --- a/src/reclaim-attribute/reclaim_attribute.h +++ b/src/reclaim-attribute/reclaim_attribute.h @@ -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 diff --git a/src/reclaim/json_reclaim.c b/src/reclaim/json_reclaim.c index a0016bac8..552ca0e69 100644 --- a/src/reclaim/json_reclaim.c +++ b/src/reclaim/json_reclaim.c @@ -374,3 +374,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 diff --git a/src/reclaim/json_reclaim.h b/src/reclaim/json_reclaim.h index 4280cce48..9e6479e5e 100644 --- a/src/reclaim/json_reclaim.h +++ b/src/reclaim/json_reclaim.h @@ -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); diff --git a/src/reclaim/plugin_gnsrecord_reclaim.c b/src/reclaim/plugin_gnsrecord_reclaim.c index 58345edc4..e00b246c2 100644 --- a/src/reclaim/plugin_gnsrecord_reclaim.c +++ b/src/reclaim/plugin_gnsrecord_reclaim.c @@ -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 } }; diff --git a/src/reclaim/plugin_rest_reclaim.c b/src/reclaim/plugin_rest_reclaim.c index 5908a38dd..b52cf9650 100644 --- a/src/reclaim/plugin_rest_reclaim.c +++ b/src/reclaim/plugin_rest_reclaim.c @@ -454,14 +454,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; diff --git a/src/reclaim/reclaim_api.c b/src/reclaim/reclaim_api.c index 860a0f0ab..a6ff0237d 100644 --- a/src/reclaim/reclaim_api.c +++ b/src/reclaim/reclaim_api.c @@ -1117,6 +1117,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`