【问题标题】:How can I reliably transpose an array of chars to an array of uint64_t and back again如何可靠地将字符数组转换为 uint64_t 数组并再次返回
【发布时间】:2017-12-29 16:38:29
【问题描述】:

我有一个对 DES 进行编码的课程项目,但在从输入文件中获取 64 位块时遇到问题。

在加密阶段一切正常,但是当我开始解密时,在将文件读入缓冲区后,我的 CharArrayToInt64() 函数将输入缓冲区的大部分替换为 F。

这是我的功能:

    void charArrayToInt64(uint64_t *bufferText, char *buffer, long length){
        for(size_t i = 0; i < length/8 + 1; i++){
            bufferText[i] = 0x0;
            bufferText[i] = ((uint64_t)buffer[i*8]<<56)
                            | ((uint64_t)buffer[i*8 + 1]<<48)
                            | ((uint64_t)buffer[i*8 + 2]<<40)
                            | ((uint64_t)buffer[i*8 + 3]<<32)
                            | ((uint64_t)buffer[i*8 + 4]<<24)
                            | ((uint64_t)buffer[i*8 + 5]<<16)
                            | ((uint64_t)buffer[i*8 + 6]<<8)
                            | ((uint64_t)buffer[i*8 + 7]);
        }
    }

这是缓冲区的输出,也是加密阶段uint64_t的数组:

    Buffer:                              uint64_t array:
    5468 6973 2069 7320                  5468 6973 2069 7320
    6120 7465 7874 2066                  6120 7465 7874 2066
    696c 6520 7772 6974                  696c 6520 7772 6974
    7465 6e20 666f 7220                  7465 6e20 666f 7220
    7465 7374 2070 7572                  7465 7374 2070 7572
    706f 7365 732e 2049                  706f 7365 732e 2049
    6620 4445 5320 776f                  6620 4445 5320 776f
    726b 7320 636f 7272                  726b 7320 636f 7272
    6563 746c 792c 2061                  6563 746c 792c 2061
    6e64 2074 6865 2066                  6e64 2074 6865 2066
    696c 6520 6973 2072                  696c 6520 6973 2072
    6561 6420 616e 6420                  6561 6420 616e 6420
    7370 6c69 7420 7072                  7370 6c69 7420 7072
    6f70 6572 6c79 2c20                  6f70 6572 6c79 2c20
    7468 6973 2074 6578                  7468 6973 2074 6578
    7420 7368 6f75 6c64                  7420 7368 6f75 6c64
    2072 6574 7572 6e20                  2072 6574 7572 6e20
    7265 6164 6162 6c65                  7265 6164 6162 6c65
    2efc                                 ffff ffff fd7f 0000

以下是解密阶段缓冲区和 uint64_t 数组的输出(注意它们与上面的不匹配,因为我使用的是之前的加密文本):

    Buffer:                              uint64_t array:
    e824 8aa4 db58 5b12                  ffff ffff db58 5b12
    b8d2 2b8f 980c 915f                  ffff ffff ffff 915f
    f942 a226 9c69 bcc4                  ffff ffff ffff ffc4
    c660 bd78 179d b628                  ffff ffff ffff b628
    1ed1 d846 ceb1 f8b5                  ffff ffff ffff ffb5
    2e67 fa25 66bd 0f13                  ffff ffff ffbd 0f13
    d11d 1203 d10f dc9e                  ffff ffff ffff ff9e
    6124 0cf1 9393 3816                  ffff ffff ff93 3816
    efab b9ad fb20 23c0                  ffff ffff ffff ffc0
    6a2a 20c1 a610 1422                  ffff ffff a610 1422
    119d d9c5 9de1 0f08                  ffff ffff ffe1 0f08
    331e d4e7 2214 bdb1                  ffff ffff ffff ffb1
    c408 74e2 6e14 84e6                  ffff ffff ffff ffe6
    3fe6 5eca 04c5 70c6                  ffff ffff ffff ffc6
    12f8 bcaa 1df7 342d                  ffff ffff fff7 342d
    93eb 15d8 eb8d b51e                  ffff ffff ffff b51e
    7fd2 a2d7 b357 a6eb                  ffff ffff ffff ffeb
    7fb9 bf2b 0ebe bb99                  ffff ffff ffff ff99
    3300                                 3300 0000 0000 0000

