encryption - 用于蒙特卡洛测试的 AES 验证标准伪代码缺少什么?
问题描述
我正在尝试在 CBC 模式下使用规定的 AES-128 验证程序,如NIST AESAVS 标准中所定义。测试套件中更重要的部分之一是 Monte Carlo 测试,它提供了一种生成许多 10000 个伪随机测试用例的算法,因此硬编码电路不太可能伪造 AES。其中的算法伪代码似乎对可变范围和定义采取了一些自由,所以我希望有人可以帮助我填写缺失的信息以正确解释这一点。
128 位密钥情况的逐字算法如下:
Key[0] = Key
IV[0] = IV
PT[0] = PT
For i = 0 to 99
Output Key[i]
Output IV[i]
Output PT[0]
For j = 0 to 999
If ( j=0 )
CT[j] = AES(Key[i], IV[i], PT[j])
PT[j+1] = IV[i]
Else
CT[j] = AES(Key[i], PT[j])
PT[j+1] = CT[j-1]
Output CT[j]
Key[i+1] = Key[i] xor CT[j]
IV[i+1] = CT[j]
PT[0] = CT[j-1]
对于上面的伪代码,从这些初始值开始:
Key = 9dc2c84a37850c11699818605f47958c
IV = 256953b2feab2a04ae0180d8335bbed6
PT = 2e586692e647f5028ec6fa47a55a2aab
外循环的前三个迭代应该输出:
KEY = 9dc2c84a37850c11699818605f47958c
IV = 256953b2feab2a04ae0180d8335bbed6
PLAINTEXT = 2e586692e647f5028ec6fa47a55a2aab
CIPHERTEXT = 1b1ebd1fc45ec43037fd4844241a437f
KEY = 86dc7555f3dbc8215e6550247b5dd6f3
IV = 1b1ebd1fc45ec43037fd4844241a437f
PLAINTEXT = c1b77ed52521525f0a4ba341bdaf51d9
CIPHERTEXT = bf43583a665fa45fdee831243a16ea8f
KEY = 399f2d6f95846c7e808d6100414b3c7c
IV = bf43583a665fa45fdee831243a16ea8f
PLAINTEXT = 7cbeea19157ec7bbf6289e2dff5e8ee4
CIPHERTEXT = 5464e1900f81e06f67139456da25fc09
看起来我们在内部循环之外使用j ,我相信这是混乱的根源。我最初假设这意味着无论密文CT的最终值是什么(CT[999]),这会让我相信下一个外部循环PT[0]的明文被初始化为CT[998]。但是,这种解释与给定的预期输出不匹配。
我还认为,括号在这里可能不是表示值数组,而是表示相对于现在的时间步长。然而,这也使得在循环之外引用j令人困惑。如果循环已过期,那么i或j是当前时间吗?
我在这里错过了一些关键步骤吗?是否有错字(文档中没有勘误表)?
对此事有一些经验的人可以对适当的解释发表评论吗?
解决方案
几个月前,我试图让 AES CBC MonteCarlo 在 Java 上运行。我遇到了同样的问题,但最终我可以找到一个完整且可运行的解决方案,满足 NIST 官方矢量结果。
在我开始之前-您的初始测试向量似乎是一个自己的向量,而不是 NIST 提供的向量-这里是 NIST 官方网站的链接,其中包含所有 AES 测试向量:
NIST 网站:https ://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/Block-Ciphers Montecarlo 测试向量:https ://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-验证程序/documents/aes/aesmct.zip
我的测试将从这些数据开始:
[ENCRYPT]
COUNT = 0
KEY = 8809e7dd3a959ee5d8dbb13f501f2274
IV = e5c0bb535d7d54572ad06d170a0e58ae
PLAINTEXT = 1fd4ee65603e6130cfc2a82ab3d56c24
CIPHERTEXT = b127a5b4c4692d87483db0c3b0d11e64
并且该函数对内部和外部循环使用“双”字节数组。我没有在 SO 上提供完整的源代码,但完整的代码可在我的 GitHub 存储库https://github.com/java-crypto/Known_Answer_Tests以及许多其他测试和测试向量文件中找到。加密/解密必须使用NoPadding完成- 不要在默认模式下使用 AES,因为在大多数情况下它将使用 PKCS#5/#7 填充运行。
如果您愿意,可以在此处在线运行代码(简化为 AES CBC 128 MonteCarlo):https ://repl.it/@javacrypto/AesCbcMonteCarloTest#Main.java
该程序将运行完整的加密和解密测试并进行额外的交叉检查(意味着加密结果通过解密进行检查,反之亦然)。
因为几个月前我处理了这个问题,所以我只是用 Java 代码提供我的解决方案——希望它可以帮助你理解 NIST 测试过程。
public static byte[] aes_cbc_mct_encrypt(byte[] PLAINTEXT, byte[] KEYinit, byte[] IVinit) throws Exception {
int i = 0; // outer loop
int j = 0; // inner loop
byte[][] KEY = new byte[101][128];
byte[][] IV = new byte[1001][128];
byte[][] PT = new byte[1001][128]; // plaintext
byte[][] CT = new byte[1001][128]; // ciphertext
byte[] CTsave = new byte[256]; // nimmt den letzten ct fuer nutzung als neuen iv auf
// init
int KEYLENGTH = KEYinit.length * 8;
KEY[0] = KEYinit;
IV[0] = IVinit;
PT[0] = PLAINTEXT;
for (i = 0; i < 100; i++) {
for (j = 0; j < 1000; j++) {
if (j == 0) {
CT[j] = aes_cbc_encrypt(PT[j], KEY[i], IV[i]);
CTsave = CT[j]; // sicherung fuer naechsten iv
PT[j + 1] = IV[i];
} else {
IV[i] = CTsave;
CT[j] = aes_cbc_encrypt(PT[j], KEY[i], IV[i]);
CTsave = CT[j];
PT[j + 1] = CT[j - 1];
}
}
j = j - 1; // correction of loop counter
if (KEYLENGTH == 128) {
KEY[i + 1] = xor(KEY[i], CT[j]);
}
if (KEYLENGTH == 192) {
KEY[i + 1] = xor192(KEY[i], CT[j - 1], CT[j]);
}
if (KEYLENGTH == 256) {
KEY[i + 1] = xor256(KEY[i], CT[j - 1], CT[j]);
}
IV[i + 1] = CT[j];
PT[0] = CT[j - 1];
ctCalculated[i] = CT[j].clone();
}
return CT[j];
}
public static byte[] xor(byte[] a, byte[] b) {
// nutzung in der mctCbcEncrypt und mctCbcDecrypt methode
byte[] result = new byte[Math.min(a.length, b.length)];
for (int i = 0; i < result.length; i++) {
result[i] = (byte) (((int) a[i]) ^ ((int) b[i]));
}
return result;
}
public static byte[] aes_cbc_encrypt(byte[] plaintextByte, byte[] keyByte, byte[] initvectorByte) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
byte[] ciphertextByte = null;
SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES");
IvParameterSpec ivKeySpec = new IvParameterSpec(initvectorByte);
Cipher aesCipherEnc = Cipher.getInstance("AES/CBC/NOPADDING");
aesCipherEnc.init(Cipher.ENCRYPT_MODE, keySpec, ivKeySpec);
ciphertextByte = aesCipherEnc.doFinal(plaintextByte);
return ciphertextByte;
}
推荐阅读
- mysql - 无法使用 awk 将数据插入 mysql 数据库
- scala.js - 升级到 1.0.0 版后不能再使用匿名类
- javascript - Wordpress“事件日历”编辑月视图
- python - 如何从位置参数增加文件名?
- javascript - 你如何测试一个内部有很多“this”的函数?
- apache-spark - Spark SubQuery 扫描整个分区
- javascript - 单击分页数字返回上一个选择(ng-bootstrap)角度8
- django - Django ORM - 在 FROM 子句中使用子查询
- apache-flink - Flink 作业生产就绪 - 验证分配给所有操作员的 UUID
- sql - 递归树作为新属性值子项中的列表(数组项)