【问题标题】:(C language) Garbled decryption in AES-CTR based encryption decryption tool(C语言) 基于AES-CTR的加密解密工具中的乱码解密
【发布时间】:2016-04-10 07:14:21
【问题描述】:

解密和加密代码(包括在下面)可能是错误的。他们正在编译无错误,但是,解密文件与纯文本文件不同。是乱码。

我试图找出它为什么是乱码。我怀疑我的代码没问题。

我做了很多可能解决问题的事情,但都没有奏效。我有一种使用 AES-CTR 的特定方式,即我不直接包含 OpenSSL。我依赖于 /dev/urandom /dev/random 的随机性(IV 等)。

这些事情包括我处理加密和解密函数的方式(例如:使用*(ptr) 而不是ptr[] 和其他几个等效的替换,这通常是不必要的),我传递和检查的参数是什么在最终保存加密或解密文本之前进行某些验证。

我没有增加(或改变)初始化的 IV。 IV 是 16 个字节。

之前,我这样做是为了增加 IV 的最后一个字节:

size_t blocksize = CCA_STRENGTH; /* CCA_STRENGTH is "#define CCA_STRENGTH 16" */
char *iv = (char *) malloc(16 * sizeof(char));
ri(); /* PRNG initialized.*/
prng_getbytes(iv, blocksize);
*   
*
*
*
size_t iv_counter = 0;
while loop starts here...
*
*
*(iv +(block_size - 1)) += iv_counter;
*
*
while loop ends here...

下面是发生加密和解密的 while 循环(这部分暂时删除,因为它是安全要求,而不是编码标准或加密或解密函数的要求)。 p>

这是我的解密代码:

#include "pv.h"

