Repository: randombit/botan
Pull request #3034 (2022-08-09 15:59:07)

PKCS#11 RSA allows choosing software EME
When enabling ` set_use_software_padding(bool)` the PKCS#11 module won't perform the unpadding. Instead, we let it decrypt a blinded ciphertext in Raw-mode and strip the EME in software afterwards.

Hence,  the PKCS#11 module (and all middleware on the way) won't gain knowledge of the decrypted secret value. Note that this might require some refactoring.

@weberph2, does that roughly address your issue? (See #3008)


Patch:
diff --git a/src/lib/prov/pkcs11/p11_rsa.cpp b/src/lib/prov/pkcs11/p11_rsa.cpp
index 6ad9d93aa..be72b58ae 100644
--- a/src/lib/prov/pkcs11/p11_rsa.cpp
+++ b/src/lib/prov/pkcs11/p11_rsa.cpp
@@ -13,6 +13,8 @@
 
 #include <botan/internal/p11_mechanism.h>
 #include <botan/internal/pk_ops.h>
+#include <botan/internal/pk_ops_impl.h>
+#include <botan/pubkey.h>
 #include <botan/rng.h>
 #include <botan/internal/blinding.h>
 
@@ -61,13 +63,14 @@ PKCS11_RSA_PrivateKey::PKCS11_RSA_PrivateKey(Session& session, ObjectHandle hand
 PKCS11_RSA_PrivateKey::PKCS11_RSA_PrivateKey(Session& session, const RSA_PrivateKeyImportProperties& priv_key_props)
    : Object(session, priv_key_props),
      RSA_PublicKey(priv_key_props.modulus(),
-                   BigInt::decode(get_attribute_value(AttributeType::PublicExponent)))
+                   BigInt::decode(get_attribute_value(AttributeType::PublicExponent))),
+     m_use_software_padding(false)
    {
    }
 
 PKCS11_RSA_PrivateKey::PKCS11_RSA_PrivateKey(Session& session, uint32_t bits,
                                              const RSA_PrivateKeyGenerationProperties& priv_key_props)
-   : Object(session), RSA_PublicKey()
+   : Object(session), RSA_PublicKey(), m_use_software_padding(false)
    {
    RSA_PublicKeyGenerationProperties pub_key_props(bits);
    pub_key_props.set_encrypt(true);
@@ -171,6 +174,32 @@ class PKCS11_RSA_Decryption_Operation final : public PK_Ops::Decryption
       Blinder m_blinder;
    };
 
+// note: multiple-part decryption operations (with C_DecryptUpdate/C_DecryptFinal)
+// are not supported (PK_Ops::Decryption does not provide an `update` method)
+class PKCS11_RSA_Decryption_Operation_Software_EME final : public PK_Ops::Decryption_with_EME
+   {
+   public:
+      PKCS11_RSA_Decryption_Operation_Software_EME(const PKCS11_RSA_PrivateKey& key,
+                                                   const std::string& padding,
+                                                   RandomNumberGenerator& rng)
+         : PK_Ops::Decryption_with_EME(padding)
+         , m_raw_decryptor(key, rng, "Raw")
+         {}
+
+      size_t plaintext_length(size_t ctext_len) const override
+         {
+         return m_raw_decryptor.plaintext_length(ctext_len);
+         }
+
+      secure_vector<uint8_t> raw_decrypt(const uint8_t input[], size_t input_len) override
+         {
+         return m_raw_decryptor.decrypt(input, input_len);
+         }
+
+   private:
+      Botan::PK_Decryptor_EME m_raw_decryptor;
+   };
+
 // note: multiple-part encryption operations (with C_EncryptUpdate/C_EncryptFinal)
 // are not supported (PK_Ops::Encryption does not provide an `update` method)
 class PKCS11_RSA_Encryption_Operation final : public PK_Ops::Encryption
@@ -345,7 +374,14 @@ PKCS11_RSA_PrivateKey::create_decryption_op(RandomNumberGenerator& rng,
                                             const std::string& params,
                                             const std::string& /*provider*/) const
    {
-   return std::make_unique<PKCS11_RSA_Decryption_Operation>(*this, params, rng);
+   if(params != "Raw" && m_use_software_padding)
+      {
+      return std::make_unique<PKCS11_RSA_Decryption_Operation_Software_EME>(*this, params, rng);
+      }
+   else
+      {
+      return std::make_unique<PKCS11_RSA_Decryption_Operation>(*this, params, rng);
+      }
    }
 
 std::unique_ptr<PK_Ops::Signature>
diff --git a/src/lib/prov/pkcs11/p11_rsa.h b/src/lib/prov/pkcs11/p11_rsa.h
index 50bb688e6..ceb78ed2c 100644
--- a/src/lib/prov/pkcs11/p11_rsa.h
+++ b/src/lib/prov/pkcs11/p11_rsa.h
@@ -198,6 +198,16 @@ class BOTAN_PUBLIC_API(2,0) PKCS11_RSA_PrivateKey final :
       /// @return the exported RSA private key
       RSA_PrivateKey export_key() const;
 
+      /**
+       * If enabled, the PKCS#11 module gets to perform the raw RSA decryption
+       * using a blinded ciphertext. The EME unpadding is performed in software.
+       * This essenially hides the plaintext value from the PKCS#11 module.
+       *
+       * @param software_padding  if true, perform the unpadding in software
+       */
+      void set_use_software_padding(bool software_padding) { m_use_software_padding = software_padding; }
+      bool uses_software_padding() const { return m_use_software_padding; }
+
       secure_vector<uint8_t> private_key_bits() const override;
 
       std::unique_ptr<Public_Key> public_key() const override;
@@ -211,6 +221,9 @@ class BOTAN_PUBLIC_API(2,0) PKCS11_RSA_PrivateKey final :
          create_signature_op(RandomNumberGenerator& rng,
                              const std::string& params,
                              const std::string& provider) const override;
+
+   private:
+      bool m_use_software_padding;
    };
 
 using PKCS11_RSA_KeyPair = std::pair<PKCS11_RSA_PublicKey, PKCS11_RSA_PrivateKey>;


