diff options
Diffstat (limited to 'src/reclaim/oidc_helper.c')
-rw-r--r-- | src/reclaim/oidc_helper.c | 532 |
1 files changed, 301 insertions, 231 deletions
diff --git a/src/reclaim/oidc_helper.c b/src/reclaim/oidc_helper.c index ad2839200..9b5938c43 100644 --- a/src/reclaim/oidc_helper.c +++ b/src/reclaim/oidc_helper.c | |||
@@ -47,9 +47,9 @@ struct OIDC_Parameters | |||
47 | struct GNUNET_RECLAIM_Ticket ticket; | 47 | struct GNUNET_RECLAIM_Ticket ticket; |
48 | 48 | ||
49 | /** | 49 | /** |
50 | * The nonce | 50 | * The nonce length |
51 | */ | 51 | */ |
52 | uint32_t nonce GNUNET_PACKED; | 52 | uint32_t nonce_len GNUNET_PACKED; |
53 | 53 | ||
54 | /** | 54 | /** |
55 | * The length of the PKCE code_challenge | 55 | * The length of the PKCE code_challenge |
@@ -69,6 +69,51 @@ struct OIDC_Parameters | |||
69 | 69 | ||
70 | GNUNET_NETWORK_STRUCT_END | 70 | GNUNET_NETWORK_STRUCT_END |
71 | 71 | ||
72 | /** | ||
73 | * Standard claims represented by the "profile" scope in OIDC | ||
74 | */ | ||
75 | static char OIDC_profile_claims[14][32] = { | ||
76 | "name", "family_name", "given_name", "middle_name", "nickname", | ||
77 | "preferred_username", "profile", "picture", "website", "gender", "birthdate", | ||
78 | "zoneinfo", "locale", "updated_at" | ||
79 | }; | ||
80 | |||
81 | /** | ||
82 | * Standard claims represented by the "email" scope in OIDC | ||
83 | */ | ||
84 | static char OIDC_email_claims[2][16] = { | ||
85 | "email", "email_verified" | ||
86 | }; | ||
87 | |||
88 | /** | ||
89 | * Standard claims represented by the "phone" scope in OIDC | ||
90 | */ | ||
91 | static char OIDC_phone_claims[2][32] = { | ||
92 | "phone_number", "phone_number_verified" | ||
93 | }; | ||
94 | |||
95 | /** | ||
96 | * Standard claims represented by the "address" scope in OIDC | ||
97 | */ | ||
98 | static char OIDC_address_claims[5][32] = { | ||
99 | "street_address", "locality", "region", "postal_code", "country" | ||
100 | }; | ||
101 | |||
102 | static enum GNUNET_GenericReturnValue | ||
103 | is_claim_in_address_scope (const char *claim) | ||
104 | { | ||
105 | int i; | ||
106 | for (i = 0; i < 5; i++) | ||
107 | { | ||
108 | if (0 == strcmp (claim, OIDC_address_claims[i])) | ||
109 | { | ||
110 | return GNUNET_YES; | ||
111 | } | ||
112 | } | ||
113 | return GNUNET_NO; | ||
114 | } | ||
115 | |||
116 | |||
72 | static char * | 117 | static char * |
73 | create_jwt_header (void) | 118 | create_jwt_header (void) |
74 | { | 119 | { |
@@ -109,49 +154,22 @@ fix_base64 (char *str) | |||
109 | replace_char (str, '/', '_'); | 154 | replace_char (str, '/', '_'); |
110 | } | 155 | } |
111 | 156 | ||
112 | 157 | static json_t* | |
113 | /** | 158 | generate_userinfo_json(const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, |
114 | * Create a JWT from attributes | 159 | struct GNUNET_RECLAIM_AttributeList *attrs, |
115 | * | 160 | struct GNUNET_RECLAIM_AttestationList *attests) |
116 | * @param aud_key the public of the audience | ||
117 | * @param sub_key the public key of the subject | ||
118 | * @param attrs the attribute list | ||
119 | * @param expiration_time the validity of the token | ||
120 | * @param secret_key the key used to sign the JWT | ||
121 | * @return a new base64-encoded JWT string. | ||
122 | */ | ||
123 | char * | ||
124 | OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | ||
125 | const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, | ||
126 | struct GNUNET_RECLAIM_AttributeList *attrs, | ||
127 | struct GNUNET_RECLAIM_AttestationList *attests, | ||
128 | const struct GNUNET_TIME_Relative *expiration_time, | ||
129 | const char *nonce, | ||
130 | const char *secret_key) | ||
131 | { | 161 | { |
132 | struct GNUNET_RECLAIM_AttributeListEntry *le; | 162 | struct GNUNET_RECLAIM_AttributeListEntry *le; |
133 | struct GNUNET_RECLAIM_AttestationListEntry *ale; | 163 | struct GNUNET_RECLAIM_AttestationListEntry *ale; |
134 | struct GNUNET_HashCode signature; | ||
135 | struct GNUNET_TIME_Absolute exp_time; | ||
136 | struct GNUNET_TIME_Absolute time_now; | ||
137 | char *audience; | ||
138 | char *subject; | 164 | char *subject; |
139 | char *header; | ||
140 | char *body_str; | ||
141 | char *aggr_names_str; | ||
142 | char *aggr_sources_str; | ||
143 | char *source_name; | 165 | char *source_name; |
144 | char *result; | ||
145 | char *header_base64; | ||
146 | char *body_base64; | ||
147 | char *signature_target; | ||
148 | char *signature_base64; | ||
149 | char *attr_val_str; | 166 | char *attr_val_str; |
150 | char *attest_val_str; | 167 | char *attest_val_str; |
151 | json_t *body; | 168 | json_t *body; |
152 | json_t *aggr_names; | 169 | json_t *aggr_names; |
153 | json_t *aggr_sources; | 170 | json_t *aggr_sources; |
154 | json_t *aggr_sources_jwt; | 171 | json_t *aggr_sources_jwt; |
172 | json_t *addr_claim = NULL; | ||
155 | int num_attestations = 0; | 173 | int num_attestations = 0; |
156 | for (le = attrs->list_head; NULL != le; le = le->next) | 174 | for (le = attrs->list_head; NULL != le; le = le->next) |
157 | { | 175 | { |
@@ -159,22 +177,10 @@ OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | |||
159 | num_attestations++; | 177 | num_attestations++; |
160 | } | 178 | } |
161 | 179 | ||
162 | // iat REQUIRED time now | ||
163 | time_now = GNUNET_TIME_absolute_get (); | ||
164 | // exp REQUIRED time expired from config | ||
165 | exp_time = GNUNET_TIME_absolute_add (time_now, *expiration_time); | ||
166 | // auth_time only if max_age | ||
167 | // nonce only if nonce | ||
168 | // OPTIONAL acr,amr,azp | ||
169 | subject = | 180 | subject = |
170 | GNUNET_STRINGS_data_to_string_alloc (sub_key, | 181 | GNUNET_STRINGS_data_to_string_alloc (sub_key, |
171 | sizeof(struct | 182 | sizeof(struct |
172 | GNUNET_CRYPTO_EcdsaPublicKey)); | 183 | GNUNET_CRYPTO_EcdsaPublicKey)); |
173 | audience = | ||
174 | GNUNET_STRINGS_data_to_string_alloc (aud_key, | ||
175 | sizeof(struct | ||
176 | GNUNET_CRYPTO_EcdsaPublicKey)); | ||
177 | header = create_jwt_header (); | ||
178 | body = json_object (); | 184 | body = json_object (); |
179 | aggr_names = json_object (); | 185 | aggr_names = json_object (); |
180 | aggr_sources = json_object (); | 186 | aggr_sources = json_object (); |
@@ -185,26 +191,7 @@ OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | |||
185 | json_object_set_new (body, "iss", json_string (SERVER_ADDRESS)); | 191 | json_object_set_new (body, "iss", json_string (SERVER_ADDRESS)); |
186 | // sub REQUIRED public key identity, not exceed 255 ASCII length | 192 | // sub REQUIRED public key identity, not exceed 255 ASCII length |
187 | json_object_set_new (body, "sub", json_string (subject)); | 193 | json_object_set_new (body, "sub", json_string (subject)); |
188 | // aud REQUIRED public key client_id must be there | ||
189 | json_object_set_new (body, "aud", json_string (audience)); | ||
190 | // iat | ||
191 | json_object_set_new (body, | ||
192 | "iat", | ||
193 | json_integer (time_now.abs_value_us / (1000 * 1000))); | ||
194 | // exp | ||
195 | json_object_set_new (body, | ||
196 | "exp", | ||
197 | json_integer (exp_time.abs_value_us / (1000 * 1000))); | ||
198 | // nbf | ||
199 | json_object_set_new (body, | ||
200 | "nbf", | ||
201 | json_integer (time_now.abs_value_us / (1000 * 1000))); | ||
202 | // nonce | ||
203 | if (NULL != nonce) | ||
204 | json_object_set_new (body, "nonce", json_string (nonce)); | ||
205 | attest_val_str = NULL; | 194 | attest_val_str = NULL; |
206 | aggr_names_str = NULL; | ||
207 | aggr_sources_str = NULL; | ||
208 | source_name = NULL; | 195 | source_name = NULL; |
209 | int i = 0; | 196 | int i = 0; |
210 | for (ale = attests->list_head; NULL != ale; ale = ale->next) | 197 | for (ale = attests->list_head; NULL != ale; ale = ale->next) |
@@ -236,8 +223,26 @@ OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | |||
236 | GNUNET_RECLAIM_attribute_value_to_string (le->attribute->type, | 223 | GNUNET_RECLAIM_attribute_value_to_string (le->attribute->type, |
237 | le->attribute->data, | 224 | le->attribute->data, |
238 | le->attribute->data_size); | 225 | le->attribute->data_size); |
239 | json_object_set_new (body, le->attribute->name, | 226 | /** |
240 | json_string (attr_val_str)); | 227 | * There is this wierd quirk that the individual address claim(s) must be |
228 | * inside a JSON object of the "address" claim. | ||
229 | * FIXME: Possibly include formatted claim here | ||
230 | */ | ||
231 | if (GNUNET_YES == is_claim_in_address_scope (le->attribute->name)) | ||
232 | { | ||
233 | if (NULL == addr_claim) | ||
234 | { | ||
235 | addr_claim = json_object (); | ||
236 | } | ||
237 | json_object_set_new (addr_claim, le->attribute->name, | ||
238 | json_string (attr_val_str)); | ||
239 | |||
240 | } | ||
241 | else | ||
242 | { | ||
243 | json_object_set_new (body, le->attribute->name, | ||
244 | json_string (attr_val_str)); | ||
245 | } | ||
241 | GNUNET_free (attr_val_str); | 246 | GNUNET_free (attr_val_str); |
242 | } | 247 | } |
243 | else | 248 | else |
@@ -262,21 +267,113 @@ OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | |||
262 | GNUNET_free (source_name); | 267 | GNUNET_free (source_name); |
263 | } | 268 | } |
264 | } | 269 | } |
270 | if (NULL != addr_claim) | ||
271 | json_object_set_new (body, "address", addr_claim); | ||
265 | 272 | ||
266 | if (NULL != attest_val_str) | 273 | if (NULL != attest_val_str) |
267 | GNUNET_free (attest_val_str); | 274 | GNUNET_free (attest_val_str); |
268 | if (0 != i) | 275 | if (0 != i) |
269 | { | 276 | { |
270 | aggr_names_str = json_dumps (aggr_names, JSON_INDENT (0) | JSON_COMPACT); | 277 | json_object_set_new (body, "_claim_names", aggr_names); |
271 | aggr_sources_str = json_dumps (aggr_sources, JSON_INDENT (0) | 278 | json_object_set_new (body, "_claim_sources", aggr_sources); |
272 | | JSON_COMPACT); | ||
273 | json_object_set_new (body, "_claim_names", json_string (aggr_names_str)); | ||
274 | json_object_set_new (body, "_claim_sources", json_string ( | ||
275 | aggr_sources_str)); | ||
276 | } | 279 | } |
277 | 280 | ||
278 | json_decref (aggr_names); | 281 | return body; |
279 | json_decref (aggr_sources); | 282 | } |
283 | |||
284 | /** | ||
285 | * Generate userinfo JSON as string | ||
286 | * | ||
287 | * @param sub_key the subject (user) | ||
288 | * @param attrs user attribute list | ||
289 | * @param attests user attribute attestation list (may be empty) | ||
290 | * @return Userinfo JSON | ||
291 | */ | ||
292 | char * | ||
293 | OIDC_generate_userinfo (const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, | ||
294 | struct GNUNET_RECLAIM_AttributeList *attrs, | ||
295 | struct GNUNET_RECLAIM_AttestationList *attests) | ||
296 | { | ||
297 | char *body_str; | ||
298 | json_t* body = generate_userinfo_json (sub_key, | ||
299 | attrs, | ||
300 | attests); | ||
301 | body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT); | ||
302 | json_decref (body); | ||
303 | return body_str; | ||
304 | } | ||
305 | |||
306 | |||
307 | /** | ||
308 | * Create a JWT from attributes | ||
309 | * | ||
310 | * @param aud_key the public of the audience | ||
311 | * @param sub_key the public key of the subject | ||
312 | * @param attrs the attribute list | ||
313 | * @param expiration_time the validity of the token | ||
314 | * @param secret_key the key used to sign the JWT | ||
315 | * @return a new base64-encoded JWT string. | ||
316 | */ | ||
317 | char * | ||
318 | OIDC_generate_id_token (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | ||
319 | const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, | ||
320 | struct GNUNET_RECLAIM_AttributeList *attrs, | ||
321 | struct GNUNET_RECLAIM_AttestationList *attests, | ||
322 | const struct GNUNET_TIME_Relative *expiration_time, | ||
323 | const char *nonce, | ||
324 | const char *secret_key) | ||
325 | { | ||
326 | struct GNUNET_HashCode signature; | ||
327 | struct GNUNET_TIME_Absolute exp_time; | ||
328 | struct GNUNET_TIME_Absolute time_now; | ||
329 | char *audience; | ||
330 | char *subject; | ||
331 | char *header; | ||
332 | char *body_str; | ||
333 | char *result; | ||
334 | char *header_base64; | ||
335 | char *body_base64; | ||
336 | char *signature_target; | ||
337 | char *signature_base64; | ||
338 | json_t *body; | ||
339 | |||
340 | body = generate_userinfo_json (sub_key, | ||
341 | attrs, | ||
342 | attests); | ||
343 | // iat REQUIRED time now | ||
344 | time_now = GNUNET_TIME_absolute_get (); | ||
345 | // exp REQUIRED time expired from config | ||
346 | exp_time = GNUNET_TIME_absolute_add (time_now, *expiration_time); | ||
347 | // auth_time only if max_age | ||
348 | // nonce only if nonce | ||
349 | // OPTIONAL acr,amr,azp | ||
350 | subject = | ||
351 | GNUNET_STRINGS_data_to_string_alloc (sub_key, | ||
352 | sizeof(struct | ||
353 | GNUNET_CRYPTO_EcdsaPublicKey)); | ||
354 | audience = | ||
355 | GNUNET_STRINGS_data_to_string_alloc (aud_key, | ||
356 | sizeof(struct | ||
357 | GNUNET_CRYPTO_EcdsaPublicKey)); | ||
358 | header = create_jwt_header (); | ||
359 | |||
360 | // aud REQUIRED public key client_id must be there | ||
361 | json_object_set_new (body, "aud", json_string (audience)); | ||
362 | // iat | ||
363 | json_object_set_new (body, | ||
364 | "iat", | ||
365 | json_integer (time_now.abs_value_us / (1000 * 1000))); | ||
366 | // exp | ||
367 | json_object_set_new (body, | ||
368 | "exp", | ||
369 | json_integer (exp_time.abs_value_us / (1000 * 1000))); | ||
370 | // nbf | ||
371 | json_object_set_new (body, | ||
372 | "nbf", | ||
373 | json_integer (time_now.abs_value_us / (1000 * 1000))); | ||
374 | // nonce | ||
375 | if (NULL != nonce) | ||
376 | json_object_set_new (body, "nonce", json_string (nonce)); | ||
280 | 377 | ||
281 | body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT); | 378 | body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT); |
282 | json_decref (body); | 379 | json_decref (body); |
@@ -315,10 +412,6 @@ OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | |||
315 | GNUNET_free (signature_target); | 412 | GNUNET_free (signature_target); |
316 | GNUNET_free (header); | 413 | GNUNET_free (header); |
317 | GNUNET_free (body_str); | 414 | GNUNET_free (body_str); |
318 | if (NULL != aggr_sources_str) | ||
319 | GNUNET_free (aggr_sources_str); | ||
320 | if (NULL != aggr_names_str) | ||
321 | GNUNET_free (aggr_names_str); | ||
322 | GNUNET_free (signature_base64); | 415 | GNUNET_free (signature_base64); |
323 | GNUNET_free (body_base64); | 416 | GNUNET_free (body_base64); |
324 | GNUNET_free (header_base64); | 417 | GNUNET_free (header_base64); |
@@ -326,89 +419,6 @@ OIDC_id_token_new (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, | |||
326 | } | 419 | } |
327 | 420 | ||
328 | 421 | ||
329 | static void | ||
330 | derive_aes_key (struct GNUNET_CRYPTO_SymmetricSessionKey *key, | ||
331 | struct GNUNET_CRYPTO_SymmetricInitializationVector *iv, | ||
332 | struct GNUNET_HashCode *key_material) | ||
333 | { | ||
334 | static const char ctx_key[] = "reclaim-aes-ctx-key"; | ||
335 | static const char ctx_iv[] = "reclaim-aes-ctx-iv"; | ||
336 | |||
337 | GNUNET_CRYPTO_kdf (key, | ||
338 | sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey), | ||
339 | ctx_key, | ||
340 | strlen (ctx_key), | ||
341 | key_material, | ||
342 | sizeof(struct GNUNET_HashCode), | ||
343 | NULL); | ||
344 | GNUNET_CRYPTO_kdf (iv, | ||
345 | sizeof( | ||
346 | struct GNUNET_CRYPTO_SymmetricInitializationVector), | ||
347 | ctx_iv, | ||
348 | strlen (ctx_iv), | ||
349 | key_material, | ||
350 | sizeof(struct GNUNET_HashCode), | ||
351 | NULL); | ||
352 | } | ||
353 | |||
354 | |||
355 | static void | ||
356 | calculate_key_priv (struct GNUNET_CRYPTO_SymmetricSessionKey *key, | ||
357 | struct GNUNET_CRYPTO_SymmetricInitializationVector *iv, | ||
358 | const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, | ||
359 | const struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pub) | ||
360 | { | ||
361 | struct GNUNET_HashCode key_material; | ||
362 | |||
363 | GNUNET_CRYPTO_ecdsa_ecdh (ecdsa_priv, ecdh_pub, &key_material); | ||
364 | derive_aes_key (key, iv, &key_material); | ||
365 | } | ||
366 | |||
367 | |||
368 | static void | ||
369 | calculate_key_pub (struct GNUNET_CRYPTO_SymmetricSessionKey *key, | ||
370 | struct GNUNET_CRYPTO_SymmetricInitializationVector *iv, | ||
371 | const struct GNUNET_CRYPTO_EcdsaPublicKey *ecdsa_pub, | ||
372 | const struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_priv) | ||
373 | { | ||
374 | struct GNUNET_HashCode key_material; | ||
375 | |||
376 | GNUNET_CRYPTO_ecdh_ecdsa (ecdh_priv, ecdsa_pub, &key_material); | ||
377 | derive_aes_key (key, iv, &key_material); | ||
378 | } | ||
379 | |||
380 | |||
381 | static void | ||
382 | decrypt_payload (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, | ||
383 | const struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pub, | ||
384 | const char *ct, | ||
385 | size_t ct_len, | ||
386 | char *buf) | ||
387 | { | ||
388 | struct GNUNET_CRYPTO_SymmetricSessionKey key; | ||
389 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
390 | |||
391 | calculate_key_priv (&key, &iv, ecdsa_priv, ecdh_pub); | ||
392 | GNUNET_break (GNUNET_CRYPTO_symmetric_decrypt (ct, ct_len, &key, &iv, buf)); | ||
393 | } | ||
394 | |||
395 | |||
396 | static void | ||
397 | encrypt_payload (const struct GNUNET_CRYPTO_EcdsaPublicKey *ecdsa_pub, | ||
398 | const struct GNUNET_CRYPTO_EcdhePrivateKey *ecdh_priv, | ||
399 | const char *payload, | ||
400 | size_t payload_len, | ||
401 | char *buf) | ||
402 | { | ||
403 | struct GNUNET_CRYPTO_SymmetricSessionKey key; | ||
404 | struct GNUNET_CRYPTO_SymmetricInitializationVector iv; | ||
405 | |||
406 | calculate_key_pub (&key, &iv, ecdsa_pub, ecdh_priv); | ||
407 | GNUNET_break ( | ||
408 | GNUNET_CRYPTO_symmetric_encrypt (payload, payload_len, &key, &iv, buf)); | ||
409 | } | ||
410 | |||
411 | |||
412 | /** | 422 | /** |
413 | * Builds an OIDC authorization code including | 423 | * Builds an OIDC authorization code including |
414 | * a reclaim ticket and nonce | 424 | * a reclaim ticket and nonce |
@@ -439,34 +449,21 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, | |||
439 | size_t attr_list_len = 0; | 449 | size_t attr_list_len = 0; |
440 | size_t attests_list_len = 0; | 450 | size_t attests_list_len = 0; |
441 | size_t code_challenge_len = 0; | 451 | size_t code_challenge_len = 0; |
442 | uint32_t nonce; | 452 | uint32_t nonce_len = 0; |
443 | uint32_t nonce_tmp; | ||
444 | struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; | 453 | struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; |
445 | struct GNUNET_CRYPTO_EcdhePrivateKey ecdh_priv; | ||
446 | struct GNUNET_CRYPTO_EcdhePublicKey ecdh_pub; | ||
447 | 454 | ||
448 | /** PLAINTEXT **/ | 455 | /** PLAINTEXT **/ |
449 | // Assign ticket | 456 | // Assign ticket |
450 | memset (¶ms, 0, sizeof(params)); | 457 | memset (¶ms, 0, sizeof(params)); |
451 | params.ticket = *ticket; | 458 | params.ticket = *ticket; |
452 | // Assign nonce | 459 | // Assign nonce |
453 | nonce = 0; | ||
454 | payload_len = sizeof(struct OIDC_Parameters); | 460 | payload_len = sizeof(struct OIDC_Parameters); |
455 | if ((NULL != nonce_str) && (strcmp ("", nonce_str) != 0)) | 461 | if ((NULL != nonce_str) && (strcmp ("", nonce_str) != 0)) |
456 | { | 462 | { |
457 | if ((1 != sscanf (nonce_str, "%u", &nonce)) || (nonce > UINT32_MAX)) | 463 | nonce_len = strlen (nonce_str); |
458 | { | 464 | payload_len += nonce_len; |
459 | GNUNET_break (0); | ||
460 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid nonce %s\n", nonce_str); | ||
461 | return NULL; | ||
462 | } | ||
463 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
464 | "Got nonce: %u from %s\n", | ||
465 | nonce, | ||
466 | nonce_str); | ||
467 | } | 465 | } |
468 | nonce_tmp = htonl (nonce); | 466 | params.nonce_len = htonl (nonce_len); |
469 | params.nonce = nonce_tmp; | ||
470 | // Assign code challenge | 467 | // Assign code challenge |
471 | if (NULL != code_challenge) | 468 | if (NULL != code_challenge) |
472 | code_challenge_len = strlen (code_challenge); | 469 | code_challenge_len = strlen (code_challenge); |
@@ -506,6 +503,11 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, | |||
506 | memcpy (tmp, code_challenge, code_challenge_len); | 503 | memcpy (tmp, code_challenge, code_challenge_len); |
507 | tmp += code_challenge_len; | 504 | tmp += code_challenge_len; |
508 | } | 505 | } |
506 | if (0 < nonce_len) | ||
507 | { | ||
508 | memcpy (tmp, nonce_str, nonce_len); | ||
509 | tmp += nonce_len; | ||
510 | } | ||
509 | if (0 < attr_list_len) | 511 | if (0 < attr_list_len) |
510 | GNUNET_RECLAIM_attribute_list_serialize (attrs, tmp); | 512 | GNUNET_RECLAIM_attribute_list_serialize (attrs, tmp); |
511 | if (0 < attests_list_len) | 513 | if (0 < attests_list_len) |
@@ -513,36 +515,24 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, | |||
513 | 515 | ||
514 | /** END **/ | 516 | /** END **/ |
515 | 517 | ||
516 | /** ENCRYPT **/ | ||
517 | // Get length | 518 | // Get length |
518 | code_payload_len = sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose) | 519 | code_payload_len = sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose) |
519 | + sizeof(struct GNUNET_CRYPTO_EcdhePublicKey) | ||
520 | + payload_len + sizeof(struct | 520 | + payload_len + sizeof(struct |
521 | GNUNET_CRYPTO_EcdsaSignature); | 521 | GNUNET_CRYPTO_EcdsaSignature); |
522 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 522 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
523 | "Length of data to encode: %lu\n", | 523 | "Length of data to encode: %lu\n", |
524 | code_payload_len); | 524 | code_payload_len); |
525 | 525 | ||
526 | // Generate ECDH key | ||
527 | GNUNET_CRYPTO_ecdhe_key_create (&ecdh_priv); | ||
528 | GNUNET_CRYPTO_ecdhe_key_get_public (&ecdh_priv, &ecdh_pub); | ||
529 | // Initialize code payload | 526 | // Initialize code payload |
530 | code_payload = GNUNET_malloc (code_payload_len); | 527 | code_payload = GNUNET_malloc (code_payload_len); |
531 | GNUNET_assert (NULL != code_payload); | 528 | GNUNET_assert (NULL != code_payload); |
532 | purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload; | 529 | purpose = (struct GNUNET_CRYPTO_EccSignaturePurpose *) code_payload; |
533 | purpose->size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose) | 530 | purpose->size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose) |
534 | + sizeof(ecdh_pub) + payload_len); | 531 | + payload_len); |
535 | purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN); | 532 | purpose->purpose = htonl (GNUNET_SIGNATURE_PURPOSE_RECLAIM_CODE_SIGN); |
536 | // Store pubkey | 533 | // Store pubkey |
537 | buf_ptr = (char *) &purpose[1]; | 534 | buf_ptr = (char *) &purpose[1]; |
538 | memcpy (buf_ptr, &ecdh_pub, sizeof(ecdh_pub)); | 535 | memcpy (buf_ptr, payload, payload_len); |
539 | buf_ptr += sizeof(ecdh_pub); | ||
540 | // Encrypt plaintext and store | ||
541 | encrypt_payload (&ticket->audience, | ||
542 | &ecdh_priv, | ||
543 | payload, | ||
544 | payload_len, | ||
545 | buf_ptr); | ||
546 | GNUNET_free (payload); | 536 | GNUNET_free (payload); |
547 | buf_ptr += payload_len; | 537 | buf_ptr += payload_len; |
548 | // Sign and store signature | 538 | // Sign and store signature |
@@ -570,14 +560,16 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, | |||
570 | * | 560 | * |
571 | * @param audience the expected audience of the code | 561 | * @param audience the expected audience of the code |
572 | * @param code the string representation of the code | 562 | * @param code the string representation of the code |
573 | * @param code_verfier PKCE code verifier | 563 | * @param code_verfier PKCE code verifier. Optional, must be provided |
564 | * if used in request. | ||
574 | * @param ticket where to store the ticket | 565 | * @param ticket where to store the ticket |
575 | * @param attrs the attributes in the code | 566 | * @param attrs the attributes in the code |
576 | * @param nonce where to store the nonce | 567 | * @param attests the attestations in the code (if any) |
568 | * @param nonce_str where to store the nonce (if contained) | ||
577 | * @return GNUNET_OK if successful, else GNUNET_SYSERR | 569 | * @return GNUNET_OK if successful, else GNUNET_SYSERR |
578 | */ | 570 | */ |
579 | int | 571 | int |
580 | OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, | 572 | OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience, |
581 | const char *code, | 573 | const char *code, |
582 | const char *code_verifier, | 574 | const char *code_verifier, |
583 | struct GNUNET_RECLAIM_Ticket *ticket, | 575 | struct GNUNET_RECLAIM_Ticket *ticket, |
@@ -595,14 +587,12 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, | |||
595 | char *code_verifier_hash; | 587 | char *code_verifier_hash; |
596 | struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; | 588 | struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; |
597 | struct GNUNET_CRYPTO_EcdsaSignature *signature; | 589 | struct GNUNET_CRYPTO_EcdsaSignature *signature; |
598 | struct GNUNET_CRYPTO_EcdsaPublicKey ecdsa_pub; | ||
599 | struct GNUNET_CRYPTO_EcdhePublicKey *ecdh_pub; | ||
600 | uint32_t code_challenge_len; | 590 | uint32_t code_challenge_len; |
601 | uint32_t attrs_ser_len; | 591 | uint32_t attrs_ser_len; |
602 | uint32_t attests_ser_len; | 592 | uint32_t attests_ser_len; |
603 | size_t plaintext_len; | 593 | size_t plaintext_len; |
604 | size_t code_payload_len; | 594 | size_t code_payload_len; |
605 | uint32_t nonce = 0; | 595 | uint32_t nonce_len = 0; |
606 | struct OIDC_Parameters *params; | 596 | struct OIDC_Parameters *params; |
607 | 597 | ||
608 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to decode `%s'\n", code); | 598 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to decode `%s'\n", code); |
@@ -611,7 +601,6 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, | |||
611 | GNUNET_STRINGS_base64url_decode (code, strlen (code), | 601 | GNUNET_STRINGS_base64url_decode (code, strlen (code), |
612 | (void **) &code_payload); | 602 | (void **) &code_payload); |
613 | if (code_payload_len < sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose) | 603 | if (code_payload_len < sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose) |
614 | + sizeof(struct GNUNET_CRYPTO_EcdhePublicKey) | ||
615 | + sizeof(struct OIDC_Parameters) | 604 | + sizeof(struct OIDC_Parameters) |
616 | + sizeof(struct GNUNET_CRYPTO_EcdsaSignature)) | 605 | + sizeof(struct GNUNET_CRYPTO_EcdsaSignature)) |
617 | { | 606 | { |
@@ -624,16 +613,8 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, | |||
624 | plaintext_len = code_payload_len; | 613 | plaintext_len = code_payload_len; |
625 | plaintext_len -= sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose); | 614 | plaintext_len -= sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose); |
626 | ptr = (char *) &purpose[1]; | 615 | ptr = (char *) &purpose[1]; |
627 | // Public ECDH key | ||
628 | ecdh_pub = (struct GNUNET_CRYPTO_EcdhePublicKey *) ptr; | ||
629 | ptr += sizeof(struct GNUNET_CRYPTO_EcdhePublicKey); | ||
630 | plaintext_len -= sizeof(struct GNUNET_CRYPTO_EcdhePublicKey); | ||
631 | |||
632 | // Decrypt ciphertext | ||
633 | plaintext_len -= sizeof(struct GNUNET_CRYPTO_EcdsaSignature); | 616 | plaintext_len -= sizeof(struct GNUNET_CRYPTO_EcdsaSignature); |
634 | plaintext = GNUNET_malloc (plaintext_len); | 617 | plaintext = ptr; |
635 | decrypt_payload (ecdsa_priv, ecdh_pub, ptr, plaintext_len, plaintext); | ||
636 | // ptr = plaintext; | ||
637 | ptr += plaintext_len; | 618 | ptr += plaintext_len; |
638 | signature = (struct GNUNET_CRYPTO_EcdsaSignature *) ptr; | 619 | signature = (struct GNUNET_CRYPTO_EcdsaSignature *) ptr; |
639 | params = (struct OIDC_Parameters *) plaintext; | 620 | params = (struct OIDC_Parameters *) plaintext; |
@@ -658,11 +639,10 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, | |||
658 | // encode code verifier | 639 | // encode code verifier |
659 | GNUNET_STRINGS_base64url_encode (code_verifier_hash, 256 / 8, | 640 | GNUNET_STRINGS_base64url_encode (code_verifier_hash, 256 / 8, |
660 | &expected_code_challenge); | 641 | &expected_code_challenge); |
661 | code_challenge = (char *) ¶ms[1]; | 642 | code_challenge = ((char *) ¶ms[1]); |
662 | GNUNET_free (code_verifier_hash); | 643 | GNUNET_free (code_verifier_hash); |
663 | if ((strlen (expected_code_challenge) != code_challenge_len) || | 644 | if (0 != |
664 | (0 != | 645 | strncmp (expected_code_challenge, code_challenge, code_challenge_len)) |
665 | strncmp (expected_code_challenge, code_challenge, code_challenge_len))) | ||
666 | { | 646 | { |
667 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 647 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
668 | "Invalid code verifier! Expected: %s, Got: %.*s\n", | 648 | "Invalid code verifier! Expected: %s, Got: %.*s\n", |
@@ -675,17 +655,23 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, | |||
675 | } | 655 | } |
676 | GNUNET_free (expected_code_challenge); | 656 | GNUNET_free (expected_code_challenge); |
677 | } | 657 | } |
658 | nonce_len = ntohl (params->nonce_len); | ||
659 | if (0 != nonce_len) | ||
660 | { | ||
661 | *nonce_str = GNUNET_strndup (code_challenge + code_challenge_len, | ||
662 | nonce_len); | ||
663 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got nonce: %s\n", *nonce_str); | ||
664 | } | ||
665 | |||
678 | // Ticket | 666 | // Ticket |
679 | memcpy (ticket, ¶ms->ticket, sizeof(params->ticket)); | 667 | memcpy (ticket, ¶ms->ticket, sizeof(params->ticket)); |
680 | // Nonce | ||
681 | nonce = ntohl (params->nonce); // ntohl (*((uint32_t *) ptr)); | ||
682 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got nonce: %u\n", nonce); | ||
683 | // Signature | 668 | // Signature |
684 | GNUNET_CRYPTO_ecdsa_key_get_public (ecdsa_priv, &ecdsa_pub); | 669 | // GNUNET_CRYPTO_ecdsa_key_get_public (ecdsa_priv, &ecdsa_pub); |
685 | if (0 != GNUNET_memcmp (&ecdsa_pub, &ticket->audience)) | 670 | if (0 != GNUNET_memcmp (audience, &ticket->audience)) |
686 | { | 671 | { |
687 | GNUNET_free (code_payload); | 672 | GNUNET_free (code_payload); |
688 | GNUNET_free (plaintext); | 673 | if (NULL != *nonce_str) |
674 | GNUNET_free (*nonce_str); | ||
689 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 675 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
690 | "Audience in ticket does not match client!\n"); | 676 | "Audience in ticket does not match client!\n"); |
691 | return GNUNET_SYSERR; | 677 | return GNUNET_SYSERR; |
@@ -697,12 +683,13 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, | |||
697 | &ticket->identity)) | 683 | &ticket->identity)) |
698 | { | 684 | { |
699 | GNUNET_free (code_payload); | 685 | GNUNET_free (code_payload); |
700 | GNUNET_free (plaintext); | 686 | if (NULL != *nonce_str) |
687 | GNUNET_free (*nonce_str); | ||
701 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signature of AuthZ code invalid!\n"); | 688 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Signature of AuthZ code invalid!\n"); |
702 | return GNUNET_SYSERR; | 689 | return GNUNET_SYSERR; |
703 | } | 690 | } |
704 | // Attributes | 691 | // Attributes |
705 | attrs_ser = ((char *) ¶ms[1]) + code_challenge_len; | 692 | attrs_ser = ((char *) ¶ms[1]) + code_challenge_len + nonce_len; |
706 | attrs_ser_len = ntohl (params->attr_list_len); | 693 | attrs_ser_len = ntohl (params->attr_list_len); |
707 | *attrs = GNUNET_RECLAIM_attribute_list_deserialize (attrs_ser, attrs_ser_len); | 694 | *attrs = GNUNET_RECLAIM_attribute_list_deserialize (attrs_ser, attrs_ser_len); |
708 | attests_ser = ((char*) attrs_ser) + attrs_ser_len; | 695 | attests_ser = ((char*) attrs_ser) + attrs_ser_len; |
@@ -710,11 +697,7 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *ecdsa_priv, | |||
710 | *attests = GNUNET_RECLAIM_attestation_list_deserialize (attests_ser, | 697 | *attests = GNUNET_RECLAIM_attestation_list_deserialize (attests_ser, |
711 | attests_ser_len); | 698 | attests_ser_len); |
712 | 699 | ||
713 | *nonce_str = NULL; | ||
714 | if (nonce != 0) | ||
715 | GNUNET_asprintf (nonce_str, "%u", nonce); | ||
716 | GNUNET_free (code_payload); | 700 | GNUNET_free (code_payload); |
717 | GNUNET_free (plaintext); | ||
718 | return GNUNET_OK; | 701 | return GNUNET_OK; |
719 | } | 702 | } |
720 | 703 | ||
@@ -757,15 +740,102 @@ OIDC_build_token_response (const char *access_token, | |||
757 | * Generate a new access token | 740 | * Generate a new access token |
758 | */ | 741 | */ |
759 | char * | 742 | char * |
760 | OIDC_access_token_new () | 743 | OIDC_access_token_new (const struct GNUNET_RECLAIM_Ticket *ticket) |
761 | { | 744 | { |
762 | char *access_token; | 745 | char *access_token; |
763 | uint64_t random_number; | ||
764 | 746 | ||
765 | random_number = | 747 | GNUNET_STRINGS_base64_encode (ticket, |
766 | GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); | 748 | sizeof(*ticket), |
767 | GNUNET_STRINGS_base64_encode (&random_number, | ||
768 | sizeof(uint64_t), | ||
769 | &access_token); | 749 | &access_token); |
770 | return access_token; | 750 | return access_token; |
771 | } | 751 | } |
752 | |||
753 | |||
754 | /** | ||
755 | * Parse an access token | ||
756 | */ | ||
757 | int | ||
758 | OIDC_access_token_parse (const char *token, | ||
759 | struct GNUNET_RECLAIM_Ticket **ticket) | ||
760 | { | ||
761 | if (sizeof (struct GNUNET_RECLAIM_Ticket) != | ||
762 | GNUNET_STRINGS_base64_decode (token, | ||
763 | strlen (token), | ||
764 | (void**) ticket)) | ||
765 | return GNUNET_SYSERR; | ||
766 | return GNUNET_OK; | ||
767 | } | ||
768 | |||
769 | |||
770 | /** | ||
771 | * Checks if a claim is implicitly requested through standard | ||
772 | * scope(s) | ||
773 | * | ||
774 | * @param scopes the scopes which have been requested | ||
775 | * @param attr the attribute name to check | ||
776 | * @return GNUNET_YES if attribute is implcitly requested | ||
777 | */ | ||
778 | enum GNUNET_GenericReturnValue | ||
779 | OIDC_check_scopes_for_claim_request (const char*scopes, | ||
780 | const char*attr) | ||
781 | { | ||
782 | char *scope_variables; | ||
783 | char *scope_variable; | ||
784 | char delimiter[] = " "; | ||
785 | int i; | ||
786 | |||
787 | scope_variables = GNUNET_strdup (scopes); | ||
788 | scope_variable = strtok (scope_variables, delimiter); | ||
789 | while (NULL != scope_variable) | ||
790 | { | ||
791 | if (0 == strcmp ("profile", scope_variable)) | ||
792 | { | ||
793 | for (i = 0; i < 14; i++) | ||
794 | { | ||
795 | if (0 == strcmp (attr, OIDC_profile_claims[i])) | ||
796 | { | ||
797 | GNUNET_free (scope_variables); | ||
798 | return GNUNET_YES; | ||
799 | } | ||
800 | } | ||
801 | } | ||
802 | else if (0 == strcmp ("address", scope_variable)) | ||
803 | { | ||
804 | for (i = 0; i < 5; i++) | ||
805 | { | ||
806 | if (0 == strcmp (attr, OIDC_address_claims[i])) | ||
807 | { | ||
808 | GNUNET_free (scope_variables); | ||
809 | return GNUNET_YES; | ||
810 | } | ||
811 | } | ||
812 | } | ||
813 | else if (0 == strcmp ("email", scope_variable)) | ||
814 | { | ||
815 | for (i = 0; i < 2; i++) | ||
816 | { | ||
817 | if (0 == strcmp (attr, OIDC_email_claims[i])) | ||
818 | { | ||
819 | GNUNET_free (scope_variables); | ||
820 | return GNUNET_YES; | ||
821 | } | ||
822 | } | ||
823 | } | ||
824 | else if (0 == strcmp ("phone", scope_variable)) | ||
825 | { | ||
826 | for (i = 0; i < 2; i++) | ||
827 | { | ||
828 | if (0 == strcmp (attr, OIDC_phone_claims[i])) | ||
829 | { | ||
830 | GNUNET_free (scope_variables); | ||
831 | return GNUNET_YES; | ||
832 | } | ||
833 | } | ||
834 | |||
835 | } | ||
836 | scope_variable = strtok (NULL, delimiter); | ||
837 | } | ||
838 | GNUNET_free (scope_variables); | ||
839 | return GNUNET_NO; | ||
840 | |||
841 | } | ||