Crates.io | vsss-rs |
lib.rs | vsss-rs |
version | |
source | src |
created_at | 2021-07-02 01:03:58.102578 |
updated_at | 2025-01-14 22:54:51.112634 |
description | Verifiable Secret Sharing Schemes for splitting, combining and verifying secret shares |
homepage | https://github.com/mikelodder7/vsss-rs |
repository | https://github.com/mikelodder7/vsss-rs |
max_upload_size | |
id | 417649 |
Cargo.toml error: | TOML parse error at line 18, column 1 | 18 | autolib = false | ^^^^^^^ unknown field `autolib`, expected one of `name`, `version`, `edition`, `authors`, `description`, `readme`, `license`, `repository`, `homepage`, `documentation`, `build`, `resolver`, `links`, `default-run`, `default_dash_run`, `rust-version`, `rust_dash_version`, `rust_version`, `license-file`, `license_dash_file`, `license_file`, `licenseFile`, `license_capital_file`, `forced-target`, `forced_dash_target`, `autobins`, `autotests`, `autoexamples`, `autobenches`, `publish`, `metadata`, `keywords`, `categories`, `exclude`, `include` |
size | 0 |
This crate provides various cryptography verifiable secret sharing schemes when the rust standard library is available.
The interfaces have been redesigned to be compatible with each other as well as serialization.
Version 3 defines a set of traits for implementing secret sharing schemes. While the standard mode provides quick and easy methods to split and combine secrets, the traits allow for more flexibility and customization especially in no-std mode. Previous versions tried to keep the two modes aligned but in doing so resulted in a lot of code duplication and stack overflow issues. The new traits allow for a single implementation to be used in both modes and allow no-std consumers to use exactly what they need while minimizing code duplication and stack overflows.
The ShareIdentifier
trait has been modified as follows:
The previous method was not flexible enough to handle different types of identifiers such as u16, u32, u64, etc. because as_bytes returned &[u8] and there was no safe method to convert the byte representation from the identifier when using u16, u32, etc. So the option was to either continue as is but use unsafe code with Statics (not good), or implement a wrapper struct that would handle the conversion but needed to implement the same methods and traits as the primitives would (also not good and results in a lot of boilerplate).
The Share
trait has been modified as follows:
Before the Share
trait was implemented for fixed sizes of 33, 49, and 97. Now all array sizes are supported.
In addition, GenericArray of any size is supported.
crypto-bigint
Uint types are supported as well.
Now tuples with the identifier as .0
and the share as .1
are supported.
Gf256 has been added as a field for secret sharing schemes with just byte sequences. All operations are constant time. Most implementations comparatively are not constant time due to: runtime is dependent on the value of the secret such as using if statements and looping with break statements, or using lookup tables that allow an attacker to monitor code or data access patterns. While great for performance, they are not constant time which is desirable for cryptographic operations. This implementation has been cross-checked for compatibility with other libraries and also implements the necessary traits to function with this library.
Share numbering methods have been added. The default method has been to use incrementing numbers starting at 1. While simple enough, again it's not flexible enough for all use cases. The following numbering methods are available:
The new method split_secret_with_participant_generator
enables the use of the above methods. If you don't need
the new flexible methods, the old method split_secret
is still available which uses SequentialParticipantNumberGenerator starting at 1 and incrementing by 1.
There was lots of requests to enable share identifiers to be more than just integer values. This is now possible
by implementing the ShareIdentifier
trait. The ShareIdentifier
trait is a simple trait that provides the necessary
methods for splitting and combining shares. The ShareIdentifier
trait is implemented for
primitive integer values by default. Other values can be used by implementing the trait for the desired type
but keep in might endianness. By default, primitive types represented as big-endian byte sequences. As explained
earlier, Share
is implemented for [u8; N], GenericArray<u8, N>, Uint
The following tuples also implement Share
:
std
or alloc
featureIf the share identifier is u8
then the additional implementations exist.
Polynomial
holds the coefficients of the polynomial and provides methods to evaluate the polynomial at a given point.
Polymomials are only used when splitting secrets.
A share set is a collection of shares that belong to the same secret. The share set provides methods to combine into
the original secret or another group. These are offered as ReadableShareSet
and WriteableShareSet
. In no-std mode,
combines require a ShareSetCombiner
.
ShareSetCombiner
is the data store used during a secret reconstruct operation.
Secret sharing schemes are implemented as traits. The traits provide methods to split secrets and if applicable return the
verifier set. Shamir
only splits secrets. Feldman
returns a verifier set. Pedersen
returns multiple verifier sets:
one for itself and one for Feldman
.
FeldmanVerifierSet
and PedersenVerifierSet
are the verifier sets returned by the schemes. They provide methods to
validate the shares.
Since Pedersen
returns a large amount of information after a split the PedersenResult
trait is used to encapsulate
the data. StdPedersenResult
is provided when an allocator is available by default.
When operating in standard mode, no traits should be necessary to be implemented and there are default functions to accomplish what you want just like in previous versions.
StdVsss
provides the majority of methods needed to accomplish splitting and reconstructing secrets but requires
specifying lots of generic parameters. If you need to use a specific field, DefaultStdVsss
is provided to make
the process easier. DefaultStdVsss
assumes the identifier is u8
and the share is a Vec<u8>
.
If you need custom structs in no-std mode the vsss_arr_impl
macro will create the necessary implementations for you.
Verifiable Secret Sharing Schemes are using to split secrets into multiple shares and distribute them among different entities, with the ability to verify if the shares are correct and belong to a specific set. This crate includes Shamir's secret sharing scheme which does not support verification but is more of a building block for the other schemes.
This crate supports Feldman and Pedersen verifiable secret sharing schemes.
Feldman and Pedersen are similar in many ways. It's hard to describe when to use one over the other. Indeed, both are used in distributed key generation.
Feldman reveals the public value of the verifier whereas Pedersen's hides it.
Feldman and Pedersen are different from Shamir when splitting the secret. Combining shares back into the original secret is identical across all methods and is available for each scheme for convenience.
This crate is no-std compliant and uses const generics to specify sizes.
Shares are represented as byte arrays by default but can be changed by implementing the provided traits. When specifying share sizes, use the field size in bytes + 1 for the identifier. Shares can represent finite fields or groups depending on the use case. The first byte is reserved for the share identifier (x-coordinate) and everything else is the actual value of the share (y-coordinate).
The default methods for splitting and combining secrets are:
To split a p256 secret using Shamir
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());
Or using the DefaultStdVsss
struct
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);
To split a k256 secret using Shamir
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());
or to use 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 is not a prime field but this crate does support it using
features=["curve25519"]
which is enabled by default. This feature
wraps curve25519-dalek libraries so they can be used with Shamir, Feldman, and Pedersen.
Here's an example of using Ed25519 and 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());
Either RistrettoPoint
or EdwardsPoint
may be used when using Feldman and Pedersen VSSS.
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.