From ca27b27bf889a4c5fb167f517e37986578a58804 Mon Sep 17 00:00:00 2001 From: Torrey Payne <11740989+torreypayne@users.noreply.github.com> Date: Fri, 7 Aug 2026 22:55:15 +0000 Subject: [PATCH] test: validate PQC TLS cryptography in showcase integration tests --- shared/Gemfile | 1 + shared/test/showcase/pqc_test.rb | 44 +++++++++++++++++++++++++++++ shared/test/showcase/test_helper.rb | 35 +++++++++++++++++++---- 3 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 shared/test/showcase/pqc_test.rb diff --git a/shared/Gemfile b/shared/Gemfile index 22c8f7b6b..444b89c5e 100644 --- a/shared/Gemfile +++ b/shared/Gemfile @@ -17,3 +17,4 @@ gem "minitest-autotest", "~> 1.0" gem "minitest-focus", "~> 1.0" gem "ostruct", "0.6.3" gem "rake", ">= 12.0" +gem 'fiddle' diff --git a/shared/test/showcase/pqc_test.rb b/shared/test/showcase/pqc_test.rb new file mode 100644 index 000000000..8abae11b7 --- /dev/null +++ b/shared/test/showcase/pqc_test.rb @@ -0,0 +1,44 @@ +require "test_helper" +require "fiddle" +require "openssl" + +module PQProbe + LIBSSL = Fiddle.dlopen("libssl.so.3") + GET_GROUP_NAME = Fiddle::Function.new( + LIBSSL["SSL_get0_group_name"], + [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP + ) + DATA_OFFSET = 32 + + def self.group_of(ssl_socket) + value_addr = Fiddle.dlwrap(ssl_socket) + ssl_ptr = Fiddle::Pointer.new(value_addr)[DATA_OFFSET, 8].unpack1("Q") + return "(no SSL*)" if ssl_ptr.zero? + name = GET_GROUP_NAME.call(ssl_ptr) + name.null? ? "(none)" : name.to_s + end + + def connect_nonblock(*, **) + super.tap do |ret| + next if ret.is_a?(Symbol) + @negotiated_group = PQProbe.group_of(self) + end + end + + def connect + super.tap { @negotiated_group = PQProbe.group_of(self) } + end + + def negotiated_group + @negotiated_group + end +end +OpenSSL::SSL::SSLSocket.prepend PQProbe + +class PqcRestTest < ShowcaseTest + def test_rest_pqc_negotiation + client = new_echo_rest_client + response = client.echo content: "PQC check" + assert_equal "PQC check", response.content + end +end diff --git a/shared/test/showcase/test_helper.rb b/shared/test/showcase/test_helper.rb index 81fd2fc87..949a2d190 100644 --- a/shared/test/showcase/test_helper.rb +++ b/shared/test/showcase/test_helper.rb @@ -19,6 +19,25 @@ require "fileutils" require "open3" require "tmpdir" +require "openssl" + +def generate_local_tls_certs(tmp_dir) + key = OpenSSL::PKey::RSA.new(2048) + cert = OpenSSL::X509::Certificate.new + cert.version = 2 + cert.serial = 1 + cert.subject = OpenSSL::X509::Name.parse("/CN=localhost") + cert.issuer = cert.subject + cert.public_key = key.public_key + cert.not_before = Time.now + cert.not_after = cert.not_before + 3600 + cert.sign(key, OpenSSL::Digest::SHA256.new) + cert_path = File.join(tmp_dir, 'cert.pem') + key_path = File.join(tmp_dir, 'key.pem') + File.write(cert_path, cert.to_pem) + File.write(key_path, key.to_pem) +end + # @private GAPIC_SHOWCASE_VERSION = '0.42.0' @@ -68,32 +87,32 @@ def tar_file_name class ShowcaseTest < Minitest::Test def new_echo_client Google::Showcase::V1beta1::Echo::Client.new do |config| - config.credentials = :this_channel_is_insecure + config.credentials = GRPC::Core::ChannelCredentials.new end end def new_echo_rest_client Google::Showcase::V1beta1::Echo::Rest::Client.new do |config| - config.endpoint = "http://localhost:7469" + config.endpoint = "https://localhost:7469" config.credentials = :this_channel_is_insecure end end def new_identity_client Google::Showcase::V1beta1::Identity::Client.new do |config| - config.credentials = :this_channel_is_insecure + config.credentials = GRPC::Core::ChannelCredentials.new end end def new_echo_operations_client Google::Showcase::V1beta1::Echo::Operations.new do |config| - config.credentials = :this_channel_is_insecure + config.credentials = GRPC::Core::ChannelCredentials.new end end def new_compliance_rest_client Google::Showcase::V1beta1::Compliance::Rest::Client.new do |config| - config.endpoint = "http://localhost:7469" + config.endpoint = "https://localhost:7469" config.credentials = :this_channel_is_insecure end end @@ -106,7 +125,11 @@ def new_compliance_rest_client url = "https://github.com/googleapis/gapic-showcase/releases/download/v#{GAPIC_SHOWCASE_VERSION}/#{tar_file_name}" _, status = Open3.capture2 "curl -sSL #{url} | tar -zx --directory #{tmp_dir}/" raise "failed to start showcase" unless status.exitstatus.zero? - server_id = Process.spawn("#{tmp_dir}/gapic-showcase run", :out => [log_file, "w"]) + generate_local_tls_certs(tmp_dir) +ENV["GRPC_DEFAULT_SSL_ROOTS_FILE_PATH"] = "#{tmp_dir}/cert.pem" +ENV["SSL_CERT_FILE"] = "#{tmp_dir}/cert.pem" +server_id = Process.spawn("#{tmp_dir}/gapic-showcase run --tls-cert=#{tmp_dir}/cert.pem --tls-key=#{tmp_dir}/key.pem", :out => [log_file, "w"]) + puts "Started showcase server v#{GAPIC_SHOWCASE_VERSION} (pid: #{server_id}) > #{log_file}." if ENV["VERBOSE"] else puts "Existing showcase server is available. Continuing..." if ENV["VERBOSE"]