grpc 1.83.1 → 1.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +21 -17
  3. data/include/grpc/credentials.h +10 -0
  4. data/include/grpc/grpc_security_constants.h +2 -0
  5. data/include/grpc/impl/channel_arg_names.h +9 -0
  6. data/src/core/call/call_filters.h +4 -2
  7. data/src/core/call/client_call.cc +20 -8
  8. data/src/core/call/client_call.h +4 -0
  9. data/src/core/call/interception_chain.cc +2 -0
  10. data/src/core/call/interception_chain.h +14 -1
  11. data/src/core/call/metadata.cc +1 -3
  12. data/src/core/call/metadata_batch.h +13 -5
  13. data/src/core/call/server_call.cc +31 -14
  14. data/src/core/call/server_call.h +4 -0
  15. data/src/core/channelz/text_encode.cc +9 -4
  16. data/src/core/channelz/v2tov1/convert.cc +2 -2
  17. data/src/core/client_channel/backup_poller.cc +1 -2
  18. data/src/core/client_channel/client_channel.cc +6 -33
  19. data/src/core/client_channel/client_channel_filter.cc +22 -55
  20. data/src/core/client_channel/client_channel_filter.h +2 -2
  21. data/src/core/client_channel/client_channel_service_config.cc +3 -19
  22. data/src/core/client_channel/direct_channel.cc +7 -1
  23. data/src/core/client_channel/direct_channel.h +2 -2
  24. data/src/core/client_channel/load_balanced_call_destination.cc +5 -3
  25. data/src/core/client_channel/retry_filter_legacy_call_data.cc +5 -12
  26. data/src/core/client_channel/subchannel.cc +310 -1058
  27. data/src/core/client_channel/subchannel.h +44 -329
  28. data/src/core/client_channel/subchannel_metrics.cc +48 -0
  29. data/src/core/client_channel/subchannel_metrics.h +60 -0
  30. data/src/core/credentials/call/external/file_external_account_credentials.cc +1 -1
  31. data/src/core/credentials/call/external/url_external_account_credentials.cc +2 -3
  32. data/src/core/credentials/call/gcp_service_account_identity/gcp_service_account_identity_credentials.cc +8 -24
  33. data/src/core/credentials/call/gcp_service_account_identity/gcp_service_account_identity_credentials.h +3 -6
  34. data/src/core/credentials/call/gdch_service_account/gdch_service_account_credentials.cc +525 -0
  35. data/src/core/credentials/call/gdch_service_account/gdch_service_account_credentials.h +138 -0
  36. data/src/core/credentials/call/json_util.h +1 -0
  37. data/src/core/credentials/call/jwt/json_token.cc +10 -2
  38. data/src/core/credentials/call/jwt/jwt_verifier.cc +15 -1
  39. data/src/core/credentials/call/jwt_token_file/jwt_token_file_call_credentials.cc +1 -1
  40. data/src/core/credentials/call/oauth2/oauth2_credentials.cc +42 -79
  41. data/src/core/credentials/call/oauth2/oauth2_credentials.h +2 -5
  42. data/src/core/credentials/call/token_fetcher/token_fetcher_credentials.cc +52 -31
  43. data/src/core/credentials/call/token_fetcher/token_fetcher_credentials.h +21 -8
  44. data/src/core/credentials/transport/google_default/google_default_credentials.cc +3 -4
  45. data/src/core/credentials/transport/ssl/ssl_credentials.cc +59 -72
  46. data/src/core/credentials/transport/ssl/ssl_credentials.h +9 -8
  47. data/src/core/credentials/transport/ssl/ssl_security_connector.cc +12 -6
  48. data/src/core/credentials/transport/ssl/ssl_security_connector.h +4 -4
  49. data/src/core/credentials/transport/tls/grpc_tls_certificate_distributor.cc +29 -18
  50. data/src/core/credentials/transport/tls/grpc_tls_certificate_distributor.h +9 -6
  51. data/src/core/credentials/transport/tls/grpc_tls_certificate_provider.cc +45 -56
  52. data/src/core/credentials/transport/tls/grpc_tls_certificate_provider.h +5 -4
  53. data/src/core/credentials/transport/tls/grpc_tls_certificate_selector.h +26 -0
  54. data/src/core/credentials/transport/tls/grpc_tls_crl_provider.cc +1 -1
  55. data/src/core/credentials/transport/tls/load_system_roots.h +2 -6
  56. data/src/core/credentials/transport/tls/load_system_roots_fallback.cc +2 -4
  57. data/src/core/credentials/transport/tls/load_system_roots_supported.cc +13 -15
  58. data/src/core/credentials/transport/tls/load_system_roots_supported.h +3 -2
  59. data/src/core/credentials/transport/tls/load_system_roots_windows.cc +5 -8
  60. data/src/core/credentials/transport/tls/spiffe_utils.cc +1 -1
  61. data/src/core/credentials/transport/tls/ssl_utils.cc +39 -28
  62. data/src/core/credentials/transport/tls/ssl_utils.h +12 -49
  63. data/src/core/credentials/transport/tls/tls_security_connector.cc +48 -33
  64. data/src/core/credentials/transport/tls/tls_security_connector.h +31 -10
  65. data/src/core/ext/filters/channel_idle/legacy_channel_idle_filter.cc +15 -11
  66. data/src/core/ext/filters/http/http_filters_plugin.cc +20 -9
  67. data/src/core/ext/filters/rbac/rbac_filter.cc +43 -21
  68. data/src/core/ext/filters/rbac/rbac_filter.h +21 -3
  69. data/src/core/ext/filters/rbac/rbac_service_config_parser.cc +2 -2
  70. data/src/core/ext/transport/chttp2/client/chttp2_connector.cc +1 -1
  71. data/src/core/ext/transport/chttp2/server/chttp2_server.cc +6 -20
  72. data/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +32 -67
  73. data/src/core/ext/transport/chttp2/transport/flow_control.cc +2 -0
  74. data/src/core/ext/transport/chttp2/transport/flow_control.h +78 -4
  75. data/src/core/ext/transport/chttp2/transport/frame.h +2 -0
  76. data/src/core/ext/transport/chttp2/transport/frame_data.cc +3 -4
  77. data/src/core/ext/transport/chttp2/transport/goaway.h +7 -0
  78. data/src/core/ext/transport/chttp2/transport/hpack_parser.cc +5 -3
  79. data/src/core/ext/transport/chttp2/transport/hpack_parser.h +3 -1
  80. data/src/core/ext/transport/chttp2/transport/http2_client_transport.cc +141 -130
  81. data/src/core/ext/transport/chttp2/transport/http2_client_transport.h +22 -22
  82. data/src/core/ext/transport/chttp2/transport/http2_server_transport.cc +402 -269
  83. data/src/core/ext/transport/chttp2/transport/http2_server_transport.h +80 -49
  84. data/src/core/ext/transport/chttp2/transport/http2_settings_promises.h +1 -2
  85. data/src/core/ext/transport/chttp2/transport/http2_status.h +38 -37
  86. data/src/core/ext/transport/chttp2/transport/http2_transport.cc +178 -5
  87. data/src/core/ext/transport/chttp2/transport/http2_transport.h +345 -5
  88. data/src/core/ext/transport/chttp2/transport/parsing.cc +5 -5
  89. data/src/core/ext/transport/chttp2/transport/ping_promise.cc +3 -2
  90. data/src/core/ext/transport/chttp2/transport/ping_promise.h +25 -5
  91. data/src/core/ext/transport/chttp2/transport/ping_rate_policy.cc +3 -4
  92. data/src/core/ext/transport/chttp2/transport/read_context.h +30 -3
  93. data/src/core/ext/transport/chttp2/transport/stream.h +65 -23
  94. data/src/core/ext/transport/chttp2/transport/writing.cc +0 -3
  95. data/src/core/ext/transport/inproc/inproc_transport.cc +1 -5
  96. data/src/core/ext/transport/inproc/legacy_inproc_transport.cc +4 -17
  97. data/src/core/ext/upb-gen/envoy/extensions/filters/http/ext_proc/v3/ext_proc.upb.h +1912 -0
  98. data/src/core/ext/upb-gen/envoy/extensions/filters/http/ext_proc/v3/ext_proc.upb_minitable.c +300 -0
  99. data/src/core/ext/upb-gen/envoy/extensions/filters/http/ext_proc/v3/ext_proc.upb_minitable.h +37 -0
  100. data/src/core/ext/upb-gen/envoy/extensions/filters/http/ext_proc/v3/processing_mode.upb.h +187 -0
  101. data/src/core/ext/upb-gen/envoy/extensions/filters/http/ext_proc/v3/processing_mode.upb_minitable.c +64 -0
  102. data/src/core/ext/upb-gen/envoy/extensions/filters/http/ext_proc/v3/processing_mode.upb_minitable.h +31 -0
  103. data/src/core/ext/upb-gen/envoy/service/ext_proc/v3/external_processor.upb.h +2362 -0
  104. data/src/core/ext/upb-gen/envoy/service/ext_proc/v3/external_processor.upb_minitable.c +556 -0
  105. data/src/core/ext/upb-gen/envoy/service/ext_proc/v3/external_processor.upb_minitable.h +47 -0
  106. data/src/core/ext/upb-gen/{src/proto/grpc/channelz → grpc/channelz/v1}/channelz.upb.h +5 -5
  107. data/src/core/ext/upb-gen/{src/proto/grpc/channelz → grpc/channelz/v1}/channelz.upb_minitable.c +3 -3
  108. data/src/core/ext/upb-gen/{src/proto/grpc/channelz → grpc/channelz/v1}/channelz.upb_minitable.h +5 -5
  109. data/src/core/ext/upbdefs-gen/envoy/extensions/filters/http/ext_proc/v3/ext_proc.upbdefs.c +453 -0
  110. data/src/core/ext/upbdefs-gen/envoy/extensions/filters/http/ext_proc/v3/ext_proc.upbdefs.h +67 -0
  111. data/src/core/ext/upbdefs-gen/envoy/extensions/filters/http/ext_proc/v3/processing_mode.upbdefs.c +142 -0
  112. data/src/core/ext/upbdefs-gen/envoy/extensions/filters/http/ext_proc/v3/processing_mode.upbdefs.h +37 -0
  113. data/src/core/ext/upbdefs-gen/grpc/channelz/v1/channelz.upbdefs.c +715 -0
  114. data/src/core/ext/upbdefs-gen/{src/proto/grpc/channelz → grpc/channelz/v1}/channelz.upbdefs.h +44 -44
  115. data/src/core/filter/auth/server_auth_filter.cc +2 -3
  116. data/src/core/filter/composite/composite_filter.cc +45 -33
  117. data/src/core/filter/composite/composite_filter.h +5 -4
  118. data/src/core/filter/ext_proc/ext_proc_filter.cc +224 -0
  119. data/src/core/filter/ext_proc/ext_proc_filter.h +155 -0
  120. data/src/core/filter/ext_proc/ext_proc_messages.cc +701 -0
  121. data/src/core/filter/ext_proc/ext_proc_messages.h +297 -0
  122. data/src/core/filter/filter_args.h +13 -2
  123. data/src/core/handshaker/http_connect/http_proxy_mapper.cc +30 -3
  124. data/src/core/handshaker/security/security_handshaker.cc +9 -6
  125. data/src/core/lib/channel/channel_stack.cc +3 -0
  126. data/src/core/lib/channel/channel_stack.h +2 -0
  127. data/src/core/lib/channel/channel_stack_builder_impl.cc +1 -2
  128. data/src/core/lib/channel/promise_based_filter.cc +132 -20
  129. data/src/core/lib/channel/promise_based_filter.h +42 -12
  130. data/src/core/lib/compression/compression_internal.cc +4 -5
  131. data/src/core/lib/debug/trace_flags.cc +8 -0
  132. data/src/core/lib/debug/trace_flags.h +3 -0
  133. data/src/core/lib/event_engine/ares_resolver.cc +54 -50
  134. data/src/core/lib/event_engine/ares_resolver.h +3 -2
  135. data/src/core/lib/event_engine/posix_engine/ev_epoll1_linux.cc +3 -3
  136. data/src/core/lib/event_engine/posix_engine/ev_poll_posix.cc +4 -10
  137. data/src/core/lib/event_engine/posix_engine/posix_endpoint.cc +74 -33
  138. data/src/core/lib/event_engine/posix_engine/posix_endpoint.h +4 -4
  139. data/src/core/lib/event_engine/posix_engine/posix_engine.cc +6 -2
  140. data/src/core/lib/event_engine/posix_engine/tcp_socket_utils.cc +3 -0
  141. data/src/core/lib/event_engine/posix_engine/tcp_socket_utils.h +3 -0
  142. data/src/core/lib/event_engine/windows/windows_endpoint.cc +1 -3
  143. data/src/core/lib/experiments/experiments.cc +60 -96
  144. data/src/core/lib/experiments/experiments.h +34 -50
  145. data/src/core/lib/iomgr/cfstream_handle.cc +2 -6
  146. data/src/core/lib/iomgr/endpoint_cfstream.cc +3 -10
  147. data/src/core/lib/iomgr/error.cc +2 -6
  148. data/src/core/lib/iomgr/error_cfstream.cc +1 -1
  149. data/src/core/lib/iomgr/ev_poll_posix.cc +5 -9
  150. data/src/core/lib/iomgr/event_engine_shims/closure.cc +1 -1
  151. data/src/core/lib/iomgr/event_engine_shims/endpoint.cc +1 -2
  152. data/src/core/lib/iomgr/event_engine_shims/tcp_client.cc +1 -2
  153. data/src/core/lib/iomgr/iomgr_posix.cc +0 -4
  154. data/src/core/lib/iomgr/iomgr_posix_cfstream.cc +0 -4
  155. data/src/core/lib/iomgr/iomgr_windows.cc +0 -4
  156. data/src/core/lib/iomgr/socket_factory_posix.h +1 -1
  157. data/src/core/lib/iomgr/socket_utils_posix.cc +3 -0
  158. data/src/core/lib/iomgr/socket_utils_posix.h +3 -0
  159. data/src/core/lib/iomgr/systemd_utils.cc +0 -1
  160. data/src/core/lib/iomgr/tcp_client.h +1 -1
  161. data/src/core/lib/iomgr/tcp_posix.cc +23 -15
  162. data/src/core/lib/iomgr/tcp_server.h +1 -1
  163. data/src/core/lib/iomgr/tcp_server_posix.cc +2 -3
  164. data/src/core/lib/iomgr/tcp_server_utils_posix.h +0 -1
  165. data/src/core/lib/iomgr/tcp_server_windows.cc +0 -1
  166. data/src/core/lib/iomgr/tcp_windows.cc +5 -10
  167. data/src/core/lib/iomgr/unix_sockets_posix.cc +2 -4
  168. data/src/core/lib/iomgr/unix_sockets_posix.h +3 -1
  169. data/src/core/lib/iomgr/vsock.h +3 -1
  170. data/src/core/lib/resource_quota/memory_quota.cc +1 -1
  171. data/src/core/lib/security/authorization/rbac_policy.cc +139 -56
  172. data/src/core/lib/security/authorization/rbac_policy.h +21 -3
  173. data/src/core/lib/surface/call.cc +3 -9
  174. data/src/core/lib/surface/call_utils.h +113 -0
  175. data/src/core/lib/surface/channel_create.cc +1 -1
  176. data/src/core/lib/surface/channel_create.h +0 -2
  177. data/src/core/lib/surface/channel_init.cc +77 -38
  178. data/src/core/lib/surface/channel_init.h +30 -16
  179. data/src/core/lib/surface/filter_stack_call.cc +5 -10
  180. data/src/core/lib/surface/init.cc +13 -27
  181. data/src/core/lib/surface/version.cc +1 -1
  182. data/src/core/lib/transport/error_utils.cc +0 -27
  183. data/src/core/lib/transport/error_utils.h +0 -16
  184. data/src/core/lib/transport/transport.h +7 -5
  185. data/src/core/load_balancing/pick_first/pick_first.cc +2 -0
  186. data/src/core/load_balancing/rls/rls.cc +28 -11
  187. data/src/core/load_balancing/weighted_round_robin/weighted_round_robin.cc +33 -42
  188. data/src/core/load_balancing/weighted_round_robin/weighted_round_robin.h +0 -2
  189. data/src/core/load_balancing/xds/cds.cc +3 -1
  190. data/src/core/load_balancing/xds/xds_cluster_impl.cc +0 -1
  191. data/src/core/mitigation_engine/mitigation.h +14 -5
  192. data/src/core/mitigation_engine/mitigation_engine.h +7 -3
  193. data/src/core/mitigation_engine/mitigation_provider.h +2 -1
  194. data/src/core/plugin_registry/grpc_plugin_registry.cc +39 -8
  195. data/src/core/resolver/dns/{event_engine/event_engine_client_channel_resolver.cc → dns_resolver.cc} +42 -41
  196. data/src/core/resolver/dns/{event_engine/event_engine_client_channel_resolver.h → dns_resolver.h} +8 -5
  197. data/src/core/resolver/dns/{event_engine/service_config_helper.cc → service_config_helper.cc} +1 -1
  198. data/src/core/resolver/dns/{event_engine/service_config_helper.h → service_config_helper.h} +3 -3
  199. data/src/core/resolver/endpoint_addresses.h +3 -0
  200. data/src/core/resolver/google_c2p/google_c2p_resolver.cc +2 -1
  201. data/src/core/resolver/xds/xds_resolver.cc +35 -9
  202. data/src/core/server/server.cc +2 -6
  203. data/src/core/server/server_config_selector.h +18 -3
  204. data/src/core/server/server_config_selector_filter.cc +178 -91
  205. data/src/core/server/server_config_selector_filter.h +58 -3
  206. data/src/core/server/xds_server_config_fetcher.cc +196 -129
  207. data/src/core/server/xds_server_config_fetcher.h +49 -0
  208. data/src/core/server/xds_server_config_fetcher_legacy.cc +41 -37
  209. data/src/core/telemetry/histogram.h +94 -14
  210. data/src/core/telemetry/instrument.cc +179 -74
  211. data/src/core/telemetry/instrument.h +78 -30
  212. data/src/core/telemetry/metrics.h +5 -0
  213. data/src/core/{resolver/dns/dns_resolver_plugin.h → telemetry/telemetry_label.h} +12 -8
  214. data/src/core/tsi/alts/crypt/aes_gcm.cc +10 -13
  215. data/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc +2 -2
  216. data/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h +3 -1
  217. data/src/core/tsi/ssl_telemetry_utils.cc +216 -259
  218. data/src/core/tsi/ssl_telemetry_utils.h +12 -2
  219. data/src/core/tsi/ssl_transport_security.cc +238 -80
  220. data/src/core/tsi/ssl_transport_security.h +63 -33
  221. data/src/core/tsi/tls_telemetry.cc +31 -0
  222. data/src/core/tsi/tls_telemetry.h +47 -0
  223. data/src/core/tsi/transport_security.cc +2 -2
  224. data/src/core/tsi/transport_security.h +1 -1
  225. data/src/core/tsi/transport_security_interface.h +1 -1
  226. data/src/core/{resolver/dns/native/dns_resolver.h → util/address_sorting_init.cc} +21 -8
  227. data/src/core/{resolver/dns/c_ares/grpc_ares_wrapper_posix.cc → util/address_sorting_init.h} +7 -8
  228. data/src/core/util/http_client/httpcli.cc +16 -53
  229. data/src/core/util/http_client/httpcli.h +0 -7
  230. data/src/core/util/http_client/httpcli_security_connector.cc +13 -8
  231. data/src/core/util/linux/cpu.cc +1 -1
  232. data/src/core/util/load_file.cc +3 -9
  233. data/src/core/util/load_file.h +3 -4
  234. data/src/core/util/matchers.h +8 -0
  235. data/src/core/util/posix/cpu.cc +1 -1
  236. data/src/core/util/status_helper.cc +0 -20
  237. data/src/core/util/status_helper.h +3 -14
  238. data/src/core/xds/grpc/xds_audit_logger_registry.cc +51 -31
  239. data/src/core/xds/grpc/xds_audit_logger_registry.h +17 -3
  240. data/src/core/xds/grpc/xds_bootstrap_grpc.cc +2 -10
  241. data/src/core/xds/grpc/xds_bootstrap_grpc.h +8 -6
  242. data/src/core/xds/grpc/xds_bootstrap_grpc_builder.cc +488 -0
  243. data/src/core/xds/grpc/xds_bootstrap_grpc_builder.h +97 -0
  244. data/src/core/xds/grpc/xds_certificate_provider.cc +7 -7
  245. data/src/core/xds/grpc/xds_client_grpc.cc +4 -8
  246. data/src/core/xds/grpc/xds_client_grpc.h +0 -4
  247. data/src/core/xds/grpc/xds_cluster.h +1 -0
  248. data/src/core/xds/grpc/xds_cluster_parser.cc +1 -5
  249. data/src/core/xds/grpc/xds_cluster_parser.h +0 -2
  250. data/src/core/xds/grpc/xds_cluster_specifier_plugin.cc +0 -2
  251. data/src/core/xds/grpc/xds_cluster_specifier_plugin.h +0 -2
  252. data/src/core/xds/grpc/xds_common_types.cc +0 -91
  253. data/src/core/xds/grpc/xds_common_types.h +1 -47
  254. data/src/core/xds/grpc/xds_common_types_parser.cc +84 -378
  255. data/src/core/xds/grpc/xds_common_types_parser.h +9 -11
  256. data/src/core/xds/grpc/xds_endpoint_parser.cc +0 -2
  257. data/src/core/xds/grpc/xds_grpc_service_parser.cc +177 -0
  258. data/src/core/{resolver/dns/c_ares/dns_resolver_ares.h → xds/grpc/xds_grpc_service_parser.h} +14 -9
  259. data/src/core/xds/grpc/xds_http_composite_filter.cc +26 -21
  260. data/src/core/xds/grpc/xds_http_composite_filter.h +4 -3
  261. data/src/core/xds/grpc/xds_http_ext_proc_filter.cc +418 -0
  262. data/src/core/xds/grpc/xds_http_ext_proc_filter.h +89 -0
  263. data/src/core/xds/grpc/xds_http_fault_filter.cc +9 -12
  264. data/src/core/xds/grpc/xds_http_fault_filter.h +1 -4
  265. data/src/core/xds/grpc/xds_http_filter.cc +2 -1
  266. data/src/core/xds/grpc/xds_http_filter.h +6 -5
  267. data/src/core/xds/grpc/xds_http_filter_registry.cc +10 -127
  268. data/src/core/xds/grpc/xds_http_filter_registry.h +7 -59
  269. data/src/core/xds/grpc/xds_http_gcp_authn_filter.cc +13 -14
  270. data/src/core/xds/grpc/xds_http_gcp_authn_filter.h +3 -2
  271. data/src/core/xds/grpc/xds_http_rbac_filter.cc +403 -25
  272. data/src/core/xds/grpc/xds_http_rbac_filter.h +1 -3
  273. data/src/core/xds/grpc/xds_http_stateful_session_filter.cc +8 -13
  274. data/src/core/xds/grpc/xds_http_stateful_session_filter.h +1 -4
  275. data/src/core/xds/grpc/xds_lb_policy_registry.cc +0 -292
  276. data/src/core/xds/grpc/xds_lb_policy_registry.h +7 -3
  277. data/src/core/xds/grpc/xds_listener.cc +2 -0
  278. data/src/core/xds/grpc/xds_listener.h +5 -0
  279. data/src/core/xds/grpc/xds_listener_parser.cc +16 -11
  280. data/src/core/xds/grpc/xds_matcher_parse.h +3 -1
  281. data/src/core/xds/grpc/xds_metadata_parser.cc +6 -1
  282. data/src/core/xds/grpc/xds_metadata_parser.h +2 -0
  283. data/src/core/xds/grpc/xds_route_config.cc +2 -0
  284. data/src/core/xds/grpc/xds_route_config.h +3 -1
  285. data/src/core/xds/grpc/xds_route_config_parser.cc +13 -79
  286. data/src/core/xds/grpc/xds_route_config_parser.h +0 -3
  287. data/src/core/xds/grpc/xds_routing.cc +116 -69
  288. data/src/core/xds/grpc/xds_routing.h +19 -8
  289. data/src/core/xds/grpc/xds_tls_context.cc +117 -0
  290. data/src/core/xds/grpc/xds_tls_context.h +76 -0
  291. data/src/core/xds/grpc/xds_tls_context_parser.cc +275 -0
  292. data/src/core/xds/grpc/xds_tls_context_parser.h +35 -0
  293. data/src/core/xds/grpc/xds_transport_grpc.cc +41 -38
  294. data/src/core/xds/grpc/xds_transport_grpc.h +4 -2
  295. data/src/ruby/ext/grpc/rb_call.c +26 -15
  296. data/src/ruby/ext/grpc/rb_completion_queue.c +6 -5
  297. data/src/ruby/ext/grpc/rb_grpc_imports.generated.c +2 -0
  298. data/src/ruby/ext/grpc/rb_grpc_imports.generated.h +3 -0
  299. data/src/ruby/lib/grpc/version.rb +1 -1
  300. data/src/ruby/pb/test/client.rb +5 -4
  301. data/src/ruby/spec/call_spec.rb +14 -0
  302. data/src/ruby/spec/generic/server_interceptors_spec.rb +3 -6
  303. metadata +50 -35
  304. data/src/core/ext/upbdefs-gen/src/proto/grpc/channelz/channelz.upbdefs.c +0 -716
  305. data/src/core/lib/iomgr/resolve_address.cc +0 -59
  306. data/src/core/lib/iomgr/resolve_address.h +0 -125
  307. data/src/core/lib/iomgr/resolve_address_impl.h +0 -58
  308. data/src/core/lib/iomgr/resolve_address_posix.cc +0 -184
  309. data/src/core/lib/iomgr/resolve_address_posix.h +0 -78
  310. data/src/core/lib/iomgr/resolve_address_windows.cc +0 -169
  311. data/src/core/lib/iomgr/resolve_address_windows.h +0 -76
  312. data/src/core/resolver/dns/c_ares/dns_resolver_ares.cc +0 -781
  313. data/src/core/resolver/dns/c_ares/grpc_ares_ev_driver.h +0 -93
  314. data/src/core/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc +0 -203
  315. data/src/core/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc +0 -838
  316. data/src/core/resolver/dns/c_ares/grpc_ares_wrapper.cc +0 -1237
  317. data/src/core/resolver/dns/c_ares/grpc_ares_wrapper.h +0 -130
  318. data/src/core/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc +0 -35
  319. data/src/core/resolver/dns/dns_resolver_plugin.cc +0 -64
  320. data/src/core/resolver/dns/native/dns_resolver.cc +0 -173
  321. data/src/core/util/json/json_util.cc +0 -101
  322. data/src/core/util/json/json_util.h +0 -163
