Crypto ++在加密/解密期间显式破坏?

Posted

技术标签:

【中文标题】Crypto ++在加密/解密期间显式破坏?【英文标题】:Crypto++ explicit destruction during encryption/decryption? 【发布时间】:2017-07-21 13:40:43 【问题描述】:

我编写了一些包装函数来使用 crypto++ 加密/解密文件。我尝试在 wiki 中查找,但可以找到我的答案。我想知道是否需要显式销毁创建的对象?

我在 wiki 中发现某些对象在传递给函数时会为您销毁,但没有我的确切使用示例,所以我只是想确定一下。

   CryptoPP::AutoSeededRandomPool prng;
   //Key generation
   byte key[AES::DEFAULT_KEYLENGTH];
   prng.GenerateBlock(key, sizeof(key));
   //IV generation
   byte iv[AES::BLOCKSIZE];
   prng.GenerateBlock(iv, sizeof(iv));



   //print key
   encoded.clear();
   StringSource(key, sizeof(key), true, new HexEncoder(new StringSink(encoded)));
   cout << "key: " << encoded << endl;
   cout << "Size of key: " << sizeof(key) << endl;

   //print iv
   encoded.clear();
   StringSource(iv, sizeof(iv), true, new HexEncoder(new StringSink(encoded)));
   cout << "iv: " << encoded << endl;
   cout << "Size of iv: " << sizeof(iv) << endl;

   //See function below
   encrypt_file(inFile, outFile, key, iv, err); 

   inFile.close();
   outFile.close();

一旦在这个函数中,字节数组由于某种原因被截断

加密文件

    bool encrypt_file(std::ifstream& inFile,
       std::ofstream& outFile,
       const byte* key, const byte* iv,
       std::string& errMsg)
    
       std::string encoded;
       //print key
       encoded.clear();
       StringSource(key, sizeof(key), true, new HexEncoder(new StringSink(encoded)));
       cout << "key: " << encoded << endl;
       cout << "Size of key: " << sizeof(key) << endl;

       //print iv
       encoded.clear();
       StringSource(iv, sizeof(iv), true, new HexEncoder(new StringSink(encoded)));
       cout << "iv: " << encoded << endl;
       cout << "Size of iv: " << sizeof(iv) << endl;
       try 
          CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption e;
          e.SetKeyWithIV(key, sizeof(key), iv);
          CryptoPP::FileSource(inFile, true, new CryptoPP::StreamTransformationFilter(e, new CryptoPP::FileSink(outFile)));
          inFile.close();
          outFile.close();
       
       catch (CryptoPP::Exception& e) 
          errMsg = e.GetWhat();
          return false;
       
       return true;
    

输出:

key: 6574D7BDFD0DD3BC59CD3846D4A196A8
Size of key: 16
iv: 1B4ED692F91A32246B41F63F6B8C6EAA
Size of iv: 16
key: 6574D7BDFD0DD3BC
Size of key: 8
iv: 1B4ED692F91A3224
Size of iv: 8

【问题讨论】:

【参考方案1】:

不,你没有。您创建的对象有automatic storage duration,这意味着它们的析构函数将在其作用域结束时自动调用。此外,您使用new 传递的参数将归 Crypto++ 对象所有,它们相应的析构函数将为您释放内存。它们属于 sinkfilter 的类别,事实证明您也传递了所有权。更多详情见:

https://www.cryptopp.com/wiki/Pipelining#Ownership

基本上是这样的(超级简化的例子):

#include <iostream>

struct Foo;

class X

    Foo *p_;
public:
    X(Foo* p): p_(p) 
    // we'd also need a copy ctor and copy assignment operator, ignored here
    ~X()
    
        std::cout << "Releasing the memory...\n";
        delete p_;
    
;

int main()

    X x(new Foo()); // sinking, no memory leak

Live on Coliru

我不得不说,这是迄今为止我最不喜欢的软件设计风格。一个人可以使用模板和 mixin 来实现类似的事情(阅读 policy based design),而没有指针浮动,没有明确的所有权。

【讨论】:

很棒的解释!谢谢!是的,我还没有遇到过这种类型的设计。 @hbchevelle68 这是一个很好的问题,我记得当我第一次遇到 Crypto++ 时,我问过自己同样的问题。我仍然认为他们的设计可以改进。 本地创建的对象或提供线程同步的全局对象,怎样才能更好地扩展?对象的本地创建和销毁可能会影响性能,因为它们是从同一个堆中分配的。【参考方案2】:

