1
// Copyright 2018 Google LLC
2
// Copyright Envoy Project Authors
3
// SPDX-License-Identifier: Apache-2.0
4

            
5
#include "source/common/jwt/jwks.h"
6

            
7
#include <assert.h>
8

            
9
#include <iostream>
10

            
11
#include "source/common/jwt/struct_utils.h"
12
#include "source/common/protobuf/protobuf.h"
13

            
14
#include "absl/strings/escaping.h"
15
#include "absl/strings/match.h"
16
#include "openssl/bio.h"
17
#include "openssl/bn.h"
18
#include "openssl/curve25519.h"
19
#include "openssl/ecdsa.h"
20
#include "openssl/evp.h"
21
#include "openssl/rsa.h"
22
#include "openssl/sha.h"
23

            
24
namespace Envoy {
25
namespace JwtVerify {
26

            
27
namespace {
28

            
29
// The x509 certificate prefix string
30
const char kX509CertPrefix[] = "-----BEGIN CERTIFICATE-----\n";
31
// The x509 certificate suffix string
32
const char kX509CertSuffix[] = "\n-----END CERTIFICATE-----\n";
33

            
34
// A convenience inline cast function.
35
2924
inline const uint8_t* castToUChar(const std::string& str) {
36
2924
  return reinterpret_cast<const uint8_t*>(str.c_str());
37
2924
}
38

            
39
/** Class to create key object from string of public key, formatted in PEM
40
 * or JWKs.
41
 * If it fails, status_ holds the failure reason.
42
 *
43
 * Usage example:
44
 * KeyGetter e;
45
 * bssl::UniquePtr<EVP_PKEY> pkey = e.createEcKeyFromJwkEC(...);
46
 */
47
class KeyGetter : public WithStatus {
48
public:
49
30
  bssl::UniquePtr<EVP_PKEY> createEvpPkeyFromPem(const std::string& pkey_pem) {
50
30
    bssl::UniquePtr<BIO> buf(BIO_new_mem_buf(pkey_pem.data(), pkey_pem.size()));
51
30
    if (buf == nullptr) {
52
      updateStatus(Status::JwksBioAllocError);
53
      return nullptr;
54
    }
55
30
    bssl::UniquePtr<EVP_PKEY> key(PEM_read_bio_PUBKEY(buf.get(), nullptr, nullptr, nullptr));
56
30
    if (key == nullptr) {
57
5
      updateStatus(Status::JwksPemBadBase64);
58
5
      return nullptr;
59
5
    }
60
25
    return key;
61
30
  }
62

            
63
  bssl::UniquePtr<EC_KEY> createEcKeyFromJwkEC(int nid, const std::string& x,
64
91
                                               const std::string& y) {
65
91
    bssl::UniquePtr<EC_KEY> ec_key(EC_KEY_new_by_curve_name(nid));
66
91
    if (!ec_key) {
67
      updateStatus(Status::JwksEcCreateKeyFail);
68
      return nullptr;
69
    }
70
91
    bssl::UniquePtr<BIGNUM> bn_x = createBigNumFromBase64UrlString(x);
71
91
    bssl::UniquePtr<BIGNUM> bn_y = createBigNumFromBase64UrlString(y);
72
91
    if (!bn_x || !bn_y) {
73
      // EC public key field x or y Base64 decode fail
74
1
      updateStatus(Status::JwksEcXorYBadBase64);
75
1
      return nullptr;
76
1
    }
77

            
78
90
    if (EC_KEY_set_public_key_affine_coordinates(ec_key.get(), bn_x.get(), bn_y.get()) == 0) {
79
2
      updateStatus(Status::JwksEcParseError);
80
2
      return nullptr;
81
2
    }
82
88
    return ec_key;
83
90
  }
84

            
85
1372
  bssl::UniquePtr<RSA> createRsaFromJwk(const std::string& n, const std::string& e) {
86
1372
    bssl::UniquePtr<BIGNUM> n_bn = createBigNumFromBase64UrlString(n);
87
1372
    bssl::UniquePtr<BIGNUM> e_bn = createBigNumFromBase64UrlString(e);
88
1372
    if (n_bn == nullptr || e_bn == nullptr) {
89
      // RSA public key field is missing or has parse error.
90
1
      updateStatus(Status::JwksRsaParseError);
91
1
      return nullptr;
92
1
    }
93
1371
    if (BN_cmp_word(e_bn.get(), 3) != 0 && BN_cmp_word(e_bn.get(), 65537) != 0) {
94
      // non-standard key; reject it early.
95
1
      updateStatus(Status::JwksRsaParseError);
96
1
      return nullptr;
97
1
    }
98
    // When jwt_verify_lib's minimum supported BoringSSL revision is past
99
    // https://boringssl-review.googlesource.com/c/boringssl/+/59386 (May 2023),
100
    // replace all this with `RSA_new_public_key` instead.
101
1370
    bssl::UniquePtr<RSA> rsa(RSA_new());
102
1370
    if (rsa == nullptr || !RSA_set0_key(rsa.get(), n_bn.get(), e_bn.get(), /*d=*/nullptr)) {
103
      // Allocation or programmer error.
104
      updateStatus(Status::JwksRsaParseError);
105
      return nullptr;
106
    }
107
    // `RSA_set0_key` takes ownership, but only on success.
108
1370
    n_bn.release();
109
1370
    e_bn.release();
110
1370
    if (!RSA_check_key(rsa.get())) {
111
      // Not a valid RSA public key.
112
      updateStatus(Status::JwksRsaParseError);
113
      return nullptr;
114
    }
115
1370
    return rsa;
116
1370
  }
117

            
118
  std::string createRawKeyFromJwkOKP([[maybe_unused]] int nid, size_t keylen,
119
21
                                     const std::string& x) {
120
21
    std::string x_decoded;
121
21
    if (!absl::WebSafeBase64Unescape(x, &x_decoded)) {
122
1
      updateStatus(Status::JwksOKPXBadBase64);
123
20
    } else if (x_decoded.length() != keylen) {
124
1
      updateStatus(Status::JwksOKPXWrongLength);
125
1
    }
126
    // For OKP the "x" value is the public key and can just be used as-is
127
21
    return x_decoded;
128
21
  }
129

            
130
private:
131
2926
  bssl::UniquePtr<BIGNUM> createBigNumFromBase64UrlString(const std::string& s) {
132
2926
    std::string s_decoded;
133
2926
    if (!absl::WebSafeBase64Unescape(s, &s_decoded)) {
134
2
      return nullptr;
135
2
    }
136
2924
    return bssl::UniquePtr<BIGNUM>(BN_bin2bn(castToUChar(s_decoded), s_decoded.length(), NULL));
137
2926
  };
138
};
139

            
140
1377
Status extractJwkFromJwkRSA(const Protobuf::Struct& jwk_pb, Jwks::Pubkey* jwk) {
141
1377
  if (!jwk->alg_.empty() && (jwk->alg_.size() < 2 || (jwk->alg_.compare(0, 2, "RS") != 0 &&
142
1371
                                                      jwk->alg_.compare(0, 2, "PS") != 0))) {
143
1
    return Status::JwksRSAKeyBadAlg;
144
1
  }
145

            
146
1376
  StructUtils jwk_getter(jwk_pb);
147
1376
  std::string n_str;
148
1376
  auto code = jwk_getter.GetString("n", &n_str);
149
1376
  if (code == StructUtils::MISSING) {
150
1
    return Status::JwksRSAKeyMissingN;
151
1
  }
152
1375
  if (code == StructUtils::WRONG_TYPE) {
153
1
    return Status::JwksRSAKeyBadN;
154
1
  }
155

            
156
1374
  std::string e_str;
157
1374
  code = jwk_getter.GetString("e", &e_str);
158
1374
  if (code == StructUtils::MISSING) {
159
1
    return Status::JwksRSAKeyMissingE;
160
1
  }
161
1373
  if (code == StructUtils::WRONG_TYPE) {
162
1
    return Status::JwksRSAKeyBadE;
163
1
  }
164

            
165
1372
  KeyGetter e;
166
1372
  jwk->rsa_ = e.createRsaFromJwk(n_str, e_str);
167
1372
  return e.getStatus();
168
1373
}
169

            
170
102
Status extractJwkFromJwkEC(const Protobuf::Struct& jwk_pb, Jwks::Pubkey* jwk) {
171
102
  if (!jwk->alg_.empty() && (jwk->alg_.size() < 2 || jwk->alg_.compare(0, 2, "ES") != 0)) {
172
1
    return Status::JwksECKeyBadAlg;
173
1
  }
174

            
175
101
  StructUtils jwk_getter(jwk_pb);
176
101
  std::string crv_str;
177
101
  auto code = jwk_getter.GetString("crv", &crv_str);
178
101
  if (code == StructUtils::MISSING) {
179
10
    crv_str = "";
180
10
  }
181
101
  if (code == StructUtils::WRONG_TYPE) {
182
1
    return Status::JwksECKeyBadCrv;
183
1
  }
184
100
  jwk->crv_ = crv_str;
185

            
186
  // If both alg and crv specified, make sure they match
187
100
  if (!jwk->alg_.empty() && !jwk->crv_.empty()) {
188
86
    if (!((jwk->alg_ == "ES256" && jwk->crv_ == "P-256") ||
189
86
          (jwk->alg_ == "ES384" && jwk->crv_ == "P-384") ||
190
86
          (jwk->alg_ == "ES512" && jwk->crv_ == "P-521"))) {
191
3
      return Status::JwksECKeyAlgNotCompatibleWithCrv;
192
3
    }
193
86
  }
194

            
195
  // If neither alg or crv is set, assume P-256
196
97
  if (jwk->alg_.empty() && jwk->crv_.empty()) {
197
2
    jwk->crv_ = "P-256";
198
2
  }
199

            
200
97
  int nid;
201
97
  if (jwk->alg_ == "ES256" || jwk->crv_ == "P-256") {
202
51
    nid = NID_X9_62_prime256v1;
203
51
    jwk->crv_ = "P-256";
204
51
  } else if (jwk->alg_ == "ES384" || jwk->crv_ == "P-384") {
205
21
    nid = NID_secp384r1;
206
21
    jwk->crv_ = "P-384";
207
25
  } else if (jwk->alg_ == "ES512" || jwk->crv_ == "P-521") {
208
23
    nid = NID_secp521r1;
209
23
    jwk->crv_ = "P-521";
210
23
  } else {
211
2
    return Status::JwksECKeyAlgOrCrvUnsupported;
212
2
  }
213

            
214
95
  std::string x_str;
215
95
  code = jwk_getter.GetString("x", &x_str);
216
95
  if (code == StructUtils::MISSING) {
217
1
    return Status::JwksECKeyMissingX;
218
1
  }
219
94
  if (code == StructUtils::WRONG_TYPE) {
220
1
    return Status::JwksECKeyBadX;
221
1
  }
222

            
223
93
  std::string y_str;
224
93
  code = jwk_getter.GetString("y", &y_str);
225
93
  if (code == StructUtils::MISSING) {
226
1
    return Status::JwksECKeyMissingY;
227
1
  }
228
92
  if (code == StructUtils::WRONG_TYPE) {
229
1
    return Status::JwksECKeyBadY;
230
1
  }
231

            
232
91
  KeyGetter e;
233
91
  jwk->ec_key_ = e.createEcKeyFromJwkEC(nid, x_str, y_str);
234
91
  return e.getStatus();
235
92
}
236

            
237
44
Status extractJwkFromJwkOct(const Protobuf::Struct& jwk_pb, Jwks::Pubkey* jwk) {
238
44
  if (!jwk->alg_.empty() && jwk->alg_ != "HS256" && jwk->alg_ != "HS384" && jwk->alg_ != "HS512") {
239
1
    return Status::JwksHMACKeyBadAlg;
240
1
  }
241

            
242
43
  StructUtils jwk_getter(jwk_pb);
243
43
  std::string k_str;
244
43
  auto code = jwk_getter.GetString("k", &k_str);
245
43
  if (code == StructUtils::MISSING) {
246
1
    return Status::JwksHMACKeyMissingK;
247
1
  }
248
42
  if (code == StructUtils::WRONG_TYPE) {
249
1
    return Status::JwksHMACKeyBadK;
250
1
  }
251

            
252
41
  std::string key;
253
41
  if (!absl::WebSafeBase64Unescape(k_str, &key) || key.empty()) {
254
1
    return Status::JwksOctBadBase64;
255
1
  }
256

            
257
40
  jwk->hmac_key_ = key;
258
40
  return Status::Ok;
259
41
}
260

            
261
// The "OKP" key type is defined in https://tools.ietf.org/html/rfc8037
262
30
Status extractJwkFromJwkOKP(const Protobuf::Struct& jwk_pb, Jwks::Pubkey* jwk) {
263
  // alg is not required, but if present it must be EdDSA
264
30
  if (!jwk->alg_.empty() && jwk->alg_ != "EdDSA") {
265
1
    return Status::JwksOKPKeyBadAlg;
266
1
  }
267

            
268
  // crv is required per https://tools.ietf.org/html/rfc8037#section-2
269
29
  StructUtils jwk_getter(jwk_pb);
270
29
  std::string crv_str;
271
29
  auto code = jwk_getter.GetString("crv", &crv_str);
272
29
  if (code == StructUtils::MISSING) {
273
1
    return Status::JwksOKPKeyMissingCrv;
274
1
  }
275
28
  if (code == StructUtils::WRONG_TYPE) {
276
1
    return Status::JwksOKPKeyBadCrv;
277
1
  }
278
27
  jwk->crv_ = crv_str;
279

            
280
  // Valid crv values:
281
  // https://tools.ietf.org/html/rfc8037#section-3
282
  // https://www.iana.org/assignments/jose/jose.xhtml#web-key-elliptic-curve
283
  // In addition to Ed25519 there are:
284
  // X25519: Implemented in boringssl but not used for JWT and thus not
285
  // supported here
286
  // Ed448 and X448: Not implemented in boringssl
287
27
  int nid;
288
27
  size_t keylen;
289
27
  if (jwk->crv_ == "Ed25519") {
290
23
    nid = EVP_PKEY_ED25519;
291
23
    keylen = ED25519_PUBLIC_KEY_LEN;
292
23
  } else {
293
4
    return Status::JwksOKPKeyCrvUnsupported;
294
4
  }
295

            
296
  // x is required per https://tools.ietf.org/html/rfc8037#section-2
297
23
  std::string x_str;
298
23
  code = jwk_getter.GetString("x", &x_str);
299
23
  if (code == StructUtils::MISSING) {
300
1
    return Status::JwksOKPKeyMissingX;
301
1
  }
302
22
  if (code == StructUtils::WRONG_TYPE) {
303
1
    return Status::JwksOKPKeyBadX;
304
1
  }
305

            
306
21
  KeyGetter e;
307
21
  jwk->okp_key_raw_ = e.createRawKeyFromJwkOKP(nid, keylen, x_str);
308
21
  return e.getStatus();
309
22
}
310

            
311
1556
Status extractJwk(const Protobuf::Struct& jwk_pb, Jwks::Pubkey* jwk) {
312
1556
  StructUtils jwk_getter(jwk_pb);
313
  // Check "kty" parameter, it should exist.
314
  // https://tools.ietf.org/html/rfc7517#section-4.1
315
1556
  auto code = jwk_getter.GetString("kty", &jwk->kty_);
316
1556
  if (code == StructUtils::MISSING) {
317
1
    return Status::JwksMissingKty;
318
1
  }
319
1555
  if (code == StructUtils::WRONG_TYPE) {
320
1
    return Status::JwksBadKty;
321
1
  }
322

            
323
  // "kid" and "alg" are optional, if they do not exist, set them to
324
  // empty. https://tools.ietf.org/html/rfc7517#page-8
325
1554
  jwk_getter.GetString("kid", &jwk->kid_);
326
1554
  jwk_getter.GetString("alg", &jwk->alg_);
327

            
328
  // Extract public key according to "kty" value.
329
  // https://tools.ietf.org/html/rfc7518#section-6.1
330
1554
  if (jwk->kty_ == "EC") {
331
102
    return extractJwkFromJwkEC(jwk_pb, jwk);
332
1524
  } else if (jwk->kty_ == "RSA") {
333
1377
    return extractJwkFromJwkRSA(jwk_pb, jwk);
334
1443
  } else if (jwk->kty_ == "oct") {
335
44
    return extractJwkFromJwkOct(jwk_pb, jwk);
336
71
  } else if (jwk->kty_ == "OKP") {
337
30
    return extractJwkFromJwkOKP(jwk_pb, jwk);
338
30
  }
339
1
  return Status::JwksNotImplementedKty;
340
1554
}
341

            
342
15
Status extractX509(const std::string& key, Jwks::Pubkey* jwk) {
343
15
  jwk->bio_.reset(BIO_new(BIO_s_mem()));
344
15
  if (BIO_write(jwk->bio_.get(), key.c_str(), key.length()) <= 0) {
345
    return Status::JwksX509BioWriteError;
346
  }
347
15
  jwk->x509_.reset(PEM_read_bio_X509(jwk->bio_.get(), nullptr, nullptr, nullptr));
348
15
  if (jwk->x509_ == nullptr) {
349
1
    return Status::JwksX509ParseError;
350
1
  }
351
14
  bssl::UniquePtr<EVP_PKEY> tmp_pkey(X509_get_pubkey(jwk->x509_.get()));
352
14
  if (tmp_pkey == nullptr) {
353
    return Status::JwksX509GetPubkeyError;
354
  }
355
14
  jwk->rsa_.reset(EVP_PKEY_get1_RSA(tmp_pkey.get()));
356
14
  if (jwk->rsa_ == nullptr) {
357
    return Status::JwksX509GetPubkeyError;
358
  }
359
14
  return Status::Ok;
360
14
}
361

            
362
13
bool shouldCheckX509(const Protobuf::Struct& jwks_pb) {
363
13
  if (jwks_pb.fields().empty()) {
364
1
    return false;
365
1
  }
366

            
367
22
  for (const auto& kid : jwks_pb.fields()) {
368
22
    if (kid.first.empty() || kid.second.kind_case() != Protobuf::Value::kStringValue) {
369
2
      return false;
370
2
    }
371
20
    const std::string& cert = kid.second.string_value();
372
20
    if (!absl::StartsWith(cert, kX509CertPrefix) || !absl::EndsWith(cert, kX509CertSuffix)) {
373
5
      return false;
374
5
    }
375
20
  }
376
5
  return true;
377
12
}
378

            
379
5
Status createFromX509(const Protobuf::Struct& jwks_pb, std::vector<Jwks::PubkeyPtr>& keys) {
380
15
  for (const auto& kid : jwks_pb.fields()) {
381
15
    Jwks::PubkeyPtr key_ptr(new Jwks::Pubkey());
382
15
    Status status = extractX509(kid.second.string_value(), key_ptr.get());
383
15
    if (status != Status::Ok) {
384
1
      return status;
385
1
    }
386

            
387
14
    key_ptr->kid_ = kid.first;
388
14
    key_ptr->kty_ = "RSA";
389
14
    keys.push_back(std::move(key_ptr));
390
14
  }
391
4
  return Status::Ok;
392
5
}
393

            
394
} // namespace
395

            
396
Status Jwks::addKeyFromPem(const std::string& pkey, const std::string& kid,
397
2
                           const std::string& alg) {
398
2
  JwksPtr tmp = Jwks::createFromPem(pkey, kid, alg);
399
2
  if (tmp->getStatus() != Status::Ok) {
400
1
    return tmp->getStatus();
401
1
  }
402
1
  keys_.insert(keys_.end(), std::make_move_iterator(tmp->keys_.begin()),
403
1
               std::make_move_iterator(tmp->keys_.end()));
404
1
  return Status::Ok;
405
2
}
406

            
407
608
JwksPtr Jwks::createFrom(const std::string& pkey, Type type) {
408
608
  JwksPtr keys(new Jwks());
409
608
  switch (type) {
410
578
  case Type::JWKS:
411
578
    keys->createFromJwksCore(pkey);
412
578
    break;
413
30
  case Type::PEM:
414
30
    keys->createFromPemCore(pkey);
415
30
    break;
416
608
  }
417
608
  return keys;
418
608
}
419

            
420
JwksPtr Jwks::createFromPem(const std::string& pkey, const std::string& kid,
421
5
                            const std::string& alg) {
422
5
  std::unique_ptr<Jwks> ret = Jwks::createFrom(pkey, Jwks::PEM);
423
5
  if (ret->getStatus() != Status::Ok) {
424
2
    return ret;
425
2
  }
426
3
  if (ret->keys_.size() != 1) {
427
    ret->updateStatus(Status::JwksPemBadBase64);
428
    return ret;
429
  }
430
3
  Pubkey* jwk = ret->keys_.at(0).get();
431
3
  jwk->kid_ = kid;
432
3
  jwk->alg_ = alg;
433

            
434
  // If alg is a known EC algorithm, set the correct crv as well.
435
3
  if (jwk->alg_ == "ES256") {
436
2
    jwk->crv_ = "P-256";
437
2
  }
438
3
  if (jwk->alg_ == "ES384") {
439
    jwk->crv_ = "P-384";
440
  }
441
3
  if (jwk->alg_ == "ES512") {
442
    jwk->crv_ = "P-521";
443
  }
444
3
  return ret;
445
3
}
446

            
447
// pkey_pem must be a PEM-encoded PKCS #8 public key.
448
// This is the format that starts with -----BEGIN PUBLIC KEY-----.
449
30
void Jwks::createFromPemCore(const std::string& pkey_pem) {
450
30
  keys_.clear();
451
30
  PubkeyPtr key_ptr(new Pubkey());
452
30
  KeyGetter e;
453
30
  bssl::UniquePtr<EVP_PKEY> evp_pkey(e.createEvpPkeyFromPem(pkey_pem));
454
30
  updateStatus(e.getStatus());
455

            
456
30
  if (evp_pkey == nullptr) {
457
5
    assert(e.getStatus() != Status::Ok);
458
5
    return;
459
5
  }
460
25
  assert(e.getStatus() == Status::Ok);
461

            
462
25
  switch (EVP_PKEY_id(evp_pkey.get())) {
463
6
  case EVP_PKEY_RSA:
464
6
    key_ptr->rsa_.reset(EVP_PKEY_get1_RSA(evp_pkey.get()));
465
6
    key_ptr->kty_ = "RSA";
466
6
    break;
467
14
  case EVP_PKEY_EC:
468
14
    key_ptr->ec_key_.reset(EVP_PKEY_get1_EC_KEY(evp_pkey.get()));
469
14
    key_ptr->kty_ = "EC";
470
14
    break;
471
#ifndef BORINGSSL_FIPS
472
4
  case EVP_PKEY_ED25519: {
473
4
    uint8_t raw_key[ED25519_PUBLIC_KEY_LEN];
474
4
    size_t out_len = ED25519_PUBLIC_KEY_LEN;
475
4
    if (EVP_PKEY_get_raw_public_key(evp_pkey.get(), raw_key, &out_len) != 1 ||
476
4
        out_len != ED25519_PUBLIC_KEY_LEN) {
477
      updateStatus(Status::JwksPemGetRawEd25519Error);
478
      return;
479
    }
480
4
    key_ptr->okp_key_raw_ = std::string(reinterpret_cast<const char*>(raw_key), out_len);
481
4
    key_ptr->kty_ = "OKP";
482
4
    key_ptr->crv_ = "Ed25519";
483
4
    break;
484
4
  }
485
#endif
486
1
  default:
487
1
    updateStatus(Status::JwksPemNotImplementedKty);
488
1
    return;
489
25
  }
490

            
491
24
  keys_.push_back(std::move(key_ptr));
492
24
}
493

            
494
578
void Jwks::createFromJwksCore(const std::string& jwks_json) {
495
578
  keys_.clear();
496

            
497
578
  Protobuf::util::JsonParseOptions options;
498
578
  Protobuf::Struct jwks_pb;
499
578
  const auto status = Protobuf::util::JsonStringToMessage(jwks_json, &jwks_pb, options);
500
578
  if (!status.ok()) {
501
15
    updateStatus(Status::JwksParseError);
502
15
    return;
503
15
  }
504

            
505
563
  const auto& fields = jwks_pb.fields();
506
563
  const auto keys_it = fields.find("keys");
507
563
  if (keys_it == fields.end()) {
508
    // X509 doesn't have "keys" field.
509
13
    if (shouldCheckX509(jwks_pb)) {
510
5
      updateStatus(createFromX509(jwks_pb, keys_));
511
5
      return;
512
5
    }
513
8
    updateStatus(Status::JwksNoKeys);
514
8
    return;
515
13
  }
516
550
  if (keys_it->second.kind_case() != Protobuf::Value::kListValue) {
517
1
    updateStatus(Status::JwksBadKeys);
518
1
    return;
519
1
  }
520

            
521
1556
  for (const auto& key_value : keys_it->second.list_value().values()) {
522
1556
    if (key_value.kind_case() != Protobuf::Value::kStructValue) {
523
      continue;
524
    }
525
1556
    PubkeyPtr key_ptr(new Pubkey());
526
1556
    Status status = extractJwk(key_value.struct_value(), key_ptr.get());
527
1556
    if (status == Status::Ok) {
528
1517
      keys_.push_back(std::move(key_ptr));
529
1517
      resetStatus(status);
530
1537
    } else {
531
39
      updateStatus(status);
532
39
    }
533
1556
  }
534

            
535
549
  if (keys_.empty()) {
536
37
    updateStatus(Status::JwksNoValidKeys);
537
537
  } else {
538
512
    resetStatus(Status::Ok);
539
512
  }
540
549
}
541

            
542
} // namespace JwtVerify
543
} // namespace Envoy