#schnorr-signature #signature-scheme #elliptic-curve #schnorr #signatures #signature #poseidon-hash

无需std jubjub-schnorr

一个纯Rust实现的Schnorr签名,附加PLONK电路模块

7个版本 (4个重大更新)

0.5.0 2024年8月14日
0.4.0 2024年5月22日
0.3.0 2024年4月24日
0.2.2 2024年4月11日
0.1.0 2024年1月8日

#342 in 密码学

Download history 38/week @ 2024-04-26 8/week @ 2024-05-03 3/week @ 2024-05-10 173/week @ 2024-05-17 35/week @ 2024-05-24 13/week @ 2024-05-31 27/week @ 2024-06-07 29/week @ 2024-06-14 22/week @ 2024-06-21 14/week @ 2024-06-28 33/week @ 2024-07-05 17/week @ 2024-07-12 6/week @ 2024-07-19 15/week @ 2024-07-26 10/week @ 2024-08-02 134/week @ 2024-08-09

每月 170 次下载
4 个crate中使用 (3个直接使用)

MPL-2.0 许可证

75KB
893 代码行

jubjub-schnorr

Build Status Repository Documentation

此crate提供了JubJub椭圆曲线组的Schnorr签名方案的Rust实现,使用Poseidon哈希函数。此实现由Dusk团队设计。

关于

Schnorr签名方案,以其创建者Claus Schnorr的名字命名,是一种以简单著称的数字签名方案。该方案提供了一种创建短签名的简单方法。

该实现使用了jubjub椭圆曲线和Poseidon哈希函数,其论文可在此处找到。

签名方案是在Phoenix事务模型中实现的,基于Schnorr Sigma协议,并编译了Fiat–Shamir变换,作为非交互式签名方案。具体来说,Phoenix协议使用了一种变体,该变体利用双Schnorr签名,可以使用双公钥进行验证,从而在协议的后期阶段实现计算过程的委托。

该仓库还包括了此处描述的基于Schnorr的SpeedyMuSig多签名方案的实现(第19页)。它允许多个签名者创建一个签名,该签名证明消息是由他们所有人签署的,前提是他们提供了公钥。该签名可以使用用于标准Schnorr签名的相同函数进行验证,使用所有签名者的公钥之和。

库结构

该库分为以下组件

  • 密钥:包含用于签名消息的私钥结构,以及用于验证的公钥和双公钥结构。
  • 签名:包含标准签名和双签名结构,以及验证Schnorr签名和双Schnorr签名的函数。
  • 小工具:包含用于电路中验证Schnorr签名和双Schnorr签名的Plonk小工具。

签名方案描述

符号表示

以下

  • 一个点$P$乘以标量$s$表示将$P$加到自身$s$次。
  • $\mathbb{F}_q$是阶为$q$的素数有限域
  • 对于素数$q$:$\mathbb{F}_q^× = \mathbb{F}_q \setminus 0$包含$\mathbb{F}_q$中的所有非零元素。

单签名

设置

在此库中,我们实现了基于 jubjub 椭圆曲线的 Schnorr 签名方案,具体来说,我们有

  • 一个有限域$\mathbb{F}_q$,其上的素数为$q$,在此实现中,此域对应于椭圆曲线BLS12-381的标量域
  • 一个椭圆曲线$E / \mathbb{F}_q$,在我们的情况下,这是 jubjub 椭圆曲线
  • 一个曲线点子群$\mathbb{G} \in E(\mathbb{F}_q)$,其阶为素数$p$
  • 一个固定生成点$G \in \mathbb{G}$
  • 一个加密哈希函数$H : {0 , 1}^∗ \rightarrow \mathbb{F}_p$,其中$\mathbb{F}_p$是 jubjub 椭圆曲线的标量域。

密钥生成

  • 选择一个私钥签名密钥$sk \in \mathbb{F}_p^×$。
  • 公钥验证密钥是$PK = skG \in \mathbb{G}$。

签名

要对消息$m \in \mathbb{F}_q^×$进行签名

  • 选择一个随机的私钥随机数$r \in \mathbb{F}_p^×$。
  • 计算随机数点$R = rG \in \mathbb{G}$。
  • 计算挑战哈希$c = H(R \parallel PK \parallel m) \in \mathbb{F}_p$,其中$\parallel$表示连接,$R$表示为一个位字符串。
  • 计算$u = r − sk \cdot c \in \mathbb{F}_p$。

签名是元组$(u, R) \in \mathbb{F}_p \times \mathbb{G}$。

