jwt.c
Go to the documentation of this file.
1 /*!**********************************************************************
2  *
3  * @copyright Copyright (C) 2016 Siemens Aktiengesellschaft.\n
4  * All rights reserved.
5  *
6  *************************************************************************
7  *
8  * @file jwt.c
9  * @date Jun 28, 2016
10  * @brief JWT module implementation file.
11  *
12  ************************************************************************/
13 
14 #include "jwt.h"
15 #include "log_util.h"
16 #include "definitions.h"
17 #include "memory.h"
18 #include "string_array.h"
19 #include "time_util.h"
20 #include "json_util.h"
21 
22 // JWT Specific constants:
23 #define JWT_HEADER_NAME_ALG "alg"
24 #define JWT_HEADER_NAME_TYPE "typ"
25 #define JWT_HEADER_NAME_KID "kid"
26 
27 #define JWT_PAYLOAD_NAME_ISS "iss"
28 #define JWT_PAYLOAD_NAME_SUB "sub"
29 #define JWT_PAYLOAD_NAME_AUD "aud"
30 #define JWT_PAYLOAD_NAME_IAT "iat"
31 #define JWT_PAYLOAD_NAME_NBF "nbf"
32 #define JWT_PAYLOAD_NAME_EXP "exp"
33 #define JWT_PAYLOAD_NAME_SCHEMAS "schemas"
34 #define JWT_PAYLOAD_NAME_TEN "ten"
35 #define JWT_PAYLOAD_NAME_JTI "jti"
36 #define JWT_PAYLOAD_NAME_PUBLIC_KEY "public_key"
37 #define JWT_PAYLOAD_NAME_CONTENT_MD5 "content-md5"
38 
39 #define JWT_HEADER_VALUE_ALG_SHARED_SECRET "HS256"
40 #define JWT_HEADER_VALUE_ALG_RSA_3072 "RS256"
41 #define JWT_HEADER_VALUE_TYP "JWT"
42 
43 #define JWT_PAYLOAD_VALUE_SCHEMAS "urn:siemens:mindsphere:v1"
44 #define JWT_PAYLOAD_VALUE_AUD "southgate"
45 
46 // Creates header JSON object of JWT.
48 
49 // Creates payload JSON object of JWT.
51 
52 // to add schema array to payload of JWT.
54 
55 // encodes header and payload with base64. Clears the input strings if fails.
56 static E_MCL_ERROR_CODE _get_header_and_payload_encoded_base64_url(string_t *header, string_t *payload, string_t **header_encoded, string_t **payload_encoded);
57 
58 // join two string with dot (.) between and generate a new string while destroys the two string :
59 static E_MCL_ERROR_CODE _join_with_dot(string_t *string_a, string_t *string_b, string_t **joined);
60 
61 // calculates the signature from the header and payload string by calling the HMAC_SHA256 and encode_base64_url functions. NOT destroys the header and payload string!:
62 static E_MCL_ERROR_CODE _calculate_signature(jwt_t *jwt, string_t *header_and_payload, string_t **signature);
63 
64 // prepares the jwt, then calculates the necessary headers and signature and produces the token :
65 static E_MCL_ERROR_CODE _generate_token(jwt_t *jwt, string_t *header, string_t *payload, string_t **token);
66 
67 // this dot will be used every time a jwt is created. so instead of creating a new one in each call, define here once:
69 
70 E_MCL_ERROR_CODE jwt_initialize(security_handler_t *security_handler, E_MCL_SECURITY_PROFILE security_profile, string_t *tenant, jwt_t **jwt)
71 {
72  E_MCL_ERROR_CODE code;
73 
74  DEBUG_ENTRY("security_handler_t *security_handler = <%p>, E_MCL_SECURITY_PROFILE security_profile = <%d>, string_t *tenant = <%p>, jwt_t **jwt = <%p>", security_handler,
75  security_profile, tenant, jwt)
76 
77  MCL_NEW(*jwt);
78  ASSERT_CODE_MESSAGE(MCL_NULL != *jwt, MCL_OUT_OF_MEMORY, "Not enough memory to allocate jwt!");
79 
80  (*jwt)->header = MCL_NULL;
81  (*jwt)->payload = MCL_NULL;
82  (*jwt)->security_handler = security_handler;
83  (*jwt)->security_profile = security_profile;
84 
85  // Create JWT header.
86  code = _create_self_issued_jwt_header(security_profile, &((*jwt)->header));
87  ASSERT_STATEMENT_CODE_MESSAGE(MCL_OK == code, jwt_destroy(jwt), code, "Header of JWT can not be created.");
88 
89  // Create JWT payload.
90  code = _create_self_issued_jwt_payload(security_handler, tenant, *jwt);
91  ASSERT_STATEMENT_CODE_MESSAGE(MCL_OK == code, jwt_destroy(jwt), code, "Payload of JWT can not be created.");
92 
93  DEBUG_LEAVE("retVal = <%d>", MCL_OK);
94  return MCL_OK;
95 }
96 
98 {
99  DEBUG_ENTRY("jwt_t *jwt = <%p>", jwt)
100 
101  E_MCL_ERROR_CODE code;
102  string_t *token = MCL_NULL;
103  string_t *header = MCL_NULL;
104  string_t *payload = MCL_NULL;
105 
106  char *header_json = MCL_NULL;
107  char *payload_json = MCL_NULL;
108 
109  code = json_util_to_string(jwt->header, &header_json);
110  (MCL_OK == code) && (code = string_initialize_dynamic(header_json, 0, &header));
111 
112  (MCL_OK == code) && (code = json_util_to_string(jwt->payload, &payload_json));
113  (MCL_OK == code) && (code = string_initialize_dynamic(payload_json, 0, &payload));
114 
115  (MCL_OK == code) && _generate_token(jwt, header, payload, &token);
116 
117  // call reset() before go back to make it ready for the next call :
118  // TODO : jwt_reset(jwt);
119 
120  // clean up
121  string_destroy(&header);
122  string_destroy(&payload);
123 
124  DEBUG_LEAVE("retVal = <%p>", token);
125  return token;
126 }
127 
128 void jwt_destroy(jwt_t **jwt)
129 {
130  DEBUG_ENTRY("jwt_t **jwt = <%p>", jwt)
131 
132  if (MCL_NULL != *jwt)
133  {
134  if (MCL_NULL != (*jwt)->header)
135  {
136  json_util_destroy(&(*jwt)->header);
137  }
138 
139  if (MCL_NULL != (*jwt)->payload)
140  {
141  json_util_destroy(&(*jwt)->payload);
142  }
143 
144  MCL_FREE(*jwt);
145 
146  MCL_DEBUG("JWT handle is destroyed.");
147  }
148  else
149  {
150  MCL_DEBUG("JWT handle is already NULL.");
151  }
152 
153  DEBUG_LEAVE("retVal = void");
154 }
155 
156 // ------ Private Functions ---------------
157 
159 {
160  DEBUG_ENTRY("E_MCL_SECURITY_PROFILE security_profile = <%d>, json_t **header = <%p>", security_profile, header)
161 
163 
164  if (MCL_SECURITY_SHARED_SECRET == security_profile)
165  {
166  (MCL_OK == code) && (code = json_util_add_string(*header, JWT_HEADER_NAME_TYPE, JWT_HEADER_VALUE_TYP));
168  }
169  else
170  {
171  // it has to be MCL_SECURITY_RSA_3072
172  (MCL_OK == code) && (code = json_util_add_string(*header, JWT_HEADER_NAME_TYPE, JWT_HEADER_VALUE_TYP));
174 
175  // TODO: Add JWT_HEADER_NAME_KID to header
176  }
177 
178  if (MCL_OK != code)
179  {
180  json_util_destroy(header);
181  }
182 
183  DEBUG_LEAVE("retVal = <%d>", code);
184  return code;
185 }
186 
188 {
189  DEBUG_ENTRY("security_handler_t *security_handler = <%p>, string_t *tenant = <%p>, jwt_t *jwt = <%p>", security_handler, tenant, jwt)
190 
191  E_MCL_ERROR_CODE code;
192  mcl_time_t current_time;
193  string_t *jti;
194  json_t *payload = MCL_NULL;
195 
196  // Create JTI.
197  code = security_handler_generate_jti(&jti);
198  ASSERT_CODE_MESSAGE(MCL_OK == code, code, "Generate jti from security_handler failed!");
199 
200  // Initialize the payload json object.
201  code = json_util_initialize(JSON_OBJECT, &payload);
202  ASSERT_STATEMENT_CODE_MESSAGE(MCL_OK == code, string_destroy(&jti), code, "JSON payload can not be initialized.");
203 
204  // Get current time in seconds.
205  time_util_get_time(&current_time);
206 
207  // Fill payload json object.
208  code = json_util_add_string(payload, JWT_PAYLOAD_NAME_ISS, security_handler->client_id->buffer);
209  (MCL_OK == code) && (code = json_util_add_string(payload, JWT_PAYLOAD_NAME_SUB, security_handler->client_id->buffer));
211  (MCL_OK == code) && (code = json_util_add_double(payload, JWT_PAYLOAD_NAME_IAT, (double)current_time));
212  (MCL_OK == code) && (code = json_util_add_double(payload, JWT_PAYLOAD_NAME_NBF, (double)current_time));
213  (MCL_OK == code) && (code = json_util_add_double(payload, JWT_PAYLOAD_NAME_EXP, (double)(current_time + JWT_EXPIRATION_TIME)));
214  (MCL_OK == code) && (code = _add_schema_to_jwt(payload));
215  (MCL_OK == code) && (code = json_util_add_string(payload, JWT_PAYLOAD_NAME_TEN, tenant->buffer));
216  (MCL_OK == code) && (code = json_util_add_string(payload, JWT_PAYLOAD_NAME_JTI, jti->buffer));
217 
218  if (MCL_OK == code)
219  {
220  jwt->payload = payload;
221  jwt->issued_at = current_time;
222  }
223  else
224  {
225  json_util_destroy(&payload);
226  }
227  string_destroy(&jti);
228 
229  DEBUG_LEAVE("retVal = <%d>", code);
230  return code;
231 }
232 
233 static E_MCL_ERROR_CODE _generate_token(jwt_t *jwt, string_t *header, string_t *payload, string_t **token)
234 {
235  DEBUG_ENTRY("jwt_t *jwt = <%p>, string_t *header = <%p>, string_t *payload = <%p>, string_t **token = <%p>", jwt, header, payload, token)
236 
237  string_t *header_encoded;
238  string_t *payload_encoded;
239  string_t *header_and_payload = MCL_NULL;
240  string_t *signature = MCL_NULL;
241 
242  // 1- Encode Header and Payload to Base64 URL :
243  ASSERT_CODE_MESSAGE(MCL_OK == _get_header_and_payload_encoded_base64_url(header, payload, &header_encoded, &payload_encoded), MCL_FAIL, "Encode header - payload failed!");
244 
245  // 2- Join Header and Payload. Don't lose this joined header and payload. We will use it again :
246  ASSERT_CODE_MESSAGE(MCL_OK == _join_with_dot(header_encoded, payload_encoded, &header_and_payload), MCL_FAIL, "Join header and payload failed!");
247 
248  // 3- Calculate the Base64 URL encoded signature from header and payload joined together :
249  if (MCL_OK != _calculate_signature(jwt, header_and_payload, &signature))
250  {
251  string_destroy(&header_and_payload);
252  MCL_ERROR_RETURN(MCL_FAIL, "Generate signature failed!");
253  }
254 
255  // 4- Join signature with header and payload to get the final token :
256  ASSERT_CODE_MESSAGE(MCL_OK == _join_with_dot(header_and_payload, signature, token), MCL_FAIL, "Joining header and payload with signature failed!");
257 
258  DEBUG_LEAVE("retVal = <%d>", MCL_OK);
259  return MCL_OK;
260 }
261 
262 static E_MCL_ERROR_CODE _get_header_and_payload_encoded_base64_url(string_t *header, string_t *payload, string_t **header_encoded, string_t **payload_encoded)
263 {
264  DEBUG_ENTRY("string_t *header = <%p>, string_t *payload = <%p>, string_t **header_encoded = <%p>, string_t **payload_encoded = <%p>", header, payload, header_encoded,
265  payload_encoded)
266 
267  E_MCL_ERROR_CODE result = security_handler_base64_url_encode((mcl_uint8_t *)header->buffer, header->length, header_encoded);
268 
269  // header ok ?
270  ASSERT_CODE_MESSAGE(MCL_OK == result, MCL_FAIL, "Encode header failed!");
271 
272  result = security_handler_base64_url_encode((mcl_uint8_t *)payload->buffer, payload->length, payload_encoded);
273 
274  // payload ok ?
275  if (MCL_OK != result)
276  {
277  // header was ok. destroy it before go back
278  string_destroy(header_encoded);
279  MCL_ERROR_RETURN(MCL_FAIL, "Encode payload failed!");
280  }
281 
282  DEBUG_LEAVE("retVal = <%d>", MCL_OK);
283  return MCL_OK;
284 }
285 
286 static E_MCL_ERROR_CODE _join_with_dot(string_t *string_a, string_t *string_b, string_t **joined)
287 {
288  DEBUG_ENTRY("string_t *string_a = <%p>, string_t *string_b = <%p>, string_t **joined = <%p>", string_a, string_b, joined)
289 
290  string_array_t *string_array;
291 
292  // 3 is for header + . + token + . + sign :
293  E_MCL_ERROR_CODE code = string_array_initialize(3, &string_array);
294  if (MCL_OK != code)
295  {
296  string_destroy(&string_a);
297  string_destroy(&string_b);
298  MCL_ERROR_RETURN(code, "Initializing string array is failed!");
299  }
300 
301  string_array_add(string_array, string_a, MCL_FALSE);
302  string_array_add(string_array, &dot, MCL_FALSE);
303  string_array_add(string_array, string_b, MCL_FALSE);
304 
305  // to_string if index correct ( if add operations succeeded ) :
306  if (string_array->index == 3)
307  {
308  *joined = string_array_to_string(string_array);
309  }
310 
311  string_array_destroy(&string_array);
312  string_destroy(&string_a);
313  string_destroy(&string_b);
314 
315  // if *joined is NULL; either string_array_add failed or string_array_to_string :
316  ASSERT_CODE_MESSAGE(MCL_NULL != *joined, MCL_FAIL, "Strings couldn't be joined!");
317 
318  DEBUG_LEAVE("retVal = <%d>", MCL_OK);
319  return MCL_OK;
320 }
321 
322 static E_MCL_ERROR_CODE _calculate_signature(jwt_t *jwt, string_t *header_and_payload, string_t **signature)
323 {
324  DEBUG_ENTRY("jwt_t *jwt = <%p>, string_t *header_and_payload = <%p>, string_t **signature = <%p>", jwt, header_and_payload, signature)
325 
326  E_MCL_ERROR_CODE code;
327  mcl_uint8_t *hash = MCL_NULL;
328  mcl_size_t hash_size = 0;
329 
330  // calculate HMAC SHA256 of header and payload. security_handler will use the appropriate key to sign:
332  {
333  code = security_handler_hmac_sha256(jwt->security_handler, (mcl_uint8_t *)header_and_payload->buffer, header_and_payload->length, &hash, &hash_size);
334  }
335  else
336  {
337  code = security_handler_rsa_sign(jwt->security_handler->rsa.private_key, header_and_payload->buffer, header_and_payload->length, &hash, &hash_size);
338  }
339  ASSERT_CODE_MESSAGE(MCL_OK == code, MCL_FAIL, "Can not sign JWT!");
340 
341  // encode calculated signature :
342  code = security_handler_base64_url_encode(hash, hash_size, signature);
343 
344  // done with this :
345  MCL_FREE(hash);
346 
347  // now can be checked if encode ok ( don't do it before MCL_FREE above ! ) :
348  ASSERT_CODE_MESSAGE(MCL_OK == code, MCL_FAIL, "Encode calculated signature failed!");
349 
350  DEBUG_LEAVE("retVal = <%d>", MCL_OK);
351  return MCL_OK;
352 }
353 
355 {
356  DEBUG_ENTRY("json_t *payload = <%p>", payload)
357 
358  json_t *schemas_array;
359  E_MCL_ERROR_CODE code = json_util_start_array(payload, JWT_PAYLOAD_NAME_SCHEMAS, &schemas_array);
360  ASSERT_CODE_MESSAGE(MCL_OK == code, code, "Array for schema can not be created.");
361 
363  json_util_finish_array(&schemas_array);
364 
365  DEBUG_LEAVE("retVal = <%d>", code);
366  return code;
367 }
#define JWT_PAYLOAD_NAME_IAT
Definition: jwt.c:30
void string_destroy(string_t **string)
Destroys the allocated resources of the string.
Definition: string_type.c:326
E_MCL_ERROR_CODE string_array_add(string_array_t *array, string_t *string, mcl_bool_t destroy)
Adds an string_t string object into the array.
Definition: string_array.c:70
Internal failure in MCL.
Definition: mcl_common.h:141
E_MCL_ERROR_CODE security_handler_hmac_sha256(security_handler_t *security_handler, const mcl_uint8_t *data, mcl_size_t data_size, mcl_uint8_t **hash, mcl_size_t *hash_size)
#define JWT_HEADER_NAME_TYPE
Definition: jwt.c:24
Memory module header file.
#define DEBUG_LEAVE(...)
Definition: log_util.h:81
E_MCL_SECURITY_PROFILE
Definitions of different security profiles for onboarding.
Definition: mcl_common.h:78
Json util module header file.
#define DEBUG_ENTRY(...)
Definition: log_util.h:80
mcl_size_t index
Item index.
Definition: string_array.h:47
#define MCL_NEW(p)
Definition: memory.h:121
#define JWT_PAYLOAD_NAME_JTI
Definition: jwt.c:35
char * buffer
Buffer of string handle.
Definition: string_type.h:45
json_t * payload
Payload of jwt.
Definition: jwt.h:28
static E_MCL_ERROR_CODE _join_with_dot(string_t *string_a, string_t *string_b, string_t **joined)
Definition: jwt.c:286
#define JWT_PAYLOAD_NAME_NBF
Definition: jwt.c:31
E_MCL_ERROR_CODE security_handler_generate_jti(string_t **jti)
To be used to generate the jti nonce.
static E_MCL_ERROR_CODE _calculate_signature(jwt_t *jwt, string_t *header_and_payload, string_t **signature)
Definition: jwt.c:322
void string_array_destroy(string_array_t **array)
Destroys the string array handle.
Definition: string_array.c:174
mcl_time_t issued_at
Time of issue.
Definition: jwt.h:31
E_MCL_ERROR_CODE json_util_start_array(json_t *root, const char *array_name, json_t **json_array)
This function creates an array in root.
Definition: json_util.c:100
static E_MCL_ERROR_CODE _create_self_issued_jwt_header(E_MCL_SECURITY_PROFILE security_profile, json_t **header)
Definition: jwt.c:158
#define MCL_DEBUG(...)
Definition: log_util.h:70
#define MCL_FALSE
MCL bool type.
Definition: mcl_common.h:53
E_MCL_ERROR_CODE security_handler_base64_url_encode(const mcl_uint8_t *data, mcl_size_t data_size, string_t **encoded_data)
To be used to encode the given data in base64 URL encoding format.
Log utility module header file.
E_MCL_ERROR_CODE
MCL Error code definitions. Every function returning an error code uses this enum values...
Definition: mcl_common.h:137
time_t mcl_time_t
Definition: mcl_common.h:48
security_handler_t * security_handler
Security handler.
Definition: jwt.h:29
void json_util_destroy(json_t **root)
This function destroys root.
Definition: json_util.c:863
#define JWT_EXPIRATION_TIME
Definition: definitions.h:60
static E_MCL_ERROR_CODE _add_schema_to_jwt(json_t *payload)
Definition: jwt.c:354
#define MCL_FREE(p)
Definition: memory.h:125
#define ASSERT_STATEMENT_CODE_MESSAGE(condition, statement, return_code,...)
Definition: definitions.h:121
#define JWT_HEADER_VALUE_ALG_SHARED_SECRET
Definition: jwt.c:39
static E_MCL_ERROR_CODE _generate_token(jwt_t *jwt, string_t *header, string_t *payload, string_t **token)
Definition: jwt.c:233
#define JWT_PAYLOAD_NAME_AUD
Definition: jwt.c:29
#define JWT_PAYLOAD_NAME_SUB
Definition: jwt.c:28
string_t * string_array_to_string(string_array_t *array)
To concatenate the strings in the array.
Definition: string_array.c:132
uint8_t mcl_uint8_t
Definition: mcl_common.h:43
void json_util_finish_array(json_t **json_array)
This function destroys json_array data struct. But the array still exists in root json object...
Definition: json_util.c:827
string_t * jwt_get_token(jwt_t *jwt)
Used to generate the JWT Token as json string.
Definition: jwt.c:97
#define JWT_PAYLOAD_NAME_ISS
Definition: jwt.c:27
E_MCL_ERROR_CODE security_handler_rsa_sign(char *rsa_key, char *data, mcl_size_t data_size, mcl_uint8_t **signature, mcl_size_t *signature_size)
To be used to sign data with RSA key.
static string_t dot
Definition: jwt.c:68
char * private_key
Private key.
#define ASSERT_CODE_MESSAGE(condition, return_code,...)
Definition: definitions.h:105
Definitions module header file.
E_MCL_ERROR_CODE string_initialize_dynamic(const char *value, mcl_size_t value_length, string_t **string)
Initializes a dynamic string_t object with the given value and length.
Definition: string_type.c:68
static E_MCL_ERROR_CODE _get_header_and_payload_encoded_base64_url(string_t *header, string_t *payload, string_t **header_encoded, string_t **payload_encoded)
Definition: jwt.c:262
rsa_t rsa
Rsa handle.
mcl_size_t length
Length of buffer.
Definition: string_type.h:46
E_MCL_ERROR_CODE json_util_add_string(json_t *root, const char *object_name, const char *object_value)
This function adds string to root which can be object or array.
Definition: json_util.c:237
void jwt_destroy(jwt_t **jwt)
To destroy the JWT Handler.
Definition: jwt.c:128
#define JWT_HEADER_VALUE_TYP
Definition: jwt.c:41
#define JWT_PAYLOAD_VALUE_SCHEMAS
Definition: jwt.c:43
size_t mcl_size_t
Definition: mcl_common.h:38
#define JWT_PAYLOAD_NAME_TEN
Definition: jwt.c:34
static E_MCL_ERROR_CODE _create_self_issued_jwt_payload(security_handler_t *security_handler, string_t *tenant, jwt_t *jwt)
Definition: jwt.c:187
Success.
Definition: mcl_common.h:140
E_MCL_ERROR_CODE jwt_initialize(security_handler_t *security_handler, E_MCL_SECURITY_PROFILE security_profile, string_t *tenant, jwt_t **jwt)
JWT Initializer.
Definition: jwt.c:70
json_t * header
Header of jwt.
Definition: jwt.h:27
Handle struct for security_handler module.
E_MCL_ERROR_CODE string_array_initialize(mcl_size_t count, string_array_t **array)
String array initialize method.
Definition: string_array.c:19
#define JWT_PAYLOAD_NAME_SCHEMAS
Definition: jwt.c:33
E_MCL_ERROR_CODE json_util_to_string(json_t *root, char **json_string)
This function gives the string of root in json format.
Definition: json_util.c:737
void time_util_get_time(mcl_time_t *current_time)
Definition: time_util.c:61
String array module header file.
Json object.
Definition: json_util.h:35
E_MCL_SECURITY_PROFILE security_profile
Security profile.
Definition: jwt.h:30
JWT Module handler.
Definition: jwt.h:25
Memory allocation fail.
Definition: mcl_common.h:143
#define JWT_PAYLOAD_NAME_EXP
Definition: jwt.c:32
#define MCL_NULL
Definition: definitions.h:24
#define JWT_HEADER_VALUE_ALG_RSA_3072
Definition: jwt.c:40
string_t * client_id
Client id.
JWT module header file.
E_MCL_ERROR_CODE json_util_initialize(E_JSON_TYPE json_type, json_t **root)
This function initializes the given root json.
Definition: json_util.c:53
#define JWT_HEADER_NAME_ALG
Definition: jwt.c:23
Time utility module header file.
This struct is used for json handling.
Definition: json_util.h:27
E_MCL_ERROR_CODE json_util_add_double(json_t *root, const char *object_name, const double number)
This function adds double number to root which can be object or array.
Definition: json_util.c:358
Strings with this type will NOT allocate its buffer during initialization (buffer only points of the ...
Definition: string_type.h:37
#define MCL_ERROR_RETURN(return_value,...)
Definition: definitions.h:69
#define JWT_PAYLOAD_VALUE_AUD
Definition: jwt.c:44