假设某网站托管着一个任何人都可以下载的视频大文件F。下载文件的浏览器需要确保文件 是真实的,然后才能向用户显示视频内容。一种可行的方法是让网站使用抗碰撞散列函数 来散列F的内容,然后通过一些可信信道将得到的散列值h = H(F)分发给用户(稍后我们 将使用数字签名)。浏览器下载整个文件F,检查H(F)是否等于可信的哈希值h;假如相等, 浏览器便将视频显示给用户。
然而,这种方法意味着只有在下载好完整的视频之后才能开始播放视频内容。我们本次实 验的目标是构建一个文件认证系统,使得浏览器在下载时可以对视频块进行身份验证和播 放,而无需等待整个文件的下载。
网站不计算整个文件的散列值,而是将文件分成1KB块(1024字节)。 它首先计算最后一个 块的哈希值,并将值附加到倒数第二个块末尾。然后,它计算扩充后的倒数第二个块的哈 希值,并将结果哈希值追加到第三个块的末尾。以此类推,直到处理完所有的块,如下图 所示:
最终的哈希值h0(带有扩充哈希值的第一个块的哈希值)被通过可信信道分发给用户。
现在,浏览器以每次一个块的方式下载文件F,其中每个块包含上图中的附加哈希值。当接 收到第一个块(B0 ∥ h1)后,浏览器检查H(B0 ∥ h1)是否等于h0;假如相等,浏览器就开始播 放第一个视频块。当接受到第二个块(B1 ∥ h2)后,浏览器检查H(B1 ∥ h2)是否等于h1;假如 相等,浏览器就开始播放第二个视频块。此过程一直持续到最后一个块。这样,每个块都 会在接收时进行认证和播放,无需等到整个文件下载完毕。
编写代码来计算文件video.mp4 (见附件)的哈希值h0,并完成对文件块的验证过程。
• 使用SHA256作为哈希函数。对于SHA256的实现,使用现有的加密库,如PyCrypto1 (Python),Crypto++2(C++)或任何其他语言和库。
• 将哈希值附加到每个块时,以二进制数据形式进行,即32个未编码的字节(256 位)。
• 如果文件大小不是1KB的倍数,则最后一个块将短于1KB,但所有其他块确保
是1KB。
• 可以使用文件test.mp4(见附件)来检查自己的代码。该文件的h0的十六进制编码为:
03c08f4ee0b576fe319338139c045c89c3e8e9409633bea29442e21425006ea8
老师给的实验内容原理说的很清楚,主要原理为下图:
从后往前计算。
首先计算最后一个块的哈希值,扩展block的内容=block内容+后一个块整体的哈希值。逐步向前迭代,直到计算出第一个扩展块SHA256后的数值 h 0 h_0 h0
from Crypto.Hash import SHA256
from binascii import a2b_hex,b2a_hex
def getFile(file,sizeNum):
#打开文件,以二进制数据读取
f=open(file,'rb')
data=f.read()
f.close()
#分块,1KB大小
bytes=len(data)
blocks=[]
for i in range(0,bytes,sizeNum):
blocks.append(data[i:i+sizeNum])
return blocks
def getHash(file):
sizeNum=1024
res_hash=b''
#获取块内容
blocks=getFile(file,sizeNum)
for i in range(len(blocks)-1,-1,-1):
#块内容=块内容+后一块的hash值
blocks[i]=blocks[i]+res_hash
#得到新hash
hash = SHA256.new(blocks[i])
#更新hash值
res_hash=hash.digest()
return b2a_hex(res_hash)
if __name__ == '__main__':
test_file1="/Users/Admin/Desktop/test.mp4"
test_file2 = "/Users/Admin/Desktop/video.mp4"
print("Chain hash for {} is:\n {}".format(test_file1, getHash(test_file1)))
print("Chain hash for {} is:\n {}".format(test_file2,getHash(test_file2)))