blob: b97706a07cfd3a814b306da2ccea91fba1c3d5e1 [file]
//
// Copyright 2022 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 "src/core/tsi/ssl_transport_security_utils.h"
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <array>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/tsi/transport_security.h"
#include "src/core/tsi/transport_security_interface.h"
#include "src/core/util/load_file.h"
#include "test/core/test_util/test_config.h"
#include "test/core/tsi/transport_security_test_lib.h"
namespace grpc_core {
namespace testing {
using ::testing::ContainerEq;
using ::testing::NotNull;
using ::testing::TestWithParam;
using ::testing::ValuesIn;
const char* kValidCrl = "test/core/tsi/test_creds/crl_data/crls/current.crl";
const char* kCrlIssuer = "test/core/tsi/test_creds/crl_data/ca.pem";
const char* kModifiedSignature =
"test/core/tsi/test_creds/crl_data/bad_crls/invalid_signature.crl";
const char* kModifiedContent =
"test/core/tsi/test_creds/crl_data/bad_crls/invalid_content.crl";
const char* kIntermediateCrl =
"test/core/tsi/test_creds/crl_data/crls/intermediate.crl";
const char* kIntermediateCrlIssuer =
"test/core/tsi/test_creds/crl_data/intermediate_ca.pem";
const char* kLeafCert =
"test/core/tsi/test_creds/crl_data/leaf_signed_by_intermediate.pem";
const char* kEvilCa = "test/core/tsi/test_creds/crl_data/evil_ca.pem";
const char* kCaWithAkid = "test/core/tsi/test_creds/crl_data/ca_with_akid.pem";
const char* kCrlWithAkid =
"test/core/tsi/test_creds/crl_data/crl_with_akid.crl";
constexpr absl::string_view kLeafCertPem =
"-----BEGIN CERTIFICATE-----\n"
"MIICZzCCAdCgAwIBAgIIN18/ctj3wpAwDQYJKoZIhvcNAQELBQAwKjEXMBUGA1UE\n"
"ChMOR29vZ2xlIFRFU1RJTkcxDzANBgNVBAMTBnRlc3RDQTAeFw0xNTAxMDEwMDAw\n"
"MDBaFw0yNTAxMDEwMDAwMDBaMC8xFzAVBgNVBAoTDkdvb2dsZSBURVNUSU5HMRQw\n"
"EgYDVQQDDAt0ZXN0X2NlcnRfMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA\n"
"20oOyI+fNCCeHJ3DNjGooPPP43Q6emhVvuWD8ppta582Rgxq/4j1bl9cPHdoCdyy\n"
"HsWFVUZzscj2qhClmlBAMEA595OU2NX2d81nSih5dwZWLMRQkEIzyxUR7Vee3eyo\n"
"nQD4HSamaevMSv79WTUBCozEGITqWnjYA152KUbA/IsCAwEAAaOBkDCBjTAOBgNV\n"
"HQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud\n"
"EwEB/wQCMAAwGQYDVR0OBBIEECnFWP/UkDrV+SoXra58k64wGwYDVR0jBBQwEoAQ\n"
"p7JSbajiTZaIRUDSV1C81jAWBgNVHREEDzANggt0ZXN0X2NlcnRfMTANBgkqhkiG\n"
"9w0BAQsFAAOBgQCpJJssfN62T3G5z+5SBB+9KCzXnGxcTHtaTJkb04KLe+19EwhV\n"
"yRY4lZadKHjcNS6GCBogd069wNFUVYOU9VI7uUiEPdcTO+VRV5MYW0wjSi1zlkBZ\n"
"e8OAfYVeGUMfvThFpJ41f8vZ6GHgg95Lwv+Zh89SL8g1J3RWll9YVG8HWw==\n"
"-----END CERTIFICATE-----";
constexpr absl::string_view kPrivateKeyPem =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIICXQIBAAKBgQDbSg7Ij580IJ4cncM2Maig88/jdDp6aFW+5YPymm1rnzZGDGr/\n"
"iPVuX1w8d2gJ3LIexYVVRnOxyPaqEKWaUEAwQDn3k5TY1fZ3zWdKKHl3BlYsxFCQ\n"
"QjPLFRHtV57d7KidAPgdJqZp68xK/v1ZNQEKjMQYhOpaeNgDXnYpRsD8iwIDAQAB\n"
"AoGAbq4kZApJeo/z/dGK0/GggQxOIylo0puSm7VQMcTL8YP8asKdxrgj2D99WG+U\n"
"LVYc+PcM4wuaHWOnTBL24roaitCNhrpIsJfWDkexzHXMj622SYlUcCuwsfjYOEyw\n"
"ntoNAnh0o4S+beYAfzT5VHCh4is9G9u+mwKYiGpJXROrYUECQQD4eq4nuGq3mfYJ\n"
"B0+md30paDVVCyBsuZTAtnu3MbRjMXy5LLE+vhno5nocvVSTOv3QC7Wk6yAa8/bG\n"
"iPT/MWixAkEA4e0zqPGo8tSimVv/1ei8Chyb+YqdSx+Oj5eZpa6X/KB/C1uS1tm6\n"
"DTgHW2GUhV4ypqdGH+t8quprJUtFuzqH+wJBAMRiicSg789eouMt4RjrdYPFdela\n"
"Gu1zm4rYb10xrqV7Vl0wYoH5U5cMmdSfGvomdLX6mzzWDJDg4ti1JBWRonECQQCD\n"
"Umtq0j1QGQUCe5Vz8zoJ7qNDI61WU1t8X7Rxt9CkiW4PXgU2WYxpzp2IImpAM4bh\n"
"k+2Q9EKc3nG1VdGMiPMtAkARkQF+pL8SBrUoh8G8glCam0brh3tW/cdW8L4UGTNF\n"
"2ZKC/LFH6DQBjYs3UXjvMGJxz4k9LysyY6o2Nf1JG6/L\n"
"-----END RSA PRIVATE KEY-----";
constexpr absl::string_view kEcPrivateKeyPem =
"-----BEGIN PRIVATE KEY-----\n"
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgOM7iHjJw/N6n8HtM\n"
"bVVVRhEYXoHFF+MSaTYQxOWM1p+hRANCAASMeWC+pIJAm/1fn0Wz3yyWGQzVPm9v\n"
"LCQo5JvK0a2t+Aa6d3AtLRwo6vh1VbJ8zFZxxIwyJNis3n1jRMWal7Vo\n"
"-----END PRIVATE KEY-----";
constexpr absl::string_view kRsaPrivateKeyPem =
"-----BEGIN PRIVATE KEY-----\n"
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCqyrzsrS8mWQwz\n"
"VFudLgte2kJX/pZ3KqJQBtMrkLxpgyJJU8mVBB+quDwnfH6PnQk+sF9omTlGAAxR\n"
"JzSEe8BS1Wnxld6rr6o/381VVW/2b+2kSifCtx/gVwCQQLnf4dbjfGW7ZClu1URG\n"
"ks2lK9T9BIh9SMSnYLEKEC8sWW1LibzJxHapFjIP88GrqgpPNGdEK7ABMsqHASuU\n"
"MvQ+0w7sdX2Pdu+Gm8ChxawvLiQVSh9ehtJiPl/jWbcZ6K3caTUxMf9tn8ky0DMK\n"
"xmHHmmxu19ehegzi7KSzjHmJ4QAtrtDaB/+ud0ZJ5l+pwfk7DL1TRjFYOyPVpExb\n"
"nLcQQxzfAgMBAAECggEATc+kFygnzQ7Q0iniu0+Y+pPxmelxX8VawZ76YmTEkkWe\n"
"P04fDvcb/kmFjm/XsVJYPelY7mywfUXUVrzH3nwK+TIl3FztX8beh89M203bfqkr\n"
"2ae3Sazopuq8ZPw4MtnPb0DjkGZnwgkD3CtR6ah4lvWTwZB/l8ojnnQVKd1sP/c4\n"
"LQSlVm2aiD6+D/NxbyJ4AOMWgUFrWBKqnV30mTZ5Lwv8fjznopgWMfsUl+Nx/HzV\n"
"J1ZRtLE+Z9euFJOUeMSEG1+YFxXAA3XuRdY/4PpzvK8Rlxb2rtJvt+dHojQCz66U\n"
"6PcspPt6MOcUFnpamJ513oKDwmdR8puRg7/bk2VKYQKBgQDVHz/NQaS8czTCkx8p\n"
"jXmZcGv1DH+T3SWeNI871BXQbSSKuqrOXDfzfrIFK7uXAWtIAOKLVaZOcSEDk+Rj\n"
"kbifkqRZuMy+iLdBLj/Gw3xVfkOb3m4g7OqWc7RBlfTCTCCUTVPiQkKZLGJ/eIJx\n"
"sGvdyJP6f12MODqUobgQC2UniQKBgQDNJ0vDHdqRQYI4zz1gAYDaCruRjtwWvRSL\n"
"tcBFlcVnMXjfZpxOKnGU1xEO4wDhEXra9yLwi/6IwGFtk0Zi2C9rYptniiURReuX\n"
"TkNNf1JmyZhYuSXD9Pg1Ssa/t3ZtauFzK1rHL1R1UB/pnD8xxuB4aAl+kZKi1Ie+\n"
"E6IXHuyfJwKBgQDOac+viq503tfww/Fgm2d0lw/YbNx7Z6rxiVJYzda64ZqMyrJ3\n"
"35VJPiJJI8wyOuue90xzSuch/ivNfUWssgwwcSTAyV10BJIIjTSz283mN75fjpT3\n"
"Sr8CLNoe05AVRwoe2K4v66D5HaXgc+VTG129lnDMIuOF1UfXgLH2yDKWkQKBgQC4\n"
"ajqQiqWPLXQB3UkupCtP1ZYGooT1a8KsVBUieB+bQ72EFJktKrovMaUD3MtNhokJ\n"
"jF68HRwRkd4CwgDjmbIGtf08ddIcVN4ShSe64lkQTOfF2ak5HVyBi1ZdwG2Urh87\n"
"iB1yL/mb+wq01N95v2zIz7y5KeLGvIXJN5zda88IwQKBgFLk68ZMEDMVCLpdvywb\n"
"bRC3rOl2CTHfXFD6RY0SIv4De+w7iQkYOn+4NIyG+hMfGfj5ooOO5gbsDyGagZqV\n"
"OLc6cW5HnwN+PERByn+hSoyGq8IOk8Vn5DeV7PoqIlbbdfUmTUx69EtzvViZoa+O\n"
"O2XDljPcjgc+pobqzebPIR6R\n"
"-----END PRIVATE KEY-----";
constexpr std::size_t kMaxPlaintextBytesPerTlsRecord = 16384;
constexpr std::size_t kTlsRecordOverhead = 100;
constexpr std::array<std::size_t, 4> kTestPlainTextSizeArray = {
1, 1000, kMaxPlaintextBytesPerTlsRecord,
kMaxPlaintextBytesPerTlsRecord + 1000};
struct FrameProtectorUtilTestData {
std::size_t plaintext_size;
std::size_t expected_encrypted_bytes_size;
};
// Generates the testing data |FrameProtectorUtilTestData|.
std::vector<FrameProtectorUtilTestData> GenerateTestData() {
std::vector<FrameProtectorUtilTestData> data;
for (std::size_t plaintext_size : kTestPlainTextSizeArray) {
std::size_t expected_size = plaintext_size + kTlsRecordOverhead;
if (plaintext_size > kMaxPlaintextBytesPerTlsRecord) {
expected_size = kMaxPlaintextBytesPerTlsRecord + kTlsRecordOverhead;
}
data.push_back({plaintext_size, expected_size});
}
return data;
}
// TODO(gtcooke94) - Tests current failing with OpenSSL 1.1.1 and 3.0. Fix and
// re-enable.
#ifdef OPENSSL_IS_BORINGSSL
class FlowTest : public TestWithParam<FrameProtectorUtilTestData> {
protected:
static void SetUpTestSuite() {
#if OPENSSL_VERSION_NUMBER >= 0x10100000
OPENSSL_init_ssl(/*opts=*/0, /*settings=*/nullptr);
#else
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
#endif
}
// Used for debugging.
static int VerifySucceed(X509_STORE_CTX* /*store_ctx*/, void* /*arg*/) {
return 1;
}
// Drives two SSL objects to finish a complete handshake with the hard-coded
// credentials and outputs those two SSL objects to `out_client` and
// `out_server` respectively.
static absl::Status DoHandshake(SSL** out_client, SSL** out_server) {
if (out_client == nullptr || out_server == nullptr) {
return absl::InvalidArgumentError(
"Client and server SSL object must not be null.");
}
// Create the context objects.
SSL_CTX* client_ctx = nullptr;
SSL_CTX* server_ctx = nullptr;
#if OPENSSL_VERSION_NUMBER >= 0x10100000
client_ctx = SSL_CTX_new(TLS_method());
server_ctx = SSL_CTX_new(TLS_method());
#else
client_ctx = SSL_CTX_new(TLSv1_2_method());
server_ctx = SSL_CTX_new(TLSv1_2_method());
#endif
BIO* client_cert_bio(
BIO_new_mem_buf(kLeafCertPem.data(), kLeafCertPem.size()));
X509* client_cert = PEM_read_bio_X509(client_cert_bio, /*x=*/nullptr,
/*cb=*/nullptr, /*u=*/nullptr);
BIO* client_key_bio(
BIO_new_mem_buf(kPrivateKeyPem.data(), kPrivateKeyPem.size()));
EVP_PKEY* client_key =
PEM_read_bio_PrivateKey(client_key_bio, /*x=*/nullptr,
/*cb=*/nullptr, /*u=*/nullptr);
BIO* server_cert_bio(
BIO_new_mem_buf(kLeafCertPem.data(), kLeafCertPem.size()));
X509* server_cert = PEM_read_bio_X509(server_cert_bio, /*x=*/nullptr,
/*cb=*/nullptr, /*u=*/nullptr);
BIO* server_key_bio(
BIO_new_mem_buf(kPrivateKeyPem.data(), kPrivateKeyPem.size()));
EVP_PKEY* server_key =
PEM_read_bio_PrivateKey(server_key_bio, /*x=*/nullptr,
/*cb=*/nullptr, /*u=*/nullptr);
// Set both client and server certificate and private key.
SSL_CTX_use_certificate(client_ctx, client_cert);
SSL_CTX_use_PrivateKey(client_ctx, client_key);
SSL_CTX_use_certificate(server_ctx, server_cert);
SSL_CTX_use_PrivateKey(server_ctx, server_key);
EVP_PKEY_free(client_key);
BIO_free(client_key_bio);
X509_free(client_cert);
BIO_free(client_cert_bio);
EVP_PKEY_free(server_key);
BIO_free(server_key_bio);
X509_free(server_cert);
BIO_free(server_cert_bio);
// Configure both client and server to request (and accept any)
// certificate but fail if none is sent.
SSL_CTX_set_verify(client_ctx,
SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
/*callback=*/nullptr);
SSL_CTX_set_cert_verify_callback(client_ctx, VerifySucceed,
/*arg=*/nullptr);
SSL_CTX_set_verify(server_ctx,
SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
/*callback=*/nullptr);
SSL_CTX_set_cert_verify_callback(server_ctx, VerifySucceed,
/*arg=*/nullptr);
// Turns off the session caching.
SSL_CTX_set_session_cache_mode(client_ctx, SSL_SESS_CACHE_OFF);
SSL_CTX_set_session_cache_mode(server_ctx, SSL_SESS_CACHE_OFF);
#if OPENSSL_VERSION_NUMBER >= 0x10100000
#if defined(TLS1_3_VERSION)
// Set both the min and max TLS version to 1.3
SSL_CTX_set_min_proto_version(client_ctx, TLS1_3_VERSION);
SSL_CTX_set_min_proto_version(server_ctx, TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(client_ctx, TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(server_ctx, TLS1_3_VERSION);
#else
SSL_CTX_set_min_proto_version(client_ctx, TLS1_2_VERSION);
SSL_CTX_set_min_proto_version(server_ctx, TLS1_2_VERSION);
SSL_CTX_set_max_proto_version(client_ctx, TLS1_2_VERSION);
SSL_CTX_set_max_proto_version(server_ctx, TLS1_2_VERSION);
#endif
#endif
// Create client and server connection objects and configure their BIOs.
SSL* client(SSL_new(client_ctx));
SSL* server(SSL_new(server_ctx));
SSL_CTX_free(client_ctx);
SSL_CTX_free(server_ctx);
// Turns off issuance of session tickets by servers.
SSL_set_options(client, SSL_OP_NO_TICKET);
SSL_set_options(server, SSL_OP_NO_TICKET);
SSL_set_connect_state(client);
SSL_set_accept_state(server);
BIO* bio1;
BIO* bio2;
BIO_new_bio_pair(&bio1, /*writebuf1=*/0, &bio2, /*writebuf2=*/0);
SSL_set_bio(client, bio1, bio1);
SSL_set_bio(server, bio2, bio2);
// Drive both the client and server handshake operations to completion.
while (true) {
int client_ret = SSL_do_handshake(client);
int client_err = SSL_get_error(client, client_ret);
if (client_err != SSL_ERROR_NONE && client_err != SSL_ERROR_WANT_READ &&
client_err != SSL_ERROR_WANT_WRITE) {
return absl::InternalError(absl::StrCat("Client error:", client_err));
}
int server_ret = SSL_do_handshake(server);
int server_err = SSL_get_error(server, server_ret);
if (server_err != SSL_ERROR_NONE && server_err != SSL_ERROR_WANT_READ &&
server_err != SSL_ERROR_WANT_WRITE) {
return absl::InternalError(absl::StrCat("Server error:", server_err));
}
if (client_ret == 1 && server_ret == 1) {
break;
}
}
*out_client = client;
*out_server = server;
return absl::OkStatus();
}
static std::size_t CalculateRecordSizeFromHeader(uint8_t fourth_header_byte,
uint8_t fifth_header_byte) {
return (static_cast<int>(fourth_header_byte & 0xff) << 8) +
static_cast<int>(fifth_header_byte & 0xff);
}
void SetUp() override {
ASSERT_EQ(DoHandshake(&client_ssl, &server_ssl), absl::OkStatus());
ASSERT_THAT(client_ssl, NotNull());
ASSERT_THAT(server_ssl, NotNull());
BIO* client_ssl_bio = nullptr;
ASSERT_EQ(BIO_new_bio_pair(&client_bio, /*writebuf1=*/0, &client_ssl_bio,
/*writebuf2=*/0),
1);
SSL_set_bio(client_ssl, client_ssl_bio, client_ssl_bio);
BIO* server_ssl_bio = nullptr;
ASSERT_EQ(BIO_new_bio_pair(&server_bio, /*writebuf1=*/0, &server_ssl_bio,
/*writebuf2=*/0),
1);
SSL_set_bio(server_ssl, server_ssl_bio, server_ssl_bio);
client_buffer_offset = 0;
client_buffer.resize(kMaxPlaintextBytesPerTlsRecord);
server_buffer_offset = 0;
server_buffer.resize(kMaxPlaintextBytesPerTlsRecord);
}
void TearDown() override {
BIO_free(client_bio);
SSL_free(client_ssl);
BIO_free(server_bio);
SSL_free(server_ssl);
}
SSL* client_ssl;
BIO* client_bio;
std::vector<uint8_t> client_buffer;
std::size_t client_buffer_offset;
SSL* server_ssl;
BIO* server_bio;
std::vector<uint8_t> server_buffer;
std::size_t server_buffer_offset;
};
// This test consists of two tests, namely for each combination of parameters,
// we create a message on one side, protect it (encrypt it), and send it to
// the other side for unprotecting (decrypting).
TEST_P(FlowTest,
ClientMessageToServerCanBeProtectedAndUnprotectedSuccessfully) {
std::vector<uint8_t> unprotected_bytes(GetParam().plaintext_size, 'a');
std::size_t unprotected_bytes_size = unprotected_bytes.size();
std::vector<uint8_t> protected_output_frames(
GetParam().expected_encrypted_bytes_size);
std::size_t protected_output_frames_size = protected_output_frames.size();
EXPECT_EQ(SslProtectorProtect(unprotected_bytes.data(), client_buffer.size(),
client_buffer_offset, client_buffer.data(),
client_ssl, client_bio, &unprotected_bytes_size,
protected_output_frames.data(),
&protected_output_frames_size),
tsi_result::TSI_OK);
// If |GetParam().plaintext_size| is larger than the inner client_buffer
// size (kMaxPlaintextBytesPerTlsRecord), then |Protect| will copy up to
// |kMaxPlaintextBytesPerTlsRecord| bytes and output the protected
// frame. Otherwise we need to manually flush the copied data in order
// to get the protected frame.
if (GetParam().plaintext_size >= kMaxPlaintextBytesPerTlsRecord) {
EXPECT_EQ(unprotected_bytes_size, kMaxPlaintextBytesPerTlsRecord);
} else {
EXPECT_EQ(unprotected_bytes_size, GetParam().plaintext_size);
EXPECT_EQ(protected_output_frames_size, 0);
protected_output_frames_size = protected_output_frames.size();
std::size_t still_pending_size = 0;
EXPECT_EQ(SslProtectorProtectFlush(
client_buffer_offset, client_buffer.data(), client_ssl,
client_bio, protected_output_frames.data(),
&protected_output_frames_size, &still_pending_size),
tsi_result::TSI_OK);
EXPECT_EQ(still_pending_size, 0);
}
// The first three bytes are always 0x17, 0x03, 0x03.
EXPECT_EQ(protected_output_frames[0], '\x17');
EXPECT_EQ(protected_output_frames[1], '\x03');
EXPECT_EQ(protected_output_frames[2], '\x03');
// The next two bytes are the size of the record, which is 5 bytes less
// than the size of the whole frame.
EXPECT_EQ(CalculateRecordSizeFromHeader(protected_output_frames[3],
protected_output_frames[4]),
protected_output_frames_size - 5);
std::vector<uint8_t> unprotected_output_bytes(GetParam().plaintext_size);
std::size_t unprotected_output_bytes_size = unprotected_output_bytes.size();
// This frame should be decrypted by peer correctly.
EXPECT_EQ(SslProtectorUnprotect(protected_output_frames.data(), server_ssl,
server_bio, &protected_output_frames_size,
unprotected_output_bytes.data(),
&unprotected_output_bytes_size),
tsi_result::TSI_OK);
EXPECT_EQ(unprotected_output_bytes_size, unprotected_bytes_size);
unprotected_output_bytes.resize(unprotected_output_bytes_size);
unprotected_bytes.resize(unprotected_bytes_size);
EXPECT_THAT(unprotected_output_bytes, ContainerEq(unprotected_bytes));
}
TEST_P(FlowTest,
ServerMessageToClientCanBeProtectedAndUnprotectedSuccessfully) {
std::vector<uint8_t> unprotected_bytes(GetParam().plaintext_size, 'a');
std::size_t unprotected_bytes_size = unprotected_bytes.size();
std::vector<uint8_t> protected_output_frames(
GetParam().expected_encrypted_bytes_size);
std::size_t protected_output_frames_size = protected_output_frames.size();
EXPECT_EQ(SslProtectorProtect(unprotected_bytes.data(), server_buffer.size(),
server_buffer_offset, server_buffer.data(),
server_ssl, server_bio, &unprotected_bytes_size,
protected_output_frames.data(),
&protected_output_frames_size),
tsi_result::TSI_OK);
// If |GetParam().plaintext_size| is larger than the inner server_buffer
// size (kMaxPlaintextBytesPerTlsRecord), then |Protect| will copy up to
// |kMaxPlaintextBytesPerTlsRecord| bytes and output the protected
// frame. Otherwise we need to manually flush the copied data in order
// to get the protected frame.
if (GetParam().plaintext_size >= kMaxPlaintextBytesPerTlsRecord) {
EXPECT_EQ(unprotected_bytes_size, kMaxPlaintextBytesPerTlsRecord);
} else {
EXPECT_EQ(unprotected_bytes_size, GetParam().plaintext_size);
EXPECT_EQ(protected_output_frames_size, 0);
protected_output_frames_size = protected_output_frames.size();
std::size_t still_pending_size = 0;
EXPECT_EQ(SslProtectorProtectFlush(
server_buffer_offset, server_buffer.data(), server_ssl,
server_bio, protected_output_frames.data(),
&protected_output_frames_size, &still_pending_size),
tsi_result::TSI_OK);
EXPECT_EQ(still_pending_size, 0);
}
// The first three bytes are always 0x17, 0x03, 0x03.
EXPECT_EQ(protected_output_frames[0], '\x17');
EXPECT_EQ(protected_output_frames[1], '\x03');
EXPECT_EQ(protected_output_frames[2], '\x03');
// The next two bytes are the size of the record, which is 5 bytes less
// than the size of the whole frame.
EXPECT_EQ(CalculateRecordSizeFromHeader(protected_output_frames[3],
protected_output_frames[4]),
protected_output_frames_size - 5);
std::vector<uint8_t> unprotected_output_bytes(GetParam().plaintext_size);
std::size_t unprotected_output_bytes_size = unprotected_output_bytes.size();
// This frame should be decrypted by peer correctly.
EXPECT_EQ(SslProtectorUnprotect(protected_output_frames.data(), client_ssl,
client_bio, &protected_output_frames_size,
unprotected_output_bytes.data(),
&unprotected_output_bytes_size),
tsi_result::TSI_OK);
EXPECT_EQ(unprotected_output_bytes_size, unprotected_bytes_size);
unprotected_output_bytes.resize(unprotected_output_bytes_size);
unprotected_bytes.resize(unprotected_bytes_size);
EXPECT_THAT(unprotected_output_bytes, ContainerEq(unprotected_bytes));
}
INSTANTIATE_TEST_SUITE_P(FrameProtectorUtil, FlowTest,
ValuesIn(GenerateTestData()));
#endif // OPENSSL_IS_BORINGSSL
class CrlUtils : public ::testing::Test {
public:
static void SetUpTestSuite() {
#if OPENSSL_VERSION_NUMBER >= 0x10100000
OPENSSL_init_ssl(/*opts=*/0, /*settings=*/nullptr);
#else
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
#endif
}
void SetUp() override {
absl::StatusOr<Slice> root_crl = LoadFile(kValidCrl, false);
ASSERT_EQ(root_crl.status(), absl::OkStatus()) << root_crl.status();
root_crl_ = ReadCrl(root_crl->as_string_view());
absl::StatusOr<Slice> intermediate_crl = LoadFile(kIntermediateCrl, false);
ASSERT_EQ(intermediate_crl.status(), absl::OkStatus())
<< intermediate_crl.status();
intermediate_crl_ = ReadCrl(intermediate_crl->as_string_view());
absl::StatusOr<Slice> invalid_signature_crl =
LoadFile(kModifiedSignature, false);
ASSERT_EQ(invalid_signature_crl.status(), absl::OkStatus())
<< invalid_signature_crl.status();
invalid_signature_crl_ = ReadCrl(invalid_signature_crl->as_string_view());
absl::StatusOr<Slice> akid_crl = LoadFile(kCrlWithAkid, false);
ASSERT_EQ(akid_crl.status(), absl::OkStatus()) << akid_crl.status();
akid_crl_ = ReadCrl(akid_crl->as_string_view());
absl::StatusOr<Slice> root_ca = LoadFile(kCrlIssuer, false);
ASSERT_EQ(root_ca.status(), absl::OkStatus());
root_ca_ = ReadPemCert(root_ca->as_string_view());
absl::StatusOr<Slice> intermediate_ca =
LoadFile(kIntermediateCrlIssuer, false);
ASSERT_EQ(intermediate_ca.status(), absl::OkStatus());
intermediate_ca_ = ReadPemCert(intermediate_ca->as_string_view());
absl::StatusOr<Slice> leaf_cert = LoadFile(kLeafCert, false);
ASSERT_EQ(leaf_cert.status(), absl::OkStatus());
leaf_cert_ = ReadPemCert(leaf_cert->as_string_view());
absl::StatusOr<Slice> evil_ca = LoadFile(kEvilCa, false);
ASSERT_EQ(evil_ca.status(), absl::OkStatus());
evil_ca_ = ReadPemCert(evil_ca->as_string_view());
absl::StatusOr<Slice> ca_with_akid = LoadFile(kCaWithAkid, false);
ASSERT_EQ(ca_with_akid.status(), absl::OkStatus());
ca_with_akid_ = ReadPemCert(ca_with_akid->as_string_view());
}
void TearDown() override {
X509_CRL_free(root_crl_);
X509_CRL_free(intermediate_crl_);
X509_CRL_free(invalid_signature_crl_);
X509_CRL_free(akid_crl_);
X509_free(root_ca_);
X509_free(intermediate_ca_);
X509_free(leaf_cert_);
X509_free(evil_ca_);
X509_free(ca_with_akid_);
}
protected:
X509_CRL* root_crl_;
X509_CRL* intermediate_crl_;
X509_CRL* invalid_signature_crl_;
X509_CRL* akid_crl_;
X509* root_ca_;
X509* intermediate_ca_;
X509* leaf_cert_;
X509* evil_ca_;
X509* ca_with_akid_;
};
TEST_F(CrlUtils, VerifySignatureValid) {
EXPECT_TRUE(VerifyCrlSignature(root_crl_, root_ca_));
}
TEST_F(CrlUtils, VerifySignatureIntermediateValid) {
EXPECT_TRUE(VerifyCrlSignature(intermediate_crl_, intermediate_ca_));
}
TEST_F(CrlUtils, VerifySignatureModifiedSignature) {
EXPECT_FALSE(VerifyCrlSignature(invalid_signature_crl_, root_ca_));
}
TEST_F(CrlUtils, VerifySignatureModifiedContent) {
absl::StatusOr<Slice> crl_slice = LoadFile(kModifiedContent, false);
ASSERT_EQ(crl_slice.status(), absl::OkStatus()) << crl_slice.status();
X509_CRL* crl = ReadCrl(crl_slice->as_string_view());
EXPECT_EQ(crl, nullptr);
}
TEST_F(CrlUtils, VerifySignatureWrongIssuer) {
EXPECT_FALSE(VerifyCrlSignature(root_crl_, intermediate_ca_));
}
TEST_F(CrlUtils, VerifySignatureWrongIssuer2) {
EXPECT_FALSE(VerifyCrlSignature(intermediate_crl_, root_ca_));
}
TEST_F(CrlUtils, VerifySignatureNullCrl) {
EXPECT_FALSE(VerifyCrlSignature(nullptr, root_ca_));
}
TEST_F(CrlUtils, VerifySignatureNullCert) {
EXPECT_FALSE(VerifyCrlSignature(intermediate_crl_, nullptr));
}
TEST_F(CrlUtils, VerifySignatureNullCrlAndCert) {
EXPECT_FALSE(VerifyCrlSignature(nullptr, nullptr));
}
TEST_F(CrlUtils, VerifyIssuerNamesMatch) {
EXPECT_TRUE(VerifyCrlCertIssuerNamesMatch(root_crl_, root_ca_));
}
TEST_F(CrlUtils, VerifyIssuerNamesDontMatch) {
EXPECT_FALSE(VerifyCrlCertIssuerNamesMatch(root_crl_, leaf_cert_));
}
TEST_F(CrlUtils, DuplicatedIssuerNamePassesButSignatureCheckFails) {
// The issuer names will match, but it should fail a signature check
EXPECT_TRUE(VerifyCrlCertIssuerNamesMatch(root_crl_, evil_ca_));
EXPECT_FALSE(VerifyCrlSignature(root_crl_, evil_ca_));
}
TEST_F(CrlUtils, VerifyIssuerNameNullCrl) {
EXPECT_FALSE(VerifyCrlCertIssuerNamesMatch(nullptr, root_ca_));
}
TEST_F(CrlUtils, VerifyIssuerNameNullCert) {
EXPECT_FALSE(VerifyCrlCertIssuerNamesMatch(intermediate_crl_, nullptr));
}
TEST_F(CrlUtils, VerifyIssuerNameNullCrlAndCert) {
EXPECT_FALSE(VerifyCrlCertIssuerNamesMatch(nullptr, nullptr));
}
TEST_F(CrlUtils, HasCrlSignBitExists) { EXPECT_TRUE(HasCrlSignBit(root_ca_)); }
TEST_F(CrlUtils, HasCrlSignBitMissing) {
EXPECT_FALSE(HasCrlSignBit(leaf_cert_));
}
TEST_F(CrlUtils, HasCrlSignBitNullCert) {
EXPECT_FALSE(HasCrlSignBit(nullptr));
}
TEST_F(CrlUtils, IssuerFromIntermediateCert) {
auto issuer = IssuerFromCert(intermediate_ca_);
// Build the known name for comparison
unsigned char* buf = nullptr;
X509_NAME* expected_issuer_name = X509_NAME_new();
ASSERT_TRUE(
X509_NAME_add_entry_by_txt(expected_issuer_name, "C", MBSTRING_ASC,
(const unsigned char*)"AU", -1, -1, 0));
ASSERT_TRUE(X509_NAME_add_entry_by_txt(
expected_issuer_name, "ST", MBSTRING_ASC,
(const unsigned char*)"Some-State", -1, -1, 0));
ASSERT_TRUE(X509_NAME_add_entry_by_txt(
expected_issuer_name, "O", MBSTRING_ASC,
(const unsigned char*)"Internet Widgits Pty Ltd", -1, -1, 0));
ASSERT_TRUE(
X509_NAME_add_entry_by_txt(expected_issuer_name, "CN", MBSTRING_ASC,
(const unsigned char*)"testca", -1, -1, 0));
int len = i2d_X509_NAME(expected_issuer_name, &buf);
std::string expected_issuer_name_der(reinterpret_cast<char const*>(buf), len);
OPENSSL_free(buf);
X509_NAME_free(expected_issuer_name);
ASSERT_EQ(issuer.status(), absl::OkStatus());
EXPECT_EQ(*issuer, expected_issuer_name_der);
}
TEST_F(CrlUtils, IssuerFromLeaf) {
auto issuer = IssuerFromCert(leaf_cert_);
// Build the known name for comparison
unsigned char* buf = nullptr;
X509_NAME* expected_issuer_name = X509_NAME_new();
ASSERT_TRUE(X509_NAME_add_entry_by_txt(
expected_issuer_name, "CN", MBSTRING_ASC,
(const unsigned char*)"intermediatecert.example.com", -1, -1, 0));
int len = i2d_X509_NAME(expected_issuer_name, &buf);
std::string expected_issuer_name_der(reinterpret_cast<char const*>(buf), len);
OPENSSL_free(buf);
X509_NAME_free(expected_issuer_name);
ASSERT_EQ(issuer.status(), absl::OkStatus());
EXPECT_EQ(*issuer, expected_issuer_name_der);
}
TEST_F(CrlUtils, IssuerFromCertNull) {
auto issuer = IssuerFromCert(nullptr);
EXPECT_EQ(issuer.status().code(), absl::StatusCode::kInvalidArgument);
}
TEST_F(CrlUtils, CertCrlAkidValid) {
auto akid = AkidFromCertificate(ca_with_akid_);
EXPECT_EQ(akid.status(), absl::OkStatus());
auto crl_akid = AkidFromCrl(akid_crl_);
EXPECT_EQ(crl_akid.status(), absl::OkStatus());
EXPECT_NE(*akid, "");
// It's easiest to compare that these two pull the same value, it's very
// difficult to create the known AKID value as a test constant, so we just
// check that they are not empty and that they are the same.
EXPECT_EQ(*akid, *crl_akid);
}
TEST_F(CrlUtils, CertNoAkid) {
auto akid = AkidFromCertificate(root_ca_);
EXPECT_EQ(akid.status().code(), absl::StatusCode::kInvalidArgument);
}
TEST_F(CrlUtils, CrlNoAkid) {
auto akid = AkidFromCrl(root_crl_);
EXPECT_EQ(akid.status().code(), absl::StatusCode::kInvalidArgument);
}
TEST_F(CrlUtils, CertAkidNullptr) {
auto akid = AkidFromCertificate(nullptr);
EXPECT_EQ(akid.status().code(), absl::StatusCode::kInvalidArgument);
}
TEST_F(CrlUtils, CrlAkidNullptr) {
auto akid = AkidFromCrl(nullptr);
EXPECT_EQ(akid.status().code(), absl::StatusCode::kInvalidArgument);
}
TEST(ParsePemCertificateChainTest, EmptyPem) {
EXPECT_EQ(ParsePemCertificateChain(/*cert_chain_pem=*/"").status(),
absl::InvalidArgumentError("Cert chain PEM is empty."));
}
TEST(ParsePemCertificateChainTest, InvalidPem) {
EXPECT_EQ(ParsePemCertificateChain("invalid-pem").status(),
absl::NotFoundError("No certificates found."));
}
TEST(ParsePemCertificateChainTest, PartialPem) {
std::string pem(kLeafCertPem);
EXPECT_EQ(ParsePemCertificateChain(pem.substr(0, pem.length() / 2)).status(),
absl::FailedPreconditionError("Invalid PEM."));
}
TEST(ParsePemCertificateChainTest, SingleCertSuccess) {
absl::StatusOr<std::vector<X509*>> certs =
ParsePemCertificateChain(kLeafCertPem);
EXPECT_EQ(certs.status(), absl::OkStatus());
EXPECT_EQ(certs->size(), 1);
EXPECT_NE(certs->at(0), nullptr);
X509_free(certs->at(0));
}
TEST(ParsePemCertificateChainTest, MultipleCertFailure) {
EXPECT_EQ(ParsePemCertificateChain(absl::StrCat(kLeafCertPem, kLeafCertPem))
.status(),
absl::FailedPreconditionError("Invalid PEM."));
}
TEST(ParsePemCertificateChainTest, MultipleCertSuccess) {
absl::StatusOr<std::vector<X509*>> certs =
ParsePemCertificateChain(absl::StrCat(kLeafCertPem, "\n", kLeafCertPem));
EXPECT_EQ(certs.status(), absl::OkStatus());
EXPECT_EQ(certs->size(), 2);
EXPECT_NE(certs->at(0), nullptr);
EXPECT_NE(certs->at(1), nullptr);
X509_free(certs->at(0));
X509_free(certs->at(1));
}
TEST(ParsePemCertificateChainTest, MultipleCertWithExtraMiddleLinesSuccess) {
absl::StatusOr<std::vector<X509*>> certs = ParsePemCertificateChain(
absl::StrCat(kLeafCertPem, "\nGarbage\n", kLeafCertPem));
EXPECT_EQ(certs.status(), absl::OkStatus());
EXPECT_EQ(certs->size(), 2);
EXPECT_NE(certs->at(0), nullptr);
EXPECT_NE(certs->at(1), nullptr);
X509_free(certs->at(0));
X509_free(certs->at(1));
}
TEST(ParsePemCertificateChainTest, MultipleCertWitManyMiddleLinesSuccess) {
absl::StatusOr<std::vector<X509*>> certs = ParsePemCertificateChain(
absl::StrCat(kLeafCertPem, "\n\n\n\n\n\n\n", kLeafCertPem));
EXPECT_EQ(certs.status(), absl::OkStatus());
EXPECT_EQ(certs->size(), 2);
EXPECT_NE(certs->at(0), nullptr);
EXPECT_NE(certs->at(1), nullptr);
X509_free(certs->at(0));
X509_free(certs->at(1));
}
TEST(ParsePemCertificateChainTest, ValidCertWithInvalidSuffix) {
EXPECT_EQ(ParsePemCertificateChain(absl::StrCat(kLeafCertPem, "invalid-pem"))
.status(),
absl::FailedPreconditionError("Invalid PEM."));
}
TEST(ParsePemCertificateChainTest, ValidCertWithInvalidPrefix) {
EXPECT_EQ(ParsePemCertificateChain(absl::StrCat("invalid-pem", kLeafCertPem))
.status(),
absl::NotFoundError("No certificates found."));
}
TEST(ParsePemCertificateChainTest, ValidCertWithInvalidLeadingLine) {
absl::StatusOr<std::vector<X509*>> certs =
ParsePemCertificateChain(absl::StrCat("invalid-pem\n", kLeafCertPem));
EXPECT_EQ(certs.status(), absl::OkStatus());
EXPECT_EQ(certs->size(), 1);
EXPECT_NE(certs->at(0), nullptr);
X509_free(certs->at(0));
}
TEST(ParsePemPrivateKeyTest, EmptyPem) {
EXPECT_EQ(ParsePemPrivateKey(/*private_key_pem=*/"").status(),
absl::NotFoundError("No private key found."));
}
TEST(ParsePemPrivateKeyTest, InvalidPem) {
EXPECT_EQ(ParsePemPrivateKey("invalid-pem").status(),
absl::NotFoundError("No private key found."));
}
TEST(ParsePemPrivateKeyTest, PartialPem) {
std::string pem(kPrivateKeyPem);
EXPECT_EQ(ParsePemPrivateKey(pem.substr(0, pem.length() / 2)).status(),
absl::NotFoundError("No private key found."));
}
TEST(ParsePemPrivateKeyTest, RsaSuccess1) {
absl::StatusOr<EVP_PKEY*> pkey = ParsePemPrivateKey(kPrivateKeyPem);
EXPECT_EQ(pkey.status(), absl::OkStatus());
EXPECT_NE(*pkey, nullptr);
EVP_PKEY_free(*pkey);
}
TEST(ParsePemPrivateKeyTest, RsaSuccess2) {
absl::StatusOr<EVP_PKEY*> pkey = ParsePemPrivateKey(kRsaPrivateKeyPem);
EXPECT_EQ(pkey.status(), absl::OkStatus());
EXPECT_NE(*pkey, nullptr);
EVP_PKEY_free(*pkey);
}
TEST(ParsePemPrivateKeyTest, EcSuccess) {
absl::StatusOr<EVP_PKEY*> pkey = ParsePemPrivateKey(kEcPrivateKeyPem);
EXPECT_EQ(pkey.status(), absl::OkStatus());
EXPECT_NE(*pkey, nullptr);
EVP_PKEY_free(*pkey);
}
TEST(ParseUriString, ValidUri) {
GENERAL_NAME* subject_alt_name = GENERAL_NAME_new();
ASN1_IA5STRING* uri = ASN1_IA5STRING_new();
ASN1_STRING_set(uri, "spiffe://foo.bar/path", -1);
GENERAL_NAME_set0_value(subject_alt_name, GEN_URI, uri);
absl::StatusOr<std::string> parsed_uri = ParseUriString(subject_alt_name);
ASSERT_EQ(parsed_uri.status(), absl::OkStatus());
EXPECT_EQ(*parsed_uri, "spiffe://foo.bar/path");
GENERAL_NAME_free(subject_alt_name);
}
TEST(ParseUriString, EmptyUri) {
GENERAL_NAME* subject_alt_name = GENERAL_NAME_new();
ASN1_IA5STRING* uri = ASN1_IA5STRING_new();
ASN1_STRING_set(uri, "", -1);
GENERAL_NAME_set0_value(subject_alt_name, GEN_URI, uri);
absl::StatusOr<std::string> parsed_uri = ParseUriString(subject_alt_name);
ASSERT_EQ(parsed_uri.status(), absl::OkStatus());
EXPECT_EQ(*parsed_uri, "");
GENERAL_NAME_free(subject_alt_name);
}
TEST(ParseUriString, InvalidUtf8) {
GENERAL_NAME* subject_alt_name = GENERAL_NAME_new();
ASN1_UTF8STRING* uri = ASN1_UTF8STRING_new();
// This sequence is invalid UTF8.
const unsigned char invalid_utf8[] = {0xc0};
ASN1_STRING_set(reinterpret_cast<ASN1_STRING*>(uri), invalid_utf8,
sizeof(invalid_utf8));
GENERAL_NAME_set0_value(subject_alt_name, GEN_URI, uri);
absl::StatusOr<std::string> parsed_uri = ParseUriString(subject_alt_name);
EXPECT_EQ(parsed_uri.status().code(), absl::StatusCode::kInvalidArgument);
GENERAL_NAME_free(subject_alt_name);
}
TEST(ParseUriString, WrongType) {
GENERAL_NAME* subject_alt_name = GENERAL_NAME_new();
ASN1_UTF8STRING* other = ASN1_UTF8STRING_new();
ASN1_STRING_set(other, "foo", -1);
GENERAL_NAME_set0_value(subject_alt_name, GEN_DNS, other);
absl::StatusOr<std::string> parsed_uri = ParseUriString(subject_alt_name);
EXPECT_EQ(parsed_uri.status().code(), absl::StatusCode::kInvalidArgument);
GENERAL_NAME_free(subject_alt_name);
}
TEST(ParseUriString, DontSetASN1String) {
GENERAL_NAME* subject_alt_name = GENERAL_NAME_new();
ASN1_UTF8STRING* other = ASN1_UTF8STRING_new();
GENERAL_NAME_set0_value(subject_alt_name, GEN_DNS, other);
absl::StatusOr<std::string> parsed_uri = ParseUriString(subject_alt_name);
EXPECT_EQ(parsed_uri.status().code(), absl::StatusCode::kInvalidArgument);
GENERAL_NAME_free(subject_alt_name);
}
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
grpc::testing::TestEnvironment env(&argc, argv);
::testing::InitGoogleTest(&argc, argv);
grpc_init();
int ret = RUN_ALL_TESTS();
grpc_shutdown();
return ret;
}