20个版本 (11个重大更改)

0.12.0 2023年6月17日
0.10.1 2023年6月5日
0.10.0 2023年2月16日
0.9.1 2022年7月26日
0.6.1 2019年11月27日

#409 in 密码学

Download history 70/week @ 2024-03-20 20/week @ 2024-03-27 7/week @ 2024-04-03 2/week @ 2024-06-26 108/week @ 2024-07-03

每月110次下载

MIT许可证

80KB
1.5K SLoC

sss-rs

Rust

Rust中秘密共享方案的一个实现。

给定需要重建的N个份额,以及生成的M个份额,可以使用任何N <= X <= M的X个份额,无需指定所需的数量(但使用更多份额会增加重建时间)。

有两个主要模块,[wrapped_sharing]和[basic_sharing]。[basic_sharing]包含核心的秘密共享实现,而[wrapped_sharing]提供了围绕这些实现的便利包装,以及验证秘密重建的选项。

此实现使用GF(256)上的算术作为核心秘密共享算法。

示例

抽象

功能包装API

适用于处理相对较小的秘密。

use sss_rs::prelude::*;
let shares_required = 3;
let shares_to_create = 3;
let verify = true;
let secret: Vec<u8> = vec![5, 4, 9, 1, 2, 128, 43];
let shares = share(&secret, shares_required, shares_to_create, verify).unwrap();
let recon = reconstruct(&shares, verify).unwrap();
assert_eq!(secret, recon);

流式包装API

适用于处理非常大的秘密以及您不想一次性加载到内存中的份额。

use sss_rs::prelude::*;
use std::io::Cursor;

let mut dest1 = Cursor::new(Vec::new());
let mut dest2 = Cursor::new(Vec::new());
let full_secret = b"This is a very long secret read in from a buffered file reader";
let secret_chunks = full_secret.chunks(8).collect::<Vec<&[u8]>>();
let mut recon_dest = Cursor::new(Vec::new());

let mut sharer = Sharer::builder()
    .with_shares_required(2)
    .with_output(&mut dest1)
    .with_output(&mut dest2)
    .with_verify(true)
    .build()
    .unwrap();

for secret in secret_chunks.iter() {
    sharer.update(secret).unwrap();
}
sharer.finalize().unwrap();

// The outputs dest1 and dest2 have had their shares written to them.

let mut reconstructor = Reconstructor::new(&mut recon_dest, true);

for (chunk1, chunk2) in dest1.get_ref().chunks(4).zip(dest2.get_ref().chunks(4)) {
    reconstructor.update(&[chunk1, chunk2]).unwrap();
}
reconstructor.finalize().unwrap();
assert_eq!(&full_secret, &recon_dest.into_inner().as_slice());

核心

单字节

如果您需要更多控制共享和重建或编写自己的抽象,可以使用[basic_sharing]函数。

use sss_rs::basic_sharing::{from_secret, reconstruct_secret};
let secret: u8 = 23; // The secret to be split into shares
let shares_required = 3; // The number of shares required to reconstruct the secret
let shares_to_create = 3; // The number of shares to create, can be greater than the required

let shares: Vec<(u8, u8)> = from_secret(
		secret,
		shares_required,
		shares_to_create,
        None,
	).unwrap();
let secret_recon = reconstruct_secret(shares);

assert_eq!(secret, secret_recon);

字节切片

与上述示例非常相似,但适用于字节切片。

use sss_rs::basic_sharing::{from_secrets, reconstruct_secrets};
let secret = b"Hello world"; // The secret to be split into shares
let shares_required = 3; // The number of shares required to reconstruct the secret
let shares_to_create = 3; // The number of shares to create, can be greater than the required

let shares: Vec<Vec<(u8, u8)>> = from_secrets(
		secret,
		shares_required,
		shares_to_create,
        None,
	).unwrap();
let secret_recon = reconstruct_secrets(shares).unwrap();

assert_eq!(secret, secret_recon.as_slice());