如您所见,在加密阶段,我的函数只弄乱了最后一行,而在解密阶段,完全相同的函数弄乱了最后一行...

任何帮助都将不胜感激,因为过去几个小时我一直在试图解决这个问题,但无济于事。

如果您需要/想要任何额外的信息,我会尽力提供。


EDIT-1:在@coderredoc、@chux 和@Myst 的帮助下,我的问题基本上得到了解决,而且代码也更加简洁! (以前的函数charArrayToInt64() 不再使用)

还有一个问题:如果bufferText 中的最后一个 64 位块没有被fread() 完全填充,我最终会得到我似乎无法摆脱的尾随位(即使我设置了完整的块在填充之前为 0)。这会导致解密阶段出现问题:

原始输入文件:

这是一个为测试目的而编写的文本文件。如果 DES 工作正常,并且文件被正确读取和拆分,则此文本应返回可读且没有错误。

带有尾随“随机”位的解密输出:

这是一个为测试目的而编写的文本文件。如果 DES 正常工作,并且文件被正确读取和拆分,则此文本应返回可读且没有错误ÒÕäTR†

我试过了: memset(bufferText, 0, length + 8);(以及最后一个参数的变化) bufferText[length/8] = bufferText[length/8]&lt;&lt;((length%8)*8);(以及之后将它们移回原始位置)


EDIT-2

以前的编辑已过时,原来我跳过了要求我一次读取一个字节的作业的一部分,而这又需要使用fgetc()fputc() 进行读写。所有答案仍然非常有帮助,而且非常重要,非常感谢!

【问题讨论】:

  • 为什么最后几行不完整?你能提供完整的线路吗?
  • bufferTextbuffer 有多大,buffer 中希望未使用的部分如何初始化?
  • @coderredoc 如果你在谈论缓冲区中的最后几行,它们并不完整,其余都是 0 的 @alk bufferTextbuffer 分别是 length/8 + 1 和 @987654337 @long with length = ftell(ptrR)(在执行fseek(ptrR, 0, SEEK_END)之后)
  • sry 对于双重帖子,我的电脑拒绝让我再次编辑以前的评论以获得更高的精度:@alk 考虑到 buffer 是按照给定文件所需的确切大小创建的,那里不应该是未使用的部分,另一方面,bufferText 确实有未使用的部分,但由于它们都是可变长度的,具体取决于文件大小,我没有专门初始化 bufferText(fread(buffer, length, 1, ptrR); 是缓冲区的唯一定义)
  • 首先我们说的是从Bufferuint64_t数组的第二次转换。不是吗? buffer 包含左列数据,buFFertext 将包含形成的数组。并在每个位置 e824 等每个字节上缓冲 conatins。澄清这一点。确认我是否正确。

标签: c arrays encryption byte


【解决方案1】:

在这个答案中,我将解决实际问题:

从输入文件中获取 64 位块时遇到问题

...而不是目前提到的那个:

在将我的文件读入缓冲区后,我的 CharArrayToInt64() 函数将输入缓冲区的大部分替换为 F。

我建议(恕我直言)将文件直接读入uint64_t 数组/缓冲区,而不是从字节数组中复制数据。

虽然unsigned char 数组您可能会遇到内存对齐问题,但uint64_t 数组可以保证正确对齐内存并且您只需要担心填充...

...哦,请使用 unsigned 版本,正如此线程中的其他人所指出的那样,这就是导致转移问题的原因。

一旦两个数组(加密和解密)都使用相同的无符号类型,您的代码就会看起来更清晰。

祝你好运。

【讨论】:

  • 同意除“可能暴露字节序问题”之外的所有内容。很可能在读取了 some 字节序的 64 位数据后,该 64 位数据包可能需要对字节序进行调整以进一步进行uint64_t 处理。 IAC,首先将其读入uint64_t 是一个非常好的主意,原因是提到的其他原因,并且可以进行任何字节序调整。
  • @Myst 我试一试,我使用了unsigned char *buffer,然后进行了转换,因为我在早上发现的每个fread() 示例都特别说要使用@987654330 @ 所以我认为这是唯一的方法,我想我应该直接查看文档!
  • @chux - 谢谢,我删除了关于字节序问题的部分,因为你可能是对的。但是,我发现某些计算允许在数据进出数值对象时抵消字节顺序。
  • @Chris,uint64_t 数组可以安全地转换为 char * 指针...但由于内存对齐问题,反过来就不行了。
  • @myst True - 如果数据的字节序与处理器字节序匹配,则在无操作时调整字节序。 (非常快那些无操作)
