Home | History | Annotate | Download | only in handshaker
      1 /*
      2  *
      3  * Copyright 2018 gRPC authors.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  *
     17  */
     18 
     19 #include <grpc/support/port_platform.h>
     20 
     21 #include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h"
     22 
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <string.h>
     26 
     27 #include <grpc/support/alloc.h>
     28 #include <grpc/support/log.h>
     29 #include <grpc/support/sync.h>
     30 #include <grpc/support/thd_id.h>
     31 
     32 #include "src/core/lib/gpr/host_port.h"
     33 #include "src/core/lib/gprpp/thd.h"
     34 #include "src/core/lib/slice/slice_internal.h"
     35 #include "src/core/tsi/alts/frame_protector/alts_frame_protector.h"
     36 #include "src/core/tsi/alts/handshaker/alts_handshaker_client.h"
     37 #include "src/core/tsi/alts/handshaker/alts_tsi_utils.h"
     38 #include "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h"
     39 #include "src/core/tsi/alts_transport_security.h"
     40 
     41 #define TSI_ALTS_INITIAL_BUFFER_SIZE 256
     42 
     43 static alts_shared_resource* kSharedResource = alts_get_shared_resource();
     44 
     45 /* Main struct for ALTS TSI handshaker. */
     46 typedef struct alts_tsi_handshaker {
     47   tsi_handshaker base;
     48   alts_handshaker_client* client;
     49   grpc_slice recv_bytes;
     50   grpc_slice target_name;
     51   unsigned char* buffer;
     52   size_t buffer_size;
     53   bool is_client;
     54   bool has_sent_start_message;
     55   grpc_alts_credentials_options* options;
     56 } alts_tsi_handshaker;
     57 
     58 /* Main struct for ALTS TSI handshaker result. */
     59 typedef struct alts_tsi_handshaker_result {
     60   tsi_handshaker_result base;
     61   char* peer_identity;
     62   char* key_data;
     63   unsigned char* unused_bytes;
     64   size_t unused_bytes_size;
     65   grpc_slice rpc_versions;
     66   bool is_client;
     67 } alts_tsi_handshaker_result;
     68 
     69 static tsi_result handshaker_result_extract_peer(
     70     const tsi_handshaker_result* self, tsi_peer* peer) {
     71   if (self == nullptr || peer == nullptr) {
     72     gpr_log(GPR_ERROR, "Invalid argument to handshaker_result_extract_peer()");
     73     return TSI_INVALID_ARGUMENT;
     74   }
     75   alts_tsi_handshaker_result* result =
     76       reinterpret_cast<alts_tsi_handshaker_result*>(
     77           const_cast<tsi_handshaker_result*>(self));
     78   GPR_ASSERT(kTsiAltsNumOfPeerProperties == 3);
     79   tsi_result ok = tsi_construct_peer(kTsiAltsNumOfPeerProperties, peer);
     80   int index = 0;
     81   if (ok != TSI_OK) {
     82     gpr_log(GPR_ERROR, "Failed to construct tsi peer");
     83     return ok;
     84   }
     85   GPR_ASSERT(&peer->properties[index] != nullptr);
     86   ok = tsi_construct_string_peer_property_from_cstring(
     87       TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_ALTS_CERTIFICATE_TYPE,
     88       &peer->properties[index]);
     89   if (ok != TSI_OK) {
     90     tsi_peer_destruct(peer);
     91     gpr_log(GPR_ERROR, "Failed to set tsi peer property");
     92     return ok;
     93   }
     94   index++;
     95   GPR_ASSERT(&peer->properties[index] != nullptr);
     96   ok = tsi_construct_string_peer_property_from_cstring(
     97       TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, result->peer_identity,
     98       &peer->properties[index]);
     99   if (ok != TSI_OK) {
    100     tsi_peer_destruct(peer);
    101     gpr_log(GPR_ERROR, "Failed to set tsi peer property");
    102   }
    103   index++;
    104   GPR_ASSERT(&peer->properties[index] != nullptr);
    105   ok = tsi_construct_string_peer_property(
    106       TSI_ALTS_RPC_VERSIONS,
    107       reinterpret_cast<char*>(GRPC_SLICE_START_PTR(result->rpc_versions)),
    108       GRPC_SLICE_LENGTH(result->rpc_versions), &peer->properties[2]);
    109   if (ok != TSI_OK) {
    110     tsi_peer_destruct(peer);
    111     gpr_log(GPR_ERROR, "Failed to set tsi peer property");
    112   }
    113   GPR_ASSERT(++index == kTsiAltsNumOfPeerProperties);
    114   return ok;
    115 }
    116 
    117 static tsi_result handshaker_result_create_zero_copy_grpc_protector(
    118     const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
    119     tsi_zero_copy_grpc_protector** protector) {
    120   if (self == nullptr || protector == nullptr) {
    121     gpr_log(GPR_ERROR,
    122             "Invalid arguments to create_zero_copy_grpc_protector()");
    123     return TSI_INVALID_ARGUMENT;
    124   }
    125   alts_tsi_handshaker_result* result =
    126       reinterpret_cast<alts_tsi_handshaker_result*>(
    127           const_cast<tsi_handshaker_result*>(self));
    128   tsi_result ok = alts_zero_copy_grpc_protector_create(
    129       reinterpret_cast<const uint8_t*>(result->key_data),
    130       kAltsAes128GcmRekeyKeyLength, /*is_rekey=*/true, result->is_client,
    131       /*is_integrity_only=*/false, /*enable_extra_copy=*/false,
    132       max_output_protected_frame_size, protector);
    133   if (ok != TSI_OK) {
    134     gpr_log(GPR_ERROR, "Failed to create zero-copy grpc protector");
    135   }
    136   return ok;
    137 }
    138 
    139 static tsi_result handshaker_result_create_frame_protector(
    140     const tsi_handshaker_result* self, size_t* max_output_protected_frame_size,
    141     tsi_frame_protector** protector) {
    142   if (self == nullptr || protector == nullptr) {
    143     gpr_log(GPR_ERROR,
    144             "Invalid arguments to handshaker_result_create_frame_protector()");
    145     return TSI_INVALID_ARGUMENT;
    146   }
    147   alts_tsi_handshaker_result* result =
    148       reinterpret_cast<alts_tsi_handshaker_result*>(
    149           const_cast<tsi_handshaker_result*>(self));
    150   tsi_result ok = alts_create_frame_protector(
    151       reinterpret_cast<const uint8_t*>(result->key_data),
    152       kAltsAes128GcmRekeyKeyLength, result->is_client, /*is_rekey=*/true,
    153       max_output_protected_frame_size, protector);
    154   if (ok != TSI_OK) {
    155     gpr_log(GPR_ERROR, "Failed to create frame protector");
    156   }
    157   return ok;
    158 }
    159 
    160 static tsi_result handshaker_result_get_unused_bytes(
    161     const tsi_handshaker_result* self, const unsigned char** bytes,
    162     size_t* bytes_size) {
    163   if (self == nullptr || bytes == nullptr || bytes_size == nullptr) {
    164     gpr_log(GPR_ERROR,
    165             "Invalid arguments to handshaker_result_get_unused_bytes()");
    166     return TSI_INVALID_ARGUMENT;
    167   }
    168   alts_tsi_handshaker_result* result =
    169       reinterpret_cast<alts_tsi_handshaker_result*>(
    170           const_cast<tsi_handshaker_result*>(self));
    171   *bytes = result->unused_bytes;
    172   *bytes_size = result->unused_bytes_size;
    173   return TSI_OK;
    174 }
    175 
    176 static void handshaker_result_destroy(tsi_handshaker_result* self) {
    177   if (self == nullptr) {
    178     return;
    179   }
    180   alts_tsi_handshaker_result* result =
    181       reinterpret_cast<alts_tsi_handshaker_result*>(
    182           const_cast<tsi_handshaker_result*>(self));
    183   gpr_free(result->peer_identity);
    184   gpr_free(result->key_data);
    185   gpr_free(result->unused_bytes);
    186   grpc_slice_unref_internal(result->rpc_versions);
    187   gpr_free(result);
    188 }
    189 
    190 static const tsi_handshaker_result_vtable result_vtable = {
    191     handshaker_result_extract_peer,
    192     handshaker_result_create_zero_copy_grpc_protector,
    193     handshaker_result_create_frame_protector,
    194     handshaker_result_get_unused_bytes, handshaker_result_destroy};
    195 
    196 static tsi_result create_handshaker_result(grpc_gcp_handshaker_resp* resp,
    197                                            bool is_client,
    198                                            tsi_handshaker_result** self) {
    199   if (self == nullptr || resp == nullptr) {
    200     gpr_log(GPR_ERROR, "Invalid arguments to create_handshaker_result()");
    201     return TSI_INVALID_ARGUMENT;
    202   }
    203   grpc_slice* key = static_cast<grpc_slice*>(resp->result.key_data.arg);
    204   GPR_ASSERT(key != nullptr);
    205   grpc_slice* identity =
    206       static_cast<grpc_slice*>(resp->result.peer_identity.service_account.arg);
    207   if (identity == nullptr) {
    208     gpr_log(GPR_ERROR, "Invalid service account");
    209     return TSI_FAILED_PRECONDITION;
    210   }
    211   if (GRPC_SLICE_LENGTH(*key) < kAltsAes128GcmRekeyKeyLength) {
    212     gpr_log(GPR_ERROR, "Bad key length");
    213     return TSI_FAILED_PRECONDITION;
    214   }
    215   alts_tsi_handshaker_result* result =
    216       static_cast<alts_tsi_handshaker_result*>(gpr_zalloc(sizeof(*result)));
    217   result->key_data =
    218       static_cast<char*>(gpr_zalloc(kAltsAes128GcmRekeyKeyLength));
    219   memcpy(result->key_data, GRPC_SLICE_START_PTR(*key),
    220          kAltsAes128GcmRekeyKeyLength);
    221   result->peer_identity = grpc_slice_to_c_string(*identity);
    222   if (!resp->result.has_peer_rpc_versions) {
    223     gpr_log(GPR_ERROR, "Peer does not set RPC protocol versions.");
    224     return TSI_FAILED_PRECONDITION;
    225   }
    226   if (!grpc_gcp_rpc_protocol_versions_encode(&resp->result.peer_rpc_versions,
    227                                              &result->rpc_versions)) {
    228     gpr_log(GPR_ERROR, "Failed to serialize peer's RPC protocol versions.");
    229     return TSI_FAILED_PRECONDITION;
    230   }
    231   result->is_client = is_client;
    232   result->base.vtable = &result_vtable;
    233   *self = &result->base;
    234   return TSI_OK;
    235 }
    236 
    237 static tsi_result handshaker_next(
    238     tsi_handshaker* self, const unsigned char* received_bytes,
    239     size_t received_bytes_size, const unsigned char** bytes_to_send,
    240     size_t* bytes_to_send_size, tsi_handshaker_result** result,
    241     tsi_handshaker_on_next_done_cb cb, void* user_data) {
    242   if (self == nullptr || cb == nullptr) {
    243     gpr_log(GPR_ERROR, "Invalid arguments to handshaker_next()");
    244     return TSI_INVALID_ARGUMENT;
    245   }
    246   if (self->handshake_shutdown) {
    247     gpr_log(GPR_ERROR, "TSI handshake shutdown");
    248     return TSI_HANDSHAKE_SHUTDOWN;
    249   }
    250   alts_tsi_handshaker* handshaker =
    251       reinterpret_cast<alts_tsi_handshaker*>(self);
    252   tsi_result ok = TSI_OK;
    253   alts_tsi_event* event = nullptr;
    254   ok = alts_tsi_event_create(handshaker, cb, user_data, handshaker->options,
    255                              handshaker->target_name, &event);
    256   if (ok != TSI_OK) {
    257     gpr_log(GPR_ERROR, "Failed to create ALTS TSI event");
    258     return ok;
    259   }
    260   grpc_slice slice = (received_bytes == nullptr || received_bytes_size == 0)
    261                          ? grpc_empty_slice()
    262                          : grpc_slice_from_copied_buffer(
    263                                reinterpret_cast<const char*>(received_bytes),
    264                                received_bytes_size);
    265   if (!handshaker->has_sent_start_message) {
    266     ok = handshaker->is_client
    267              ? alts_handshaker_client_start_client(handshaker->client, event)
    268              : alts_handshaker_client_start_server(handshaker->client, event,
    269                                                    &slice);
    270     handshaker->has_sent_start_message = true;
    271   } else {
    272     if (!GRPC_SLICE_IS_EMPTY(handshaker->recv_bytes)) {
    273       grpc_slice_unref_internal(handshaker->recv_bytes);
    274     }
    275     handshaker->recv_bytes = grpc_slice_ref(slice);
    276     ok = alts_handshaker_client_next(handshaker->client, event, &slice);
    277   }
    278   grpc_slice_unref_internal(slice);
    279   if (ok != TSI_OK) {
    280     gpr_log(GPR_ERROR, "Failed to schedule ALTS handshaker requests");
    281     return ok;
    282   }
    283   return TSI_ASYNC;
    284 }
    285 
    286 static void handshaker_shutdown(tsi_handshaker* self) {
    287   GPR_ASSERT(self != nullptr);
    288   if (self->handshake_shutdown) {
    289     return;
    290   }
    291   alts_tsi_handshaker* handshaker =
    292       reinterpret_cast<alts_tsi_handshaker*>(self);
    293   alts_handshaker_client_shutdown(handshaker->client);
    294 }
    295 
    296 static void handshaker_destroy(tsi_handshaker* self) {
    297   if (self == nullptr) {
    298     return;
    299   }
    300   alts_tsi_handshaker* handshaker =
    301       reinterpret_cast<alts_tsi_handshaker*>(self);
    302   alts_handshaker_client_destroy(handshaker->client);
    303   grpc_slice_unref_internal(handshaker->recv_bytes);
    304   grpc_slice_unref_internal(handshaker->target_name);
    305   grpc_alts_credentials_options_destroy(handshaker->options);
    306   gpr_free(handshaker->buffer);
    307   gpr_free(handshaker);
    308 }
    309 
    310 static const tsi_handshaker_vtable handshaker_vtable = {
    311     nullptr,         nullptr,
    312     nullptr,         nullptr,
    313     nullptr,         handshaker_destroy,
    314     handshaker_next, handshaker_shutdown};
    315 
    316 static void thread_worker(void* arg) {
    317   while (true) {
    318     grpc_event event = grpc_completion_queue_next(
    319         kSharedResource->cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr);
    320     GPR_ASSERT(event.type != GRPC_QUEUE_TIMEOUT);
    321     if (event.type == GRPC_QUEUE_SHUTDOWN) {
    322       /* signal alts_tsi_shutdown() to destroy completion queue. */
    323       grpc_tsi_alts_signal_for_cq_destroy();
    324       break;
    325     }
    326     /* event.type == GRPC_OP_COMPLETE. */
    327     alts_tsi_event* alts_event = static_cast<alts_tsi_event*>(event.tag);
    328     alts_tsi_event_dispatch_to_handshaker(alts_event, event.success);
    329     alts_tsi_event_destroy(alts_event);
    330   }
    331 }
    332 
    333 static void init_shared_resources(const char* handshaker_service_url) {
    334   GPR_ASSERT(handshaker_service_url != nullptr);
    335   gpr_mu_lock(&kSharedResource->mu);
    336   if (kSharedResource->channel == nullptr) {
    337     gpr_cv_init(&kSharedResource->cv);
    338     kSharedResource->channel =
    339         grpc_insecure_channel_create(handshaker_service_url, nullptr, nullptr);
    340     kSharedResource->cq = grpc_completion_queue_create_for_next(nullptr);
    341     kSharedResource->thread =
    342         grpc_core::Thread("alts_tsi_handshaker", &thread_worker, nullptr);
    343     kSharedResource->thread.Start();
    344   }
    345   gpr_mu_unlock(&kSharedResource->mu);
    346 }
    347 
    348 tsi_result alts_tsi_handshaker_create(
    349     const grpc_alts_credentials_options* options, const char* target_name,
    350     const char* handshaker_service_url, bool is_client, tsi_handshaker** self) {
    351   if (handshaker_service_url == nullptr || self == nullptr ||
    352       options == nullptr || (is_client && target_name == nullptr)) {
    353     gpr_log(GPR_ERROR, "Invalid arguments to alts_tsi_handshaker_create()");
    354     return TSI_INVALID_ARGUMENT;
    355   }
    356   init_shared_resources(handshaker_service_url);
    357   alts_handshaker_client* client = alts_grpc_handshaker_client_create(
    358       kSharedResource->channel, kSharedResource->cq, handshaker_service_url);
    359   if (client == nullptr) {
    360     gpr_log(GPR_ERROR, "Failed to create ALTS handshaker client");
    361     return TSI_FAILED_PRECONDITION;
    362   }
    363   alts_tsi_handshaker* handshaker =
    364       static_cast<alts_tsi_handshaker*>(gpr_zalloc(sizeof(*handshaker)));
    365   handshaker->client = client;
    366   handshaker->buffer_size = TSI_ALTS_INITIAL_BUFFER_SIZE;
    367   handshaker->buffer =
    368       static_cast<unsigned char*>(gpr_zalloc(handshaker->buffer_size));
    369   handshaker->is_client = is_client;
    370   handshaker->has_sent_start_message = false;
    371   handshaker->target_name = target_name == nullptr
    372                                 ? grpc_empty_slice()
    373                                 : grpc_slice_from_static_string(target_name);
    374   handshaker->options = grpc_alts_credentials_options_copy(options);
    375   handshaker->base.vtable = &handshaker_vtable;
    376   *self = &handshaker->base;
    377   return TSI_OK;
    378 }
    379 
    380 static bool is_handshake_finished_properly(grpc_gcp_handshaker_resp* resp) {
    381   GPR_ASSERT(resp != nullptr);
    382   if (resp->has_result) {
    383     return true;
    384   }
    385   return false;
    386 }
    387 
    388 static void set_unused_bytes(tsi_handshaker_result* self,
    389                              grpc_slice* recv_bytes, size_t bytes_consumed) {
    390   GPR_ASSERT(recv_bytes != nullptr && self != nullptr);
    391   if (GRPC_SLICE_LENGTH(*recv_bytes) == bytes_consumed) {
    392     return;
    393   }
    394   alts_tsi_handshaker_result* result =
    395       reinterpret_cast<alts_tsi_handshaker_result*>(self);
    396   result->unused_bytes_size = GRPC_SLICE_LENGTH(*recv_bytes) - bytes_consumed;
    397   result->unused_bytes =
    398       static_cast<unsigned char*>(gpr_zalloc(result->unused_bytes_size));
    399   memcpy(result->unused_bytes,
    400          GRPC_SLICE_START_PTR(*recv_bytes) + bytes_consumed,
    401          result->unused_bytes_size);
    402 }
    403 
    404 void alts_tsi_handshaker_handle_response(alts_tsi_handshaker* handshaker,
    405                                          grpc_byte_buffer* recv_buffer,
    406                                          grpc_status_code status,
    407                                          grpc_slice* details,
    408                                          tsi_handshaker_on_next_done_cb cb,
    409                                          void* user_data, bool is_ok) {
    410   /* Invalid input check. */
    411   if (cb == nullptr) {
    412     gpr_log(GPR_ERROR,
    413             "cb is nullptr in alts_tsi_handshaker_handle_response()");
    414     return;
    415   }
    416   if (handshaker == nullptr || recv_buffer == nullptr) {
    417     gpr_log(GPR_ERROR,
    418             "Invalid arguments to alts_tsi_handshaker_handle_response()");
    419     cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
    420     return;
    421   }
    422   if (handshaker->base.handshake_shutdown) {
    423     gpr_log(GPR_ERROR, "TSI handshake shutdown");
    424     cb(TSI_HANDSHAKE_SHUTDOWN, user_data, nullptr, 0, nullptr);
    425     return;
    426   }
    427   /* Failed grpc call check. */
    428   if (!is_ok || status != GRPC_STATUS_OK) {
    429     gpr_log(GPR_ERROR, "grpc call made to handshaker service failed");
    430     if (details != nullptr) {
    431       char* error_details = grpc_slice_to_c_string(*details);
    432       gpr_log(GPR_ERROR, "error details:%s", error_details);
    433       gpr_free(error_details);
    434     }
    435     cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr);
    436     return;
    437   }
    438   grpc_gcp_handshaker_resp* resp =
    439       alts_tsi_utils_deserialize_response(recv_buffer);
    440   /* Invalid handshaker response check. */
    441   if (resp == nullptr) {
    442     gpr_log(GPR_ERROR, "alts_tsi_utils_deserialize_response() failed");
    443     cb(TSI_DATA_CORRUPTED, user_data, nullptr, 0, nullptr);
    444     return;
    445   }
    446   grpc_slice* slice = static_cast<grpc_slice*>(resp->out_frames.arg);
    447   unsigned char* bytes_to_send = nullptr;
    448   size_t bytes_to_send_size = 0;
    449   if (slice != nullptr) {
    450     bytes_to_send_size = GRPC_SLICE_LENGTH(*slice);
    451     while (bytes_to_send_size > handshaker->buffer_size) {
    452       handshaker->buffer_size *= 2;
    453       handshaker->buffer = static_cast<unsigned char*>(
    454           gpr_realloc(handshaker->buffer, handshaker->buffer_size));
    455     }
    456     memcpy(handshaker->buffer, GRPC_SLICE_START_PTR(*slice),
    457            bytes_to_send_size);
    458     bytes_to_send = handshaker->buffer;
    459   }
    460   tsi_handshaker_result* result = nullptr;
    461   if (is_handshake_finished_properly(resp)) {
    462     create_handshaker_result(resp, handshaker->is_client, &result);
    463     set_unused_bytes(result, &handshaker->recv_bytes, resp->bytes_consumed);
    464   }
    465   grpc_status_code code = static_cast<grpc_status_code>(resp->status.code);
    466   if (code != GRPC_STATUS_OK) {
    467     grpc_slice* details = static_cast<grpc_slice*>(resp->status.details.arg);
    468     if (details != nullptr) {
    469       char* error_details = grpc_slice_to_c_string(*details);
    470       gpr_log(GPR_ERROR, "Error from handshaker service:%s", error_details);
    471       gpr_free(error_details);
    472     }
    473   }
    474   grpc_gcp_handshaker_resp_destroy(resp);
    475   cb(alts_tsi_utils_convert_to_tsi_result(code), user_data, bytes_to_send,
    476      bytes_to_send_size, result);
    477 }
    478 
    479 namespace grpc_core {
    480 namespace internal {
    481 
    482 bool alts_tsi_handshaker_get_has_sent_start_message_for_testing(
    483     alts_tsi_handshaker* handshaker) {
    484   GPR_ASSERT(handshaker != nullptr);
    485   return handshaker->has_sent_start_message;
    486 }
    487 
    488 bool alts_tsi_handshaker_get_is_client_for_testing(
    489     alts_tsi_handshaker* handshaker) {
    490   GPR_ASSERT(handshaker != nullptr);
    491   return handshaker->is_client;
    492 }
    493 
    494 void alts_tsi_handshaker_set_recv_bytes_for_testing(
    495     alts_tsi_handshaker* handshaker, grpc_slice* slice) {
    496   GPR_ASSERT(handshaker != nullptr && slice != nullptr);
    497   handshaker->recv_bytes = grpc_slice_ref(*slice);
    498 }
    499 
    500 grpc_slice alts_tsi_handshaker_get_recv_bytes_for_testing(
    501     alts_tsi_handshaker* handshaker) {
    502   GPR_ASSERT(handshaker != nullptr);
    503   return handshaker->recv_bytes;
    504 }
    505 
    506 void alts_tsi_handshaker_set_client_for_testing(
    507     alts_tsi_handshaker* handshaker, alts_handshaker_client* client) {
    508   GPR_ASSERT(handshaker != nullptr && client != nullptr);
    509   alts_handshaker_client_destroy(handshaker->client);
    510   handshaker->client = client;
    511 }
    512 
    513 alts_handshaker_client* alts_tsi_handshaker_get_client_for_testing(
    514     alts_tsi_handshaker* handshaker) {
    515   return handshaker->client;
    516 }
    517 
    518 }  // namespace internal
    519 }  // namespace grpc_core
    520