跳转至

如何保证k是随机的

来源:密码学引论 / digital_signature/ElGamal/如何保证k是随机的.md

Q:如何确保k是随机生成的,即使用一些随机数生成器,也不能消除掉碰撞的风险

对,不能从数学上“绝对确保”随机 (k) 永不碰撞。只能做到:

碰撞概率低到可以忽略,或者干脆不用真随机 (k),改用确定性 (k)。

在 ElGamal / DSA / ECDSA 这类签名里,(k) 是命门。只要 (k) 重复、偏弱、可预测,就可能泄露私钥。

真随机生成 (k):只能降低碰撞概率

假设 (k) 在一个很大的范围里均匀随机选,比如:

\[ k \in [1, q-1] \]

其中 (q) 是一个 256 bit 左右的大素数,那么可选空间大约是:

\[ 2^{256} \]

如果签名次数是 (n),根据生日悖论,碰撞概率大约是:

\[ P \approx \frac{n^2}{2q} \]

比如你签名 (2^{32}) 次,碰撞概率大约是:

\[ P \approx \frac{2^{64}}{2^{257}} = 2^{-193} \]

这个概率极低,现实中可以认为不可能发生。

所以正常做法是:

  1. 使用密码学安全随机数生成器;
  2. 每次生成新的 (k);
  3. 检查 (1 \le k \le q-1);
  4. 必要时检查 (\gcd(k, p-1)=1),这是 ElGamal 原始签名里的要求。

但是你说得对:概率上仍然不是零。

更现代的做法:确定性生成 (k)

为了避免随机数生成器出问题,DSA/ECDSA 常用一种思路:

\[ k = F(x, H(m)) \]

也就是说,(k) 不完全靠随机数,而是由:

  • 私钥 (x)
  • 消息哈希 (H(m))

通过安全的伪随机函数生成。

典型方案是 RFC 6979 deterministic k

它的思想是:

对同一个私钥和同一个消息,(k) 固定; 对不同消息,(k) 看起来像随机数; 不依赖系统随机数质量。

这样可以避免:

  • 随机数生成器坏掉;
  • 熵不足;
  • 两次刚好生成相同的 (k);
  • VM 快照、嵌入式设备重启导致随机状态重复。

但确定性 (k) 也不是万能的

确定性 (k) 的问题是:

如果私钥 (x) 泄露,那 (k) 也能被重算出来。不过这不是额外问题,因为私钥泄露本来签名系统就完了。

还有一个问题是侧信道攻击。

即使 (k) 生成得很好,如果计算过程中泄露了:

\[ k \]

或者泄露了 (k) 的部分比特,也可能被 lattice/HNP 攻击恢复私钥。

所以还要保证:

  • 模逆计算尽量常数时间;
  • 标量乘法不要泄露分支信息;
  • 不要打印、缓存、复用 (k);
  • 签名失败时不要重用同一个 (k)。

原始 ElGamal 签名里还要注意 \(gcd(k,p-1)=1\)

因为签名公式是:

\[ s \equiv k^{-1}(H(m)-xr) \pmod{p-1} \]

所以需要:

\[ \gcd(k,p-1)=1 \]

否则 \(k^{-1} \pmod{p-1}\) 不存在。

因此生成 (k) 时不能只是随机取一个数,还要检查:

\[ \gcd(k,p-1)=1 \]

如果不满足,就重新生成。