aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Schwieren <tristan.schwieren@tum.de>2022-04-11 13:29:20 +0200
committerMartin Schanzenbach <schanzen@gnunet.org>2022-06-09 11:43:01 +0200
commit349b4e37123368e3f03d563770d72bcbfd8109ad (patch)
tree865732d15ee62fa5c7e5d651445ea276a4d4756d
parent79eff4349f89d13a8cc707550f6bb7d5fe8c99c3 (diff)
downloadgnunet-349b4e37123368e3f03d563770d72bcbfd8109ad.tar.gz
gnunet-349b4e37123368e3f03d563770d72bcbfd8109ad.zip
-init oidc RSA256 feature
-rw-r--r--configure.ac5
-rw-r--r--src/reclaim/Makefile.am2
-rw-r--r--src/reclaim/oidc_helper.c160
-rw-r--r--src/reclaim/oidc_helper.h47
-rw-r--r--src/reclaim/plugin_rest_openid_connect.c256
-rw-r--r--src/reclaim/reclaim.conf4
6 files changed, 403 insertions, 71 deletions
diff --git a/configure.ac b/configure.ac
index 733eea06e..3553d93b9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -719,6 +719,11 @@ CHECK_WITH_LIB([jansson], [json_loads], [jansson.h], [HAVE_JANSSON])
719AS_IF([test "x$jansson" = "x0"], 719AS_IF([test "x$jansson" = "x0"],
720 [AC_MSG_ERROR([GNUnet requires jansson])]) 720 [AC_MSG_ERROR([GNUnet requires jansson])])
721 721
722# check for jose
723CHECK_WITH_LIB([jose], [jose_jwk_gen], [jose/jose.h], [HAVE_JOSE])
724AS_IF([test "x$jose" = "x0"],
725 [AC_MSG_ERROR([GNUnet requires jose])])
726
722# check for libpulse (pulseaudio) 727# check for libpulse (pulseaudio)
723CHECK_WITH_LIB([pulse], [pa_stream_peek], [pulse/simple.h], [HAVE_PULSE]) 728CHECK_WITH_LIB([pulse], [pa_stream_peek], [pulse/simple.h], [HAVE_PULSE])
724 729
diff --git a/src/reclaim/Makefile.am b/src/reclaim/Makefile.am
index a8300d6af..710207d8b 100644
--- a/src/reclaim/Makefile.am
+++ b/src/reclaim/Makefile.am
@@ -79,7 +79,7 @@ libgnunet_plugin_rest_openid_connect_la_LIBADD = \
79 $(top_builddir)/src/gns/libgnunetgns.la \ 79 $(top_builddir)/src/gns/libgnunetgns.la \
80 $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ 80 $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \
81 $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \ 81 $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \
82 $(LTLIBINTL) -ljansson $(MHD_LIBS) \ 82 $(LTLIBINTL) -ljansson -ljose $(MHD_LIBS) \
83 $(LIBGCRYPT_LIBS) 83 $(LIBGCRYPT_LIBS)
84libgnunet_plugin_rest_openid_connect_la_LDFLAGS = \ 84libgnunet_plugin_rest_openid_connect_la_LDFLAGS = \
85 $(GN_PLUGIN_LDFLAGS) 85 $(GN_PLUGIN_LDFLAGS)
diff --git a/src/reclaim/oidc_helper.c b/src/reclaim/oidc_helper.c
index 9237902ce..cfa71b26c 100644
--- a/src/reclaim/oidc_helper.c
+++ b/src/reclaim/oidc_helper.c
@@ -22,10 +22,12 @@
22 * @file reclaim/oidc_helper.c 22 * @file reclaim/oidc_helper.c
23 * @brief helper library for OIDC related functions 23 * @brief helper library for OIDC related functions
24 * @author Martin Schanzenbach 24 * @author Martin Schanzenbach
25 * @author Tristan Schwieren
25 */ 26 */
26#include "platform.h" 27#include "platform.h"
27#include <inttypes.h> 28#include <inttypes.h>
28#include <jansson.h> 29#include <jansson.h>
30#include <jose/jose.h>
29#include "gnunet_util_lib.h" 31#include "gnunet_util_lib.h"
30#include "gnunet_reclaim_lib.h" 32#include "gnunet_reclaim_lib.h"
31#include "gnunet_reclaim_service.h" 33#include "gnunet_reclaim_service.h"
@@ -115,13 +117,13 @@ is_claim_in_address_scope (const char *claim)
115 117
116 118
117static char * 119static char *
118create_jwt_header (void) 120create_jwt_hmac_header (void)
119{ 121{
120 json_t *root; 122 json_t *root;
121 char *json_str; 123 char *json_str;
122 124
123 root = json_object (); 125 root = json_object ();
124 json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE)); 126 json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE_HMAC));
125 json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE)); 127 json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE));
126 128
127 json_str = json_dumps (root, JSON_INDENT (0) | JSON_COMPACT); 129 json_str = json_dumps (root, JSON_INDENT (0) | JSON_COMPACT);
@@ -356,40 +358,22 @@ OIDC_generate_userinfo (const struct GNUNET_IDENTITY_PublicKey *sub_key,
356} 358}
357 359
358 360
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 * 361char *
371OIDC_generate_id_token (const struct GNUNET_IDENTITY_PublicKey *aud_key, 362generate_id_token_body (const struct GNUNET_IDENTITY_PublicKey *aud_key,
372 const struct GNUNET_IDENTITY_PublicKey *sub_key, 363 const struct GNUNET_IDENTITY_PublicKey *sub_key,
373 const struct GNUNET_RECLAIM_AttributeList *attrs, 364 const struct GNUNET_RECLAIM_AttributeList *attrs,
374 const struct 365 const struct
375 GNUNET_RECLAIM_PresentationList *presentations, 366 GNUNET_RECLAIM_PresentationList *presentations,
376 const struct GNUNET_TIME_Relative *expiration_time, 367 const struct GNUNET_TIME_Relative *expiration_time,
377 const char *nonce, 368 const char *nonce)
378 const char *secret_key)
379{ 369{
380 struct GNUNET_HashCode signature; 370 struct GNUNET_HashCode signature;
381 struct GNUNET_TIME_Absolute exp_time; 371 struct GNUNET_TIME_Absolute exp_time;
382 struct GNUNET_TIME_Absolute time_now; 372 struct GNUNET_TIME_Absolute time_now;
373 json_t *body;
383 char *audience; 374 char *audience;
384 char *subject; 375 char *subject;
385 char *header;
386 char *body_str; 376 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 377
394 body = generate_userinfo_json (sub_key, 378 body = generate_userinfo_json (sub_key,
395 attrs, 379 attrs,
@@ -409,7 +393,6 @@ OIDC_generate_id_token (const struct GNUNET_IDENTITY_PublicKey *aud_key,
409 GNUNET_STRINGS_data_to_string_alloc (aud_key, 393 GNUNET_STRINGS_data_to_string_alloc (aud_key,
410 sizeof(struct 394 sizeof(struct
411 GNUNET_IDENTITY_PublicKey)); 395 GNUNET_IDENTITY_PublicKey));
412 header = create_jwt_header ();
413 396
414 // aud REQUIRED public key client_id must be there 397 // aud REQUIRED public key client_id must be there
415 json_object_set_new (body, "aud", json_string (audience)); 398 json_object_set_new (body, "aud", json_string (audience));
@@ -429,19 +412,132 @@ OIDC_generate_id_token (const struct GNUNET_IDENTITY_PublicKey *aud_key,
429 if (NULL != nonce) 412 if (NULL != nonce)
430 json_object_set_new (body, "nonce", json_string (nonce)); 413 json_object_set_new (body, "nonce", json_string (nonce));
431 414
432 body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT); 415 // Error checking
433 json_decref (body); 416 body_str = json_dumps (body, JSON_INDENT (2) | JSON_COMPACT);
434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"ID-Token: %s\n", body_str); 417 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"ID-Token: %s\n", body_str);
435 418
419 json_decref (body);
420 GNUNET_free (subject);
421 GNUNET_free (audience);
422
423 return body_str;
424}
425
426
427/**
428 * Create a JWT using RSA256 algorithm from attributes
429 *
430 * @param aud_key the public of the audience
431 * @param sub_key the public key of the subject
432 * @param attrs the attribute list
433 * @param presentations credential presentation list (may be empty)
434 * @param expiration_time the validity of the token
435 * @param secret_rsa_key the key used to sign the JWT
436 * @return a new base64-encoded JWT string.
437 */
438char *
439OIDC_generate_id_token_rsa (const struct GNUNET_IDENTITY_PublicKey *aud_key,
440 const struct GNUNET_IDENTITY_PublicKey *sub_key,
441 const struct GNUNET_RECLAIM_AttributeList *attrs,
442 const struct
443 GNUNET_RECLAIM_PresentationList *presentations,
444 const struct GNUNET_TIME_Relative *expiration_time,
445 const char *nonce,
446 const json_t *secret_rsa_key)
447{
448 json_t *jws;
449 char *body_str;
450 char *result;
451
452 // Generate the body of the JSON Web Signature
453 body_str = generate_id_token_body (aud_key,
454 sub_key,
455 attrs,
456 presentations,
457 expiration_time,
458 nonce);
459
460 if (! body_str)
461 {
462 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
463 "Body for the JWS could not be generated\n");
464 }
465
466 // Creating the JSON Web Signature.
467 jws = json_pack ("{s:o}", "payload",
468 jose_b64_enc (body_str, strlen (body_str)));
469
470 if (! jose_jws_sig (NULL, jws, NULL, secret_rsa_key))
471 {
472 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
473 "Signature generation failed\n");
474 }
475
476 // Encoding JSON as compact JSON Web Signature
477 GNUNET_asprintf (&result, "%s.%s.%s",
478 json_string_value (json_object_get (jws, "protected")),
479 json_string_value (json_object_get (jws, "payload")),
480 json_string_value (json_object_get (jws, "signature")) );
481
482 json_decref(jws);
483 GNUNET_free(body_str);
484 return result;
485}
486
487/**
488 * Create a JWT using HMAC (HS256) from attributes
489 *
490 * @param aud_key the public of the audience
491 * @param sub_key the public key of the subject
492 * @param attrs the attribute list
493 * @param presentations credential presentation list (may be empty)
494 * @param expiration_time the validity of the token
495 * @param secret_key the key used to sign the JWT
496 * @return a new base64-encoded JWT string.
497 */
498char *
499OIDC_generate_id_token_hmac (const struct GNUNET_IDENTITY_PublicKey *aud_key,
500 const struct GNUNET_IDENTITY_PublicKey *sub_key,
501 const struct GNUNET_RECLAIM_AttributeList *attrs,
502 const struct
503 GNUNET_RECLAIM_PresentationList *presentations,
504 const struct GNUNET_TIME_Relative *expiration_time,
505 const char *nonce,
506 const char *secret_key)
507{
508 struct GNUNET_HashCode signature;
509 struct GNUNET_TIME_Absolute exp_time;
510 struct GNUNET_TIME_Absolute time_now;
511 char *header;
512 char *header_base64;
513 char *body_str;
514 char *body_base64;
515 char *signature_target;
516 char *signature_base64;
517 char *result;
518
519 // Generate and encode Header
520 header = create_jwt_hmac_header ();
436 GNUNET_STRINGS_base64url_encode (header, strlen (header), &header_base64); 521 GNUNET_STRINGS_base64url_encode (header, strlen (header), &header_base64);
437 fix_base64 (header_base64); 522 fix_base64 (header_base64);
438 523
524 // Generate and encode the body of the JSON Web Signature
525 body_str = generate_id_token_body (aud_key,
526 sub_key,
527 attrs,
528 presentations,
529 expiration_time,
530 nonce);
531
532 if (! body_str)
533 {
534 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
535 "Body for the JWS could not be generated\n");
536 }
537
439 GNUNET_STRINGS_base64url_encode (body_str, strlen (body_str), &body_base64); 538 GNUNET_STRINGS_base64url_encode (body_str, strlen (body_str), &body_base64);
440 fix_base64 (body_base64); 539 fix_base64 (body_base64);
441 540
442 GNUNET_free (subject);
443 GNUNET_free (audience);
444
445 /** 541 /**
446 * Creating the JWT signature. This might not be 542 * Creating the JWT signature. This might not be
447 * standards compliant, check. 543 * standards compliant, check.
@@ -463,12 +559,12 @@ OIDC_generate_id_token (const struct GNUNET_IDENTITY_PublicKey *aud_key,
463 body_base64, 559 body_base64,
464 signature_base64); 560 signature_base64);
465 561
466 GNUNET_free (signature_target);
467 GNUNET_free (header); 562 GNUNET_free (header);
563 GNUNET_free (header_base64);
468 GNUNET_free (body_str); 564 GNUNET_free (body_str);
469 GNUNET_free (signature_base64);
470 GNUNET_free (body_base64); 565 GNUNET_free (body_base64);
471 GNUNET_free (header_base64); 566 GNUNET_free (signature_target);
567 GNUNET_free (signature_base64);
472 return result; 568 return result;
473} 569}
474 570
diff --git a/src/reclaim/oidc_helper.h b/src/reclaim/oidc_helper.h
index 2a8b7bbae..ea106b4f2 100644
--- a/src/reclaim/oidc_helper.h
+++ b/src/reclaim/oidc_helper.h
@@ -28,14 +28,12 @@
28#define JWT_H 28#define JWT_H
29 29
30#define JWT_ALG "alg" 30#define JWT_ALG "alg"
31
32/* Use 512bit HMAC */
33#define JWT_ALG_VALUE "HS512"
34
35#define JWT_TYP "typ" 31#define JWT_TYP "typ"
36
37#define JWT_TYP_VALUE "jwt" 32#define JWT_TYP_VALUE "jwt"
38 33
34#define JWT_ALG_VALUE_HMAC "HS512"
35#define JWT_ALG_VALUE_RSA "RS256"
36
39#define SERVER_ADDRESS "https://api.reclaim" 37#define SERVER_ADDRESS "https://api.reclaim"
40 38
41enum OIDC_VerificationOptions 39enum OIDC_VerificationOptions
@@ -52,7 +50,28 @@ enum OIDC_VerificationOptions
52}; 50};
53 51
54/** 52/**
55 * Create a JWT from attributes 53 * Create a JWT using RSA256 from attributes
54 *
55 * @param aud_key the public of the audience
56 * @param sub_key the public key of the subject
57 * @param attrs the attribute list
58 * @param presentations credential presentation list (may be empty)
59 * @param expiration_time the validity of the token
60 * @param secret_key the key used to sign the JWT
61 * @return a new base64-encoded JWT string.
62 */
63char *
64OIDC_generate_id_token_rsa (const struct GNUNET_IDENTITY_PublicKey *aud_key,
65 const struct GNUNET_IDENTITY_PublicKey *sub_key,
66 const struct GNUNET_RECLAIM_AttributeList *attrs,
67 const struct
68 GNUNET_RECLAIM_PresentationList *presentations,
69 const struct GNUNET_TIME_Relative *expiration_time,
70 const char *nonce,
71 const json_t *secret_rsa_key);
72
73/**
74 * Create a JWT using HMAC (HS256) from attributes
56 * 75 *
57 * @param aud_key the public of the audience 76 * @param aud_key the public of the audience
58 * @param sub_key the public key of the subject 77 * @param sub_key the public key of the subject
@@ -63,14 +82,14 @@ enum OIDC_VerificationOptions
63 * @return a new base64-encoded JWT string. 82 * @return a new base64-encoded JWT string.
64 */ 83 */
65char* 84char*
66OIDC_generate_id_token (const struct GNUNET_IDENTITY_PublicKey *aud_key, 85OIDC_generate_id_token_hmac (const struct GNUNET_IDENTITY_PublicKey *aud_key,
67 const struct GNUNET_IDENTITY_PublicKey *sub_key, 86 const struct GNUNET_IDENTITY_PublicKey *sub_key,
68 const struct GNUNET_RECLAIM_AttributeList *attrs, 87 const struct GNUNET_RECLAIM_AttributeList *attrs,
69 const struct 88 const struct
70 GNUNET_RECLAIM_PresentationList *presentations, 89 GNUNET_RECLAIM_PresentationList *presentations,
71 const struct GNUNET_TIME_Relative *expiration_time, 90 const struct GNUNET_TIME_Relative *expiration_time,
72 const char *nonce, 91 const char *nonce,
73 const char *secret_key); 92 const char *secret_key);
74 93
75/** 94/**
76 * Builds an OIDC authorization code including 95 * Builds an OIDC authorization code including
diff --git a/src/reclaim/plugin_rest_openid_connect.c b/src/reclaim/plugin_rest_openid_connect.c
index 1c00abcbd..bb8e1cd1e 100644
--- a/src/reclaim/plugin_rest_openid_connect.c
+++ b/src/reclaim/plugin_rest_openid_connect.c
@@ -20,6 +20,7 @@
20/** 20/**
21 * @author Martin Schanzenbach 21 * @author Martin Schanzenbach
22 * @author Philippe Buschmann 22 * @author Philippe Buschmann
23 * @author Tristan Schwieren
23 * @file identity/plugin_rest_openid_connect.c 24 * @file identity/plugin_rest_openid_connect.c
24 * @brief GNUnet Namestore REST plugin 25 * @brief GNUnet Namestore REST plugin
25 * 26 *
@@ -27,6 +28,7 @@
27#include "platform.h" 28#include "platform.h"
28#include <inttypes.h> 29#include <inttypes.h>
29#include <jansson.h> 30#include <jansson.h>
31#include <jose/jose.h>
30 32
31#include "gnunet_buffer_lib.h" 33#include "gnunet_buffer_lib.h"
32#include "gnunet_strings_lib.h" 34#include "gnunet_strings_lib.h"
@@ -63,6 +65,11 @@
63#define GNUNET_REST_API_NS_TOKEN "/openid/token" 65#define GNUNET_REST_API_NS_TOKEN "/openid/token"
64 66
65/** 67/**
68 * JSON Web Keys endpoint
69 */
70#define GNUNET_REST_API_JWKS "/jwks.json"
71
72/**
66 * UserInfo endpoint 73 * UserInfo endpoint
67 */ 74 */
68#define GNUNET_REST_API_NS_USERINFO "/openid/userinfo" 75#define GNUNET_REST_API_NS_USERINFO "/openid/userinfo"
@@ -228,6 +235,11 @@
228#define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied" 235#define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied"
229 236
230/** 237/**
238 * OIDC key store file name
239 */
240#define OIDC_JWK_RSA_FILENAME "jwk_rsa.json"
241
242/**
231 * How long to wait for a consume in userinfo endpoint 243 * How long to wait for a consume in userinfo endpoint
232 */ 244 */
233#define CONSUME_TIMEOUT GNUNET_TIME_relative_multiply ( \ 245#define CONSUME_TIMEOUT GNUNET_TIME_relative_multiply ( \
@@ -303,6 +315,11 @@ struct Plugin
303}; 315};
304 316
305/** 317/**
318 * @brief The RSA key used by the oidc enpoint
319 */
320json_t *oidc_jwk;
321
322/**
306 * OIDC needed variables 323 * OIDC needed variables
307 */ 324 */
308struct OIDC_Variables 325struct OIDC_Variables
@@ -859,6 +876,103 @@ cookie_identity_interpretation (struct RequestHandle *handle)
859 876
860 877
861/** 878/**
879 * @brief Read the the JSON Web Key in the given file and return it.
880 * Return NULL and emit warning if JSON can not be decoded or the key is
881 * invalid
882 *
883 * @param filename the file to read the JWK from
884 * @return json_t* the reed JWK
885 */
886json_t *
887read_jwk_from_file (const char *filename)
888{
889 json_t *jwk;
890 json_error_t error;
891
892 jwk = json_load_file (filename, JSON_DECODE_ANY, &error);
893
894 if (! jwk)
895 {
896 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
897 ("Could not read OIDC RSA key from config file; %s\n"),
898 error.text);
899 }
900
901 return jwk;
902}
903
904/**
905 * @brief Write the JWK to file. If unsuccessful emit warning
906 *
907 * @param filename the name of the file the JWK is writen to
908 * @param jwk the JWK that is going to be written
909 * @return int Return GNUNET_OK if write is sucessfull
910 */
911static int
912write_jwk_to_file (const char *filename,
913 json_t *jwk)
914{
915 if (json_dump_file (jwk, filename, JSON_INDENT (2)))
916 {
917 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
918 ("Could not write OIDC RSA key to file %s\n"),
919 filename);
920 return GNUNET_ERROR_TYPE_WARNING;
921 }
922 else
923 return GNUNET_OK;
924}
925
926/**
927 * @brief Generate a new RSA JSON Web Key
928 *
929 * @return json_t* the generated JWK
930 */
931json_t *
932generate_jwk ()
933{
934 json_t *jwk;
935 jwk = json_pack ("{s:s,s:i}", "kty", "RSA", "bits", 2048);
936 jose_jwk_gen (NULL, jwk);
937 json_incref (jwk);
938 return jwk;
939}
940
941/**
942 * Return the path to the RSA JWK key file
943 *
944 * @param cls the RequestHandle
945 */
946char *
947get_oidc_jwk_path (void *cls)
948{
949 char *oidc_directory;
950 char *oidc_jwk_path;
951 struct RequestHandle *handle = cls;
952
953 // Read OIDC directory from config
954 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
955 "reclaim-rest-plugin",
956 "oidc_dir",
957 &oidc_directory))
958 {
959 // Could not read Config file
960 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
961 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
962 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
963 GNUNET_SCHEDULER_add_now (&do_error, handle);
964 return NULL;
965 }
966
967 // Create path to file
968 GNUNET_asprintf (&oidc_jwk_path, "%s/%s", oidc_directory,
969 OIDC_JWK_RSA_FILENAME);
970
971 return oidc_jwk_path;
972}
973
974
975/**
862 * Redirects to login page stored in configuration file 976 * Redirects to login page stored in configuration file
863 */ 977 */
864static void 978static void
@@ -1954,7 +2068,7 @@ check_authorization (struct RequestHandle *handle,
1954 // check client password 2068 // check client password
1955 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, 2069 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1956 "reclaim-rest-plugin", 2070 "reclaim-rest-plugin",
1957 "OIDC_CLIENT_SECRET", 2071 "OIDC_CLIENT_HMAC_SECRET",
1958 &expected_pass)) 2072 &expected_pass))
1959 { 2073 {
1960 if (0 != strcmp (expected_pass, received_cpw)) 2074 if (0 != strcmp (expected_pass, received_cpw))
@@ -2047,9 +2161,12 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2047 char *json_response; 2161 char *json_response;
2048 char *id_token; 2162 char *id_token;
2049 char *access_token; 2163 char *access_token;
2164 char *jwa;
2050 char *jwt_secret; 2165 char *jwt_secret;
2051 char *nonce = NULL; 2166 char *nonce = NULL;
2052 char *code_verifier; 2167 char *code_verifier;
2168 json_t *oidc_jwk;
2169 char *oidc_jwk_path;
2053 2170
2054 /* 2171 /*
2055 * Check Authorization 2172 * Check Authorization
@@ -2156,32 +2273,66 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2156 return; 2273 return;
2157 } 2274 }
2158 2275
2159 2276 // Check if HMAC or RSA should be used
2160 // TODO OPTIONAL acr,amr,azp
2161 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, 2277 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2162 "reclaim-rest-plugin", 2278 "reclaim-rest-plugin",
2163 "jwt_secret", 2279 "oidc_json_web_algorithm",
2164 &jwt_secret)) 2280 &jwa))
2165 { 2281 {
2166 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); 2282 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2167 handle->edesc = GNUNET_strdup ("No signing secret configured!"); 2283 "Could not read OIDC JSON Web Algorithm config attribute."
2168 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 2284 "Defaulting to RS256.");
2169 GNUNET_free (code); 2285 jwa = JWT_ALG_VALUE_RSA;
2170 GNUNET_RECLAIM_attribute_list_destroy (cl); 2286 }
2171 GNUNET_RECLAIM_presentation_list_destroy (pl); 2287
2172 if (NULL != nonce) 2288 if (strcmp(jwa, JWT_ALG_VALUE_RSA))
2173 GNUNET_free (nonce); 2289 {
2174 GNUNET_SCHEDULER_add_now (&do_error, handle); 2290 // Replace for now
2175 return; 2291 oidc_jwk_path = get_oidc_jwk_path (cls);
2292 oidc_jwk = read_jwk_from_file (oidc_jwk_path);
2293 id_token = OIDC_generate_id_token_rsa (&ticket.audience,
2294 &ticket.identity,
2295 cl,
2296 pl,
2297 &expiration_time,
2298 (NULL != nonce) ? nonce : NULL,
2299 oidc_jwk);
2300 }
2301 else if (strcmp(jwa, JWT_ALG_VALUE_HMAC))
2302 {
2303 // TODO OPTIONAL acr,amr,azp
2304 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2305 "reclaim-rest-plugin",
2306 "jwt_secret",
2307 &jwt_secret))
2308 {
2309 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2310 handle->edesc = GNUNET_strdup ("No signing secret configured!");
2311 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2312 GNUNET_free (code);
2313 GNUNET_RECLAIM_attribute_list_destroy (cl);
2314 GNUNET_RECLAIM_presentation_list_destroy (pl);
2315 if (NULL != nonce)
2316 GNUNET_free (nonce);
2317 GNUNET_SCHEDULER_add_now (&do_error, handle);
2318 return;
2319 }
2320
2321 id_token = OIDC_generate_id_token_hmac (&ticket.audience,
2322 &ticket.identity,
2323 cl,
2324 pl,
2325 &expiration_time,
2326 (NULL != nonce) ? nonce : NULL,
2327 jwt_secret);
2328
2329 GNUNET_free (jwt_secret);
2330 }
2331 else
2332 {
2333 // TODO: OPTION NOT FOUND ERROR
2176 } 2334 }
2177 id_token = OIDC_generate_id_token (&ticket.audience, 2335
2178 &ticket.identity,
2179 cl,
2180 pl,
2181 &expiration_time,
2182 (NULL != nonce) ? nonce : NULL,
2183 jwt_secret);
2184 GNUNET_free (jwt_secret);
2185 if (NULL != nonce) 2336 if (NULL != nonce)
2186 GNUNET_free (nonce); 2337 GNUNET_free (nonce);
2187 access_token = OIDC_access_token_new (&ticket); 2338 access_token = OIDC_access_token_new (&ticket);
@@ -2474,6 +2625,59 @@ userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2474 GNUNET_free (authorization); 2625 GNUNET_free (authorization);
2475} 2626}
2476 2627
2628/**
2629 * Responds to /jwks.json
2630 *
2631 * @param con_handle the connection handle
2632 * @param url the url
2633 * @param cls the RequestHandle
2634 */
2635static void
2636jwks_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2637 const char *url,
2638 void *cls)
2639{
2640 char *oidc_directory;
2641 char *oidc_jwk_path;
2642 char *oidc_jwk_pub_str;
2643 json_t *oidc_jwk;
2644 struct MHD_Response *resp;
2645 struct RequestHandle *handle = cls;
2646
2647 oidc_jwk_path = get_oidc_jwk_path (cls);
2648 oidc_jwk = read_jwk_from_file (oidc_jwk_path);
2649
2650 // Check if secret JWK exists
2651 if (! oidc_jwk)
2652 {
2653 // Generate and save a new key
2654 oidc_jwk = generate_jwk ();
2655
2656 // Create new oidc directory
2657 if (GNUNET_OK != GNUNET_DISK_directory_create (oidc_directory))
2658 {
2659 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2660 ("Failed to create directory `%s' for storing oidc data\n"),
2661 oidc_directory);
2662 }
2663 else
2664 {
2665 write_jwk_to_file (oidc_jwk_path, oidc_jwk);
2666 }
2667 }
2668
2669 // Convert secret JWK to public JWK
2670 jose_jwk_pub (NULL, oidc_jwk);
2671
2672 // Encode JWK as string and return to API endpoint
2673 oidc_jwk_pub_str = json_dumps (oidc_jwk, JSON_INDENT (1));
2674 resp = GNUNET_REST_create_response (oidc_jwk_pub_str);
2675 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2676 json_decref (oidc_jwk);
2677 GNUNET_free (oidc_jwk_pub_str);
2678 free (oidc_jwk_pub_str);
2679 cleanup_handle (handle);
2680}
2477 2681
2478/** 2682/**
2479 * If listing is enabled, prints information about the egos. 2683 * If listing is enabled, prints information about the egos.
@@ -2621,10 +2825,15 @@ oidc_config_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2621 sig_algs = json_array (); 2825 sig_algs = json_array ();
2622 json_array_append_new (sig_algs, 2826 json_array_append_new (sig_algs,
2623 json_string ("HS512")); 2827 json_string ("HS512"));
2828 json_array_append_new (sig_algs,
2829 json_string ("RS256"));
2624 json_object_set_new (oidc_config, 2830 json_object_set_new (oidc_config,
2625 "id_token_signing_alg_values_supported", 2831 "id_token_signing_alg_values_supported",
2626 sig_algs); 2832 sig_algs);
2627 json_object_set_new (oidc_config, 2833 json_object_set_new (oidc_config,
2834 "jwks_uri",
2835 json_string ("http://localhost:7776/jwks.json"));
2836 json_object_set_new (oidc_config,
2628 "userinfo_endpoint", 2837 "userinfo_endpoint",
2629 json_string ("http://localhost:7776/openid/userinfo")); 2838 json_string ("http://localhost:7776/openid/userinfo"));
2630 scopes = json_array (); 2839 scopes = json_array ();
@@ -2719,6 +2928,7 @@ rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
2719 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint }, 2928 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
2720 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint }, 2929 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2721 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint }, 2930 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2931 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_JWKS, &jwks_endpoint },
2722 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_OIDC_CONFIG, 2932 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_OIDC_CONFIG,
2723 &oidc_config_endpoint }, 2933 &oidc_config_endpoint },
2724 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC_CONFIG, 2934 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC_CONFIG,
diff --git a/src/reclaim/reclaim.conf b/src/reclaim/reclaim.conf
index 8655f2e0b..c685042db 100644
--- a/src/reclaim/reclaim.conf
+++ b/src/reclaim/reclaim.conf
@@ -14,6 +14,8 @@ TICKET_REFRESH_INTERVAL = 6h
14[reclaim-rest-plugin] 14[reclaim-rest-plugin]
15#ADDRESS = https://identity.gnu:8000#/login 15#ADDRESS = https://identity.gnu:8000#/login
16ADDRESS = https://ui.reclaim/#/login 16ADDRESS = https://ui.reclaim/#/login
17OIDC_CLIENT_SECRET = secret 17OIDC_JSON_WEB_ALGORITHM = RS256
18OIDC_CLIENT_HMAC_SECRET = secret
19OIDC_DIR = $GNUNET_DATA_HOME/oidc
18JWT_SECRET = secret 20JWT_SECRET = secret
19EXPIRATION_TIME = 1d 21EXPIRATION_TIME = 1d