@@ -0,0 +1,525 @@
1
+ //
2
+ // Copyright 2026 gRPC authors.
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ //
16
+
17
+ #include "src/core/credentials/call/gdch_service_account/gdch_service_account_credentials.h"
18
+
19
+ #include <grpc/credentials.h>
20
+ #include <grpc/support/alloc.h>
21
+ #include <grpc/support/string_util.h>
22
+ #include <openssl/bio.h>
23
+ #include <openssl/bn.h>
24
+ #include <openssl/crypto.h>
25
+ #include <openssl/ecdsa.h>
26
+ #include <openssl/err.h>
27
+ #include <openssl/evp.h>
28
+ #include <openssl/pem.h>
29
+
30
+ #include <array>
31
+ #include <memory>
32
+ #include <optional>
33
+ #include <string>
34
+ #include <utility>
35
+
36
+ #include "src/core/credentials/transport/transport_credentials.h"
37
+ #include "src/core/lib/iomgr/closure.h"
38
+ #include "src/core/lib/iomgr/exec_ctx.h"
39
+ #include "src/core/lib/slice/slice.h"
40
+ #include "src/core/util/http_client/httpcli.h"
41
+ #include "src/core/util/http_client/httpcli_ssl_credentials.h"
42
+ #include "src/core/util/http_client/parser.h"
43
+ #include "src/core/util/json/json.h"
44
+ #include "src/core/util/json/json_object_loader.h"
45
+ #include "src/core/util/json/json_reader.h"
46
+ #include "src/core/util/json/json_writer.h"
47
+ #include "src/core/util/ref_counted_ptr.h"
48
+ #include "src/core/util/status_helper.h"
49
+ #include "src/core/util/time.h"
50
+ #include "src/core/util/unique_type_name.h"
51
+ #include "src/core/util/uri.h"
52
+ #include "src/core/util/validation_errors.h"
53
+ #include "absl/log/log.h"
54
+ #include "absl/status/status.h"
55
+ #include "absl/status/statusor.h"
56
+ #include "absl/strings/escaping.h"
57
+ #include "absl/strings/str_cat.h"
58
+ #include "absl/strings/str_format.h"
59
+ #include "absl/strings/string_view.h"
60
+
61
+ namespace grpc_core {
62
+ namespace {
63
+
64
+ constexpr const char* kExpectedType = "gdch_service_account";
65
+ constexpr const char* kExpectedFormatVersion = "1";
66
+ constexpr Duration kTokenLifetime = Duration::Hours(1);
67
+
68
+ struct OpenSslDeleter {
69
+ void operator()(EVP_MD_CTX* ptr) {
70
+ // The name of the function to free an EVP_MD_CTX changed in OpenSSL 1.1.0.
71
+ #if (OPENSSL_VERSION_NUMBER < 0x10100000L) // Older than version 1.1.0.
72
+ EVP_MD_CTX_destroy(ptr);
73
+ #else
74
+ EVP_MD_CTX_free(ptr);
75
+ #endif
76
+ }
77
+
78
+ void operator()(EVP_PKEY* ptr) { EVP_PKEY_free(ptr); }
79
+ void operator()(BIO* ptr) { BIO_free(ptr); }
80
+ void operator()(ECDSA_SIG* ptr) { ECDSA_SIG_free(ptr); }
81
+ };
82
+
83
+ std::unique_ptr<EVP_MD_CTX, OpenSslDeleter> GetDigestCtx() {
84
+ // The name of the function to create an EVP_MD_CTX changed in OpenSSL 1.1.0.
85
+ #if (OPENSSL_VERSION_NUMBER < 0x10100000L) // Older than version 1.1.0.
86
+ return std::unique_ptr<EVP_MD_CTX, OpenSslDeleter>(EVP_MD_CTX_create());
87
+ #else
88
+ return std::unique_ptr<EVP_MD_CTX, OpenSslDeleter>(EVP_MD_CTX_new());
89
+ #endif
90
+ }
91
+
92
+ std::string CaptureSslErrors() {
93
+ std::string msg;
94
+ bool is_first = true;
95
+ while (unsigned long code = ERR_get_error()) {
96
+ // OpenSSL guarantees that 256 bytes is enough:
97
+ // https://www.openssl.org/docs/man1.1.1/man3/ERR_error_string_n.html
98
+ // https://www.openssl.org/docs/man1.0.2/man3/ERR_error_string_n.html
99
+ // we could not find a macro or constant to replace the 256 literal.
100
+ constexpr size_t kMaxOpenSslErrorLength = 256;
101
+ std::array<char, kMaxOpenSslErrorLength> buf{};
102
+ ERR_error_string_n(code, buf.data(), buf.size());
103
+ if (!is_first) StrAppend(msg, ", ");
104
+ StrAppend(msg, buf.data());
105
+ is_first = false;
106
+ }
107
+ return msg;
108
+ }
109
+
110
+ #if OPENSSL_VERSION_NUMBER < 0x10100000L
111
+ void ECDSA_SIG_get0(const ECDSA_SIG* sig, const BIGNUM** pr,
112
+ const BIGNUM** ps) {
113
+ if (pr != nullptr) *pr = sig->r;
114
+ if (ps != nullptr) *ps = sig->s;
115
+ }
116
+
117
+ int BN_bn2binpad(const BIGNUM* a, unsigned char* to, int len) {
118
+ int n = BN_num_bytes(a);
119
+ if (n > len) {
120
+ return -1; // Buffer too small
121
+ }
122
+ // Zero out the padding area
123
+ memset(to, 0, len - n);
124
+ // Write the number at the end of the buffer
125
+ BN_bn2bin(a, to + (len - n));
126
+ return len;
127
+ }
128
+ #endif
129
+
130
+ absl::StatusOr<std::string> DERToRawSignature(absl::string_view der_sig,
131
+ int coord_size) {
132
+ if (der_sig.empty()) {
133
+ return absl::InternalError("Input DER signature is empty");
134
+ }
135
+
136
+ const unsigned char* der_sig_ptr =
137
+ reinterpret_cast<const unsigned char*>(der_sig.data());
138
+ std::unique_ptr<ECDSA_SIG, OpenSslDeleter> ecdsa_sig(
139
+ d2i_ECDSA_SIG(nullptr, &der_sig_ptr, der_sig.size()));
140
+
141
+ if (ecdsa_sig == nullptr) {
142
+ char err_buf[256];
143
+ ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
144
+ return absl::InternalError(
145
+ absl::StrCat("Error parsing DER signature: ", err_buf));
146
+ }
147
+
148
+ const BIGNUM* r;
149
+ const BIGNUM* s;
150
+ ECDSA_SIG_get0(ecdsa_sig.get(), &r, &s);
151
+
152
+ if (r == nullptr || s == nullptr) {
153
+ return absl::InternalError("Error: Could not get r or s from ECDSA_SIG");
154
+ }
155
+
156
+ std::string raw_sig(2 * coord_size, '\0');
157
+ unsigned char* raw_sig_ptr = reinterpret_cast<unsigned char*>(raw_sig.data());
158
+
159
+ constexpr const char* kErrorMessage =
160
+ R"""(Error converting %s to binary (expected %d bytes, got %d): %s)""";
161
+ // Convert r to binary, padded to coord_size.
162
+ int r_len = BN_bn2binpad(r, &raw_sig_ptr[0], coord_size);
163
+ if (r_len != coord_size) {
164
+ char err_buf[256];
165
+ ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
166
+ std::string err_msg =
167
+ absl::StrFormat(kErrorMessage, "r", coord_size, r_len, err_buf);
168
+ return absl::InternalError(err_msg);
169
+ }
170
+
171
+ // Convert s to binary, padded to coord_size.
172
+ int s_len = BN_bn2binpad(s, &raw_sig_ptr[coord_size], coord_size);
173
+ if (s_len != coord_size) {
174
+ char err_buf[256];
175
+ ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
176
+ std::string err_msg =
177
+ absl::StrFormat(kErrorMessage, "s", coord_size, s_len, err_buf);
178
+ return absl::InternalError(err_msg);
179
+ }
180
+
181
+ return raw_sig;
182
+ }
183
+
184
+ struct Response {
185
+ static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
186
+ static const auto* loader =
187
+ JsonObjectLoader<Response>()
188
+ .Field("access_token", &Response::access_token)
189
+ .Finish();
190
+ return loader;
191
+ }
192
+
193
+ std::string access_token;
194
+ };
195
+
196
+ struct Info {
197
+ static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
198
+ static const auto* loader =
199
+ JsonObjectLoader<Info>()
200
+ .Field("type", &Info::type)
201
+ .Field("format_version", &Info::format_version)
202
+ .Field("project", &Info::project_id)
203
+ .Field("private_key_id", &Info::private_key_id)
204
+ .Field("private_key", &Info::private_key)
205
+ .Field("name", &Info::service_identity_name)
206
+ .OptionalField("ca_cert_path", &Info::ca_cert_path)
207
+ .Field("token_uri", &Info::token_uri)
208
+ .Finish();
209
+ return loader;
210
+ }
211
+
212
+ void JsonPostLoad(const Json&, const JsonArgs&, ValidationErrors* errors);
213
+
214
+ std::string type;
215
+ std::string format_version;
216
+ std::string project_id;
217
+ std::string private_key_id;
218
+ std::string private_key;
219
+ std::string service_identity_name;
220
+ std::optional<std::string> ca_cert_path;
221
+ std::string token_uri;
222
+ };
223
+
224
+ void Info::JsonPostLoad(const Json& json, const JsonArgs& /*args*/,
225
+ ValidationErrors* errors) {
226
+ auto check_non_empty = [&](absl::string_view field_name,
227
+ absl::string_view field_value) {
228
+ ValidationErrors::ScopedField field(errors, absl::StrCat(".", field_name));
229
+ if (!errors->FieldHasErrors() && field_value.empty()) {
230
+ errors->AddError("field must not be empty");
231
+ }
232
+ };
233
+
234
+ auto check_expected_value = [&](absl::string_view field_name,
235
+ absl::string_view field_value,
236
+ absl::string_view expected_value) {
237
+ ValidationErrors::ScopedField field(errors, absl::StrCat(".", field_name));
238
+ if (!errors->FieldHasErrors() && field_value != expected_value) {
239
+ errors->AddError(absl::StrCat("field must be ", expected_value));
240
+ }
241
+ };
242
+
243
+ check_expected_value("type", type, kExpectedType);
244
+ check_expected_value("format_version", format_version,
245
+ kExpectedFormatVersion);
246
+ check_non_empty("project", project_id);
247
+ check_non_empty("private_key_id", private_key_id);
248
+ check_non_empty("private_key", private_key);
249
+ check_non_empty("name", service_identity_name);
250
+ check_non_empty("token_uri", token_uri);
251
+
252
+ if (ca_cert_path.has_value()) {
253
+ check_non_empty("ca_cert_path", *ca_cert_path);
254
+ }
255
+ }
256
+
257
+ } // namespace
258
+
259
+ absl::StatusOr<std::string> GDCHServiceAccountCredentials::SignUsingSha256(
260
+ absl::string_view str, absl::string_view pem_contents,
261
+ SignatureFormat format) {
262
+ ERR_clear_error();
263
+ std::unique_ptr<BIO, OpenSslDeleter> pem_buffer(BIO_new_mem_buf(
264
+ pem_contents.data(), static_cast<int>(pem_contents.length())));
265
+ if (pem_buffer == nullptr) {
266
+ return absl::InternalError(absl::StrCat(
267
+ "Invalid ServiceAccountCredentials could not create PEM buffer: ",
268
+ CaptureSslErrors()));
269
+ }
270
+
271
+ std::unique_ptr<EVP_PKEY, OpenSslDeleter> private_key(PEM_read_bio_PrivateKey(
272
+ pem_buffer.get(),
273
+ nullptr, // EVP_PKEY **x
274
+ nullptr, // pem_password_cb *cb -- a custom callback.
275
+ // void *u -- this represents the password for the PEM (only
276
+ // applicable for formats such as PKCS12 (.p12 files) that use
277
+ // a password, which we don't currently support.
278
+ nullptr));
279
+ if (private_key == nullptr) {
280
+ return absl::InternalError(
281
+ absl::StrCat("Invalid ServiceAccountCredentials could not parse PEM to "
282
+ "get private key: ",
283
+ CaptureSslErrors()));
284
+ }
285
+
286
+ std::unique_ptr<EVP_MD_CTX, OpenSslDeleter> digest_ctx = GetDigestCtx();
287
+ if (digest_ctx == nullptr) {
288
+ return absl::InternalError(
289
+ absl::StrCat("Invalid ServiceAccountCredentials could not create "
290
+ "context for OpenSSL digest: ",
291
+ CaptureSslErrors()));
292
+ }
293
+
294
+ constexpr int kOpenSslSuccess = 1;
295
+ if (EVP_DigestSignInit(digest_ctx.get(), nullptr, EVP_sha256(), nullptr,
296
+ private_key.get()) != kOpenSslSuccess) {
297
+ return absl::InternalError(
298
+ absl::StrCat("Invalid ServiceAccountCredentials - could not initialize "
299
+ "signing digest: ",
300
+ CaptureSslErrors()));
301
+ }
302
+
303
+ if (EVP_DigestSignUpdate(digest_ctx.get(), str.data(), str.size()) !=
304
+ kOpenSslSuccess) {
305
+ return absl::InternalError(absl::StrCat(
306
+ "Invalid ServiceAccountCredentials - could not sign blob: ",
307
+ CaptureSslErrors()));
308
+ }
309
+
310
+ // The signed SHA256 size depends on the size (the experts say "modulus") of
311
+ // they key. First query the size:
312
+ std::size_t actual_len = 0;
313
+ if (EVP_DigestSignFinal(digest_ctx.get(), nullptr, &actual_len) !=
314
+ kOpenSslSuccess) {
315
+ return absl::InternalError(absl::StrCat(
316
+ "Invalid ServiceAccountCredentials - could not sign blob: ",
317
+ CaptureSslErrors()));
318
+ }
319
+
320
+ // Then compute the actual signed digest. Note that OpenSSL requires a
321
+ // `unsigned char*` buffer, so we feed it that.
322
+ std::string buffer(actual_len, '\0');
323
+ if (EVP_DigestSignFinal(digest_ctx.get(),
324
+ reinterpret_cast<unsigned char*>(buffer.data()),
325
+ &actual_len) != kOpenSslSuccess) {
326
+ return absl::InternalError(absl::StrCat(
327
+ "Invalid ServiceAccountCredentials - could not sign blob: ",
328
+ CaptureSslErrors()));
329
+ }
330
+ buffer.resize(actual_len);
331
+
332
+ if (format == SignatureFormat::kDER) {
333
+ return buffer;
334
+ }
335
+
336
+ return DERToRawSignature(buffer, 32);
337
+ }
338
+
339
+ absl::StatusOr<RefCountedPtr<GDCHServiceAccountCredentials>>
340
+ GDCHServiceAccountCredentials::Create(absl::string_view key_file_contents,
341
+ std::string audience) {
342
+ absl::StatusOr<Json> json = JsonParse(key_file_contents);
343
+ if (!json.ok()) return json.status();
344
+ absl::StatusOr<Info> info = LoadFromJson<Info>(*json);
345
+ if (!info.ok()) return info.status();
346
+ absl::StatusOr<URI> token_url = URI::Parse(info->token_uri);
347
+ if (!token_url.ok()) return token_url.status();
348
+ std::string service_account_identity =
349
+ absl::StrCat("system:serviceaccount:", info->project_id, ":",
350
+ info->service_identity_name);
351
+ return MakeRefCounted<GDCHServiceAccountCredentials>(
352
+ std::move(info->private_key_id), std::move(info->private_key),
353
+ std::move(service_account_identity), std::move(info->ca_cert_path),
354
+ *std::move(token_url), std::move(audience));
355
+ }
356
+
357
+ GDCHServiceAccountCredentials::GDCHServiceAccountCredentials(
358
+ std::string private_key_id, std::string private_key,
359
+ std::string service_account_identity,
360
+ std::optional<std::string> ca_cert_path, URI token_url,
361
+ std::string audience)
362
+ : private_key_id_(std::move(private_key_id)),
363
+ private_key_(std::move(private_key)),
364
+ service_account_identity_(std::move(service_account_identity)),
365
+ ca_cert_path_(std::move(ca_cert_path)),
366
+ token_url_(std::move(token_url)),
367
+ audience_(std::move(audience)) {}
368
+
369
+ GDCHServiceAccountCredentials::AssertionComponents
370
+ GDCHServiceAccountCredentials::CreateAssertionComponents(Timestamp now) const {
371
+ Json header = Json::FromObject({
372
+ {"alg", Json::FromString("ES256")},
373
+ {"typ", Json::FromString("JWT")},
374
+ {"kid", Json::FromString(private_key_id_)},
375
+ });
376
+
377
+ Timestamp expiration = now + kTokenLifetime;
378
+
379
+ const int64_t now_from_epoch = now.as_timespec(GPR_CLOCK_REALTIME).tv_sec;
380
+ const int64_t expiration_from_epoch =
381
+ expiration.as_timespec(GPR_CLOCK_REALTIME).tv_sec;
382
+
383
+ Json claim = Json::FromObject({
384
+ {"iss", Json::FromString(service_account_identity_)},
385
+ {"sub", Json::FromString(service_account_identity_)},
386
+ {"aud", Json::FromString(token_url_.ToString())},
387
+ {"iat", Json::FromNumber(now_from_epoch)},
388
+ {"exp", Json::FromNumber(expiration_from_epoch)},
389
+ });
390
+
391
+ return {JsonDump(header), JsonDump(claim)};
392
+ }
393
+
394
+ absl::StatusOr<std::string> GDCHServiceAccountCredentials::MakeJWTAssertion(
395
+ absl::string_view header, absl::string_view claim,
396
+ absl::string_view pem_contents, SignatureFormat format) {
397
+ const std::string body = absl::StrCat(absl::WebSafeBase64Escape(header), ".",
398
+ absl::WebSafeBase64Escape(claim));
399
+ absl::StatusOr<std::string> signature =
400
+ SignUsingSha256(body, pem_contents, format);
401
+ if (!signature.ok()) return signature.status();
402
+ return absl::StrCat(body, ".", absl::WebSafeBase64Escape(*signature));
403
+ }
404
+
405
+ absl::StatusOr<std::string> GDCHServiceAccountCredentials::CreateRequestBody()
406
+ const {
407
+ AssertionComponents components = CreateAssertionComponents(Timestamp::Now());
408
+ absl::StatusOr<std::string> jwt = MakeJWTAssertion(
409
+ components.header, components.claim, private_key_, SignatureFormat::kRaw);
410
+ if (!jwt.ok()) return jwt.status();
411
+
412
+ Json payload = Json::FromObject({
413
+ {"grant_type",
414
+ Json::FromString("urn:ietf:params:oauth:token-type:token-exchange")},
415
+ {"audience", Json::FromString(audience_)},
416
+ {"requested_token_type",
417
+ Json::FromString("urn:ietf:params:oauth:token-type:access_token")},
418
+ {"subject_token", Json::FromString(std::move(*jwt))},
419
+ {"subject_token_type",
420
+ Json::FromString("urn:k8s:params:oauth:token-type:serviceaccount")},
421
+ });
422
+
423
+ return JsonDump(payload);
424
+ }
425
+
426
+ void GDCHServiceAccountCredentials::GrpcDeleter::operator()(
427
+ grpc_http_request* ptr) {
428
+ grpc_http_request_destroy(ptr);
429
+ delete ptr;
430
+ }
431
+
432
+ absl::StatusOr<GDCHServiceAccountCredentials::GrpcHttpRequestUniquePtr>
433
+ GDCHServiceAccountCredentials::FormatHttpRequest() const {
434
+ absl::StatusOr<std::string> body = CreateRequestBody();
435
+ if (!body.ok()) return body.status();
436
+
437
+ GrpcHttpRequestUniquePtr request(new grpc_http_request);
438
+ memset(request.get(), 0, sizeof(grpc_http_request));
439
+ request->path =
440
+ gpr_strdup(token_url_.path().empty() ? "/" : token_url_.path().c_str());
441
+ request->hdr_count = 1;
442
+ request->hdrs =
443
+ static_cast<grpc_http_header*>(gpr_malloc(sizeof(grpc_http_header)));
444
+ request->hdrs[0].key = gpr_strdup("content-type");
445
+ request->hdrs[0].value = gpr_strdup("application/json");
446
+
447
+ request->body_length = static_cast<int>(body->size());
448
+ request->body = gpr_strdup(body->c_str());
449
+ return request;
450
+ }
451
+
452
+ absl::StatusOr<std::string> GDCHServiceAccountCredentials::ParseHttpResponse(
453
+ absl::string_view response_body) {
454
+ absl::StatusOr<Json> response_json = JsonParse(response_body);
455
+ if (!response_json.ok()) {
456
+ return absl::InternalError(
457
+ "The format of response is not a valid json object.");
458
+ }
459
+ auto response = LoadFromJson<Response>(*response_json);
460
+ if (!response.ok()) return response.status();
461
+ return std::move(response->access_token);
462
+ }
463
+
464
+ UniqueTypeName GDCHServiceAccountCredentials::type() const {
465
+ return GRPC_UNIQUE_TYPE_NAME_HERE("GDCHServiceAccountCredentials");
466
+ }
467
+
468
+ std::string GDCHServiceAccountCredentials::debug_string() {
469
+ return absl::StrCat("GDCHServiceAccountCredentials{Audience:", audience_,
470
+ "}");
471
+ }
472
+
473
+ OrphanablePtr<HttpRequest> GDCHServiceAccountCredentials::StartHttpRequest(
474
+ grpc_polling_entity* pollent, Timestamp deadline,
475
+ grpc_http_response* response, grpc_closure* on_complete) {
476
+ absl::StatusOr<GrpcHttpRequestUniquePtr> request = FormatHttpRequest();
477
+ if (!request.ok()) {
478
+ ExecCtx::Run(DEBUG_LOCATION, on_complete, request.status());
479
+ return nullptr;
480
+ }
481
+ RefCountedPtr<grpc_channel_credentials> http_request_creds;
482
+ if (token_url_.scheme() == "http") {
483
+ http_request_creds = RefCountedPtr<grpc_channel_credentials>(
484
+ grpc_insecure_credentials_create());
485
+ } else {
486
+ http_request_creds = CreateHttpRequestSSLCredentials();
487
+ }
488
+ OrphanablePtr<HttpRequest> http_request = HttpRequest::Post(
489
+ token_url_, /*args=*/nullptr, pollent, request->get(), deadline,
490
+ on_complete, response, std::move(http_request_creds));
491
+ http_request->Start();
492
+ return http_request;
493
+ }
494
+
495
+ absl::StatusOr<RefCountedPtr<TokenFetcherCredentials::Token>>
496
+ GDCHServiceAccountCredentials::ExtractToken(
497
+ const grpc_http_response& response) {
498
+ absl::string_view response_body(response.body, response.body_length);
499
+ absl::StatusOr<std::string> token_str = ParseHttpResponse(response_body);
500
+ if (!token_str.ok()) return token_str.status();
501
+ return MakeRefCounted<Token>(
502
+ Slice::FromCopiedString(absl::StrCat("Bearer ", *token_str)),
503
+ Timestamp::Now() + kTokenLifetime);
504
+ }
505
+
506
+ } // namespace grpc_core
507
+
508
+ grpc_call_credentials* grpc_gdch_service_account_credentials_create(
509
+ const char* json_string, const char* audience_string) {
510
+ if (json_string == nullptr || audience_string == nullptr) {
511
+ LOG(ERROR) << "GDCH service account credentials creation failed: "
512
+ << "json_string or audience_string is null.";
513
+ return nullptr;
514
+ }
515
+ absl::StatusOr<
516
+ grpc_core::RefCountedPtr<grpc_core::GDCHServiceAccountCredentials>>
517
+ creds = grpc_core::GDCHServiceAccountCredentials::Create(json_string,
518
+ audience_string);
519
+ if (!creds.ok()) {
520
+ LOG(ERROR) << "GDCH service account credentials creation failed. Error: "
521
+ << grpc_core::StatusToString(creds.status());
522
+ return nullptr;
523
+ }
524
+ return creds->release();
525
+ }
@@ -0,0 +1,138 @@
1
+ //
2
+ // Copyright 2026 gRPC authors.
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ //
16
+
17
+ #ifndef GRPC_SRC_CORE_CREDENTIALS_CALL_GDCH_SERVICE_ACCOUNT_GDCH_SERVICE_ACCOUNT_CREDENTIALS_H
18
+ #define GRPC_SRC_CORE_CREDENTIALS_CALL_GDCH_SERVICE_ACCOUNT_GDCH_SERVICE_ACCOUNT_CREDENTIALS_H
19
+
20
+ #include <memory>
21
+ #include <optional>
22
+ #include <string>
23
+
24
+ #include "src/core/credentials/call/token_fetcher/token_fetcher_credentials.h"
25
+ #include "src/core/util/http_client/httpcli.h"
26
+ #include "src/core/util/orphanable.h"
27
+ #include "src/core/util/ref_counted_ptr.h"
28
+ #include "src/core/util/time.h"
29
+ #include "src/core/util/unique_type_name.h"
30
+ #include "src/core/util/uri.h"
31
+ #include "absl/status/statusor.h"
32
+ #include "absl/strings/string_view.h"
33
+
34
+ namespace grpc_core {
35
+
36
+ // GDCH Service Account credentials.
37
+ //
38
+ // Uses the service account to create a JWT assertion which is then exchanged
39
+ // for a STS bearer token.
40
+ //
41
+ // JSON Schema for the service account key file:
42
+ // {
43
+ // "type": "object",
44
+ // "properties": {
45
+ // "type": { "type": "string", "const": "gdch_service_account" },
46
+ // "format_version": { "type": "string", "const": "1" },
47
+ // "project": { "type": "string" },
48
+ // "private_key_id": { "type": "string" },
49
+ // "private_key": { "type": "string" },
50
+ // "name": { "type": "string" },
51
+ // "ca_cert_path": { "type": "string" },
52
+ // "token_uri": { "type": "string" }
53
+ // },
54
+ // "required": [
55
+ // "type",
56
+ // "format_version",
57
+ // "project",
58
+ // "private_key_id",
59
+ // "private_key",
60
+ // "name",
61
+ // "token_uri"
62
+ // ]
63
+ // }
64
+ class GDCHServiceAccountCredentials final : public HttpTokenFetcherCredentials {
65
+ public:
66
+ static absl::StatusOr<RefCountedPtr<GDCHServiceAccountCredentials>> Create(
67
+ absl::string_view key_file_contents, std::string audience);
68
+
69
+ GDCHServiceAccountCredentials(std::string private_key_id,
70
+ std::string private_key,
71
+ std::string service_account_identity,
72
+ std::optional<std::string> ca_cert_path,
73
+ URI token_url, std::string audience);
74
+
75
+ const std::optional<std::string>& ca_cert_path() const {
76
+ return ca_cert_path_;
77
+ }
78
+
79
+ std::string debug_string() override;
80
+
81
+ UniqueTypeName type() const override;
82
+
83
+ OrphanablePtr<HttpRequest> StartHttpRequest(
84
+ grpc_polling_entity* pollent, Timestamp deadline,
85
+ grpc_http_response* response, grpc_closure* on_complete) override;
86
+
87
+ absl::StatusOr<RefCountedPtr<Token>> ExtractToken(
88
+ const grpc_http_response& response) override;
89
+
90
+ private:
91
+ friend class GDCHServiceAccountCredentialsTest;
92
+
93
+ // OpenSSL outputs DER format signatures by default. RFC-7515 (JWT/JWS)
94
+ // specifies the Raw format should be used.
95
+ enum class SignatureFormat { kDER, kRaw };
96
+
97
+ struct AssertionComponents {
98
+ std::string header;
99
+ std::string claim;
100
+ };
101
+
102
+ struct GrpcDeleter {
103
+ void operator()(grpc_http_request* ptr);
104
+ };
105
+ using GrpcHttpRequestUniquePtr =
106
+ std::unique_ptr<grpc_http_request, GrpcDeleter>;
107
+
108
+ // Signs a string with the private key from a PEM container.
109
+ //
110
+ // @return the signature as an *unencoded* string.
111
+ static absl::StatusOr<std::string> SignUsingSha256(
112
+ absl::string_view str, absl::string_view pem_contents,
113
+ SignatureFormat format);
114
+
115
+ AssertionComponents CreateAssertionComponents(Timestamp now) const;
116
+
117
+ static absl::StatusOr<std::string> MakeJWTAssertion(
118
+ absl::string_view header, absl::string_view claim,
119
+ absl::string_view pem_contents, SignatureFormat format);
120
+
121
+ absl::StatusOr<std::string> CreateRequestBody() const;
122
+
123
+ absl::StatusOr<GrpcHttpRequestUniquePtr> FormatHttpRequest() const;
124
+
125
+ static absl::StatusOr<std::string> ParseHttpResponse(
126
+ absl::string_view response_body);
127
+
128
+ std::string private_key_id_;
129
+ std::string private_key_;
130
+ std::string service_account_identity_;
131
+ std::optional<std::string> ca_cert_path_;
132
+ URI token_url_;
133
+ std::string audience_;
134
+ };
135
+
136
+ } // namespace grpc_core
137
+
138
+ #endif // GRPC_SRC_CORE_CREDENTIALS_CALL_GDCH_SERVICE_ACCOUNT_GDCH_SERVICE_ACCOUNT_CREDENTIALS_H
@@ -29,6 +29,7 @@
29
29
  #define GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT "service_account"
