jwt.c
Go to the documentation of this file.
1 
9 #include "jwt.h"
10 #include "time_util.h"
11 #include "json_util.h"
12 #include "definitions.h"
13 #include "string_util.h"
14 #include "mcl_core/mcl_assert.h"
15 #include "mcl_core/mcl_memory.h"
16 
17 // JWT specific constants.
18 #define JWT_HEADER_NAME_ALG "alg"
19 #define JWT_HEADER_NAME_TYPE "typ"
20 #define JWT_HEADER_NAME_KID "kid"
21 
22 #define JWT_PAYLOAD_NAME_ISS "iss"
23 #define JWT_PAYLOAD_NAME_SUB "sub"
24 #define JWT_PAYLOAD_NAME_AUD "aud"
25 #define JWT_PAYLOAD_NAME_IAT "iat"
26 #define JWT_PAYLOAD_NAME_NBF "nbf"
27 #define JWT_PAYLOAD_NAME_EXP "exp"
28 #define JWT_PAYLOAD_NAME_SCHEMAS "schemas"
29 #define JWT_PAYLOAD_NAME_TEN "ten"
30 #define JWT_PAYLOAD_NAME_JTI "jti"
31 #define JWT_PAYLOAD_NAME_PUBLIC_KEY "public_key"
32 #define JWT_PAYLOAD_NAME_CONTENT_MD5 "content-md5"
33 
34 #define JWT_HEADER_VALUE_ALG_SHARED_SECRET "HS256"
35 #define JWT_HEADER_VALUE_ALG_RSA_3072 "RS256"
36 #define JWT_HEADER_VALUE_TYP "JWT"
37 
38 #define JWT_PAYLOAD_VALUE_SCHEMAS "urn:siemens:mindsphere:v1"
39 #define JWT_PAYLOAD_VALUE_AUD "southgate"
40 
41 // Creates header JSON object of JWT.
43 
44 // Creates payload JSON object of JWT.
45 static mcl_error_t _create_self_issued_jwt_payload(security_handler_t *security_handler, char *tenant, jwt_t *jwt);
46 
47 // Adds schema array to payload of JWT.
49 
50 // Encodes header and payload with base64. Clears the input strings if fails.
51 static mcl_error_t _get_header_and_payload_encoded_base64_url(char *header, char *payload, char **header_encoded, char **payload_encoded);
52 
53 // Joins two strings with dot (.) in between and generates a new string while destroying the two input strings.
54 static mcl_error_t _join_with_dot(char *string_a, char *string_b, char **joined);
55 
56 // Calculates the signature from the header and payload strings.
57 static mcl_error_t _calculate_signature(jwt_t *jwt, char *header_and_payload, char **signature);
58 
59 // Generates the JWT.
60 static mcl_error_t _generate_token(jwt_t *jwt, char *header, char *payload, char **token);
61 
62 mcl_error_t jwt_initialize(security_handler_t *security_handler, E_MCL_SECURITY_PROFILE security_profile, char *tenant, jwt_t **jwt)
63 {
64  mcl_error_t code;
65 
66  MCL_DEBUG_ENTRY("security_handler_t *security_handler = <%p>, E_MCL_SECURITY_PROFILE security_profile = <%d>, char *tenant = <%s>, jwt_t **jwt = <%p>",
67  security_handler, security_profile, tenant, jwt);
68 
69  // Allocate memory for JWT handle.
70  MCL_NEW(*jwt);
71  MCL_ASSERT_CODE_MESSAGE(MCL_NULL != *jwt, MCL_OUT_OF_MEMORY, "Not enough memory to allocate jwt!");
72 
73  (*jwt)->header = MCL_NULL;
74  (*jwt)->payload = MCL_NULL;
75  (*jwt)->security_handler = security_handler;
76  (*jwt)->security_profile = security_profile;
77 
78  // Create JWT header.
79  code = _create_self_issued_jwt_header(security_profile, &((*jwt)->header));
80  MCL_ASSERT_STATEMENT_CODE_MESSAGE(MCL_OK == code, jwt_destroy(jwt), code, "Header of JWT can not be created.");
81 
82  // Create JWT payload.
83  code = _create_self_issued_jwt_payload(security_handler, tenant, *jwt);
84  MCL_ASSERT_STATEMENT_CODE_MESSAGE(MCL_OK == code, jwt_destroy(jwt), code, "Payload of JWT can not be created.");
85 
86  MCL_DEBUG_LEAVE("retVal = <%d>", MCL_OK);
87  return MCL_OK;
88 }
89 
90 char *jwt_get_token(jwt_t *jwt)
91 {
92  mcl_error_t code;
93  char *token = MCL_NULL;
94  char *header_json = MCL_NULL;
95  char *payload_json = MCL_NULL;
96 
97  MCL_DEBUG_ENTRY("jwt_t *jwt = <%p>", jwt);
98 
99  code = mcl_json_util_to_string(jwt->header, &header_json);
100 
101  if (MCL_OK == code)
102  {
103  code = mcl_json_util_to_string(jwt->payload, &payload_json);
104  }
105 
106  if (MCL_OK == code)
107  {
108  code = _generate_token(jwt, header_json, payload_json, &token);
109  }
110 
111  if (MCL_OK != code)
112  {
113  MCL_ERROR("Token could not be generated <%s>", mcl_core_return_code_strings[code]);
114  }
115 
116  // Clean up.
117  MCL_FREE(header_json);
118  MCL_FREE(payload_json);
119 
120  MCL_DEBUG_LEAVE("retVal = <%p>", token);
121  return token;
122 }
123 
124 void jwt_destroy(jwt_t **jwt)
125 {
126  MCL_DEBUG_ENTRY("jwt_t **jwt = <%p>", jwt);
127 
128  if (MCL_NULL != *jwt)
129  {
130  if (MCL_NULL != (*jwt)->header)
131  {
132  json_util_destroy(&(*jwt)->header);
133  }
134 
135  if (MCL_NULL != (*jwt)->payload)
136  {
137  json_util_destroy(&(*jwt)->payload);
138  }
139 
140  MCL_FREE(*jwt);
141 
142  MCL_DEBUG("JWT handle is destroyed.");
143  }
144  else
145  {
146  MCL_DEBUG("JWT handle is already NULL.");
147  }
148 
149  MCL_DEBUG_LEAVE("retVal = void");
150 }
151 
153 {
154  mcl_error_t code;
155 
156  MCL_DEBUG_ENTRY("E_MCL_SECURITY_PROFILE security_profile = <%d>, mcl_json_t **header = <%p>", security_profile, header);
157 
158  code = json_util_initialize(MCL_JSON_OBJECT, header);
159 
160  if (MCL_SECURITY_SHARED_SECRET == security_profile)
161  {
162  if (MCL_OK == code)
163  {
165  }
166 
167  if (MCL_OK == code)
168  {
170  }
171  }
172  else
173  {
174  // It has to be MCL_SECURITY_RSA_3072.
175  if (MCL_OK == code)
176  {
178  }
179 
180  if (MCL_OK == code)
181  {
183  }
184  }
185 
186  if (MCL_OK != code)
187  {
188  json_util_destroy(header);
189  }
190 
191  MCL_DEBUG_LEAVE("retVal = <%d>", code);
192  return code;
193 }
194 
195 static mcl_error_t _create_self_issued_jwt_payload(security_handler_t *security_handler, char *tenant, jwt_t *jwt)
196 {
197  mcl_error_t code;
198  mcl_time_t current_time;
199  char *jti;
200  mcl_json_t *payload = MCL_NULL;
201 
202  MCL_DEBUG_ENTRY("security_handler_t *security_handler = <%p>, char *tenant = <%s>, jwt_t *jwt = <%p>", security_handler, tenant, jwt);
203 
204  // Create JTI.
205  code = security_handler_generate_jti(&jti);
206  MCL_ASSERT_CODE_MESSAGE(MCL_OK == code, code, "Generate jti from security_handler failed!");
207 
208  // Initialize the payload json object.
209  code = json_util_initialize(MCL_JSON_OBJECT, &payload);
210  MCL_ASSERT_STATEMENT_CODE_MESSAGE(MCL_OK == code, MCL_FREE(jti), code, "JSON payload can not be initialized.");
211 
212  // Get current time in seconds.
213  time_util_get_time(&current_time);
214 
215  // Fill payload json object.
216  code = json_util_add_string(payload, JWT_PAYLOAD_NAME_ISS, security_handler->client_id);
217 
218  if (MCL_OK == code)
219  {
220  code = json_util_add_string(payload, JWT_PAYLOAD_NAME_SUB, security_handler->client_id);
221  }
222 
223  if (MCL_OK == code)
224  {
226  }
227 
228  if (MCL_OK == code)
229  {
230  code = json_util_add_double(payload, JWT_PAYLOAD_NAME_IAT, (double) current_time);
231  }
232 
233  if (MCL_OK == code)
234  {
235  code = json_util_add_double(payload, JWT_PAYLOAD_NAME_NBF, (double) current_time);
236  }
237 
238  if (MCL_OK == code)
239  {
240  mcl_time_t expiration = current_time + JWT_EXPIRATION_TIME;
241  code = json_util_add_double(payload, JWT_PAYLOAD_NAME_EXP, (double) expiration);
242  }
243 
244  if (MCL_OK == code)
245  {
246  code = _add_schema_to_jwt(payload);
247  }
248 
249  if (MCL_OK == code)
250  {
251  code = json_util_add_string(payload, JWT_PAYLOAD_NAME_TEN, tenant);
252  }
253 
254  if (MCL_OK == code)
255  {
256  code = json_util_add_string(payload, JWT_PAYLOAD_NAME_JTI, jti);
257  }
258 
259  if (MCL_OK == code)
260  {
261  jwt->payload = payload;
262  jwt->issued_at = current_time;
263  }
264  else
265  {
266  json_util_destroy(&payload);
267  }
268  MCL_FREE(jti);
269 
270  MCL_DEBUG_LEAVE("retVal = <%d>", code);
271  return code;
272 }
273 
274 static mcl_error_t _generate_token(jwt_t *jwt, char *header, char *payload, char **token)
275 {
276  char *header_encoded;
277  char *payload_encoded;
278  char *header_and_payload = MCL_NULL;
279  char *signature = MCL_NULL;
280 
281  MCL_DEBUG_ENTRY("jwt_t *jwt = <%p>, char *header = <%s>, char *payload = <%s>, char **token = <%p>", jwt, header, payload, token);
282 
283  // Encode header and payload to base64 url.
284  MCL_ASSERT_CODE_MESSAGE(MCL_OK == _get_header_and_payload_encoded_base64_url(header, payload, &header_encoded, &payload_encoded), MCL_FAIL,
285  "Encode header - payload failed!");
286 
287  // Join header and payload. This joined header and payload must not be lost, it will be used again.
288  MCL_ASSERT_CODE_MESSAGE(MCL_OK == _join_with_dot(header_encoded, payload_encoded, &header_and_payload), MCL_FAIL, "Join header and payload failed!");
289 
290  // Calculate the base64 url encoded signature from header and payload joined together.
291  if (MCL_OK != _calculate_signature(jwt, header_and_payload, &signature))
292  {
293  MCL_FREE(header_and_payload);
294  MCL_ERROR_RETURN(MCL_FAIL, "Generate signature failed!");
295  }
296 
297  // Join signature with header and payload to get the final token.
298  MCL_ASSERT_CODE_MESSAGE(MCL_OK == _join_with_dot(header_and_payload, signature, token), MCL_FAIL, "Joining header and payload with signature failed!");
299 
300  MCL_DEBUG_LEAVE("retVal = <%d>", MCL_OK);
301  return MCL_OK;
302 }
303 
304 static mcl_error_t _get_header_and_payload_encoded_base64_url(char *header, char *payload, char **header_encoded, char **payload_encoded)
305 {
306  MCL_DEBUG_ENTRY("char *header = <%s>, char *payload = <%s>, char **header_encoded = <%p>, char **payload_encoded = <%p>",
307  header, payload, header_encoded, payload_encoded);
308 
309  // Encode header.
311  MCL_FAIL, "Header encoding failed!");
312 
313  // Encode payload.
315  MCL_FREE(*header_encoded), MCL_FAIL, "Payload encoding failed!");
316 
317  MCL_DEBUG_LEAVE("retVal = <%d>", MCL_OK);
318  return MCL_OK;
319 }
320 
321 static mcl_error_t _join_with_dot(char *string_a, char *string_b, char **joined)
322 {
323  mcl_error_t code = MCL_OK;
324  char *dot = ".";
325  mcl_size_t dot_length = 1;
326  mcl_size_t joined_string_length;
327 
328  MCL_DEBUG_ENTRY("char *string_a = <%s>, char *string_b = <%s>, char **joined = <%p>", string_a, string_b, joined);
329 
330  // Allocate memory for joined string.
331  joined_string_length = string_util_strlen(string_a) + dot_length + string_util_strlen(string_b);
332  *joined = MCL_MALLOC(joined_string_length + MCL_NULL_CHAR_SIZE);
333 
334  if (MCL_NULL != *joined)
335  {
336  // Copy "string a" to target string.
337  string_util_memcpy(*joined, string_a, string_util_strlen(string_a));
338 
339  // Copy "." to target string.
340  string_util_memcpy(*joined + string_util_strlen(string_a), dot, dot_length);
341 
342  // Copy "string b" to target string with null termination.
343  string_util_memcpy(*joined + string_util_strlen(string_a) + dot_length, string_b, string_util_strlen(string_b) + MCL_NULL_CHAR_SIZE);
344  }
345  else
346  {
347  code = MCL_OUT_OF_MEMORY;
348  }
349 
350  // Destroy strings a and b.
351  MCL_FREE(string_a);
352  MCL_FREE(string_b);
353 
354  MCL_DEBUG_LEAVE("retVal = <%d>", code);
355  return code;
356 }
357 
358 static mcl_error_t _calculate_signature(jwt_t *jwt, char *header_and_payload, char **signature)
359 {
360  mcl_error_t code;
361  mcl_uint8_t *hash = MCL_NULL;
362  mcl_size_t hash_size = 0;
363 
364  MCL_DEBUG_ENTRY("jwt_t *jwt = <%p>, char *header_and_payload = <%s>, char **signature = <%p>", jwt, header_and_payload, signature);
365 
366  // Calculate HMAC SHA256 of header and payload.
368  {
369  code = security_handler_hmac_sha256(jwt->security_handler, (mcl_uint8_t *) header_and_payload, string_util_strlen(header_and_payload),
370  &hash, &hash_size);
371  }
372  else
373  {
374  code = security_handler_rsa_sign(jwt->security_handler->rsa.private_key, header_and_payload, string_util_strlen(header_and_payload), &hash, &hash_size);
375  }
376  MCL_ASSERT_CODE_MESSAGE(MCL_OK == code, MCL_FAIL, "Can not sign JWT!");
377 
378  // Encode calculated signature.
379  code = security_handler_base64_url_encode(hash, hash_size, signature);
380 
381  // Free hash, not needed anymore.
382  MCL_FREE(hash);
383 
384  // Check encode operation.
385  MCL_ASSERT_CODE_MESSAGE(MCL_OK == code, MCL_FAIL, "Encode calculated signature failed!");
386 
387  MCL_DEBUG_LEAVE("retVal = <%d>", MCL_OK);
388  return MCL_OK;
389 }
390 
392 {
393  mcl_json_t *schemas_array;
394  mcl_error_t code;
395 
396  MCL_DEBUG_ENTRY("mcl_json_t *payload = <%p>", payload);
397 
398  code = json_util_start_array(payload, JWT_PAYLOAD_NAME_SCHEMAS, &schemas_array);
399 
400  if (MCL_OK == code)
401  {
403  }
404 
405  MCL_DEBUG_LEAVE("retVal = <%d>", code);
406  return code;
407 }
#define JWT_PAYLOAD_NAME_IAT
Definition: jwt.c:25
static mcl_error_t _create_self_issued_jwt_payload(security_handler_t *security_handler, char *tenant, jwt_t *jwt)
Definition: jwt.c:195
mcl_error_t security_handler_generate_jti(char **jti)
char * client_id
Client id.
size_t mcl_size_t
static mcl_error_t _join_with_dot(char *string_a, char *string_b, char **joined)
Definition: jwt.c:321
#define JWT_HEADER_NAME_TYPE
Definition: jwt.c:19
Assert module header file.
Success.
#define MCL_DEBUG(...)
Definition: mcl_log_util.h:114
Json utility module header file.
void mcl_json_t
Definition: mcl_json_util.h:24
Json object.
Definition: mcl_json_util.h:32
#define JWT_PAYLOAD_NAME_JTI
Definition: jwt.c:30
mcl_int32_t mcl_error_t
#define JWT_PAYLOAD_NAME_NBF
Definition: jwt.c:26
mcl_time_t issued_at
Time of issue.
Definition: jwt.h:26
char * jwt_get_token(jwt_t *jwt)
Definition: jwt.c:90
#define MCL_DEBUG_ENTRY(...)
Definition: mcl_log_util.h:115
static mcl_error_t _create_self_issued_jwt_header(E_MCL_SECURITY_PROFILE security_profile, mcl_json_t **header)
Definition: jwt.c:152
static mcl_error_t _get_header_and_payload_encoded_base64_url(char *header, char *payload, char **header_encoded, char **payload_encoded)
Definition: jwt.c:304
String utility module header file.
security_handler_t * security_handler
Security handler.
Definition: jwt.h:24
#define JWT_EXPIRATION_TIME
Definition: definitions.h:34
mcl_error_t security_handler_base64_url_encode(const mcl_uint8_t *data, mcl_size_t data_size, char **encoded_data)
#define MCL_ASSERT_CODE_MESSAGE(condition, return_code,...)
Definition: mcl_assert.h:77
#define MCL_NEW(p)
Definition: mcl_memory.h:55
#define MCL_NULL
void json_util_destroy(mcl_json_t **root)
Definition: json_util.c:913
#define MCL_ERROR(...)
Definition: mcl_log_util.h:142
#define JWT_HEADER_VALUE_ALG_SHARED_SECRET
Definition: jwt.c:34
#define JWT_PAYLOAD_NAME_AUD
Definition: jwt.c:24
#define JWT_PAYLOAD_NAME_SUB
Definition: jwt.c:23
mcl_error_t jwt_initialize(security_handler_t *security_handler, E_MCL_SECURITY_PROFILE security_profile, char *tenant, jwt_t **jwt)
Definition: jwt.c:62
#define MCL_FREE(p)
Definition: mcl_memory.h:59
mcl_error_t security_handler_rsa_sign(char *rsa_key, char *data, mcl_size_t data_size, mcl_uint8_t **signature, mcl_size_t *signature_size)
#define JWT_PAYLOAD_NAME_ISS
Definition: jwt.c:22
mcl_error_t 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)
void string_util_memcpy(void *destination, const void *source, mcl_size_t count)
Definition: string_util.c:229
char * private_key
Private key.
Definitions module header file.
uint8_t mcl_uint8_t
#define MCL_ASSERT_STATEMENT_CODE_MESSAGE(condition, statement, return_code,...)
Definition: mcl_assert.h:93
MCL_CORE_EXPORT const char * mcl_core_return_code_strings[MCL_CORE_RETURN_CODE_END]
Definition: core_common.c:11
rsa_t rsa
RSA handle.
mcl_error_t json_util_start_array(mcl_json_t *root, const char *array_name, mcl_json_t **json_array)
Definition: json_util.c:107
void jwt_destroy(jwt_t **jwt)
Definition: jwt.c:124
time_t mcl_time_t
#define JWT_HEADER_VALUE_TYP
Definition: jwt.c:36
mcl_json_t * payload
Payload of JWT.
Definition: jwt.h:23
#define MCL_ERROR_RETURN(return_value,...)
Definition: mcl_assert.h:26
#define JWT_PAYLOAD_VALUE_SCHEMAS
Definition: jwt.c:38
static mcl_error_t _calculate_signature(jwt_t *jwt, char *header_and_payload, char **signature)
Definition: jwt.c:358
#define JWT_PAYLOAD_NAME_TEN
Definition: jwt.c:29
#define JWT_PAYLOAD_NAME_SCHEMAS
Definition: jwt.c:28
Memory allocation fail.
void time_util_get_time(mcl_time_t *current_time)
Definition: time_util.c:129
MCL_CORE_EXPORT mcl_error_t mcl_json_util_to_string(mcl_json_t *root, char **json_string)
Definition: json_util.c:792
#define MCL_NULL_CHAR_SIZE
mcl_error_t json_util_add_string(mcl_json_t *root, const char *object_name, const char *object_value)
Definition: json_util.c:273
#define MCL_MALLOC(bytes)
Definition: mcl_memory.h:54
mcl_error_t json_util_initialize(E_MCL_JSON_TYPE mcl_json_type, mcl_json_t **root)
Definition: json_util.c:57
E_MCL_SECURITY_PROFILE security_profile
Security profile.
Definition: jwt.h:25
static mcl_error_t _generate_token(jwt_t *jwt, char *header, char *payload, char **token)
Definition: jwt.c:274
Definition: jwt.h:20
#define JWT_PAYLOAD_NAME_EXP
Definition: jwt.c:27
#define JWT_HEADER_VALUE_ALG_RSA_3072
Definition: jwt.c:35
#define MCL_DEBUG_LEAVE(...)
Definition: mcl_log_util.h:116
mcl_json_t * header
Header of JWT.
Definition: jwt.h:22
Internal failure in MCL.
JWT module header file.
mcl_size_t string_util_strlen(const char *buffer)
Definition: string_util.c:35
#define JWT_HEADER_NAME_ALG
Definition: jwt.c:18
Time utility module header file.
static mcl_error_t _add_schema_to_jwt(mcl_json_t *payload)
Definition: jwt.c:391
mcl_error_t json_util_add_double(mcl_json_t *root, const char *object_name, const double number)
Definition: json_util.c:364
E_MCL_SECURITY_PROFILE
#define JWT_PAYLOAD_VALUE_AUD
Definition: jwt.c:39
Memory module interface header file.