62个版本 (40个稳定版)
4.3.8 | 2024年7月24日 |
---|---|
4.3.0 | 2024年5月2日 |
4.0.0 | 2024年3月26日 |
3.4.0 | 2024年2月21日 |
0.1.0 | 2021年7月2日 |
在密码学分类中排名第28
每月下载量15,213次
在14个crate(6个直接)中使用
240KB
6K SLoC
可验证的秘密共享方案
此crate在Rust标准库可用时提供各种密码学可验证的秘密共享方案。
- 此实现目前正在审计中,完成时将公布结果。在此之前,请自行承担风险使用。
- 此实现不需要Rust标准库。
- 除非明确指出,所有操作都是常数时间。
注意:从版本2升级
接口已被重新设计以相互兼容以及序列化。
版本3定义了一组用于实现秘密共享方案的特质。虽然标准模式提供了快速简便的方法来分割和合并秘密,但这些特质允许在无-std模式下具有更多灵活性和定制性。以前的版本试图保持两种模式的一致性,但这样做导致了大量代码重复和栈溢出问题。新的特质允许单个实现用于两种模式,并允许无-std消费者使用他们确切需要的,同时最小化代码重复和栈溢出。
注意:从版本3升级
“ShareIdentifier”特质已被修改如下
- fn to_buffer:接收一个字节数组缓冲区并填充标识符的字节表示形式
- fn from_buffer:接收一个字节数组缓冲区并从字节表示形式创建标识符
- to_vec:如果启用features=alloc或std,则返回字节表示形式作为Vec。
为什么进行此更改?
之前的方法不足以处理不同类型的标识符,例如u16、u32、u64等,因为as_bytes返回&[u8],在使用u16、u32等时没有安全的方法将标识符的字节表示形式转换为标识符。因此,选择是继续使用不安全的代码(不好)或实现一个包装结构体来处理转换,但需要实现与原始类型相同的方法和特性(也不太好,导致大量样板代码)。
对Share
特质进行了如下修改
- value和value_mut现在与ShareIdentifier中的to_buffer和from_buffer相对应
- value_vec与ShareIdentifier中的to_vec相对应
其他更改
在为Share
特质实现固定大小为33、49和97之前。现在支持所有数组大小。
此外,支持任何大小的GenericArray。同样支持crypto-bigint
Uint类型。
现在支持以标识符作为.0
和份额作为.1
的元组。
Gf256已添加为仅包含字节序列的秘密共享方案的字段。所有操作都是常数时间。与其他库相比,大多数实现都不是常数时间,因为:运行时间取决于秘密的值,例如使用if语句和带有break语句的循环,或使用允许攻击者监控代码或数据访问模式的查找表。虽然对性能有很大帮助,但它们不是常数时间,这对于密码学操作来说是理想的选择。此实现已与其他库进行了交叉检查,以确保兼容性,并实现了必要的特性以与该库一起使用。
编号
已添加Share编号方法。默认方法是使用从1开始的递增数字。虽然足够简单,但仍然不足以适应所有用例。以下编号方法可用
- SequentialParticipantNumberGenerator:share标识符的索引从指定的数字开始,以指定的值递增,直到达到限制。默认是开始于1,递增1直到达到255。
- RandomParticipantNumberGenerator:share标识符的索引是随机的。随机数生成器基于所需的索引,一个域分隔符,这些分隔符使用Shake256进行哈希。
- ListParticipantNumberGenerator:share标识符的索引基于一个列表。提供的列表必须至少与要生成的份额数量一样长。这在份额标识符在事先已知的情况下很有用,如主动秘密共享的情况。
- ListAndRandomParticipantNumberGenerator:上述两种方法的组合。首先使用列表,然后使用随机数字,直到列表耗尽。
- ListAndSequentialParticipantNumberGenerator:上述两种方法的组合。首先使用列表,然后使用递增数字,直到列表耗尽。
新方法split_secret_with_participant_generator
允许使用上述方法。如果您不需要新的灵活方法,仍然可以使用旧方法split_secret
,它使用从1开始的递增数字的SequentialParticipantNumberGenerator。
份额和标识符
有许多请求希望能够使共享标识符不仅仅局限于整数值。现在通过实现ShareIdentifier
特质,这一需求已成为可能。ShareIdentifier
特质是一个简单的特质,它提供了分割和合并共享所需的方法。默认情况下,ShareIdentifier
特质是为原始整数值实现的。其他值可以通过为所需类型实现该特质来使用,但需要注意字节序。默认情况下,原始类型以大端字节序列表示。正如之前所述,Share
实现了[u8; N]、GenericArray
以下元组也实现了Share
- ({原始整数类型}, [u8; N])
- ({原始整数类型}, GenericArray
) - ({原始整数类型}, Vec) 当与
std
或alloc
功能一起使用时
如果共享标识符是u8
,则存在额外的实现。
- [u8; N+1]其中N是共享大小,第一个字节是标识符
- GenericArray
其中N是共享大小,第一个字节是标识符 - Vec其中第一个字节是标识符
多项式
Polynomial
存储多项式的系数,并提供在给定点评估多项式的方法。多项式仅在分割机密时使用。
共享集
共享集是同一机密所属的共享集合。共享集提供将共享合并回原始机密或另一个组的方法。这些作为ReadableShareSet
和WriteableShareSet
提供。在无std模式下,合并需要ShareSetCombiner
。
ShareSetCombiner
是在机密重构操作期间使用的数据存储。
秘密共享方案
秘密共享方案作为特质实现。这些特质提供分割机密的方法,如果适用,则返回验证器集。Shamir
仅分割机密。Feldman
返回一个验证器集。Pedersen
返回多个验证器集:一个用于自身和一个用于Feldman
。
FeldmanVerifierSet
和PedersenVerifierSet
是方案返回的验证器集。它们提供验证共享的方法。
由于Pedersen
在分割后返回大量信息,因此使用PedersenResult
特质来封装数据。StdPedersenResult
在默认情况下提供分配器时提供。
其他值得注意的事项
在标准模式下运行时,不需要实现任何特质,并且有默认函数可以完成您想要的功能,就像在之前的版本中一样。
StdVsss
提供了完成分割和重构机密所需的大部分方法,但需要指定大量的泛型参数。如果您需要使用特定字段,则提供DefaultStdVsss
以简化过程。DefaultStdVsss
假定标识符是u8
,共享是Vec<u8>
。
如果您在无std模式下需要自定义结构,则vsss_arr_impl
宏将为您创建必要的实现。
文档
可验证的秘密共享方案用于将秘密分割成多个份额,并分发到不同的实体中,能够验证份额是否正确并且属于特定集合。这个crate包括Shamir的秘密共享方案,它不支持验证,但更多是其他方案的基础构建块。
这个crate支持Feldman和Pedersen的可验证秘密共享方案。
Feldman和Pedersen在很多方面都很相似。很难描述在什么情况下使用其中一个而不用另一个。实际上,两者都用于分布式密钥生成。
Feldman揭示了验证者的公开值,而Pedersen则隐藏它。
Feldman和Pedersen在分割秘密方面与Shamir不同。将份额重新组合成原始秘密的方法在所有方法中都是相同的,并且每个方案都提供了便利。
这个crate符合no-std规范,并使用const generics来指定大小。
默认情况下,份额由字节数组表示,但可以通过实现提供的特性来更改。在指定份额大小时,使用字段大小(字节)+ 1 作为标识符。份额可以表示有限域或群,具体取决于用例。第一个字节保留为份额标识符(x坐标),其余的都是份额的实际值(y坐标)。
默认方法
分割和组合秘密的默认方法是
- shamir::split_secret
- feldman::split_secret
- pedersen::split_secret
- combine_shares
- combine_shares_group
P-256
使用Shamir分割p256秘密
use vsss_rs::{*, shamir};
use elliptic_curve::ff::PrimeField;
use p256::{NonZeroScalar, Scalar, SecretKey};
let mut osrng = rand_core::OsRng::default();
let sk = SecretKey::random(&mut osrng);
let nzs = sk.to_nonzero_scalar();
let res = shamir::split_secret::<Scalar, u8, Vec<u8>>(2, 3, *nzs.as_ref(), &mut osrng);
assert!(res.is_ok());
let shares = res.unwrap();
let res = combine_shares(&shares);
assert!(res.is_ok());
let scalar: Scalar = res.unwrap();
let nzs_dup = NonZeroScalar::from_repr(scalar.to_repr()).unwrap();
let sk_dup = SecretKey::from(nzs_dup);
assert_eq!(sk_dup.to_bytes(), sk.to_bytes());
或使用DefaultStdVsss
结构体
use elliptic_curve::ff::Field;
let mut osrng = rand_core::OsRng::default();
let secret = p256::Scalar::random(&mut osrng);
let res = DefaultStdVsss::<p256::ProjectivePoint>::split_secret(2, 3, secret, &mut osrng);
assert!(res.is_ok());
let shares = res.unwrap();
let res = combine_shares(&shares);
assert!(res.is_ok());
let scalar: p256::Scalar = res.unwrap();
assert_eq!(secret, scalar);
Secp256k1
使用Shamir分割k256秘密
use vsss_rs::{*, shamir};
use elliptic_curve::ff::PrimeField;
use k256::{NonZeroScalar, Scalar, ProjectivePoint, SecretKey};
let mut osrng = rand_core::OsRng::default();
let sk = SecretKey::random(&mut osrng);
let secret = *sk.to_nonzero_scalar();
let res = shamir::split_secret::<Scalar, [u8; 1], u8, Vec<u8>>(2, 3, secret, &mut osrng);
assert!(res.is_ok());
let shares = res.unwrap();
let res = combine_shares(&shares);
assert!(res.is_ok());
let scalar: Scalar = res.unwrap();
let nzs_dup = NonZeroScalar::from_repr(scalar.to_repr()).unwrap();
let sk_dup = SecretKey::from(nzs_dup);
assert_eq!(sk_dup.to_bytes(), sk.to_bytes());
或使用feldman
use vsss_rs::{*, feldman};
use bls12_381_plus::{Scalar, G1Projective};
use elliptic_curve::ff::Field;
let mut rng = rand_core::OsRng::default();
let secret = Scalar::random(&mut rng);
let res = feldman::split_secret::<G1Projective, [u8; 1], u8, Vec<u8>>(2, 3, secret, None, &mut rng);
assert!(res.is_ok());
let (shares, verifier) = res.unwrap();
for s in &shares {
assert!(verifier.verify_share(s).is_ok());
}
let res = combine_shares(&shares);
assert!(res.is_ok());
let secret_1: Scalar = res.unwrap();
assert_eq!(secret, secret_1);
Curve25519
Curve25519不是一个素域,但这个crate使用features=["curve25519"]
支持它,默认情况下启用。这个特性包装了curve25519-dalek库,以便与Shamir、Feldman和Pedersen一起使用。
这是一个使用Ed25519和x25519的示例
use curve25519_dalek::scalar::Scalar;
use rand::Rng;
use ed25519_dalek::SecretKey;
use vsss_rs::{curve25519::WrappedScalar, *};
use x25519_dalek::StaticSecret;
let mut osrng = rand::rngs::OsRng::default();
let sc = Scalar::hash_from_bytes::<sha2::Sha512>(&osrng.gen::<[u8; 32]>());
let sk1 = StaticSecret::from(sc.to_bytes());
let ske1 = SecretKey::from_bytes(&sc.to_bytes()).unwrap();
let res = shamir::split_secret::<WrappedScalar, [u8; 1], u8, Vec<u8>>(2, 3, sc.into(), &mut osrng);
assert!(res.is_ok());
let shares = res.unwrap();
let res = combine_shares(&shares);
assert!(res.is_ok());
let scalar: WrappedScalar = res.unwrap();
assert_eq!(scalar.0, sc);
let sk2 = StaticSecret::from(scalar.0.to_bytes());
let ske2 = SecretKey::from_bytes(&scalar.0.to_bytes()).unwrap();
assert_eq!(sk2.to_bytes(), sk1.to_bytes());
assert_eq!(ske1.to_bytes(), ske2.to_bytes());
使用Feldman和Pedersen VSSS时,可以使用RistrettoPoint
或EdwardsPoint
。
许可证
许可证
根据您选择的以下任何一个许可证授权
- Apache许可证版本2.0(LICENSE-APACHE或https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT许可证(LICENSE-MIT或http://opensource.org/licenses/MIT)
供您选择。
贡献
除非您明确声明,否则任何有意提交给作品以供您包含的贡献,根据Apache-2.0许可证的定义,都应按上述方式授权,没有任何额外的条款或条件。
参考
依赖关系
~3.5–6.5MB
~131K SLoC