验证

  • 计算挑战哈希$c = H(R \parallel PK \parallel m) \in \mathbb{F}_p$。
  • 验证$uG + cPK = R$。

如果签名是用与$PK$对应的秘密密钥签名的,这将成立,因为

$$ uG + cPK = (r - sk\cdot c)G + (sk\cdot c)G = (r - sk\cdot c + sk\cdot c)G = rG = R $$

双签名

设置

与上面的单签名相同,增加了一个不同的生成点$G' \in \mathbb{G}$,该点不同于$G$,且其与$G$的离散对数关系未知。

密钥生成

  • 选择一个私钥签名密钥$sk \in \mathbb{F}_p^×$。
  • 公钥验证密钥是元组$(PK, PK')$,其中$PK = skG \in \mathbb{G}$,$PK' = skG' \in \mathbb{G}$。

签名

要对消息$m \in \mathbb{F}_q^×$进行签名

  • 选择一个随机的私钥随机数$r \in \mathbb{F}_p^×$。
  • 计算随机数点$R = rG \in \mathbb{G}$和$R' = rG' \in \mathbb{G}$。
  • 计算挑战哈希$c = H(R \parallel R' \parallel PK \parallel m) \in \mathbb{F}_p$,其中$\parallel$表示连接,$R, R'$表示为位字符串。
  • 计算$u = r − sk \cdot c \in \mathbb{F}_p$。

签名是元组$(u, R, R') \in \mathbb{F}_p \times \mathbb{G} \times \mathbb{G}$。

验证

  • 计算挑战哈希$c = H(R \parallel R' \parallel PK \parallel m) \in \mathbb{F}_p$。
  • 验证$rG + cPK = R$和$uG' + cPK' = R'$。

如果签名是用正确的私钥签名的,这应该成立,因为

$$ uG + cPK = (r - sk\cdot c)G + (sk\cdot c)G = (r - sk\cdot c + sk\cdot c)G = rG = R $$

$$ uG' + cPK' = (r - sk\cdot c)G' + (sk\cdot c)G' = (r - sk\cdot c + sk\cdot c)G' = rG' = R' $$

关于安全和实现的注释

在随机预言模型下,该签名方案在选言攻击下是存在性不可伪造的,前提是离散对数问题的困难性。这一性质在 Katz 和 Lindell 的《现代密码学导论》第12.5.1节中有详细说明。

虽然基本的 Schnorr 签名方案是一个广泛认可的构造,但 Phoenix 所采用的双密钥变体是一个新颖的引入。在交易协议的背景下,这允许在不泄露签名者秘密密钥的机密性的情况下委派证明计算。

使用

要将 jubjub-schnorr 包集成到您的项目中,请使用以下命令

cargo add jubjub-schnorr

一个基本示例,演示如何生成和验证Schnorr签名

use dusk_bls12_381::BlsScalar;
use jubjub_schnorr::{SecretKey};
use rand::rngs::StdRng;
use rand::SeedableRng;
use ff::Field;

fn main() {
    // Setup
    let mut rng = StdRng::seed_from_u64(1234u64);
    let message = BlsScalar::random(&mut rng);

    // Key generation
    let sk = SecretKey::random(&mut rng);

    // Standard Schnorr signature scheme:
    use jubjub_schnorr::PublicKey;

    let pk = PublicKey::from(&sk);
    let signature = sk.sign(&mut rng, message);
    assert!(pk.verify(&signature, message).is_ok(), "The signature should be valid.");

    // Double Dusk-Schnorr signature scheme:
    use jubjub_schnorr::PublicKeyDouble;

    let pk = PublicKeyDouble::from(&sk);
    let signature = sk.sign_double(&mut rng, message);
    assert!(pk.verify(&signature, message).is_ok(), "The signature should be valid.");

    // Dusk-Schnorr signature scheme with variable generator:
    use dusk_jubjub::{GENERATOR_EXTENDED, JubJubScalar};
    use jubjub_schnorr::PublicKeyVarGen;

    let generator = GENERATOR_EXTENDED * JubJubScalar::from(42u64);
    let sk = sk.with_variable_generator(generator);
    let pk = PublicKeyVarGen::from(&sk);
    let signature = sk.sign(&mut rng, message);
    assert!(pk.verify(&signature, message).is_ok(), "The signature should be valid.");
}

许可

此源代码形式受Mozilla公共许可证第2.0版条款约束。如果此文件未附带Mozilla公共许可证副本,您可以在http://mozilla.org/MPL/2.0/获取一份。

版权(c)DUSK NETWORK。保留所有权利。

依赖项

~4.5MB
~100K SLoC