Test patch:
diff --git a/src/tests/test_pkcs11_high_level.cpp b/src/tests/test_pkcs11_high_level.cpp
index 54fd5a707..cb77ddf48 100644
--- a/src/tests/test_pkcs11_high_level.cpp
+++ b/src/tests/test_pkcs11_high_level.cpp
@@ -804,7 +804,7 @@ Test::Result test_rsa_encrypt_decrypt()
    // generate key pair
    PKCS11_RSA_KeyPair keypair = generate_rsa_keypair(test_session);
 
-   auto encrypt_and_decrypt = [&keypair, &result](const std::vector<uint8_t>& plaintext, const std::string& padding)
+   auto encrypt_and_decrypt = [&keypair, &result](const std::vector<uint8_t>& plaintext, const std::string& padding, const bool blinding)
       {
       std::vector<uint8_t> encrypted;
 
@@ -822,6 +822,7 @@ Test::Result test_rsa_encrypt_decrypt()
 
       try
          {
+         keypair.second.set_use_software_padding(blinding);
          Botan::PK_Decryptor_EME decryptor(keypair.second, Test::rng(), padding);
          decrypted = decryptor.decrypt(encrypted);
          }
@@ -835,12 +836,14 @@ Test::Result test_rsa_encrypt_decrypt()
 
    std::vector<uint8_t> plaintext(256);
    std::iota(std::begin(plaintext), std::end(plaintext), static_cast<uint8_t>(0));
-   encrypt_and_decrypt(plaintext, "Raw");
+   encrypt_and_decrypt(plaintext, "Raw", false);
 
    plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x00 };
-   encrypt_and_decrypt(plaintext, "EME-PKCS1-v1_5");
+   encrypt_and_decrypt(plaintext, "EME-PKCS1-v1_5", false);
+   encrypt_and_decrypt(plaintext, "EME-PKCS1-v1_5", true);
 
-   encrypt_and_decrypt(plaintext, "OAEP(SHA-1)");
+   encrypt_and_decrypt(plaintext, "OAEP(SHA-1)", false);
+   encrypt_and_decrypt(plaintext, "OAEP(SHA-1)", true);
 
    keypair.first.destroy();
    keypair.second.destroy();