/* This file is part of GNUnet Copyright (C) 2010-2015 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ /** * @file reclaim/jwt.c * @brief helper library for JSON-Web-Tokens * @author Martin Schanzenbach */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_signatures.h" #include "gnunet_reclaim_attribute_lib.h" #include #define JWT_ALG "alg" /* Use 512bit HMAC */ #define JWT_ALG_VALUE "HS512" #define JWT_TYP "typ" #define JWT_TYP_VALUE "jwt" #define SERVER_ADDRESS "https://reclaim.id" static char* create_jwt_header(void) { json_t *root; char *json_str; root = json_object (); json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE)); json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE)); json_str = json_dumps (root, JSON_INDENT(0) | JSON_COMPACT); json_decref (root); return json_str; } static void replace_char(char* str, char find, char replace){ char *current_pos = strchr(str,find); while (current_pos){ *current_pos = replace; current_pos = strchr(current_pos,find); } } //RFC4648 static void fix_base64(char* str) { char *padding; //First, remove trailing padding '=' padding = strtok(str, "="); while (NULL != padding) padding = strtok(NULL, "="); //Replace + with - replace_char (str, '+', '-'); //Replace / with _ replace_char (str, '/', '_'); } /** * Create a JWT from attributes * * @param aud_key the public of the audience * @param sub_key the public key of the subject * @param attrs the attribute list * @param expiration_time the validity of the token * @param secret_key the key used to sign the JWT * @return a new base64-encoded JWT string. */ char* jwt_create_from_list (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs, const struct GNUNET_TIME_Relative *expiration_time, const char *nonce, const char *secret_key) { struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le; struct GNUNET_HashCode signature; struct GNUNET_TIME_Absolute exp_time; struct GNUNET_TIME_Absolute time_now; char* audience; char* subject; char* header; char* body_str; char* result; char* header_base64; char* body_base64; char* signature_target; char* signature_base64; char* attr_val_str; json_t* body; //iat REQUIRED time now time_now = GNUNET_TIME_absolute_get(); //exp REQUIRED time expired from config exp_time = GNUNET_TIME_absolute_add (time_now, *expiration_time); //auth_time only if max_age //nonce only if nonce // OPTIONAL acr,amr,azp subject = GNUNET_STRINGS_data_to_string_alloc (&sub_key, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); audience = GNUNET_STRINGS_data_to_string_alloc (&aud_key, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); header = create_jwt_header (); body = json_object (); //iss REQUIRED case sensitive server uri with https //The issuer is the local reclaim instance (e.g. https://reclaim.id/api/openid) json_object_set_new (body, "iss", json_string (SERVER_ADDRESS)); //sub REQUIRED public key identity, not exceed 255 ASCII length json_object_set_new (body, "sub", json_string (subject)); //aud REQUIRED public key client_id must be there json_object_set_new (body, "aud", json_string (audience)); //iat json_object_set_new (body, "iat", json_integer (time_now.abs_value_us / (1000*1000))); //exp json_object_set_new (body, "exp", json_integer (exp_time.abs_value_us / (1000*1000))); //nbf json_object_set_new (body, "nbf", json_integer (time_now.abs_value_us / (1000*1000))); //nonce if (NULL != nonce) json_object_set_new (body, "nonce", json_string (nonce)); for (le = attrs->list_head; NULL != le; le = le->next) { attr_val_str = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (le->claim->type, le->claim->data, le->claim->data_size); json_object_set_new (body, le->claim->name, json_string (attr_val_str)); GNUNET_free (attr_val_str); } body_str = json_dumps (body, JSON_INDENT(0) | JSON_COMPACT); json_decref (body); GNUNET_STRINGS_base64_encode (header, strlen (header), &header_base64); fix_base64(header_base64); GNUNET_STRINGS_base64_encode (body_str, strlen (body_str), &body_base64); fix_base64(body_base64); GNUNET_free (subject); GNUNET_free (audience); /** * Creating the JWT signature. This might not be * standards compliant, check. */ GNUNET_asprintf (&signature_target, "%s.%s", header_base64, body_base64); GNUNET_CRYPTO_hmac_raw (secret_key, strlen (secret_key), signature_target, strlen (signature_target), &signature); GNUNET_STRINGS_base64_encode ((const char*)&signature, sizeof (struct GNUNET_HashCode), &signature_base64); fix_base64(signature_base64); GNUNET_asprintf (&result, "%s.%s.%s", header_base64, body_base64, signature_base64); GNUNET_free (signature_target); GNUNET_free (header); GNUNET_free (body_str); GNUNET_free (signature_base64); GNUNET_free (body_base64); GNUNET_free (header_base64); return result; }