/*
      This file is part of GNUnet
      Copyright (C) 2010-2015 GNUnet e.V.

      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
      by the Free Software Foundation; either version 3, or (at your
      option) any later version.

      GNUnet is distributed in the hope that it will be useful, but
      WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with GNUnet; see the file COPYING.  If not, write to the
      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
      Boston, MA 02110-1301, USA.
 */

/**
 * @file identity-provider/identity_token.c
 * @brief helper library to manage identity tokens
 * @author Martin Schanzenbach
 */
#include "platform.h"
#include "gnunet_util_lib.h"
#include "gnunet_signatures.h"
#include "identity_token.h"
#include <jansson.h>
#include <inttypes.h>

#define JWT_ALG "alg"

#define JWT_ALG_VALUE "ED512"

#define JWT_TYP "typ"

#define JWT_TYP_VALUE "jwt"

/**
 * Crypto helper functions
 */

static int
create_sym_key_from_ecdh(const struct GNUNET_HashCode *new_key_hash,
                         struct GNUNET_CRYPTO_SymmetricSessionKey *skey,
                         struct GNUNET_CRYPTO_SymmetricInitializationVector *iv)
{
  struct GNUNET_CRYPTO_HashAsciiEncoded new_key_hash_str;

  GNUNET_CRYPTO_hash_to_enc (new_key_hash,
                             &new_key_hash_str);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating symmetric rsa key from %s\n", (char*)&new_key_hash_str);
  static const char ctx_key[] = "gnuid-aes-ctx-key";
  GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey),
                     new_key_hash, sizeof (struct GNUNET_HashCode),
                     ctx_key, strlen (ctx_key),
                     NULL, 0);
  static const char ctx_iv[] = "gnuid-aes-ctx-iv";
  GNUNET_CRYPTO_kdf (iv, sizeof (struct GNUNET_CRYPTO_SymmetricInitializationVector),
                     new_key_hash, sizeof (struct GNUNET_HashCode),
                     ctx_iv, strlen (ctx_iv),
                     NULL, 0);
  return GNUNET_OK;
}



/**
 * Decrypts data part from a token code
 */
static int
decrypt_str_ecdhe (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key,
                   const struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_key,
                   const char *cyphertext,
                   size_t cyphertext_len,
                   char **result_str)
{
  struct GNUNET_HashCode new_key_hash;
  struct GNUNET_CRYPTO_SymmetricSessionKey enc_key;
  struct GNUNET_CRYPTO_SymmetricInitializationVector enc_iv;

  char *str_buf = GNUNET_malloc (cyphertext_len);
  size_t str_size;

  //Calculate symmetric key from ecdh parameters
  GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdsa_ecdh (priv_key,
                                                        ecdh_key,
                                                        &new_key_hash));

  create_sym_key_from_ecdh (&new_key_hash,
                            &enc_key,
                            &enc_iv);

  str_size = GNUNET_CRYPTO_symmetric_decrypt (cyphertext,
                                              cyphertext_len,
                                              &enc_key,
                                              &enc_iv,
                                              str_buf);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Decrypted bytes: %zd Expected bytes: %zd\n",
              str_size,
              cyphertext_len);
  if (-1 == str_size)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ECDH invalid\n");
    GNUNET_free (str_buf);
    return GNUNET_SYSERR;
  }
  *result_str = GNUNET_malloc (str_size+1);
  GNUNET_memcpy (*result_str, str_buf, str_size);
  (*result_str)[str_size] = '\0';
  GNUNET_free (str_buf);
  return GNUNET_OK;

}

/**
 * Decrypt string using pubkey and ECDHE
*/
static int
decrypt_str_ecdhe2 (const struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_privkey,
                    const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key,
                    const char *ciphertext,
                    size_t ciphertext_len,
                    char **plaintext)
{
  struct GNUNET_CRYPTO_SymmetricSessionKey skey;
  struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
  struct GNUNET_HashCode new_key_hash;

  //This is true see documentation for GNUNET_CRYPTO_symmetric_encrypt
  *plaintext = GNUNET_malloc (ciphertext_len);

  // Derived key K = H(eB)
  GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_ecdsa (ecdh_privkey,
                                                        aud_key,
                                                        &new_key_hash));
  create_sym_key_from_ecdh(&new_key_hash, &skey, &iv);
  GNUNET_CRYPTO_symmetric_decrypt (ciphertext,
                                   ciphertext_len,
                                   &skey, &iv,
                                   *plaintext);
  return GNUNET_OK;
}