void decrypt_file (const char *ptxt_fname, void *raw_sk, size_t raw_len, int fin)
{
    int fd;
   if((fd = open(ptxt_fname, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0)
       {
           /*fd will become -1 on failure.*/
           perror("plaintext file permission error\n");
           exit(EXIT_FAILURE);
           }

    size_t block_size = CCA_STRENGTH;

  const char *aes_key;
  aes_key = (const char *) raw_sk;
  aes_ctx aes;

  aes_setkey(&aes, aes_key, block_size);
  char *iv;  
  iv = (char *) malloc(block_size * sizeof(char));
  size_t bytes_read;
  bytes_read = read(fin, iv, block_size);

  char *buf = (char *) malloc(block_size * sizeof(char)); /*ctxt-file read buffer*/
  char *ptxt = (char *) malloc(block_size * sizeof(char)); /*p-text buffer*/
  memset(ptxt, 0, block_size);

    bytes_read = read(fin, buf, block_size);

        while(bytes_read >= 1)
    {

        aes_encrypt(&aes, ptxt, iv); /* earlier it was "aes_decrypt(&aes, ptxt, iv);" which was not the correct reverse transformation function */
        for(loop_variable = 0; loop_variable < bytes_read; loop_variable++)
        {
            *(ptxt + loop_variable) = *(ptxt + loop_variable) ^ *(buf + loop_variable);
        }

        if((result = write_chunk(fd, ptxt, bytes_read)) == -1)
        {
            perror("Problem when writing to ptxt file... \n");
            close(fd);
            unlink(ptxt_fname); /*for file deletion*/
            aes_clrkey(&aes);
            free(ptxt);
            free(iv);
            free(buf);
            exit(EXIT_FAILURE);
        }
        if((bytes_read = read(fin, buf, block_size)) < 1)
        {
            close(fd);
            aes_clrkey(&aes);
            free(ptxt);
            free(iv);
            free(buf);
            break;
        }
    }

}

void 
usage (const char *pname)
{
  printf ("Simple File Decryption Utility\n");
  printf ("Usage: %s SK-FILE CTEXT-FILE PTEXT-FILE\n", pname);
  printf ("       Exits if either SK-FILE or CTEXT-FILE don't exist, or\n");
  printf ("       if a symmetric key sk cannot be found in SK-FILE.\n");
  printf ("       Otherwise, tries to use sk to decrypt the content of\n");
  printf ("       CTEXT-FILE: upon success, places the resulting plaintext\n");
  printf ("       in PTEXT-FILE; if a decryption problem is encountered\n"); 
  printf ("       after the processing started, PTEXT-FILE is truncated\n");
  printf ("       to zero-length and its previous content is lost.\n");

  exit (1);
}

int main (int argc, char **argv)
{
  int fdsk, fdctxt;
  char *sk = NULL;
  size_t sk_len = 0;

  if (argc != 4) {
    usage (argv[0]);
  }   
  else if (((fdsk = open (argv[1], O_RDONLY)) == -1)
       || ((fdctxt = open (argv[2], O_RDONLY)) == -1)) {
    if (errno == ENOENT) {
      usage (argv[0]);
    }
    else {
      perror (argv[0]);

      exit (-1);
    }
  }   
  else {
    setprogname (argv[0]);

    if (!(sk = import_sk_from_file (&sk, &sk_len, fdsk))) {
      printf ("%s: no symmetric key found in %s\n", argv[0], argv[1]);

      close (fdsk);
      exit (2);
    }
    close (fdsk);

    decrypt_file (argv[3], sk, sk_len, fdctxt);    
    bzero(sk, sk_len);
    free(sk);


    close (fdctxt);
  }

  return 0;
}

这是我的加密代码:

#include "pv.h"

void encrypt_file (const char *ctxt_fname, void *raw_sk, size_t raw_len, int fin)
{

    size_t block_size = CCA_STRENGTH;
    int fd; /* ctxt fd */
    if((fd = open(ctxt_fname,O_WRONLY|O_TRUNC|O_CREAT,0600)) < 0)
    {
        perror("Ciphertext file permission error\n");
        exit(EXIT_FAILURE);
    }

    char *iv;
    iv = (char *) malloc(block_size * sizeof(char));

    ri(); /*IV initialized*/
    prng_getbytes(iv, block_size);

    struct aes_ctx aes;
    const char *aes_key = aes_key = (const char *) raw_sk;

    aes_setkey(&aes, aes_key, block_size); /*sets the encryption key.*/

    char *buf = buf = (char *) malloc(block_size * sizeof(char)); /*file read buffer*/
    char *ctxt = ctxt = (char *) malloc(block_size * sizeof(char)); /*ciphertext buffer*/
    int result;
    size_t looper = 0;
    size_t bytes_read;
    result = write_chunk(fd, iv, block_size); 

    if(result == -1)
    {
        exit(-1);
    }

    bytes_read = read(fin, buf, block_size); /*returns how many bytes read*/
    while(bytes_read >= 1)
    {
        aes_encrypt(&aes, ctxt, iv);
        for(looper = 0; looper < bytes_read; looper++)
        {

            *(ctxt + looper) = *(ctxt + looper) ^ *(buf + looper);
        }

        result = write_chunk(fd, ctxt, bytes_read);
        if(result == -1)
        {
            perror("Problem when writing to ctxt file... \n");
            close(fd);
            unlink(ctxt_fname); /*for file deletion*/
            aes_clrkey(&aes);
            free(ctxt);
            free(iv);
            free(buf);
            exit(EXIT_FAILURE);
        }       
        printf("crossed written to file\n");

        if((bytes_read = read(fin, buf, block_size)) < 1)
        {
            close(fd);
            aes_clrkey(&aes);
            free(ctxt);
            free(iv);
            free(buf);
            break;
        }

    }
}

void usage (const char *pname)
{
    printf ("Personal Vault: Encryption \n");
    printf ("Usage: %s SK-FILE PTEXT-FILE CTEXT-FILE\n", pname);
    printf ("       Exits if either SK-FILE or PTEXT-FILE don't exist.\n");
    printf ("       Otherwise, encrpyts the content of PTEXT-FILE under\n");
    printf ("       sk, and place the resulting ciphertext in CTEXT-FILE.\n");
    printf ("       If CTEXT-FILE existed, any previous content is lost.\n");

    exit (1);
}

int main (int argc, char **argv)
{
    int fdsk, fdptxt;
    char *raw_sk;
    size_t raw_len;



    if (argc != 4)
    {
        usage (argv[0]);
    }
    else if (((fdsk = open (argv[1], O_RDONLY)) == -1) || ((fdptxt = open (argv[2], O_RDONLY)) == -1))   
    {
        if (errno == ENOENT)
        {
            usage (argv[0]);
        }
        else
        {
            perror (argv[0]);

            exit (-1);
        }
    }
    else
    {
        setprogname (argv[0]);

        if (!(import_sk_from_file (&raw_sk, &raw_len, fdsk)))   
        {
            printf ("%s: no symmetric key found in %s\n", argv[0], argv[1]);

            close (fdsk);
            exit (2);
        }
        close (fdsk);


        bzero(raw_sk, raw_len);
        free(raw_sk);


        close (fdptxt);
    }

    return 0;
}

加密代码首先将 16 字节的 IV 添加到密文文件(我猜应该是这样;我们在解密过程中从 ctxt 文件中读取这 16 个字节),然后进行实际加密(将其视为黑盒子)我们发送IV和密钥的地方;返回 16 个字节。返回的这 16 个字节必须与明文文件缓冲区进行异或(最多 16 个字节,如果文件超过 16 个字节,则每轮)。

之后,异或字节(最多 16 个字节)被写入文件。这件事一直发生到最后一轮,它最终打破了尝试读取没有更多内容要读取的文件的循环。 read 函数,在每次调用时,都会尝试读取下一个可用字节(直到指定的字节数)。

这不是一个完美的实现,因为我已经放松了安全方面(随机化每个周期的 IV),但是,它必须首先像这样工作。

在数学上,我想做的应该是这个概念的精确复制:

ciphertext_block = message_block ^ [AES(IV, Key)]

message_block = ciphertext_block ^ [AES(IV, Key)]

这里,ciphertext/message_block 是指一个字符块,在第 1 到 n-1 轮中为 16 字节,但在最后一轮中可以或不能是 16 字节。

在任何情况下,XORing 将在 16 个字节字符(AES-CTR 的输出)和另一个块(用于加密的消息块,用于解密的密文块,任何情况下,它最多可以是 16 个字节,因此它们是XOR 约束,即它们将成为 XOR 输出长度的决定者)。

由于它们是决策者,一旦 XORing 操作覆盖了它们的长度,XORing 循环就会停止,我们继续将其写入文件(用于加密的 ctxt 文件,用于解密的 ptxt 文件)。

不应有任何填充要求。

我不确定是否应该在任何地方使用realloc 函数。

如果任何读者在理解我正在尝试做的事情时遇到问题,我很乐意提供额外的文档。

该程序有几个库依赖项,但是,代码编译时没有错误。

这是我的编译命令:

gcc -g -O2 -ansi -Wall -Wsign-compare -Wchar-subscripts -Werror -I. -I/usr/include/ -I/home/devel/libdcrypt/include/ -c pv_keygen.c pv_misc.c
gcc -g -O2 -ansi -Wall -Wsign-compare -Wchar-subscripts -Werror -o pv_keygen pv_keygen.o pv_misc.o -L. -L/usr/lib/ -L/home/devel/libdcrypt/lib/ -ldcrypt  -lgmp

这只是为了编译我的加密文件。几乎相等(同样严格)的命令适用于我的密钥生成和解密文件。注册机代码似乎工作正常(我还没有在这里包含);我能够生成一个密钥并将其序列化为一个文件。该密钥文件实际上包含两个密钥,我只阅读 AES-CTR 的前半部分。下半部分将用于 MACing 目的(使用 AES-CBC)。

文件大小详情

明文文件:x 个字节

密文文件:(x + 16) 字节

解密的文本文件:x 个字节

统计是正确的,内容是错误的。解密后的文本文件和明文文件必须相同。

我正在尝试:diff plaintext_file decrypted_file 在 RedHat 上进行文件比较。

密钥文件实际上是 32 个字节,其中前 16 个字节用于加密,后面的 16 个字节将用于 MACing 后加密。

密钥文件(序列化为base-64)(十六进制):

0000000 4662 6e4b 6631 7268 4876 676c 772f 664e
0000010 4d5a 6f32 384e 5141 7139 6635 3442 7245
0000020 646c 4a77 5553 4c30 4f63 3d6f 000a     
000002d

输入明文文件(十六进制):

0000000 6161 6161 6161 6161 6161 6161 610a 6161
0000010 000a                                   
0000011

加密文件(十六进制):

0000000 540e 0e30 d74d 5000 78c1 13e3 0476 d4a2
0000010 61c9 76ac e717 cd6d 013e e872 8e16 4827
0000020 00a2                                   
0000021

解密文件(十六进制):

0000000 8bea 616a 1f1b d6b0 fd13 da46 5824 ec00
0000010 0081                                   
0000011

外部参考(我已将内容缩减到可能存在错误的位置):

1.) http://www.scs.stanford.edu/nyu/05sp/lab/lab1.html

2) http://www.scs.stanford.edu/nyu/05sp/lab/install-libs.html

