【问题标题】:What's the need of NULL terminator at end of decrypted string?解密字符串末尾需要 NULL 终止符是什么?
【发布时间】:2015-03-25 13:20:01
【问题描述】:

我正在开发一个 google chrome NaCl 扩展,该扩展涉及使用 openssl 库函数对数据进行加密和解密。加密工作完美,解密目前也工作正常,但为此我不得不进行某种破解,但我不确定这是否是处理它的正确方法。

else if(action == "decryption")
    {
      pp::Var content = dict_message.Get("content");

      //aes decryption starts here
      pp::VarArrayBuffer buffer(content);
      const char *password = "password";
      unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
      int cipherlen = buffer.ByteLength();
      int len = cipherlen + EVP_MAX_BLOCK_LENGTH;
      unsigned char *plaintext = (unsigned char*)malloc(len*sizeof(unsigned char));
      unsigned char* ciphertext = static_cast<unsigned char*>(buffer.Map());

      aes_init(password, (int)strlen(password), key, iv);
      len = decrypt(ciphertext, cipherlen, key, iv, plaintext);
      buffer.Unmap();
      plaintext[len]='\0';  //fix a bug of overshooting plaintext sent
      //aes decryption ends here

      pp::VarDictionary reply;
      reply.Set("action","decryption");
      reply.Set("plaintext", (char*)plaintext);
      reply.Set("fileSize", len);
      PostMessage(reply);
      free(plaintext);
    }

现在此代码解密数据并发送回扩展页面上的 javascript。请注意plaintext[len]='\0'; 行,如果我不放它,有时我会在plaintext 中正确解密的文本之后得到一个垃圾,这在我的javascript 中反映为null。那么处理这个错误的正确方法是什么?

【问题讨论】:

  • 任何 c 风格字符串的结尾都需要有一个空终止符。如果您的解密方法没有将它放在字符串的末尾,那么您需要手动进行。
  • 如果原始数据加密包含终止空字符,解密数据也不会。因此,您必须提供一个。我的猜测是加密代码使用类似于strlen() 而不是strlen()+1 的原始明文长度加密字符串。顺便说一句,您应该使用像std::vector&lt;char&gt; plaintext(len); 这样的RAII 容器,而不是手动跳过malloc/free 潜在火灾环。在mallocfree 之间抛出的任何异常目前都保证会泄漏内存。

标签: c++ encryption google-chrome-extension openssl google-nativeclient


【解决方案1】:

正如其他人所提到的,您使用的是 C 字符串,它必须以 NULL 结尾。当您调用 pp::VarDictionary::Set 时,the parameters are pp::Vars,并且您正在利用 implicit conversion constructor from C-string to pp::Var.

如果您将纯文本设置为 std::string 或使用 pp::VarArrayBuffer,则无需这样做。 PostMessage() 已针对处理大型 VarArrayBuffers 进行了优化,因此无论如何您应该更喜欢它。所以我建议替换这一行:

unsigned char *plaintext = (unsigned char*)malloc(len*sizeof(unsigned char));

类似:

pp::VarArrayBuffer plaintext(len);

...并将您的解密行更改为:

len = decrypt(ciphertext, cipherlen, key, iv,\b
              static_cast<unsigned char*>(plaintext.Map()));

注意,这会将 JavaScript 接收的类型从字符串更改为 ArrayBuffer。如果您希望它保留为字符串,则可以使用 std::string 代替:

std::string plaintext(len, '\0');

并使用 operator[] 访问字符串的缓冲区。所以解密的调用看起来像这样(我假设 len >0):

len = decrypt(ciphertext, cipherlen, key, iv, &plaintext[0]);

【讨论】:

    【解决方案2】:

    '\0' 是 C 中所有字符串的终止符。由于您的输出是字符串,如果缺少 '\0' 字符,您的程序将不知道字符串在哪里结束,因此可能会继续使用时超出字符串的区域,对应接近末尾的垃圾值。

    无论何时声明字符串,'\0' 都会放在末尾。但是,您首先为解密的文本分配内存,然后写入其中。在这种情况下,您必须注意末尾的字符串终止字符。

    字符串终止字符存在的原因是字符串在C中是以字符数组的形式存储的,仅仅通过指向字符串开头的指针是不知道数组大小的。

    【讨论】:

      【解决方案3】:

      如果\0 不在 c 字符串的末尾,代码将不知道字符串何时结束,并会进入内存的未知部分。空终止符让程序知道“这是我的字符串的结尾”。没有这个,你就会在你的程序中引入未定义的行为。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多