/**
 * Encrypt string using pubkey and ECDHE
 * Returns ECDHE pubkey to be used for decryption
 */
static int
encrypt_str_ecdhe (const char *plaintext,
                   const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key,
                   char **cyphertext,
                   struct GNUNET_CRYPTO_EcdhePrivateKey **ecdh_privkey,
                   struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pubkey)
{
  struct GNUNET_CRYPTO_SymmetricSessionKey skey;
  struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
  struct GNUNET_HashCode new_key_hash;
  ssize_t enc_size;

  // ECDH keypair E = eG
  *ecdh_privkey = GNUNET_CRYPTO_ecdhe_key_create();
  GNUNET_CRYPTO_ecdhe_key_get_public (*ecdh_privkey,
                                      ecdh_pubkey);

  //This is true see documentation for GNUNET_CRYPTO_symmetric_encrypt
  *cyphertext = GNUNET_malloc (strlen (plaintext));

  // Derived key K = H(eB)
  GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_ecdsa (*ecdh_privkey,
                                                        pub_key,
                                                        &new_key_hash));
  create_sym_key_from_ecdh(&new_key_hash, &skey, &iv);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting string %s\n (len=%zd)",
              plaintext,
              strlen (plaintext));
  enc_size = GNUNET_CRYPTO_symmetric_encrypt (plaintext,
                                              strlen (plaintext),
                                              &skey, &iv,
                                              *cyphertext);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypted (len=%zd)", enc_size);
  return GNUNET_OK;
}


/**
 * Identity Token API
 */


/**
 * Create an Identity Token
 *
 * @param type the JSON API resource type
 * @param id the JSON API resource id
 * @return a new JSON API resource or NULL on error.
 */
struct IdentityToken*
token_create (const struct GNUNET_CRYPTO_EcdsaPublicKey* iss,
                                       const struct GNUNET_CRYPTO_EcdsaPublicKey* aud)
{
  struct IdentityToken *token;
  char* audience;
  char* issuer;

  issuer = GNUNET_STRINGS_data_to_string_alloc (iss,
                                                sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
  audience = GNUNET_STRINGS_data_to_string_alloc (aud,
                                                  sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));

  token = GNUNET_malloc (sizeof (struct IdentityToken));
  token_add_attr (token, "iss", issuer);
  token_add_attr (token, "aud", audience);
  token_add_attr (token, "sub", issuer);
  token->aud_key = *aud;
  GNUNET_free (issuer);
  GNUNET_free (audience);
  return token;
}

void
token_destroy (struct IdentityToken *token)
{
  struct TokenAttr *attr;
  struct TokenAttr *tmp_attr;
  struct TokenAttrValue *val;
  struct TokenAttrValue *tmp_val;

  for (attr = token->attr_head; NULL != attr;)
  {
    tmp_attr = attr->next;
    GNUNET_CONTAINER_DLL_remove (token->attr_head,
                                 token->attr_tail,
                                 attr);
    for (val = attr->val_head; NULL != val;)
    {
      tmp_val = val->next;
      GNUNET_CONTAINER_DLL_remove (attr->val_head,
                                   attr->val_tail,
                                   val);
      if (NULL != val->value)
        GNUNET_free (val->value);
      GNUNET_free (val);
      val = tmp_val;
    }
    GNUNET_free (attr->name);
    GNUNET_free (attr);
    attr = tmp_attr;
  }

  
  GNUNET_free (token);
}

void
token_add_attr (struct IdentityToken *token,
                const char* key,
                const char* value)
{
  struct TokenAttr *attr;
  struct TokenAttrValue *new_val;
  GNUNET_assert (NULL != token);

  new_val = GNUNET_malloc (sizeof (struct TokenAttrValue));
  new_val->value = GNUNET_strdup (value);
  for (attr = token->attr_head; NULL != attr; attr = attr->next)
  {
    if (0 == strcmp (key, attr->name))
      break;
  }

  if (NULL == attr)
  {
    attr = GNUNET_malloc (sizeof (struct TokenAttr));
    attr->name = GNUNET_strdup (key);
    GNUNET_CONTAINER_DLL_insert (token->attr_head,
                                 token->attr_tail,
                                 attr);
  }

  GNUNET_CONTAINER_DLL_insert (attr->val_head,
                               attr->val_tail,
                               new_val);
}

