#bitcoin #scalar #point #zero #secret #optimized #default

无 std secp256kfun

一个中级secp256k1库,针对娱乐进行了优化!

27次发布

0.10.0 2024年2月19日
0.9.1 2023年7月12日
0.9.0 2023年5月8日
0.8.2 2022年12月16日
0.1.5 2020年7月29日

#1332 in 魔豆

Download history 83/week @ 2024-03-14 31/week @ 2024-03-21 93/week @ 2024-03-28 69/week @ 2024-04-04 110/week @ 2024-04-11 2505/week @ 2024-04-18 2314/week @ 2024-04-25 2783/week @ 2024-05-02 2700/week @ 2024-05-09 2550/week @ 2024-05-16 3408/week @ 2024-05-23 3977/week @ 2024-05-30 3413/week @ 2024-06-06 3724/week @ 2024-06-13 3196/week @ 2024-06-20 2471/week @ 2024-06-27

13,432 每月下载量
用于 4 crates

0BSD 许可证

330KB
6K SLoC

secp256kFUN!   crates_badge actions_badge docs_badge

这是一个中级Rust secp256k1椭圆曲线密码学库,针对娱乐进行了优化!这里的“娱乐”意味着

  • 类型安全:使用其他API时通常必须处理的错误情况,通过Rust的类型系统在编译时就被排除了。
  • 抽象化:该库公开了两个简单的抽象 标量,以便您可以干净地实现加密。
  • 不可优化:在群上表达某种操作的最直接方式也是最有效的方式。
  • 文档化:我们试图为每个函数制作工作示例并对其进行文档化。

娱乐并不意味着(目前还没有 — 请帮助!)

  • 未经过充分审查:这里的实现尚未得到很多审查。
  • 抵抗侧信道攻击:尚未对是否该库或其底层的算术(来自 k256)抵抗时间攻击等进行实证研究。《strong>没有尝试在释放内存时“归零”秘密。
  • 性能:该库在总体上不如 libsecp256k1

本库的目标是让研究人员能够实验想法,让他们在比特币上工作,并享受这个过程!像 rust-secp256k1 这样的 高级 库使得正确和高效地实现异构加密方案变得困难。而像 parity/libsecp256k1 这样的 低级 库虽然可行,但生成的代码往往容易出错且难以阅读。

使用

[dependencies]
secp256kfun = "0.10"

应该使用?

只要你的目标是制造有趣和娱乐性的东西,这个库就适用于生产。如果你想设计一些稳定的东西,很多人会依赖它,那么这个库可能是一个风险选择。以下是一些替代方案

  1. rust-secp256k1 - Bitcoin 本身使用的 libsecp256k1 的 rust 绑定
  2. k256 - 该库的算术默认基于此库。
  3. ristretto - 如果你不需要使用 secp256k1

文档

docs.rs/secp256kfun

特性

以下是本库的显著特性。

零元素

secp256k1 点和标量都有一个概念上的 元素。不幸的是,在围绕比特币的东西中,零标量和零点在大多数情况下都是非法值。《secp256kfun》通过使用 标记类型 解决这些困难。点和标量在编译时用 ZeroNonZero 标记(默认为 NonZero)。因此,如果你用 NonZero 类型声明你的函数,传递一个 Zero 类型将会在编译时出错,如下所示

use secp256kfun::{marker::*, Scalar, Point,G,g};
// a randomly selected Scalar will never be zero (statistically unreachable)
let x = Scalar::random(&mut rand::thread_rng());
dbg!(&x); // Scalar<.., NonZero>
// Multiplying a NonZero scalar by G (which is also NonZero) results in a NonZero point
let X = g!(x * G);
dbg!(&X) // Point<..,NonZero>
let Y = g!(-x * G)
// An addition **can** lead to a zero so the result is marked Zero
let sum = g!(X + Y);
dbg!(&sum); // Point<.., Zero>
// Now let's say I naively decide to use this value as my public key...
let public_key = sum.normalize();
// BOOM! This is a compile time Error! 🎉
send_pubkey_to_bob(&public_key);

