consolidate reclaim attribute lib
[oweals/gnunet.git] / src / reclaim / oidc_helper.c
1 /*
2    This file is part of GNUnet
3    Copyright (C) 2010-2015 GNUnet e.V.
4
5    GNUnet is free software: you can redistribute it and/or modify it
6    under the terms of the GNU Affero General Public License as published
7    by the Free Software Foundation, either version 3 of the License,
8    or (at your option) any later version.
9
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    Affero General Public License for more details.
14
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18    SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file reclaim/oidc_helper.c
23  * @brief helper library for OIDC related functions
24  * @author Martin Schanzenbach
25  */
26 #include "platform.h"
27 #include <inttypes.h>
28 #include <jansson.h>
29 #include "gnunet_util_lib.h"
30 #include "gnunet_reclaim_lib.h"
31 #include "gnunet_reclaim_service.h"
32 #include "gnunet_signatures.h"
33 #include "oidc_helper.h"
34 // #include "benchmark.h"
35 #include <gcrypt.h>
36
37 GNUNET_NETWORK_STRUCT_BEGIN
38
39 /**
40  * The signature used to generate the authorization code
41  */
42 struct OIDC_Parameters
43 {
44   /**
45    * The reclaim ticket
46    */
47   struct GNUNET_RECLAIM_Ticket ticket;
48
49   /**
50    * The nonce
51    */
52   uint32_t nonce GNUNET_PACKED;
53
54   /**
55    * The length of the PKCE code_challenge
56    */
57   uint32_t code_challenge_len GNUNET_PACKED;
58
59   /**
60    * The length of the attributes list
61    */
62   uint32_t attr_list_len GNUNET_PACKED;
63
64   /**
65    * The length of the attestation list
66    */
67   uint32_t attest_list_len GNUNET_PACKED;
68 };
69
70 GNUNET_NETWORK_STRUCT_END
71
72 static char *
73 create_jwt_header (void)
74 {
75   json_t *root;
76   char *json_str;
77
78   root = json_object ();
79   json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE));
80   json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE));
81
82   json_str = json_dumps (root, JSON_INDENT (0) | JSON_COMPACT);
83   json_decref (root);
84   return json_str;
85 }
86
87
88 static void
89 replace_char (char *str, char find, char replace)
90 {
91   char *current_pos = strchr (str, find);
92
93   while (current_pos)
94   {
95     *current_pos = replace;
96     current_pos = strchr (current_pos, find);
97   }
98 }
99
100
101 // RFC4648
102 static void
103 fix_base64 (char *str)
104 {
105   // Replace + with -
106   replace_char (str, '+', '-');
107
108   // Replace / with _
109   replace_char (str, '/', '_');
110 }
111
112
113 /**
114  * Create a JWT from attributes
115  *
116  * @param aud_key the public of the audience
117  * @param sub_key the public key of the subject
118  * @param attrs the attribute list
119  * @param expiration_time the validity of the token
120  * @param secret_key the key used to sign the JWT
121  * @return a new base64-encoded JWT string.
122  */
123 char *
124 OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key,
125                    const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key,
126                    struct GNUNET_RECLAIM_AttributeList *attrs,
127                    struct GNUNET_RECLAIM_AttestationList *attests,
128                    const struct GNUNET_TIME_Relative *expiration_time,
129                    const char *nonce,
130                    const char *secret_key)
131 {
132   struct GNUNET_RECLAIM_AttributeListEntry *le;
133   struct GNUNET_RECLAIM_AttestationListEntry *ale;
134   struct GNUNET_HashCode signature;
135   struct GNUNET_TIME_Absolute exp_time;
136   struct GNUNET_TIME_Absolute time_now;
137   char *audience;
138   char *subject;
139   char *header;
140   char *body_str;
141   char *aggr_names_str;
142   char *aggr_sources_str;
143   char *aggr_sources_jwt_str;
144   char *source_name;
145   char *result;
146   char *header_base64;
147   char *body_base64;
148   char *signature_target;
149   char *signature_base64;
150   char *attr_val_str;
151   char *attest_val_str;
152   json_t *body;
153   json_t *aggr_names;
154   json_t *aggr_sources;
155   json_t *aggr_sources_jwt;
156   int num_attestations = 0;
157   for (le = attrs->list_head; NULL != le; le = le->next)
158   {
159     if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->attestation))
160       num_attestations++;
161   }
162
163   // iat REQUIRED time now
164   time_now = GNUNET_TIME_absolute_get ();
165   // exp REQUIRED time expired from config
166   exp_time = GNUNET_TIME_absolute_add (time_now, *expiration_time);
167   // auth_time only if max_age
168   // nonce only if nonce
169   // OPTIONAL acr,amr,azp
170   subject =
171     GNUNET_STRINGS_data_to_string_alloc (sub_key,
172                                          sizeof(struct
173                                                 GNUNET_CRYPTO_EcdsaPublicKey));
174   audience =
175     GNUNET_STRINGS_data_to_string_alloc (aud_key,
176                                          sizeof(struct
177                                                 GNUNET_CRYPTO_EcdsaPublicKey));
178   header = create_jwt_header ();
179   body = json_object ();
180   aggr_names = json_object ();
181   aggr_sources = json_object ();
182
183   // iss REQUIRED case sensitive server uri with https
184   // The issuer is the local reclaim instance (e.g.
185   // https://reclaim.id/api/openid)
186   json_object_set_new (body, "iss", json_string (SERVER_ADDRESS));
187   // sub REQUIRED public key identity, not exceed 255 ASCII  length
188   json_object_set_new (body, "sub", json_string (subject));
189   // aud REQUIRED public key client_id must be there
190   json_object_set_new (body, "aud", json_string (audience));
191   // iat
192   json_object_set_new (body,
193                        "iat",
194                        json_integer (time_now.abs_value_us / (1000 * 1000)));
195   // exp
196   json_object_set_new (body,
197                        "exp",
198                        json_integer (exp_time.abs_value_us / (1000 * 1000)));
199   // nbf
200   json_object_set_new (body,
201                        "nbf",
202                        json_integer (time_now.abs_value_us / (1000 * 1000)));
203   // nonce
204   if (NULL != nonce)
205     json_object_set_new (body, "nonce", json_string (nonce));
206   attest_val_str = NULL;
207   aggr_names_str = NULL;
208   aggr_sources_str = NULL;
209   aggr_sources_jwt_str = NULL;
210   source_name = NULL;
211   int i = 0;
212   for (ale = attests->list_head; NULL != ale; ale = ale->next)
213   {
214     // New Attestation
215     GNUNET_asprintf (&source_name,
216                      "src%d",
217                      i);
218     aggr_sources_jwt = json_object ();
219     attest_val_str =
220       GNUNET_RECLAIM_attestation_value_to_string (ale->attestation->type,
221                                                   ale->attestation->data,
222                                                   ale->attestation->data_size);
223     json_object_set_new (aggr_sources_jwt, "JWT",
224                          json_string (attest_val_str) );
225     aggr_sources_jwt_str = json_dumps (aggr_sources_jwt, JSON_INDENT (0)
226                                        | JSON_COMPACT);
227     json_object_set_new (aggr_sources, source_name,json_string (
228                            aggr_sources_jwt_str));
229     i++;
230   }
231
232   for (le = attrs->list_head; NULL != le; le = le->next)
233   {
234
235     if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&le->attribute->attestation))
236     {
237
238       attr_val_str =
239         GNUNET_RECLAIM_attribute_value_to_string (le->attribute->type,
240                                                   le->attribute->data,
241                                                   le->attribute->data_size);
242       json_object_set_new (body, le->attribute->name,
243                            json_string (attr_val_str));
244       GNUNET_free (attr_val_str);
245     }
246     else
247     {
248       // Check if attest is there
249       int j = 0;
250       for (ale = attests->list_head; NULL != ale; ale = ale->next)
251       {
252         if (GNUNET_YES ==
253             GNUNET_RECLAIM_id_is_equal (&ale->attestation->id,
254                                         &le->attribute->attestation))
255           break;
256         j++;
257       }
258       GNUNET_assert (NULL != ale);
259       // Attestation is existing, hence take the respective source str
260       GNUNET_asprintf (&source_name,
261                        "src%d",
262                        j);
263       json_object_set_new (aggr_names, le->attribute->data,
264                            json_string (source_name));
265     }
266   }
267
268   if (NULL != attest_val_str)
269     GNUNET_free (attest_val_str);
270   if (NULL != source_name)
271     GNUNET_free (source_name);
272   if (0 != i)
273   {
274     aggr_names_str = json_dumps (aggr_names, JSON_INDENT (0) | JSON_COMPACT);
275     aggr_sources_str = json_dumps (aggr_sources, JSON_INDENT (0)
276                                    | JSON_COMPACT);
277     json_object_set_new (body, "_claim_names", json_string (aggr_names_str));
278     json_object_set_new (body, "_claim_sources", json_string (
279                            aggr_sources_str));
280   }
281
282   json_decref (aggr_names);
283   json_decref (aggr_sources);
284   json_decref (aggr_sources_jwt);
285
286   body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT);
287   json_decref (body);
288   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"ID-Token: %s\n", body_str);
289
290   GNUNET_STRINGS_base64url_encode (header, strlen (header), &header_base64);
291   fix_base64 (header_base64);
292
293   GNUNET_STRINGS_base64url_encode (body_str, strlen (body_str), &body_base64);
294   fix_base64 (body_base64);
295
296   GNUNET_free (subject);
297   GNUNET_free (audience);
298
299   /**
300    * Creating the JWT signature. This might not be
301    * standards compliant, check.
302    */
303   GNUNET_asprintf (&signature_target, "%s.%s", header_base64, body_base64);
304   GNUNET_CRYPTO_hmac_raw (secret_key,
305                           strlen (secret_key),
306                           signature_target,
307                           strlen (signature_target),
308                           &signature);
309   GNUNET_STRINGS_base64url_encode ((const char *) &signature,
310                                    sizeof(struct GNUNET_HashCode),
311                                    &signature_base64);
312   fix_base64 (signature_base64);
313
314   GNUNET_asprintf (&result,
315                    "%s.%s.%s",
316                    header_base64,
317                    body_base64,
318                    signature_base64);
319
320   GNUNET_free (signature_target);
321   GNUNET_free (header);
322   GNUNET_free (body_str);
323   if (NULL != aggr_sources_str)
324     GNUNET_free (aggr_sources_str);
325   if (NULL != aggr_names_str)
326     GNUNET_free (aggr_names_str);
327   if (NULL != aggr_sources_jwt_str)
328     GNUNET_free (aggr_sources_jwt_str);
329   GNUNET_free (signature_base64);
330   GNUNET_free (body_base64);
331   GNUNET_free (header_base64);
332   return result;
333 }
334
335
336 static void
337 derive_aes_key (struct GNUNET_CRYPTO_SymmetricSessionKey *key,
338                 struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
339                 struct GNUNET_HashCode *key_material)
340 {
341   static const char ctx_key[] = "reclaim-aes-ctx-key";
342   static const char ctx_iv[] = "reclaim-aes-ctx-iv";
343
344   GNUNET_CRYPTO_kdf (key,
345                      sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey),
346                      ctx_key,
347                      strlen (ctx_key),
348                      key_material,
349                      sizeof(struct GNUNET_HashCode),
350                      NULL);
351   GNUNET_CRYPTO_kdf (iv,
352                      sizeof(
353                        struct GNUNET_CRYPTO_SymmetricInitializationVector),
354                      ctx_iv,
355                      strlen (ctx_iv),
356                      key_material,
357                      sizeof(struct GNUNET_HashCode),
358                      NULL);
359 }
360
361
362 static void
363 calculate_key_priv (struct GNUNET_CRYPTO_SymmetricSessionKey *key,
364                     struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
365                     const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv,
366                     const struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pub)
367 {
368   struct GNUNET_HashCode key_material;
369
370   GNUNET_CRYPTO_ecdsa_ecdh (ecdsa_priv, ecdh_pub, &key_material);
371   derive_aes_key (key, iv, &key_material);
372 }
373
374
375 static void
376 calculate_key_pub (struct GNUNET_CRYPTO_SymmetricSessionKey *key,
377                    struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
378                    const struct GNUNET_CRYPTO_EcdsaPublicKey *ecdsa_pub,
379                    const struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_priv)
380 {
381   struct GNUNET_HashCode key_material;
382
383   GNUNET_CRYPTO_ecdh_ecdsa (ecdh_priv, ecdsa_pub, &key_material);
384   derive_aes_key (key, iv, &key_material);
385 }
386
387
388 static void
389 decrypt_payload (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv,
390                  const struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pub,
391                  const char *ct,
392                  size_t ct_len,
393                  char *buf)
394 {
395   struct GNUNET_CRYPTO_SymmetricSessionKey key;
396   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
397
398   calculate_key_priv (&key, &iv, ecdsa_priv, ecdh_pub);
399   GNUNET_break (GNUNET_CRYPTO_symmetric_decrypt (ct, ct_len, &key, &iv, buf));
400 }
401
402
403 static void
404 encrypt_payload (const struct GNUNET_CRYPTO_EcdsaPublicKey *ecdsa_pub,
405                  const struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_priv,
406                  const char *payload,
407                  size_t payload_len,
408                  char *buf)
409 {
410   struct GNUNET_CRYPTO_SymmetricSessionKey key;
411   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
412
413   calculate_key_pub (&key, &iv, ecdsa_pub, ecdh_priv);
414   GNUNET_break (
415     GNUNET_CRYPTO_symmetric_encrypt (payload, payload_len, &key, &iv, buf));
416 }
417
418
419 /**
420  * Builds an OIDC authorization code including
421  * a reclaim ticket and nonce
422  *
423  * @param issuer the issuer of the ticket, used to sign the ticket and nonce
424  * @param ticket the ticket to include in the code
425  * @param attrs list of attributes which are shared
426  * @param nonce the nonce to include in the code
427  * @param code_challenge PKCE code challenge
428  * @return a new authorization code (caller must free)
429  */
430 char *
431 OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer,
432                        const struct GNUNET_RECLAIM_Ticket *ticket,
433                        struct GNUNET_RECLAIM_AttributeList *attrs,
434                        struct GNUNET_RECLAIM_AttestationList *attests,
435                        const char *nonce_str,
436                        const char *code_challenge)
437 {
438   struct OIDC_Parameters params;
439   char *code_payload;
440   char *payload;
441   char *tmp;
442   char *code_str;
443   char *buf_ptr = NULL;
444   size_t payload_len;
445   size_t code_payload_len;
446   size_t attr_list_len = 0;
447   size_t attests_list_len = 0;
448   size_t code_challenge_len = 0;
449   uint32_t nonce;
450   uint32_t nonce_tmp;
451   struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
452   struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_priv;
453   struct GNUNET_CRYPTO_EcdhePublicKey ecdh_pub;
454
455   /** PLAINTEXT **/
456   // Assign ticket
457   memset (&params, 0, sizeof(params));
458   params.ticket = *ticket;
459   // Assign nonce
460   nonce = 0;
461   payload_len = sizeof(struct OIDC_Parameters);
462   if ((NULL != nonce_str) && (strcmp ("", nonce_str) != 0))
463   {
464     if ((1 != sscanf (nonce_str, "%u", &nonce)) || (nonce > UINT32_MAX))
465     {
466       GNUNET_break (0);
467       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid nonce %s\n", nonce_str);
468       return NULL;
469     }
470     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471                 "Got nonce: %u from %s\n",
472                 nonce,
473                 nonce_str);
474   }
475   nonce_tmp = htonl (nonce);
476   params.nonce = nonce_tmp;
477   // Assign code challenge
478   if (NULL != code_challenge)
479     code_challenge_len = strlen (code_challenge);
480   payload_len += code_challenge_len;
481   params.code_challenge_len = htonl (code_challenge_len);
482   // Assign attributes
483   if (NULL != attrs)
484   {
485     // Get length
486     attr_list_len = GNUNET_RECLAIM_attribute_list_serialize_get_size (attrs);
487     params.attr_list_len = htonl (attr_list_len);
488     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489                 "Length of serialized attributes: %lu\n",
490                 attr_list_len);
491     // Get serialized attributes
492     payload_len += attr_list_len;
493   }
494   if (NULL != attests)
495   {
496     // Get length
497     attests_list_len =
498       GNUNET_RECLAIM_attestation_list_serialize_get_size (attests);
499     params.attest_list_len = htonl (attests_list_len);
500     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
501                 "Length of serialized attestations: %lu\n",
502                 attests_list_len);
503     // Get serialized attributes
504     payload_len += attests_list_len;
505   }
506
507   // Get plaintext length
508   payload = GNUNET_malloc (payload_len);
509   memcpy (payload, &params, sizeof(params));
510   tmp = payload + sizeof(params);
511   if (0 < code_challenge_len)
512   {
513     memcpy (tmp, code_challenge, code_challenge_len);
514     tmp += code_challenge_len;
515   }
516   if (0 < attr_list_len)
517     GNUNET_RECLAIM_attribute_list_serialize (attrs, tmp);
518   if (0 < attests_list_len)
519     GNUNET_RECLAIM_attestation_list_serialize (attests, tmp);
520
521   /** END **/
522
523   /** ENCRYPT **/
524   // Get length
525   code_payload_len = sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
526                      + sizeof(struct GNUNET_CRYPTO_EcdhePublicKey)
527                      + payload_len + sizeof(struct
528                                             GNUNET_CRYPTO_EcdsaSignature);
529   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
530               "Length of data to encode: %lu\n",
531               code_payload_len);
532
533   // Generate ECDH key
534   ecdh_priv = GNUNET_CRYPTO_ecdhe_key_create ();
535   GNUNET_CRYPTO_ecdhe_key_get_public (ecdh_priv, &ecdh_pub);
536   // Initialize code payload
537   code_payload = GNUNET_malloc (code_payload_len);
538   GNUNET_assert (NULL != code_payload);
539   purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
540   purpose->size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
541                          + sizeof(ecdh_pub) + payload_len);
542   purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN);
543   // Store pubkey
544   buf_ptr = (char *) &purpose[1];
545   memcpy (buf_ptr, &ecdh_pub, sizeof(ecdh_pub));
546   buf_ptr += sizeof(ecdh_pub);
547   // Encrypt plaintext and store
548   encrypt_payload (&ticket->audience, ecdh_priv, payload, payload_len, buf_ptr);
549   GNUNET_free (ecdh_priv);
550   GNUNET_free (payload);
551   buf_ptr += payload_len;
552   // Sign and store signature
553   if (GNUNET_SYSERR ==
554       GNUNET_CRYPTO_ecdsa_sign (issuer,
555                                 purpose,
556                                 (struct GNUNET_CRYPTO_EcdsaSignature *)
557                                 buf_ptr))
558   {
559     GNUNET_break (0);
560     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to sign code\n");
561     GNUNET_free (code_payload);
562     return NULL;
563   }
564   GNUNET_STRINGS_base64url_encode (code_payload, code_payload_len, &code_str);
565   GNUNET_free (code_payload);
566   return code_str;
567 }
568
569
570 /**
571  * Parse reclaim ticket and nonce from
572  * authorization code.
573  * This also verifies the signature in the code.
574  *
575  * @param audience the expected audience of the code
576  * @param code the string representation of the code
577  * @param code_verfier PKCE code verifier
578  * @param ticket where to store the ticket
579  * @param attrs the attributes in the code
580  * @param nonce where to store the nonce
581  * @return GNUNET_OK if successful, else GNUNET_SYSERR
582  */
583 int
584 OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv,
585                        const char *code,
586                        const char *code_verifier,
587                        struct GNUNET_RECLAIM_Ticket *ticket,
588                        struct GNUNET_RECLAIM_AttributeList **attrs,
589                        struct GNUNET_RECLAIM_AttestationList **attests,
590                        char **nonce_str)
591 {
592   char *code_payload;
593   char *ptr;
594   char *plaintext;
595   char *attrs_ser;
596   char *expected_code_challenge;
597   char *code_challenge;
598   char *code_verifier_hash;
599   struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
600   struct GNUNET_CRYPTO_EcdsaSignature *signature;
601   struct GNUNET_CRYPTO_EcdsaPublicKey ecdsa_pub;
602   struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pub;
603   uint32_t code_challenge_len;
604   uint32_t attrs_ser_len;
605   size_t plaintext_len;
606   size_t code_payload_len;
607   uint32_t nonce = 0;
608   struct OIDC_Parameters *params;
609
610   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to decode `%s'\n", code);
611   code_payload = NULL;
612   code_payload_len =
613     GNUNET_STRINGS_base64url_decode (code, strlen (code),
614                                      (void **) &code_payload);
615   if (code_payload_len < sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
616       + sizeof(struct GNUNET_CRYPTO_EcdhePublicKey)
617       + sizeof(struct OIDC_Parameters)
618       + sizeof(struct GNUNET_CRYPTO_EcdsaSignature))
619   {
620     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Authorization code malformed\n");
621     GNUNET_free_non_null (code_payload);
622     return GNUNET_SYSERR;
623   }
624
625   purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
626   plaintext_len = code_payload_len;
627   plaintext_len -= sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose);
628   ptr = (char *) &purpose[1];
629   // Public ECDH key
630   ecdh_pub = (struct GNUNET_CRYPTO_EcdhePublicKey *) ptr;
631   ptr += sizeof(struct GNUNET_CRYPTO_EcdhePublicKey);
632   plaintext_len -= sizeof(struct GNUNET_CRYPTO_EcdhePublicKey);
633
634   // Decrypt ciphertext
635   plaintext_len -= sizeof(struct GNUNET_CRYPTO_EcdsaSignature);
636   plaintext = GNUNET_malloc (plaintext_len);
637   decrypt_payload (ecdsa_priv, ecdh_pub, ptr, plaintext_len, plaintext);
638   // ptr = plaintext;
639   ptr += plaintext_len;
640   signature = (struct GNUNET_CRYPTO_EcdsaSignature *) ptr;
641   params = (struct OIDC_Parameters *) plaintext;
642
643   // cmp code_challenge code_verifier
644   code_challenge_len = ntohl (params->code_challenge_len);
645   if (0 != code_challenge_len) /* Only check if this code requires a CV */
646   {
647     if (NULL == code_verifier)
648     {
649       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
650                   "Expected code verifier!\n");
651       GNUNET_free_non_null (code_payload);
652       return GNUNET_SYSERR;
653     }
654     code_verifier_hash = GNUNET_malloc (256 / 8);
655     // hash code verifier
656     gcry_md_hash_buffer (GCRY_MD_SHA256,
657                          code_verifier_hash,
658                          code_verifier,
659                          strlen (code_verifier));
660     // encode code verifier
661     GNUNET_STRINGS_base64url_encode (code_verifier_hash, 256 / 8, &expected_code_challenge);
662     code_challenge = (char *) &params[1];
663     GNUNET_free (code_verifier_hash);
664     if ((strlen (expected_code_challenge) != code_challenge_len) ||
665         (0 !=
666          strncmp (expected_code_challenge, code_challenge, code_challenge_len)))
667     {
668       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
669                   "Invalid code verifier! Expected: %s, Got: %.*s\n",
670                   expected_code_challenge,
671                   code_challenge_len,
672                   code_challenge);
673       GNUNET_free_non_null (code_payload);
674       GNUNET_free (expected_code_challenge);
675       return GNUNET_SYSERR;
676     }
677     GNUNET_free (expected_code_challenge);
678   }
679   // Ticket
680   memcpy (ticket, &params->ticket, sizeof(params->ticket));
681   // Nonce
682   nonce = ntohl (params->nonce);  // ntohl (*((uint32_t *) ptr));
683   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got nonce: %u\n", nonce);
684   // Signature
685   GNUNET_CRYPTO_ecdsa_key_get_public (ecdsa_priv, &ecdsa_pub);
686   if (0 != GNUNET_memcmp (&ecdsa_pub, &ticket->audience))
687   {
688     GNUNET_free (code_payload);
689     GNUNET_free (plaintext);
690     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
691                 "Audience in ticket does not match client!\n");
692     return GNUNET_SYSERR;
693   }
694   if (GNUNET_OK !=
695       GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN,
696                                   purpose,
697                                   signature,
698                                   &ticket->identity))
699   {
700     GNUNET_free (code_payload);
701     GNUNET_free (plaintext);
702     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signature of AuthZ code invalid!\n");
703     return GNUNET_SYSERR;
704   }
705   // Attributes
706   attrs_ser = ((char *) &params[1]) + code_challenge_len;
707   attrs_ser_len = ntohl (params->attr_list_len);
708   *attrs = GNUNET_RECLAIM_attribute_list_deserialize (attrs_ser, attrs_ser_len);
709
710   *nonce_str = NULL;
711   if (nonce != 0)
712     GNUNET_asprintf (nonce_str, "%u", nonce);
713   GNUNET_free (code_payload);
714   GNUNET_free (plaintext);
715   return GNUNET_OK;
716 }
717
718
719 /**
720  * Build a token response for a token request
721  * TODO: Maybe we should add the scope here?
722  *
723  * @param access_token the access token to include
724  * @param id_token the id_token to include
725  * @param expiration_time the expiration time of the token(s)
726  * @param token_response where to store the response
727  */
728 void
729 OIDC_build_token_response (const char *access_token,
730                            const char *id_token,
731                            const struct GNUNET_TIME_Relative *expiration_time,
732                            char **token_response)
733 {
734   json_t *root_json;
735
736   root_json = json_object ();
737
738   GNUNET_assert (NULL != access_token);
739   GNUNET_assert (NULL != id_token);
740   GNUNET_assert (NULL != expiration_time);
741   json_object_set_new (root_json, "access_token", json_string (access_token));
742   json_object_set_new (root_json, "token_type", json_string ("Bearer"));
743   json_object_set_new (root_json,
744                        "expires_in",
745                        json_integer (expiration_time->rel_value_us
746                                      / (1000 * 1000)));
747   json_object_set_new (root_json, "id_token", json_string (id_token));
748   *token_response = json_dumps (root_json, JSON_INDENT (0) | JSON_COMPACT);
749   json_decref (root_json);
750 }
751
752
753 /**
754  * Generate a new access token
755  */
756 char *
757 OIDC_access_token_new ()
758 {
759   char *access_token;
760   uint64_t random_number;
761
762   random_number =
763     GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
764   GNUNET_STRINGS_base64_encode (&random_number,
765                                 sizeof(uint64_t),
766                                 &access_token);
767   return access_token;
768 }