void
token_add_attr_int (struct IdentityToken *token,
                    const char* key,
                    uint64_t value)
{
  struct TokenAttr *attr;
  struct TokenAttrValue *new_val;
  GNUNET_assert (NULL != token);

  new_val = GNUNET_malloc (sizeof (struct TokenAttrValue));
  new_val->int_value = value;
  for (attr = token->attr_head; NULL != attr; attr = attr->next)
  {
    if (0 == strcmp (key, attr->name))
      break;
  }

  if (NULL == attr)
  {
    attr = GNUNET_malloc (sizeof (struct TokenAttr));
    attr->name = GNUNET_strdup (key);
    GNUNET_CONTAINER_DLL_insert (token->attr_head,
                                 token->attr_tail,
                                 attr);
  }

  GNUNET_CONTAINER_DLL_insert (attr->val_head,
                               attr->val_tail,
                               new_val);
}

static void
parse_json_payload(const char* payload_base64,
                   struct IdentityToken *token) 
{
  const char *key;
  const json_t *value;
  const json_t *arr_value;
  char *payload;
  int idx;
  json_t *payload_json;
  json_error_t err_json;

  GNUNET_STRINGS_base64_decode (payload_base64,
                                strlen (payload_base64),
                                &payload);
  //TODO signature and aud key
  payload_json = json_loads (payload, JSON_DECODE_ANY, &err_json);

  json_object_foreach (payload_json, key, value)
  {
    if (json_is_array (value))
    {
      json_array_foreach (value, idx, arr_value)
      {
        if (json_is_integer (arr_value))
          token_add_attr_int (token, key,
                              json_integer_value (arr_value));
        else
          token_add_attr (token,
                          key,
                          json_string_value (arr_value));
      }
    } else {
      if (json_is_integer (value))
        token_add_attr_int (token, key,
                            json_integer_value (value));
      else
        token_add_attr (token, key, json_string_value (value));
    }
  }

  json_decref (payload_json);
  GNUNET_free (payload);
}

int
token_parse2 (const char* raw_data,
              const struct GNUNET_CRYPTO_EcdhePrivateKey *priv_key,
              const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key,
              struct IdentityToken **result)
{
  char *enc_token_str;
  char *tmp_buf;
  char *token_str;
  char *enc_token;
  char *payload_base64;
  size_t enc_token_len;

  GNUNET_asprintf (&tmp_buf, "%s", raw_data);
  strtok (tmp_buf, ",");
  enc_token_str = strtok (NULL, ",");

  enc_token_len = GNUNET_STRINGS_base64_decode (enc_token_str,
                                                strlen (enc_token_str),
                                                &enc_token);
  if (GNUNET_OK != decrypt_str_ecdhe2 (priv_key,
                                       aud_key,
                                       enc_token,
                                       enc_token_len,
                                       &token_str))
  {
    GNUNET_free (tmp_buf);
    GNUNET_free (enc_token);
    return GNUNET_SYSERR;
  }

  GNUNET_assert (NULL != strtok (token_str, "."));
  payload_base64 = strtok (NULL, ".");

  *result = GNUNET_malloc (sizeof (struct IdentityToken));
  parse_json_payload (payload_base64, *result);

  (*result)->aud_key =  *aud_key;
  GNUNET_free (enc_token);
  GNUNET_free (token_str);
  GNUNET_free (tmp_buf);
  return GNUNET_OK;
}

int
token_parse (const char* raw_data,
             const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key,
             struct IdentityToken **result)
{
  char *ecdh_pubkey_str;
  char *enc_token_str;
  char *tmp_buf;
  char *token_str;
  char *enc_token;
  char *payload_base64;
  size_t enc_token_len;
  struct GNUNET_CRYPTO_EcdhePublicKey ecdh_pubkey;

  GNUNET_asprintf (&tmp_buf, "%s", raw_data);
  ecdh_pubkey_str = strtok (tmp_buf, ",");
  enc_token_str = strtok (NULL, ",");
  
  GNUNET_assert (NULL != ecdh_pubkey_str);
  GNUNET_assert (NULL != enc_token_str);

