/proc/self/cwd/src/jwks.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2018 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include "jwt_verify_lib/jwks.h" |
16 | | |
17 | | #include <assert.h> |
18 | | |
19 | | #include <iostream> |
20 | | |
21 | | #include "absl/strings/escaping.h" |
22 | | #include "absl/strings/match.h" |
23 | | #include "google/protobuf/struct.pb.h" |
24 | | #include "google/protobuf/util/json_util.h" |
25 | | #include "jwt_verify_lib/struct_utils.h" |
26 | | #include "openssl/bio.h" |
27 | | #include "openssl/bn.h" |
28 | | #include "openssl/curve25519.h" |
29 | | #include "openssl/ecdsa.h" |
30 | | #include "openssl/evp.h" |
31 | | #include "openssl/rsa.h" |
32 | | #include "openssl/sha.h" |
33 | | |
34 | | namespace google { |
35 | | namespace jwt_verify { |
36 | | |
37 | | namespace { |
38 | | |
39 | | // The x509 certificate prefix string |
40 | | const char kX509CertPrefix[] = "-----BEGIN CERTIFICATE-----\n"; |
41 | | // The x509 certificate suffix string |
42 | | const char kX509CertSuffix[] = "\n-----END CERTIFICATE-----\n"; |
43 | | |
44 | | // A convinence inline cast function. |
45 | 79.2k | inline const uint8_t* castToUChar(const std::string& str) { |
46 | 79.2k | return reinterpret_cast<const uint8_t*>(str.c_str()); |
47 | 79.2k | } |
48 | | |
49 | | /** Class to create key object from string of public key, formatted in PEM |
50 | | * or JWKs. |
51 | | * If it fails, status_ holds the failure reason. |
52 | | * |
53 | | * Usage example: |
54 | | * KeyGetter e; |
55 | | * bssl::UniquePtr<EVP_PKEY> pkey = e.createEcKeyFromJwkEC(...); |
56 | | */ |
57 | | class KeyGetter : public WithStatus { |
58 | | public: |
59 | 10.4k | bssl::UniquePtr<EVP_PKEY> createEvpPkeyFromPem(const std::string& pkey_pem) { |
60 | 10.4k | bssl::UniquePtr<BIO> buf(BIO_new_mem_buf(pkey_pem.data(), pkey_pem.size())); |
61 | 10.4k | if (buf == nullptr) { |
62 | 0 | updateStatus(Status::JwksBioAllocError); |
63 | 0 | return nullptr; |
64 | 0 | } |
65 | 10.4k | bssl::UniquePtr<EVP_PKEY> key( |
66 | 10.4k | PEM_read_bio_PUBKEY(buf.get(), nullptr, nullptr, nullptr)); |
67 | 10.4k | if (key == nullptr) { |
68 | 10.4k | updateStatus(Status::JwksPemBadBase64); |
69 | 10.4k | return nullptr; |
70 | 10.4k | } |
71 | 17 | return key; |
72 | 10.4k | } |
73 | | |
74 | | bssl::UniquePtr<EC_KEY> createEcKeyFromJwkEC(int nid, const std::string& x, |
75 | 17.8k | const std::string& y) { |
76 | 17.8k | bssl::UniquePtr<EC_KEY> ec_key(EC_KEY_new_by_curve_name(nid)); |
77 | 17.8k | if (!ec_key) { |
78 | 0 | updateStatus(Status::JwksEcCreateKeyFail); |
79 | 0 | return nullptr; |
80 | 0 | } |
81 | 17.8k | bssl::UniquePtr<BIGNUM> bn_x = createBigNumFromBase64UrlString(x); |
82 | 17.8k | bssl::UniquePtr<BIGNUM> bn_y = createBigNumFromBase64UrlString(y); |
83 | 17.8k | if (!bn_x || !bn_y) { |
84 | | // EC public key field x or y Base64 decode fail |
85 | 2.36k | updateStatus(Status::JwksEcXorYBadBase64); |
86 | 2.36k | return nullptr; |
87 | 2.36k | } |
88 | | |
89 | 15.5k | if (EC_KEY_set_public_key_affine_coordinates(ec_key.get(), bn_x.get(), |
90 | 15.5k | bn_y.get()) == 0) { |
91 | 4.03k | updateStatus(Status::JwksEcParseError); |
92 | 4.03k | return nullptr; |
93 | 4.03k | } |
94 | 11.4k | return ec_key; |
95 | 15.5k | } |
96 | | |
97 | | bssl::UniquePtr<RSA> createRsaFromJwk(const std::string& n, |
98 | 26.6k | const std::string& e) { |
99 | 26.6k | bssl::UniquePtr<BIGNUM> n_bn = createBigNumFromBase64UrlString(n); |
100 | 26.6k | bssl::UniquePtr<BIGNUM> e_bn = createBigNumFromBase64UrlString(e); |
101 | 26.6k | if (n_bn == nullptr || e_bn == nullptr) { |
102 | | // RSA public key field is missing or has parse error. |
103 | 5.37k | updateStatus(Status::JwksRsaParseError); |
104 | 5.37k | return nullptr; |
105 | 5.37k | } |
106 | 21.2k | if (BN_cmp_word(e_bn.get(), 3) != 0 && |
107 | 21.2k | BN_cmp_word(e_bn.get(), 65537) != 0) { |
108 | | // non-standard key; reject it early. |
109 | 1.66k | updateStatus(Status::JwksRsaParseError); |
110 | 1.66k | return nullptr; |
111 | 1.66k | } |
112 | | // When jwt_verify_lib's minimum supported BoringSSL revision is past |
113 | | // https://boringssl-review.googlesource.com/c/boringssl/+/59386 (May 2023), |
114 | | // replace all this with `RSA_new_public_key` instead. |
115 | 19.6k | bssl::UniquePtr<RSA> rsa(RSA_new()); |
116 | 19.6k | if (rsa == nullptr || |
117 | 19.6k | !RSA_set0_key(rsa.get(), n_bn.get(), e_bn.get(), /*d=*/nullptr)) { |
118 | | // Allocation or programmer error. |
119 | 0 | updateStatus(Status::JwksRsaParseError); |
120 | 0 | return nullptr; |
121 | 0 | } |
122 | | // `RSA_set0_key` takes ownership, but only on success. |
123 | 19.6k | n_bn.release(); |
124 | 19.6k | e_bn.release(); |
125 | 19.6k | if (!RSA_check_key(rsa.get())) { |
126 | | // Not a valid RSA public key. |
127 | 4.13k | updateStatus(Status::JwksRsaParseError); |
128 | 4.13k | return nullptr; |
129 | 4.13k | } |
130 | 15.4k | return rsa; |
131 | 19.6k | } |
132 | | |
133 | | std::string createRawKeyFromJwkOKP(int nid, size_t keylen, |
134 | 21.5k | const std::string& x) { |
135 | 21.5k | std::string x_decoded; |
136 | 21.5k | if (!absl::WebSafeBase64Unescape(x, &x_decoded)) { |
137 | 1.56k | updateStatus(Status::JwksOKPXBadBase64); |
138 | 20.0k | } else if (x_decoded.length() != keylen) { |
139 | 1.44k | updateStatus(Status::JwksOKPXWrongLength); |
140 | 1.44k | } |
141 | | // For OKP the "x" value is the public key and can just be used as-is |
142 | 21.5k | return x_decoded; |
143 | 21.5k | } |
144 | | |
145 | | private: |
146 | | bssl::UniquePtr<BIGNUM> createBigNumFromBase64UrlString( |
147 | 89.0k | const std::string& s) { |
148 | 89.0k | std::string s_decoded; |
149 | 89.0k | if (!absl::WebSafeBase64Unescape(s, &s_decoded)) { |
150 | 9.85k | return nullptr; |
151 | 9.85k | } |
152 | 79.2k | return bssl::UniquePtr<BIGNUM>( |
153 | 79.2k | BN_bin2bn(castToUChar(s_decoded), s_decoded.length(), NULL)); |
154 | 89.0k | }; |
155 | | }; |
156 | | |
157 | | Status extractJwkFromJwkRSA(const ::google::protobuf::Struct& jwk_pb, |
158 | 34.1k | Jwks::Pubkey* jwk) { |
159 | 34.1k | if (!jwk->alg_.empty() && |
160 | 34.1k | (jwk->alg_.size() < 2 || (jwk->alg_.compare(0, 2, "RS") != 0 && |
161 | 5.79k | jwk->alg_.compare(0, 2, "PS") != 0))) { |
162 | 1.23k | return Status::JwksRSAKeyBadAlg; |
163 | 1.23k | } |
164 | | |
165 | 32.8k | StructUtils jwk_getter(jwk_pb); |
166 | 32.8k | std::string n_str; |
167 | 32.8k | auto code = jwk_getter.GetString("n", &n_str); |
168 | 32.8k | if (code == StructUtils::MISSING) { |
169 | 4.01k | return Status::JwksRSAKeyMissingN; |
170 | 4.01k | } |
171 | 28.8k | if (code == StructUtils::WRONG_TYPE) { |
172 | 563 | return Status::JwksRSAKeyBadN; |
173 | 563 | } |
174 | | |
175 | 28.2k | std::string e_str; |
176 | 28.2k | code = jwk_getter.GetString("e", &e_str); |
177 | 28.2k | if (code == StructUtils::MISSING) { |
178 | 1.44k | return Status::JwksRSAKeyMissingE; |
179 | 1.44k | } |
180 | 26.8k | if (code == StructUtils::WRONG_TYPE) { |
181 | 200 | return Status::JwksRSAKeyBadE; |
182 | 200 | } |
183 | | |
184 | 26.6k | KeyGetter e; |
185 | 26.6k | jwk->rsa_ = e.createRsaFromJwk(n_str, e_str); |
186 | 26.6k | return e.getStatus(); |
187 | 26.8k | } |
188 | | |
189 | | Status extractJwkFromJwkEC(const ::google::protobuf::Struct& jwk_pb, |
190 | 30.9k | Jwks::Pubkey* jwk) { |
191 | 30.9k | if (!jwk->alg_.empty() && |
192 | 30.9k | (jwk->alg_.size() < 2 || jwk->alg_.compare(0, 2, "ES") != 0)) { |
193 | 660 | return Status::JwksECKeyBadAlg; |
194 | 660 | } |
195 | | |
196 | 30.3k | StructUtils jwk_getter(jwk_pb); |
197 | 30.3k | std::string crv_str; |
198 | 30.3k | auto code = jwk_getter.GetString("crv", &crv_str); |
199 | 30.3k | if (code == StructUtils::MISSING) { |
200 | 14.7k | crv_str = ""; |
201 | 14.7k | } |
202 | 30.3k | if (code == StructUtils::WRONG_TYPE) { |
203 | 195 | return Status::JwksECKeyBadCrv; |
204 | 195 | } |
205 | 30.1k | jwk->crv_ = crv_str; |
206 | | |
207 | | // If both alg and crv specified, make sure they match |
208 | 30.1k | if (!jwk->alg_.empty() && !jwk->crv_.empty()) { |
209 | 4.32k | if (!((jwk->alg_ == "ES256" && jwk->crv_ == "P-256") || |
210 | 4.32k | (jwk->alg_ == "ES384" && jwk->crv_ == "P-384") || |
211 | 4.32k | (jwk->alg_ == "ES512" && jwk->crv_ == "P-521"))) { |
212 | 3.02k | return Status::JwksECKeyAlgNotCompatibleWithCrv; |
213 | 3.02k | } |
214 | 4.32k | } |
215 | | |
216 | | // If neither alg or crv is set, assume P-256 |
217 | 27.0k | if (jwk->alg_.empty() && jwk->crv_.empty()) { |
218 | 13.5k | jwk->crv_ = "P-256"; |
219 | 13.5k | } |
220 | | |
221 | 27.0k | int nid; |
222 | 27.0k | if (jwk->alg_ == "ES256" || jwk->crv_ == "P-256") { |
223 | 13.9k | nid = NID_X9_62_prime256v1; |
224 | 13.9k | jwk->crv_ = "P-256"; |
225 | 13.9k | } else if (jwk->alg_ == "ES384" || jwk->crv_ == "P-384") { |
226 | 4.24k | nid = NID_secp384r1; |
227 | 4.24k | jwk->crv_ = "P-384"; |
228 | 8.90k | } else if (jwk->alg_ == "ES512" || jwk->crv_ == "P-521") { |
229 | 5.74k | nid = NID_secp521r1; |
230 | 5.74k | jwk->crv_ = "P-521"; |
231 | 5.74k | } else { |
232 | 3.16k | return Status::JwksECKeyAlgOrCrvUnsupported; |
233 | 3.16k | } |
234 | | |
235 | 23.9k | std::string x_str; |
236 | 23.9k | code = jwk_getter.GetString("x", &x_str); |
237 | 23.9k | if (code == StructUtils::MISSING) { |
238 | 3.49k | return Status::JwksECKeyMissingX; |
239 | 3.49k | } |
240 | 20.4k | if (code == StructUtils::WRONG_TYPE) { |
241 | 235 | return Status::JwksECKeyBadX; |
242 | 235 | } |
243 | | |
244 | 20.1k | std::string y_str; |
245 | 20.1k | code = jwk_getter.GetString("y", &y_str); |
246 | 20.1k | if (code == StructUtils::MISSING) { |
247 | 2.09k | return Status::JwksECKeyMissingY; |
248 | 2.09k | } |
249 | 18.0k | if (code == StructUtils::WRONG_TYPE) { |
250 | 196 | return Status::JwksECKeyBadY; |
251 | 196 | } |
252 | | |
253 | 17.8k | KeyGetter e; |
254 | 17.8k | jwk->ec_key_ = e.createEcKeyFromJwkEC(nid, x_str, y_str); |
255 | 17.8k | return e.getStatus(); |
256 | 18.0k | } |
257 | | |
258 | | Status extractJwkFromJwkOct(const ::google::protobuf::Struct& jwk_pb, |
259 | 13.6k | Jwks::Pubkey* jwk) { |
260 | 13.6k | if (!jwk->alg_.empty() && jwk->alg_ != "HS256" && jwk->alg_ != "HS384" && |
261 | 13.6k | jwk->alg_ != "HS512") { |
262 | 1.11k | return Status::JwksHMACKeyBadAlg; |
263 | 1.11k | } |
264 | | |
265 | 12.5k | StructUtils jwk_getter(jwk_pb); |
266 | 12.5k | std::string k_str; |
267 | 12.5k | auto code = jwk_getter.GetString("k", &k_str); |
268 | 12.5k | if (code == StructUtils::MISSING) { |
269 | 1.23k | return Status::JwksHMACKeyMissingK; |
270 | 1.23k | } |
271 | 11.2k | if (code == StructUtils::WRONG_TYPE) { |
272 | 106 | return Status::JwksHMACKeyBadK; |
273 | 106 | } |
274 | | |
275 | 11.1k | std::string key; |
276 | 11.1k | if (!absl::WebSafeBase64Unescape(k_str, &key) || key.empty()) { |
277 | 1.96k | return Status::JwksOctBadBase64; |
278 | 1.96k | } |
279 | | |
280 | 9.22k | jwk->hmac_key_ = key; |
281 | 9.22k | return Status::Ok; |
282 | 11.1k | } |
283 | | |
284 | | // The "OKP" key type is defined in https://tools.ietf.org/html/rfc8037 |
285 | | Status extractJwkFromJwkOKP(const ::google::protobuf::Struct& jwk_pb, |
286 | 25.4k | Jwks::Pubkey* jwk) { |
287 | | // alg is not required, but if present it must be EdDSA |
288 | 25.4k | if (!jwk->alg_.empty() && jwk->alg_ != "EdDSA") { |
289 | 712 | return Status::JwksOKPKeyBadAlg; |
290 | 712 | } |
291 | | |
292 | | // crv is required per https://tools.ietf.org/html/rfc8037#section-2 |
293 | 24.7k | StructUtils jwk_getter(jwk_pb); |
294 | 24.7k | std::string crv_str; |
295 | 24.7k | auto code = jwk_getter.GetString("crv", &crv_str); |
296 | 24.7k | if (code == StructUtils::MISSING) { |
297 | 1.17k | return Status::JwksOKPKeyMissingCrv; |
298 | 1.17k | } |
299 | 23.5k | if (code == StructUtils::WRONG_TYPE) { |
300 | 67 | return Status::JwksOKPKeyBadCrv; |
301 | 67 | } |
302 | 23.4k | jwk->crv_ = crv_str; |
303 | | |
304 | | // Valid crv values: |
305 | | // https://tools.ietf.org/html/rfc8037#section-3 |
306 | | // https://www.iana.org/assignments/jose/jose.xhtml#web-key-elliptic-curve |
307 | | // In addition to Ed25519 there are: |
308 | | // X25519: Implemented in boringssl but not used for JWT and thus not |
309 | | // supported here |
310 | | // Ed448 and X448: Not implemented in boringssl |
311 | 23.4k | int nid; |
312 | 23.4k | size_t keylen; |
313 | 23.4k | if (jwk->crv_ == "Ed25519") { |
314 | 21.9k | nid = EVP_PKEY_ED25519; |
315 | 21.9k | keylen = ED25519_PUBLIC_KEY_LEN; |
316 | 21.9k | } else { |
317 | 1.55k | return Status::JwksOKPKeyCrvUnsupported; |
318 | 1.55k | } |
319 | | |
320 | | // x is required per https://tools.ietf.org/html/rfc8037#section-2 |
321 | 21.9k | std::string x_str; |
322 | 21.9k | code = jwk_getter.GetString("x", &x_str); |
323 | 21.9k | if (code == StructUtils::MISSING) { |
324 | 286 | return Status::JwksOKPKeyMissingX; |
325 | 286 | } |
326 | 21.6k | if (code == StructUtils::WRONG_TYPE) { |
327 | 66 | return Status::JwksOKPKeyBadX; |
328 | 66 | } |
329 | | |
330 | 21.5k | KeyGetter e; |
331 | 21.5k | jwk->okp_key_raw_ = e.createRawKeyFromJwkOKP(nid, keylen, x_str); |
332 | 21.5k | return e.getStatus(); |
333 | 21.6k | } |
334 | | |
335 | 138k | Status extractJwk(const ::google::protobuf::Struct& jwk_pb, Jwks::Pubkey* jwk) { |
336 | 138k | StructUtils jwk_getter(jwk_pb); |
337 | | // Check "kty" parameter, it should exist. |
338 | | // https://tools.ietf.org/html/rfc7517#section-4.1 |
339 | 138k | auto code = jwk_getter.GetString("kty", &jwk->kty_); |
340 | 138k | if (code == StructUtils::MISSING) { |
341 | 12.6k | return Status::JwksMissingKty; |
342 | 12.6k | } |
343 | 125k | if (code == StructUtils::WRONG_TYPE) { |
344 | 401 | return Status::JwksBadKty; |
345 | 401 | } |
346 | | |
347 | | // "kid" and "alg" are optional, if they do not exist, set them to |
348 | | // empty. https://tools.ietf.org/html/rfc7517#page-8 |
349 | 125k | jwk_getter.GetString("kid", &jwk->kid_); |
350 | 125k | jwk_getter.GetString("alg", &jwk->alg_); |
351 | | |
352 | | // Extract public key according to "kty" value. |
353 | | // https://tools.ietf.org/html/rfc7518#section-6.1 |
354 | 125k | if (jwk->kty_ == "EC") { |
355 | 30.9k | return extractJwkFromJwkEC(jwk_pb, jwk); |
356 | 94.2k | } else if (jwk->kty_ == "RSA") { |
357 | 34.1k | return extractJwkFromJwkRSA(jwk_pb, jwk); |
358 | 60.1k | } else if (jwk->kty_ == "oct") { |
359 | 13.6k | return extractJwkFromJwkOct(jwk_pb, jwk); |
360 | 46.5k | } else if (jwk->kty_ == "OKP") { |
361 | 25.4k | return extractJwkFromJwkOKP(jwk_pb, jwk); |
362 | 25.4k | } |
363 | 21.0k | return Status::JwksNotImplementedKty; |
364 | 125k | } |
365 | | |
366 | 1.10k | Status extractX509(const std::string& key, Jwks::Pubkey* jwk) { |
367 | 1.10k | jwk->bio_.reset(BIO_new(BIO_s_mem())); |
368 | 1.10k | if (BIO_write(jwk->bio_.get(), key.c_str(), key.length()) <= 0) { |
369 | 0 | return Status::JwksX509BioWriteError; |
370 | 0 | } |
371 | 1.10k | jwk->x509_.reset( |
372 | 1.10k | PEM_read_bio_X509(jwk->bio_.get(), nullptr, nullptr, nullptr)); |
373 | 1.10k | if (jwk->x509_ == nullptr) { |
374 | 1.09k | return Status::JwksX509ParseError; |
375 | 1.09k | } |
376 | 13 | bssl::UniquePtr<EVP_PKEY> tmp_pkey(X509_get_pubkey(jwk->x509_.get())); |
377 | 13 | if (tmp_pkey == nullptr) { |
378 | 3 | return Status::JwksX509GetPubkeyError; |
379 | 3 | } |
380 | 10 | jwk->rsa_.reset(EVP_PKEY_get1_RSA(tmp_pkey.get())); |
381 | 10 | if (jwk->rsa_ == nullptr) { |
382 | 0 | return Status::JwksX509GetPubkeyError; |
383 | 0 | } |
384 | 10 | return Status::Ok; |
385 | 10 | } |
386 | | |
387 | 1.79k | bool shouldCheckX509(const ::google::protobuf::Struct& jwks_pb) { |
388 | 1.79k | if (jwks_pb.fields().empty()) { |
389 | 3 | return false; |
390 | 3 | } |
391 | | |
392 | 2.46k | for (const auto& kid : jwks_pb.fields()) { |
393 | 2.46k | if (kid.first.empty() || |
394 | 2.46k | kid.second.kind_case() != google::protobuf::Value::kStringValue) { |
395 | 575 | return false; |
396 | 575 | } |
397 | 1.89k | const std::string& cert = kid.second.string_value(); |
398 | 1.89k | if (!absl::StartsWith(cert, kX509CertPrefix) || |
399 | 1.89k | !absl::EndsWith(cert, kX509CertSuffix)) { |
400 | 109 | return false; |
401 | 109 | } |
402 | 1.89k | } |
403 | 1.10k | return true; |
404 | 1.79k | } |
405 | | |
406 | | Status createFromX509(const ::google::protobuf::Struct& jwks_pb, |
407 | 1.10k | std::vector<Jwks::PubkeyPtr>& keys) { |
408 | 1.10k | for (const auto& kid : jwks_pb.fields()) { |
409 | 1.10k | Jwks::PubkeyPtr key_ptr(new Jwks::Pubkey()); |
410 | 1.10k | Status status = extractX509(kid.second.string_value(), key_ptr.get()); |
411 | 1.10k | if (status != Status::Ok) { |
412 | 1.09k | return status; |
413 | 1.09k | } |
414 | | |
415 | 10 | key_ptr->kid_ = kid.first; |
416 | 10 | key_ptr->kty_ = "RSA"; |
417 | 10 | keys.push_back(std::move(key_ptr)); |
418 | 10 | } |
419 | 10 | return Status::Ok; |
420 | 1.10k | } |
421 | | |
422 | | } // namespace |
423 | | |
424 | | Status Jwks::addKeyFromPem(const std::string& pkey, const std::string& kid, |
425 | 0 | const std::string& alg) { |
426 | 0 | JwksPtr tmp = Jwks::createFromPem(pkey, kid, alg); |
427 | 0 | if (tmp->getStatus() != Status::Ok) { |
428 | 0 | return tmp->getStatus(); |
429 | 0 | } |
430 | 0 | keys_.insert(keys_.end(), std::make_move_iterator(tmp->keys_.begin()), |
431 | 0 | std::make_move_iterator(tmp->keys_.end())); |
432 | 0 | return Status::Ok; |
433 | 0 | } |
434 | | |
435 | 20.9k | JwksPtr Jwks::createFrom(const std::string& pkey, Type type) { |
436 | 20.9k | JwksPtr keys(new Jwks()); |
437 | 20.9k | switch (type) { |
438 | 10.4k | case Type::JWKS: |
439 | 10.4k | keys->createFromJwksCore(pkey); |
440 | 10.4k | break; |
441 | 10.4k | case Type::PEM: |
442 | 10.4k | keys->createFromPemCore(pkey); |
443 | 10.4k | break; |
444 | 20.9k | } |
445 | 20.9k | return keys; |
446 | 20.9k | } |
447 | | |
448 | | JwksPtr Jwks::createFromPem(const std::string& pkey, const std::string& kid, |
449 | 0 | const std::string& alg) { |
450 | 0 | std::unique_ptr<Jwks> ret = Jwks::createFrom(pkey, Jwks::PEM); |
451 | 0 | if (ret->getStatus() != Status::Ok) { |
452 | 0 | return ret; |
453 | 0 | } |
454 | 0 | if (ret->keys_.size() != 1) { |
455 | 0 | ret->updateStatus(Status::JwksPemBadBase64); |
456 | 0 | return ret; |
457 | 0 | } |
458 | 0 | Pubkey* jwk = ret->keys_.at(0).get(); |
459 | 0 | jwk->kid_ = kid; |
460 | 0 | jwk->alg_ = alg; |
461 | | |
462 | | // If alg is a known EC algorithm, set the correct crv as well. |
463 | 0 | if (jwk->alg_ == "ES256") { |
464 | 0 | jwk->crv_ = "P-256"; |
465 | 0 | } |
466 | 0 | if (jwk->alg_ == "ES384") { |
467 | 0 | jwk->crv_ = "P-384"; |
468 | 0 | } |
469 | 0 | if (jwk->alg_ == "ES512") { |
470 | 0 | jwk->crv_ = "P-521"; |
471 | 0 | } |
472 | 0 | return ret; |
473 | 0 | } |
474 | | |
475 | | // pkey_pem must be a PEM-encoded PKCS #8 public key. |
476 | | // This is the format that starts with -----BEGIN PUBLIC KEY-----. |
477 | 10.4k | void Jwks::createFromPemCore(const std::string& pkey_pem) { |
478 | 10.4k | keys_.clear(); |
479 | 10.4k | PubkeyPtr key_ptr(new Pubkey()); |
480 | 10.4k | KeyGetter e; |
481 | 10.4k | bssl::UniquePtr<EVP_PKEY> evp_pkey(e.createEvpPkeyFromPem(pkey_pem)); |
482 | 10.4k | updateStatus(e.getStatus()); |
483 | | |
484 | 10.4k | if (evp_pkey == nullptr) { |
485 | 10.4k | assert(e.getStatus() != Status::Ok); |
486 | 10.4k | return; |
487 | 10.4k | } |
488 | 17 | assert(e.getStatus() == Status::Ok); |
489 | | |
490 | 17 | switch (EVP_PKEY_id(evp_pkey.get())) { |
491 | 13 | case EVP_PKEY_RSA: |
492 | 13 | key_ptr->rsa_.reset(EVP_PKEY_get1_RSA(evp_pkey.get())); |
493 | 13 | key_ptr->kty_ = "RSA"; |
494 | 13 | break; |
495 | 4 | case EVP_PKEY_EC: |
496 | 4 | key_ptr->ec_key_.reset(EVP_PKEY_get1_EC_KEY(evp_pkey.get())); |
497 | 4 | key_ptr->kty_ = "EC"; |
498 | 4 | break; |
499 | 0 | #ifndef BORINGSSL_FIPS |
500 | 0 | case EVP_PKEY_ED25519: { |
501 | 0 | uint8_t raw_key[ED25519_PUBLIC_KEY_LEN]; |
502 | 0 | size_t out_len = ED25519_PUBLIC_KEY_LEN; |
503 | 0 | if (EVP_PKEY_get_raw_public_key(evp_pkey.get(), raw_key, &out_len) != 1 || |
504 | 0 | out_len != ED25519_PUBLIC_KEY_LEN) { |
505 | 0 | updateStatus(Status::JwksPemGetRawEd25519Error); |
506 | 0 | return; |
507 | 0 | } |
508 | 0 | key_ptr->okp_key_raw_ = |
509 | 0 | std::string(reinterpret_cast<const char*>(raw_key), out_len); |
510 | 0 | key_ptr->kty_ = "OKP"; |
511 | 0 | key_ptr->crv_ = "Ed25519"; |
512 | 0 | break; |
513 | 0 | } |
514 | 0 | #endif |
515 | 0 | default: |
516 | 0 | updateStatus(Status::JwksPemNotImplementedKty); |
517 | 0 | return; |
518 | 17 | } |
519 | | |
520 | 17 | keys_.push_back(std::move(key_ptr)); |
521 | 17 | } |
522 | | |
523 | 10.4k | void Jwks::createFromJwksCore(const std::string& jwks_json) { |
524 | 10.4k | keys_.clear(); |
525 | | |
526 | 10.4k | ::google::protobuf::util::JsonParseOptions options; |
527 | 10.4k | ::google::protobuf::Struct jwks_pb; |
528 | 10.4k | const auto status = ::google::protobuf::util::JsonStringToMessage( |
529 | 10.4k | jwks_json, &jwks_pb, options); |
530 | 10.4k | if (!status.ok()) { |
531 | 4.60k | updateStatus(Status::JwksParseError); |
532 | 4.60k | return; |
533 | 4.60k | } |
534 | | |
535 | 5.87k | const auto& fields = jwks_pb.fields(); |
536 | 5.87k | const auto keys_it = fields.find("keys"); |
537 | 5.87k | if (keys_it == fields.end()) { |
538 | | // X509 doesn't have "keys" field. |
539 | 1.79k | if (shouldCheckX509(jwks_pb)) { |
540 | 1.10k | updateStatus(createFromX509(jwks_pb, keys_)); |
541 | 1.10k | return; |
542 | 1.10k | } |
543 | 687 | updateStatus(Status::JwksNoKeys); |
544 | 687 | return; |
545 | 1.79k | } |
546 | 4.07k | if (keys_it->second.kind_case() != google::protobuf::Value::kListValue) { |
547 | 3 | updateStatus(Status::JwksBadKeys); |
548 | 3 | return; |
549 | 3 | } |
550 | | |
551 | 2.07M | for (const auto& key_value : keys_it->second.list_value().values()) { |
552 | 2.07M | if (key_value.kind_case() != ::google::protobuf::Value::kStructValue) { |
553 | 1.93M | continue; |
554 | 1.93M | } |
555 | 138k | PubkeyPtr key_ptr(new Pubkey()); |
556 | 138k | Status status = extractJwk(key_value.struct_value(), key_ptr.get()); |
557 | 138k | if (status == Status::Ok) { |
558 | 54.7k | keys_.push_back(std::move(key_ptr)); |
559 | 54.7k | resetStatus(status); |
560 | 83.4k | } else { |
561 | 83.4k | updateStatus(status); |
562 | 83.4k | } |
563 | 138k | } |
564 | | |
565 | 4.07k | if (keys_.empty()) { |
566 | 1.26k | updateStatus(Status::JwksNoValidKeys); |
567 | 2.81k | } else { |
568 | 2.81k | resetStatus(Status::Ok); |
569 | 2.81k | } |
570 | 4.07k | } |
571 | | |
572 | | } // namespace jwt_verify |
573 | | } // namespace google |