#密钥交换 #椭圆曲线 #Schnorr签名 #Schnorr #ed448-goldilocks #ed448

tiny_ed448_goldilocks

一个轻量级、高性能、纯Rust实现的Ed448-Goldilocks,易于签名和密钥交换

9个版本

0.1.8 2024年4月22日
0.1.7 2024年4月5日
0.1.5 2024年3月4日
0.1.4 2023年12月11日

#367 in 加密学


用于 capycrypt

MIT 许可证

345KB
705

Ed448-Goldilocks

警告:此软件未经过审计。使用风险自担。

crates.io Build Status License: MIT

1. 目标

Edward形式曲线的Goldilocks变种在安全性和性能之间提供了一个令人信服的平衡。我们希望利用这个曲线以满足以下群属性

身份
0 * G = 𝒪
G * 1 = G
G + (-G) = 𝒪
2 * G = G + G
4 * G = 2 * (2 * G)
4 * G > 𝒪
r * G = 𝒪
(k + 1) * G = (k * G) + G
k*G = (k % r) * G
(k + t) * G = (k * G) + (t * G)
k * (t * G) = t * (k * G) = (k * t % r) * G

我们想要的

  • 尽可能快的组合和双倍操作
  • 标量字段操作固定时间侧信道抵抗
  • 仅我们需要的功能用于Schnorr签名和非对称DH,理想情况下使此实现尽可能精简。

2. 策略

在很大程度上遵循这个这个的方法,我们在点/标量乘法期间执行以下一系列变换

  1. 从扭曲形式开始
  2. 分解标量并重新定位到-8和8之间的16进制基数
  3. 创建一个P的倍数映射到16进制数字的查找表,具有固定时间查找以确保侧信道抵抗。
  4. 在variable_base_mul中,我们以扭曲形式执行双倍操作,在射影Niels形式中执行加法和固定时间条件否定。
  5. 点以扩展形式返回,最后转换为仿射形式以供用户操作。

在更高层次上,我们有

仿射 扩展 扭曲 射影Niels
(x, y) (x, y, z, t) (x, y, z, t1, t2) (y + x, y - x, td, z)

然后我们的标量乘法将遵循

仿射 → 扩展 → 扭曲 → 射影Niels → 扭曲 → 扩展 → 仿射

3. 固定时间

分解标量的查找表是计算和遍历的固定时间

/// Selects a projective niels point from a lookup table in fixed-time
pub fn select(&self, index: u32) -> ProjectiveNielsPoint {
    let mut result = ProjectiveNielsPoint::id_point();
    for i in 1..9 {
        let swap = index.ct_eq(&(i as u32));
        result.conditional_assign(&self.0[i - 1], swap);
    }
    result
}

这确保了固定时间乘法,无需在Montgomery形式中使用曲线点。此外,我们利用了crypto-bigint库,它确保我们的标量类型具有固定时间操作。域元素由fiat-crypto的p448-solinas-64素域表示。它在机器级别上进行了形式验证和高度优化。

4. 签名和DH

将此crate用作capyCRYPT的椭圆曲线后端,我们有

Schnorr签名

 use capycrypt::{
    Signable,
    KeyPair,
    Message,
    sha3::aux_functions::byte_utils::get_random_bytes
};
/// # Schnorr Signatures
/// Signs a [`Message`] under passphrase pw.
///
/// ## Algorithm:
/// * `s` ← kmac_xof(pw, “”, 448, “SK”); s ← 4s
/// * `k` ← kmac_xof(s, m, 448, “N”); k ← 4k
/// * `𝑈` ← k*𝑮;
/// * `ℎ` ← kmac_xof(𝑈ₓ , m, 448, “T”); 𝑍 ← (𝑘 – ℎ𝑠) mod r
/// ```
fn sign(&mut self, key: &KeyPair, d: u64) {
    self.d = Some(d);

    let s: Scalar = bytes_to_scalar(kmac_xof(&key.priv_key, &[], 448, "SK", self.d.unwrap()))
        * (Scalar::from(4_u64));

    let s_bytes = scalar_to_bytes(&s);

    let k: Scalar =
        bytes_to_scalar(kmac_xof(&s_bytes, &self.msg, 448, "N", d)) * (Scalar::from(4_u64));

    let U = ExtendedPoint::generator() * k;
    let ux_bytes = U.to_affine().x.to_bytes().to_vec();
    let h = kmac_xof(&ux_bytes, &self.msg, 448, "T", d);
    let h_big = bytes_to_scalar(h.clone());
    let z = k - (h_big.mul_mod_r(&s));
    self.sig = Some(Signature { h, z })
}

5. 基准测试

运行以下命令

cargo bench

在5mb随机数据上,Intel® Core™ i7-10710U × 12的近似运行时间

操作 ~时间(毫秒) OpenSSL(毫秒)
加密 75
解密 75
签名 42 15
验证 18

我们的自定义SHA3与OpenSSL表现相当,但截至目前,我们还不知道那个端口的实验所使用的哈希后端是什么。因此,一些开销可能来自使用SHA3而不是OpenSSL上的更优化的哈希器,例如SHA2。尽管如此,这种性能远远优于我们之前仅使用仿射坐标的尝试。

致谢

作者衷心感谢Paulo Barreto博士在固定时间操作方面的咨询以及在该领域的一般工作。我们还希望向曲线-dalek作者此处此处表示感谢,因为他们提供了出色的参考实现和岩石般可靠的密码学实例。感谢otsmr对原始仿射坐标Montgomery梯形的尝试。

依赖关系

~6.5MB
~137K SLoC