  GNUNET_STRINGS_string_to_data (ecdh_pubkey_str,
                                 strlen (ecdh_pubkey_str),
                                 &ecdh_pubkey,
                                 sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));
  enc_token_len = GNUNET_STRINGS_base64_decode (enc_token_str,
                                                strlen (enc_token_str),
                                                &enc_token);
  if (GNUNET_OK != decrypt_str_ecdhe (priv_key,
                                      &ecdh_pubkey,
                                      enc_token,
                                      enc_token_len,
                                      &token_str))
  {
    GNUNET_free (tmp_buf);
    GNUNET_free (enc_token);
    return GNUNET_SYSERR;
  }

  GNUNET_assert (NULL != strtok (token_str, "."));
  payload_base64 = strtok (NULL, ".");

  *result = GNUNET_malloc (sizeof (struct IdentityToken));
  parse_json_payload (payload_base64, *result);

  GNUNET_free (enc_token);
  GNUNET_free (token_str);
  GNUNET_free (tmp_buf);
  return GNUNET_OK;
}

static char*
create_json_payload (const struct IdentityToken *token)
{
  struct TokenAttr *attr;
  struct TokenAttrValue *val;
  json_t *root;
  char *json_str;

  root = json_object();
  for (attr = token->attr_head; NULL != attr; attr = attr->next)
  {
    for (val = attr->val_head; NULL != val; val = val->next)
    {
      if (NULL != val->value)
      {
        json_object_set_new (root,
                             attr->name,
                             json_string (val->value)); 
      } else {
        json_object_set_new (root,
                             attr->name,
                             json_integer (val->int_value));
      }
    }
  }
  json_str = json_dumps (root, JSON_INDENT(1));
  json_decref (root);
  return json_str;
}

static char*
create_json_header(void)
{
  json_t *root;
  char *json_str;

  root = json_object ();
  json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE));
  json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE));

  json_str = json_dumps (root, JSON_INDENT(1));
  json_decref (root);
  return json_str;
}

int
token_to_string (const struct IdentityToken *token,
                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key,
                 char **result)
{
  char *payload_str;
  char *header_str;
  char *payload_base64;
  char *header_base64;
  char *padding;
  char *signature_target;
  char *signature_str;
  struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
  header_str = create_json_header();
  GNUNET_STRINGS_base64_encode (header_str,
                                strlen (header_str),
                                &header_base64);
  //Remove GNUNET padding of base64
  padding = strtok(header_base64, "=");
  while (NULL != padding)
    padding = strtok(NULL, "=");

  payload_str = create_json_payload (token);
  GNUNET_STRINGS_base64_encode (payload_str,
                                strlen (payload_str),
                                &payload_base64);

  //Remove GNUNET padding of base64
  padding = strtok(payload_base64, "=");
  while (NULL != padding)
    padding = strtok(NULL, "=");

  GNUNET_asprintf (&signature_target, "%s,%s", header_base64, payload_base64);
  purpose =
    GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
                   strlen (signature_target));
  purpose->size =
    htonl (strlen (signature_target) + sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose));
  purpose->purpose = htonl(GNUNET_SIGNATURE_PURPOSE_GNUID_TOKEN);
  GNUNET_memcpy (&purpose[1], signature_target, strlen (signature_target));
  if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_sign (priv_key,
                                             purpose,
                                             (struct GNUNET_CRYPTO_EcdsaSignature *)&token->signature))
  {
    GNUNET_free (signature_target);
    GNUNET_free (payload_str);
    GNUNET_free (payload_base64);
    GNUNET_free (header_base64);
    GNUNET_free (purpose);
    return GNUNET_SYSERR;
  }

  GNUNET_STRINGS_base64_encode ((const char*)&token->signature,
                                sizeof (struct GNUNET_CRYPTO_EcdsaSignature),
                                &signature_str);
  GNUNET_asprintf (result, "%s.%s.%s",
                   header_base64, payload_base64, signature_str);
  GNUNET_free (signature_target);
  GNUNET_free (payload_str);
  GNUNET_free (header_str);
  GNUNET_free (signature_str);
  GNUNET_free (payload_base64);
  GNUNET_free (header_base64);
  GNUNET_free (purpose);
  return GNUNET_OK;
}