我编写了一些包装函数来使用 crypto++ 加密/解密文件。我尝试在 wiki 中查找,但可以找到我的答案。我想知道是否需要显式销毁创建的对象?

这取决于。来自重要使用说明下的README(这两项已列出):

    如果 A 的构造函数采用指向对象 B 的指针(除了基本类型,如 int 和 char),则 A 拥有 B 并将删除 B 在 A 的破坏。如果 A 的构造函数引用了 对象 B,则调用者保留 B 的所有权,不应 销毁它,直到 A 不再需要它为止。

    Crypto++ 在类级别是线程安全的。这意味着您可以在多线程应用程序中安全地使用 Crypto++,但您必须提供 当多个线程访问一个公共 Crypto++ 对象时同步。

这是您的代码。它看起来不错,不需要更改。但是为了完整起见,我们可以遍历它(为简洁起见,删除了CryptoPP):

FileSource(inFile, true, new StreamTransformationFilter(encryptor, new FileSink(outFile)));
    您拥有基于FileSource 的堆栈。它是一个自动变量,超出范围时会被删除。它的样板 C++。 你有inFile。它是一个参考,你有责任删除它。它基于堆栈,并且在超出调用者范围时被删除。它的样板 C++。 您已使用new 创建了StreamTransformationFilter。它是一个指针,FileSource 拥有它。当FileSource 析构函数运行时,它将被删除。管道是一种后天习得的品味。 你有encryptor。它是一个参考,你有责任删除它。它基于堆栈,超出范围时将被删除。它的样板 C++。 您已使用new 创建了FileSink。它是一个指针,StreamTransformationFilter 拥有它。当StreamTransformationFilter 析构函数运行时,它将被删除。管道是一种后天习得的品味。 你有outFile。它是一个参考,你有责任删除它。它基于堆栈,并且在超出调用者范围时被删除。它的样板 C++。

信息在 wiki 上,但如果您不知道自己在寻找什么,就很难找到。另请参阅 wiki 上的 Pipelining | Ownership。


相关,这看起来很可疑:

e.SetKeyWithIV(key, sizeof(key), iv);

因为key是声明为... byte key[], byte iv[] ...的函数参数,所以我相信decays to a pointer的大小为4(i686)或8(x86_64)。你应该使用类似下面的东西,它允许你指定数组的大小:

bool encrypt_file(std::ifstream& inFile,
    std::ofstream& outFile,
    const byte* key, size_t ksize,
    const byte* iv, size_t vsize,
    std::string& errMsg)

    ...
    e.SetKeyWithIV(key, ksize, iv);
    ...

所以,给定:

byte key[AES::DEFAULT_KEYLENGTH];
prng.GenerateBlock(key, sizeof(key));
byte iv[AES::BLOCKSIZE];
prng.GenerateBlock(iv, sizeof(iv));

然后这样称呼它:

encrypt_file(inFile, outFile, key, sizeof(key), iv, sizeof(iv), err); 

【讨论】:

哇,我得到了 2 个超级棒的答案。走过有助于使整个事情的头成为尾,我真的很感激。我继续并听取了您对字节指针的建议,感谢您的收获! 在集成字节指针后,我做了检查以验证大小,它仍然会降级到大小 8(从 16)。有什么想法吗? @hbchevelle68 - 我们需要看看您是如何调用encrypt_filedecrypt_file 的。如果是SecByteBlock key,只需使用key, key.size()。如果是std::string key,则使用reinterpret_cast&lt;const byte*&gt;(&amp;key[0]), key.size() 我继续编辑发布以显示我如何调用它以及密钥生成和输出。 您可以使用key, sizeof(key)调用函数。在调用者中,sizeof(key) 将是 AES::DEFAULT_KEYLENGTH。但是,一旦进入被调用函数,byte key[AES::DEFAULT_KEYLENGTH] 就会降级为大小为 4 (i686) 或 8 (x86_64) 的指针。

以上是关于Crypto ++在加密/解密期间显式破坏?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Crypto++ 中使用 RSA OAEP SHA-256 加密/解密数据

对称加密-DES加密解密

crypto-js 与 php-mcrypt AES 加密/解密

Harmony OSARK UIets使用第三方类库crypto实现加密解密

在 Java 中加密字符串并在 C++ 中解密。加密++

Windows 2000 和 Windows 2003 RC2 使用 Windows Crypto API 加密/解密?