Imported Upstream version 1.36.0
[platform/upstream/grpc.git] / src / core / lib / security / credentials / jwt / json_token.cc
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18
19 #include <grpc/support/port_platform.h>
20
21 #include "src/core/lib/iomgr/error.h"
22 #include "src/core/lib/security/credentials/jwt/json_token.h"
23
24 #include <string.h>
25
26 #include <grpc/grpc_security.h>
27 #include <grpc/support/alloc.h>
28 #include <grpc/support/log.h>
29 #include <grpc/support/string_util.h>
30 #include <grpc/support/time.h>
31
32 #include "src/core/lib/gpr/string.h"
33 #include "src/core/lib/security/util/json_util.h"
34 #include "src/core/lib/slice/b64.h"
35
36 extern "C" {
37 #include <openssl/bio.h>
38 #include <openssl/evp.h>
39 #include <openssl/pem.h>
40 }
41
42 using grpc_core::Json;
43
44 /* --- Constants. --- */
45
46 /* 1 hour max. */
47 gpr_timespec grpc_max_auth_token_lifetime() {
48   gpr_timespec out;
49   out.tv_sec = 3600;
50   out.tv_nsec = 0;
51   out.clock_type = GPR_TIMESPAN;
52   return out;
53 }
54
55 #define GRPC_JWT_RSA_SHA256_ALGORITHM "RS256"
56 #define GRPC_JWT_TYPE "JWT"
57
58 /* --- Override for testing. --- */
59
60 static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override =
61     nullptr;
62
63 /* --- grpc_auth_json_key. --- */
64
65 int grpc_auth_json_key_is_valid(const grpc_auth_json_key* json_key) {
66   return (json_key != nullptr) &&
67          strcmp(json_key->type, GRPC_AUTH_JSON_TYPE_INVALID) != 0;
68 }
69
70 grpc_auth_json_key grpc_auth_json_key_create_from_json(const Json& json) {
71   grpc_auth_json_key result;
72   BIO* bio = nullptr;
73   const char* prop_value;
74   int success = 0;
75   grpc_error* error = GRPC_ERROR_NONE;
76
77   memset(&result, 0, sizeof(grpc_auth_json_key));
78   result.type = GRPC_AUTH_JSON_TYPE_INVALID;
79   if (json.type() == Json::Type::JSON_NULL) {
80     gpr_log(GPR_ERROR, "Invalid json.");
81     goto end;
82   }
83
84   prop_value = grpc_json_get_string_property(json, "type", &error);
85   GRPC_LOG_IF_ERROR("JSON key parsing", error);
86   if (prop_value == nullptr ||
87       strcmp(prop_value, GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT) != 0) {
88     goto end;
89   }
90   result.type = GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT;
91
92   if (!grpc_copy_json_string_property(json, "private_key_id",
93                                       &result.private_key_id) ||
94       !grpc_copy_json_string_property(json, "client_id", &result.client_id) ||
95       !grpc_copy_json_string_property(json, "client_email",
96                                       &result.client_email)) {
97     goto end;
98   }
99
100   prop_value = grpc_json_get_string_property(json, "private_key", &error);
101   GRPC_LOG_IF_ERROR("JSON key parsing", error);
102   if (prop_value == nullptr) {
103     goto end;
104   }
105   bio = BIO_new(BIO_s_mem());
106   success = BIO_puts(bio, prop_value);
107   if ((success < 0) || (static_cast<size_t>(success) != strlen(prop_value))) {
108     gpr_log(GPR_ERROR, "Could not write into openssl BIO.");
109     goto end;
110   }
111   result.private_key =
112       PEM_read_bio_RSAPrivateKey(bio, nullptr, nullptr, const_cast<char*>(""));
113   if (result.private_key == nullptr) {
114     gpr_log(GPR_ERROR, "Could not deserialize private key.");
115     goto end;
116   }
117   success = 1;
118
119 end:
120   if (bio != nullptr) BIO_free(bio);
121   if (!success) grpc_auth_json_key_destruct(&result);
122   return result;
123 }
124
125 grpc_auth_json_key grpc_auth_json_key_create_from_string(
126     const char* json_string) {
127   grpc_error* error = GRPC_ERROR_NONE;
128   Json json = Json::Parse(json_string, &error);
129   GRPC_LOG_IF_ERROR("JSON key parsing", error);
130   return grpc_auth_json_key_create_from_json(json);
131 }
132
133 void grpc_auth_json_key_destruct(grpc_auth_json_key* json_key) {
134   if (json_key == nullptr) return;
135   json_key->type = GRPC_AUTH_JSON_TYPE_INVALID;
136   if (json_key->client_id != nullptr) {
137     gpr_free(json_key->client_id);
138     json_key->client_id = nullptr;
139   }
140   if (json_key->private_key_id != nullptr) {
141     gpr_free(json_key->private_key_id);
142     json_key->private_key_id = nullptr;
143   }
144   if (json_key->client_email != nullptr) {
145     gpr_free(json_key->client_email);
146     json_key->client_email = nullptr;
147   }
148   if (json_key->private_key != nullptr) {
149     RSA_free(json_key->private_key);
150     json_key->private_key = nullptr;
151   }
152 }
153
154 /* --- jwt encoding and signature. --- */
155
156 static char* encoded_jwt_header(const char* key_id, const char* algorithm) {
157   Json json = Json::Object{
158       {"alg", algorithm},
159       {"typ", GRPC_JWT_TYPE},
160       {"kid", key_id},
161   };
162   std::string json_str = json.Dump();
163   return grpc_base64_encode(json_str.c_str(), json_str.size(), 1, 0);
164 }
165
166 static char* encoded_jwt_claim(const grpc_auth_json_key* json_key,
167                                const char* audience,
168                                gpr_timespec token_lifetime, const char* scope) {
169   gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
170   gpr_timespec expiration = gpr_time_add(now, token_lifetime);
171   if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime()) > 0) {
172     gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value.");
173     expiration = gpr_time_add(now, grpc_max_auth_token_lifetime());
174   }
175
176   Json::Object object = {
177       {"iss", json_key->client_email},
178       {"aud", audience},
179       {"iat", now.tv_sec},
180       {"exp", expiration.tv_sec},
181   };
182   if (scope != nullptr) {
183     object["scope"] = scope;
184   } else {
185     /* Unscoped JWTs need a sub field. */
186     object["sub"] = json_key->client_email;
187   }
188
189   Json json(object);
190   std::string json_str = json.Dump();
191   return grpc_base64_encode(json_str.c_str(), json_str.size(), 1, 0);
192 }
193
194 static char* dot_concat_and_free_strings(char* str1, char* str2) {
195   size_t str1_len = strlen(str1);
196   size_t str2_len = strlen(str2);
197   size_t result_len = str1_len + 1 /* dot */ + str2_len;
198   char* result =
199       static_cast<char*>(gpr_malloc(result_len + 1 /* NULL terminated */));
200   char* current = result;
201   memcpy(current, str1, str1_len);
202   current += str1_len;
203   *(current++) = '.';
204   memcpy(current, str2, str2_len);
205   current += str2_len;
206   GPR_ASSERT(current >= result);
207   GPR_ASSERT((uintptr_t)(current - result) == result_len);
208   *current = '\0';
209   gpr_free(str1);
210   gpr_free(str2);
211   return result;
212 }
213
214 const EVP_MD* openssl_digest_from_algorithm(const char* algorithm) {
215   if (strcmp(algorithm, GRPC_JWT_RSA_SHA256_ALGORITHM) == 0) {
216     return EVP_sha256();
217   } else {
218     gpr_log(GPR_ERROR, "Unknown algorithm %s.", algorithm);
219     return nullptr;
220   }
221 }
222
223 char* compute_and_encode_signature(const grpc_auth_json_key* json_key,
224                                    const char* signature_algorithm,
225                                    const char* to_sign) {
226   const EVP_MD* md = openssl_digest_from_algorithm(signature_algorithm);
227   EVP_MD_CTX* md_ctx = nullptr;
228   EVP_PKEY* key = EVP_PKEY_new();
229   size_t sig_len = 0;
230   unsigned char* sig = nullptr;
231   char* result = nullptr;
232   if (md == nullptr) return nullptr;
233   md_ctx = EVP_MD_CTX_create();
234   if (md_ctx == nullptr) {
235     gpr_log(GPR_ERROR, "Could not create MD_CTX");
236     goto end;
237   }
238   EVP_PKEY_set1_RSA(key, json_key->private_key);
239   if (EVP_DigestSignInit(md_ctx, nullptr, md, nullptr, key) != 1) {
240     gpr_log(GPR_ERROR, "DigestInit failed.");
241     goto end;
242   }
243   if (EVP_DigestSignUpdate(md_ctx, to_sign, strlen(to_sign)) != 1) {
244     gpr_log(GPR_ERROR, "DigestUpdate failed.");
245     goto end;
246   }
247   if (EVP_DigestSignFinal(md_ctx, nullptr, &sig_len) != 1) {
248     gpr_log(GPR_ERROR, "DigestFinal (get signature length) failed.");
249     goto end;
250   }
251   sig = static_cast<unsigned char*>(gpr_malloc(sig_len));
252   if (EVP_DigestSignFinal(md_ctx, sig, &sig_len) != 1) {
253     gpr_log(GPR_ERROR, "DigestFinal (signature compute) failed.");
254     goto end;
255   }
256   result = grpc_base64_encode(sig, sig_len, 1, 0);
257
258 end:
259   if (key != nullptr) EVP_PKEY_free(key);
260   if (md_ctx != nullptr) EVP_MD_CTX_destroy(md_ctx);
261   if (sig != nullptr) gpr_free(sig);
262   return result;
263 }
264
265 char* grpc_jwt_encode_and_sign(const grpc_auth_json_key* json_key,
266                                const char* audience,
267                                gpr_timespec token_lifetime, const char* scope) {
268   if (g_jwt_encode_and_sign_override != nullptr) {
269     return g_jwt_encode_and_sign_override(json_key, audience, token_lifetime,
270                                           scope);
271   } else {
272     const char* sig_algo = GRPC_JWT_RSA_SHA256_ALGORITHM;
273     char* to_sign = dot_concat_and_free_strings(
274         encoded_jwt_header(json_key->private_key_id, sig_algo),
275         encoded_jwt_claim(json_key, audience, token_lifetime, scope));
276     char* sig = compute_and_encode_signature(json_key, sig_algo, to_sign);
277     if (sig == nullptr) {
278       gpr_free(to_sign);
279       return nullptr;
280     }
281     return dot_concat_and_free_strings(to_sign, sig);
282   }
283 }
284
285 void grpc_jwt_encode_and_sign_set_override(
286     grpc_jwt_encode_and_sign_override func) {
287   g_jwt_encode_and_sign_override = func;
288 }