Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cryptomb: add support for PKCS #1 1.5 RSA padding. #21286

Merged
merged 3 commits into from
May 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -169,24 +169,65 @@ ssl_private_key_result_t rsaPrivateKeySignInternal(CryptoMbPrivateKeyConnection*
return status;
}

// Add RSA padding to the the hash. Only `PSS` padding is supported.
// TODO(ipuustin): add PKCS #1 v1.5 padding scheme later if requested.
if (!SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) {
ops->logDebugMsg("non-supported RSA padding");
return status;
}

uint8_t* msg;
size_t msg_len;

msg_len = RSA_size(rsa.get());
msg = static_cast<uint8_t*>(OPENSSL_malloc(msg_len));
if (msg == nullptr) {
return status;
}
if (!RSA_padding_add_PKCS1_PSS_mgf1(rsa.get(), msg, hash, md, nullptr, -1)) {
OPENSSL_free(msg);
return status;
// Add RSA padding to the the hash. `PSS` and `PKCS#1` v1.5 padding schemes are supported.
if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) {
msg_len = RSA_size(rsa.get());
msg = static_cast<uint8_t*>(OPENSSL_malloc(msg_len));
if (msg == nullptr) {
return status;
}

if (!RSA_padding_add_PKCS1_PSS_mgf1(rsa.get(), msg, hash, md, nullptr, -1)) {
OPENSSL_free(msg);
return status;
}
} else {
// PKCS#1 1.5
int prefix_allocated = 0;
if (!RSA_add_pkcs1_prefix(&msg, &msg_len, &prefix_allocated, EVP_MD_type(md), hash, hash_len)) {
if (prefix_allocated) {
OPENSSL_free(msg);
}
return status;
}

// RFC 8017 section 9.2

unsigned long rsa_len = RSA_size(rsa.get());
// Header is 3 bytes, padding is min 8 bytes
unsigned long header_len = 3;
unsigned long padding_len = rsa_len - msg_len - header_len;

if (padding_len < 8) {
OPENSSL_free(msg);
return status;
}

uint8_t* full_msg = static_cast<uint8_t*>(OPENSSL_malloc(rsa_len));
if (full_msg == nullptr) {
if (prefix_allocated) {
OPENSSL_free(msg);
}
return status;
}

int idx = 0;
full_msg[idx++] = 0x0; // first header byte
full_msg[idx++] = 0x1; // second header byte
memset(full_msg + idx, 0xff, padding_len); // padding
idx += padding_len;
full_msg[idx++] = 0x0; // third header byte
memcpy(full_msg + idx, msg, msg_len); // NOLINT(safe-memcpy)

if (prefix_allocated) {
OPENSSL_free(msg);
}

msg = full_msg;
msg_len = rsa_len;
}

// Create MB context which will be used for this particular
Expand Down
4 changes: 0 additions & 4 deletions contrib/cryptomb/private_key_providers/test/fake_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ uint32_t FakeIppCryptoImpl::mbxRsaPrivateCrtSslMb8(
status = mbxSetSts(status, i, inject_errors_ ? !ret : ret);
}

UNREFERENCED_PARAMETER(expected_rsa_bitsize);

return status;
}

Expand Down Expand Up @@ -110,8 +108,6 @@ uint32_t FakeIppCryptoImpl::mbxRsaPublicSslMb8(const uint8_t* const from_pa[8],
status = mbxSetSts(status, i, inject_errors_ ? !ret : ret);
}

UNREFERENCED_PARAMETER(expected_rsa_bitsize);

return status;
}

Expand Down
58 changes: 52 additions & 6 deletions contrib/cryptomb/private_key_providers/test/ops_test.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
Expand All @@ -12,6 +13,8 @@
#include "contrib/cryptomb/private_key_providers/source/cryptomb_private_key_provider.h"
#include "fake_factory.h"
#include "gtest/gtest.h"
#include "openssl/evp.h"
#include "openssl/rsa.h"

