Home | History | Annotate | Download | only in jwt
      1 /*
      2  *
      3  * Copyright 2015 gRPC authors.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  *
     17  */
     18 
     19 #include <grpc/support/port_platform.h>
     20 
     21 #include "src/core/tsi/grpc_shadow_boringssl.h"
     22 
     23 #include "src/core/lib/security/credentials/jwt/jwt_verifier.h"
     24 
     25 #include <limits.h>
     26 #include <string.h>
     27 
     28 #include <grpc/support/alloc.h>
     29 #include <grpc/support/log.h>
     30 #include <grpc/support/string_util.h>
     31 #include <grpc/support/sync.h>
     32 
     33 extern "C" {
     34 #include <openssl/pem.h>
     35 }
     36 
     37 #include "src/core/lib/gpr/string.h"
     38 #include "src/core/lib/http/httpcli.h"
     39 #include "src/core/lib/iomgr/polling_entity.h"
     40 #include "src/core/lib/slice/b64.h"
     41 #include "src/core/lib/slice/slice_internal.h"
     42 #include "src/core/tsi/ssl_types.h"
     43 
     44 /* --- Utils. --- */
     45 
     46 const char* grpc_jwt_verifier_status_to_string(
     47     grpc_jwt_verifier_status status) {
     48   switch (status) {
     49     case GRPC_JWT_VERIFIER_OK:
     50       return "OK";
     51     case GRPC_JWT_VERIFIER_BAD_SIGNATURE:
     52       return "BAD_SIGNATURE";
     53     case GRPC_JWT_VERIFIER_BAD_FORMAT:
     54       return "BAD_FORMAT";
     55     case GRPC_JWT_VERIFIER_BAD_AUDIENCE:
     56       return "BAD_AUDIENCE";
     57     case GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR:
     58       return "KEY_RETRIEVAL_ERROR";
     59     case GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE:
     60       return "TIME_CONSTRAINT_FAILURE";
     61     case GRPC_JWT_VERIFIER_GENERIC_ERROR:
     62       return "GENERIC_ERROR";
     63     default:
     64       return "UNKNOWN";
     65   }
     66 }
     67 
     68 static const EVP_MD* evp_md_from_alg(const char* alg) {
     69   if (strcmp(alg, "RS256") == 0) {
     70     return EVP_sha256();
     71   } else if (strcmp(alg, "RS384") == 0) {
     72     return EVP_sha384();
     73   } else if (strcmp(alg, "RS512") == 0) {
     74     return EVP_sha512();
     75   } else {
     76     return nullptr;
     77   }
     78 }
     79 
     80 static grpc_json* parse_json_part_from_jwt(const char* str, size_t len,
     81                                            grpc_slice* buffer) {
     82   grpc_json* json;
     83 
     84   *buffer = grpc_base64_decode_with_len(str, len, 1);
     85   if (GRPC_SLICE_IS_EMPTY(*buffer)) {
     86     gpr_log(GPR_ERROR, "Invalid base64.");
     87     return nullptr;
     88   }
     89   json = grpc_json_parse_string_with_len(
     90       reinterpret_cast<char*> GRPC_SLICE_START_PTR(*buffer),
     91       GRPC_SLICE_LENGTH(*buffer));
     92   if (json == nullptr) {
     93     grpc_slice_unref_internal(*buffer);
     94     gpr_log(GPR_ERROR, "JSON parsing error.");
     95   }
     96   return json;
     97 }
     98 
     99 static const char* validate_string_field(const grpc_json* json,
    100                                          const char* key) {
    101   if (json->type != GRPC_JSON_STRING) {
    102     gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value);
    103     return nullptr;
    104   }
    105   return json->value;
    106 }
    107 
    108 static gpr_timespec validate_time_field(const grpc_json* json,
    109                                         const char* key) {
    110   gpr_timespec result = gpr_time_0(GPR_CLOCK_REALTIME);
    111   if (json->type != GRPC_JSON_NUMBER) {
    112     gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value);
    113     return result;
    114   }
    115   result.tv_sec = strtol(json->value, nullptr, 10);
    116   return result;
    117 }
    118 
    119 /* --- JOSE header. see http://tools.ietf.org/html/rfc7515#section-4 --- */
    120 
    121 typedef struct {
    122   const char* alg;
    123   const char* kid;
    124   const char* typ;
    125   /* TODO(jboeuf): Add others as needed (jku, jwk, x5u, x5c and so on...). */
    126   grpc_slice buffer;
    127 } jose_header;
    128 
    129 static void jose_header_destroy(jose_header* h) {
    130   grpc_slice_unref_internal(h->buffer);
    131   gpr_free(h);
    132 }
    133 
    134 /* Takes ownership of json and buffer. */
    135 static jose_header* jose_header_from_json(grpc_json* json, grpc_slice buffer) {
    136   grpc_json* cur;
    137   jose_header* h = static_cast<jose_header*>(gpr_zalloc(sizeof(jose_header)));
    138   h->buffer = buffer;
    139   for (cur = json->child; cur != nullptr; cur = cur->next) {
    140     if (strcmp(cur->key, "alg") == 0) {
    141       /* We only support RSA-1.5 signatures for now.
    142          Beware of this if we add HMAC support:
    143          https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
    144        */
    145       if (cur->type != GRPC_JSON_STRING || strncmp(cur->value, "RS", 2) ||
    146           evp_md_from_alg(cur->value) == nullptr) {
    147         gpr_log(GPR_ERROR, "Invalid alg field [%s]", cur->value);
    148         goto error;
    149       }
    150       h->alg = cur->value;
    151     } else if (strcmp(cur->key, "typ") == 0) {
    152       h->typ = validate_string_field(cur, "typ");
    153       if (h->typ == nullptr) goto error;
    154     } else if (strcmp(cur->key, "kid") == 0) {
    155       h->kid = validate_string_field(cur, "kid");
    156       if (h->kid == nullptr) goto error;
    157     }
    158   }
    159   if (h->alg == nullptr) {
    160     gpr_log(GPR_ERROR, "Missing alg field.");
    161     goto error;
    162   }
    163   grpc_json_destroy(json);
    164   h->buffer = buffer;
    165   return h;
    166 
    167 error:
    168   grpc_json_destroy(json);
    169   jose_header_destroy(h);
    170   return nullptr;
    171 }
    172 
    173 /* --- JWT claims. see http://tools.ietf.org/html/rfc7519#section-4.1 */
    174 
    175 struct grpc_jwt_claims {
    176   /* Well known properties already parsed. */
    177   const char* sub;
    178   const char* iss;
    179   const char* aud;
    180   const char* jti;
    181   gpr_timespec iat;
    182   gpr_timespec exp;
    183   gpr_timespec nbf;
    184 
    185   grpc_json* json;
    186   grpc_slice buffer;
    187 };
    188 
    189 void grpc_jwt_claims_destroy(grpc_jwt_claims* claims) {
    190   grpc_json_destroy(claims->json);
    191   grpc_slice_unref_internal(claims->buffer);
    192   gpr_free(claims);
    193 }
    194 
    195 const grpc_json* grpc_jwt_claims_json(const grpc_jwt_claims* claims) {
    196   if (claims == nullptr) return nullptr;
    197   return claims->json;
    198 }
    199 
    200 const char* grpc_jwt_claims_subject(const grpc_jwt_claims* claims) {
    201   if (claims == nullptr) return nullptr;
    202   return claims->sub;
    203 }
    204 
    205 const char* grpc_jwt_claims_issuer(const grpc_jwt_claims* claims) {
    206   if (claims == nullptr) return nullptr;
    207   return claims->iss;
    208 }
    209 
    210 const char* grpc_jwt_claims_id(const grpc_jwt_claims* claims) {
    211   if (claims == nullptr) return nullptr;
    212   return claims->jti;
    213 }
    214 
    215 const char* grpc_jwt_claims_audience(const grpc_jwt_claims* claims) {
    216   if (claims == nullptr) return nullptr;
    217   return claims->aud;
    218 }
    219 
    220 gpr_timespec grpc_jwt_claims_issued_at(const grpc_jwt_claims* claims) {
    221   if (claims == nullptr) return gpr_inf_past(GPR_CLOCK_REALTIME);
    222   return claims->iat;
    223 }
    224 
    225 gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims* claims) {
    226   if (claims == nullptr) return gpr_inf_future(GPR_CLOCK_REALTIME);
    227   return claims->exp;
    228 }
    229 
    230 gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims* claims) {
    231   if (claims == nullptr) return gpr_inf_past(GPR_CLOCK_REALTIME);
    232   return claims->nbf;
    233 }
    234 
    235 /* Takes ownership of json and buffer even in case of failure. */
    236 grpc_jwt_claims* grpc_jwt_claims_from_json(grpc_json* json, grpc_slice buffer) {
    237   grpc_json* cur;
    238   grpc_jwt_claims* claims =
    239       static_cast<grpc_jwt_claims*>(gpr_malloc(sizeof(grpc_jwt_claims)));
    240   memset(claims, 0, sizeof(grpc_jwt_claims));
    241   claims->json = json;
    242   claims->buffer = buffer;
    243   claims->iat = gpr_inf_past(GPR_CLOCK_REALTIME);
    244   claims->nbf = gpr_inf_past(GPR_CLOCK_REALTIME);
    245   claims->exp = gpr_inf_future(GPR_CLOCK_REALTIME);
    246 
    247   /* Per the spec, all fields are optional. */
    248   for (cur = json->child; cur != nullptr; cur = cur->next) {
    249     if (strcmp(cur->key, "sub") == 0) {
    250       claims->sub = validate_string_field(cur, "sub");
    251       if (claims->sub == nullptr) goto error;
    252     } else if (strcmp(cur->key, "iss") == 0) {
    253       claims->iss = validate_string_field(cur, "iss");
    254       if (claims->iss == nullptr) goto error;
    255     } else if (strcmp(cur->key, "aud") == 0) {
    256       claims->aud = validate_string_field(cur, "aud");
    257       if (claims->aud == nullptr) goto error;
    258     } else if (strcmp(cur->key, "jti") == 0) {
    259       claims->jti = validate_string_field(cur, "jti");
    260       if (claims->jti == nullptr) goto error;
    261     } else if (strcmp(cur->key, "iat") == 0) {
    262       claims->iat = validate_time_field(cur, "iat");
    263       if (gpr_time_cmp(claims->iat, gpr_time_0(GPR_CLOCK_REALTIME)) == 0)
    264         goto error;
    265     } else if (strcmp(cur->key, "exp") == 0) {
    266       claims->exp = validate_time_field(cur, "exp");
    267       if (gpr_time_cmp(claims->exp, gpr_time_0(GPR_CLOCK_REALTIME)) == 0)
    268         goto error;
    269     } else if (strcmp(cur->key, "nbf") == 0) {
    270       claims->nbf = validate_time_field(cur, "nbf");
    271       if (gpr_time_cmp(claims->nbf, gpr_time_0(GPR_CLOCK_REALTIME)) == 0)
    272         goto error;
    273     }
    274   }
    275   return claims;
    276 
    277 error:
    278   grpc_jwt_claims_destroy(claims);
    279   return nullptr;
    280 }
    281 
    282 grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims* claims,
    283                                                const char* audience) {
    284   gpr_timespec skewed_now;
    285   int audience_ok;
    286 
    287   GPR_ASSERT(claims != nullptr);
    288 
    289   skewed_now =
    290       gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew);
    291   if (gpr_time_cmp(skewed_now, claims->nbf) < 0) {
    292     gpr_log(GPR_ERROR, "JWT is not valid yet.");
    293     return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE;
    294   }
    295   skewed_now =
    296       gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew);
    297   if (gpr_time_cmp(skewed_now, claims->exp) > 0) {
    298     gpr_log(GPR_ERROR, "JWT is expired.");
    299     return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE;
    300   }
    301 
    302   /* This should be probably up to the upper layer to decide but let's harcode
    303      the 99% use case here for email issuers, where the JWT must be self
    304      issued. */
    305   if (grpc_jwt_issuer_email_domain(claims->iss) != nullptr &&
    306       claims->sub != nullptr && strcmp(claims->iss, claims->sub) != 0) {
    307     gpr_log(GPR_ERROR,
    308             "Email issuer (%s) cannot assert another subject (%s) than itself.",
    309             claims->iss, claims->sub);
    310     return GRPC_JWT_VERIFIER_BAD_SUBJECT;
    311   }
    312 
    313   if (audience == nullptr) {
    314     audience_ok = claims->aud == nullptr;
    315   } else {
    316     audience_ok = claims->aud != nullptr && strcmp(audience, claims->aud) == 0;
    317   }
    318   if (!audience_ok) {
    319     gpr_log(GPR_ERROR, "Audience mismatch: expected %s and found %s.",
    320             audience == nullptr ? "NULL" : audience,
    321             claims->aud == nullptr ? "NULL" : claims->aud);
    322     return GRPC_JWT_VERIFIER_BAD_AUDIENCE;
    323   }
    324   return GRPC_JWT_VERIFIER_OK;
    325 }
    326 
    327 /* --- verifier_cb_ctx object. --- */
    328 
    329 typedef enum {
    330   HTTP_RESPONSE_OPENID = 0,
    331   HTTP_RESPONSE_KEYS,
    332   HTTP_RESPONSE_COUNT /* must be last */
    333 } http_response_index;
    334 
    335 typedef struct {
    336   grpc_jwt_verifier* verifier;
    337   grpc_polling_entity pollent;
    338   jose_header* header;
    339   grpc_jwt_claims* claims;
    340   char* audience;
    341   grpc_slice signature;
    342   grpc_slice signed_data;
    343   void* user_data;
    344   grpc_jwt_verification_done_cb user_cb;
    345   grpc_http_response responses[HTTP_RESPONSE_COUNT];
    346 } verifier_cb_ctx;
    347 
    348 /* Takes ownership of the header, claims and signature. */
    349 static verifier_cb_ctx* verifier_cb_ctx_create(
    350     grpc_jwt_verifier* verifier, grpc_pollset* pollset, jose_header* header,
    351     grpc_jwt_claims* claims, const char* audience, grpc_slice signature,
    352     const char* signed_jwt, size_t signed_jwt_len, void* user_data,
    353     grpc_jwt_verification_done_cb cb) {
    354   grpc_core::ExecCtx exec_ctx;
    355   verifier_cb_ctx* ctx =
    356       static_cast<verifier_cb_ctx*>(gpr_zalloc(sizeof(verifier_cb_ctx)));
    357   ctx->verifier = verifier;
    358   ctx->pollent = grpc_polling_entity_create_from_pollset(pollset);
    359   ctx->header = header;
    360   ctx->audience = gpr_strdup(audience);
    361   ctx->claims = claims;
    362   ctx->signature = signature;
    363   ctx->signed_data = grpc_slice_from_copied_buffer(signed_jwt, signed_jwt_len);
    364   ctx->user_data = user_data;
    365   ctx->user_cb = cb;
    366 
    367   return ctx;
    368 }
    369 
    370 void verifier_cb_ctx_destroy(verifier_cb_ctx* ctx) {
    371   if (ctx->audience != nullptr) gpr_free(ctx->audience);
    372   if (ctx->claims != nullptr) grpc_jwt_claims_destroy(ctx->claims);
    373   grpc_slice_unref_internal(ctx->signature);
    374   grpc_slice_unref_internal(ctx->signed_data);
    375   jose_header_destroy(ctx->header);
    376   for (size_t i = 0; i < HTTP_RESPONSE_COUNT; i++) {
    377     grpc_http_response_destroy(&ctx->responses[i]);
    378   }
    379   /* TODO: see what to do with claims... */
    380   gpr_free(ctx);
    381 }
    382 
    383 /* --- grpc_jwt_verifier object. --- */
    384 
    385 /* Clock skew defaults to one minute. */
    386 gpr_timespec grpc_jwt_verifier_clock_skew = {60, 0, GPR_TIMESPAN};
    387 
    388 /* Max delay defaults to one minute. */
    389 grpc_millis grpc_jwt_verifier_max_delay = 60 * GPR_MS_PER_SEC;
    390 
    391 typedef struct {
    392   char* email_domain;
    393   char* key_url_prefix;
    394 } email_key_mapping;
    395 
    396 struct grpc_jwt_verifier {
    397   email_key_mapping* mappings;
    398   size_t num_mappings; /* Should be very few, linear search ok. */
    399   size_t allocated_mappings;
    400   grpc_httpcli_context http_ctx;
    401 };
    402 
    403 static grpc_json* json_from_http(const grpc_httpcli_response* response) {
    404   grpc_json* json = nullptr;
    405 
    406   if (response == nullptr) {
    407     gpr_log(GPR_ERROR, "HTTP response is NULL.");
    408     return nullptr;
    409   }
    410   if (response->status != 200) {
    411     gpr_log(GPR_ERROR, "Call to http server failed with error %d.",
    412             response->status);
    413     return nullptr;
    414   }
    415 
    416   json = grpc_json_parse_string_with_len(response->body, response->body_length);
    417   if (json == nullptr) {
    418     gpr_log(GPR_ERROR, "Invalid JSON found in response.");
    419   }
    420   return json;
    421 }
    422 
    423 static const grpc_json* find_property_by_name(const grpc_json* json,
    424                                               const char* name) {
    425   const grpc_json* cur;
    426   for (cur = json->child; cur != nullptr; cur = cur->next) {
    427     if (strcmp(cur->key, name) == 0) return cur;
    428   }
    429   return nullptr;
    430 }
    431 
    432 static EVP_PKEY* extract_pkey_from_x509(const char* x509_str) {
    433   X509* x509 = nullptr;
    434   EVP_PKEY* result = nullptr;
    435   BIO* bio = BIO_new(BIO_s_mem());
    436   size_t len = strlen(x509_str);
    437   GPR_ASSERT(len < INT_MAX);
    438   BIO_write(bio, x509_str, static_cast<int>(len));
    439   x509 = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
    440   if (x509 == nullptr) {
    441     gpr_log(GPR_ERROR, "Unable to parse x509 cert.");
    442     goto end;
    443   }
    444   result = X509_get_pubkey(x509);
    445   if (result == nullptr) {
    446     gpr_log(GPR_ERROR, "Cannot find public key in X509 cert.");
    447   }
    448 
    449 end:
    450   BIO_free(bio);
    451   X509_free(x509);
    452   return result;
    453 }
    454 
    455 static BIGNUM* bignum_from_base64(const char* b64) {
    456   BIGNUM* result = nullptr;
    457   grpc_slice bin;
    458 
    459   if (b64 == nullptr) return nullptr;
    460   bin = grpc_base64_decode(b64, 1);
    461   if (GRPC_SLICE_IS_EMPTY(bin)) {
    462     gpr_log(GPR_ERROR, "Invalid base64 for big num.");
    463     return nullptr;
    464   }
    465   result = BN_bin2bn(GRPC_SLICE_START_PTR(bin),
    466                      TSI_SIZE_AS_SIZE(GRPC_SLICE_LENGTH(bin)), nullptr);
    467   grpc_slice_unref_internal(bin);
    468   return result;
    469 }
    470 
    471 #if OPENSSL_VERSION_NUMBER < 0x10100000L
    472 
    473 // Provide compatibility across OpenSSL 1.02 and 1.1.
    474 static int RSA_set0_key(RSA* r, BIGNUM* n, BIGNUM* e, BIGNUM* d) {
    475   /* If the fields n and e in r are NULL, the corresponding input
    476    * parameters MUST be non-NULL for n and e.  d may be
    477    * left NULL (in case only the public key is used).
    478    */
    479   if ((r->n == nullptr && n == nullptr) || (r->e == nullptr && e == nullptr)) {
    480     return 0;
    481   }
    482 
    483   if (n != nullptr) {
    484     BN_free(r->n);
    485     r->n = n;
    486   }
    487   if (e != nullptr) {
    488     BN_free(r->e);
    489     r->e = e;
    490   }
    491   if (d != nullptr) {
    492     BN_free(r->d);
    493     r->d = d;
    494   }
    495 
    496   return 1;
    497 }
    498 #endif  // OPENSSL_VERSION_NUMBER < 0x10100000L
    499 
    500 static EVP_PKEY* pkey_from_jwk(const grpc_json* json, const char* kty) {
    501   const grpc_json* key_prop;
    502   RSA* rsa = nullptr;
    503   EVP_PKEY* result = nullptr;
    504   BIGNUM* tmp_n = nullptr;
    505   BIGNUM* tmp_e = nullptr;
    506 
    507   GPR_ASSERT(kty != nullptr && json != nullptr);
    508   if (strcmp(kty, "RSA") != 0) {
    509     gpr_log(GPR_ERROR, "Unsupported key type %s.", kty);
    510     goto end;
    511   }
    512   rsa = RSA_new();
    513   if (rsa == nullptr) {
    514     gpr_log(GPR_ERROR, "Could not create rsa key.");
    515     goto end;
    516   }
    517   for (key_prop = json->child; key_prop != nullptr; key_prop = key_prop->next) {
    518     if (strcmp(key_prop->key, "n") == 0) {
    519       tmp_n = bignum_from_base64(validate_string_field(key_prop, "n"));
    520       if (tmp_n == nullptr) goto end;
    521     } else if (strcmp(key_prop->key, "e") == 0) {
    522       tmp_e = bignum_from_base64(validate_string_field(key_prop, "e"));
    523       if (tmp_e == nullptr) goto end;
    524     }
    525   }
    526   if (tmp_e == nullptr || tmp_n == nullptr) {
    527     gpr_log(GPR_ERROR, "Missing RSA public key field.");
    528     goto end;
    529   }
    530   if (!RSA_set0_key(rsa, tmp_n, tmp_e, nullptr)) {
    531     gpr_log(GPR_ERROR, "Cannot set RSA key from inputs.");
    532     goto end;
    533   }
    534   /* RSA_set0_key takes ownership on success. */
    535   tmp_n = nullptr;
    536   tmp_e = nullptr;
    537   result = EVP_PKEY_new();
    538   EVP_PKEY_set1_RSA(result, rsa); /* uprefs rsa. */
    539 
    540 end:
    541   RSA_free(rsa);
    542   BN_free(tmp_n);
    543   BN_free(tmp_e);
    544   return result;
    545 }
    546 
    547 static EVP_PKEY* find_verification_key(const grpc_json* json,
    548                                        const char* header_alg,
    549                                        const char* header_kid) {
    550   const grpc_json* jkey;
    551   const grpc_json* jwk_keys;
    552   /* Try to parse the json as a JWK set:
    553      https://tools.ietf.org/html/rfc7517#section-5. */
    554   jwk_keys = find_property_by_name(json, "keys");
    555   if (jwk_keys == nullptr) {
    556     /* Use the google proprietary format which is:
    557        { <kid1>: <x5091>, <kid2>: <x5092>, ... } */
    558     const grpc_json* cur = find_property_by_name(json, header_kid);
    559     if (cur == nullptr) return nullptr;
    560     return extract_pkey_from_x509(cur->value);
    561   }
    562 
    563   if (jwk_keys->type != GRPC_JSON_ARRAY) {
    564     gpr_log(GPR_ERROR,
    565             "Unexpected value type of keys property in jwks key set.");
    566     return nullptr;
    567   }
    568   /* Key format is specified in:
    569      https://tools.ietf.org/html/rfc7518#section-6. */
    570   for (jkey = jwk_keys->child; jkey != nullptr; jkey = jkey->next) {
    571     grpc_json* key_prop;
    572     const char* alg = nullptr;
    573     const char* kid = nullptr;
    574     const char* kty = nullptr;
    575 
    576     if (jkey->type != GRPC_JSON_OBJECT) continue;
    577     for (key_prop = jkey->child; key_prop != nullptr;
    578          key_prop = key_prop->next) {
    579       if (strcmp(key_prop->key, "alg") == 0 &&
    580           key_prop->type == GRPC_JSON_STRING) {
    581         alg = key_prop->value;
    582       } else if (strcmp(key_prop->key, "kid") == 0 &&
    583                  key_prop->type == GRPC_JSON_STRING) {
    584         kid = key_prop->value;
    585       } else if (strcmp(key_prop->key, "kty") == 0 &&
    586                  key_prop->type == GRPC_JSON_STRING) {
    587         kty = key_prop->value;
    588       }
    589     }
    590     if (alg != nullptr && kid != nullptr && kty != nullptr &&
    591         strcmp(kid, header_kid) == 0 && strcmp(alg, header_alg) == 0) {
    592       return pkey_from_jwk(jkey, kty);
    593     }
    594   }
    595   gpr_log(GPR_ERROR,
    596           "Could not find matching key in key set for kid=%s and alg=%s",
    597           header_kid, header_alg);
    598   return nullptr;
    599 }
    600 
    601 static int verify_jwt_signature(EVP_PKEY* key, const char* alg,
    602                                 grpc_slice signature, grpc_slice signed_data) {
    603   EVP_MD_CTX* md_ctx = EVP_MD_CTX_create();
    604   const EVP_MD* md = evp_md_from_alg(alg);
    605   int result = 0;
    606 
    607   GPR_ASSERT(md != nullptr); /* Checked before. */
    608   if (md_ctx == nullptr) {
    609     gpr_log(GPR_ERROR, "Could not create EVP_MD_CTX.");
    610     goto end;
    611   }
    612   if (EVP_DigestVerifyInit(md_ctx, nullptr, md, nullptr, key) != 1) {
    613     gpr_log(GPR_ERROR, "EVP_DigestVerifyInit failed.");
    614     goto end;
    615   }
    616   if (EVP_DigestVerifyUpdate(md_ctx, GRPC_SLICE_START_PTR(signed_data),
    617                              GRPC_SLICE_LENGTH(signed_data)) != 1) {
    618     gpr_log(GPR_ERROR, "EVP_DigestVerifyUpdate failed.");
    619     goto end;
    620   }
    621   if (EVP_DigestVerifyFinal(md_ctx, GRPC_SLICE_START_PTR(signature),
    622                             GRPC_SLICE_LENGTH(signature)) != 1) {
    623     gpr_log(GPR_ERROR, "JWT signature verification failed.");
    624     goto end;
    625   }
    626   result = 1;
    627 
    628 end:
    629   EVP_MD_CTX_destroy(md_ctx);
    630   return result;
    631 }
    632 
    633 static void on_keys_retrieved(void* user_data, grpc_error* error) {
    634   verifier_cb_ctx* ctx = static_cast<verifier_cb_ctx*>(user_data);
    635   grpc_json* json = json_from_http(&ctx->responses[HTTP_RESPONSE_KEYS]);
    636   EVP_PKEY* verification_key = nullptr;
    637   grpc_jwt_verifier_status status = GRPC_JWT_VERIFIER_GENERIC_ERROR;
    638   grpc_jwt_claims* claims = nullptr;
    639 
    640   if (json == nullptr) {
    641     status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR;
    642     goto end;
    643   }
    644   verification_key =
    645       find_verification_key(json, ctx->header->alg, ctx->header->kid);
    646   if (verification_key == nullptr) {
    647     gpr_log(GPR_ERROR, "Could not find verification key with kid %s.",
    648             ctx->header->kid);
    649     status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR;
    650     goto end;
    651   }
    652 
    653   if (!verify_jwt_signature(verification_key, ctx->header->alg, ctx->signature,
    654                             ctx->signed_data)) {
    655     status = GRPC_JWT_VERIFIER_BAD_SIGNATURE;
    656     goto end;
    657   }
    658 
    659   status = grpc_jwt_claims_check(ctx->claims, ctx->audience);
    660   if (status == GRPC_JWT_VERIFIER_OK) {
    661     /* Pass ownership. */
    662     claims = ctx->claims;
    663     ctx->claims = nullptr;
    664   }
    665 
    666 end:
    667   if (json != nullptr) grpc_json_destroy(json);
    668   EVP_PKEY_free(verification_key);
    669   ctx->user_cb(ctx->user_data, status, claims);
    670   verifier_cb_ctx_destroy(ctx);
    671 }
    672 
    673 static void on_openid_config_retrieved(void* user_data, grpc_error* error) {
    674   const grpc_json* cur;
    675   verifier_cb_ctx* ctx = static_cast<verifier_cb_ctx*>(user_data);
    676   const grpc_http_response* response = &ctx->responses[HTTP_RESPONSE_OPENID];
    677   grpc_json* json = json_from_http(response);
    678   grpc_httpcli_request req;
    679   const char* jwks_uri;
    680   grpc_resource_quota* resource_quota = nullptr;
    681 
    682   /* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time. */
    683   if (json == nullptr) goto error;
    684   cur = find_property_by_name(json, "jwks_uri");
    685   if (cur == nullptr) {
    686     gpr_log(GPR_ERROR, "Could not find jwks_uri in openid config.");
    687     goto error;
    688   }
    689   jwks_uri = validate_string_field(cur, "jwks_uri");
    690   if (jwks_uri == nullptr) goto error;
    691   if (strstr(jwks_uri, "https://") != jwks_uri) {
    692     gpr_log(GPR_ERROR, "Invalid non https jwks_uri: %s.", jwks_uri);
    693     goto error;
    694   }
    695   jwks_uri += 8;
    696   req.handshaker = &grpc_httpcli_ssl;
    697   req.host = gpr_strdup(jwks_uri);
    698   req.http.path = const_cast<char*>(strchr(jwks_uri, '/'));
    699   if (req.http.path == nullptr) {
    700     req.http.path = (char*)"";
    701   } else {
    702     *(req.host + (req.http.path - jwks_uri)) = '\0';
    703   }
    704 
    705   /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
    706      channel. This would allow us to cancel an authentication query when under
    707      extreme memory pressure. */
    708   resource_quota = grpc_resource_quota_create("jwt_verifier");
    709   grpc_httpcli_get(
    710       &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
    711       grpc_core::ExecCtx::Get()->Now() + grpc_jwt_verifier_max_delay,
    712       GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx),
    713       &ctx->responses[HTTP_RESPONSE_KEYS]);
    714   grpc_resource_quota_unref_internal(resource_quota);
    715   grpc_json_destroy(json);
    716   gpr_free(req.host);
    717   return;
    718 
    719 error:
    720   if (json != nullptr) grpc_json_destroy(json);
    721   ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, nullptr);
    722   verifier_cb_ctx_destroy(ctx);
    723 }
    724 
    725 static email_key_mapping* verifier_get_mapping(grpc_jwt_verifier* v,
    726                                                const char* email_domain) {
    727   size_t i;
    728   if (v->mappings == nullptr) return nullptr;
    729   for (i = 0; i < v->num_mappings; i++) {
    730     if (strcmp(email_domain, v->mappings[i].email_domain) == 0) {
    731       return &v->mappings[i];
    732     }
    733   }
    734   return nullptr;
    735 }
    736 
    737 static void verifier_put_mapping(grpc_jwt_verifier* v, const char* email_domain,
    738                                  const char* key_url_prefix) {
    739   email_key_mapping* mapping = verifier_get_mapping(v, email_domain);
    740   GPR_ASSERT(v->num_mappings < v->allocated_mappings);
    741   if (mapping != nullptr) {
    742     gpr_free(mapping->key_url_prefix);
    743     mapping->key_url_prefix = gpr_strdup(key_url_prefix);
    744     return;
    745   }
    746   v->mappings[v->num_mappings].email_domain = gpr_strdup(email_domain);
    747   v->mappings[v->num_mappings].key_url_prefix = gpr_strdup(key_url_prefix);
    748   v->num_mappings++;
    749   GPR_ASSERT(v->num_mappings <= v->allocated_mappings);
    750 }
    751 
    752 /* Very non-sophisticated way to detect an email address. Should be good
    753    enough for now... */
    754 const char* grpc_jwt_issuer_email_domain(const char* issuer) {
    755   const char* at_sign = strchr(issuer, '@');
    756   if (at_sign == nullptr) return nullptr;
    757   const char* email_domain = at_sign + 1;
    758   if (*email_domain == '\0') return nullptr;
    759   const char* dot = strrchr(email_domain, '.');
    760   if (dot == nullptr || dot == email_domain) return email_domain;
    761   GPR_ASSERT(dot > email_domain);
    762   /* There may be a subdomain, we just want the domain. */
    763   dot = static_cast<const char*>(gpr_memrchr(
    764       (void*)email_domain, '.', static_cast<size_t>(dot - email_domain)));
    765   if (dot == nullptr) return email_domain;
    766   return dot + 1;
    767 }
    768 
    769 /* Takes ownership of ctx. */
    770 static void retrieve_key_and_verify(verifier_cb_ctx* ctx) {
    771   const char* email_domain;
    772   grpc_closure* http_cb;
    773   char* path_prefix = nullptr;
    774   const char* iss;
    775   grpc_httpcli_request req;
    776   grpc_resource_quota* resource_quota = nullptr;
    777   memset(&req, 0, sizeof(grpc_httpcli_request));
    778   req.handshaker = &grpc_httpcli_ssl;
    779   http_response_index rsp_idx;
    780 
    781   GPR_ASSERT(ctx != nullptr && ctx->header != nullptr &&
    782              ctx->claims != nullptr);
    783   iss = ctx->claims->iss;
    784   if (ctx->header->kid == nullptr) {
    785     gpr_log(GPR_ERROR, "Missing kid in jose header.");
    786     goto error;
    787   }
    788   if (iss == nullptr) {
    789     gpr_log(GPR_ERROR, "Missing iss in claims.");
    790     goto error;
    791   }
    792 
    793   /* This code relies on:
    794      https://openid.net/specs/openid-connect-discovery-1_0.html
    795      Nobody seems to implement the account/email/webfinger part 2. of the spec
    796      so we will rely instead on email/url mappings if we detect such an issuer.
    797      Part 4, on the other hand is implemented by both google and salesforce. */
    798   email_domain = grpc_jwt_issuer_email_domain(iss);
    799   if (email_domain != nullptr) {
    800     email_key_mapping* mapping;
    801     GPR_ASSERT(ctx->verifier != nullptr);
    802     mapping = verifier_get_mapping(ctx->verifier, email_domain);
    803     if (mapping == nullptr) {
    804       gpr_log(GPR_ERROR, "Missing mapping for issuer email.");
    805       goto error;
    806     }
    807     req.host = gpr_strdup(mapping->key_url_prefix);
    808     path_prefix = strchr(req.host, '/');
    809     if (path_prefix == nullptr) {
    810       gpr_asprintf(&req.http.path, "/%s", iss);
    811     } else {
    812       *(path_prefix++) = '\0';
    813       gpr_asprintf(&req.http.path, "/%s/%s", path_prefix, iss);
    814     }
    815     http_cb =
    816         GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx);
    817     rsp_idx = HTTP_RESPONSE_KEYS;
    818   } else {
    819     req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss);
    820     path_prefix = strchr(req.host, '/');
    821     if (path_prefix == nullptr) {
    822       req.http.path = gpr_strdup(GRPC_OPENID_CONFIG_URL_SUFFIX);
    823     } else {
    824       *(path_prefix++) = 0;
    825       gpr_asprintf(&req.http.path, "/%s%s", path_prefix,
    826                    GRPC_OPENID_CONFIG_URL_SUFFIX);
    827     }
    828     http_cb = GRPC_CLOSURE_CREATE(on_openid_config_retrieved, ctx,
    829                                   grpc_schedule_on_exec_ctx);
    830     rsp_idx = HTTP_RESPONSE_OPENID;
    831   }
    832 
    833   /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host
    834      channel. This would allow us to cancel an authentication query when under
    835      extreme memory pressure. */
    836   resource_quota = grpc_resource_quota_create("jwt_verifier");
    837   grpc_httpcli_get(
    838       &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req,
    839       grpc_core::ExecCtx::Get()->Now() + grpc_jwt_verifier_max_delay, http_cb,
    840       &ctx->responses[rsp_idx]);
    841   grpc_resource_quota_unref_internal(resource_quota);
    842   gpr_free(req.host);
    843   gpr_free(req.http.path);
    844   return;
    845 
    846 error:
    847   ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, nullptr);
    848   verifier_cb_ctx_destroy(ctx);
    849 }
    850 
    851 void grpc_jwt_verifier_verify(grpc_jwt_verifier* verifier,
    852                               grpc_pollset* pollset, const char* jwt,
    853                               const char* audience,
    854                               grpc_jwt_verification_done_cb cb,
    855                               void* user_data) {
    856   const char* dot = nullptr;
    857   grpc_json* json;
    858   jose_header* header = nullptr;
    859   grpc_jwt_claims* claims = nullptr;
    860   grpc_slice header_buffer;
    861   grpc_slice claims_buffer;
    862   grpc_slice signature;
    863   size_t signed_jwt_len;
    864   const char* cur = jwt;
    865 
    866   GPR_ASSERT(verifier != nullptr && jwt != nullptr && audience != nullptr &&
    867              cb != nullptr);
    868   dot = strchr(cur, '.');
    869   if (dot == nullptr) goto error;
    870   json = parse_json_part_from_jwt(cur, static_cast<size_t>(dot - cur),
    871                                   &header_buffer);
    872   if (json == nullptr) goto error;
    873   header = jose_header_from_json(json, header_buffer);
    874   if (header == nullptr) goto error;
    875 
    876   cur = dot + 1;
    877   dot = strchr(cur, '.');
    878   if (dot == nullptr) goto error;
    879   json = parse_json_part_from_jwt(cur, static_cast<size_t>(dot - cur),
    880                                   &claims_buffer);
    881   if (json == nullptr) goto error;
    882   claims = grpc_jwt_claims_from_json(json, claims_buffer);
    883   if (claims == nullptr) goto error;
    884 
    885   signed_jwt_len = static_cast<size_t>(dot - jwt);
    886   cur = dot + 1;
    887   signature = grpc_base64_decode(cur, 1);
    888   if (GRPC_SLICE_IS_EMPTY(signature)) goto error;
    889   retrieve_key_and_verify(
    890       verifier_cb_ctx_create(verifier, pollset, header, claims, audience,
    891                              signature, jwt, signed_jwt_len, user_data, cb));
    892   return;
    893 
    894 error:
    895   if (header != nullptr) jose_header_destroy(header);
    896   if (claims != nullptr) grpc_jwt_claims_destroy(claims);
    897   cb(user_data, GRPC_JWT_VERIFIER_BAD_FORMAT, nullptr);
    898 }
    899 
    900 grpc_jwt_verifier* grpc_jwt_verifier_create(
    901     const grpc_jwt_verifier_email_domain_key_url_mapping* mappings,
    902     size_t num_mappings) {
    903   grpc_jwt_verifier* v =
    904       static_cast<grpc_jwt_verifier*>(gpr_zalloc(sizeof(grpc_jwt_verifier)));
    905   grpc_httpcli_context_init(&v->http_ctx);
    906 
    907   /* We know at least of one mapping. */
    908   v->allocated_mappings = 1 + num_mappings;
    909   v->mappings = static_cast<email_key_mapping*>(
    910       gpr_malloc(v->allocated_mappings * sizeof(email_key_mapping)));
    911   verifier_put_mapping(v, GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN,
    912                        GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX);
    913   /* User-Provided mappings. */
    914   if (mappings != nullptr) {
    915     size_t i;
    916     for (i = 0; i < num_mappings; i++) {
    917       verifier_put_mapping(v, mappings[i].email_domain,
    918                            mappings[i].key_url_prefix);
    919     }
    920   }
    921   return v;
    922 }
    923 
    924 void grpc_jwt_verifier_destroy(grpc_jwt_verifier* v) {
    925   size_t i;
    926   if (v == nullptr) return;
    927   grpc_httpcli_context_destroy(&v->http_ctx);
    928   if (v->mappings != nullptr) {
    929     for (i = 0; i < v->num_mappings; i++) {
    930       gpr_free(v->mappings[i].email_domain);
    931       gpr_free(v->mappings[i].key_url_prefix);
    932     }
    933     gpr_free(v->mappings);
    934   }
    935   gpr_free(v);
    936 }
    937