30
30
  #define GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER "authorized_user"
31
31
  #define GRPC_AUTH_JSON_TYPE_EXTERNAL_ACCOUNT "external_account"
32
+ #define GRPC_AUTH_JSON_TYPE_GDCH_SERVICE_ACCOUNT "gdch_service_account"
32
33
 
33
34
  // Gets a child property from a json node.
34
35
  const char* grpc_json_get_string_property(const grpc_core::Json& json,
@@ -244,7 +244,7 @@ char* compute_and_encode_signature(const grpc_auth_json_key* json_key,
244
244
  const EVP_MD* md = openssl_digest_from_algorithm(signature_algorithm);
245
245
  EVP_MD_CTX* md_ctx = nullptr;
246
246
  #if OPENSSL_VERSION_NUMBER < 0x30000000L
247
- EVP_PKEY* key = EVP_PKEY_new();
247
+ EVP_PKEY* key = nullptr;
248
248
  #endif
249
249
  size_t sig_len = 0;
250
250
  unsigned char* sig = nullptr;
@@ -256,7 +256,15 @@ char* compute_and_encode_signature(const grpc_auth_json_key* json_key,
256
256
  goto end;
257
257
  }
258
258
  #if OPENSSL_VERSION_NUMBER < 0x30000000L
259
- EVP_PKEY_set1_RSA(key, json_key->private_key);
259
+ key = EVP_PKEY_new();
260
+ if (key == nullptr) {
261
+ LOG(ERROR) << "Could not create EVP_PKEY";
262
+ goto end;
263
+ }
264
+ if (EVP_PKEY_set1_RSA(key, json_key->private_key) == 0) {
265
+ LOG(ERROR) << "Could not set RSA key";
266
+ goto end;
267
+ }
260
268
  if (EVP_DigestSignInit(md_ctx, nullptr, md, nullptr, key) != 1) {
261
269
  #else
262
270
  if (EVP_DigestSignInit(md_ctx, nullptr, md, nullptr, json_key->private_key) !=