【问题标题】:What hash (Python 3 hashlib) yields a portable hash of file contents?什么哈希(Python 3 hashlib)产生文件内容的可移植哈希?
【发布时间】:2021-11-19 04:31:16
【问题描述】:

我想计算一个文件(其长度可以是任意位数,因此不一定是流行的八的倍数)并将该文件与哈希值一起发送给朋友。我的朋友应该能够从文件内容中计算出相同的哈希值。我想使用 Python 3 来计算哈希,但我的朋友不能使用 Python 3(因为我会等到明年发送文件,到那时 Python 3 会过时,他会想要正在使用 Python++ 或其他)。我所能保证的是,我的朋友会知道如何计算散列,在数学意义上——他可能必须编写自己的代码才能在他的MIX 机器的实现上运行(他会知道怎么做)。

我使用什么散列,更重要的是,我使用什么散列?例如,我是否对以 text 格式打开的文件上从 read 返回的 str 进行哈希处理?我是否散列了一些从 binary read 返回的类似 bytes 的对象?如果文件有奇怪的行尾标记怎么办?我是否先填充尾端,以便我要散列的东西大小合适?

import hashlib
FILENAME = "filename"
# Now, what?

我说“位序列是因为不是所有的计算机都是基于 8 位字节的,所以说“字节序列”太含糊了。例如GreenArrays, Inc.设计了一个supercomputer on a chip,其中每台计算机都有18位(18位)字(当这些字用于编码本机指令时,它们由三个5位“字节”和一个每个 3 位字节)。我还了解到,在 1970 年代之前,使用了各种字节大小。虽然 8 位字节可能是最常见的选择,并且在某种意义上可能是最优的,但每个字节 8 位的选择是任意的。

另见

Is python's hash() portable?

【问题讨论】:

  • 您确定要查看的是位序列而不是字节吗?我不知道任何允许以位指定文件大小的文件系统(尽管我只熟悉基于 x86 和 ARM 的机器)。无论哪种方式,您都绝对不想使用str,因为它与文件编码分离。
  • 嗯,也许我不明白。 Python 如何打开文件以读取位序列而不是字节?如果您确实需要将文件发送到具有奇异架构的计算机,您会怎么做?

标签: python hash portability


【解决方案1】:

首先,Python 中的hash() 函数不同于一般的加密哈希函数。区别如下:

hash()

哈希是一个固定大小的整数,用于标识一个特定的值。每个值都需要有自己的哈希值,因此对于相同的值,即使不是同一个对象,您也会得到相同的哈希值。

请注意,一个值的哈希值只需要在一次 Python 运行中相同。在 Python 3.3 中,它们实际上会随着 Python 的每次新运行而改变

What does hash do in python?

加密哈希函数

加密哈希函数 (CHF) 是一种数学算法,可将任意大小的数据(通常称为“消息”)映射到固定大小的位数组

它是确定性的,这意味着相同的消息总是会产生相同的哈希值。

https://en.wikipedia.org/wiki/Cryptographic_hash_function


现在让我们回到你的问题:

我想计算文件内容(位序列)的哈希值(其长度可以是任意位数,因此不一定是流行的八的倍数)并将该文件发送给朋友与哈希值。我的朋友应该能够从文件内容中计算出相同的哈希值。

您正在寻找的是加密哈希函数之一。通常,要计算文件哈希,使用 MD5、SHA-1、SHA-256。您想以 binary 格式打开文件并对二进制位进行哈希处理,最后对其进行消化并以十六进制形式对其进行编码。

import hashlib

def calculateSHA256Hash(filePath):
    h = hashlib.sha256()
    with open(filePath, "rb") as f:
        data = f.read(2048)
        while data != b"":
            h.update(data)
            data = f.read(2048)
    return h.hexdigest()

print(calculateSHA256Hash(filePath = 'stackoverflow_hash.py'))

上面的代码将自己作为输入,因此它为自己生成了一个 SHA-256 哈希,即610e15155439c75f6b63cd084c6a235b42bb6a54950dcb8f2edab45d0280335e。只要代码没有更改,这将保持一致。

另一个例子是散列一个txt文件,test.txt,内容为Helloworld

只需将代码的最后一行更改为“test.txt”即可完成

print(calculateSHA256Hash(filePath = 'text.txt'))

这给出了5ab92ff2e9e8e609398a36733c057e4903ac6643c646fbd9ab12d0f6234c8daf 的 SHA-256 哈希。

【讨论】:

  • Binary I/O 使用字节,而不是位。我不确定这对这个答案意味着什么,但知道这一点很重要。
  • @wjandrea , RE: "bytes, not bits"---我已经编辑了这个问题以添加一段详细说明。
  • calculateSHA256Hash 中有一个magic number2048。请加评论解释。
  • 2048 表示正在读取的块的大小。这是为了允许程序在不使用大量内存的情况下加载大文件。您可以谷歌f.read()或类似的词以获取与此主题相关的更多信息,例如官方Python Docs
【解决方案2】:

在查看了 standard 的 SHA-256 之后,我到达了 sha256hexdigestFromFile,这是 @Lincoln Yan 的 calculateSHA256Hash 的替代品。

这也是对我对2048的评论的回应。

def sha256hexdigestFromFile(filePath, blocks = 1):
    '''Return as a str the SHA-256 message digest of contents of
    file at filePath.
        Reference: Introduction of NIST (2015) Secure Hash
    Standard (SHS), FIPS PUB 180-4.  DOI:10.6028/NIST.FIPS.180-4
    '''
    assert isinstance(blocks, int) and 0 < blocks, \
            'The blocks argument must be an int greater than zero.'
    with open(filePath, 'rb') as MessageStream:
        from hashlib import sha256
        from functools import reduce
        def hashUpdated(Hash, MESSAGE_BLOCK):
            Hash.update(MESSAGE_BLOCK)
            return Hash
        def messageBlocks():
            'Return a generator over the blocks of the MessageStream.'
            WORD_SIZE, BLOCK_SIZE = 4, 512 # PER THE SHA-256 STANDARD
            BYTE_COUNT = WORD_SIZE * BLOCK_SIZE * blocks
            yield MessageStream.read(BYTE_COUNT)
        return reduce(hashUpdated, messageBlocks(), sha256()).hexdigest()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-02
    • 1970-01-01
    • 2016-03-20
    • 1970-01-01
    相关资源
    最近更新 更多