int
token_serialize (const struct IdentityToken *token,
                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key,
                 struct GNUNET_CRYPTO_EcdhePrivateKey **ecdh_privkey,
                 char **result)
{
  char *token_str;
  char *enc_token;
  char *dh_key_str;
  char *enc_token_base64;
  struct GNUNET_CRYPTO_EcdhePublicKey ecdh_pubkey;

  GNUNET_assert (GNUNET_OK == token_to_string (token,
                                               priv_key,
                                               &token_str));

  GNUNET_assert (GNUNET_OK == encrypt_str_ecdhe (token_str,
                                                 &token->aud_key,
                                                 &enc_token,
                                                 ecdh_privkey,
                                                 &ecdh_pubkey));
  GNUNET_STRINGS_base64_encode (enc_token,
                                strlen (token_str),
                                &enc_token_base64);
  dh_key_str = GNUNET_STRINGS_data_to_string_alloc (&ecdh_pubkey,
                                                    sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));
  GNUNET_asprintf (result, "%s,%s", dh_key_str, enc_token_base64);
  GNUNET_free (dh_key_str);
  GNUNET_free (enc_token_base64);
  GNUNET_free (enc_token);
  GNUNET_free (token_str);
  return GNUNET_OK;
}

struct TokenTicketPayload*
ticket_payload_create (uint64_t nonce,
                       const struct GNUNET_CRYPTO_EcdsaPublicKey* identity_pkey,
                       const char* lbl_str)
{
  struct TokenTicketPayload* payload;

  payload = GNUNET_malloc (sizeof (struct TokenTicketPayload));
  payload->nonce = nonce;
  payload->identity_key = *identity_pkey;
  GNUNET_asprintf (&payload->label, lbl_str, strlen (lbl_str));
  return payload;
}

void
ticket_payload_destroy (struct TokenTicketPayload* payload)
{
  if (NULL != payload->label)
    GNUNET_free (payload->label);
  GNUNET_free (payload);
}

void
ticket_payload_serialize (struct TokenTicketPayload *payload,
                          char **result)
{
  char* identity_key_str;

  identity_key_str = GNUNET_STRINGS_data_to_string_alloc (&payload->identity_key,
                                                          sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));

  GNUNET_asprintf (result, 
                   "{\"nonce\": \"%"SCNu64"\",\"identity\": \"%s\",\"label\": \"%s\"}",
                   payload->nonce, identity_key_str, payload->label);
  GNUNET_free (identity_key_str);

}


/**
 * Create the token code
 * The data is encrypted with a share ECDH derived secret using B (aud_key)
 * and e (ecdh_privkey)
 * The ticket also contains E (ecdh_pubkey) and a signature over the
 * data and E
 */
struct TokenTicket*
ticket_create (uint64_t nonce,
               const struct GNUNET_CRYPTO_EcdsaPublicKey* identity_pkey,
               const char* lbl_str,
               const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key)
{
  struct TokenTicket *ticket;
  struct TokenTicketPayload *code_payload;

  ticket = GNUNET_malloc (sizeof (struct TokenTicket));
  code_payload = ticket_payload_create (nonce,
                                        identity_pkey,
                                        lbl_str);
  ticket->aud_key = *aud_key;
  ticket->payload = code_payload;


  return ticket;
}

void
ticket_destroy (struct TokenTicket *ticket)
{
  ticket_payload_destroy (ticket->payload);
  GNUNET_free (ticket);
}