namespace Envoy {
namespace Extensions {
Expand Down Expand Up @@ -133,13 +136,41 @@ TEST_F(CryptoMbProviderEcdsaTest, TestEcdsaSigning) {
}

TEST_F(CryptoMbProviderRsaTest, TestRsaPkcs1Signing) {
// PKCS #1 v1.5 is not supported.
TestCallbacks cb;
CryptoMbPrivateKeyConnection op(cb, *dispatcher_, bssl::UpRef(pkey_), queue_);
// Initialize connections.
TestCallbacks cbs[CryptoMbQueue::MULTIBUFF_BATCH];
std::vector<std::unique_ptr<CryptoMbPrivateKeyConnection>> connections;
for (auto& cb : cbs) {
connections.push_back(std::make_unique<CryptoMbPrivateKeyConnection>(
cb, *dispatcher_, bssl::UpRef(pkey_), queue_));
}

res_ = rsaPrivateKeySignForTest(&op, nullptr, nullptr, max_out_len_, SSL_SIGN_RSA_PKCS1_SHA256,
in_, in_len_);
EXPECT_EQ(res_, ssl_private_key_failure);
// Create MULTIBUFF_BATCH amount of signing operations.
for (uint32_t i = 0; i < CryptoMbQueue::MULTIBUFF_BATCH; i++) {
// Create request.
res_ = rsaPrivateKeySignForTest(connections[i].get(), nullptr, nullptr, max_out_len_,
SSL_SIGN_RSA_PKCS1_SHA256, in_, in_len_);
EXPECT_EQ(res_, ssl_private_key_retry);

// No processing done after first requests.
// After the last request, the status is set only from the event loop which is not run. This
// should still be "retry", the cryptographic result is present anyway.
res_ = privateKeyCompleteForTest(connections[i].get(), nullptr, nullptr, max_out_len_);
EXPECT_EQ(res_, ssl_private_key_retry);
}

// Timeout does not have to be triggered when queue is at maximum size.
dispatcher_->run(Event::Dispatcher::RunType::NonBlock);

res_ = privateKeyCompleteForTest(connections[0].get(), out_, &out_len_, max_out_len_);
EXPECT_EQ(res_, ssl_private_key_success);
EXPECT_NE(out_len_, 0);

// Check the signature in out_.
RSA* rsa = EVP_PKEY_get0_RSA(pkey_.get());

uint8_t buf[max_out_len_] = {0};
size_t buf_len = 0;
EXPECT_EQ(RSA_verify_raw(rsa, &buf_len, buf, max_out_len_, out_, out_len_, RSA_PKCS1_PADDING), 1);
}

TEST_F(CryptoMbProviderRsaTest, TestRsaPssSigning) {
Expand Down Expand Up @@ -171,6 +202,21 @@ TEST_F(CryptoMbProviderRsaTest, TestRsaPssSigning) {
res_ = privateKeyCompleteForTest(connections[0].get(), out_, &out_len_, max_out_len_);
EXPECT_EQ(res_, ssl_private_key_success);
EXPECT_NE(out_len_, 0);

// Check the signature in out_.
RSA* rsa = EVP_PKEY_get0_RSA(pkey_.get());

uint8_t buf[max_out_len_] = {0};
unsigned int buf_len = 0;
const EVP_MD* md = SSL_get_signature_algorithm_digest(SSL_SIGN_RSA_PSS_SHA256);
EXPECT_NE(md, nullptr);
bssl::ScopedEVP_MD_CTX ctx;
// Calculate the message digest (so that we can be sure that it has been signed).
EXPECT_EQ(EVP_DigestInit_ex(ctx.get(), md, nullptr), 1);
EXPECT_EQ(EVP_DigestUpdate(ctx.get(), in_, in_len_), 1);
EXPECT_EQ(EVP_DigestFinal_ex(ctx.get(), buf, &buf_len), 1);

EXPECT_EQ(RSA_verify_pss_mgf1(rsa, buf, buf_len, md, nullptr, -1, out_, out_len_), 1);
}

TEST_F(CryptoMbProviderRsaTest, TestRsaDecrypt) {
Expand Down