【解决方案2】:

在您的情况下,问题是 char 已签名(这是实现定义的 - char 在您的机器中默认已签名) - 因此,当移动时,对于具有 MSB 1 的字符,它会被符号扩展。导致数字左侧出现一系列ff

例如,如果我们将 0x24 扩展到所有这些班次,它将是

2400000000000000
24000000000000
240000000000
2400000000
24000000
240000
2400
24

0xe8 也是这样

e800000000000000
ffe8000000000000
ffffe80000000000
ffffffe800000000
ffffffffe8000000
ffffffffffe80000
ffffffffffffe800
ffffffffffffffe8

现在想想这个,当你OR 和其他人一起时,ff 的胜利和所有位都设置好了。你会看到ff的。

在你的情况下正确的解决方案是

  bufferText[i]  = (((uint64_t)buffer[i*8]<<56) & (uint64_t)0xff<<56)
                                | (((uint64_t)buffer[i*8 + 1]<<48) & (uint64_t)0xff<<48)
                                | (((uint64_t)buffer[i*8 + 2]<<40) & (uint64_t)0xff<<40)
                                | (((uint64_t)buffer[i*8 + 3]<<32) & (uint64_t)0xff<<32)
                                | (((uint64_t)buffer[i*8 + 4]<<24) & (uint64_t)0xff<<24)
                                | (((uint64_t)buffer[i*8 + 5]<<16) & (uint64_t)0xff<<16)
                                | (((uint64_t)buffer[i*8 + 6]<<8) & (uint64_t)0xff<<8)
                                | (((uint64_t)buffer[i*8 + 7]) & (uint64_t)0xff<<0);

正如 chux 所提到的,有一个使用unsigned char 类型转换的更清洁的解决方案,类似于

 bufferText[i] = ((uint64_t)(unsigned char)buffer[i*8]<<56) |
                  ...

编辑-1

如果您已经将 char 缓冲区类型声明为 unsigned,则不需要 void charArrayToInt64(uint64_t *bufferText, char *buffer, long length){,而是在函数中明确提及它是 unsigned char 缓冲区

void charArrayToInt64(uint64_t *bufferText,unsigned char *buffer, long length){ ...

那么就不需要显式转换了。你会用你原来的方法做得很好。


使用memset 可以将整个缓冲区归零。检查函数。此外,如果您在使用bufferText[i] = 0x0; 的上下文中进行讨论,那么从逻辑上讲,您甚至不需要使用0 对其进行初始化,因为毕竟您是在下一次设置它。这就是为什么您甚至可以从代码 bufferText[i] = 0x0; 中删除这一行,以及另一件事,而不是使用 long 长度,最好使用 size_tlength 一起使用。

【讨论】:

  • 非常感谢!!我不可能自己发现这个,因为在我的主文件中我将缓冲区声明为unsigned char buffer,所以我什至没有想过要查看函数声明!一个额外的快速问题:考虑到它是一个可变大小的数组,用全 0 初始化 bufferText 有没有比循环它更快的方法?
  • @Chris.: memset 就是这样。
  • @Chris 如果buffer 中的mainunsigned char buffer,为什么使用char *buffer 编码charArrayToInt64()?相反,使用void charArrayToInt64(uint64_t *bufferText, unsigned char *buffer, long length) 编码并避免(unsigned char) 演员表。
  • @chux.:看来 OP 忘了检查他是如何声明的。否则不会出现问题。
  • @chux.: 我明白了..你是说我们正在设置它,为什么要用0 来初始化它?没错
猜你喜欢
  • 2011-06-23
  • 1970-01-01
  • 2016-10-19
  • 2010-11-28
  • 2015-09-02
  • 2012-07-18
  • 1970-01-01
  • 2016-04-11
  • 2012-02-11
相关资源
最近更新 更多