字符串加密函数与char []一起使用,但不是普通字符串

时间:2016-05-05 15:32:25

标签: c++

我使用维基百科的xtea加密版本,用C ++编写。我写了一个加密字符串的函数

const char* charPtrDecrypt(const char* encString, int len, bool encrypt)
    {

        /********************************************************
        * This currently uses a hard-coded key, but I'll implement
        * a dynamic key based on string length or something.
        *********************************************************/
        unsigned int key[4] = { 0xB5D1, 0x22BA, 0xC2BC, 0x9A4E };

        int n_blocks=len/BLOCK_SIZE;
        if (len%BLOCK_SIZE != 0)
            ++n_blocks;

        for (int i = 0; i < n_blocks; i++)
        {
            if (encrypt)
                xtea::Encrypt(32, (uint32_t*)(encString + (i*BLOCK_SIZE)), key);
            else
                xtea::Decrypt(32, (uint32_t*)(encString + (i*BLOCK_SIZE)), key);
        }
        return encString;
    }

当我提供const char encString[] = "Hello, World!"时它会起作用,但是当我提供原始字符串时,例如const char* a = charPtrDecrypt("Hello, World!", 14, true)它崩溃了。

2 个答案:

答案 0 :(得分:4)

有一句古老的说法(我知道它已经老了,因为我在1992年左右首次将它发布到Usenet):&#34;如果你撒谎到编译器,它会得到它的。报复&#34;这就是这里发生的事情。

下面:

const char* charPtrDecrypt(const char* encString, int len, bool encrypt)

...您保证修改encString指向的字符。这就是const所说的/意味着什么。

然而,这里:

xtea::Encrypt(32, (uint32_t*)(encString + (i*BLOCK_SIZE)), key);

...你抛弃了const ness(强制转换为uint32_t *,没有const限定符),并将指针传递给修改它指向的缓冲区的函数。

然后编译器得到它的报复:它允许你传递指向你无法修改的数据的指针,因为你保证不会修改它 - 但是当你转身时无论如何,尝试修改它,程序崩溃并烧毁,因为你试图修改只读数据。

这可以通过多种方式避免。一种方法是远离现在使用的相对较低级别的构造,并传递/返回std::string而不是指向[const] char的指针。

尽管如此,代码仍然存在更多问题。首先,它将输入视为一个uint32_t项块,并将其长度 up 的视图四舍五入为uint32_t(通常为4)的下一个倍数。不幸的是,它实际上并没有改变缓冲区的大小,所以即使缓冲区是可写的,它也不能正常工作 - 它仍然可以读取和写入超出缓冲区的末尾。

再次,std::string会有所帮助:它允许我们将字符串调整到正确的大小,而不是仅仅读取/写入固定大小缓冲区的末尾。

除此之外,编译器还不会关心,但是你(以及任何此代码的读者)将(或至少应该):函数的名称具有误导性,并且其参数的含义根本不明显 - 尤其是控制是加密还是解密的布尔值。我建议使用枚举,并将函数重命名为可以包含加密或解密的内容:

最后,我移动if语句来决定是否在循环外加密或解密,因为我们在处理一个输入字符串时不会从一个变为另一个

考虑到所有这些因素,我们最终会得到类似这样的代码:

enum direction { ENCRYPT, DECRYPT };

std::string xtea_process(std::string enc_string, direction d) {

    unsigned int key[4] = { 0xB5D1, 0x22BA, 0xC2BC, 0x9A4E };

    size_t len = enc_string.size();

    len += len % BLOCK_SIZE;    // round up to next multiple of BLOCK_SIZE

    enc_string.resize(len);     // enlarge the string to that size, if necessary

    if (direction == DECRYPT)
        for (size_t i = 0; i < len; i+=BLOCK_SIZE)
            xtea::Decrypt(32, reinterpret_cast<uint32_t *>(&encString[i]), key);
    else
        for (size_t i = 0; i < len; i += BLOCK_SIZE)
            xtea::Encrypt(32, reinterpret_cast<uint32_t *>(&encString[i]), key);
    }
    return encString;
}

这仍然会留下(至少)一个我无法处理的问题:某些机器可能对uint32_t比对char有更严格的对齐要求,而且&# 39;理论上可能在string中使用的缓冲区不符合那些更严格的对齐要求。您可能遇到需要将数据从string复制到正确对齐uint32_t访问的缓冲区,执行加密/解密,然后复制结果的情况回来。

答案 1 :(得分:2)

您将常量 const char*传递给该函数,但将其强制转换为非常量 uint32_t*。我想xtea::Encrypt修改了字符串缓冲区。

在第一个版本const char encString[] = "Hello, World!"中,变量 - 虽然是const - 很可能位于可修改的堆栈上。所以删除const并不“很好”,但它确实有效。

在第二个版本中,字符串很可能位于只读数据段中。抛弃const让你调用加密函数,但是一旦函数真正尝试修改字符串就会崩溃。