use NULL value in load_path_suffix to NOT load any files
[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_attribute_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 GNUNET_NETWORK_STRUCT_END
66
67 static char *
68 create_jwt_header (void)
69 {
70   json_t *root;
71   char *json_str;
72
73   root = json_object ();
74   json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE));
75   json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE));
76
77   json_str = json_dumps (root, JSON_INDENT (0) | JSON_COMPACT);
78   json_decref (root);
79   return json_str;
80 }
81
82
83 static void
84 replace_char (char *str, char find, char replace)
85 {
86   char *current_pos = strchr (str, find);
87
88   while (current_pos)
89   {
90     *current_pos = replace;
91     current_pos = strchr (current_pos, find);
92   }
93 }
94
95
96 // RFC4648
97 static void
98 fix_base64 (char *str)
99 {
100   // Replace + with -
101   replace_char (str, '+', '-');
102
103   // Replace / with _
104   replace_char (str, '/', '_');
105 }
106
107
108 /**
109  * Create a JWT from attributes
110  *
111  * @param aud_key the public of the audience
112  * @param sub_key the public key of the subject
113  * @param attrs the attribute list
114  * @param expiration_time the validity of the token
115  * @param secret_key the key used to sign the JWT
116  * @return a new base64-encoded JWT string.
117  */
118 char *
119 OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key,
120                    const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key,
121                    struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs,
122                    const struct GNUNET_TIME_Relative *expiration_time,
123                    const char *nonce,
124                    const char *secret_key)
125 {
126   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
127   struct GNUNET_HashCode signature;
128   struct GNUNET_TIME_Absolute exp_time;
129   struct GNUNET_TIME_Absolute time_now;
130   char *audience;
131   char *subject;
132   char *header;
133   char *body_str;
134   char *aggr_names_str;
135   char *aggr_sources_str;
136   char *aggr_sources_jwt_str;
137   char *source_name;
138   char *result;
139   char *header_base64;
140   char *body_base64;
141   char *signature_target;
142   char *signature_base64;
143   char *attr_val_str;
144   char *attest_val_str;
145   json_t *body;
146   json_t *aggr_names;
147   json_t *aggr_sources;
148   json_t *aggr_sources_jwt;
149   uint64_t attest_arr[GNUNET_RECLAIM_ATTRIBUTE_list_count_attest (attrs)];
150
151   // iat REQUIRED time now
152   time_now = GNUNET_TIME_absolute_get ();
153   // exp REQUIRED time expired from config
154   exp_time = GNUNET_TIME_absolute_add (time_now, *expiration_time);
155   // auth_time only if max_age
156   // nonce only if nonce
157   // OPTIONAL acr,amr,azp
158   subject =
159     GNUNET_STRINGS_data_to_string_alloc (sub_key,
160                                          sizeof(struct
161                                                 GNUNET_CRYPTO_EcdsaPublicKey));
162   audience =
163     GNUNET_STRINGS_data_to_string_alloc (aud_key,
164                                          sizeof(struct
165                                                 GNUNET_CRYPTO_EcdsaPublicKey));
166   header = create_jwt_header ();
167   body = json_object ();
168   aggr_names = json_object ();
169   aggr_sources = json_object ();
170
171   // iss REQUIRED case sensitive server uri with https
172   // The issuer is the local reclaim instance (e.g.
173   // https://reclaim.id/api/openid)
174   json_object_set_new (body, "iss", json_string (SERVER_ADDRESS));
175   // sub REQUIRED public key identity, not exceed 255 ASCII  length
176   json_object_set_new (body, "sub", json_string (subject));
177   // aud REQUIRED public key client_id must be there
178   json_object_set_new (body, "aud", json_string (audience));
179   // iat
180   json_object_set_new (body,
181                        "iat",
182                        json_integer (time_now.abs_value_us / (1000 * 1000)));
183   // exp
184   json_object_set_new (body,
185                        "exp",
186                        json_integer (exp_time.abs_value_us / (1000 * 1000)));
187   // nbf
188   json_object_set_new (body,
189                        "nbf",
190                        json_integer (time_now.abs_value_us / (1000 * 1000)));
191   // nonce
192   if (NULL != nonce)
193     json_object_set_new (body, "nonce", json_string (nonce));
194   int i = 0;
195   attest_val_str = NULL;
196   aggr_names_str = NULL;
197   aggr_sources_str = NULL;
198   aggr_sources_jwt_str = NULL;
199   source_name = NULL;
200   for (le = attrs->list_head; NULL != le; le = le->next)
201   {
202
203     if (le->claim != NULL)
204     {
205
206       attr_val_str =
207         GNUNET_RECLAIM_ATTRIBUTE_value_to_string (le->claim->type,
208                                                   le->claim->data,
209                                                   le->claim->data_size);
210       json_object_set_new (body, le->claim->name, json_string (attr_val_str));
211       GNUNET_free (attr_val_str);
212     }
213     else if (NULL != le->reference)
214     {
215       // Check if attest is there
216       int j = 0;
217       while (j<i)
218       {
219         if (attest_arr[j] == le->reference->id_attest)
220           break;
221         j++;
222       }
223       if (j==i)
224       {
225         // Attest not yet existent. Append to the end of the list
226         GNUNET_CONTAINER_DLL_remove (attrs->list_head, attrs->list_tail, le);
227         GNUNET_CONTAINER_DLL_insert_tail (attrs->list_head, attrs->list_tail,
228                                           le);
229         continue;
230       }
231       else
232       {
233         // Attestation is existing, hence take the respective source str
234         GNUNET_asprintf (&source_name,
235                          "src%d",
236                          j);
237         json_object_set_new (aggr_names, le->reference->name, json_string (
238                                source_name));
239       }
240
241     }
242     else if (NULL != le->attest)
243     {
244       // We assume that at max 99 different attestations
245       int j = 0;
246       while (j<i)
247       {
248         if (attest_arr[j] == le->attest->id)
249           break;
250         j++;
251       }
252       if (j==i)
253       {
254         // New Attestation
255         attest_arr[i] = le->attest->id;
256         GNUNET_asprintf (&source_name,
257                          "src%d",
258                          i);
259         aggr_sources_jwt = json_object ();
260         attest_val_str = GNUNET_RECLAIM_ATTESTATION_value_to_string (
261           le->attest->type, le->attest->data, le->attest->data_size);
262         json_object_set_new (aggr_sources_jwt, "JWT",json_string (
263                                attest_val_str) );
264         aggr_sources_jwt_str = json_dumps (aggr_sources_jwt, JSON_INDENT (0)
265                                            | JSON_COMPACT);
266         json_object_set_new (aggr_sources, source_name,json_string (
267                                aggr_sources_jwt_str));
268         i++;
269       }
270       else
271       {
272         // Attestation already existent. Ignore
273         continue;
274       }
275
276     }
277   }
278   if (NULL != attest_val_str)
279     GNUNET_free (attest_val_str);
280   if (NULL != source_name)
281     GNUNET_free (source_name);
282   if (0!=i)
283   {
284     aggr_names_str = json_dumps (aggr_names, JSON_INDENT (0) | JSON_COMPACT);
285     aggr_sources_str = json_dumps (aggr_sources, JSON_INDENT (0)
286                                    | JSON_COMPACT);
287     json_object_set_new (body, "_claim_names", json_string (aggr_names_str));
288     json_object_set_new (body, "_claim_sources", json_string (
289                            aggr_sources_str));
290   }
291
292   json_decref (aggr_names);
293   json_decref (aggr_sources);
294   json_decref (aggr_sources_jwt);
295
296   body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT);
297   json_decref (body);
298   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"ID-Token: %s\n", body_str);
299
300   GNUNET_STRINGS_base64_encode (header, strlen (header), &header_base64);
301   fix_base64 (header_base64);
302
303   GNUNET_STRINGS_base64_encode (body_str, strlen (body_str), &body_base64);
304   fix_base64 (body_base64);
305
306   GNUNET_free (subject);
307   GNUNET_free (audience);
308
309   /**
310    * Creating the JWT signature. This might not be
311    * standards compliant, check.
312    */
313   GNUNET_asprintf (&signature_target, "%s.%s", header_base64, body_base64);
314   GNUNET_CRYPTO_hmac_raw (secret_key,
315                           strlen (secret_key),
316                           signature_target,
317                           strlen (signature_target),
318                           &signature);
319   GNUNET_STRINGS_base64_encode ((const char *) &signature,
320                                 sizeof(struct GNUNET_HashCode),
321                                 &signature_base64);
322   fix_base64 (signature_base64);
323
324   GNUNET_asprintf (&result,
325                    "%s.%s.%s",
326                    header_base64,
327                    body_base64,
328                    signature_base64);
329
330   GNUNET_free (signature_target);
331   GNUNET_free (header);
332   GNUNET_free (body_str);
333   if (NULL != aggr_sources_str)
334     GNUNET_free (aggr_sources_str);
335   if (NULL != aggr_names_str)
336     GNUNET_free (aggr_names_str);
337   if (NULL != aggr_sources_jwt_str)
338     GNUNET_free (aggr_sources_jwt_str);
339   GNUNET_free (signature_base64);
340   GNUNET_free (body_base64);
341   GNUNET_free (header_base64);
342   return result;
343 }
344
345
346 /* Converts a hex character to its integer value */
347 static char
348 from_hex (char ch)
349 {
350   return isdigit (ch) ? ch - '0' : tolower (ch) - 'a' + 10;
351 }
352
353
354 /* Converts an integer value to its hex character*/
355 static char
356 to_hex (char code)
357 {
358   static char hex[] = "0123456789abcdef";
359
360   return hex[code & 15];
361 }
362
363
364 /* Returns a url-encoded version of str */
365 /* IMPORTANT: be sure to free() the returned string after use */
366 static char *
367 url_encode (const char *str)
368 {
369   char *pstr = (char *) str;
370   char *buf = GNUNET_malloc (strlen (str) * 3 + 1);
371   char *pbuf = buf;
372
373   while (*pstr)
374   {
375     if (isalnum (*pstr) || (*pstr == '-') || (*pstr == '_') || (*pstr == '.') ||
376         (*pstr == '~') )
377       *pbuf++ = *pstr;
378     else if (*pstr == ' ')
379       *pbuf++ = '+';
380     else
381     {
382       *pbuf++ = '%';
383       *pbuf++ = to_hex (*pstr >> 4);
384       *pbuf++ = to_hex (*pstr & 15);
385     }
386     pstr++;
387   }
388   *pbuf = '\0';
389   return buf;
390 }
391
392
393 /* Returns a url-decoded version of str */
394 /* IMPORTANT: be sure to free() the returned string after use */
395 static char *
396 url_decode (const char *str)
397 {
398   char *pstr = (char *) str;
399   char *buf = GNUNET_malloc (strlen (str) + 1);
400   char *pbuf = buf;
401
402   while (*pstr)
403   {
404     if (*pstr == '%')
405     {
406       if (pstr[1] && pstr[2])
407       {
408         *pbuf++ = from_hex (pstr[1]) << 4 | from_hex (pstr[2]);
409         pstr += 2;
410       }
411     }
412     else if (*pstr == '+')
413     {
414       *pbuf++ = ' ';
415     }
416     else
417     {
418       *pbuf++ = *pstr;
419     }
420     pstr++;
421   }
422   *pbuf = '\0';
423   return buf;
424 }
425
426
427 /**
428  * Returns base64 encoded string urlencoded
429  *
430  * @param string the string to encode
431  * @return base64 encoded string
432  */
433 static char *
434 base64_and_urlencode (const char *data, size_t data_size)
435 {
436   char *enc;
437   char *urlenc;
438
439   GNUNET_STRINGS_base64_encode (data, data_size, &enc);
440   urlenc = url_encode (enc);
441   GNUNET_free (enc);
442   return urlenc;
443 }
444
445
446 /**
447  * Returns base64 encoded string urlencoded
448  *
449  * @param string the string to encode
450  * @return base64 encoded string
451  */
452 static char *
453 base64url_encode (const char *data, size_t data_size)
454 {
455   char *enc;
456   size_t pos;
457
458   GNUNET_STRINGS_base64_encode (data, data_size, &enc);
459   // Replace with correct characters for base64url
460   pos = 0;
461   while ('\0' != enc[pos])
462   {
463     if ('+' == enc[pos])
464       enc[pos] = '-';
465     if ('/' == enc[pos])
466       enc[pos] = '_';
467     if ('=' == enc[pos])
468     {
469       enc[pos] = '\0';
470       break;
471     }
472     pos++;
473   }
474   return enc;
475 }
476
477
478 static void
479 derive_aes_key (struct GNUNET_CRYPTO_SymmetricSessionKey *key,
480                 struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
481                 struct GNUNET_HashCode *key_material)
482 {
483   static const char ctx_key[] = "reclaim-aes-ctx-key";
484   static const char ctx_iv[] = "reclaim-aes-ctx-iv";
485
486   GNUNET_CRYPTO_kdf (key,
487                      sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey),
488                      ctx_key,
489                      strlen (ctx_key),
490                      key_material,
491                      sizeof(struct GNUNET_HashCode),
492                      NULL);
493   GNUNET_CRYPTO_kdf (iv,
494                      sizeof(
495                        struct GNUNET_CRYPTO_SymmetricInitializationVector),
496                      ctx_iv,
497                      strlen (ctx_iv),
498                      key_material,
499                      sizeof(struct GNUNET_HashCode),
500                      NULL);
501 }
502
503
504 static void
505 calculate_key_priv (struct GNUNET_CRYPTO_SymmetricSessionKey *key,
506                     struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
507                     const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv,
508                     const struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pub)
509 {
510   struct GNUNET_HashCode key_material;
511
512   GNUNET_CRYPTO_ecdsa_ecdh (ecdsa_priv, ecdh_pub, &key_material);
513   derive_aes_key (key, iv, &key_material);
514 }
515
516
517 static void
518 calculate_key_pub (struct GNUNET_CRYPTO_SymmetricSessionKey *key,
519                    struct GNUNET_CRYPTO_SymmetricInitializationVector *iv,
520                    const struct GNUNET_CRYPTO_EcdsaPublicKey *ecdsa_pub,
521                    const struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_priv)
522 {
523   struct GNUNET_HashCode key_material;
524
525   GNUNET_CRYPTO_ecdh_ecdsa (ecdh_priv, ecdsa_pub, &key_material);
526   derive_aes_key (key, iv, &key_material);
527 }
528
529
530 static void
531 decrypt_payload (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv,
532                  const struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pub,
533                  const char *ct,
534                  size_t ct_len,
535                  char *buf)
536 {
537   struct GNUNET_CRYPTO_SymmetricSessionKey key;
538   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
539
540   calculate_key_priv (&key, &iv, ecdsa_priv, ecdh_pub);
541   GNUNET_break (GNUNET_CRYPTO_symmetric_decrypt (ct, ct_len, &key, &iv, buf));
542 }
543
544
545 static void
546 encrypt_payload (const struct GNUNET_CRYPTO_EcdsaPublicKey *ecdsa_pub,
547                  const struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_priv,
548                  const char *payload,
549                  size_t payload_len,
550                  char *buf)
551 {
552   struct GNUNET_CRYPTO_SymmetricSessionKey key;
553   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
554
555   calculate_key_pub (&key, &iv, ecdsa_pub, ecdh_priv);
556   GNUNET_break (
557     GNUNET_CRYPTO_symmetric_encrypt (payload, payload_len, &key, &iv, buf));
558 }
559
560
561 /**
562  * Builds an OIDC authorization code including
563  * a reclaim ticket and nonce
564  *
565  * @param issuer the issuer of the ticket, used to sign the ticket and nonce
566  * @param ticket the ticket to include in the code
567  * @param attrs list of attributes which are shared
568  * @param nonce the nonce to include in the code
569  * @param code_challenge PKCE code challenge
570  * @return a new authorization code (caller must free)
571  */
572 char *
573 OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer,
574                        const struct GNUNET_RECLAIM_Ticket *ticket,
575                        struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs,
576                        const char *nonce_str,
577                        const char *code_challenge)
578 {
579   struct OIDC_Parameters params;
580   char *code_payload;
581   char *payload;
582   char *tmp;
583   char *code_str;
584   char *buf_ptr = NULL;
585   size_t payload_len;
586   size_t code_payload_len;
587   size_t attr_list_len = 0;
588   size_t code_challenge_len = 0;
589   uint32_t nonce;
590   uint32_t nonce_tmp;
591   struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
592   struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_priv;
593   struct GNUNET_CRYPTO_EcdhePublicKey ecdh_pub;
594
595   /** PLAINTEXT **/
596   // Assign ticket
597   memset (&params, 0, sizeof(params));
598   params.ticket = *ticket;
599   // Assign nonce
600   nonce = 0;
601   payload_len = sizeof(struct OIDC_Parameters);
602   if ((NULL != nonce_str) && (strcmp ("", nonce_str) != 0))
603   {
604     if ((1 != sscanf (nonce_str, "%u", &nonce)) || (nonce > UINT32_MAX))
605     {
606       GNUNET_break (0);
607       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid nonce %s\n", nonce_str);
608       return NULL;
609     }
610     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
611                 "Got nonce: %u from %s\n",
612                 nonce,
613                 nonce_str);
614   }
615   nonce_tmp = htonl (nonce);
616   params.nonce = nonce_tmp;
617   // Assign code challenge
618   if (NULL != code_challenge)
619     code_challenge_len = strlen (code_challenge);
620   payload_len += code_challenge_len;
621   params.code_challenge_len = htonl (code_challenge_len);
622   // Assign attributes
623   if (NULL != attrs)
624   {
625     // Get length
626     attr_list_len = GNUNET_RECLAIM_ATTRIBUTE_list_serialize_get_size (attrs);
627     params.attr_list_len = htonl (attr_list_len);
628     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
629                 "Length of serialized attributes: %lu\n",
630                 attr_list_len);
631     // Get serialized attributes
632     payload_len += attr_list_len;
633   }
634   // Get plaintext length
635   payload = GNUNET_malloc (payload_len);
636   memcpy (payload, &params, sizeof(params));
637   tmp = payload + sizeof(params);
638   if (0 < code_challenge_len)
639   {
640     memcpy (tmp, code_challenge, code_challenge_len);
641     tmp += code_challenge_len;
642   }
643   if (0 < attr_list_len)
644     GNUNET_RECLAIM_ATTRIBUTE_list_serialize (attrs, tmp);
645   /** END **/
646
647   /** ENCRYPT **/
648   // Get length
649   code_payload_len = sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
650                      + sizeof(struct GNUNET_CRYPTO_EcdhePublicKey)
651                      + payload_len + sizeof(struct
652                                             GNUNET_CRYPTO_EcdsaSignature);
653   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
654               "Length of data to encode: %lu\n",
655               code_payload_len);
656
657   // Generate ECDH key
658   ecdh_priv = GNUNET_CRYPTO_ecdhe_key_create ();
659   GNUNET_CRYPTO_ecdhe_key_get_public (ecdh_priv, &ecdh_pub);
660   // Initialize code payload
661   code_payload = GNUNET_malloc (code_payload_len);
662   GNUNET_assert (NULL != code_payload);
663   purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
664   purpose->size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
665                          + sizeof(ecdh_pub) + payload_len);
666   purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN);
667   // Store pubkey
668   buf_ptr = (char *) &purpose[1];
669   memcpy (buf_ptr, &ecdh_pub, sizeof(ecdh_pub));
670   buf_ptr += sizeof(ecdh_pub);
671   // Encrypt plaintext and store
672   encrypt_payload (&ticket->audience, ecdh_priv, payload, payload_len, buf_ptr);
673   GNUNET_free (ecdh_priv);
674   GNUNET_free (payload);
675   buf_ptr += payload_len;
676   // Sign and store signature
677   if (GNUNET_SYSERR ==
678       GNUNET_CRYPTO_ecdsa_sign (issuer,
679                                 purpose,
680                                 (struct GNUNET_CRYPTO_EcdsaSignature *)
681                                 buf_ptr))
682   {
683     GNUNET_break (0);
684     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to sign code\n");
685     GNUNET_free (code_payload);
686     return NULL;
687   }
688   code_str = base64_and_urlencode (code_payload, code_payload_len);
689   GNUNET_free (code_payload);
690   return code_str;
691 }
692
693
694 /**
695  * Parse reclaim ticket and nonce from
696  * authorization code.
697  * This also verifies the signature in the code.
698  *
699  * @param audience the expected audience of the code
700  * @param code the string representation of the code
701  * @param code_verfier PKCE code verifier
702  * @param ticket where to store the ticket
703  * @param attrs the attributes in the code
704  * @param nonce where to store the nonce
705  * @return GNUNET_OK if successful, else GNUNET_SYSERR
706  */
707 int
708 OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv,
709                        const char *code,
710                        const char *code_verifier,
711                        struct GNUNET_RECLAIM_Ticket *ticket,
712                        struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList **attrs,
713                        char **nonce_str)
714 {
715   char *code_payload;
716   char *ptr;
717   char *plaintext;
718   char *attrs_ser;
719   char *expected_code_challenge;
720   char *code_challenge;
721   char *code_verifier_hash;
722   struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
723   struct GNUNET_CRYPTO_EcdsaSignature *signature;
724   struct GNUNET_CRYPTO_EcdsaPublicKey ecdsa_pub;
725   struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pub;
726   uint32_t code_challenge_len;
727   uint32_t attrs_ser_len;
728   size_t plaintext_len;
729   size_t code_payload_len;
730   uint32_t nonce = 0;
731   struct OIDC_Parameters *params;
732
733   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to decode `%s'\n", code);
734   code_payload = NULL;
735   code_payload_len =
736     GNUNET_STRINGS_base64_decode (code, strlen (code), (void **) &code_payload);
737   if (code_payload_len < sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
738       + sizeof(struct GNUNET_CRYPTO_EcdhePublicKey)
739       + sizeof(struct OIDC_Parameters)
740       + sizeof(struct GNUNET_CRYPTO_EcdsaSignature))
741   {
742     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Authorization code malformed\n");
743     GNUNET_free_non_null (code_payload);
744     return GNUNET_SYSERR;
745   }
746
747   purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
748   plaintext_len = code_payload_len;
749   plaintext_len -= sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose);
750   ptr = (char *) &purpose[1];
751   // Public ECDH key
752   ecdh_pub = (struct GNUNET_CRYPTO_EcdhePublicKey *) ptr;
753   ptr += sizeof(struct GNUNET_CRYPTO_EcdhePublicKey);
754   plaintext_len -= sizeof(struct GNUNET_CRYPTO_EcdhePublicKey);
755
756   // Decrypt ciphertext
757   plaintext_len -= sizeof(struct GNUNET_CRYPTO_EcdsaSignature);
758   plaintext = GNUNET_malloc (plaintext_len);
759   decrypt_payload (ecdsa_priv, ecdh_pub, ptr, plaintext_len, plaintext);
760   // ptr = plaintext;
761   ptr += plaintext_len;
762   signature = (struct GNUNET_CRYPTO_EcdsaSignature *) ptr;
763   params = (struct OIDC_Parameters *) plaintext;
764
765   // cmp code_challenge code_verifier
766   code_challenge_len = ntohl (params->code_challenge_len);
767   if (0 != code_challenge_len) /* Only check if this code requires a CV */
768   {
769     if (NULL == code_verifier)
770     {
771       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
772                   "Expected code verifier!\n");
773       GNUNET_free_non_null (code_payload);
774       return GNUNET_SYSERR;
775     }
776     code_verifier_hash = GNUNET_malloc (256 / 8);
777     // hash code verifier
778     gcry_md_hash_buffer (GCRY_MD_SHA256,
779                          code_verifier_hash,
780                          code_verifier,
781                          strlen (code_verifier));
782     // encode code verifier
783     expected_code_challenge = base64url_encode (code_verifier_hash, 256 / 8);
784     code_challenge = (char *) &params[1];
785     GNUNET_free (code_verifier_hash);
786     if ((strlen (expected_code_challenge) != code_challenge_len) ||
787         (0 !=
788          strncmp (expected_code_challenge, code_challenge, code_challenge_len)))
789     {
790       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
791                   "Invalid code verifier! Expected: %s, Got: %.*s\n",
792                   expected_code_challenge,
793                   code_challenge_len,
794                   code_challenge);
795       GNUNET_free_non_null (code_payload);
796       GNUNET_free (expected_code_challenge);
797       return GNUNET_SYSERR;
798     }
799     GNUNET_free (expected_code_challenge);
800   }
801   // Ticket
802   memcpy (ticket, &params->ticket, sizeof(params->ticket));
803   // Nonce
804   nonce = ntohl (params->nonce);  // ntohl (*((uint32_t *) ptr));
805   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got nonce: %u\n", nonce);
806   // Signature
807   GNUNET_CRYPTO_ecdsa_key_get_public (ecdsa_priv, &ecdsa_pub);
808   if (0 != GNUNET_memcmp (&ecdsa_pub, &ticket->audience))
809   {
810     GNUNET_free (code_payload);
811     GNUNET_free (plaintext);
812     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
813                 "Audience in ticket does not match client!\n");
814     return GNUNET_SYSERR;
815   }
816   if (GNUNET_OK !=
817       GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN,
818                                   purpose,
819                                   signature,
820                                   &ticket->identity))
821   {
822     GNUNET_free (code_payload);
823     GNUNET_free (plaintext);
824     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signature of AuthZ code invalid!\n");
825     return GNUNET_SYSERR;
826   }
827   // Attributes
828   attrs_ser = ((char *) &params[1]) + code_challenge_len;
829   attrs_ser_len = ntohl (params->attr_list_len);
830   *attrs = GNUNET_RECLAIM_ATTRIBUTE_list_deserialize (attrs_ser, attrs_ser_len);
831
832   *nonce_str = NULL;
833   if (nonce != 0)
834     GNUNET_asprintf (nonce_str, "%u", nonce);
835   GNUNET_free (code_payload);
836   GNUNET_free (plaintext);
837   return GNUNET_OK;
838 }
839
840
841 /**
842  * Build a token response for a token request
843  * TODO: Maybe we should add the scope here?
844  *
845  * @param access_token the access token to include
846  * @param id_token the id_token to include
847  * @param expiration_time the expiration time of the token(s)
848  * @param token_response where to store the response
849  */
850 void
851 OIDC_build_token_response (const char *access_token,
852                            const char *id_token,
853                            const struct GNUNET_TIME_Relative *expiration_time,
854                            char **token_response)
855 {
856   json_t *root_json;
857
858   root_json = json_object ();
859
860   GNUNET_assert (NULL != access_token);
861   GNUNET_assert (NULL != id_token);
862   GNUNET_assert (NULL != expiration_time);
863   json_object_set_new (root_json, "access_token", json_string (access_token));
864   json_object_set_new (root_json, "token_type", json_string ("Bearer"));
865   json_object_set_new (root_json,
866                        "expires_in",
867                        json_integer (expiration_time->rel_value_us
868                                      / (1000 * 1000)));
869   json_object_set_new (root_json, "id_token", json_string (id_token));
870   *token_response = json_dumps (root_json, JSON_INDENT (0) | JSON_COMPACT);
871   json_decref (root_json);
872 }
873
874
875 /**
876  * Generate a new access token
877  */
878 char *
879 OIDC_access_token_new ()
880 {
881   char *access_token;
882   uint64_t random_number;
883
884   random_number =
885     GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
886   GNUNET_STRINGS_base64_encode (&random_number,
887                                 sizeof(uint64_t),
888                                 &access_token);
889   return access_token;
890 }