int
ticket_serialize (struct TokenTicket *ticket,
                  const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key,
                  char **result)
{
  char *code_payload_str;
  char *enc_ticket_payload;
  char *ticket_payload_str;
  char *ticket_sig_str;
  char *ticket_str;
  char *dh_key_str;
  char *write_ptr;
  struct GNUNET_CRYPTO_EcdhePrivateKey *ecdhe_privkey;

  struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;

  ticket_payload_serialize (ticket->payload,
                            &code_payload_str);

  GNUNET_assert (GNUNET_OK == encrypt_str_ecdhe (code_payload_str,
                                                 &ticket->aud_key,
                                                 &enc_ticket_payload,
                                                 &ecdhe_privkey,
                                                 &ticket->ecdh_pubkey));

  GNUNET_free (ecdhe_privkey);

  purpose = 
    GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + 
                   sizeof (struct GNUNET_CRYPTO_EcdhePublicKey) + //E
                   strlen (code_payload_str)); // E_K (code_str)
  purpose->size = 
    htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
           sizeof (struct GNUNET_CRYPTO_EcdhePublicKey) +
           strlen (code_payload_str));
  purpose->purpose = htonl(GNUNET_SIGNATURE_PURPOSE_GNUID_TICKET);
  write_ptr = (char*) &purpose[1];
  GNUNET_memcpy (write_ptr,
          &ticket->ecdh_pubkey,
          sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));
  write_ptr += sizeof (struct GNUNET_CRYPTO_EcdhePublicKey);
  GNUNET_memcpy (write_ptr, enc_ticket_payload, strlen (code_payload_str));
  GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdsa_sign (priv_key,
                                                        purpose,
                                                        &ticket->signature));
  GNUNET_STRINGS_base64_encode (enc_ticket_payload,
                                strlen (code_payload_str),
                                &ticket_payload_str);
  ticket_sig_str = GNUNET_STRINGS_data_to_string_alloc (&ticket->signature,
                                                        sizeof (struct GNUNET_CRYPTO_EcdsaSignature));

  dh_key_str = GNUNET_STRINGS_data_to_string_alloc (&ticket->ecdh_pubkey,
                                                    sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Using ECDH pubkey %s to encrypt\n", dh_key_str);
  GNUNET_asprintf (&ticket_str, "{\"data\": \"%s\", \"ecdh\": \"%s\", \"signature\": \"%s\"}",
                   ticket_payload_str, dh_key_str, ticket_sig_str);
  GNUNET_STRINGS_base64_encode (ticket_str, strlen (ticket_str), result);
  GNUNET_free (dh_key_str);
  GNUNET_free (purpose);
  GNUNET_free (ticket_str);
  GNUNET_free (ticket_sig_str);
  GNUNET_free (code_payload_str);
  GNUNET_free (enc_ticket_payload);
  GNUNET_free (ticket_payload_str);
  return GNUNET_OK;
}

int
ticket_payload_parse(const char *raw_data,
                     ssize_t data_len,
                     const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key,
                     const struct GNUNET_CRYPTO_EcdhePublicKey *ecdhe_pkey,
                     struct TokenTicketPayload **result)
{
  const char* label_str;
  const char* nonce_str;
  const char* identity_key_str;

  json_t *root;
  json_t *label_json;
  json_t *identity_json;
  json_t *nonce_json;
  json_error_t err_json;
  char* data_str;
  uint64_t nonce;
  struct GNUNET_CRYPTO_EcdsaPublicKey id_pkey;

  if (GNUNET_OK != decrypt_str_ecdhe (priv_key,
                                      ecdhe_pkey,
                                      raw_data,
                                      data_len,
                                      &data_str))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Data decryption failed\n");
    return GNUNET_SYSERR;
  }

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Data: %s\n", data_str);
  root = json_loads (data_str, JSON_DECODE_ANY, &err_json);
  if (!root)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Error parsing data: %s\n", err_json.text);
    GNUNET_free (data_str);
    return GNUNET_SYSERR;
  }

  identity_json = json_object_get (root, "identity");
  if (!json_is_string (identity_json))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Error parsing data: %s\n", err_json.text);
    json_decref (root);
    GNUNET_free (data_str);
    return GNUNET_SYSERR;
  }
  identity_key_str = json_string_value (identity_json);
  GNUNET_STRINGS_string_to_data (identity_key_str,
                                 strlen (identity_key_str),
                                 &id_pkey,
                                 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));


  label_json = json_object_get (root, "label");
  if (!json_is_string (label_json))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Error parsing data: %s\n", err_json.text);
    json_decref (root);
    GNUNET_free (data_str);
    return GNUNET_SYSERR;
  }

  label_str = json_string_value (label_json);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found label: %s\n", label_str);

  nonce_json = json_object_get (root, "nonce");
  if (!json_is_string (label_json))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Error parsing data: %s\n", err_json.text);
    json_decref (root);
    GNUNET_free (data_str);
    return GNUNET_SYSERR;
  }

  nonce_str = json_string_value (nonce_json);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found nonce: %s\n", nonce_str);
  
  GNUNET_assert (0 != sscanf (nonce_str, "%"SCNu64, &nonce));

  *result = ticket_payload_create (nonce,
                                   (const struct GNUNET_CRYPTO_EcdsaPublicKey*)&id_pkey,
                                   label_str);
  GNUNET_free (data_str);
  json_decref (root);
  return GNUNET_OK;

}