字节切片去重x值

与上述示例非常相似,但会将每个份额的x值去重并放置在每个份额列表的开头。[wrapped_sharing]中的所有功能都利用了这一点。以下是对其如何以及为什么如此工作的更详细解释。

use sss_rs::basic_sharing::{from_secrets_compressed, reconstruct_secrets_compressed};
let secret = b"Hello world"; // The secret to be split into shares
let shares_required = 3; // The number of shares required to reconstruct the secret
let shares_to_create = 3; // The number of shares to create, can be greater than the required

let shares: Vec<Vec<u8>> = from_secrets_compressed(
		secret,
		shares_required,
		shares_to_create,
        None,
	).unwrap();
let secret_recon = reconstruct_secrets_compressed(shares).unwrap();

assert_eq!(secret, secret_recon.as_slice());

共享和份额'压缩'解释

N = Number of shares required for reconstruction
M = Number of shares that were created
S = Length of the secret being shared.

通常,需要一个点列表来重建一个字节。例如:(x1, y1), (x2, y2), (x3, y3), ... (xM, yM) 其中至少需要N个点从M个创建的点中重建字节。每个份额的大小是原始秘密的两倍。

当我们共享一个字节切片时,我们得到如下份额列表

(x1a, y1a), (x2a, y2a), (x3a, y3a), ... (xMa, yMa)
(x1b, y1b), (x2b, y2b), (x3b, y3b), ... (xMb, yMb)
(x1c, y1c), (x2c, y2c), (x3c, y3c), ... (xMc, yMc)
...
(x1S, y1S), (x2S, y2S), (x3S, y3S), ... (xMS, yMS)

上述列表中的每个点对应秘密的1个字节。例如,从第一个列表中选择任何N个点将重构秘密的第一个字节。由于每个份额对应一个字节,而不是整个秘密的一部分,因此这种方式不能分发。因此,我们将它转置,使得每个列表都包含秘密中每个字节的某一部分。

(x1a, y1a), (x1b, y1b), (x1c, y1c), ... (x1S, y1S)
(x2a, y2a), (x2b, y2b), (x2c, y2c), ... (x2S, y2S)
(x3a, y3a), (x2b, y2b), (x3c, y3c), ... (x3S, y3S)
...
(xMa, yMa), (xMb, yMb), (xMc, yMc), ... (xMS, yMS)

现在,给定一个索引,可以至少从N个份额中重构出该索引处的秘密字节。例如,要重构秘密的第三个字节,你需要从至少N个份额中获取第三个点来重构。现在每个人都有一份点列表,其中每个点对应原始秘密的1个字节。这些现在可以分发。

接下来是压缩,x值可以预测,而不会对份额的安全性产生重大影响。这是转置前的份额列表的预测x值的样子。

(1, y1a), (2, y2a), (3, y3a), ... (M, yMa)
(1, y1b), (2, y2b), (3, y3b), ... (M, yMb)
(1, y1c), (2, y2c), (3, y3c), ... (M, yMc)
...
(1, y1S), (2, y2S), (3, y3S), ... (M, yMS)

And when we transposed for the reasons stated prior to make these shares distributable:

```notrust
(1, y1a), (1, y1b), (1, y1c), ... (1, y1S)
(2, y2a), (2, y2b), (2, y2c), ... (2, y2S)
(3, y3a), (3, y3b), (3, y3c), ... (3, y3S)
...
(M, yMa), (M, yMb), (M, yMc), ... (M, yMS)

我们可以看到,每个份额中每个点的x值都是相同的。因此,我们可以去重这个x值,并在每个份额的开头保留一个副本。

1, y1a, y1b, y1c, ... y1S
2, y2a, y2b, y2c, ... y2S
3, y3a, y3b, y3c, ... y3S
...
M, yMa, yMb, yMc, ... yMS

这使得每个份额的大小仅比之前减少到S + 1。

依赖关系

~3MB
~42K SLoC