fn send_pubkey_to_bob(public_key: &Point) {
    unimplemented!()
}

这给了我们

error[E0308]: mismatched types
 --> src/lib.rs:77:20
   |
17 | send_pubkey_to_bob(&public_key);
     |                  ^^^^^^^^^^^ expected struct `secp256kfun::marker::NonZero`, found struct `secp256kfun::marker::Zero`

为了解决这个问题,库会强制你手动将值标记为 NonZero,然后处理它为零的情况。

match sum.normalize().non_zero() {
    Some(public_key) => send_pubkey_to_bob(&public_key), // it was actually NonZero
    None => .. // deal with the case it is Zero
}

变量时间还是常数时间?

注意:从 v0.7.0 版本开始,由于我们更改了算术后端到 k256,因此 SecretPublic 标记几乎不起作用,因为 k256 没有变量时间算法。然而,这种情况可能在未来的版本中有所改善。

如果加密函数的执行时间应该与其秘密输入无关。否则,有关这些输入的信息可能会泄露给任何能够测量其执行时间的人。

在 secp256kfun 中,我们试图通过允许你将不同的输入标记为 PublicSecret 来解决这个问题。根据标记,rust 编译器可能会选择不同的底层操作。为 Public 输入选择更快的但变量时间操作,为标记为 Secret 的事物选择更慢但更安全的常数时间操作。换句话说,调用者可以决定哪些输入是

例如,以下是一个 pedersen_commitment 函数,它由提交方用秘密值调用,并由验证方在最终揭示秘密值时调用。请注意,我们只需要编写一次函数,而调用者通过标记来决定该函数是否应以常数时间或变量时间运行。

use secp256kfun::{marker::*, Point, Scalar, g};

/// commit to a secret value x with publicly known A and B.
fn pedersen_commit(
    A: &Point<impl PointType>, // Accept any kind of Point
    B: &Point<impl PointType>,
    r: &Scalar<impl Secrecy>, // Accept a Secret or Public Scalar
    x: &Scalar<impl Secrecy, Zero>, // Allow commitment to Zero
) -> Point {
    // Make the commitment
    g!(r * A +  x * B)
        .normalize()
        // If the result is zero we could easily compute the discrete
        // logarithm of B with respect to A. Since this is meant to be unknown
        // this is computionally unreachable.
        .non_zero().expect("computationally unreachable")
}

// public setup
let A = secp256kfun::G; // use the standard basepoint for one of the points
let B = Point::random(&mut rand::thread_rng());

// Alice commits to her secret value x with randomness r
let r = Scalar::random(&mut rand::thread_rng());
let x = Scalar::<Secret, Zero>::from(42);
let commitment = pedersen_commit(A, &B, &r, &x);

// Imagine Later on, Bob receives the public opening (r,x) for commitment. He
// doesn't care about leaking these values via execution time so he marks them
// as public.
let r = r.public();
let x = x.public();

// Now he'll compute the commitment in faster variable time and check it
// against the original
assert_eq!(commitment, pedersen_commit(A, &B, &r, &x));

特性

  • 内置类型安全的“仅 x”点压缩和解压缩。
  • 用于清晰表达群操作的算术表达式宏 g!s!(如上所示)。
  • nonce 导出 API,以帮助避免出错。
  • no_std 支持(仅禁用默认的 std 功能)
  • 功能标志
    • serde 序列化和反序列化二进制和十六进制(十六进制解码需要 alloc 功能)
    • bincode 直接实现了 bincodeEncode / Decode / BorrowDecode 特性
    • libsecp_compatrust-secp256k1 类型添加了从和到 From 的实现。
    • 使用 proptest 特性实现核心类型

MSRV

最低支持的 Rust 版本是 v1.63

许可协议

代码在 0BSD 许可下发布,但 secp256kfun/src/vendor 目录下的代码除外,在那里您可以找到供应商代码的许可协议。

依赖项

~0.4–2MB
~31K SLoC