【发布时间】:2021-10-21 08:17:43
【问题描述】:
我有this function 来生成感觉从序列中的随机数:
const fetch = (x, o) => {
if (x >= o) {
return x
} else {
const v = (x * x) % o
return (x <= o / 2) ? v : o - v
}
}
const fetch16 = (x) => fetch(x, 65519)
const fetch8 = (x) => fetch(x, 251)
// the last number can be anything.
const build16 = (x, o) => fetch16((fetch16(x) + o) % 65536 ^ 42703)
const build8 = (x, o) => fetch8((fetch8(x) + o) % 256 ^ 101)
const j = 115; // If you don't want duplicates, either i or j should stay fixed
let i = 0
let invalid = [];
let valid = new Set;
while (i <= 255) { // <-- small fix here!
let x = build8(i, j); // To test, you can swap i and j here, and run again.
if (x > 255) {
invalid.push([ i, j, x ]);
} else {
valid.add(x);
}
i++;
}
console.log("invalid:", JSON.stringify(invalid));
console.log("count of valid:", valid.size);
这不是真正的 PRNG,请查看 linked post 了解理论。这需要一个递增序列并从递增序列生成感觉像随机整数。也就是说,它将递增序列映射到看似随机的输出,但它遵循一个模式!从输出中很难看出该模式,但它都在理论。 “随机”输出从不重复序列长度(build8 的 8 位整数序列从 0 到 255 的 256 个值)。
鉴于此,您如何获取该函数的输出并取回原始输入?假设我们知道 j 最初插入以生成原始输出。假设你知道j 并且有输出号码,你如何取回原始号码?也就是如何反转这个build8或build16函数?
我一开始就卡住了,我不知道如何反转这样的函数实现的理论。如果你知道这个理论并且可以解释一下,也许这会帮助我自己尝试一下,但现在我会在黑暗中拍摄,想知道如果你已经知道这个理论是否很简单。
我们可以通过说我们知道build8 函数中的o 是什么来简化问题,这在正向和反向/反向版本中始终是固定和已知的。所以我们可以摆脱o,离开x。
const build8b = (x) => fetch8((fetch8(x) + 123) % 256 ^ 101)
问题是,你如何找到x 的输出来自build8b?
console.log(build8b(100)) // => 92
// then this is what we need to figure out how to implement:
console.log(reverse8b(92)) // => 100
如果无法找到逆运算,那么这也是一个很好的答案(以及为什么),那么我可以停止寻找解决方案。
【问题讨论】:
-
PRNG 通常是散列函数,因此不能反转。例如,没有取模 (
%) 运算符的逆。请参阅:单向函数。 -
这不像普通的 PRNG,这是一个一对一的映射函数,但它感觉是随机的。请参阅here 它是如何工作的。这就是我说“伪伪RNG”的原因。
-
@LancePollard 你在哪里卡住了?
-
在它的核心,您要反转的公式是
y = x * x % p。在数学上,这是x^2 = y (mod p),其中p是一个奇数素数。 Complexity of finding square roots 谈到了这一点,但总而言之,没有逆向,它需要搜索。 -
PRNG 可以看作是一种散列机制。它们不应该是可逆的。所以“攻击者”是一些想要扭转它的假设实体。根据@PresidentJamesK.Polk 的评论,可能有一个聪明的解决方案来修剪原本会是蛮力搜索的东西,以使其在大多数情况下变得微不足道。但是,我对数学的了解还不够,无法研究它,仅足以知道 Polk 可能是正确的。
标签: javascript math random cryptography