【问题标题】:implementation of sha-256 in perl在 perl 中实现 sha-256
【发布时间】:2012-06-01 19:40:57
【问题描述】:

我正在努力实现 sha-256 算法。我对消息的填充有问题。对于 sha-256,您必须在消息末尾附加一位,到目前为止,我已经使用 $message .= (chr 0x80); 下一步应该是用 0 填充 emtpy 空间(512 位块)。 我用这个公式计算它:l+1+k=448-l 然后将它附加到消息中。 现在我的问题来了:在最后一个 64 位块中附加消息长度的二进制表示,并再次用 0 填充其余部分。由于 perl 自己处理它们的数据类型,所以没有“字节”数据类型。我怎样才能确定我应该附加哪个值?

另请参阅官方规范: http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf

【问题讨论】:

  • 为什么不直接使用Digest::SHA / Digest::SHA::PurePerl
  • 我这样做是为了教育目的
  • 几乎每个人都会将其作为 32 位整数。我还没有看到一个库(在 c 或 perl 中)逐字节进行。

标签: perl padding sha


【解决方案1】:

如果可能的话,从架子上拿出一些东西。您不想推出自己的 SHA-256 实施,因为要获得官方的认可,您必须对其进行认证。

也就是说,规范是

5.1.1 SHA-1、SHA-224 和 SHA-256

假设消息的长度Ml位。将位 1 附加到消息的末尾,然后是 k 个零位,其中 k 是方程的最小非负解

l + 1 + k ≡ 448 mod 512

然后附加等于使用二进制表示表示的数字l 的 64 位块。例如,(8 位 ASCII)消息“abc”的长度为 8 × 3 = 24,因此消息用一位填充,然后 448 - (24 + 1) = 423 零位,然后是消息长度,成为 512 位填充消息

                                  423       64
                                 .-^-.  .---^---.
01100001  01100010  01100011  1  00…00  00…011000
   “a”       “b”       “c”                  '-v-'
                                             l=24

那么填充消息的长度现在应该是 512 位的倍数。

您可能很想使用vec,因为它允许您寻址单个位,但您必须解决时髦的寻址问题。

如果位为 4 或更少,则将字符串分成字节,然后每个字节的位分为 8/BITS 组。字节的位以 little-endian-ish 方式编号,如0x010x020x040x080x100x200x400x80。例如,将单个输入字节chr(0x36) 分成两组给出一个列表(0x6, 0x3);将其分成 4 组给出(0x2, 0x1, 0x3, 0x0)

相反,B*pack 模板指定

一个位串(每个字节内的降序位顺序)。

N

“网络”(大端)顺序的无符号长整数(32 位)。

后者对于组装消息长度很有用。尽管pack 有一个用于quad 的Q 参数,但结果是本机顺序。

从一些准备工作开始

our($UPPER32BITS,$LOWER32BITS);
BEGIN {
  use Config;
  die "$0: $^X not configured for 64-bit ints"
    unless $Config{use64bitint};

  # create non-portable 64-bit masks as constants
  no warnings "portable";
  *UPPER32BITS = \0xffff_ffff_0000_0000;
  *LOWER32BITS = \0x0000_0000_ffff_ffff;
}

那么你可以将pad_message定义为

sub pad_message {
  use bytes;

  my($msg) = @_;
  my $l = bytes::length($msg) * 8;
  my $extra = $l % 512;  # pad to 512-bit boundary
  my $k = 448 - ($extra + 1);

  # append 1 bit followed by $k zero bits
  $msg .= pack "B*", 1 . 0 x $k;

  # add big-endian length
  $msg .= pack "NN", (($l & $UPPER32BITS) >> 32), ($l & $LOWER32BITS);

  die "$0: bad length: ", bytes::length $msg
    if (bytes::length($msg) * 8) % 512;

  $msg;
}

假设代码打印带有填充的消息

my $padded = pad_message "abc";

# break into multiple lines for readability
for (unpack("H*", $padded) =~ /(.{64})/g) {
  print $_, "\n";
}

那么输出就是

6162638000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000018

符合规范。

【讨论】:

  • 在将您的代码编辑为我的之后,它似乎工作了!很好的解释,谢谢。之后我想以 cc 的身份发布“我的”作品。你会同意吗?这是 github 链接 github.com/napcae/sha256_perl 编辑:我必须修改我的评论。我注释掉了die "$0: $^X not configured for 64-bit ints" unless $Config{use64bitint}; 并用print unpack("H*", $message); 打印了这条消息,但这给了我类似 616263800000...001800000018 的信息
  • @TrungNguyenChi 上面的代码是一份礼物。随意将其包含在您的存储库中。我强烈建议反对注释掉use64bitint 检查,因为代码将在不支持64 位int 的平台上静默失败。无声的失败是最糟糕的一种。我很好奇:是什么促使您评论该能力测试?
  • 因为代码仅适用于 64 位版本的 perl,谢谢。我会及时通知你
【解决方案2】:

首先,我希望您将其作为练习——核心中有一个 Digest 模块,它已经可以很好地计算 SHA-256。

注意$message .= (chr 0x80); 附加一个字节,而不是一个位。如果您确实需要按位操作,请查看vec 函数。

要获得整数的二进制表示,您应该使用pack。要使其达到 64 位,请执行以下操作

$message .= pack 'Q', length($message)

请注意,“Q”格式仅适用于 64 位 perls;如果你的不是一个,只需将四个 0 字节与一个 32 位值连接起来(包格式 L)。

【讨论】:

  • 我仍然得到 1 的 ascii 表示,尽管我连接了 $message .= 0b1;。我不明白如何使用vec,也没有很好的教程:/
猜你喜欢
  • 2015-06-01
  • 2012-07-07
  • 1970-01-01
  • 1970-01-01
  • 2016-09-30
  • 2012-08-09
  • 2018-02-06
  • 2019-11-22
  • 1970-01-01
相关资源
最近更新 更多