Skip to content

Commit c73aac8

Browse files
committed
Working.
1 parent fd75f3a commit c73aac8

File tree

8 files changed

+328
-96
lines changed

8 files changed

+328
-96
lines changed

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ authors = ["Blockchain Commons"]
77
keywords = ["cryptography"]
88
repository = "https://github.com/BlockchainCommons/bc-sskr-rust"
99
readme = "README.md"
10+
license = "BSD-2-Clause-Patent"
1011
categories = ["cryptography"]
1112
documentation = "https://docs.rs/sskr"
1213

1314
[dependencies]
14-
bc-crypto = { path = "../bc-crypto-rust" }
15-
bc-shamir = { path = "../bc-shamir-rust" }
15+
bc-crypto = "0.1.0"
16+
bc-shamir = "0.1.0"
1617

1718
[dev-dependencies]
1819
hex-literal = "0.4.1"

LICENSE.md

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,47 @@
1-
Unless otherwise noted (either in /README.md or in the file's header comments) the contents of this repository are released under the following license:
1+
Copyright © 2023 Blockchain Commons, LLC
22

3-
BSD-2-Clause Plus Patent License
3+
Redistribution and use in source and binary forms, with or without modification,
4+
are permitted provided that the following conditions are met:
45

5-
SPDX-License-Identifier: [BSD-2-Clause-Patent](https://spdx.org/licenses/BSD-2-Clause-Patent.html)
6+
1. Redistributions of source code must retain the above copyright notice,
7+
this list of conditions and the following disclaimer.
68

7-
Copyright © 2019 Blockchain Commons, LLC
9+
2. Redistributions in binary form must reproduce the above copyright notice,
10+
this list of conditions and the following disclaimer in the documentation
11+
and/or other materials provided with the distribution.
812

9-
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
13+
Subject to the terms and conditions of this license, each copyright holder and
14+
contributor hereby grants to those receiving rights under this license a
15+
perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
16+
(except for failure to satisfy the conditions of this license) patent license to
17+
make, have made, use, offer to sell, sell, import, and otherwise transfer this
18+
software, where such license applies only to those patent claims, already
19+
acquired or hereafter acquired, licensable by such copyright holder or
20+
contributor that are necessarily infringed by:
1021

11-
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
12-
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
13-
Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by:
22+
(a) their Contribution(s) (the licensed copyrights of copyright holders and
23+
non-copyrightable additions of contributors, in source or binary form)
24+
alone; or
1425

15-
(a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of contributors, in source or binary form) alone; or
16-
(b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution.
17-
Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this license, whether expressly, by implication, estoppel or otherwise.
26+
(b) combination of their Contribution(s) with the work of authorship to
27+
which such Contribution(s) was added by such copyright holder or
28+
contributor, if, at the time the Contribution is added, such addition causes
29+
such combination to be necessarily infringed. The patent license shall not
30+
apply to any other combinations which include the Contribution.
31+
32+
Except as expressly stated above, no rights or licenses from any copyright
33+
holder or contributor is granted under this license, whether expressly, by
34+
implication, estoppel or otherwise.
1835

1936
DISCLAIMER
2037

21-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
39+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
40+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
42+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
44+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
45+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
46+
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
47+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
## Introduction
1010

11+
Sharded Secret Key Reconstruction (SSKR) is a protocol for splitting a *secret* into a set of *shares* across one or more *groups*, such that the secret can be reconstructed from any combination of shares totaling or exceeding a *threshold* number of shares within each group and across all groups. SSKR is a generalization of Shamir's Secret Sharing (SSS) that allows for multiple groups and multiple thresholds.
12+
1113
## Getting Started
1214

1315
```toml
@@ -17,9 +19,11 @@ sskr = "0.1.0"
1719

1820
## Specification
1921

22+
SSKR is described in [BCR-2020-011](https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2020-011-sskr.md).
2023

2124
## Related Projects
2225

26+
- [bc-shamir](https://crates.io/crates/bc-shamir) - Shamir's Secret Sharing for Rust
2327

2428
## Status - Alpha
2529

src/encoding.rs

Lines changed: 69 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,67 @@
11
use bc_crypto::RandomNumberGenerator;
22
use bc_shamir::{split_secret, recover_secret};
3-
use crate::{Error, SSKRShare, METADATA_SIZE_BYTES, Secret, Spec};
3+
use crate::{Error, METADATA_SIZE_BYTES, Secret, Spec, share::SSKRShare};
4+
5+
/// Generates SSKR shares for the given `Spec` and `Secret`.
6+
///
7+
/// # Arguments
8+
///
9+
/// * `spec` - The `Spec` instance that defines the group and member thresholds.
10+
/// * `master_secret` - The `Secret` instance to be split into shares.
11+
pub fn sskr_generate(
12+
spec: &Spec,
13+
master_secret: &Secret
14+
) -> Result<Vec<Vec<Vec<u8>>>, Error> {
15+
let mut rng = bc_crypto::SecureRandomNumberGenerator;
16+
sskr_generate_using(spec, master_secret, &mut rng)
17+
}
18+
19+
/// Generates SSKR shares for the given `Spec` and `Secret` using the provided
20+
/// random number generator.
21+
///
22+
/// # Arguments
23+
///
24+
/// * `spec` - The `Spec` instance that defines the group and member thresholds.
25+
/// * `master_secret` - The `Secret` instance to be split into shares.
26+
/// * `random_generator` - The random number generator to use for generating
27+
/// shares.
28+
pub fn sskr_generate_using(
29+
spec: &Spec,
30+
master_secret: &Secret,
31+
random_generator: &mut impl RandomNumberGenerator
32+
) -> Result<Vec<Vec<Vec<u8>>>, Error> {
33+
let groups_shares = generate_shares(spec, master_secret, random_generator)?;
34+
35+
let result: Vec<Vec<Vec<u8>>> = groups_shares.iter().map (|group| {
36+
group.iter().map(serialize_share).collect()
37+
}).collect();
38+
39+
Ok(result)
40+
}
41+
42+
/// Combines the given SSKR shares into a `Secret`.
43+
///
44+
/// # Arguments
45+
///
46+
/// * `shares` - A slice of SSKR shares to be combined.
47+
///
48+
/// # Errors
49+
///
50+
/// Returns an error if the shares do not meet the necessary quorum of groups
51+
/// and member shares within each group.
52+
pub fn sskr_combine<T>(shares: &[T]) -> Result<Secret, Error>
53+
where
54+
T: AsRef<[u8]>
55+
{
56+
let mut sskr_shares = Vec::with_capacity(shares.len());
57+
58+
for share in shares {
59+
let sskr_share = deserialize_share(share.as_ref())?;
60+
sskr_shares.push(sskr_share);
61+
}
62+
63+
combine_shares(&sskr_shares)
64+
}
465

566
fn serialize_share(share: &SSKRShare) -> Vec<u8> {
667
// pack the id, group and member data into 5 bytes:
@@ -38,25 +99,25 @@ fn serialize_share(share: &SSKRShare) -> Vec<u8> {
3899

39100
fn deserialize_share(source: &[u8]) -> Result<SSKRShare, Error> {
40101
if source.len() < METADATA_SIZE_BYTES {
41-
return Err(Error::NotEnoughSerializedBytes);
102+
return Err(Error::ShareLengthInvalid);
42103
}
43104

44105
let group_threshold = ((source[2] >> 4) + 1) as usize;
45106
let group_count = ((source[2] & 0xf) + 1) as usize;
46107

47108
if group_threshold > group_count {
48-
return Err(Error::InvalidGroupThreshold);
109+
return Err(Error::GroupThresholdInvalid);
49110
}
50111

51112
let identifier = ((source[0] as u16) << 8) | source[1] as u16;
52113
let group_index = (source[3] >> 4) as usize;
53114
let member_threshold = ((source[3] & 0xf) + 1) as usize;
54115
let reserved = source[4] >> 4;
55116
if reserved != 0 {
56-
return Err(Error::InvalidReservedBits);
117+
return Err(Error::ShareReservedBitsInvalid);
57118
}
58119
let member_index = (source[4] & 0xf) as usize;
59-
let value = Secret::new(source[METADATA_SIZE_BYTES..].to_vec())?;
120+
let value = Secret::new(&source[METADATA_SIZE_BYTES..])?;
60121

61122
Ok(SSKRShare::new(
62123
identifier,
@@ -106,28 +167,6 @@ fn generate_shares(
106167
Ok(groups_shares)
107168
}
108169

109-
pub fn sskr_generate_using(
110-
spec: &Spec,
111-
master_secret: &Secret,
112-
random_generator: &mut impl RandomNumberGenerator
113-
) -> Result<Vec<Vec<Vec<u8>>>, Error> {
114-
let groups_shares = generate_shares(spec, master_secret, random_generator)?;
115-
116-
let result: Vec<Vec<Vec<u8>>> = groups_shares.iter().map (|group| {
117-
group.iter().map(serialize_share).collect()
118-
}).collect();
119-
120-
Ok(result)
121-
}
122-
123-
pub fn sskr_generate(
124-
spec: &Spec,
125-
master_secret: &Secret
126-
) -> Result<Vec<Vec<Vec<u8>>>, Error> {
127-
let mut rng = bc_crypto::SecureRandomNumberGenerator;
128-
sskr_generate_using(spec, master_secret, &mut rng)
129-
}
130-
131170
#[derive(Debug)]
132171
struct Group {
133172
group_index: usize,
@@ -155,7 +194,7 @@ fn combine_shares(shares: &[SSKRShare]) -> Result<Secret, Error> {
155194
let mut group_count = 0;
156195

157196
if shares.is_empty() {
158-
return Err(Error::EmptyShareSet);
197+
return Err(Error::SharesEmpty);
159198
}
160199

161200
let mut next_group = 0;
@@ -176,7 +215,7 @@ fn combine_shares(shares: &[SSKRShare]) -> Result<Secret, Error> {
176215
share.group_count() != group_count ||
177216
share.value().len() != secret_len
178217
{
179-
return Err(Error::InvalidShareSet);
218+
return Err(Error::ShareSetInvalid);
180219
}
181220
}
182221

@@ -186,7 +225,7 @@ fn combine_shares(shares: &[SSKRShare]) -> Result<Secret, Error> {
186225
if share.group_index() == group.group_index {
187226
group_found = true;
188227
if share.member_threshold() != group.member_threshold {
189-
return Err(Error::InvalidMemberThreshold);
228+
return Err(Error::MemberThresholdInvalid);
190229
}
191230
for k in 0..group.count {
192231
if share.member_index() == group.member_indexes[k] {
@@ -228,17 +267,3 @@ fn combine_shares(shares: &[SSKRShare]) -> Result<Secret, Error> {
228267

229268
Ok(master_secret)
230269
}
231-
232-
pub fn sskr_combine<T>(shares: &[T]) -> Result<Secret, Error>
233-
where
234-
T: AsRef<[u8]>
235-
{
236-
let mut sskr_shares = Vec::with_capacity(shares.len());
237-
238-
for share in shares {
239-
let sskr_share = deserialize_share(share.as_ref())?;
240-
sskr_shares.push(sskr_share);
241-
}
242-
243-
combine_shares(&sskr_shares)
244-
}

src/error.rs

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,65 @@
1+
/// Errors that can occur when using the SSKR library.
12
#[derive(Debug, PartialEq)]
23
pub enum Error {
3-
NotEnoughShares,
4-
TooManyShares,
5-
SecretTooShort,
6-
SecretTooLong,
7-
SecretLengthNotEven,
8-
NotEnoughSerializedBytes,
9-
InvalidGroupThreshold,
10-
InvalidReservedBits,
11-
InvalidMemberThreshold,
12-
InvalidSingletonMember,
13-
EmptyShareSet,
14-
InvalidShareSet,
4+
/// When combining shares, the provided shares contained a duplicate member index.
155
DuplicateMemberIndex,
6+
7+
/// When creating a split spec, the group count is invalid.
8+
GroupCountInvalid,
9+
10+
/// When creating a split spec, the group threshold is invalid.
11+
GroupThresholdInvalid,
12+
13+
/// When creating a group spec, the member count is invalid.
14+
MemberCountInvalid,
15+
16+
/// When creating a group spec, the member threshold is invalid.
17+
MemberThresholdInvalid,
18+
19+
/// When combining shares, the provided shares did not contain enough groups.
1620
NotEnoughGroups,
21+
22+
/// When creating a secret, the secret is not of even length.
23+
SecretLengthNotEven,
24+
25+
/// When creating a secret, the secret is too long.
26+
SecretTooLong,
27+
28+
/// When creating a secret, the secret is too short.
29+
SecretTooShort,
30+
31+
/// When combining shares, the provided shares did not contain enough serialized bytes.
32+
ShareLengthInvalid,
33+
34+
/// When combining shares, the provided shares contained invalid reserved bits.
35+
ShareReservedBitsInvalid,
36+
37+
/// When combining shares, the provided shares were empty.
38+
SharesEmpty,
39+
40+
/// When combining shares, the provided shares were invalid.
41+
ShareSetInvalid,
42+
43+
/// An error returned from the `bc-shamir` crate.
1744
ShamirError(bc_shamir::Error),
1845
}
1946

2047
impl std::fmt::Display for Error {
2148
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2249
match *self {
23-
Error::NotEnoughShares => write!(f, "Not enough shares"),
24-
Error::TooManyShares => write!(f, "Too many shares"),
25-
Error::SecretTooLong => write!(f, "Secret is too long"),
26-
Error::SecretTooShort => write!(f, "Secret is too short"),
27-
Error::SecretLengthNotEven => write!(f, "Secret is not of even length"),
28-
Error::NotEnoughSerializedBytes => write!(f, "Not enough serialized bytes"),
29-
Error::InvalidGroupThreshold => write!(f, "Invalid group threshold"),
30-
Error::InvalidReservedBits => write!(f, "Invalid reserved bits"),
31-
Error::InvalidMemberThreshold => write!(f, "Invalid member threshold"),
32-
Error::InvalidSingletonMember => write!(f, "Invalid singleton member"),
33-
Error::EmptyShareSet => write!(f, "Empty share set"),
34-
Error::InvalidShareSet => write!(f, "Invalid share set"),
3550
Error::DuplicateMemberIndex => write!(f, "Duplicate member index"),
51+
Error::GroupCountInvalid => write!(f, "Invalid group count"),
52+
Error::GroupThresholdInvalid => write!(f, "Invalid group threshold"),
53+
Error::MemberCountInvalid => write!(f, "Not enough shares"),
54+
Error::MemberThresholdInvalid => write!(f, "Invalid member threshold"),
3655
Error::NotEnoughGroups => write!(f, "Not enough groups"),
56+
Error::SecretLengthNotEven => write!(f, "Secret is not of even length"),
57+
Error::SecretTooLong => write!(f, "Secret is too long"),
58+
Error::SecretTooShort => write!(f, "Secret is too short"),
59+
Error::ShareLengthInvalid => write!(f, "Not enough serialized bytes"),
60+
Error::ShareReservedBitsInvalid => write!(f, "Invalid reserved bits"),
61+
Error::SharesEmpty => write!(f, "Empty share set"),
62+
Error::ShareSetInvalid => write!(f, "Invalid share set"),
3763
Error::ShamirError(ref e) => write!(f, "{}", e),
3864
}
3965
}

0 commit comments

Comments
 (0)