【问题标题】:Crypto++ explicit destruction during encryption/decryption?Crypto ++在加密/解密期间显式破坏?
【发布时间】: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

【问题讨论】:

    标签: c++ encryption crypto++


    【解决方案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(这两项已列出):

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

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

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

    FileSource(inFile, true, new StreamTransformationFilter(encryptor, new FileSink(outFile)));
    
    1. 您拥有基于FileSource 的堆栈。它是一个自动变量,超出范围时会被删除。它的样板 C++。
    2. 你有inFile。它是一个参考,你有责任删除它。它基于堆栈,并且在超出调用者范围时被删除。它的样板 C++。
    3. 您已使用new 创建了StreamTransformationFilter。它是一个指针,FileSource 拥有它。当FileSource 析构函数运行时,它将被删除。管道是一种后天习得的品味。
    4. 你有encryptor。它是一个参考,你有责任删除它。它基于堆栈,超出范围时将被删除。它的样板 C++。
    5. 您已使用new 创建了FileSink。它是一个指针,StreamTransformationFilter 拥有它。当StreamTransformationFilter 析构函数运行时,它将被删除。管道是一种后天习得的品味。
    6. 你有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) 的指针。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-06
    • 1970-01-01
    • 1970-01-01
    • 2018-06-26
    • 2014-09-12
    相关资源
    最近更新 更多