从学习路线进入这组任务。交付物是三份逐块计算、一次只用公开记录构造的有效新标签,以及三项改变输入结构后的复算。所有密钥和nonce均为公布的离线测试值;不要拿它们保护真实数据。
下载标准库核验器,使用Python3运行:
python foundation-block-modes-check.py
python -O foundation-block-modes-check.py
两个命令均输出同一份JSON。检查由显式require执行,不会随-O消失。代码不联网、不修改外部状态,AES实现用于逐步阅读,并无常数时间保证。输出含公开测试值的认证子密钥和中间链,只能用于离线教学。
一、CMAC:把40字节消息认证到最后一块
使用CMAC的AES-128密钥
2b7e151628aed2a6abf7158809cf4f3c。
完整测试消息M为以下64字节;主任务先取前40字节:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
先计算L=E_K(0¹²⁸),再算K₁、K₂。交付三行格式化输入和链值,说明为什么前两块不加掩码,而8字节末块补80及零后异或K₂。最终标签应为dfa66747de9ae63030ca32611497c827;对应输出位置是cmac.vectors中n=40的trace。
空串、前16字节、完整64字节的标签应分别为bb1d6929e95937287fa37d129b756746、070a16b46b4d4144f79bdd9dd04a287c、51f0bebf7e3b9d92fc49741779363cfe。空串也消耗一个认证块,不能因0能被16整除而误走完整块分支。
接着实现一个允许任意正整块长度的裸CBC标签oracle。询问首块M₁得到t,提交M₁∥(M₁⊕t)和旧t,验证器会接受。对CMAC必须只拿它自己公开的标签τ构造M₁∥(M₁⊕τ),不能偷偷复用内部裸链t;附件这次尝试被拒绝。解释两个接口的首块内部链与公开标签在哪一步不再相等。
结构迁移:把消息由前40字节扩成前48字节。现在末块恰满16字节,改为异或K₁,不再加pad;第三块格式化结果为cb26ca5e962dd777997e2196683cfa31,标签为c47c4d9d64588f67fb9de6fe745d7fbf。两个长度都用三次消息AES,变化来自末块含义,而不是循环次数。该结果是本附件复算值,不是RFC4493的四个标准向量之一。
二、GCM:七块认证输入与一份新鲜伪造
K feffe9928665731c6d6a8f9467308308
IV cafebabefacedbaddecaf888
A 3ad77bb40d7a3660a89ecaf32466ef97f5d3d585
P d9313225f88406e5a55909c5aff5269a
86a7a9531534f7da2e4c303d8a318a72
1c3c0c95956809532fcf0e2449a6b525
b16aedf5aa0de657ba637b39
P的最后一行只有12字节。四个正文计数块后缀依次为2、3、4、5,输出C为
42831ec2217774244b7221b784d0d49c
e3aa212f2c02a4e035c17e2329aca12e
21d514b25466931c7d8f6a5aac84aa05
1ba30b396a0aac973d58e091
交付GHASH的七块输入和七个链值。前两块来自A,中间四块来自C,第七块是00000000000000a000000000000001e0。域乘法的单位元编码为80000000000000000000000000000000;用整数1代替会造成位序错误。
应得到H=b83b533708bf535d0aa6e52980d53b78,GHASH末值=c23b3d63d2ed95056ca342769cd13c03,完整标签=f07c2528eea2fca1211f905e1b6a881b。这是NIST公布的AES128 #5。正文AES四次、标签AES一次、域乘法七次;同一密钥预计算H的一次AES另外计。
现在故意重复该IV,令A为空,分别加密16字节全零与01后接15个零。只把两份(C,T)交给forge_one_block,不传K、H或明文。函数在域中求
再将第一份密文的最高位翻转,构造新标签。输出中的gcm_forgery应给出G=8a6ff5aca561c0d865805055eb728397,新C=1bb22ce7d9f372c1ee2b28722b25f206,新T=ad724e10a3864da40e699a213bc83989。验证成功返回80后接15个零,且这份密文不是先前两个查询之一。这个实验故意违反IV唯一性,不属于正常nonce-respecting保证。
结构迁移A:在原20字节A末尾追加一个零,明文不变。密文仍相同,补零后的两个AAD块也相同,只有第七块的AAD位长度从0xa0变0xa8。重新计算标签,并用checks.aad_zero_extension比对。接收方用旧A验证新标签必须失败。
结构迁移B:令第二个重复IV记录变成两整块D₁、D₂。设Λ₁编码16字节正文、Λ₂编码32字节正文,则
这里的C₁表示第一条记录的一整块密文。写出次数和两个长度块,说明为何不能再用一块公式做一次除法便得到H²。该任务改变了代数关系,不只替换测试数字。
三、SIV:短尾、长尾和向量分界
先复算AES-SIV的RFC5297附录A.1:
K fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0
f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
A1 101112131415161718191a1b1c1d1e1f2021222324252627
P 112233445566778899aabbccddee
前半密钥给CMAC,后半给CTR。交付D₀、处理A₁后的D₁、短尾认证输入、V、清位后的Q和密文。输出为
V 85632d07c6e8f37f950acd320a2ecc93
C 40c02b9690c4dc04daef7f6afe5c
V中第31、63位的清零只发生在Q副本上。若输出的V也被改掉,接收方重算S2V便对不上。
再使用附录A.2的长尾向量。下面三个关联数据分量必须保持分界,nonce是第三个分量:
K 7f7e7d7c7b7a79787776757473727170
404142434445464748494a4b4c4d4e4f
A1 00112233445566778899aabbccddeeffdeaddadadeaddadaffeeddccbbaa99887766554433221100
A2 102030405060708090a0
N 09f911029d74e35bd84156c5635688c0
P this is some plaintext to encrypt using SIV-AES
P按ASCII编码为47字节。最终D异或到P末16字节,然后认证整个修改后的47字节串;CTR使用三个块。结果为
V 7bdb6e3b432667eb06f4d14bff2fbd0f
C cb900f2fddbe404326601965c889bf17
dba77ceb094fa663b7a3f748ba8af829e
a64ad544a272e9c485b62a3fd5c0d
结构迁移:将A.1明文追加两个零,成为16字节,最后一步从短尾pad切换到xorend。输出V应为b99e2795c1c86eb1d4cb7f85aae7d1d9,完整Z见checks.siv16。再把A.2的A₁、A₂交换但保留nonce第三的位置,用旧Z解密应返回FAIL。最后比较向量["ab","c"]与["a","bc"]:它们拼接相等,S2V的逐分量调用却不同,附件在固定样例给出不同输出。
四、交付边界与验收
提交计算表之外,还要回答以下问题,并用返回值而非打印候选值验收:
- GCM为何可以先验标签再解正文,SIV却必须先在内部解候选?坏标签时两个公开open接口都应返回None。
- SIV重复同一完整输入为何返回同一个Z?重复nonce但改变明文时,哪些输入进入了S2V,为什么不再必然复用Q?解释允许的相等泄漏。
- 原样重复提交正确记录时,两个模式都接受;若应用只许交付一次,需要在哪里增加状态?
- 空S2V分量列表、无关联数据的空明文、一个空关联数据加空明文是三个不同调用。附件给出三个值,列出各自经过的分支。
- GCM标准长度边界用长度函数即可验证,无需分配巨大数组;SIV至多126个关联数据分量,而最终明文占第127个。教学上限为总字节1 MiB,不能据此推断密钥寿命。
主脚本检查13类非法参数拒绝、79个标签或密文字节翻转、向量顺序和边界、32位与128位计数进位、实际AES调用数。有限测试能够发现实现与规范不一致,却不能证明PRP/PRF假设、替代认证安全归约或认证生产侧信道。三篇正文给出相应数学条件与原始规范位置。