├── .bazelrc
├── .bazelversion
├── .gitignore
├── BUILD
├── CONTRIBUTING.md
├── LICENSE
├── OWNERS
├── README.md
├── SECURITY.md
├── WORKSPACE
├── docker
├── Dockerfile-prow-env
└── README
├── googletest.BUILD
├── jwt_verify_lib
├── check_audience.h
├── jwks.h
├── jwt.h
├── status.h
├── struct_utils.h
└── verify.h
├── libprotobuf_mutator.BUILD
├── repositories.bzl
├── script
├── check-style
└── ci.sh
├── simple_lru_cache
├── simple_lru_cache.h
└── simple_lru_cache_inl.h
├── src
├── check_audience.cc
├── jwks.cc
├── jwt.cc
├── status.cc
├── struct_utils.cc
└── verify.cc
└── test
├── check_audience_test.cc
├── fuzz
├── BUILD
├── corpus
│ └── jwt_verify_lib_fuzz_test
│ │ ├── jwks_ec.txt
│ │ ├── jwks_hmac.txt
│ │ ├── jwks_okp.txt
│ │ ├── jwks_pem.txt
│ │ ├── jwks_rsa.txt
│ │ └── jwks_x509.txt
├── corpus_format_test.cc
├── jwt_verify_lib_fuzz_input.proto
└── jwt_verify_lib_fuzz_test.cc
├── jwks_test.cc
├── jwt_test.cc
├── jwt_time_test.cc
├── simple_lru_cache_test.cc
├── test_common.h
├── verify_audiences_test.cc
├── verify_jwk_ec_test.cc
├── verify_jwk_hmac_test.cc
├── verify_jwk_okp_test.cc
├── verify_jwk_rsa_pss_test.cc
├── verify_jwk_rsa_test.cc
├── verify_pem_ec_test.cc
├── verify_pem_okp_test.cc
├── verify_pem_rsa_test.cc
└── verify_x509_test.cc
/.bazelrc:
--------------------------------------------------------------------------------
1 | # Match Envoy's toolchain.
2 | build --cxxopt=-std=c++17 --host_cxxopt=-std=c++17
3 |
4 | # Force the use of Clang for C++ builds.
5 | build --action_env=CC=clang-13
6 | build --action_env=CXX=clang++-13
7 |
8 | # Define the --config=asan-libfuzzer configuration.
9 | build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:libfuzzer
10 | build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer
11 | build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=asan
12 |
--------------------------------------------------------------------------------
/.bazelversion:
--------------------------------------------------------------------------------
1 | 5.3.2
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bazel-*
2 | .vscode/
3 |
--------------------------------------------------------------------------------
/BUILD:
--------------------------------------------------------------------------------
1 | licenses(["notice"])
2 |
3 | package(default_visibility = ["//visibility:public"])
4 |
5 | exports_files(["LICENSE"])
6 |
7 | cc_library(
8 | name = "jwt_verify_lib",
9 | srcs = [
10 | "src/check_audience.cc",
11 | "src/jwks.cc",
12 | "src/jwt.cc",
13 | "src/status.cc",
14 | "src/struct_utils.cc",
15 | "src/verify.cc",
16 | ],
17 | hdrs = [
18 | "jwt_verify_lib/check_audience.h",
19 | "jwt_verify_lib/jwks.h",
20 | "jwt_verify_lib/jwt.h",
21 | "jwt_verify_lib/status.h",
22 | "jwt_verify_lib/struct_utils.h",
23 | "jwt_verify_lib/verify.h",
24 | ],
25 | deps = [
26 | "//external:abseil_flat_hash_set",
27 | "//external:abseil_strings",
28 | "//external:abseil_time",
29 | "//external:protobuf",
30 | "//external:ssl",
31 | ],
32 | )
33 |
34 | cc_library(
35 | name = "simple_lru_cache_lib",
36 | hdrs = [
37 | "simple_lru_cache/simple_lru_cache.h",
38 | "simple_lru_cache/simple_lru_cache_inl.h",
39 | ],
40 | deps = [
41 | "//external:abseil_flat_hash_map",
42 | ],
43 | )
44 |
45 | cc_test(
46 | name = "check_audience_test",
47 | timeout = "short",
48 | srcs = [
49 | "test/check_audience_test.cc",
50 | ],
51 | linkopts = [
52 | "-lm",
53 | "-lpthread",
54 | ],
55 | linkstatic = 1,
56 | deps = [
57 | ":jwt_verify_lib",
58 | "//external:googletest_main",
59 | ],
60 | )
61 |
62 | cc_test(
63 | name = "jwt_test",
64 | timeout = "short",
65 | srcs = [
66 | "test/jwt_test.cc",
67 | ],
68 | linkopts = [
69 | "-lm",
70 | "-lpthread",
71 | ],
72 | linkstatic = 1,
73 | deps = [
74 | ":jwt_verify_lib",
75 | "//external:googletest_main",
76 | ],
77 | )
78 |
79 | cc_test(
80 | name = "jwks_test",
81 | timeout = "short",
82 | srcs = [
83 | "test/jwks_test.cc",
84 | "test/test_common.h",
85 | ],
86 | linkopts = [
87 | "-lm",
88 | "-lpthread",
89 | ],
90 | linkstatic = 1,
91 | deps = [
92 | ":jwt_verify_lib",
93 | "//external:googletest_main",
94 | ],
95 | )
96 |
97 | cc_test(
98 | name = "simple_lru_cache_test",
99 | timeout = "short",
100 | srcs = [
101 | "test/simple_lru_cache_test.cc",
102 | ],
103 | linkopts = [
104 | "-lm",
105 | "-lpthread",
106 | ],
107 | linkstatic = 1,
108 | deps = [
109 | ":simple_lru_cache_lib",
110 | "//external:googletest_main",
111 | ],
112 | )
113 |
114 | cc_test(
115 | name = "verify_x509_test",
116 | timeout = "short",
117 | srcs = [
118 | "test/test_common.h",
119 | "test/verify_x509_test.cc",
120 | ],
121 | linkopts = [
122 | "-lm",
123 | "-lpthread",
124 | ],
125 | linkstatic = 1,
126 | deps = [
127 | ":jwt_verify_lib",
128 | "//external:googletest_main",
129 | ],
130 | )
131 |
132 | cc_test(
133 | name = "verify_audiences_test",
134 | timeout = "short",
135 | srcs = [
136 | "test/test_common.h",
137 | "test/verify_audiences_test.cc",
138 | ],
139 | linkopts = [
140 | "-lm",
141 | "-lpthread",
142 | ],
143 | linkstatic = 1,
144 | deps = [
145 | ":jwt_verify_lib",
146 | "//external:googletest_main",
147 | ],
148 | )
149 |
150 | cc_test(
151 | name = "jwt_time_test",
152 | timeout = "short",
153 | srcs = [
154 | "test/jwt_time_test.cc",
155 | "test/test_common.h",
156 | ],
157 | linkopts = [
158 | "-lm",
159 | "-lpthread",
160 | ],
161 | linkstatic = 1,
162 | deps = [
163 | ":jwt_verify_lib",
164 | "//external:googletest_main",
165 | ],
166 | )
167 |
168 | cc_test(
169 | name = "verify_jwk_rsa_test",
170 | timeout = "short",
171 | srcs = [
172 | "test/test_common.h",
173 | "test/verify_jwk_rsa_test.cc",
174 | ],
175 | linkopts = [
176 | "-lm",
177 | "-lpthread",
178 | ],
179 | linkstatic = 1,
180 | deps = [
181 | ":jwt_verify_lib",
182 | "//external:googletest_main",
183 | ],
184 | )
185 |
186 | cc_test(
187 | name = "verify_jwk_rsa_pss_test",
188 | timeout = "short",
189 | srcs = [
190 | "test/test_common.h",
191 | "test/verify_jwk_rsa_pss_test.cc",
192 | ],
193 | linkopts = [
194 | "-lm",
195 | "-lpthread",
196 | ],
197 | linkstatic = 1,
198 | deps = [
199 | ":jwt_verify_lib",
200 | "//external:googletest_main",
201 | ],
202 | )
203 |
204 | cc_test(
205 | name = "verify_jwk_ec_test",
206 | timeout = "short",
207 | srcs = [
208 | "test/test_common.h",
209 | "test/verify_jwk_ec_test.cc",
210 | ],
211 | linkopts = [
212 | "-lm",
213 | "-lpthread",
214 | ],
215 | linkstatic = 1,
216 | deps = [
217 | ":jwt_verify_lib",
218 | "//external:googletest_main",
219 | ],
220 | )
221 |
222 | cc_test(
223 | name = "verify_jwk_hmac_test",
224 | timeout = "short",
225 | srcs = [
226 | "test/test_common.h",
227 | "test/verify_jwk_hmac_test.cc",
228 | ],
229 | linkopts = [
230 | "-lm",
231 | "-lpthread",
232 | ],
233 | linkstatic = 1,
234 | deps = [
235 | ":jwt_verify_lib",
236 | "//external:googletest_main",
237 | ],
238 | )
239 |
240 | cc_test(
241 | name = "verify_jwk_okp_test",
242 | timeout = "short",
243 | srcs = [
244 | "test/test_common.h",
245 | "test/verify_jwk_okp_test.cc",
246 | ],
247 | linkopts = [
248 | "-lm",
249 | "-lpthread",
250 | ],
251 | linkstatic = 1,
252 | deps = [
253 | ":jwt_verify_lib",
254 | "//external:googletest_main",
255 | ],
256 | )
257 |
258 | cc_test(
259 | name = "verify_pem_rsa_test",
260 | timeout = "short",
261 | srcs = [
262 | "test/test_common.h",
263 | "test/verify_pem_rsa_test.cc",
264 | ],
265 | linkopts = [
266 | "-lm",
267 | "-lpthread",
268 | ],
269 | linkstatic = 1,
270 | deps = [
271 | ":jwt_verify_lib",
272 | "//external:googletest_main",
273 | ],
274 | )
275 |
276 | cc_test(
277 | name = "verify_pem_ec_test",
278 | timeout = "short",
279 | srcs = [
280 | "test/test_common.h",
281 | "test/verify_pem_ec_test.cc",
282 | ],
283 | linkopts = [
284 | "-lm",
285 | "-lpthread",
286 | ],
287 | linkstatic = 1,
288 | deps = [
289 | ":jwt_verify_lib",
290 | "//external:googletest_main",
291 | ],
292 | )
293 |
294 | cc_test(
295 | name = "verify_pem_okp_test",
296 | timeout = "short",
297 | srcs = [
298 | "test/test_common.h",
299 | "test/verify_pem_okp_test.cc",
300 | ],
301 | linkopts = [
302 | "-lm",
303 | "-lpthread",
304 | ],
305 | linkstatic = 1,
306 | deps = [
307 | ":jwt_verify_lib",
308 | "//external:googletest_main",
309 | ],
310 | )
311 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution,
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows [Google's Open Source Community
28 | Guidelines](https://opensource.google.com/conduct/).
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2018 Google Inc. All rights reserved.
2 |
3 | Apache License
4 | Version 2.0, January 2004
5 | http://www.apache.org/licenses/
6 |
7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8 |
9 | 1. Definitions.
10 |
11 | "License" shall mean the terms and conditions for use, reproduction,
12 | and distribution as defined by Sections 1 through 9 of this document.
13 |
14 | "Licensor" shall mean the copyright owner or entity authorized by
15 | the copyright owner that is granting the License.
16 |
17 | "Legal Entity" shall mean the union of the acting entity and all
18 | other entities that control, are controlled by, or are under common
19 | control with that entity. For the purposes of this definition,
20 | "control" means (i) the power, direct or indirect, to cause the
21 | direction or management of such entity, whether by contract or
22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
23 | outstanding shares, or (iii) beneficial ownership of such entity.
24 |
25 | "You" (or "Your") shall mean an individual or Legal Entity
26 | exercising permissions granted by this License.
27 |
28 | "Source" form shall mean the preferred form for making modifications,
29 | including but not limited to software source code, documentation
30 | source, and configuration files.
31 |
32 | "Object" form shall mean any form resulting from mechanical
33 | transformation or translation of a Source form, including but
34 | not limited to compiled object code, generated documentation,
35 | and conversions to other media types.
36 |
37 | "Work" shall mean the work of authorship, whether in Source or
38 | Object form, made available under the License, as indicated by a
39 | copyright notice that is included in or attached to the work
40 | (an example is provided in the Appendix below).
41 |
42 | "Derivative Works" shall mean any work, whether in Source or Object
43 | form, that is based on (or derived from) the Work and for which the
44 | editorial revisions, annotations, elaborations, or other modifications
45 | represent, as a whole, an original work of authorship. For the purposes
46 | of this License, Derivative Works shall not include works that remain
47 | separable from, or merely link (or bind by name) to the interfaces of,
48 | the Work and Derivative Works thereof.
49 |
50 | "Contribution" shall mean any work of authorship, including
51 | the original version of the Work and any modifications or additions
52 | to that Work or Derivative Works thereof, that is intentionally
53 | submitted to Licensor for inclusion in the Work by the copyright owner
54 | or by an individual or Legal Entity authorized to submit on behalf of
55 | the copyright owner. For the purposes of this definition, "submitted"
56 | means any form of electronic, verbal, or written communication sent
57 | to the Licensor or its representatives, including but not limited to
58 | communication on electronic mailing lists, source code control systems,
59 | and issue tracking systems that are managed by, or on behalf of, the
60 | Licensor for the purpose of discussing and improving the Work, but
61 | excluding communication that is conspicuously marked or otherwise
62 | designated in writing by the copyright owner as "Not a Contribution."
63 |
64 | "Contributor" shall mean Licensor and any individual or Legal Entity
65 | on behalf of whom a Contribution has been received by Licensor and
66 | subsequently incorporated within the Work.
67 |
68 | 2. Grant of Copyright License. Subject to the terms and conditions of
69 | this License, each Contributor hereby grants to You a perpetual,
70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71 | copyright license to reproduce, prepare Derivative Works of,
72 | publicly display, publicly perform, sublicense, and distribute the
73 | Work and such Derivative Works in Source or Object form.
74 |
75 | 3. Grant of Patent License. Subject to the terms and conditions of
76 | this License, each Contributor hereby grants to You a perpetual,
77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78 | (except as stated in this section) patent license to make, have made,
79 | use, offer to sell, sell, import, and otherwise transfer the Work,
80 | where such license applies only to those patent claims licensable
81 | by such Contributor that are necessarily infringed by their
82 | Contribution(s) alone or by combination of their Contribution(s)
83 | with the Work to which such Contribution(s) was submitted. If You
84 | institute patent litigation against any entity (including a
85 | cross-claim or counterclaim in a lawsuit) alleging that the Work
86 | or a Contribution incorporated within the Work constitutes direct
87 | or contributory patent infringement, then any patent licenses
88 | granted to You under this License for that Work shall terminate
89 | as of the date such litigation is filed.
90 |
91 | 4. Redistribution. You may reproduce and distribute copies of the
92 | Work or Derivative Works thereof in any medium, with or without
93 | modifications, and in Source or Object form, provided that You
94 | meet the following conditions:
95 |
96 | (a) You must give any other recipients of the Work or
97 | Derivative Works a copy of this License; and
98 |
99 | (b) You must cause any modified files to carry prominent notices
100 | stating that You changed the files; and
101 |
102 | (c) You must retain, in the Source form of any Derivative Works
103 | that You distribute, all copyright, patent, trademark, and
104 | attribution notices from the Source form of the Work,
105 | excluding those notices that do not pertain to any part of
106 | the Derivative Works; and
107 |
108 | (d) If the Work includes a "NOTICE" text file as part of its
109 | distribution, then any Derivative Works that You distribute must
110 | include a readable copy of the attribution notices contained
111 | within such NOTICE file, excluding those notices that do not
112 | pertain to any part of the Derivative Works, in at least one
113 | of the following places: within a NOTICE text file distributed
114 | as part of the Derivative Works; within the Source form or
115 | documentation, if provided along with the Derivative Works; or,
116 | within a display generated by the Derivative Works, if and
117 | wherever such third-party notices normally appear. The contents
118 | of the NOTICE file are for informational purposes only and
119 | do not modify the License. You may add Your own attribution
120 | notices within Derivative Works that You distribute, alongside
121 | or as an addendum to the NOTICE text from the Work, provided
122 | that such additional attribution notices cannot be construed
123 | as modifying the License.
124 |
125 | You may add Your own copyright statement to Your modifications and
126 | may provide additional or different license terms and conditions
127 | for use, reproduction, or distribution of Your modifications, or
128 | for any such Derivative Works as a whole, provided Your use,
129 | reproduction, and distribution of the Work otherwise complies with
130 | the conditions stated in this License.
131 |
132 | 5. Submission of Contributions. Unless You explicitly state otherwise,
133 | any Contribution intentionally submitted for inclusion in the Work
134 | by You to the Licensor shall be under the terms and conditions of
135 | this License, without any additional terms or conditions.
136 | Notwithstanding the above, nothing herein shall supersede or modify
137 | the terms of any separate license agreement you may have executed
138 | with Licensor regarding such Contributions.
139 |
140 | 6. Trademarks. This License does not grant permission to use the trade
141 | names, trademarks, service marks, or product names of the Licensor,
142 | except as required for reasonable and customary use in describing the
143 | origin of the Work and reproducing the content of the NOTICE file.
144 |
145 | 7. Disclaimer of Warranty. Unless required by applicable law or
146 | agreed to in writing, Licensor provides the Work (and each
147 | Contributor provides its Contributions) on an "AS IS" BASIS,
148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149 | implied, including, without limitation, any warranties or conditions
150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151 | PARTICULAR PURPOSE. You are solely responsible for determining the
152 | appropriateness of using or redistributing the Work and assume any
153 | risks associated with Your exercise of permissions under this License.
154 |
155 | 8. Limitation of Liability. In no event and under no legal theory,
156 | whether in tort (including negligence), contract, or otherwise,
157 | unless required by applicable law (such as deliberate and grossly
158 | negligent acts) or agreed to in writing, shall any Contributor be
159 | liable to You for damages, including any direct, indirect, special,
160 | incidental, or consequential damages of any character arising as a
161 | result of this License or out of the use or inability to use the
162 | Work (including but not limited to damages for loss of goodwill,
163 | work stoppage, computer failure or malfunction, or any and all
164 | other commercial damages or losses), even if such Contributor
165 | has been advised of the possibility of such damages.
166 |
167 | 9. Accepting Warranty or Additional Liability. While redistributing
168 | the Work or Derivative Works thereof, You may choose to offer,
169 | and charge a fee for, acceptance of support, warranty, indemnity,
170 | or other liability obligations and/or rights consistent with this
171 | License. However, in accepting such obligations, You may act only
172 | on Your own behalf and on Your sole responsibility, not on behalf
173 | of any other Contributor, and only if You agree to indemnify,
174 | defend, and hold each Contributor harmless for any liability
175 | incurred by, or claims asserted against, such Contributor by reason
176 | of your accepting any such warranty or additional liability.
177 |
178 | END OF TERMS AND CONDITIONS
179 |
180 | APPENDIX: How to apply the Apache License to your work.
181 |
182 | To apply the Apache License to your work, attach the following
183 | boilerplate notice, with the fields enclosed by brackets "[]"
184 | replaced with your own identifying information. (Don't include
185 | the brackets!) The text should be enclosed in the appropriate
186 | comment syntax for the file format. We also recommend that a
187 | file or class name and description of purpose be included on the
188 | same "printed page" as the copyright notice for easier
189 | identification within third-party archives.
190 |
191 | Copyright [yyyy] [name of copyright owner]
192 |
193 | Licensed under the Apache License, Version 2.0 (the "License");
194 | you may not use this file except in compliance with the License.
195 | You may obtain a copy of the License at
196 |
197 | http://www.apache.org/licenses/LICENSE-2.0
198 |
199 | Unless required by applicable law or agreed to in writing, software
200 | distributed under the License is distributed on an "AS IS" BASIS,
201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
202 | See the License for the specific language governing permissions and
203 | limitations under the License.
204 |
--------------------------------------------------------------------------------
/OWNERS:
--------------------------------------------------------------------------------
1 | # See the OWNERS docs at https://go.k8s.io/owners
2 | approvers:
3 | - qiwzhang
4 | - nareddyt
5 |
6 | reviewers:
7 | - orionHong
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://testgrid.k8s.io/googleoss-jwt-verify-lib#Summary)
2 |
3 | This repository stores JWT verification files for c++.
4 | These files are originally created in [istio/proxy jwt_auth folder](https://github.com/istio/proxy/blob/master/src/envoy/http/jwt_auth/jwt.h).
5 | The key reason to create a separate repo for them is that they can be used by other projects. For example, [envoyproxy](https://github.com/envoyproxy/envoy) likes to use these code to build a jwt_auth HTTP filter.
6 |
7 | This is not an officially supported Google product
8 |
9 | For contributors:
10 | If you make any changes, please make sure to use Bazel to pass all unit tests by running:
11 |
12 | ```
13 | bazel test //...
14 | ```
15 | Please format your codes by running:
16 |
17 | ```
18 | script/check-style
19 | ```
20 |
21 | ## Continuous Integration
22 | This repository is integreated with [OSS Prow](https://github.com/GoogleCloudPlatform/oss-test-infra), and the job setup is in the [OSS Prow repo](https://github.com/GoogleCloudPlatform/oss-test-infra/blob/master/prow/prowjobs/google/jwt_verify_lib/jwt-verify-lib-presubmit.yaml). Currently, Prow runs the [presubmit script](./script/ci.sh) on each Pull Request to verify tests pass. Note:
23 | - PR submission is only allowed if the job passes.
24 | - If you are an outside contributor, Prow may not run until a Googler LGTMs.
25 |
26 | The CI is running in a [docker image](./docker/Dockerfile-prow-env) that was pre-built and pushed to Google Cloud Registry. For future updates to the CI image, please refer to the commands listed in [./docker folder README](./docker/README).
27 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Follow envoy security policy
4 |
5 | This code is mainly used by [envoy](https://github.com/envoyproxy/envoy). Please follow envoy security [policy](https://github.com/envoyproxy/envoy/security/policy).
6 |
7 |
8 | ## Supported Versions
9 |
10 | This repo is small, there is not versioned releases. Please always use top of the tree. If there is any bugs or security fixes, they will be fixed in the master branch.
11 |
12 |
13 | ## Reporting a Vulnerability
14 |
15 | Please report any vulnerability to envoy according to its security [policy](https://github.com/envoyproxy/envoy/security/policy).
16 |
--------------------------------------------------------------------------------
/WORKSPACE:
--------------------------------------------------------------------------------
1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
2 |
3 | http_archive(
4 | name = "rules_fuzzing",
5 | sha256 = "d9002dd3cd6437017f08593124fdd1b13b3473c7b929ceb0e60d317cb9346118",
6 | strip_prefix = "rules_fuzzing-0.3.2",
7 | urls = ["https://github.com/bazelbuild/rules_fuzzing/archive/v0.3.2.zip"],
8 | )
9 |
10 | load("@rules_fuzzing//fuzzing:repositories.bzl", "rules_fuzzing_dependencies")
11 | rules_fuzzing_dependencies()
12 |
13 | load("@rules_fuzzing//fuzzing:init.bzl", "rules_fuzzing_init")
14 | rules_fuzzing_init()
15 |
16 | load(
17 | "//:repositories.bzl",
18 | "boringssl_repositories",
19 | "googletest_repositories",
20 | "abseil_repositories",
21 | "protobuf_repositories",
22 | "libprotobuf_mutator_repositories",
23 | )
24 |
25 | boringssl_repositories()
26 | googletest_repositories()
27 | abseil_repositories()
28 | protobuf_repositories()
29 | libprotobuf_mutator_repositories()
30 |
31 | load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
32 |
33 | protobuf_deps()
34 |
35 | load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
36 |
37 | rules_proto_dependencies()
38 |
39 | rules_proto_toolchains()
40 |
--------------------------------------------------------------------------------
/docker/Dockerfile-prow-env:
--------------------------------------------------------------------------------
1 | # Copyright 2022 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 | # http://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 | FROM debian:bookworm
16 |
17 | LABEL maintainer="cloud-esf-dev@google.com"
18 |
19 | # add env we can debug with the image name:tag
20 | ARG IMAGE_ARG
21 | ENV IMAGE=${IMAGE_ARG}
22 |
23 |
24 | RUN apt-get update -y
25 | RUN apt-get -y install \
26 | wget make cmake python3 python3-pip pkg-config coreutils \
27 | zlib1g-dev curl libtool automake zip time rsync ninja-build \
28 | git bash-completion jq default-jdk python3-distutils libicu-dev libbrotli-dev
29 |
30 | # # install Bazelisk
31 | RUN wget -O /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.15.0/bazelisk-linux-amd64 && \
32 | chmod +x /usr/local/bin/bazelisk && \
33 | cp /usr/local/bin/bazelisk /usr/local/bin/bazel
34 |
35 | # install clang-13 and associated tools
36 | RUN wget -O- https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add - && \
37 | echo "deb https://apt.llvm.org/buster/ llvm-toolchain-buster-13 main" >> /etc/apt/sources.list && \
38 | apt-get update && \
39 | apt-get install -y llvm-13 llvm-13-dev libclang-13-dev clang-13 \
40 | lld-13 clang-tools-13 clang-format-13 libc++-dev xz-utils
41 |
42 | ENV CC clang-13
43 | ENV CXX clang++-13
44 |
45 |
--------------------------------------------------------------------------------
/docker/README:
--------------------------------------------------------------------------------
1 | ## Docker images for jwt_verify_lib
2 |
3 | ### How to build the image
4 | ```sh
5 | docker build -f ${DOCKER_FILE_NAME} -t ${IMAGE_TAG} .
6 | ```
7 | We can also inspect the image by running
8 | ```sh
9 | docker run -it --entrypoint /bin/sh ${IMAGE_TAG}
10 | ```
11 |
12 | ### How to push the image
13 | Please refer to [Docker official
14 | documentation](https://docs.docker.com/engine/reference/commandline/push/#push-a-new-image-to-a-registry).
15 |
16 | #### Dockerfile-prow-env
17 | This image is used to run OSS Prow CI jobs.
18 | ```sh
19 | # Build image
20 | docker build -f Dockerfile-prow-env -t gcr.io/cloudesf-testing/jwt-verify-lib-prow:v{YYYYMMDD} .
21 | # Push image to GCR
22 | docker image push gcr.io/cloudesf-testing/jwt-verify-lib-prow:v{YYYYMMDD}
23 | ```
24 |
25 |
--------------------------------------------------------------------------------
/googletest.BUILD:
--------------------------------------------------------------------------------
1 |
2 | cc_library(
3 | name = "googletest",
4 | srcs = [
5 | "googletest/src/gtest-all.cc",
6 | "googlemock/src/gmock-all.cc",
7 | ],
8 | hdrs = glob([
9 | "googletest/include/**/*.h",
10 | "googlemock/include/**/*.h",
11 | "googletest/src/*.cc",
12 | "googletest/src/*.h",
13 | "googlemock/src/*.cc",
14 | ]),
15 | includes = [
16 | "googlemock",
17 | "googletest",
18 | "googletest/include",
19 | "googlemock/include",
20 | ],
21 | visibility = ["//visibility:public"],
22 | )
23 |
24 | cc_library(
25 | name = "googletest_main",
26 | srcs = ["googlemock/src/gmock_main.cc"],
27 | visibility = ["//visibility:public"],
28 | deps = [":googletest"],
29 | )
30 |
31 | cc_library(
32 | name = "googletest_prod",
33 | hdrs = [
34 | "googletest/include/gtest/gtest_prod.h",
35 | ],
36 | includes = [
37 | "googletest/include",
38 | ],
39 | visibility = ["//visibility:public"],
40 | )
41 |
--------------------------------------------------------------------------------
/jwt_verify_lib/check_audience.h:
--------------------------------------------------------------------------------
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.#pragma once
14 |
15 | #pragma once
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | #include "jwt_verify_lib/status.h"
23 |
24 | namespace google {
25 | namespace jwt_verify {
26 |
27 | /**
28 | * RFC for JWT `aud `_ only
29 | * specifies case sensitive comparison. But experiences showed that users
30 | * easily add wrong scheme and tailing slash to cause mis-match.
31 | * In this implemeation, scheme portion of URI and tailing slash is removed
32 | * before comparison.
33 | */
34 | class CheckAudience {
35 | public:
36 | // Construct the object with a list audiences from config.
37 | CheckAudience(const std::vector& config_audiences);
38 |
39 | // Check any of jwt_audiences is matched with one of configurated ones.
40 | bool areAudiencesAllowed(const std::vector& jwt_audiences) const;
41 |
42 | // check if config audiences is empty
43 | bool empty() const { return config_audiences_.empty(); }
44 |
45 | private:
46 | // configured audiences;
47 | std::set config_audiences_;
48 | };
49 |
50 | typedef std::unique_ptr CheckAudiencePtr;
51 |
52 | } // namespace jwt_verify
53 | } // namespace google
54 |
--------------------------------------------------------------------------------
/jwt_verify_lib/jwks.h:
--------------------------------------------------------------------------------
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.#pragma once
14 |
15 | #pragma once
16 |
17 | #include
18 | #include
19 |
20 | #include "jwt_verify_lib/status.h"
21 | #include "openssl/ec.h"
22 | #include "openssl/evp.h"
23 | #include "openssl/pem.h"
24 |
25 | namespace google {
26 | namespace jwt_verify {
27 |
28 | /**
29 | * Class to parse and a hold JSON Web Key Set.
30 | *
31 | * Usage example:
32 | * JwksPtr keys = Jwks::createFrom(jwks_string, type);
33 | * if (keys->getStatus() == Status::Ok) { ... }
34 | */
35 | class Jwks : public WithStatus {
36 | public:
37 | // Format of public key.
38 | enum Type { JWKS, PEM };
39 |
40 | // Create from string
41 | static std::unique_ptr createFrom(const std::string& pkey, Type type);
42 | // Executes to createFrom with type=PEM and sets additional JWKS paramaters
43 | // not specified within the PEM.
44 | static std::unique_ptr createFromPem(const std::string& pkey,
45 | const std::string& kid,
46 | const std::string& alg);
47 |
48 | // Adds a key to this keyset.
49 | Status addKeyFromPem(const std::string& pkey, const std::string& kid,
50 | const std::string& alg);
51 |
52 | // Struct for JSON Web Key
53 | struct Pubkey {
54 | std::string hmac_key_;
55 | std::string kid_;
56 | std::string kty_;
57 | std::string alg_;
58 | std::string crv_;
59 | bssl::UniquePtr rsa_;
60 | bssl::UniquePtr ec_key_;
61 | std::string okp_key_raw_;
62 | bssl::UniquePtr bio_;
63 | bssl::UniquePtr x509_;
64 | };
65 | typedef std::unique_ptr PubkeyPtr;
66 |
67 | // Access to list of Jwks
68 | const std::vector& keys() const { return keys_; }
69 |
70 | private:
71 | // Create Jwks
72 | void createFromJwksCore(const std::string& pkey_jwks);
73 | // Create PEM
74 | void createFromPemCore(const std::string& pkey_pem);
75 |
76 | // List of Jwks
77 | std::vector keys_;
78 | };
79 |
80 | typedef std::unique_ptr JwksPtr;
81 |
82 | } // namespace jwt_verify
83 | } // namespace google
84 |
--------------------------------------------------------------------------------
/jwt_verify_lib/jwt.h:
--------------------------------------------------------------------------------
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.#pragma once
14 |
15 | #pragma once
16 |
17 | #include
18 | #include
19 |
20 | #include "google/protobuf/struct.pb.h"
21 | #include "jwt_verify_lib/status.h"
22 |
23 | namespace google {
24 | namespace jwt_verify {
25 |
26 | // Clock skew defaults to one minute.
27 | constexpr uint64_t kClockSkewInSecond = 60;
28 |
29 | /**
30 | * struct to hold a JWT data.
31 | */
32 | struct Jwt {
33 | // entire jwt
34 | std::string jwt_;
35 |
36 | // header string
37 | std::string header_str_;
38 | // header base64_url encoded
39 | std::string header_str_base64url_;
40 | // header in Struct protobuf
41 | ::google::protobuf::Struct header_pb_;
42 |
43 | // payload string
44 | std::string payload_str_;
45 | // payload base64_url encoded
46 | std::string payload_str_base64url_;
47 | // payload in Struct protobuf
48 | ::google::protobuf::Struct payload_pb_;
49 | // signature string
50 | std::string signature_;
51 | // alg
52 | std::string alg_;
53 | // kid
54 | std::string kid_;
55 | // iss
56 | std::string iss_;
57 | // audiences
58 | std::vector audiences_;
59 | // sub
60 | std::string sub_;
61 | // issued at
62 | uint64_t iat_ = 0;
63 | // not before
64 | uint64_t nbf_ = 0;
65 | // expiration
66 | uint64_t exp_ = 0;
67 | // JWT ID
68 | std::string jti_;
69 |
70 | /**
71 | * Standard constructor.
72 | */
73 | Jwt() {}
74 | /**
75 | * Copy constructor. The copy constructor is marked as explicit as the caller
76 | * should understand the copy operation is non-trivial as a complete
77 | * re-deserialization occurs.
78 | * @param rhs the instance to copy.
79 | */
80 | explicit Jwt(const Jwt& instance);
81 |
82 | /**
83 | * Copy Jwt instance.
84 | * @param rhs the instance to copy.
85 | * @return this
86 | */
87 | Jwt& operator=(const Jwt& rhs);
88 |
89 | /**
90 | * Parse Jwt from string text
91 | * @return the status.
92 | */
93 | Status parseFromString(const std::string& jwt);
94 |
95 | /*
96 | * Verify Jwt time constraint if specified
97 | * esp: expiration time, nbf: not before time.
98 | * @param now: is the current time in seconds since the unix epoch
99 | * @param clock_skew: the the clock skew in second.
100 | * @return the verification status.
101 | */
102 | Status verifyTimeConstraint(uint64_t now,
103 | uint64_t clock_skew = kClockSkewInSecond) const;
104 | };
105 |
106 | } // namespace jwt_verify
107 | } // namespace google
108 |
--------------------------------------------------------------------------------
/jwt_verify_lib/status.h:
--------------------------------------------------------------------------------
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 | #pragma once
16 |
17 | #include
18 |
19 | namespace google {
20 | namespace jwt_verify {
21 |
22 | /**
23 | * Define the Jwt verification error status.
24 | */
25 | enum class Status {
26 | Ok = 0,
27 |
28 | // Jwt errors:
29 |
30 | // Jwt missing.
31 | JwtMissed,
32 |
33 | // Jwt not valid yet.
34 | JwtNotYetValid,
35 |
36 | // Jwt expired.
37 | JwtExpired,
38 |
39 | // JWT is not in the form of Header.Payload.Signature
40 | JwtBadFormat,
41 |
42 | // Jwt header is an invalid Base64url encoded.
43 | JwtHeaderParseErrorBadBase64,
44 |
45 | // Jwt header is an invalid JSON.
46 | JwtHeaderParseErrorBadJson,
47 |
48 | // "alg" in the header is not a string.
49 | JwtHeaderBadAlg,
50 |
51 | // Value of "alg" in the header is invalid.
52 | JwtHeaderNotImplementedAlg,
53 |
54 | // "kid" in the header is not a string.
55 | JwtHeaderBadKid,
56 |
57 | // Jwt payload is an invalid Base64url encoded.
58 | JwtPayloadParseErrorBadBase64,
59 |
60 | // Jwt payload is an invalid JSON.
61 | JwtPayloadParseErrorBadJson,
62 |
63 | // Jwt payload field [iss] must be string.
64 | JwtPayloadParseErrorIssNotString,
65 |
66 | // Jwt payload field [sub] must be string.
67 | JwtPayloadParseErrorSubNotString,
68 |
69 | // Jwt payload field [iat] must be integer.
70 | JwtPayloadParseErrorIatNotInteger,
71 |
72 | // Jwt payload field [iat] must be within a 64 bit positive integer range.
73 | JwtPayloadParseErrorIatOutOfRange,
74 |
75 | // Jwt payload field [nbf] must be integer.
76 | JwtPayloadParseErrorNbfNotInteger,
77 |
78 | // Jwt payload field [nbf] must be within a 64 bit positive integer range.
79 | JwtPayloadParseErrorNbfOutOfRange,
80 |
81 | // Jwt payload field [exp] must be integer.
82 | JwtPayloadParseErrorExpNotInteger,
83 |
84 | // Jwt payload field [exp] must be within a 64 bit positive integer range.
85 | JwtPayloadParseErrorExpOutOfRange,
86 |
87 | // Jwt payload field [jti] must be string.
88 | JwtPayloadParseErrorJtiNotString,
89 |
90 | // Jwt payload field [aud] must be string or string list.
91 | JwtPayloadParseErrorAudNotString,
92 |
93 | // Jwt signature is an invalid Base64url input.
94 | JwtSignatureParseErrorBadBase64,
95 |
96 | // Jwt ED25519 signature is wrong length
97 | JwtEd25519SignatureWrongLength,
98 |
99 | // Issuer is not configured.
100 | JwtUnknownIssuer,
101 |
102 | // Audience is not allowed.
103 | JwtAudienceNotAllowed,
104 |
105 | // Jwt verification fails.
106 | JwtVerificationFail,
107 |
108 | // Found multiple Jwt tokens.
109 | JwtMultipleTokens,
110 |
111 | // Jwks errors
112 |
113 | // Jwks is an invalid JSON.
114 | JwksParseError,
115 |
116 | // Jwks does not have "keys".
117 | JwksNoKeys,
118 |
119 | // "keys" in Jwks is not an array.
120 | JwksBadKeys,
121 |
122 | // Jwks doesn't have any valid public key.
123 | JwksNoValidKeys,
124 |
125 | // Jwks doesn't have key to match kid or alg from Jwt.
126 | JwksKidAlgMismatch,
127 |
128 | // "n" or "e" field of a Jwk RSA is missing or has a parse error.
129 | JwksRsaParseError,
130 |
131 | // Failed to create a EC_KEY object.
132 | JwksEcCreateKeyFail,
133 |
134 | // "x" or "y" field is an invalid Base64
135 | JwksEcXorYBadBase64,
136 |
137 | // "x" or "y" field of a Jwk EC is missing or has a parse error.
138 | JwksEcParseError,
139 |
140 | // Jwks Oct key is an invalid Base64.
141 | JwksOctBadBase64,
142 |
143 | // "x" field is invalid Base64
144 | JwksOKPXBadBase64,
145 | // "x" field is wrong length
146 | JwksOKPXWrongLength,
147 |
148 | // Failed to fetch public key
149 | JwksFetchFail,
150 |
151 | // "kty" is missing in "keys".
152 | JwksMissingKty,
153 | // "kty" is not string type in "keys".
154 | JwksBadKty,
155 | // "kty" is not supported in "keys".
156 | JwksNotImplementedKty,
157 |
158 | // "alg" is not started with "RS" for a RSA key
159 | JwksRSAKeyBadAlg,
160 | // "n" field is missing for a RSA key
161 | JwksRSAKeyMissingN,
162 | // "n" field is not string for a RSA key
163 | JwksRSAKeyBadN,
164 | // "e" field is missing for a RSA key
165 | JwksRSAKeyMissingE,
166 | // "e" field is not string for a RSA key
167 | JwksRSAKeyBadE,
168 |
169 | // "alg" is not "ES256", "ES384" or "ES512" for an EC key
170 | JwksECKeyBadAlg,
171 | // "crv" field is not string for an EC key
172 | JwksECKeyBadCrv,
173 | // "crv" or "alg" is not supported for an EC key
174 | JwksECKeyAlgOrCrvUnsupported,
175 | // "crv" is not compatible with "alg" for an EC key
176 | JwksECKeyAlgNotCompatibleWithCrv,
177 | // "x" field is missing for an EC key
178 | JwksECKeyMissingX,
179 | // "x" field is not string for an EC key
180 | JwksECKeyBadX,
181 | // "y" field is missing for an EC key
182 | JwksECKeyMissingY,
183 | // "y" field is not string for an EC key
184 | JwksECKeyBadY,
185 |
186 | // "alg" is not "HS256", "HS384" or "HS512" for an HMAC key
187 | JwksHMACKeyBadAlg,
188 | // "k" field is missing for an HMAC key
189 | JwksHMACKeyMissingK,
190 | // "k" field is not string for an HMAC key
191 | JwksHMACKeyBadK,
192 |
193 | // "alg" is not "EdDSA" for an OKP key
194 | JwksOKPKeyBadAlg,
195 | // "crv" field is missing for an OKP key
196 | JwksOKPKeyMissingCrv,
197 | // "crv" field is not string for an OKP key
198 | JwksOKPKeyBadCrv,
199 | // "crv" is not supported for an OKP key
200 | JwksOKPKeyCrvUnsupported,
201 | // "x" field is missing for an OKP key
202 | JwksOKPKeyMissingX,
203 | // "x" field is not string for an OKP key
204 | JwksOKPKeyBadX,
205 |
206 | // X509 BIO_Write function fails
207 | JwksX509BioWriteError,
208 | // X509 parse pubkey fails
209 | JwksX509ParseError,
210 | // X509 get pubkey fails
211 | JwksX509GetPubkeyError,
212 |
213 | // Key type is not supported.
214 | JwksPemNotImplementedKty,
215 | // Unable to parse public key
216 | JwksPemBadBase64,
217 | // Failed to get raw ED25519 key from PEM
218 | JwksPemGetRawEd25519Error,
219 |
220 | // Failed to create BIO
221 | JwksBioAllocError,
222 | };
223 |
224 | /**
225 | * Convert enum status to string.
226 | * @param status is the enum status.
227 | * @return the string status.
228 | */
229 | std::string getStatusString(Status status);
230 |
231 | /**
232 | * Base class to keep the status that represents "OK" or the first failure.
233 | */
234 | class WithStatus {
235 | public:
236 | WithStatus() : status_(Status::Ok) {}
237 |
238 | /**
239 | * Get the current status.
240 | * @return the enum status.
241 | */
242 | Status getStatus() const { return status_; }
243 |
244 | protected:
245 | void updateStatus(Status status) {
246 | // Only keep the first failure
247 | if (status_ == Status::Ok) {
248 | status_ = status;
249 | }
250 | }
251 |
252 | void resetStatus(Status status) { status_ = status; }
253 |
254 | private:
255 | // The internal status.
256 | Status status_;
257 | };
258 |
259 | } // namespace jwt_verify
260 | } // namespace google
261 |
--------------------------------------------------------------------------------
/jwt_verify_lib/struct_utils.h:
--------------------------------------------------------------------------------
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.#pragma once
14 |
15 | #pragma once
16 |
17 | #include "google/protobuf/struct.pb.h"
18 |
19 | namespace google {
20 | namespace jwt_verify {
21 |
22 | class StructUtils {
23 | public:
24 | StructUtils(const ::google::protobuf::Struct& struct_pb);
25 |
26 | enum FindResult {
27 | OK = 0,
28 | MISSING,
29 | WRONG_TYPE,
30 | OUT_OF_RANGE,
31 | };
32 |
33 | FindResult GetString(const std::string& name, std::string* str_value);
34 |
35 | // Return error if the JSON value is not within a positive 64 bit integer
36 | // range. The decimals in the JSON value are dropped.
37 | FindResult GetUInt64(const std::string& name, uint64_t* int_value);
38 |
39 | FindResult GetDouble(const std::string& name, double* double_value);
40 |
41 | FindResult GetBoolean(const std::string& name, bool* bool_value);
42 |
43 | // Get string or list of string, designed to get "aud" field
44 | // "aud" can be either string array or string.
45 | // Try as string array, read it as empty array if doesn't exist.
46 | FindResult GetStringList(const std::string& name,
47 | std::vector* list);
48 |
49 | // Find the value with nested names.
50 | FindResult GetValue(const std::string& nested_names,
51 | const google::protobuf::Value*& found);
52 |
53 | private:
54 | const ::google::protobuf::Struct& struct_pb_;
55 | };
56 |
57 | } // namespace jwt_verify
58 | } // namespace google
59 |
--------------------------------------------------------------------------------
/jwt_verify_lib/verify.h:
--------------------------------------------------------------------------------
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 | #pragma once
16 |
17 | #include "jwt_verify_lib/jwks.h"
18 | #include "jwt_verify_lib/jwt.h"
19 | #include "jwt_verify_lib/status.h"
20 |
21 | namespace google {
22 | namespace jwt_verify {
23 |
24 | /**
25 | * This function verifies JWT signature is valid.
26 | * If verification failed, returns the failure reason.
27 | * Note this method does not verify the "aud" claim.
28 | * @param jwt is Jwt object
29 | * @param jwks is Jwks object
30 | * @return the verification status
31 | */
32 | Status verifyJwtWithoutTimeChecking(const Jwt& jwt, const Jwks& jwks);
33 |
34 | /**
35 | * This function verifies JWT signature is valid and that it has not expired
36 | * checking the "exp" and "nbf" claims against the system's current wall clock.
37 | * If verification failed, returns the failure reason.
38 | * Note this method does not verify the "aud" claim.
39 | * @param jwt is Jwt object
40 | * @param jwks is Jwks object
41 | * @return the verification status
42 | */
43 | Status verifyJwt(const Jwt& jwt, const Jwks& jwks);
44 |
45 | /**
46 | * This function verifies JWT signature is valid and that it has not expired
47 | * checking the "exp" and "nbf" claims against the provided time. If
48 | * verification failed, returns the failure reason. Note this method does not
49 | * verify the "aud" claim.
50 | * @param jwt is Jwt object
51 | * @param jwks is Jwks object
52 | * @param now is the number of seconds since the unix epoch
53 | * @param clock_skew is the clock skew in second
54 | * @return the verification status
55 | */
56 | Status verifyJwt(const Jwt& jwt, const Jwks& jwks, uint64_t now,
57 | uint64_t clock_skew = kClockSkewInSecond);
58 |
59 | /**
60 | * This function verifies JWT signature is valid, that it has not expired
61 | * checking the "exp" and "nbf" claims against the system's current wall clock
62 | * as well as validating that one of the entries in the audience list appears
63 | * as a member in the "aud" claim of the specified JWT. If the supplied
64 | * audience list is empty, no verification of the JWT's "aud" field is
65 | * performed. If verification failed, returns the failure reason.
66 | * @param jwt is Jwt object
67 | * @param jwks is Jwks object
68 | * @param audiences a list of audience by which to check against
69 | * @return the verification status
70 | */
71 | Status verifyJwt(const Jwt& jwt, const Jwks& jwks,
72 | const std::vector& audiences);
73 |
74 | /**
75 | * This function verifies JWT signature is valid, that it has not expired
76 | * checking the "exp" and "nbf" claims against the provided time
77 | * as well as validating that one of the entries in the audience list appears
78 | * as a member in the "aud" claim of the specified JWT. If the supplied
79 | * audience list is empty, no verification of the JWT's "aud" field is
80 | * performed.
81 | * If verification failed,
82 | * returns the failure reason.
83 | * @param jwt is Jwt object
84 | * @param jwks is Jwks object
85 | * @param audiences a list of audience by which to check against.
86 | * @return the verification status
87 | */
88 | Status verifyJwt(const Jwt& jwt, const Jwks& jwks,
89 | const std::vector& audiences, uint64_t now);
90 |
91 | } // namespace jwt_verify
92 | } // namespace google
93 |
--------------------------------------------------------------------------------
/libprotobuf_mutator.BUILD:
--------------------------------------------------------------------------------
1 | licenses(["notice"]) # Apache 2
2 |
3 | cc_library(
4 | name = "libprotobuf_mutator",
5 | srcs = glob(
6 | [
7 | "src/**/*.cc",
8 | "src/**/*.h",
9 | "port/protobuf.h",
10 | ],
11 | exclude = ["**/*_test.cc"],
12 | ),
13 | hdrs = ["src/libfuzzer/libfuzzer_macro.h"],
14 | include_prefix = "libprotobuf_mutator",
15 | includes = ["."],
16 | visibility = ["//visibility:public"],
17 | deps = ["//external:protobuf"],
18 | )
19 |
--------------------------------------------------------------------------------
/repositories.bzl:
--------------------------------------------------------------------------------
1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
2 |
3 | BORINGSSL_COMMIT = "88d7a40bd06a34da6ee0d985545755199d047258" # 2023-05-17, same as Envoy
4 | BORINGSSL_SHA256 = "1e759891e168c5957f2f4d519929e2b4cef9303b7cf2049601081f4fca95bf21"
5 |
6 | def boringssl_repositories(bind = True):
7 | http_archive(
8 | name = "boringssl",
9 | strip_prefix = "boringssl-" + BORINGSSL_COMMIT,
10 | url = "https://github.com/google/boringssl/archive/" + BORINGSSL_COMMIT + ".tar.gz",
11 | sha256 = BORINGSSL_SHA256,
12 | )
13 |
14 | if bind:
15 | native.bind(
16 | name = "ssl",
17 | actual = "@boringssl//:ssl",
18 | )
19 |
20 | GOOGLETEST_COMMIT = "43863938377a9ea1399c0596269e0890b5c5515a"
21 | GOOGLETEST_SHA256 = "7c8ece456ad588c30160429498e108e2df6f42a30888b3ec0abf5d9792d9d3a0"
22 |
23 | def googletest_repositories(bind = True):
24 | http_archive(
25 | name = "googletest_git",
26 | build_file = "//:googletest.BUILD",
27 | strip_prefix = "googletest-" + GOOGLETEST_COMMIT,
28 | url = "https://github.com/google/googletest/archive/" + GOOGLETEST_COMMIT + ".tar.gz",
29 | sha256 = GOOGLETEST_SHA256,
30 | )
31 |
32 | if bind:
33 | native.bind(
34 | name = "googletest",
35 | actual = "@googletest_git//:googletest",
36 | )
37 |
38 | native.bind(
39 | name = "googletest_main",
40 | actual = "@googletest_git//:googletest_main",
41 | )
42 |
43 | native.bind(
44 | name = "googletest_prod",
45 | actual = "@googletest_git//:googletest_prod",
46 | )
47 |
48 | ABSEIL_COMMIT = "cc8dcd307b76a575d2e3e0958a4fe4c7193c2f68" # same as Envoy
49 | ABSEIL_SHA256 = "e35082e88b9da04f4d68094c05ba112502a5063712f3021adfa465306d238c76"
50 |
51 | def abseil_repositories(bind = True):
52 | http_archive(
53 | name = "com_google_absl",
54 | strip_prefix = "abseil-cpp-" + ABSEIL_COMMIT,
55 | url = "https://github.com/abseil/abseil-cpp/archive/" + ABSEIL_COMMIT + ".tar.gz",
56 | sha256 = ABSEIL_SHA256,
57 | )
58 |
59 | if bind:
60 | native.bind(
61 | name = "abseil_strings",
62 | actual = "@com_google_absl//absl/strings:strings",
63 | )
64 | native.bind(
65 | name = "abseil_time",
66 | actual = "@com_google_absl//absl/time:time",
67 | )
68 | native.bind(
69 | name = "abseil_flat_hash_set",
70 | actual = "@com_google_absl//absl/container:flat_hash_set",
71 | )
72 | native.bind(
73 | name = "abseil_flat_hash_map",
74 | actual = "@com_google_absl//absl/container:flat_hash_map",
75 | )
76 | _cctz_repositories(bind)
77 |
78 | CCTZ_COMMIT = "e19879df3a14791b7d483c359c4acd6b2a1cd96b"
79 | CCTZ_SHA256 = "35d2c6cf7ddef1cf7c1bb054bdf2e8d7778242f6d199591a834c14d224b80c39"
80 |
81 | def _cctz_repositories(bind = True):
82 | http_archive(
83 | name = "com_googlesource_code_cctz",
84 | url = "https://github.com/google/cctz/archive/" + CCTZ_COMMIT + ".tar.gz",
85 | sha256 = CCTZ_SHA256,
86 | )
87 |
88 |
89 | RULES_CC_COMMIT = "b7fe9697c0c76ab2fd431a891dbb9a6a32ed7c3e"
90 | RULES_CC_SHA256 = "29daf0159f0cf552fcff60b49d8bcd4f08f08506d2da6e41b07058ec50cfeaec"
91 |
92 | def _rules_cc_repositories():
93 | http_archive(
94 | name = "rules_cc",
95 | sha256 = RULES_CC_SHA256,
96 | strip_prefix = "rules_cc-" + RULES_CC_COMMIT,
97 | urls = ["https://github.com/bazelbuild/rules_cc/archive/" + RULES_CC_COMMIT + ".tar.gz"],
98 | )
99 |
100 | RULES_JAVA_COMMIT = "981f06c3d2bd10225e85209904090eb7b5fb26bd"
101 | RULES_JAVA_SHA256 = "f5a3e477e579231fca27bf202bb0e8fbe4fc6339d63b38ccb87c2760b533d1c3"
102 |
103 | def _rules_java_repositories():
104 | http_archive(
105 | name = "rules_java",
106 | sha256 = RULES_JAVA_SHA256,
107 | strip_prefix = "rules_java-" + RULES_JAVA_COMMIT,
108 | urls = ["https://github.com/bazelbuild/rules_java/archive/" + RULES_JAVA_COMMIT + ".tar.gz"],
109 | )
110 |
111 | RULES_PROTO_COMMIT = "97d8af4dc474595af3900dd85cb3a29ad28cc313" # Oct 31, 2019
112 | RULES_PROTO_SHA256 = "602e7161d9195e50246177e7c55b2f39950a9cf7366f74ed5f22fd45750cd208"
113 |
114 | def _rules_proto_repositories():
115 | http_archive(
116 | name = "rules_proto",
117 | sha256 = RULES_PROTO_SHA256,
118 | strip_prefix = "rules_proto-" + RULES_PROTO_COMMIT,
119 | urls = ["https://github.com/bazelbuild/rules_proto/archive/" + RULES_PROTO_COMMIT + ".tar.gz"],
120 | )
121 |
122 | ZLIB_RELEASE = "1.2.13"
123 | ZLIB_SHA256 = "b3a24de97a8fdbc835b9833169501030b8977031bcb54b3b3ac13740f846ab30"
124 |
125 | def _zlib_repositories():
126 | http_archive(
127 | name = "zlib",
128 | build_file = "@com_google_protobuf//:third_party/zlib.BUILD",
129 | sha256 = ZLIB_SHA256,
130 | strip_prefix = "zlib-" + ZLIB_RELEASE,
131 | urls = ["https://zlib.net/zlib-" + ZLIB_RELEASE + ".tar.gz"],
132 | )
133 |
134 | PROTOBUF_RELEASE = "3.16.0" # Mar 04, 2021
135 | PROTOBUF_SHA256 = "7892a35d979304a404400a101c46ce90e85ec9e2a766a86041bb361f626247f5"
136 |
137 | def protobuf_repositories(bind = True):
138 | _rules_cc_repositories()
139 | _rules_java_repositories()
140 | _rules_proto_repositories()
141 | _zlib_repositories()
142 | http_archive(
143 | name = "com_google_protobuf",
144 | strip_prefix = "protobuf-" + PROTOBUF_RELEASE,
145 | url = "https://github.com/protocolbuffers/protobuf/archive/v" + PROTOBUF_RELEASE + ".tar.gz",
146 | sha256 = PROTOBUF_SHA256,
147 | )
148 |
149 | if bind:
150 | native.bind(
151 | name = "protobuf",
152 | actual = "@com_google_protobuf//:protobuf",
153 | )
154 |
155 | LIBPROTOBUF_MUTATOR_VERSION = "1.0"
156 | LIBPROTOBUF_MUTATOR_SHA256 = "792f250fb546bde8590e72d64311ea00a70c175fd77df6bb5e02328fa15fe28e"
157 |
158 | def libprotobuf_mutator_repositories(bind = True):
159 | http_archive(
160 | name = "com_google_libprotobuf_mutator",
161 | build_file = "//:libprotobuf_mutator.BUILD",
162 | strip_prefix = "libprotobuf-mutator-" + LIBPROTOBUF_MUTATOR_VERSION,
163 | url = "https://github.com/google/libprotobuf-mutator/archive/v" + LIBPROTOBUF_MUTATOR_VERSION + ".tar.gz",
164 | sha256 = LIBPROTOBUF_MUTATOR_SHA256,
165 | )
166 |
167 | if bind:
168 | native.bind(
169 | name = "libprotobuf_mutator",
170 | actual = "@com_google_libprotobuf_mutator//:libprotobuf_mutator",
171 | )
172 |
--------------------------------------------------------------------------------
/script/check-style:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Copyright 2018 Google LLC
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 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
20 |
21 | CLANG_VERSION_REQUIRED="13.0.0"
22 | CLANG_FORMAT=$(which clang-format-${CLANG_VERSION_REQUIRED%%.*})
23 | if [[ ! -x "${CLANG_FORMAT}" ]]; then
24 | # Install required clang version to a folder and cache it.
25 | CLANG_DIRECTORY="${HOME}/clang"
26 | CLANG_FORMAT="${CLANG_DIRECTORY}/bin/clang-format"
27 |
28 | if [ "$(uname)" == "Darwin" ]; then
29 | CLANG_BIN="x86_64-apple-darwin.tar.xz"
30 | elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
31 | CLANG_BIN="x86_64-linux-gnu-ubuntu-16.04.tar.xz"
32 | else
33 | echo "Unsupported environment." ; exit 1 ;
34 | fi
35 |
36 | echo "clang-bin: https://github.com/llvm/llvm-project/releases/download/llvmorg-${CLANG_VERSION_REQUIRED}/clang+llvm-${CLANG_VERSION_REQUIRED}-${CLANG_BIN}"
37 |
38 | CLANG_VERSION="$(${CLANG_FORMAT} -version | cut -d ' ' -f 3)"
39 | if [[ "${CLANG_VERSION}" != "${CLANG_VERSION_REQUIRED}" ]]; then
40 | echo "Installing required clang-format ${CLANG_VERSION_REQUIRED} to ${CLANG_DIRECTORY}"
41 | mkdir -p ${CLANG_DIRECTORY}
42 | curl --show-error --retry 10 \
43 | -L "https://github.com/llvm/llvm-project/releases/download/llvmorg-${CLANG_VERSION_REQUIRED}/clang+llvm-${CLANG_VERSION_REQUIRED}-${CLANG_BIN}" \
44 | | tar Jx -C "${CLANG_DIRECTORY}" --strip=1 \
45 | || { echo "Could not install required clang-format. Skip formating." ; exit 0 ; }
46 | fi
47 | fi
48 |
49 | echo "Checking file format ..."
50 |
51 | pushd ${ROOT} > /dev/null
52 |
53 | SOURCE_FILES=($(git ls-tree -r HEAD --name-only | grep -E '\.(h|c|cc|proto)$'))
54 | "${CLANG_FORMAT}" -style=Google -i "${SOURCE_FILES[@]}" \
55 | || { echo "Could not run clang-format." ; exit 1 ; }
56 |
57 | CHANGED_FILES=($(git diff HEAD --name-only | grep -E '\.(h|c|cc|proto)$'))
58 |
59 | if [[ "${#CHANGED_FILES}" -ne 0 ]]; then
60 | echo "Files not formatted: ${CHANGED_FILES[@]}"
61 | exit 1
62 | fi
63 | echo "All files are properly formatted."
64 |
65 | popd
66 |
--------------------------------------------------------------------------------
/script/ci.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Copyright 2022 Google Inc.
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 | # Check code format before running build and test
20 | ./script/check-style
21 |
22 | # Build and test the entire repo
23 | bazel build //...
24 | bazel test //...
25 |
--------------------------------------------------------------------------------
/simple_lru_cache/simple_lru_cache.h:
--------------------------------------------------------------------------------
1 | /* Copyright 2016 Google Inc. All Rights Reserved.
2 | Licensed under the Apache License, Version 2.0 (the "License");
3 | you may not use this file except in compliance with the License.
4 | You may obtain a copy of the License at
5 | http://www.apache.org/licenses/LICENSE-2.0
6 | Unless required by applicable law or agreed to in writing, software
7 | distributed under the License is distributed on an "AS IS" BASIS,
8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | See the License for the specific language governing permissions and
10 | limitations under the License.
11 | ==============================================================================*/
12 |
13 | // For inclusion in .h files. The real class definition is in
14 | // simple_lru_cache_inl.h.
15 |
16 | #pragma once
17 |
18 | #include
19 |
20 | #include "absl/container/flat_hash_map.h" // for hash<>
21 |
22 | namespace google {
23 | namespace simple_lru_cache {
24 |
25 | namespace internal {
26 | template
27 | struct SimpleLRUHash : public std::hash {};
28 | } // namespace internal
29 |
30 | template ,
32 | typename EQ = std::equal_to>
33 | class SimpleLRUCache;
34 |
35 | // Deleter is a functor that defines how to delete a Value*. That is, it
36 | // contains a public method:
37 | // operator() (Value* value)
38 | // See example in the associated unittest.
39 | template ,
41 | typename EQ = std::equal_to>
42 | class SimpleLRUCacheWithDeleter;
43 |
44 | } // namespace simple_lru_cache
45 | } // namespace google
--------------------------------------------------------------------------------
/src/check_audience.cc:
--------------------------------------------------------------------------------
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/check_audience.h"
16 |
17 | #include "absl/strings/match.h"
18 |
19 | namespace google {
20 | namespace jwt_verify {
21 | namespace {
22 |
23 | // HTTP Protocol scheme prefix in JWT aud claim.
24 | constexpr absl::string_view HTTPSchemePrefix("http://");
25 |
26 | // HTTPS Protocol scheme prefix in JWT aud claim.
27 | constexpr absl::string_view HTTPSSchemePrefix("https://");
28 |
29 | std::string sanitizeAudience(const std::string& aud) {
30 | if (aud.empty()) {
31 | return aud;
32 | }
33 |
34 | size_t beg_pos = 0;
35 | bool sanitized = false;
36 | // Point beg to first character after protocol scheme prefix in audience.
37 | if (absl::StartsWith(aud, HTTPSchemePrefix)) {
38 | beg_pos = HTTPSchemePrefix.size();
39 | sanitized = true;
40 | } else if (absl::StartsWith(aud, HTTPSSchemePrefix)) {
41 | beg_pos = HTTPSSchemePrefix.size();
42 | sanitized = true;
43 | }
44 |
45 | // Point end to trailing slash in aud.
46 | size_t end_pos = aud.length();
47 | if (aud[end_pos - 1] == '/') {
48 | --end_pos;
49 | sanitized = true;
50 | }
51 | if (sanitized) {
52 | return aud.substr(beg_pos, end_pos - beg_pos);
53 | }
54 | return aud;
55 | }
56 |
57 | } // namespace
58 |
59 | CheckAudience::CheckAudience(const std::vector& config_audiences) {
60 | for (const auto& aud : config_audiences) {
61 | config_audiences_.insert(sanitizeAudience(aud));
62 | }
63 | }
64 |
65 | bool CheckAudience::areAudiencesAllowed(
66 | const std::vector& jwt_audiences) const {
67 | if (config_audiences_.empty()) {
68 | return true;
69 | }
70 | for (const auto& aud : jwt_audiences) {
71 | if (config_audiences_.find(sanitizeAudience(aud)) !=
72 | config_audiences_.end()) {
73 | return true;
74 | }
75 | }
76 | return false;
77 | }
78 |
79 | } // namespace jwt_verify
80 | } // namespace google
81 |
--------------------------------------------------------------------------------
/src/jwks.cc:
--------------------------------------------------------------------------------
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
18 |
19 | #include
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 | inline const uint8_t* castToUChar(const std::string& str) {
46 | return reinterpret_cast(str.c_str());
47 | }
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 pkey = e.createEcKeyFromJwkEC(...);
56 | */
57 | class KeyGetter : public WithStatus {
58 | public:
59 | bssl::UniquePtr createEvpPkeyFromPem(const std::string& pkey_pem) {
60 | bssl::UniquePtr buf(BIO_new_mem_buf(pkey_pem.data(), pkey_pem.size()));
61 | if (buf == nullptr) {
62 | updateStatus(Status::JwksBioAllocError);
63 | return nullptr;
64 | }
65 | bssl::UniquePtr key(
66 | PEM_read_bio_PUBKEY(buf.get(), nullptr, nullptr, nullptr));
67 | if (key == nullptr) {
68 | updateStatus(Status::JwksPemBadBase64);
69 | return nullptr;
70 | }
71 | return key;
72 | }
73 |
74 | bssl::UniquePtr createEcKeyFromJwkEC(int nid, const std::string& x,
75 | const std::string& y) {
76 | bssl::UniquePtr ec_key(EC_KEY_new_by_curve_name(nid));
77 | if (!ec_key) {
78 | updateStatus(Status::JwksEcCreateKeyFail);
79 | return nullptr;
80 | }
81 | bssl::UniquePtr bn_x = createBigNumFromBase64UrlString(x);
82 | bssl::UniquePtr bn_y = createBigNumFromBase64UrlString(y);
83 | if (!bn_x || !bn_y) {
84 | // EC public key field x or y Base64 decode fail
85 | updateStatus(Status::JwksEcXorYBadBase64);
86 | return nullptr;
87 | }
88 |
89 | if (EC_KEY_set_public_key_affine_coordinates(ec_key.get(), bn_x.get(),
90 | bn_y.get()) == 0) {
91 | updateStatus(Status::JwksEcParseError);
92 | return nullptr;
93 | }
94 | return ec_key;
95 | }
96 |
97 | bssl::UniquePtr createRsaFromJwk(const std::string& n,
98 | const std::string& e) {
99 | bssl::UniquePtr n_bn = createBigNumFromBase64UrlString(n);
100 | bssl::UniquePtr e_bn = createBigNumFromBase64UrlString(e);
101 | if (n_bn == nullptr || e_bn == nullptr) {
102 | // RSA public key field is missing or has parse error.
103 | updateStatus(Status::JwksRsaParseError);
104 | return nullptr;
105 | }
106 | if (BN_cmp_word(e_bn.get(), 3) != 0 &&
107 | BN_cmp_word(e_bn.get(), 65537) != 0) {
108 | // non-standard key; reject it early.
109 | updateStatus(Status::JwksRsaParseError);
110 | return nullptr;
111 | }
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 | bssl::UniquePtr rsa(RSA_new());
116 | if (rsa == nullptr ||
117 | !RSA_set0_key(rsa.get(), n_bn.get(), e_bn.get(), /*d=*/nullptr)) {
118 | // Allocation or programmer error.
119 | updateStatus(Status::JwksRsaParseError);
120 | return nullptr;
121 | }
122 | // `RSA_set0_key` takes ownership, but only on success.
123 | n_bn.release();
124 | e_bn.release();
125 | if (!RSA_check_key(rsa.get())) {
126 | // Not a valid RSA public key.
127 | updateStatus(Status::JwksRsaParseError);
128 | return nullptr;
129 | }
130 | return rsa;
131 | }
132 |
133 | std::string createRawKeyFromJwkOKP(int nid, size_t keylen,
134 | const std::string& x) {
135 | std::string x_decoded;
136 | if (!absl::WebSafeBase64Unescape(x, &x_decoded)) {
137 | updateStatus(Status::JwksOKPXBadBase64);
138 | } else if (x_decoded.length() != keylen) {
139 | updateStatus(Status::JwksOKPXWrongLength);
140 | }
141 | // For OKP the "x" value is the public key and can just be used as-is
142 | return x_decoded;
143 | }
144 |
145 | private:
146 | bssl::UniquePtr createBigNumFromBase64UrlString(
147 | const std::string& s) {
148 | std::string s_decoded;
149 | if (!absl::WebSafeBase64Unescape(s, &s_decoded)) {
150 | return nullptr;
151 | }
152 | return bssl::UniquePtr(
153 | BN_bin2bn(castToUChar(s_decoded), s_decoded.length(), NULL));
154 | };
155 | };
156 |
157 | Status extractJwkFromJwkRSA(const ::google::protobuf::Struct& jwk_pb,
158 | Jwks::Pubkey* jwk) {
159 | if (!jwk->alg_.empty() &&
160 | (jwk->alg_.size() < 2 || (jwk->alg_.compare(0, 2, "RS") != 0 &&
161 | jwk->alg_.compare(0, 2, "PS") != 0))) {
162 | return Status::JwksRSAKeyBadAlg;
163 | }
164 |
165 | StructUtils jwk_getter(jwk_pb);
166 | std::string n_str;
167 | auto code = jwk_getter.GetString("n", &n_str);
168 | if (code == StructUtils::MISSING) {
169 | return Status::JwksRSAKeyMissingN;
170 | }
171 | if (code == StructUtils::WRONG_TYPE) {
172 | return Status::JwksRSAKeyBadN;
173 | }
174 |
175 | std::string e_str;
176 | code = jwk_getter.GetString("e", &e_str);
177 | if (code == StructUtils::MISSING) {
178 | return Status::JwksRSAKeyMissingE;
179 | }
180 | if (code == StructUtils::WRONG_TYPE) {
181 | return Status::JwksRSAKeyBadE;
182 | }
183 |
184 | KeyGetter e;
185 | jwk->rsa_ = e.createRsaFromJwk(n_str, e_str);
186 | return e.getStatus();
187 | }
188 |
189 | Status extractJwkFromJwkEC(const ::google::protobuf::Struct& jwk_pb,
190 | Jwks::Pubkey* jwk) {
191 | if (!jwk->alg_.empty() &&
192 | (jwk->alg_.size() < 2 || jwk->alg_.compare(0, 2, "ES") != 0)) {
193 | return Status::JwksECKeyBadAlg;
194 | }
195 |
196 | StructUtils jwk_getter(jwk_pb);
197 | std::string crv_str;
198 | auto code = jwk_getter.GetString("crv", &crv_str);
199 | if (code == StructUtils::MISSING) {
200 | crv_str = "";
201 | }
202 | if (code == StructUtils::WRONG_TYPE) {
203 | return Status::JwksECKeyBadCrv;
204 | }
205 | jwk->crv_ = crv_str;
206 |
207 | // If both alg and crv specified, make sure they match
208 | if (!jwk->alg_.empty() && !jwk->crv_.empty()) {
209 | if (!((jwk->alg_ == "ES256" && jwk->crv_ == "P-256") ||
210 | (jwk->alg_ == "ES384" && jwk->crv_ == "P-384") ||
211 | (jwk->alg_ == "ES512" && jwk->crv_ == "P-521"))) {
212 | return Status::JwksECKeyAlgNotCompatibleWithCrv;
213 | }
214 | }
215 |
216 | // If neither alg or crv is set, assume P-256
217 | if (jwk->alg_.empty() && jwk->crv_.empty()) {
218 | jwk->crv_ = "P-256";
219 | }
220 |
221 | int nid;
222 | if (jwk->alg_ == "ES256" || jwk->crv_ == "P-256") {
223 | nid = NID_X9_62_prime256v1;
224 | jwk->crv_ = "P-256";
225 | } else if (jwk->alg_ == "ES384" || jwk->crv_ == "P-384") {
226 | nid = NID_secp384r1;
227 | jwk->crv_ = "P-384";
228 | } else if (jwk->alg_ == "ES512" || jwk->crv_ == "P-521") {
229 | nid = NID_secp521r1;
230 | jwk->crv_ = "P-521";
231 | } else {
232 | return Status::JwksECKeyAlgOrCrvUnsupported;
233 | }
234 |
235 | std::string x_str;
236 | code = jwk_getter.GetString("x", &x_str);
237 | if (code == StructUtils::MISSING) {
238 | return Status::JwksECKeyMissingX;
239 | }
240 | if (code == StructUtils::WRONG_TYPE) {
241 | return Status::JwksECKeyBadX;
242 | }
243 |
244 | std::string y_str;
245 | code = jwk_getter.GetString("y", &y_str);
246 | if (code == StructUtils::MISSING) {
247 | return Status::JwksECKeyMissingY;
248 | }
249 | if (code == StructUtils::WRONG_TYPE) {
250 | return Status::JwksECKeyBadY;
251 | }
252 |
253 | KeyGetter e;
254 | jwk->ec_key_ = e.createEcKeyFromJwkEC(nid, x_str, y_str);
255 | return e.getStatus();
256 | }
257 |
258 | Status extractJwkFromJwkOct(const ::google::protobuf::Struct& jwk_pb,
259 | Jwks::Pubkey* jwk) {
260 | if (!jwk->alg_.empty() && jwk->alg_ != "HS256" && jwk->alg_ != "HS384" &&
261 | jwk->alg_ != "HS512") {
262 | return Status::JwksHMACKeyBadAlg;
263 | }
264 |
265 | StructUtils jwk_getter(jwk_pb);
266 | std::string k_str;
267 | auto code = jwk_getter.GetString("k", &k_str);
268 | if (code == StructUtils::MISSING) {
269 | return Status::JwksHMACKeyMissingK;
270 | }
271 | if (code == StructUtils::WRONG_TYPE) {
272 | return Status::JwksHMACKeyBadK;
273 | }
274 |
275 | std::string key;
276 | if (!absl::WebSafeBase64Unescape(k_str, &key) || key.empty()) {
277 | return Status::JwksOctBadBase64;
278 | }
279 |
280 | jwk->hmac_key_ = key;
281 | return Status::Ok;
282 | }
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 | Jwks::Pubkey* jwk) {
287 | // alg is not required, but if present it must be EdDSA
288 | if (!jwk->alg_.empty() && jwk->alg_ != "EdDSA") {
289 | return Status::JwksOKPKeyBadAlg;
290 | }
291 |
292 | // crv is required per https://tools.ietf.org/html/rfc8037#section-2
293 | StructUtils jwk_getter(jwk_pb);
294 | std::string crv_str;
295 | auto code = jwk_getter.GetString("crv", &crv_str);
296 | if (code == StructUtils::MISSING) {
297 | return Status::JwksOKPKeyMissingCrv;
298 | }
299 | if (code == StructUtils::WRONG_TYPE) {
300 | return Status::JwksOKPKeyBadCrv;
301 | }
302 | 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 | int nid;
312 | size_t keylen;
313 | if (jwk->crv_ == "Ed25519") {
314 | nid = EVP_PKEY_ED25519;
315 | keylen = ED25519_PUBLIC_KEY_LEN;
316 | } else {
317 | return Status::JwksOKPKeyCrvUnsupported;
318 | }
319 |
320 | // x is required per https://tools.ietf.org/html/rfc8037#section-2
321 | std::string x_str;
322 | code = jwk_getter.GetString("x", &x_str);
323 | if (code == StructUtils::MISSING) {
324 | return Status::JwksOKPKeyMissingX;
325 | }
326 | if (code == StructUtils::WRONG_TYPE) {
327 | return Status::JwksOKPKeyBadX;
328 | }
329 |
330 | KeyGetter e;
331 | jwk->okp_key_raw_ = e.createRawKeyFromJwkOKP(nid, keylen, x_str);
332 | return e.getStatus();
333 | }
334 |
335 | Status extractJwk(const ::google::protobuf::Struct& jwk_pb, Jwks::Pubkey* jwk) {
336 | StructUtils jwk_getter(jwk_pb);
337 | // Check "kty" parameter, it should exist.
338 | // https://tools.ietf.org/html/rfc7517#section-4.1
339 | auto code = jwk_getter.GetString("kty", &jwk->kty_);
340 | if (code == StructUtils::MISSING) {
341 | return Status::JwksMissingKty;
342 | }
343 | if (code == StructUtils::WRONG_TYPE) {
344 | return Status::JwksBadKty;
345 | }
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 | jwk_getter.GetString("kid", &jwk->kid_);
350 | 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 | if (jwk->kty_ == "EC") {
355 | return extractJwkFromJwkEC(jwk_pb, jwk);
356 | } else if (jwk->kty_ == "RSA") {
357 | return extractJwkFromJwkRSA(jwk_pb, jwk);
358 | } else if (jwk->kty_ == "oct") {
359 | return extractJwkFromJwkOct(jwk_pb, jwk);
360 | } else if (jwk->kty_ == "OKP") {
361 | return extractJwkFromJwkOKP(jwk_pb, jwk);
362 | }
363 | return Status::JwksNotImplementedKty;
364 | }
365 |
366 | Status extractX509(const std::string& key, Jwks::Pubkey* jwk) {
367 | jwk->bio_.reset(BIO_new(BIO_s_mem()));
368 | if (BIO_write(jwk->bio_.get(), key.c_str(), key.length()) <= 0) {
369 | return Status::JwksX509BioWriteError;
370 | }
371 | jwk->x509_.reset(
372 | PEM_read_bio_X509(jwk->bio_.get(), nullptr, nullptr, nullptr));
373 | if (jwk->x509_ == nullptr) {
374 | return Status::JwksX509ParseError;
375 | }
376 | bssl::UniquePtr tmp_pkey(X509_get_pubkey(jwk->x509_.get()));
377 | if (tmp_pkey == nullptr) {
378 | return Status::JwksX509GetPubkeyError;
379 | }
380 | jwk->rsa_.reset(EVP_PKEY_get1_RSA(tmp_pkey.get()));
381 | if (jwk->rsa_ == nullptr) {
382 | return Status::JwksX509GetPubkeyError;
383 | }
384 | return Status::Ok;
385 | }
386 |
387 | bool shouldCheckX509(const ::google::protobuf::Struct& jwks_pb) {
388 | if (jwks_pb.fields().empty()) {
389 | return false;
390 | }
391 |
392 | for (const auto& kid : jwks_pb.fields()) {
393 | if (kid.first.empty() ||
394 | kid.second.kind_case() != google::protobuf::Value::kStringValue) {
395 | return false;
396 | }
397 | const std::string& cert = kid.second.string_value();
398 | if (!absl::StartsWith(cert, kX509CertPrefix) ||
399 | !absl::EndsWith(cert, kX509CertSuffix)) {
400 | return false;
401 | }
402 | }
403 | return true;
404 | }
405 |
406 | Status createFromX509(const ::google::protobuf::Struct& jwks_pb,
407 | std::vector& keys) {
408 | for (const auto& kid : jwks_pb.fields()) {
409 | Jwks::PubkeyPtr key_ptr(new Jwks::Pubkey());
410 | Status status = extractX509(kid.second.string_value(), key_ptr.get());
411 | if (status != Status::Ok) {
412 | return status;
413 | }
414 |
415 | key_ptr->kid_ = kid.first;
416 | key_ptr->kty_ = "RSA";
417 | keys.push_back(std::move(key_ptr));
418 | }
419 | return Status::Ok;
420 | }
421 |
422 | } // namespace
423 |
424 | Status Jwks::addKeyFromPem(const std::string& pkey, const std::string& kid,
425 | const std::string& alg) {
426 | JwksPtr tmp = Jwks::createFromPem(pkey, kid, alg);
427 | if (tmp->getStatus() != Status::Ok) {
428 | return tmp->getStatus();
429 | }
430 | keys_.insert(keys_.end(), std::make_move_iterator(tmp->keys_.begin()),
431 | std::make_move_iterator(tmp->keys_.end()));
432 | return Status::Ok;
433 | }
434 |
435 | JwksPtr Jwks::createFrom(const std::string& pkey, Type type) {
436 | JwksPtr keys(new Jwks());
437 | switch (type) {
438 | case Type::JWKS:
439 | keys->createFromJwksCore(pkey);
440 | break;
441 | case Type::PEM:
442 | keys->createFromPemCore(pkey);
443 | break;
444 | }
445 | return keys;
446 | }
447 |
448 | JwksPtr Jwks::createFromPem(const std::string& pkey, const std::string& kid,
449 | const std::string& alg) {
450 | std::unique_ptr ret = Jwks::createFrom(pkey, Jwks::PEM);
451 | if (ret->getStatus() != Status::Ok) {
452 | return ret;
453 | }
454 | if (ret->keys_.size() != 1) {
455 | ret->updateStatus(Status::JwksPemBadBase64);
456 | return ret;
457 | }
458 | Pubkey* jwk = ret->keys_.at(0).get();
459 | jwk->kid_ = kid;
460 | jwk->alg_ = alg;
461 |
462 | // If alg is a known EC algorithm, set the correct crv as well.
463 | if (jwk->alg_ == "ES256") {
464 | jwk->crv_ = "P-256";
465 | }
466 | if (jwk->alg_ == "ES384") {
467 | jwk->crv_ = "P-384";
468 | }
469 | if (jwk->alg_ == "ES512") {
470 | jwk->crv_ = "P-521";
471 | }
472 | return ret;
473 | }
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 | void Jwks::createFromPemCore(const std::string& pkey_pem) {
478 | keys_.clear();
479 | PubkeyPtr key_ptr(new Pubkey());
480 | KeyGetter e;
481 | bssl::UniquePtr evp_pkey(e.createEvpPkeyFromPem(pkey_pem));
482 | updateStatus(e.getStatus());
483 |
484 | if (evp_pkey == nullptr) {
485 | assert(e.getStatus() != Status::Ok);
486 | return;
487 | }
488 | assert(e.getStatus() == Status::Ok);
489 |
490 | switch (EVP_PKEY_id(evp_pkey.get())) {
491 | case EVP_PKEY_RSA:
492 | key_ptr->rsa_.reset(EVP_PKEY_get1_RSA(evp_pkey.get()));
493 | key_ptr->kty_ = "RSA";
494 | break;
495 | case EVP_PKEY_EC:
496 | key_ptr->ec_key_.reset(EVP_PKEY_get1_EC_KEY(evp_pkey.get()));
497 | key_ptr->kty_ = "EC";
498 | break;
499 | #ifndef BORINGSSL_FIPS
500 | case EVP_PKEY_ED25519: {
501 | uint8_t raw_key[ED25519_PUBLIC_KEY_LEN];
502 | size_t out_len = ED25519_PUBLIC_KEY_LEN;
503 | if (EVP_PKEY_get_raw_public_key(evp_pkey.get(), raw_key, &out_len) != 1 ||
504 | out_len != ED25519_PUBLIC_KEY_LEN) {
505 | updateStatus(Status::JwksPemGetRawEd25519Error);
506 | return;
507 | }
508 | key_ptr->okp_key_raw_ =
509 | std::string(reinterpret_cast(raw_key), out_len);
510 | key_ptr->kty_ = "OKP";
511 | key_ptr->crv_ = "Ed25519";
512 | break;
513 | }
514 | #endif
515 | default:
516 | updateStatus(Status::JwksPemNotImplementedKty);
517 | return;
518 | }
519 |
520 | keys_.push_back(std::move(key_ptr));
521 | }
522 |
523 | void Jwks::createFromJwksCore(const std::string& jwks_json) {
524 | keys_.clear();
525 |
526 | ::google::protobuf::util::JsonParseOptions options;
527 | ::google::protobuf::Struct jwks_pb;
528 | const auto status = ::google::protobuf::util::JsonStringToMessage(
529 | jwks_json, &jwks_pb, options);
530 | if (!status.ok()) {
531 | updateStatus(Status::JwksParseError);
532 | return;
533 | }
534 |
535 | const auto& fields = jwks_pb.fields();
536 | const auto keys_it = fields.find("keys");
537 | if (keys_it == fields.end()) {
538 | // X509 doesn't have "keys" field.
539 | if (shouldCheckX509(jwks_pb)) {
540 | updateStatus(createFromX509(jwks_pb, keys_));
541 | return;
542 | }
543 | updateStatus(Status::JwksNoKeys);
544 | return;
545 | }
546 | if (keys_it->second.kind_case() != google::protobuf::Value::kListValue) {
547 | updateStatus(Status::JwksBadKeys);
548 | return;
549 | }
550 |
551 | for (const auto& key_value : keys_it->second.list_value().values()) {
552 | if (key_value.kind_case() != ::google::protobuf::Value::kStructValue) {
553 | continue;
554 | }
555 | PubkeyPtr key_ptr(new Pubkey());
556 | Status status = extractJwk(key_value.struct_value(), key_ptr.get());
557 | if (status == Status::Ok) {
558 | keys_.push_back(std::move(key_ptr));
559 | resetStatus(status);
560 | } else {
561 | updateStatus(status);
562 | }
563 | }
564 |
565 | if (keys_.empty()) {
566 | updateStatus(Status::JwksNoValidKeys);
567 | } else {
568 | resetStatus(Status::Ok);
569 | }
570 | }
571 |
572 | } // namespace jwt_verify
573 | } // namespace google
574 |
--------------------------------------------------------------------------------
/src/jwt.cc:
--------------------------------------------------------------------------------
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/jwt.h"
16 |
17 | #include
18 |
19 | #include "absl/container/flat_hash_set.h"
20 | #include "absl/strings/escaping.h"
21 | #include "absl/strings/str_split.h"
22 | #include "absl/time/clock.h"
23 | #include "google/protobuf/util/json_util.h"
24 | #include "jwt_verify_lib/struct_utils.h"
25 |
26 | namespace google {
27 | namespace jwt_verify {
28 |
29 | namespace {
30 |
31 | bool isImplemented(absl::string_view alg) {
32 | static const absl::flat_hash_set implemented_algs = {
33 | {"ES256"}, {"ES384"}, {"ES512"}, {"HS256"}, {"HS384"},
34 | {"HS512"}, {"RS256"}, {"RS384"}, {"RS512"}, {"PS256"},
35 | {"PS384"}, {"PS512"}, {"EdDSA"},
36 | };
37 |
38 | return implemented_algs.find(alg) != implemented_algs.end();
39 | }
40 |
41 | } // namespace
42 |
43 | Jwt::Jwt(const Jwt& instance) { *this = instance; }
44 |
45 | Jwt& Jwt::operator=(const Jwt& rhs) {
46 | parseFromString(rhs.jwt_);
47 | return *this;
48 | }
49 |
50 | Status Jwt::parseFromString(const std::string& jwt) {
51 | // jwt must have exactly 2 dots with 3 sections.
52 | jwt_ = jwt;
53 | std::vector jwt_split =
54 | absl::StrSplit(jwt, '.', absl::SkipEmpty());
55 | if (jwt_split.size() != 3) {
56 | return Status::JwtBadFormat;
57 | }
58 |
59 | // Parse header json
60 | header_str_base64url_ = std::string(jwt_split[0]);
61 | if (!absl::WebSafeBase64Unescape(header_str_base64url_, &header_str_)) {
62 | return Status::JwtHeaderParseErrorBadBase64;
63 | }
64 |
65 | ::google::protobuf::util::JsonParseOptions options;
66 | const auto header_status = ::google::protobuf::util::JsonStringToMessage(
67 | header_str_, &header_pb_, options);
68 | if (!header_status.ok()) {
69 | return Status::JwtHeaderParseErrorBadJson;
70 | }
71 |
72 | StructUtils header_getter(header_pb_);
73 | // Header should contain "alg" and should be a string.
74 | if (header_getter.GetString("alg", &alg_) != StructUtils::OK) {
75 | return Status::JwtHeaderBadAlg;
76 | }
77 |
78 | if (!isImplemented(alg_)) {
79 | return Status::JwtHeaderNotImplementedAlg;
80 | }
81 |
82 | // Header may contain "kid", should be a string if exists.
83 | if (header_getter.GetString("kid", &kid_) == StructUtils::WRONG_TYPE) {
84 | return Status::JwtHeaderBadKid;
85 | }
86 |
87 | // Parse payload json
88 | payload_str_base64url_ = std::string(jwt_split[1]);
89 | if (!absl::WebSafeBase64Unescape(payload_str_base64url_, &payload_str_)) {
90 | return Status::JwtPayloadParseErrorBadBase64;
91 | }
92 |
93 | const auto payload_status = ::google::protobuf::util::JsonStringToMessage(
94 | payload_str_, &payload_pb_, options);
95 | if (!payload_status.ok()) {
96 | return Status::JwtPayloadParseErrorBadJson;
97 | }
98 |
99 | StructUtils payload_getter(payload_pb_);
100 | if (payload_getter.GetString("iss", &iss_) == StructUtils::WRONG_TYPE) {
101 | return Status::JwtPayloadParseErrorIssNotString;
102 | }
103 | if (payload_getter.GetString("sub", &sub_) == StructUtils::WRONG_TYPE) {
104 | return Status::JwtPayloadParseErrorSubNotString;
105 | }
106 |
107 | auto result = payload_getter.GetUInt64("iat", &iat_);
108 | if (result == StructUtils::WRONG_TYPE) {
109 | return Status::JwtPayloadParseErrorIatNotInteger;
110 | } else if (result == StructUtils::OUT_OF_RANGE) {
111 | return Status::JwtPayloadParseErrorIatOutOfRange;
112 | }
113 |
114 | result = payload_getter.GetUInt64("nbf", &nbf_);
115 | if (result == StructUtils::WRONG_TYPE) {
116 | return Status::JwtPayloadParseErrorNbfNotInteger;
117 | } else if (result == StructUtils::OUT_OF_RANGE) {
118 | return Status::JwtPayloadParseErrorNbfOutOfRange;
119 | }
120 |
121 | result = payload_getter.GetUInt64("exp", &exp_);
122 | if (result == StructUtils::WRONG_TYPE) {
123 | return Status::JwtPayloadParseErrorExpNotInteger;
124 | } else if (result == StructUtils::OUT_OF_RANGE) {
125 | return Status::JwtPayloadParseErrorExpOutOfRange;
126 | }
127 |
128 | if (payload_getter.GetString("jti", &jti_) == StructUtils::WRONG_TYPE) {
129 | return Status::JwtPayloadParseErrorJtiNotString;
130 | }
131 |
132 | // "aud" can be either string array or string.
133 | // GetStringList function will try to read as string, if fails,
134 | // try to read as string array.
135 | if (payload_getter.GetStringList("aud", &audiences_) ==
136 | StructUtils::WRONG_TYPE) {
137 | return Status::JwtPayloadParseErrorAudNotString;
138 | }
139 |
140 | // Set up signature
141 | if (!absl::WebSafeBase64Unescape(jwt_split[2], &signature_)) {
142 | // Signature is a bad Base64url input.
143 | return Status::JwtSignatureParseErrorBadBase64;
144 | }
145 | return Status::Ok;
146 | }
147 |
148 | Status Jwt::verifyTimeConstraint(uint64_t now, uint64_t clock_skew) const {
149 | // Check Jwt is active (nbf).
150 | if (now + clock_skew < nbf_) {
151 | return Status::JwtNotYetValid;
152 | }
153 | // Check JWT has not expired (exp).
154 | if (exp_ && now > exp_ + clock_skew) {
155 | return Status::JwtExpired;
156 | }
157 | return Status::Ok;
158 | }
159 |
160 | } // namespace jwt_verify
161 | } // namespace google
162 |
--------------------------------------------------------------------------------
/src/status.cc:
--------------------------------------------------------------------------------
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/status.h"
16 |
17 | #include
18 | #include