使用memcpy在OpenSUSE 13.2解密期间的垃圾字符

时间:2016-02-09 20:39:46

标签: c linux openssl opensuse sles

我有一个程序,在SUSE Linux Enterprise Server下已经有一段时间了。最近,它被转移到OpenSUSE 13.2系统并遇到了问题。程序与第三方接口,数据被接收到我们的程序中,其中数据块由一些头信息和AES加密数据组成。使用OpenSSL libcrypto库,我们成功地在SLES下连接到该系统。但是,在OpenSUSE下,我们始终看到一个错误,其中解密数据的末尾包含垃圾。我已经确定了问题发生的地方并且有一个解决方法,但是在查看代码时,我不明白为什么会出现问题。

我创建了一个模拟问题的测试程序。测试程序在SUSE Linux Enterprise Server 11和Red Hat 7.2 Enterprise Linux下运行良好,但在OpenSUSE 13.2上使用不同版本的OpenSSL库失败。在SLES和Red Hat下,干净地返回解密数据。在OpenSUSE下,除了在数据块末尾出现的一些垃圾之外,大多数数据都被彻底解密。返回的数据块是正确的,然后包含一些垃圾,然后结束正确。我的示例程序的代码如下所示,但导致问题的行是memcpy(),我将加密数据移到数据块的前面进行处理。我的示例程序中的行如下:

   // Generates Garbage
   memcpy(encbuf, encbuf+100, enclen);                 

如果我将加密数据移动到临时缓冲区,然后再将其移动到encbuf的开头,则不会生成垃圾。

   // This does not generate garbage
   memcpy(tmpbuf, encbuf+100, enclen);                 
   memcpy(encbuf, tmpbuf, enclen);                 

我的示例程序采用明文的已定义缓冲区,使用密钥和IV对其进行加密,然后将其解密并显示结果。浓缩代码如下:

#include <stdio.h>      
#include <stdlib.h>     
#include <string.h>     
#include <unistd.h>     
#include <time.h>       
#include <fcntl.h>      
#include <sys/types.h>  

#include <openssl/evp.h>

#define EVP_DECRYPT   0 
#define EVP_ENCRYPT   1

char clrbuf[100000];
char encbuf[100000];
char tmpbuf[100000];
int clrlen;         
int enclen;         

char enckey[1024];      
unsigned char enciv[16];

main()
{
   int rc;

   // Set clear text to 50 lines of text
   sprintf(clrbuf,                                                   
         "0001this is a test this is a test this is a test this is a test\n" \
         "0002this is a test this is a test this is a test this is a test\n" \
         "0003this is a test this is a test this is a test this is a test\n" \
         // etc etc etc……………….
         "0048this is a test this is a test this is a test this is a test\n" \
         "0049this is a test this is a test this is a test this is a test\n" \
         "0050this is a test this is a test this is a test this is a test\n"  

   sprintf(enckey, "this is the key this is the key ");
   sprintf(enciv, "1234567890123456");

   // Encrypt the data and simulate a 100 byte header by returning encrypted data 100 bytes into the data block
   //
   memcpy(encbuf, "Some header stuff that will need to be removed", 46);
   rc = evp_aes256_cbc(clrbuf, strlen(clrbuf), encbuf+100, &enclen, enckey, enciv, EVP_ENCRYPT);                  

   // Now remove the header by shifting the encrypted data to the start of the data block and decrypt
   // This is where doing the memcpy() as coded in OpenSUSE results in garbage at the end of clrbuf
   // but everything is returned correctly in SLES and Red Hat
   //
   // This work fines on all OSes:
   //         memcpy(tmpbuf, encbuf+100, enclen);                 
   //         memcpy(encbuf, tmpbuf, enclen);                 

   memcpy(encbuf, encbuf+100, enclen);                 
   rc = evp_aes256_cbc(encbuf, enclen, clrbuf, &clrlen, enckey, enciv, EVP_DECRYPT);

   printf("Decrypt: rc=%d  EncLen=%d  ClrLen=%d\n", rc, enclen, clrlen);
   printf("Data:\n\n<\n%s\n>\n\n", clrbuf);                             
}

/****************************************************************************/  

evp_aes256_cbc(char *InBuf, int InLen, char *OutBuf, int OutLen, char *Key, char *IV, int EncryptFlag)              
{                                                                               
   EVP_CIPHER_CTX ctx;                                                          
   int padlen;                                                                  

   EVP_CIPHER_CTX_init(&ctx);                                                   

   if (! EVP_CipherInit_ex(&ctx, EVP_aes_256_cbc(), NULL, Key, IV, EncryptFlag))
      return(0);                                                                

   if (! EVP_CipherUpdate(&ctx, OutBuf, OutLen, InBuf, InLen))
      return(0);                                              

   if (! EVP_CipherFinal_ex(&ctx, OutBuf+(*OutLen), &padlen)) 
      return(0);                                              

   *OutLen = *OutLen + padlen;                                

   EVP_CIPHER_CTX_cleanup(&ctx);                              

   return(1);                                                 
}                                                             

在SLES和Red Hat上,最终输出如下:

  0046这是一个测试,这是一个测试,这是一个测试,这是一个测试   0047这是一个测试,这是一个测试,这是一个测试,这是一个测试   0048这是一个测试,这是一个测试,这是一个测试,这是一个测试   0049这是一个测试,这是一个测试,这是一个测试,这是一个测试   0050这是一个测试,这是一个测试,这是一个测试,这是一个测试

在OpenSUSE上,最终输出可能如下所示:

  0046这是一个测试,这是一个测试,这是一个测试,这是一个测试   0047这是一个测试,这是一个测试,这是一个测试,这是一个测试   0048这是一个测试,这是一个tes╧┬S_úReÅ▌
  |τZk╠½çP≥ii≡w╙p▓8ª'MêÅt▒g{Y¥ΩEô¬   ⌡n}⌐*╘¿μ2└╠LS4=Qüü├;〜&╕ÇLT;╗^¿ßD0┤T.OQΣq#≈
  0050这是一个测试,这是一个测试,这是一个测试,这是一个测试

有什么想法吗?

由于

2 个答案:

答案 0 :(得分:1)

// Generates Garbage
memcpy(encbuf, encbuf+100, enclen);

重叠缓冲区和memcpy是C / C ++中未定义的行为。请改用memmove。听起来你有一个glibc版本,可以向前或向后记忆。在您的情况下,您正在通过HAS_FAST_COPY_BACKWARD

的处理器上执行

这是针对该问题的经典错误报告:Strange sound on mp3 flash website。但Adobe没有任何东西可以让你大吃一惊他们之所以知道这个星球上最不安全的软件是有原因的。

此外,如果您在Valgrind下运行二进制文件,该工具有时会将代码标记为问题。我记得看到Valgrind将其标记为Crypto++ library

size_t ArraySink::Put2(const byte *begin, size_t length, int messageEnd, bool blocking)
{
    // Avoid passing NULL pointer to memcpy. Using memmove due to
    //  Valgrind finding on overlapping buffers.
    size_t copied = 0;
    if (m_buf && begin)
    {
        copied = STDMIN(length, SaturatingSubtract(m_size, m_total));
        memmove(m_buf+m_total, begin, copied);
    }
    m_total += copied;
    return length - copied;
}

答案 1 :(得分:0)

通过其他来源提供的答案:

由于内存重叠,应该使用memmove()而不是memcpy()。