Skip to content

Commit

Permalink
cryptomb: add support for PKCS #1 1.5 RSA padding. (envoyproxy#21286)
Browse files Browse the repository at this point in the history
Signed-off-by: Ismo Puustinen <[email protected]>
  • Loading branch information
ipuustin authored and ravenblackx committed Jun 8, 2022
1 parent be4bdba commit af7a0af
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 25 deletions.
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

0 comments on commit af7a0af

Please sign in to comment.