int
ticket_parse (const char *raw_data,
              const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key,
              struct TokenTicket **result)
{
  const char* enc_data_str;
  const char* ecdh_enc_str;
  const char* signature_enc_str;

  json_t *root;
  json_t *signature_json;
  json_t *ecdh_json;
  json_t *enc_data_json;
  json_error_t err_json;
  char* enc_data;
  char* ticket_decoded;
  char* write_ptr;
  size_t enc_data_len;
  struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
  struct TokenTicket *ticket;
  struct TokenTicketPayload *ticket_payload;

  ticket_decoded = NULL;
  GNUNET_STRINGS_base64_decode (raw_data, strlen (raw_data), &ticket_decoded);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ticket: %s\n", ticket_decoded);
  root = json_loads (ticket_decoded, JSON_DECODE_ANY, &err_json);
  if (!root)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "%s\n", err_json.text);
    return GNUNET_SYSERR;
  }

  signature_json = json_object_get (root, "signature");
  ecdh_json = json_object_get (root, "ecdh");
  enc_data_json = json_object_get (root, "data");

  signature_enc_str = json_string_value (signature_json);
  ecdh_enc_str = json_string_value (ecdh_json);
  enc_data_str = json_string_value (enc_data_json);

  ticket = GNUNET_malloc (sizeof (struct TokenTicket));

  if (GNUNET_OK != GNUNET_STRINGS_string_to_data (ecdh_enc_str,
                                                  strlen (ecdh_enc_str),
                                                  &ticket->ecdh_pubkey,
                                                  sizeof  (struct GNUNET_CRYPTO_EcdhePublicKey)))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ECDH PKEY %s invalid in data\n", ecdh_enc_str);
    json_decref (root);
    GNUNET_free (ticket);
    return GNUNET_SYSERR;
  }
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Using ECDH pubkey %s for data decryption\n", ecdh_enc_str);
  if (GNUNET_OK != GNUNET_STRINGS_string_to_data (signature_enc_str,
                                                  strlen (signature_enc_str),
                                                  &ticket->signature,
                                                  sizeof (struct GNUNET_CRYPTO_EcdsaSignature)))
  {
    json_decref (root);
    GNUNET_free (ticket_decoded);
    GNUNET_free (ticket);
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "ECDH signature invalid in data\n");
    return GNUNET_SYSERR;
  }

  enc_data_len = GNUNET_STRINGS_base64_decode (enc_data_str,
                                               strlen (enc_data_str),
                                               &enc_data);


  if (GNUNET_OK != ticket_payload_parse (enc_data,
                                         enc_data_len,
                                         priv_key,
                                         (const struct GNUNET_CRYPTO_EcdhePublicKey*)&ticket->ecdh_pubkey,
                                         &ticket_payload))
  {
    json_decref (root);
    GNUNET_free (enc_data);
    GNUNET_free (ticket_decoded);
    GNUNET_free (ticket);
    return GNUNET_SYSERR;
  }

  ticket->payload = ticket_payload;
  purpose = 
    GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + 
                   sizeof (struct GNUNET_CRYPTO_EcdhePublicKey) + //E
                   enc_data_len); // E_K (code_str)
  purpose->size = 
    htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
           sizeof (struct GNUNET_CRYPTO_EcdhePublicKey) +
           enc_data_len);
  purpose->purpose = htonl(GNUNET_SIGNATURE_PURPOSE_GNUID_TICKET);
  write_ptr = (char*) &purpose[1];
  GNUNET_memcpy (write_ptr, &ticket->ecdh_pubkey, sizeof (struct GNUNET_CRYPTO_EcdhePublicKey));
  write_ptr += sizeof (struct GNUNET_CRYPTO_EcdhePublicKey);
  GNUNET_memcpy (write_ptr, enc_data, enc_data_len);

  if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_GNUID_TICKET,
                                               purpose,
                                               &ticket->signature,
                                               &ticket_payload->identity_key))
  {
    ticket_destroy (ticket);
    GNUNET_free (ticket_decoded);
    json_decref (root);
    GNUNET_free (purpose);
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Error verifying signature for ticket\n");
    return GNUNET_SYSERR;
  }
  *result = ticket;
  GNUNET_free (purpose);

  GNUNET_free (enc_data);
  GNUNET_free (ticket_decoded);
  json_decref (root);
  return GNUNET_OK;

}



/* end of identity_token.c */
