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