diff options
author | Tristan Schwieren <tristan.schwieren@tum.de> | 2022-04-11 13:29:20 +0200 |
---|---|---|
committer | Martin Schanzenbach <schanzen@gnunet.org> | 2022-06-09 11:43:01 +0200 |
commit | 349b4e37123368e3f03d563770d72bcbfd8109ad (patch) | |
tree | 865732d15ee62fa5c7e5d651445ea276a4d4756d | |
parent | 79eff4349f89d13a8cc707550f6bb7d5fe8c99c3 (diff) | |
download | gnunet-349b4e37123368e3f03d563770d72bcbfd8109ad.tar.gz gnunet-349b4e37123368e3f03d563770d72bcbfd8109ad.zip |
-init oidc RSA256 feature
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | src/reclaim/Makefile.am | 2 | ||||
-rw-r--r-- | src/reclaim/oidc_helper.c | 160 | ||||
-rw-r--r-- | src/reclaim/oidc_helper.h | 47 | ||||
-rw-r--r-- | src/reclaim/plugin_rest_openid_connect.c | 256 | ||||
-rw-r--r-- | src/reclaim/reclaim.conf | 4 |
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]) | |||
719 | AS_IF([test "x$jansson" = "x0"], | 719 | AS_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 | ||
723 | CHECK_WITH_LIB([jose], [jose_jwk_gen], [jose/jose.h], [HAVE_JOSE]) | ||
724 | AS_IF([test "x$jose" = "x0"], | ||
725 | [AC_MSG_ERROR([GNUnet requires jose])]) | ||
726 | |||
722 | # check for libpulse (pulseaudio) | 727 | # check for libpulse (pulseaudio) |
723 | CHECK_WITH_LIB([pulse], [pa_stream_peek], [pulse/simple.h], [HAVE_PULSE]) | 728 | CHECK_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) |
84 | libgnunet_plugin_rest_openid_connect_la_LDFLAGS = \ | 84 | libgnunet_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 | ||
117 | static char * | 119 | static char * |
118 | create_jwt_header (void) | 120 | create_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 | */ | ||
370 | char * | 361 | char * |
371 | OIDC_generate_id_token (const struct GNUNET_IDENTITY_PublicKey *aud_key, | 362 | generate_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 | */ | ||
438 | char * | ||
439 | OIDC_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 | */ | ||
498 | char * | ||
499 | OIDC_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 | ||
41 | enum OIDC_VerificationOptions | 39 | enum 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 | */ | ||
63 | char * | ||
64 | OIDC_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 | */ |
65 | char* | 84 | char* |
66 | OIDC_generate_id_token (const struct GNUNET_IDENTITY_PublicKey *aud_key, | 85 | OIDC_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 | */ | ||
320 | json_t *oidc_jwk; | ||
321 | |||
322 | /** | ||
306 | * OIDC needed variables | 323 | * OIDC needed variables |
307 | */ | 324 | */ |
308 | struct OIDC_Variables | 325 | struct 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 | */ | ||
886 | json_t * | ||
887 | read_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 | */ | ||
911 | static int | ||
912 | write_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 | */ | ||
931 | json_t * | ||
932 | generate_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 | */ | ||
946 | char * | ||
947 | get_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 | */ |
864 | static void | 978 | static 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 | */ | ||
2635 | static void | ||
2636 | jwks_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 |
16 | ADDRESS = https://ui.reclaim/#/login | 16 | ADDRESS = https://ui.reclaim/#/login |
17 | OIDC_CLIENT_SECRET = secret | 17 | OIDC_JSON_WEB_ALGORITHM = RS256 |
18 | OIDC_CLIENT_HMAC_SECRET = secret | ||
19 | OIDC_DIR = $GNUNET_DATA_HOME/oidc | ||
18 | JWT_SECRET = secret | 20 | JWT_SECRET = secret |
19 | EXPIRATION_TIME = 1d | 21 | EXPIRATION_TIME = 1d |