REST/NAMESTORE: rework API
[oweals/gnunet.git] / src / reclaim / oidc_helper.c
index 5bcf932f566bafcd71833b921499e93a51268006..167ee1729fc1600ffdd07877344f974950ea010a 100644 (file)
@@ -111,12 +111,14 @@ OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key,
   // auth_time only if max_age
   // nonce only if nonce
   // OPTIONAL acr,amr,azp
-  subject = GNUNET_STRINGS_data_to_string_alloc (
-    sub_key,
-    sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
-  audience = GNUNET_STRINGS_data_to_string_alloc (
-    aud_key,
-    sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+  subject =
+    GNUNET_STRINGS_data_to_string_alloc (sub_key,
+                                         sizeof (struct
+                                                 GNUNET_CRYPTO_EcdsaPublicKey));
+  audience =
+    GNUNET_STRINGS_data_to_string_alloc (aud_key,
+                                         sizeof (struct
+                                                 GNUNET_CRYPTO_EcdsaPublicKey));
   header = create_jwt_header ();
   body = json_object ();
 
@@ -290,6 +292,86 @@ base64_encode (const char *data, size_t data_size)
 }
 
 
+static void
+derive_aes_key (struct GNUNET_CRYPTO_SymmetricSessionKey *key,
+                struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
+                struct GNUNET_HashCode *key_material)
+{
+  static const char ctx_key[] = "reclaim-aes-ctx-key";
+  static const char ctx_iv[] = "reclaim-aes-ctx-iv";
+  GNUNET_CRYPTO_kdf (key,
+                     sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey),
+                     ctx_key,
+                     strlen (ctx_key),
+                     key_material,
+                     sizeof (struct GNUNET_HashCode),
+                     NULL);
+  GNUNET_CRYPTO_kdf (iv,
+                     sizeof (
+                       struct GNUNET_CRYPTO_SymmetricInitializationVector),
+                     ctx_iv,
+                     strlen (ctx_iv),
+                     key_material,
+                     sizeof (struct GNUNET_HashCode),
+                     NULL);
+}
+
+
+static void
+calculate_key_priv (struct GNUNET_CRYPTO_SymmetricSessionKey *key,
+                    struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
+                    const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv,
+                    const struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pub)
+{
+  struct GNUNET_HashCode key_material;
+  GNUNET_CRYPTO_ecdsa_ecdh (ecdsa_priv, ecdh_pub, &key_material);
+  derive_aes_key (key, iv, &key_material);
+}
+
+
+static void
+calculate_key_pub (struct GNUNET_CRYPTO_SymmetricSessionKey *key,
+                   struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
+                   const struct GNUNET_CRYPTO_EcdsaPublicKey *ecdsa_pub,
+                   const struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_priv)
+{
+  struct GNUNET_HashCode key_material;
+  GNUNET_CRYPTO_ecdh_ecdsa (ecdh_priv, ecdsa_pub, &key_material);
+  derive_aes_key (key, iv, &key_material);
+}
+
+
+static void
+decrypt_payload (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv,
+                 const struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pub,
+                 const char *ct,
+                 size_t ct_len,
+                 char *buf)
+{
+  struct GNUNET_CRYPTO_SymmetricSessionKey key;
+  struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+
+  calculate_key_priv (&key, &iv, ecdsa_priv, ecdh_pub);
+  GNUNET_break (GNUNET_CRYPTO_symmetric_decrypt (ct, ct_len, &key, &iv, buf));
+}
+
+
+static void
+encrypt_payload (const struct GNUNET_CRYPTO_EcdsaPublicKey *ecdsa_pub,
+                 const struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_priv,
+                 const char *payload,
+                 size_t payload_len,
+                 char *buf)
+{
+  struct GNUNET_CRYPTO_SymmetricSessionKey key;
+  struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+
+  calculate_key_pub (&key, &iv, ecdsa_pub, ecdh_priv);
+  GNUNET_break (
+    GNUNET_CRYPTO_symmetric_encrypt (payload, payload_len, &key, &iv, buf));
+}
+
+
 /**
  * Builds an OIDC authorization code including
  * a reclaim ticket and nonce
@@ -307,70 +389,108 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer,
                        const char *nonce_str)
 {
   char *code_payload;
+  char *plaintext;
   char *attrs_ser;
   char *code_str;
   char *buf_ptr;
   size_t signature_payload_len;
   size_t attr_list_len;
   size_t code_payload_len;
-  unsigned int nonce;
-  unsigned int nonce_tmp;
-  struct GNUNET_CRYPTO_EcdsaSignature signature;
+  uint32_t nonce;
+  uint32_t nonce_tmp;
   struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
+  struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_priv;
+  struct GNUNET_CRYPTO_EcdhePublicKey ecdh_pub;
 
   attrs_ser = NULL;
   signature_payload_len =
-    sizeof (struct GNUNET_RECLAIM_Ticket) + sizeof (unsigned int);
+    sizeof (struct GNUNET_RECLAIM_Ticket) + sizeof (uint32_t);
   if (NULL != attrs)
   {
     attr_list_len = GNUNET_RECLAIM_ATTRIBUTE_list_serialize_get_size (attrs);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Length of serialized attributes: %lu\n",
+                attr_list_len);
     signature_payload_len += attr_list_len;
     attrs_ser = GNUNET_malloc (attr_list_len);
     GNUNET_RECLAIM_ATTRIBUTE_list_serialize (attrs, attrs_ser);
   }
   code_payload_len = sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
-                     signature_payload_len + sizeof (signature);
-  code_payload = GNUNET_malloc (code_payload_len);
-  purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
-  purpose->size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
-                         signature_payload_len);
-  purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN);
+                     sizeof (struct GNUNET_CRYPTO_EcdhePublicKey) +
+                     signature_payload_len +
+                     sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Length of data to encode: %lu\n",
+              code_payload_len);
+  plaintext = GNUNET_malloc (signature_payload_len);
   // First, copy ticket
-  buf_ptr = (char *) &purpose[1];
+  buf_ptr = plaintext;
   memcpy (buf_ptr, ticket, sizeof (struct GNUNET_RECLAIM_Ticket));
   buf_ptr += sizeof (struct GNUNET_RECLAIM_Ticket);
   // Then copy nonce
   nonce = 0;
   if (NULL != nonce_str)
   {
-    if ((1 != SSCANF (nonce_str, "%u", &nonce)) || (nonce > UINT16_MAX))
+    if ((1 != SSCANF (nonce_str, "%u", &nonce)) || (nonce > UINT32_MAX))
     {
       GNUNET_break (0);
-      GNUNET_free (code_payload);
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid nonce %s\n", nonce_str);
+      GNUNET_free (plaintext);
       GNUNET_free_non_null (attrs_ser);
       return NULL;
     }
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Got nonce: %u from %s\n",
+                nonce,
+                nonce_str);
   }
-  nonce_tmp = htons (nonce);
-  memcpy (buf_ptr, &nonce_tmp, sizeof (unsigned int));
-  buf_ptr += sizeof (unsigned int);
+  nonce_tmp = htonl (nonce);
+  memcpy (buf_ptr, &nonce_tmp, sizeof (uint32_t));
+  buf_ptr += sizeof (uint32_t);
   // Finally, attributes
   if (NULL != attrs_ser)
   {
     memcpy (buf_ptr, attrs_ser, attr_list_len);
     buf_ptr += attr_list_len;
+    GNUNET_free (attrs_ser);
   }
-  if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdsa_sign (issuer, purpose, &signature))
+  // Generate ECDH key
+  ecdh_priv = GNUNET_CRYPTO_ecdhe_key_create ();
+  GNUNET_CRYPTO_ecdhe_key_get_public (ecdh_priv, &ecdh_pub);
+  // Initialize code payload
+  code_payload = GNUNET_malloc (code_payload_len);
+  GNUNET_assert (NULL != code_payload);
+  purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
+  purpose->size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
+                         sizeof (ecdh_pub) + signature_payload_len);
+  purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN);
+  // Store pubkey
+  buf_ptr = (char *) &purpose[1];
+  memcpy (buf_ptr, &ecdh_pub, sizeof (ecdh_pub));
+  buf_ptr += sizeof (ecdh_pub);
+  // Encrypt plaintext and store
+  encrypt_payload (&ticket->audience,
+                   ecdh_priv,
+                   plaintext,
+                   signature_payload_len,
+                   buf_ptr);
+  GNUNET_free (ecdh_priv);
+  GNUNET_free (plaintext);
+  buf_ptr += signature_payload_len;
+  // Sign and store signature
+  if (GNUNET_SYSERR ==
+      GNUNET_CRYPTO_ecdsa_sign (issuer,
+                                purpose,
+                                (struct GNUNET_CRYPTO_EcdsaSignature *)
+                                  buf_ptr))
   {
     GNUNET_break (0);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to sign code\n");
     GNUNET_free (code_payload);
-    GNUNET_free_non_null (attrs_ser);
     return NULL;
   }
-  memcpy (buf_ptr, &signature, sizeof (signature));
-  code_str = base64_encode ((const char *) &code_payload, code_payload_len);
+  code_str = base64_encode (code_payload, code_payload_len);
   GNUNET_free (code_payload);
-  GNUNET_free_non_null (attrs_ser);
   return code_str;
 }
 
@@ -388,43 +508,77 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer,
  * @return GNUNET_OK if successful, else GNUNET_SYSERR
  */
 int
-OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience,
+OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv,
                        const char *code,
                        struct GNUNET_RECLAIM_Ticket *ticket,
                        struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList **attrs,
                        char **nonce_str)
 {
   char *code_payload;
-  char *attrs_ser;
   char *ptr;
+  char *plaintext;
   struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
   struct GNUNET_CRYPTO_EcdsaSignature *signature;
+  struct GNUNET_CRYPTO_EcdsaPublicKey ecdsa_pub;
+  struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pub;
   size_t code_payload_len;
   size_t attrs_ser_len;
   size_t signature_offset;
-  unsigned int nonce;
+  size_t plaintext_len;
+  uint32_t nonce = 0;
 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to decode `%s'\n", code);
   code_payload = NULL;
   code_payload_len =
     GNUNET_STRINGS_base64_decode (code, strlen (code), (void **) &code_payload);
+  if (code_payload_len < sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
+                           sizeof (struct GNUNET_CRYPTO_EcdhePublicKey) +
+                           sizeof (struct GNUNET_RECLAIM_Ticket) +
+                           sizeof (uint32_t) +
+                           sizeof (struct GNUNET_CRYPTO_EcdsaSignature))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Authorization code malformed\n");
+    GNUNET_free_non_null (code_payload);
+    return GNUNET_SYSERR;
+  }
+
   purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
   attrs_ser_len = code_payload_len;
   attrs_ser_len -= sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose);
-  *ticket = *((struct GNUNET_RECLAIM_Ticket *) &purpose[1]);
+  ptr = (char *) &purpose[1];
+  // Public ECDH key
+  ecdh_pub = (struct GNUNET_CRYPTO_EcdhePublicKey *) ptr;
+  ptr += sizeof (struct GNUNET_CRYPTO_EcdhePublicKey);
+  attrs_ser_len -= sizeof (struct GNUNET_CRYPTO_EcdhePublicKey);
+
+  // Decrypt ciphertext
+  plaintext_len = attrs_ser_len - sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
+  plaintext = GNUNET_malloc (plaintext_len);
+  decrypt_payload (ecdsa_priv, ecdh_pub, ptr, plaintext_len, plaintext);
+  ptr = plaintext;
+  // Ticket
+  *ticket = *((struct GNUNET_RECLAIM_Ticket *) ptr);
   attrs_ser_len -= sizeof (struct GNUNET_RECLAIM_Ticket);
-  nonce = ntohs (((unsigned int *) &ticket[1]));
-  attrs_ser_len -= sizeof (unsigned int);
-  ptr = code_payload;
+  ptr += sizeof (struct GNUNET_RECLAIM_Ticket);
+  // Nonce
+  nonce = ntohl (*((uint32_t *) ptr));
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got nonce: %u\n", nonce);
+  attrs_ser_len -= sizeof (uint32_t);
+  ptr += sizeof (uint32_t);
+  // Attributes
+  attrs_ser_len -= sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
+  *attrs = GNUNET_RECLAIM_ATTRIBUTE_list_deserialize (ptr, attrs_ser_len);
+  // Signature
   signature_offset =
     code_payload_len - sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
-  signature = (struct GNUNET_CRYPTO_EcdsaSignature *) &ptr[signature_offset];
-  attrs_ser_len -= sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
-  attrs_ser = ((char *) &ticket[1]) + sizeof (unsigned int);
-  *attrs = GNUNET_RECLAIM_ATTRIBUTE_list_deserialize (attrs_ser, attrs_ser_len);
-  if (0 != GNUNET_memcmp (audience, &ticket->audience))
+  signature =
+    (struct GNUNET_CRYPTO_EcdsaSignature *) &code_payload[signature_offset];
+  GNUNET_CRYPTO_ecdsa_key_get_public (ecdsa_priv, &ecdsa_pub);
+  if (0 != GNUNET_memcmp (&ecdsa_pub, &ticket->audience))
   {
     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (*attrs);
     GNUNET_free (code_payload);
+    GNUNET_free (plaintext);
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Audience in ticket does not match client!\n");
     return GNUNET_SYSERR;
@@ -437,12 +591,15 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience,
   {
     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (*attrs);
     GNUNET_free (code_payload);
+    GNUNET_free (plaintext);
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signature of AuthZ code invalid!\n");
     return GNUNET_SYSERR;
   }
   *nonce_str = NULL;
   if (nonce != 0)
     GNUNET_asprintf (nonce_str, "%u", nonce);
+  GNUNET_free (code_payload);
+  GNUNET_free (plaintext);
   return GNUNET_OK;
 }
 
@@ -471,10 +628,10 @@ OIDC_build_token_response (const char *access_token,
   GNUNET_assert (NULL != expiration_time);
   json_object_set_new (root_json, "access_token", json_string (access_token));
   json_object_set_new (root_json, "token_type", json_string ("Bearer"));
-  json_object_set_new (
-    root_json,
-    "expires_in",
-    json_integer (expiration_time->rel_value_us / (1000 * 1000)));
+  json_object_set_new (root_json,
+                       "expires_in",
+                       json_integer (expiration_time->rel_value_us /
+                                     (1000 * 1000)));
   json_object_set_new (root_json, "id_token", json_string (id_token));
   *token_response = json_dumps (root_json, JSON_INDENT (0) | JSON_COMPACT);
   json_decref (root_json);
@@ -486,15 +643,13 @@ OIDC_build_token_response (const char *access_token,
 char *
 OIDC_access_token_new ()
 {
-  char *access_token_number;
   char *access_token;
   uint64_t random_number;
 
   random_number =
     GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
-  GNUNET_asprintf (&access_token_number, "%" PRIu64, random_number);
-  GNUNET_STRINGS_base64_encode (access_token_number,
-                                strlen (access_token_number),
+  GNUNET_STRINGS_base64_encode (&random_number,
+                                sizeof (uint64_t),
                                 &access_token);
   return access_token;
 }