blob: b1020c3218333a7b37dd94fb847ec896aeb9c20a [file]
//
//
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <string.h>
#include <string>
#include <gtest/gtest.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/grpc_security_constants.h>
#include <grpc/impl/channel_arg_names.h>
#include <grpc/impl/propagation_bits.h>
#include <grpc/slice.h>
#include <grpc/status.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/config/config_vars.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/gprpp/host_port.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/security/credentials/ssl/ssl_credentials.h"
#include "test/core/end2end/cq_verifier.h"
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
constexpr char kCaCertPath0[] = "src/core/tsi/test_creds/ca_p256.pem";
constexpr char kCaCertPath1[] = "src/core/tsi/test_creds/ca_p384.pem";
constexpr char kCaCertPath2[] = "src/core/tsi/test_creds/ca_p521.pem";
constexpr char kClientCertPath0[] = "src/core/tsi/test_creds/client_p256.pem";
constexpr char kClientKeyPath0[] = "src/core/tsi/test_creds/client_p256.key";
constexpr char kClientCertPath1[] = "src/core/tsi/test_creds/client_p384.pem";
constexpr char kClientKeyPath1[] = "src/core/tsi/test_creds/client_p384.key";
constexpr char kClientCertPath2[] = "src/core/tsi/test_creds/client_p521.pem";
constexpr char kClientKeyPath2[] = "src/core/tsi/test_creds/client_p521.key";
constexpr char kServerCertPath0[] = "src/core/tsi/test_creds/server1_p256.pem";
constexpr char kServerKeyPath0[] = "src/core/tsi/test_creds/server1_p256.key";
constexpr char kServerCertPath1[] = "src/core/tsi/test_creds/server1_p384.pem";
constexpr char kServerKeyPath1[] = "src/core/tsi/test_creds/server1_p384.key";
constexpr char kServerCertPath2[] = "src/core/tsi/test_creds/server1_p521.pem";
constexpr char kServerKeyPath2[] = "src/core/tsi/test_creds/server1_p521.key";
namespace grpc {
namespace testing {
namespace {
gpr_timespec five_seconds_time() { return grpc_timeout_seconds_to_deadline(5); }
grpc_server* server_create(grpc_completion_queue* cq, const char* server_addr,
const char* ca_cert_path,
const char* server_cert_path,
const char* server_key_path) {
grpc_slice ca_slice, cert_slice, key_slice;
GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file",
grpc_load_file(ca_cert_path, 1, &ca_slice)));
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"load_file", grpc_load_file(server_cert_path, 1, &cert_slice)));
GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file",
grpc_load_file(server_key_path, 1, &key_slice)));
const char* ca_cert =
reinterpret_cast<const char*> GRPC_SLICE_START_PTR(ca_slice);
const char* server_cert =
reinterpret_cast<const char*> GRPC_SLICE_START_PTR(cert_slice);
const char* server_key =
reinterpret_cast<const char*> GRPC_SLICE_START_PTR(key_slice);
grpc_ssl_pem_key_cert_pair pem_cert_key_pair = {server_key, server_cert};
auto* cert_config =
grpc_ssl_server_certificate_config_create(ca_cert, &pem_cert_key_pair, 1);
auto* options = grpc_ssl_server_credentials_create_options_using_config(
GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY, cert_config);
grpc_server_credentials* server_creds =
grpc_ssl_server_credentials_create_with_options(options);
// This is a hack but we don't have a public API to force TLS version yet.
//
// The tests in this file are only meaningful with TLSv1.2 only there is the
// public key from the certificate used for key exchange and the key type's
// compatibility will be checked:
// https://datatracker.ietf.org/doc/html/rfc8422#section-5.3.
// In TLSv1.3 key exchange negotiation follows a different flow
// (https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.7) and no longer
// involves the key in the certificate.
reinterpret_cast<grpc_ssl_server_credentials*>(server_creds)
->set_max_tls_version(grpc_tls_version::TLS1_2);
grpc_server* server = grpc_server_create(nullptr, nullptr);
grpc_server_register_completion_queue(server, cq, nullptr);
GPR_ASSERT(grpc_server_add_http2_port(server, server_addr, server_creds));
grpc_server_credentials_release(server_creds);
grpc_server_start(server);
grpc_slice_unref(cert_slice);
grpc_slice_unref(key_slice);
grpc_slice_unref(ca_slice);
return server;
}
grpc_channel* client_create(const char* server_addr, const char* ca_cert_path,
const char* client_cert_path,
const char* client_key_path) {
grpc_slice ca_slice, cert_slice, key_slice;
GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file",
grpc_load_file(ca_cert_path, 1, &ca_slice)));
GPR_ASSERT(GRPC_LOG_IF_ERROR(
"load_file", grpc_load_file(client_cert_path, 1, &cert_slice)));
GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file",
grpc_load_file(client_key_path, 1, &key_slice)));
const char* ca_cert =
reinterpret_cast<const char*> GRPC_SLICE_START_PTR(ca_slice);
const char* client_cert =
reinterpret_cast<const char*> GRPC_SLICE_START_PTR(cert_slice);
const char* client_key =
reinterpret_cast<const char*> GRPC_SLICE_START_PTR(key_slice);
grpc_ssl_pem_key_cert_pair signed_client_key_cert_pair = {client_key,
client_cert};
grpc_channel_credentials* client_creds = grpc_ssl_credentials_create(
ca_cert, &signed_client_key_cert_pair, nullptr, nullptr);
grpc_arg args[] = {
grpc_channel_arg_string_create(
const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
const_cast<char*>("waterzooi.test.google.be")),
};
grpc_channel_args* client_args =
grpc_channel_args_copy_and_add(nullptr, args, GPR_ARRAY_SIZE(args));
grpc_channel* client =
grpc_channel_create(server_addr, client_creds, client_args);
GPR_ASSERT(client != nullptr);
grpc_channel_credentials_release(client_creds);
{
grpc_core::ExecCtx exec_ctx;
grpc_channel_args_destroy(client_args);
}
grpc_slice_unref(cert_slice);
grpc_slice_unref(key_slice);
grpc_slice_unref(ca_slice);
return client;
}
void do_round_trip(grpc_completion_queue* cq, grpc_server* server,
const char* server_addr, const char* ca_cert_path,
const char* client_cert_path, const char* client_key_path) {
grpc_channel* client = client_create(server_addr, ca_cert_path,
client_cert_path, client_key_path);
grpc_core::CqVerifier cqv(cq);
grpc_op ops[6];
grpc_op* op;
grpc_metadata_array initial_metadata_recv;
grpc_metadata_array trailing_metadata_recv;
grpc_metadata_array request_metadata_recv;
grpc_call_details call_details;
grpc_status_code status;
grpc_call_error error;
grpc_slice details;
int was_cancelled = 2;
gpr_timespec deadline = grpc_timeout_seconds_to_deadline(60);
grpc_call* c = grpc_channel_create_call(
client, nullptr, GRPC_PROPAGATE_DEFAULTS, cq,
grpc_slice_from_static_string("/foo"), nullptr, deadline, nullptr);
GPR_ASSERT(c);
grpc_metadata_array_init(&initial_metadata_recv);
grpc_metadata_array_init(&trailing_metadata_recv);
grpc_metadata_array_init(&request_metadata_recv);
grpc_call_details_init(&call_details);
memset(ops, 0, sizeof(ops));
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
op->flags = 0;
op->reserved = nullptr;
op++;
op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
op->flags = 0;
op->reserved = nullptr;
op++;
op->op = GRPC_OP_RECV_INITIAL_METADATA;
op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
op->flags = 0;
op->reserved = nullptr;
op++;
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
op->data.recv_status_on_client.status = &status;
op->data.recv_status_on_client.status_details = &details;
op->flags = 0;
op->reserved = nullptr;
op++;
error = grpc_call_start_batch(c, ops, static_cast<size_t>(op - ops),
grpc_core::CqVerifier::tag(1), nullptr);
GPR_ASSERT(GRPC_CALL_OK == error);
grpc_call* s;
error = grpc_server_request_call(server, &s, &call_details,
&request_metadata_recv, cq, cq,
grpc_core::CqVerifier::tag(101));
GPR_ASSERT(GRPC_CALL_OK == error);
cqv.Expect(grpc_core::CqVerifier::tag(101), true);
cqv.Verify();
memset(ops, 0, sizeof(ops));
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
op->flags = 0;
op->reserved = nullptr;
op++;
op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
op->data.recv_close_on_server.cancelled = &was_cancelled;
op->flags = 0;
op->reserved = nullptr;
op++;
op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
op->data.send_status_from_server.trailing_metadata_count = 0;
op->data.send_status_from_server.status = GRPC_STATUS_OK;
op->flags = 0;
op->reserved = nullptr;
op++;
error = grpc_call_start_batch(s, ops, static_cast<size_t>(op - ops),
grpc_core::CqVerifier::tag(103), nullptr);
GPR_ASSERT(GRPC_CALL_OK == error);
cqv.Expect(grpc_core::CqVerifier::tag(103), true);
cqv.Expect(grpc_core::CqVerifier::tag(1), true);
cqv.Verify();
grpc_metadata_array_destroy(&initial_metadata_recv);
grpc_metadata_array_destroy(&trailing_metadata_recv);
grpc_metadata_array_destroy(&request_metadata_recv);
grpc_call_details_destroy(&call_details);
grpc_call_unref(c);
grpc_call_unref(s);
grpc_channel_destroy(client);
}
void drain_cq(grpc_completion_queue* cq) {
grpc_event ev;
do {
ev = grpc_completion_queue_next(cq, five_seconds_time(), nullptr);
} while (ev.type != GRPC_QUEUE_SHUTDOWN);
}
TEST(H2SslCertsEllipicCurvesTest, RoundTripWithP256Curve) {
int port = grpc_pick_unused_port_or_die();
std::string server_addr = grpc_core::JoinHostPort("localhost", port);
grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr);
grpc_server* server = server_create(cq, server_addr.c_str(), kCaCertPath0,
kServerCertPath0, kServerKeyPath0);
do_round_trip(cq, server, server_addr.c_str(), kCaCertPath0, kClientCertPath0,
kClientKeyPath0);
GPR_ASSERT(grpc_completion_queue_next(
cq, grpc_timeout_milliseconds_to_deadline(100), nullptr)
.type == GRPC_QUEUE_TIMEOUT);
grpc_server_shutdown_and_notify(server, cq, grpc_core::CqVerifier::tag(1000));
grpc_event ev;
do {
ev = grpc_completion_queue_next(cq, grpc_timeout_seconds_to_deadline(5),
nullptr);
} while (ev.type != GRPC_OP_COMPLETE ||
ev.tag != grpc_core::CqVerifier::tag(1000));
grpc_server_destroy(server);
grpc_completion_queue_shutdown(cq);
drain_cq(cq);
grpc_completion_queue_destroy(cq);
}
TEST(H2SslCertsEllipicCurvesTest, RoundTripWithP384Curve) {
int port = grpc_pick_unused_port_or_die();
std::string server_addr = grpc_core::JoinHostPort("localhost", port);
grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr);
grpc_server* server = server_create(cq, server_addr.c_str(), kCaCertPath1,
kServerCertPath1, kServerKeyPath1);
do_round_trip(cq, server, server_addr.c_str(), kCaCertPath1, kClientCertPath1,
kClientKeyPath1);
GPR_ASSERT(grpc_completion_queue_next(
cq, grpc_timeout_milliseconds_to_deadline(100), nullptr)
.type == GRPC_QUEUE_TIMEOUT);
grpc_server_shutdown_and_notify(server, cq, grpc_core::CqVerifier::tag(1000));
grpc_event ev;
do {
ev = grpc_completion_queue_next(cq, grpc_timeout_seconds_to_deadline(5),
nullptr);
} while (ev.type != GRPC_OP_COMPLETE ||
ev.tag != grpc_core::CqVerifier::tag(1000));
grpc_server_destroy(server);
grpc_completion_queue_shutdown(cq);
drain_cq(cq);
grpc_completion_queue_destroy(cq);
}
TEST(H2SslCertsEllipicCurvesTest, RoundTripWithP521Curve) {
int port = grpc_pick_unused_port_or_die();
std::string server_addr = grpc_core::JoinHostPort("localhost", port);
grpc_completion_queue* cq = grpc_completion_queue_create_for_next(nullptr);
grpc_server* server = server_create(cq, server_addr.c_str(), kCaCertPath2,
kServerCertPath2, kServerKeyPath2);
do_round_trip(cq, server, server_addr.c_str(), kCaCertPath2, kClientCertPath2,
kClientKeyPath2);
GPR_ASSERT(grpc_completion_queue_next(
cq, grpc_timeout_milliseconds_to_deadline(100), nullptr)
.type == GRPC_QUEUE_TIMEOUT);
grpc_server_shutdown_and_notify(server, cq, grpc_core::CqVerifier::tag(1000));
grpc_event ev;
do {
ev = grpc_completion_queue_next(cq, grpc_timeout_seconds_to_deadline(5),
nullptr);
} while (ev.type != GRPC_OP_COMPLETE ||
ev.tag != grpc_core::CqVerifier::tag(1000));
grpc_server_destroy(server);
grpc_completion_queue_shutdown(cq);
drain_cq(cq);
grpc_completion_queue_destroy(cq);
}
} // namespace
} // namespace testing
} // namespace grpc
int main(int argc, char** argv) {
grpc::testing::TestEnvironment env(&argc, argv);
grpc_core::ConfigVars::Overrides overrides;
grpc_core::ConfigVars::SetOverrides(overrides);
grpc_init();
::testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
grpc_shutdown();
return ret;
}