aboutsummaryrefslogtreecommitdiff
path: root/src/reclaim/oidc_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reclaim/oidc_helper.c')
-rw-r--r--src/reclaim/oidc_helper.c934
1 files changed, 0 insertions, 934 deletions
diff --git a/src/reclaim/oidc_helper.c b/src/reclaim/oidc_helper.c
deleted file mode 100644
index 9237902ce..000000000
--- a/src/reclaim/oidc_helper.c
+++ /dev/null
@@ -1,934 +0,0 @@
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
37GNUNET_NETWORK_STRUCT_BEGIN
38
39/**
40 * The signature used to generate the authorization code
41 */
42struct OIDC_Parameters
43{
44 /**
45 * The reclaim ticket
46 */
47 struct GNUNET_RECLAIM_Ticket ticket;
48
49 /**
50 * The nonce length
51 */
52 uint32_t nonce_len 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 presentation list
66 */
67 uint32_t pres_list_len GNUNET_PACKED;
68};
69
70GNUNET_NETWORK_STRUCT_END
71
72/**
73 * Standard claims represented by the "profile" scope in OIDC
74 */
75static char OIDC_profile_claims[14][32] = {
76 "name", "family_name", "given_name", "middle_name", "nickname",
77 "preferred_username", "profile", "picture", "website", "gender", "birthdate",
78 "zoneinfo", "locale", "updated_at"
79};
80
81/**
82 * Standard claims represented by the "email" scope in OIDC
83 */
84static char OIDC_email_claims[2][16] = {
85 "email", "email_verified"
86};
87
88/**
89 * Standard claims represented by the "phone" scope in OIDC
90 */
91static char OIDC_phone_claims[2][32] = {
92 "phone_number", "phone_number_verified"
93};
94
95/**
96 * Standard claims represented by the "address" scope in OIDC
97 */
98static char OIDC_address_claims[5][32] = {
99 "street_address", "locality", "region", "postal_code", "country"
100};
101
102static enum GNUNET_GenericReturnValue
103is_claim_in_address_scope (const char *claim)
104{
105 int i;
106 for (i = 0; i < 5; i++)
107 {
108 if (0 == strcmp (claim, OIDC_address_claims[i]))
109 {
110 return GNUNET_YES;
111 }
112 }
113 return GNUNET_NO;
114}
115
116
117static char *
118create_jwt_header (void)
119{
120 json_t *root;
121 char *json_str;
122
123 root = json_object ();
124 json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE));
125 json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE));
126
127 json_str = json_dumps (root, JSON_INDENT (0) | JSON_COMPACT);
128 json_decref (root);
129 return json_str;
130}
131
132
133static void
134replace_char (char *str, char find, char replace)
135{
136 char *current_pos = strchr (str, find);
137
138 while (current_pos)
139 {
140 *current_pos = replace;
141 current_pos = strchr (current_pos, find);
142 }
143}
144
145
146// RFC4648
147static void
148fix_base64 (char *str)
149{
150 // Replace + with -
151 replace_char (str, '+', '-');
152
153 // Replace / with _
154 replace_char (str, '/', '_');
155}
156
157
158static json_t*
159generate_userinfo_json (const struct GNUNET_IDENTITY_PublicKey *sub_key,
160 const struct GNUNET_RECLAIM_AttributeList *attrs,
161 const struct
162 GNUNET_RECLAIM_PresentationList *presentations)
163{
164 struct GNUNET_RECLAIM_AttributeListEntry *le;
165 struct GNUNET_RECLAIM_PresentationListEntry *ple;
166 char *subject;
167 char *source_name;
168 char *attr_val_str;
169 char *pres_val_str;
170 json_t *body;
171 json_t *aggr_names;
172 json_t *aggr_sources;
173 json_t *aggr_sources_jwt;
174 json_t *addr_claim = NULL;
175 int num_presentations = 0;
176 for (le = attrs->list_head; NULL != le; le = le->next)
177 {
178 if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->credential))
179 num_presentations++;
180 }
181
182 subject =
183 GNUNET_STRINGS_data_to_string_alloc (sub_key,
184 sizeof(struct
185 GNUNET_IDENTITY_PublicKey));
186 body = json_object ();
187 aggr_names = json_object ();
188 aggr_sources = json_object ();
189
190 // iss REQUIRED case sensitive server uri with https
191 // The issuer is the local reclaim instance (e.g.
192 // https://reclaim.id/api/openid)
193 json_object_set_new (body, "iss", json_string (SERVER_ADDRESS));
194 // sub REQUIRED public key identity, not exceed 255 ASCII length
195 json_object_set_new (body, "sub", json_string (subject));
196 GNUNET_free (subject);
197 pres_val_str = NULL;
198 source_name = NULL;
199 int i = 0;
200 for (ple = presentations->list_head; NULL != ple; ple = ple->next)
201 {
202 // New presentation
203 GNUNET_asprintf (&source_name,
204 "src%d",
205 i);
206 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
207 "Adding new presentation source #%d\n", i);
208 aggr_sources_jwt = json_object ();
209 pres_val_str =
210 GNUNET_RECLAIM_presentation_value_to_string (ple->presentation->type,
211 ple->presentation->data,
212 ple->presentation->data_size);
213 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
214 "Presentation is: %s\n", pres_val_str);
215 json_object_set_new (aggr_sources_jwt,
216 GNUNET_RECLAIM_presentation_number_to_typename (
217 ple->presentation->type),
218 json_string (pres_val_str) );
219 json_object_set_new (aggr_sources, source_name, aggr_sources_jwt);
220 GNUNET_free (pres_val_str);
221 GNUNET_free (source_name);
222 source_name = NULL;
223 i++;
224 }
225
226 int addr_is_aggregated = GNUNET_NO;
227 int addr_is_normal = GNUNET_NO;
228 for (le = attrs->list_head; NULL != le; le = le->next)
229 {
230 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
231 "Processing %s for userinfo body\n",
232 le->attribute->name);
233 if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&le->attribute->credential))
234 {
235 attr_val_str =
236 GNUNET_RECLAIM_attribute_value_to_string (le->attribute->type,
237 le->attribute->data,
238 le->attribute->data_size);
239 /**
240 * There is this weird quirk that the individual address claim(s) must be
241 * inside a JSON object of the "address" claim.
242 */
243 if (GNUNET_YES == is_claim_in_address_scope (le->attribute->name))
244 {
245 if (GNUNET_YES == addr_is_aggregated)
246 {
247 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
248 "Address is set as aggregated claim. Skipping self-issued value...\n");
249 GNUNET_free (attr_val_str);
250 continue;
251 }
252 addr_is_normal = GNUNET_YES;
253
254 if (NULL == addr_claim)
255 {
256 addr_claim = json_object ();
257 json_object_set_new (body, "address", addr_claim);
258 }
259 json_object_set_new (addr_claim, le->attribute->name,
260 json_string (attr_val_str));
261
262 }
263 else
264 {
265 json_object_set_new (body, le->attribute->name,
266 json_string (attr_val_str));
267 }
268 GNUNET_free (attr_val_str);
269 }
270 else
271 {
272 // Check if presentation is there
273 int j = 0;
274 for (ple = presentations->list_head; NULL != ple; ple = ple->next)
275 {
276 if (GNUNET_YES ==
277 GNUNET_RECLAIM_id_is_equal (&ple->presentation->credential_id,
278 &le->attribute->credential))
279 break;
280 j++;
281 }
282 if (NULL == ple)
283 {
284 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
285 "Presentation for `%s' missing...\n",
286 le->attribute->name);
287 continue;
288 }
289 /**
290 * There is this weird quirk that the individual address claim(s) must be
291 * inside a JSON object of the "address" claim.
292 */
293 if (GNUNET_YES == is_claim_in_address_scope (le->attribute->name))
294 {
295 if (GNUNET_YES == addr_is_normal)
296 {
297 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
298 "Address is already set as normal claim. Skipping attested value...\n");
299 continue;
300 }
301 addr_is_aggregated = GNUNET_YES;
302 /** This is/can only be set once! **/
303 if (NULL != addr_claim)
304 continue;
305 addr_claim = json_object ();
306 GNUNET_asprintf (&source_name,
307 "src%d",
308 j);
309 json_object_set_new (aggr_names, "address",
310 json_string (source_name));
311 GNUNET_free (source_name);
312 }
313 else
314 {
315 // Presentation exists, hence take the respective source str
316 GNUNET_asprintf (&source_name,
317 "src%d",
318 j);
319 json_object_set_new (aggr_names, le->attribute->name,
320 json_string (source_name));
321 GNUNET_free (source_name);
322 }
323 }
324 }
325 if (0 != i)
326 {
327 json_object_set_new (body, "_claim_names", aggr_names);
328 json_object_set_new (body, "_claim_sources", aggr_sources);
329 }
330
331 return body;
332}
333
334
335/**
336 * Generate userinfo JSON as string
337 *
338 * @param sub_key the subject (user)
339 * @param attrs user attribute list
340 * @param presentations credential presentation list (may be empty)
341 * @return Userinfo JSON
342 */
343char *
344OIDC_generate_userinfo (const struct GNUNET_IDENTITY_PublicKey *sub_key,
345 const struct GNUNET_RECLAIM_AttributeList *attrs,
346 const struct
347 GNUNET_RECLAIM_PresentationList *presentations)
348{
349 char *body_str;
350 json_t*body = generate_userinfo_json (sub_key,
351 attrs,
352 presentations);
353 body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT);
354 json_decref (body);
355 return body_str;
356}
357
358
359/**
360 * Create a JWT from attributes
361 *
362 * @param aud_key the public of the audience
363 * @param sub_key the public key of the subject
364 * @param attrs the attribute list
365 * @param presentations credential presentation list (may be empty)
366 * @param expiration_time the validity of the token
367 * @param secret_key the key used to sign the JWT
368 * @return a new base64-encoded JWT string.
369 */
370char *
371OIDC_generate_id_token (const struct GNUNET_IDENTITY_PublicKey *aud_key,
372 const struct GNUNET_IDENTITY_PublicKey *sub_key,
373 const struct GNUNET_RECLAIM_AttributeList *attrs,
374 const struct
375 GNUNET_RECLAIM_PresentationList *presentations,
376 const struct GNUNET_TIME_Relative *expiration_time,
377 const char *nonce,
378 const char *secret_key)
379{
380 struct GNUNET_HashCode signature;
381 struct GNUNET_TIME_Absolute exp_time;
382 struct GNUNET_TIME_Absolute time_now;
383 char *audience;
384 char *subject;
385 char *header;
386 char *body_str;
387 char *result;
388 char *header_base64;
389 char *body_base64;
390 char *signature_target;
391 char *signature_base64;
392 json_t *body;
393
394 body = generate_userinfo_json (sub_key,
395 attrs,
396 presentations);
397 // iat REQUIRED time now
398 time_now = GNUNET_TIME_absolute_get ();
399 // exp REQUIRED time expired from config
400 exp_time = GNUNET_TIME_absolute_add (time_now, *expiration_time);
401 // auth_time only if max_age
402 // nonce only if nonce
403 // OPTIONAL acr,amr,azp
404 subject =
405 GNUNET_STRINGS_data_to_string_alloc (sub_key,
406 sizeof(struct
407 GNUNET_IDENTITY_PublicKey));
408 audience =
409 GNUNET_STRINGS_data_to_string_alloc (aud_key,
410 sizeof(struct
411 GNUNET_IDENTITY_PublicKey));
412 header = create_jwt_header ();
413
414 // aud REQUIRED public key client_id must be there
415 json_object_set_new (body, "aud", json_string (audience));
416 // iat
417 json_object_set_new (body,
418 "iat",
419 json_integer (time_now.abs_value_us / (1000 * 1000)));
420 // exp
421 json_object_set_new (body,
422 "exp",
423 json_integer (exp_time.abs_value_us / (1000 * 1000)));
424 // nbf
425 json_object_set_new (body,
426 "nbf",
427 json_integer (time_now.abs_value_us / (1000 * 1000)));
428 // nonce
429 if (NULL != nonce)
430 json_object_set_new (body, "nonce", json_string (nonce));
431
432 body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT);
433 json_decref (body);
434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"ID-Token: %s\n", body_str);
435
436 GNUNET_STRINGS_base64url_encode (header, strlen (header), &header_base64);
437 fix_base64 (header_base64);
438
439 GNUNET_STRINGS_base64url_encode (body_str, strlen (body_str), &body_base64);
440 fix_base64 (body_base64);
441
442 GNUNET_free (subject);
443 GNUNET_free (audience);
444
445 /**
446 * Creating the JWT signature. This might not be
447 * standards compliant, check.
448 */
449 GNUNET_asprintf (&signature_target, "%s.%s", header_base64, body_base64);
450 GNUNET_CRYPTO_hmac_raw (secret_key,
451 strlen (secret_key),
452 signature_target,
453 strlen (signature_target),
454 &signature);
455 GNUNET_STRINGS_base64url_encode ((const char *) &signature,
456 sizeof(struct GNUNET_HashCode),
457 &signature_base64);
458 fix_base64 (signature_base64);
459
460 GNUNET_asprintf (&result,
461 "%s.%s.%s",
462 header_base64,
463 body_base64,
464 signature_base64);
465
466 GNUNET_free (signature_target);
467 GNUNET_free (header);
468 GNUNET_free (body_str);
469 GNUNET_free (signature_base64);
470 GNUNET_free (body_base64);
471 GNUNET_free (header_base64);
472 return result;
473}
474
475
476/**
477 * Builds an OIDC authorization code including
478 * a reclaim ticket and nonce
479 *
480 * @param issuer the issuer of the ticket, used to sign the ticket and nonce
481 * @param ticket the ticket to include in the code
482 * @param attrs list of attributes which are shared
483 * @param presentations credential presentation list (may be empty)
484 * @param nonce the nonce to include in the code
485 * @param code_challenge PKCE code challenge
486 * @return a new authorization code (caller must free)
487 */
488char *
489OIDC_build_authz_code (const struct GNUNET_IDENTITY_PrivateKey *issuer,
490 const struct GNUNET_RECLAIM_Ticket *ticket,
491 const struct GNUNET_RECLAIM_AttributeList *attrs,
492 const struct
493 GNUNET_RECLAIM_PresentationList *presentations,
494 const char *nonce_str,
495 const char *code_challenge)
496{
497 struct OIDC_Parameters params;
498 char *code_payload;
499 char *payload;
500 char *tmp;
501 char *code_str;
502 char *buf_ptr = NULL;
503 size_t payload_len;
504 size_t code_payload_len;
505 size_t attr_list_len = 0;
506 size_t pres_list_len = 0;
507 size_t code_challenge_len = 0;
508 uint32_t nonce_len = 0;
509 struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
510
511 /** PLAINTEXT **/
512 // Assign ticket
513 memset (&params, 0, sizeof(params));
514 params.ticket = *ticket;
515 // Assign nonce
516 payload_len = sizeof(struct OIDC_Parameters);
517 if ((NULL != nonce_str) && (strcmp ("", nonce_str) != 0))
518 {
519 nonce_len = strlen (nonce_str);
520 payload_len += nonce_len;
521 }
522 params.nonce_len = htonl (nonce_len);
523 // Assign code challenge
524 if (NULL != code_challenge)
525 code_challenge_len = strlen (code_challenge);
526 payload_len += code_challenge_len;
527 params.code_challenge_len = htonl (code_challenge_len);
528 // Assign attributes
529 if (NULL != attrs)
530 {
531 // Get length
532 attr_list_len = GNUNET_RECLAIM_attribute_list_serialize_get_size (attrs);
533 params.attr_list_len = htonl (attr_list_len);
534 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
535 "Length of serialized attributes: %lu\n",
536 attr_list_len);
537 // Get serialized attributes
538 payload_len += attr_list_len;
539 }
540 if (NULL != presentations)
541 {
542 // Get length
543 // FIXME only add presentations relevant for attribute list!!!
544 // This is important because of the distinction between id_token and
545 // userinfo in OIDC
546 pres_list_len =
547 GNUNET_RECLAIM_presentation_list_serialize_get_size (presentations);
548 params.pres_list_len = htonl (pres_list_len);
549 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
550 "Length of serialized presentations: %lu\n",
551 pres_list_len);
552 // Get serialized attributes
553 payload_len += pres_list_len;
554 }
555
556 // Get plaintext length
557 payload = GNUNET_malloc (payload_len);
558 memcpy (payload, &params, sizeof(params));
559 tmp = payload + sizeof(params);
560 if (0 < code_challenge_len)
561 {
562 memcpy (tmp, code_challenge, code_challenge_len);
563 tmp += code_challenge_len;
564 }
565 if (0 < nonce_len)
566 {
567 memcpy (tmp, nonce_str, nonce_len);
568 tmp += nonce_len;
569 }
570 if (0 < attr_list_len)
571 GNUNET_RECLAIM_attribute_list_serialize (attrs, tmp);
572 tmp += attr_list_len;
573 if (0 < pres_list_len)
574 GNUNET_RECLAIM_presentation_list_serialize (presentations, tmp);
575 tmp += pres_list_len;
576
577 /** END **/
578
579 // Get length
580 code_payload_len = sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
581 + payload_len + sizeof(struct
582 GNUNET_IDENTITY_Signature);
583 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
584 "Length of data to encode: %lu\n",
585 code_payload_len);
586
587 // Initialize code payload
588 code_payload = GNUNET_malloc (code_payload_len);
589 GNUNET_assert (NULL != code_payload);
590 purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
591 purpose->size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
592 + payload_len);
593 purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN);
594 // Store pubkey
595 buf_ptr = (char *) &purpose[1];
596 memcpy (buf_ptr, payload, payload_len);
597 GNUNET_free (payload);
598 buf_ptr += payload_len;
599 // Sign and store signature
600 if (GNUNET_SYSERR ==
601 GNUNET_IDENTITY_sign_ (issuer,
602 purpose,
603 (struct GNUNET_IDENTITY_Signature *)
604 buf_ptr))
605 {
606 GNUNET_break (0);
607 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to sign code\n");
608 GNUNET_free (code_payload);
609 return NULL;
610 }
611 GNUNET_STRINGS_base64url_encode (code_payload, code_payload_len, &code_str);
612 GNUNET_free (code_payload);
613 return code_str;
614}
615
616
617enum GNUNET_GenericReturnValue
618check_code_challenge (const char *code_challenge,
619 uint32_t code_challenge_len,
620 const char *code_verifier)
621{
622 char *code_verifier_hash;
623 char *expected_code_challenge;
624
625 if (0 == code_challenge_len) /* Only check if this code requires a CV */
626 return GNUNET_OK;
627 if (NULL == code_verifier)
628 {
629 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
630 "Expected code verifier!\n");
631 return GNUNET_SYSERR;
632 }
633 code_verifier_hash = GNUNET_malloc (256 / 8);
634 // hash code verifier
635 gcry_md_hash_buffer (GCRY_MD_SHA256,
636 code_verifier_hash,
637 code_verifier,
638 strlen (code_verifier));
639 // encode code verifier
640 GNUNET_STRINGS_base64url_encode (code_verifier_hash, 256 / 8,
641 &expected_code_challenge);
642 GNUNET_free (code_verifier_hash);
643 if (0 !=
644 strncmp (expected_code_challenge, code_challenge, code_challenge_len))
645 {
646 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
647 "Invalid code verifier! Expected: %s, Got: %.*s\n",
648 expected_code_challenge,
649 code_challenge_len,
650 code_challenge);
651 GNUNET_free (expected_code_challenge);
652 return GNUNET_SYSERR;
653 }
654 GNUNET_free (expected_code_challenge);
655 return GNUNET_OK;
656}
657
658
659/**
660 * Parse reclaim ticket and nonce from
661 * authorization code.
662 * This also verifies the signature in the code.
663 *
664 * @param audience the expected audience of the code
665 * @param code the string representation of the code
666 * @param code_verfier PKCE code verifier. Optional, must be provided
667 * if used in request.
668 * @param ticket where to store the ticket
669 * @param attrs the attributes in the code
670 * @param presentations credential presentation list
671 * @param nonce_str where to store the nonce (if contained)
672 * @return GNUNET_OK if successful, else GNUNET_SYSERR
673 */
674int
675OIDC_parse_authz_code (const struct GNUNET_IDENTITY_PublicKey *audience,
676 const char *code,
677 const char *code_verifier,
678 struct GNUNET_RECLAIM_Ticket *ticket,
679 struct GNUNET_RECLAIM_AttributeList **attrs,
680 struct GNUNET_RECLAIM_PresentationList **presentations,
681 char **nonce_str,
682 enum OIDC_VerificationOptions opts)
683{
684 char *code_payload;
685 char *ptr;
686 char *plaintext;
687 char *attrs_ser;
688 char *presentations_ser;
689 char *code_challenge;
690 struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
691 struct GNUNET_IDENTITY_Signature *signature;
692 uint32_t code_challenge_len;
693 uint32_t attrs_ser_len;
694 uint32_t pres_ser_len;
695 size_t plaintext_len;
696 size_t code_payload_len;
697 uint32_t nonce_len = 0;
698 struct OIDC_Parameters *params;
699
700 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to decode `%s'\n", code);
701 code_payload = NULL;
702 code_payload_len =
703 GNUNET_STRINGS_base64url_decode (code, strlen (code),
704 (void **) &code_payload);
705 if (code_payload_len < sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
706 + sizeof(struct OIDC_Parameters)
707 + sizeof(struct GNUNET_IDENTITY_Signature))
708 {
709 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Authorization code malformed\n");
710 GNUNET_free (code_payload);
711 return GNUNET_SYSERR;
712 }
713
714 purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload;
715 plaintext_len = code_payload_len;
716 plaintext_len -= sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose);
717 ptr = (char *) &purpose[1];
718 plaintext_len -= sizeof(struct GNUNET_IDENTITY_Signature);
719 plaintext = ptr;
720 ptr += plaintext_len;
721 signature = (struct GNUNET_IDENTITY_Signature *) ptr;
722 params = (struct OIDC_Parameters *) plaintext;
723
724 // cmp code_challenge code_verifier
725 code_challenge_len = ntohl (params->code_challenge_len);
726 code_challenge = ((char *) &params[1]);
727 if (! (opts & OIDC_VERIFICATION_NO_CODE_VERIFIER))
728 {
729 if (GNUNET_OK != check_code_challenge (code_challenge,
730 code_challenge_len,
731 code_verifier))
732 {
733 GNUNET_free (code_payload);
734 return GNUNET_SYSERR;
735 }
736 }
737 nonce_len = ntohl (params->nonce_len);
738 if (0 != nonce_len)
739 {
740 *nonce_str = GNUNET_strndup (code_challenge + code_challenge_len,
741 nonce_len);
742 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got nonce: %s\n", *nonce_str);
743 }
744
745 // Ticket
746 memcpy (ticket, &params->ticket, sizeof(params->ticket));
747 // Signature
748 // GNUNET_CRYPTO_ecdsa_key_get_public (ecdsa_priv, &ecdsa_pub);
749 if (0 != GNUNET_memcmp (audience, &ticket->audience))
750 {
751 GNUNET_free (code_payload);
752 if (NULL != *nonce_str)
753 GNUNET_free (*nonce_str);
754 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
755 "Audience in ticket does not match client!\n");
756 return GNUNET_SYSERR;
757 }
758 if (GNUNET_OK !=
759 GNUNET_IDENTITY_signature_verify_ (
760 GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN,
761 purpose,
762 signature,
763 &(ticket->identity)))
764 {
765 GNUNET_free (code_payload);
766 if (NULL != *nonce_str)
767 GNUNET_free (*nonce_str);
768 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signature of AuthZ code invalid!\n");
769 return GNUNET_SYSERR;
770 }
771 // Attributes
772 attrs_ser = ((char *) &params[1]) + code_challenge_len + nonce_len;
773 attrs_ser_len = ntohl (params->attr_list_len);
774 *attrs = GNUNET_RECLAIM_attribute_list_deserialize (attrs_ser, attrs_ser_len);
775 presentations_ser = ((char*) attrs_ser) + attrs_ser_len;
776 pres_ser_len = ntohl (params->pres_list_len);
777 *presentations =
778 GNUNET_RECLAIM_presentation_list_deserialize (presentations_ser,
779 pres_ser_len);
780
781 GNUNET_free (code_payload);
782 return GNUNET_OK;
783}
784
785
786/**
787 * Build a token response for a token request
788 * TODO: Maybe we should add the scope here?
789 *
790 * @param access_token the access token to include
791 * @param id_token the id_token to include
792 * @param expiration_time the expiration time of the token(s)
793 * @param token_response where to store the response
794 */
795void
796OIDC_build_token_response (const char *access_token,
797 const char *id_token,
798 const struct GNUNET_TIME_Relative *expiration_time,
799 char **token_response)
800{
801 json_t *root_json;
802
803 root_json = json_object ();
804
805 GNUNET_assert (NULL != access_token);
806 GNUNET_assert (NULL != id_token);
807 GNUNET_assert (NULL != expiration_time);
808 json_object_set_new (root_json, "access_token", json_string (access_token));
809 json_object_set_new (root_json, "token_type", json_string ("Bearer"));
810 json_object_set_new (root_json,
811 "expires_in",
812 json_integer (expiration_time->rel_value_us
813 / (1000 * 1000)));
814 json_object_set_new (root_json, "id_token", json_string (id_token));
815 *token_response = json_dumps (root_json, JSON_INDENT (0) | JSON_COMPACT);
816 json_decref (root_json);
817}
818
819
820/**
821 * Generate a new access token
822 */
823char *
824OIDC_access_token_new (const struct GNUNET_RECLAIM_Ticket *ticket)
825{
826 char *access_token;
827
828 GNUNET_STRINGS_base64_encode (ticket,
829 sizeof(*ticket),
830 &access_token);
831 return access_token;
832}
833
834
835/**
836 * Parse an access token
837 */
838int
839OIDC_access_token_parse (const char *token,
840 struct GNUNET_RECLAIM_Ticket **ticket)
841{
842 size_t sret;
843 char *decoded;
844 sret = GNUNET_STRINGS_base64_decode (token,
845 strlen (token),
846 (void**) &decoded);
847 if (sizeof (struct GNUNET_RECLAIM_Ticket) != sret)
848 {
849 GNUNET_free (decoded);
850 return GNUNET_SYSERR;
851 }
852 *ticket = (struct GNUNET_RECLAIM_Ticket *) decoded;
853 return GNUNET_OK;
854}
855
856
857/**
858 * Checks if a claim is implicitly requested through standard
859 * scope(s) or explicitly through non-standard scope.
860 *
861 * @param scopes the scopes which have been requested
862 * @param attr the attribute name to check
863 * @return GNUNET_YES if attribute is implcitly requested
864 */
865enum GNUNET_GenericReturnValue
866OIDC_check_scopes_for_claim_request (const char*scopes,
867 const char*attr)
868{
869 char *scope_variables;
870 char *scope_variable;
871 char delimiter[] = " ";
872 int i;
873
874 scope_variables = GNUNET_strdup (scopes);
875 scope_variable = strtok (scope_variables, delimiter);
876 while (NULL != scope_variable)
877 {
878 if (0 == strcmp ("profile", scope_variable))
879 {
880 for (i = 0; i < 14; i++)
881 {
882 if (0 == strcmp (attr, OIDC_profile_claims[i]))
883 {
884 GNUNET_free (scope_variables);
885 return GNUNET_YES;
886 }
887 }
888 }
889 else if (0 == strcmp ("address", scope_variable))
890 {
891 for (i = 0; i < 5; i++)
892 {
893 if (0 == strcmp (attr, OIDC_address_claims[i]))
894 {
895 GNUNET_free (scope_variables);
896 return GNUNET_YES;
897 }
898 }
899 }
900 else if (0 == strcmp ("email", scope_variable))
901 {
902 for (i = 0; i < 2; i++)
903 {
904 if (0 == strcmp (attr, OIDC_email_claims[i]))
905 {
906 GNUNET_free (scope_variables);
907 return GNUNET_YES;
908 }
909 }
910 }
911 else if (0 == strcmp ("phone", scope_variable))
912 {
913 for (i = 0; i < 2; i++)
914 {
915 if (0 == strcmp (attr, OIDC_phone_claims[i]))
916 {
917 GNUNET_free (scope_variables);
918 return GNUNET_YES;
919 }
920 }
921
922 }
923 else if (0 == strcmp (attr, scope_variable))
924 {
925 /** attribute matches requested scope **/
926 GNUNET_free (scope_variables);
927 return GNUNET_YES;
928 }
929 scope_variable = strtok (NULL, delimiter);
930 }
931 GNUNET_free (scope_variables);
932 return GNUNET_NO;
933
934}