From de8c2a206a6e1fe92cf01be548b6069e7e78e679 Mon Sep 17 00:00:00 2001 From: Alexia Pagkopoulou Date: Wed, 4 Sep 2019 15:27:54 +0200 Subject: support for PKCE extension --- src/reclaim/Makefile.am | 3 +- src/reclaim/oidc_helper.c | 179 +++++++++++++++++++++---------- src/reclaim/oidc_helper.h | 8 +- src/reclaim/plugin_rest_openid_connect.c | 55 +++++++++- 4 files changed, 183 insertions(+), 62 deletions(-) (limited to 'src/reclaim') diff --git a/src/reclaim/Makefile.am b/src/reclaim/Makefile.am index 6937b1af6..69dcc605e 100644 --- a/src/reclaim/Makefile.am +++ b/src/reclaim/Makefile.am @@ -77,7 +77,8 @@ libgnunet_plugin_rest_openid_connect_la_LIBADD = \ $(top_builddir)/src/gns/libgnunetgns.la \ $(top_builddir)/src/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \ - $(LTLIBINTL) -ljansson $(MHD_LIBS) + $(LTLIBINTL) -ljansson $(MHD_LIBS) \ + $(LIBGCRYPT_LIBS) libgnunet_plugin_rest_openid_connect_la_LDFLAGS = \ $(GN_PLUGIN_LDFLAGS) libgnunet_plugin_rest_openid_connect_la_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS) diff --git a/src/reclaim/oidc_helper.c b/src/reclaim/oidc_helper.c index d2789f978..01a3d0179 100644 --- a/src/reclaim/oidc_helper.c +++ b/src/reclaim/oidc_helper.c @@ -31,7 +31,48 @@ #include "gnunet_reclaim_service.h" #include "gnunet_signatures.h" #include "oidc_helper.h" +//#include "benchmark.h" +#include +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * The signature used to generate the authorization code + */ +struct OIDC_Parameters +{ + /** + * The reclaim ticket + */ + const struct GNUNET_RECLAIM_Ticket *ticket; + + /** + * The nonce + */ + uint32_t nonce GNUNET_PACKED; + + /** + * The length of the PKCE code_challenge + */ + uint16_t code_challenge_len GNUNET_PACKED; + + /** + * The length of the attributes list + */ + uint16_t attr_list_len GNUNET_PACKED; + + /** + * The PKCE code_challenge + */ + const char *code_challenge; + + /** + * The (serialized) attributes + */ + char *attrs_ser; +}; + +GNUNET_NETWORK_STRUCT_END static char * create_jwt_header (void) @@ -371,30 +412,30 @@ encrypt_payload (const struct GNUNET_CRYPTO_EcdsaPublicKey *ecdsa_pub, GNUNET_CRYPTO_symmetric_encrypt (payload, payload_len, &key, &iv, buf)); } - /** * Builds an OIDC authorization code including * a reclaim ticket and nonce * * @param issuer the issuer of the ticket, used to sign the ticket and nonce * @param ticket the ticket to include in the code - * @param attrs list of attributes whicha re shared + * @param attrs list of attributes which are shared * @param nonce the nonce to include in the code + * @param code_challenge PKCE code challenge * @return a new authorization code (caller must free) */ char * OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, const struct GNUNET_RECLAIM_Ticket *ticket, struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs, - const char *nonce_str) + const char *nonce_str, + const char *code_challenge) { + struct OIDC_Parameters *params = GNUNET_new (struct OIDC_Parameters); char *code_payload; char *plaintext; - char *attrs_ser; char *code_str; - char *buf_ptr; + char *buf_ptr = NULL; size_t signature_payload_len; - size_t attr_list_len; size_t code_payload_len; uint32_t nonce; uint32_t nonce_tmp; @@ -402,61 +443,67 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_priv; struct GNUNET_CRYPTO_EcdhePublicKey ecdh_pub; - attrs_ser = NULL; - signature_payload_len = - sizeof (struct GNUNET_RECLAIM_Ticket) + sizeof (uint32_t); - - if (NULL != attrs) - { - attr_list_len = GNUNET_RECLAIM_ATTRIBUTE_list_serialize_get_size (attrs); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Length of serialized attributes: %lu\n", - attr_list_len); - signature_payload_len += attr_list_len; - attrs_ser = GNUNET_malloc (attr_list_len); - GNUNET_RECLAIM_ATTRIBUTE_list_serialize (attrs, attrs_ser); - } - - code_payload_len = sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + - sizeof (struct GNUNET_CRYPTO_EcdhePublicKey) + - signature_payload_len + - sizeof (struct GNUNET_CRYPTO_EcdsaSignature); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Length of data to encode: %lu\n", - code_payload_len); - plaintext = GNUNET_malloc (signature_payload_len); - // First, copy ticket - buf_ptr = plaintext; - memcpy (buf_ptr, ticket, sizeof (struct GNUNET_RECLAIM_Ticket)); - buf_ptr += sizeof (struct GNUNET_RECLAIM_Ticket); - - // Then copy nonce + /** PLAINTEXT **/ + // Assign ticket + params->ticket = ticket; + // Assign nonce nonce = 0; if (NULL != nonce_str && strcmp("", nonce_str) != 0) { if ((1 != SSCANF (nonce_str, "%u", &nonce)) || (nonce > UINT32_MAX)) - { + { GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid nonce %s\n", nonce_str); - GNUNET_free (plaintext); - GNUNET_free_non_null (attrs_ser); + GNUNET_free (params); return NULL; - } + } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got nonce: %u from %s\n", nonce, nonce_str); } nonce_tmp = htonl (nonce); - memcpy (buf_ptr, &nonce_tmp, sizeof (uint32_t)); - buf_ptr += sizeof (uint32_t); - - // Finally, attributes - if (NULL != attrs_ser) + params->nonce = nonce_tmp; + // Assign code challenge + if (NULL == code_challenge || strcmp("", code_challenge) == 0) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "PKCE: Code challenge missing"); + GNUNET_free (params); + return NULL; + } + params->code_challenge_len = strlen (code_challenge); + params->code_challenge = code_challenge; + // Assign attributes + params->attrs_ser = NULL; + if (NULL != attrs) { - memcpy (buf_ptr, attrs_ser, attr_list_len); - GNUNET_free (attrs_ser); + // Get length + params->attr_list_len = GNUNET_RECLAIM_ATTRIBUTE_list_serialize_get_size (attrs); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Length of serialized attributes: %lu\n", + params->attr_list_len); + // Get serialized attributes + params->attrs_ser = GNUNET_malloc (params->attr_list_len); + GNUNET_RECLAIM_ATTRIBUTE_list_serialize (attrs, params->attrs_ser); } + + // Get plaintext length + signature_payload_len = sizeof (struct OIDC_Parameters); + plaintext = GNUNET_malloc (signature_payload_len); + memcpy (plaintext, params, signature_payload_len); + /** END **/ + + /** ENCRYPT **/ + // Get length + code_payload_len = sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey) + + signature_payload_len + + sizeof (struct GNUNET_CRYPTO_EcdsaSignature); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Length of data to encode: %lu\n", + code_payload_len); + // Generate ECDH key ecdh_priv = GNUNET_CRYPTO_ecdhe_key_create (); GNUNET_CRYPTO_ecdhe_key_get_public (ecdh_priv, &ecdh_pub); @@ -479,6 +526,7 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, buf_ptr); GNUNET_free (ecdh_priv); GNUNET_free (plaintext); + GNUNET_free (params); buf_ptr += signature_payload_len; // Sign and store signature if (GNUNET_SYSERR == @@ -505,6 +553,7 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, * * @param audience the expected audience of the code * @param code the string representation of the code + * @param code_verfier PKCE code verifier * @param ticket where to store the ticket * @param attrs the attributes in the code * @param nonce where to store the nonce @@ -513,6 +562,7 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, int OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, const char *code, + const char *code_verifier, struct GNUNET_RECLAIM_Ticket *ticket, struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList **attrs, char **nonce_str) @@ -520,6 +570,7 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, char *code_payload; char *ptr; char *plaintext; + char *code_verifier_tmp; struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; struct GNUNET_CRYPTO_EcdsaSignature *signature; struct GNUNET_CRYPTO_EcdsaPublicKey ecdsa_pub; @@ -529,6 +580,7 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, size_t signature_offset; size_t plaintext_len; uint32_t nonce = 0; + struct OIDC_Parameters *params; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to decode `%s'\n", code); code_payload = NULL; @@ -558,19 +610,35 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, plaintext_len = attrs_ser_len - sizeof (struct GNUNET_CRYPTO_EcdsaSignature); plaintext = GNUNET_malloc (plaintext_len); decrypt_payload (ecdsa_priv, ecdh_pub, ptr, plaintext_len, plaintext); - ptr = plaintext; + //ptr = plaintext; + params = (struct OIDC_Parameters *) plaintext; + + // cmp code_challenge code_verifier + code_verifier_tmp = GNUNET_malloc (strlen (code_verifier)); + // hash code verifier + gcry_md_hash_buffer (GCRY_MD_SHA256, + code_verifier_tmp, + code_verifier, + strlen(code_verifier)); + // encode code verifier + code_verifier_tmp = base64_encode (code_verifier_tmp, strlen (code_verifier_tmp)); + + if (0 != strcmp (code_verifier_tmp, params->code_challenge)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid code verifier\n"); + GNUNET_free_non_null (code_payload); + GNUNET_free (code_verifier_tmp); + return GNUNET_SYSERR; + } + GNUNET_free (code_verifier_tmp); + // Ticket - *ticket = *((struct GNUNET_RECLAIM_Ticket *) ptr); - attrs_ser_len -= sizeof (struct GNUNET_RECLAIM_Ticket); - ptr += sizeof (struct GNUNET_RECLAIM_Ticket); + ticket = params->ticket; // Nonce - nonce = ntohl (*((uint32_t *) ptr)); + nonce = ntohl (params->nonce);//ntohl (*((uint32_t *) ptr)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got nonce: %u\n", nonce); - attrs_ser_len -= sizeof (uint32_t); - ptr += sizeof (uint32_t); // Attributes - attrs_ser_len -= sizeof (struct GNUNET_CRYPTO_EcdsaSignature); - *attrs = GNUNET_RECLAIM_ATTRIBUTE_list_deserialize (ptr, attrs_ser_len); + *attrs = GNUNET_RECLAIM_ATTRIBUTE_list_deserialize (params->attrs_ser, params->attr_list_len); // Signature signature_offset = code_payload_len - sizeof (struct GNUNET_CRYPTO_EcdsaSignature); @@ -656,3 +724,4 @@ OIDC_access_token_new () &access_token); return access_token; } + diff --git a/src/reclaim/oidc_helper.h b/src/reclaim/oidc_helper.h index 6c10a4ab0..10e43ea27 100644 --- a/src/reclaim/oidc_helper.h +++ b/src/reclaim/oidc_helper.h @@ -64,13 +64,15 @@ OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, * @param ticket the ticket to include in the code * @param attrs list of attributes to share * @param nonce the nonce to include in the code + * @param code_challenge PKCE code challenge * @return a new authorization code (caller must free) */ char* OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, const struct GNUNET_RECLAIM_Ticket *ticket, struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs, - const char* nonce); + const char *nonce, + const char *code_challenge); /** * Parse reclaim ticket and nonce from @@ -79,6 +81,7 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, * * @param ecdsa_priv the audience of the ticket * @param code the string representation of the code + * @param code_verfier PKCE code verifier * @param ticket where to store the ticket * @param attrs the attributes found in the code * @param nonce where to store the nonce @@ -86,7 +89,8 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, */ int OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, - const char* code, + const char *code, + const char *code_verifier, struct GNUNET_RECLAIM_Ticket *ticket, struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList **attrs, char **nonce); diff --git a/src/reclaim/plugin_rest_openid_connect.c b/src/reclaim/plugin_rest_openid_connect.c index 03e037261..a16e6592c 100644 --- a/src/reclaim/plugin_rest_openid_connect.c +++ b/src/reclaim/plugin_rest_openid_connect.c @@ -119,6 +119,16 @@ */ #define OIDC_NONCE_KEY "nonce" +/** + * OIDC PKCE code challenge + */ +#define OIDC_CODE_CHALLENGE_KEY "code_challenge" + +/** + * OIDC PKCE code verifier + */ +#define OIDC_CODE_VERIFIER_KEY "code_verifier" + /** * OIDC cookie expiration (in seconds) */ @@ -295,6 +305,16 @@ struct OIDC_Variables */ int user_cancelled; + /** + * The PKCE code_challenge + */ + char *code_challenge; + + /** + * The PKCE code_verifier + */ + char *code_verifier; + /** * The response JSON */ @@ -812,7 +832,7 @@ login_redirect (void *cls) &login_base_url)) { GNUNET_asprintf (&new_redirect, - "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s", + "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s", login_base_url, OIDC_RESPONSE_TYPE_KEY, handle->oidc->response_type, @@ -824,6 +844,8 @@ login_redirect (void *cls) handle->oidc->scope, OIDC_STATE_KEY, (NULL != handle->oidc->state) ? handle->oidc->state : "", + OIDC_CODE_CHALLENGE_KEY, + (NULL != handle->oidc->code_challenge) ? handle->oidc->code_challenge : "", OIDC_NONCE_KEY, (NULL != handle->oidc->nonce) ? handle->oidc->nonce : ""); resp = GNUNET_REST_create_response (""); @@ -885,7 +907,8 @@ oidc_ticket_issue_cb (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket) code_string = OIDC_build_authz_code (&handle->priv_key, &handle->ticket, handle->attr_list, - handle->oidc->nonce); + handle->oidc->nonce, + handle->oidc->code_challenge); if ((NULL != handle->redirect_prefix) && (NULL != handle->redirect_suffix) && (NULL != handle->tld)) { @@ -1382,6 +1405,17 @@ authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle, return; } + // REQUIRED value: code_challenge + handle->oidc->code_challenge = get_url_parameter_copy (handle, OIDC_CODE_CHALLENGE_KEY); + if (NULL == handle->oidc->code_challenge) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); + handle->edesc = GNUNET_strdup ("missing parameter code_challenge"); + handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id, strlen ( @@ -1666,7 +1700,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, char *access_token; char *jwt_secret; char *nonce; - + char *code_verifier; /* * Check Authorization */ @@ -1728,8 +1762,20 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, return; } privkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + + // REQUIRED code verifier + code_verifier = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY); + if (NULL == code_verifier) + { + handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); + handle->edesc = GNUNET_strdup ("missing parameter code_verifier"); + handle->response_code = MHD_HTTP_BAD_REQUEST; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + // decode code - if (GNUNET_OK != OIDC_parse_authz_code (privkey, code, &ticket, &cl, &nonce)) + if (GNUNET_OK != OIDC_parse_authz_code (privkey, code, code_verifier, &ticket, &cl, &nonce)) { handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); handle->edesc = GNUNET_strdup ("invalid code"); @@ -2003,6 +2049,7 @@ list_ego (void *cls, } GNUNET_assert (NULL != ego); if (ID_REST_STATE_INIT == handle->state) + { ego_entry = GNUNET_new (struct EgoEntry); GNUNET_IDENTITY_ego_get_public_key (ego, &pk); -- cgit v1.2.3