【问题讨论】:

  • Side:“做了一堆随机的事情”——飞镖板和眼罩即使在看似简单的任务上也很少起作用,而在密码学之类的事情上几乎从来没有。所以这个结果有点自我预言。
  • @WhozCraig Randomness 无济于事。让我重新引用。这些是试图使其发挥作用的相当深思熟虑的尝试。似乎出现了严重错误(仅在加密和/或解密中)。这就是我所期待的。
  • 这对于reviewdebug 来说相当复杂。在发布之前,请确保您首先正确地编写您的应用程序。所以从加密代码中删除文件处理,并围绕它编写单元测试。很高兴看到您尝试消除复杂性,例如IV 创建,但您需要更进一步。
  • @MaartenBodewes 谢谢。我在逻辑上减少了代码。加密或解密文件应该有问题。社区审查可能会有所帮助。文件无论如何编译都没有错误,除了解密是乱码(但是,与明文的大小相同,因为它现在应该根据这个实现)
  • 为什么不在 CTR 模式下使用 AES,不需要每 16 字节进行一次 AES 调用。

标签: c user-interface cryptography aes encryption-symmetric


【解决方案1】:

为什么不在 CTR 模式下使用 AES,无需为每 16 字节进行一次 AES 调用,然后将输出与数据进行异或。

CTR 模式接受纯文本、密钥和 IV,并生成 AES CTR 模式加密数据。 IOW CTR 模式执行所有异或运算和计数器递增。

注意 CTR 模式,相同的密钥/IV 对只能使用一次。

大多数支持 AES 和其他分组密码的库都具有低级别的分组加密,并使用它来实现更高级的模式,例如 CBC、CFB、CTR、OFB 和选项,例如 PKCS#7 née PKCS# 5 填充。

【讨论】:

    【解决方案2】:

    这里是修复:

    问题存在于我想要做的方式和我正在做的方式。由于不存在与实现相关的问题,因此问题存在于我的解密代码中。

    在我的解密代码中,当我真正要寻找的是一个精确的反向转换(正确的技术术语超过解密 w.r.t. AES)相反,相同的密钥被用于解密相同的文件。

    作为修复,aes_decrypt 应该只是 aes_encrypt 并且会成功。

    现在,我可以继续进行额外的添加(加强安全性和数据完整性考虑)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-09
      • 1970-01-01
      • 2021-06-21
      • 2015-04-21
      • 1970-01-01
      • 1970-01-01
      • 2020-01-20
      • 2016-08-22
      相关资源
      最近更新 更多