Merge branch 'identity_oidc'
[oweals/gnunet.git] / src / identity-provider / jwt.c
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
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 3, or (at your
8       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       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18       Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * @file identity-provider/jwt.c
23  * @brief helper library for JSON-Web-Tokens
24  * @author Martin Schanzenbach
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_signatures.h"
29 #include "gnunet_identity_attribute_lib.h"
30 #include <jansson.h>
31
32
33 #define JWT_ALG "alg"
34
35 /*TODO is this the correct way to define new algs? */
36 #define JWT_ALG_VALUE "urn:org:gnunet:jwt:alg:ecdsa:ed25519"
37
38 #define JWT_TYP "typ"
39
40 #define JWT_TYP_VALUE "jwt"
41
42 //TODO change server address
43 #define SERVER_ADDRESS "https://localhost"
44
45 static char*
46 create_jwt_header(void)
47 {
48   json_t *root;
49   char *json_str;
50
51   root = json_object ();
52   json_object_set_new (root, JWT_ALG, json_string (JWT_ALG_VALUE));
53   json_object_set_new (root, JWT_TYP, json_string (JWT_TYP_VALUE));
54
55   json_str = json_dumps (root, JSON_INDENT(1));
56   json_decref (root);
57   return json_str;
58 }
59
60 /**
61  * Create a JWT from attributes
62  *
63  * @param aud_key the public of the subject
64  * @param attrs the attribute list
65  * @param priv_key the key used to sign the JWT
66  * @return a new base64-encoded JWT string.
67  */
68 char*
69 jwt_create_from_list (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key,
70                                                 const struct GNUNET_IDENTITY_ATTRIBUTE_ClaimList *attrs,
71                                                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key)
72 {
73   struct GNUNET_IDENTITY_ATTRIBUTE_ClaimListEntry *le;
74   struct GNUNET_CRYPTO_EcdsaPublicKey sub_key;
75   struct GNUNET_CRYPTO_EcdsaSignature signature;
76   struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
77   char* audience;
78   char* subject;
79   char* header;
80   char* padding;
81   char* body_str;
82   char* result;
83   char* header_base64;
84   char* body_base64;
85   char* signature_target;
86   char* signature_base64;
87   char* attr_val_str;
88   json_t* body;
89
90   //exp REQUIRED time expired from config
91   //iat REQUIRED time now
92   //auth_time only if max_age
93   //nonce only if nonce
94   // OPTIONAL acr,amr,azp
95   GNUNET_CRYPTO_ecdsa_key_get_public (priv_key, &sub_key);
96   /* TODO maybe we should use a local identity here */
97   subject = GNUNET_STRINGS_data_to_string_alloc (&sub_key,
98                                                 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
99   audience = GNUNET_STRINGS_data_to_string_alloc (aud_key,
100                                                   sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
101   header = create_jwt_header ();
102   body = json_object ();
103   /* TODO who is the issuer? local IdP or subject ? See self-issued tokens? */
104   //iss REQUIRED case sensitive server uri with https
105   json_object_set_new (body,
106                        "iss", json_string (SERVER_ADDRESS));
107   //sub REQUIRED public key identity, not exceed 255 ASCII  length
108   json_object_set_new (body,
109                        "sub", json_string (subject));
110   /* TODO what should be in here exactly? */
111   //aud REQUIRED public key client_id must be there
112   json_object_set_new (body,
113                        "aud", json_string (audience));
114   for (le = attrs->list_head; NULL != le; le = le->next)
115   {
116     /**
117      * TODO here we should have a function that
118      * calls the Attribute plugins to create a
119      * json representation for its value
120      */
121     attr_val_str = GNUNET_IDENTITY_ATTRIBUTE_value_to_string (le->claim->type,
122                                                               le->claim->data,
123                                                               le->claim->data_size);
124     json_object_set_new (body,
125                          le->claim->name,
126                          json_string (attr_val_str));
127     GNUNET_free (attr_val_str);
128   }
129   body_str = json_dumps (body, JSON_INDENT(0));
130   json_decref (body);
131
132   GNUNET_STRINGS_base64_encode (header,
133                                 strlen (header),
134                                 &header_base64);
135   //Remove GNUNET padding of base64
136   padding = strtok(header_base64, "=");
137   while (NULL != padding)
138     padding = strtok(NULL, "=");
139
140   GNUNET_STRINGS_base64_encode (body_str,
141                                 strlen (body_str),
142                                 &body_base64);
143
144   //Remove GNUNET padding of base64
145   padding = strtok(body_base64, "=");
146   while (NULL != padding)
147     padding = strtok(NULL, "=");
148
149   GNUNET_free (subject);
150   GNUNET_free (audience);
151
152   /**
153    * TODO
154    * Creating the JWT signature. This might not be
155    * standards compliant, check.
156    */
157   GNUNET_asprintf (&signature_target, "%s,%s", header_base64, body_base64);
158
159   purpose =
160     GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
161                    strlen (signature_target));
162   purpose->size =
163     htonl (strlen (signature_target) + sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose));
164   purpose->purpose = htonl(GNUNET_SIGNATURE_PURPOSE_GNUID_TOKEN);
165   GNUNET_memcpy (&purpose[1], signature_target, strlen (signature_target));
166   if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_sign (priv_key,
167                                              purpose,
168                                              (struct GNUNET_CRYPTO_EcdsaSignature *)&signature))
169   {
170     GNUNET_free (signature_target);
171     GNUNET_free (body_str);
172     GNUNET_free (body_base64);
173     GNUNET_free (header_base64);
174     GNUNET_free (purpose);
175     return NULL;
176   }
177   GNUNET_STRINGS_base64_encode ((const char*)&signature,
178                                 sizeof (struct GNUNET_CRYPTO_EcdsaSignature),
179                                 &signature_base64);
180   GNUNET_asprintf (&result, "%s.%s.%s",
181                    header_base64, body_base64, signature_base64);
182
183   GNUNET_free (signature_target);
184   GNUNET_free (header);
185   GNUNET_free (body_str);
186   GNUNET_free (signature_base64);
187   GNUNET_free (body_base64);
188   GNUNET_free (header_base64);
189   GNUNET_free (purpose);
190   return result;
191 }