pyquorum 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyquorum-0.1.3/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 svvqt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,76 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyquorum
3
+ Version: 0.1.3
4
+ Classifier: Programming Language :: Python :: 3
5
+ Classifier: License :: OSI Approved :: MIT License
6
+ Classifier: Topic :: Security :: Cryptography
7
+ License-File: LICENSE
8
+ Summary: Cryptographic library for secret sharing and key management, powered by Rust
9
+ Author-email: svvqt <kon.vitkovskii@gmail.com>
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
12
+ Project-URL: Repository, https://github.com/svvqt/pyquorum
13
+
14
+ # PyQuorum
15
+
16
+ Cryptographic library for secret sharing and key management, powered by Rust
17
+
18
+ ## Installation
19
+ ```bash
20
+ pip install pyquorum
21
+ ```
22
+
23
+ ## Quick Start
24
+ ```python
25
+ from pyquorum import ShamirScheme, generate_key
26
+
27
+ k=3 # threshold for combine
28
+ n=4 # number of total shares
29
+ key = generate_key()
30
+ Scheme = ShamirScheme(k, n)
31
+ shares = Scheme.split(key)
32
+ combine = Scheme.combine(shares)
33
+ ```
34
+
35
+ ## Security
36
+ This library does:
37
+ - generate key
38
+ - split secret
39
+ - combine secret
40
+
41
+ What it doesn't:
42
+ - replace encryption packages like cryptography, pyserpent and etc
43
+
44
+ If you found a security issue, please refer to [SECURITY.md](SECURITY.md)
45
+
46
+ ## Roadmap to v1.0.0
47
+ - [x] - Shamir Scheme
48
+ - [ ] - Blakley Scheme
49
+ - [ ] - Additive Scheme
50
+ - [ ] - HKDF Key Derivation
51
+ - [ ] - Threshold ECDSA
52
+
53
+ ## Support
54
+ If you find this package usefull, you can star repo on github
55
+
56
+ ## Theory
57
+
58
+ ### Shamir Scheme Share
59
+
60
+ How to split a secret key
61
+
62
+ ![Shamir split diagram](docs/shamir1.png)
63
+
64
+ How to combine a secret key
65
+
66
+ ![Shamir combine diagram](docs/shamir2.png)
67
+
68
+ ### Example
69
+
70
+ ![Source secret key](docs/shamir_example1.png)
71
+
72
+ ![Spliting secret key to 5 shares](docs/shamir_example2.png)
73
+
74
+ ![Combining 3 shares to secret key](docs/shamir_example3.png)
75
+
76
+ ![Result](docs/shamir_example4.png)
@@ -0,0 +1,63 @@
1
+ # PyQuorum
2
+
3
+ Cryptographic library for secret sharing and key management, powered by Rust
4
+
5
+ ## Installation
6
+ ```bash
7
+ pip install pyquorum
8
+ ```
9
+
10
+ ## Quick Start
11
+ ```python
12
+ from pyquorum import ShamirScheme, generate_key
13
+
14
+ k=3 # threshold for combine
15
+ n=4 # number of total shares
16
+ key = generate_key()
17
+ Scheme = ShamirScheme(k, n)
18
+ shares = Scheme.split(key)
19
+ combine = Scheme.combine(shares)
20
+ ```
21
+
22
+ ## Security
23
+ This library does:
24
+ - generate key
25
+ - split secret
26
+ - combine secret
27
+
28
+ What it doesn't:
29
+ - replace encryption packages like cryptography, pyserpent and etc
30
+
31
+ If you found a security issue, please refer to [SECURITY.md](SECURITY.md)
32
+
33
+ ## Roadmap to v1.0.0
34
+ - [x] - Shamir Scheme
35
+ - [ ] - Blakley Scheme
36
+ - [ ] - Additive Scheme
37
+ - [ ] - HKDF Key Derivation
38
+ - [ ] - Threshold ECDSA
39
+
40
+ ## Support
41
+ If you find this package usefull, you can star repo on github
42
+
43
+ ## Theory
44
+
45
+ ### Shamir Scheme Share
46
+
47
+ How to split a secret key
48
+
49
+ ![Shamir split diagram](docs/shamir1.png)
50
+
51
+ How to combine a secret key
52
+
53
+ ![Shamir combine diagram](docs/shamir2.png)
54
+
55
+ ### Example
56
+
57
+ ![Source secret key](docs/shamir_example1.png)
58
+
59
+ ![Spliting secret key to 5 shares](docs/shamir_example2.png)
60
+
61
+ ![Combining 3 shares to secret key](docs/shamir_example3.png)
62
+
63
+ ![Result](docs/shamir_example4.png)
@@ -0,0 +1,34 @@
1
+ [build-system]
2
+ requires = ["maturin>=1.0,<2.0"]
3
+ build-backend = "maturin"
4
+
5
+ [project]
6
+ name = "pyquorum"
7
+ version = "0.1.3"
8
+ description = "Cryptographic library for secret sharing and key management, powered by Rust"
9
+ license = {file = "LICENSE"}
10
+ authors = [
11
+ {name = "svvqt", email = "kon.vitkovskii@gmail.com"}
12
+ ]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Topic :: Security :: Cryptography",
17
+ ]
18
+ dependencies = []
19
+ requires-python = ">=3.9"
20
+ readme = "README.md"
21
+
22
+ [project.urls]
23
+ Repository = "https://github.com/svvqt/pyquorum"
24
+
25
+ [tool.maturin]
26
+ python-source = "src"
27
+ module-name = "pyquorum.pyquorum_core"
28
+ features = ["pyo3/abi3-py39"]
29
+ include = ["LICENSE", "README.md"]
30
+ python-package = ["pyquorum"]
31
+ manifest-path = "src/rust/Cargo.toml"
32
+
33
+ [tool.pytest.ini_options]
34
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ from .sharing.shamir import ShamirScheme
2
+ from .keys.generate import generate_key
3
+
4
+ __all__ = ["ShamirScheme", "generate_key"]
@@ -0,0 +1,3 @@
1
+ def generate_key() -> bytes: ...
2
+ def split_secret(secret: bytes, k: int, n: int) -> list[str]: ...
3
+ def combine_shares(shares: list[str], k: int) -> bytes: ...
@@ -0,0 +1,19 @@
1
+ class PyQuorumError(Exception):
2
+ pass
3
+
4
+ class InvalidKeyError(PyQuorumError):
5
+ pass
6
+
7
+
8
+ class InvalidShareError(PyQuorumError):
9
+ pass
10
+
11
+ class ThresholdError(PyQuorumError):
12
+ def __init__(self, k, n):
13
+ self.k = k
14
+ self.n = n
15
+ def __str__(self):
16
+ return f"K must be less then N, now k={self.k}, n={self.n}"
17
+
18
+ class GenerateKeyError(PyQuorumError):
19
+ pass
File without changes
@@ -0,0 +1,15 @@
1
+ from .. import pyquorum_core
2
+ from ..exceptions import GenerateKeyError
3
+
4
+ def generate_key() -> bytes:
5
+ """Generate a random secret key
6
+
7
+ Returns
8
+ --------
9
+ bytes:
10
+ 32 byte generated key
11
+ """
12
+ try:
13
+ return pyquorum_core.generate_key()
14
+ except ValueError as e:
15
+ raise GenerateKeyError(str(e))
File without changes
File without changes
@@ -0,0 +1,20 @@
1
+ from abc import ABC, abstractmethod
2
+ from ..exceptions import ThresholdError
3
+
4
+
5
+ class Scheme(ABC):
6
+ def __init__(self, k: int, n: int):
7
+ self.k = k
8
+ self.n = n
9
+ if self.k > self.n:
10
+ raise ThresholdError(self.k, self.n)
11
+
12
+ @abstractmethod
13
+ def split(self, key: bytes) -> list[str]:
14
+ """Разделение ключа"""
15
+ pass
16
+
17
+ @abstractmethod
18
+ def combine(self, shares: list[str]) -> bytes:
19
+ """Соединение ключа"""
20
+ pass
@@ -0,0 +1,76 @@
1
+ from .. import pyquorum_core
2
+ from .base import Scheme
3
+ from ..exceptions import InvalidKeyError, InvalidShareError
4
+
5
+ class ShamirScheme(Scheme):
6
+ """
7
+ Class for shamir secret sharing
8
+
9
+
10
+ Parameters
11
+ ----------
12
+ k: int
13
+ minimum number of shares required to reconstruct the secret
14
+ n: int
15
+ total number of shares to generate
16
+ """
17
+ def split(self, key: bytes) -> list[str]:
18
+ """
19
+ method for splitting secret key to number of shares
20
+
21
+ Parameters
22
+ ----------
23
+ key: bytes
24
+ secret key
25
+
26
+ Returns
27
+ -------
28
+ shares: list[str]:
29
+ secret key that splitted on shares
30
+
31
+ Raises
32
+ ------
33
+ InvalidKeyError
34
+ If key not bytes or not 32 length
35
+ """
36
+ if not isinstance(key, bytes):
37
+ raise InvalidKeyError(f"Key must be bytes, not {type(key)}")
38
+
39
+ if len(key) != 32:
40
+ raise InvalidKeyError(f"Key length must be 32, not {len(key)}")
41
+
42
+ try:
43
+ return pyquorum_core.split_secret(key, self.k, self.n)
44
+ except ValueError as e:
45
+ raise InvalidKeyError(str(e))
46
+
47
+ def combine(self, shares: list[str]) -> bytes:
48
+ """
49
+ combine shares to secret key
50
+
51
+ Parameters
52
+ ----------
53
+ shares: list[str]
54
+ splitted pieces of secret key
55
+
56
+ Returns
57
+ -------
58
+ secret key: bytes
59
+ combined secret key
60
+
61
+ Raises
62
+ ------
63
+ InvalidShareError
64
+ If shares not string or less then k
65
+ """
66
+ for share in shares:
67
+ if not isinstance(share, str):
68
+ raise InvalidShareError(f"Share must be String, not {type(share)}")
69
+
70
+ if len(shares) < self.k:
71
+ raise InvalidShareError(f"Need at least {self.k} shares")
72
+
73
+ try:
74
+ return pyquorum_core.combine_shares(shares, self.k)
75
+ except ValueError as e:
76
+ raise InvalidShareError(str(e))
@@ -0,0 +1,309 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "block-buffer"
7
+ version = "0.10.4"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
10
+ dependencies = [
11
+ "generic-array",
12
+ ]
13
+
14
+ [[package]]
15
+ name = "cfg-if"
16
+ version = "1.0.4"
17
+ source = "registry+https://github.com/rust-lang/crates.io-index"
18
+ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
19
+
20
+ [[package]]
21
+ name = "cpufeatures"
22
+ version = "0.2.17"
23
+ source = "registry+https://github.com/rust-lang/crates.io-index"
24
+ checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
25
+ dependencies = [
26
+ "libc",
27
+ ]
28
+
29
+ [[package]]
30
+ name = "crypto-common"
31
+ version = "0.1.7"
32
+ source = "registry+https://github.com/rust-lang/crates.io-index"
33
+ checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
34
+ dependencies = [
35
+ "generic-array",
36
+ "typenum",
37
+ ]
38
+
39
+ [[package]]
40
+ name = "digest"
41
+ version = "0.10.7"
42
+ source = "registry+https://github.com/rust-lang/crates.io-index"
43
+ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
44
+ dependencies = [
45
+ "block-buffer",
46
+ "crypto-common",
47
+ ]
48
+
49
+ [[package]]
50
+ name = "generic-array"
51
+ version = "0.14.7"
52
+ source = "registry+https://github.com/rust-lang/crates.io-index"
53
+ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
54
+ dependencies = [
55
+ "typenum",
56
+ "version_check",
57
+ ]
58
+
59
+ [[package]]
60
+ name = "getrandom"
61
+ version = "0.2.17"
62
+ source = "registry+https://github.com/rust-lang/crates.io-index"
63
+ checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
64
+ dependencies = [
65
+ "cfg-if",
66
+ "libc",
67
+ "wasi",
68
+ ]
69
+
70
+ [[package]]
71
+ name = "heck"
72
+ version = "0.5.0"
73
+ source = "registry+https://github.com/rust-lang/crates.io-index"
74
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
75
+
76
+ [[package]]
77
+ name = "libc"
78
+ version = "0.2.185"
79
+ source = "registry+https://github.com/rust-lang/crates.io-index"
80
+ checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
81
+
82
+ [[package]]
83
+ name = "once_cell"
84
+ version = "1.21.4"
85
+ source = "registry+https://github.com/rust-lang/crates.io-index"
86
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
87
+
88
+ [[package]]
89
+ name = "portable-atomic"
90
+ version = "1.13.1"
91
+ source = "registry+https://github.com/rust-lang/crates.io-index"
92
+ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
93
+
94
+ [[package]]
95
+ name = "ppv-lite86"
96
+ version = "0.2.21"
97
+ source = "registry+https://github.com/rust-lang/crates.io-index"
98
+ checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
99
+ dependencies = [
100
+ "zerocopy",
101
+ ]
102
+
103
+ [[package]]
104
+ name = "proc-macro2"
105
+ version = "1.0.106"
106
+ source = "registry+https://github.com/rust-lang/crates.io-index"
107
+ checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
108
+ dependencies = [
109
+ "unicode-ident",
110
+ ]
111
+
112
+ [[package]]
113
+ name = "pyo3"
114
+ version = "0.28.3"
115
+ source = "registry+https://github.com/rust-lang/crates.io-index"
116
+ checksum = "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12"
117
+ dependencies = [
118
+ "libc",
119
+ "once_cell",
120
+ "portable-atomic",
121
+ "pyo3-build-config",
122
+ "pyo3-ffi",
123
+ "pyo3-macros",
124
+ ]
125
+
126
+ [[package]]
127
+ name = "pyo3-build-config"
128
+ version = "0.28.3"
129
+ source = "registry+https://github.com/rust-lang/crates.io-index"
130
+ checksum = "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e"
131
+ dependencies = [
132
+ "target-lexicon",
133
+ ]
134
+
135
+ [[package]]
136
+ name = "pyo3-ffi"
137
+ version = "0.28.3"
138
+ source = "registry+https://github.com/rust-lang/crates.io-index"
139
+ checksum = "7f29e10af80b1f7ccaf7f69eace800a03ecd13e883acfacc1e5d0988605f651e"
140
+ dependencies = [
141
+ "libc",
142
+ "pyo3-build-config",
143
+ ]
144
+
145
+ [[package]]
146
+ name = "pyo3-macros"
147
+ version = "0.28.3"
148
+ source = "registry+https://github.com/rust-lang/crates.io-index"
149
+ checksum = "df6e520eff47c45997d2fc7dd8214b25dd1310918bbb2642156ef66a67f29813"
150
+ dependencies = [
151
+ "proc-macro2",
152
+ "pyo3-macros-backend",
153
+ "quote",
154
+ "syn",
155
+ ]
156
+
157
+ [[package]]
158
+ name = "pyo3-macros-backend"
159
+ version = "0.28.3"
160
+ source = "registry+https://github.com/rust-lang/crates.io-index"
161
+ checksum = "c4cdc218d835738f81c2338f822078af45b4afdf8b2e33cbb5916f108b813acb"
162
+ dependencies = [
163
+ "heck",
164
+ "proc-macro2",
165
+ "pyo3-build-config",
166
+ "quote",
167
+ "syn",
168
+ ]
169
+
170
+ [[package]]
171
+ name = "pyquorum_core"
172
+ version = "0.1.0"
173
+ dependencies = [
174
+ "pyo3",
175
+ "rand",
176
+ "sha2",
177
+ "zeroize",
178
+ ]
179
+
180
+ [[package]]
181
+ name = "quote"
182
+ version = "1.0.45"
183
+ source = "registry+https://github.com/rust-lang/crates.io-index"
184
+ checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
185
+ dependencies = [
186
+ "proc-macro2",
187
+ ]
188
+
189
+ [[package]]
190
+ name = "rand"
191
+ version = "0.8.5"
192
+ source = "registry+https://github.com/rust-lang/crates.io-index"
193
+ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
194
+ dependencies = [
195
+ "libc",
196
+ "rand_chacha",
197
+ "rand_core",
198
+ ]
199
+
200
+ [[package]]
201
+ name = "rand_chacha"
202
+ version = "0.3.1"
203
+ source = "registry+https://github.com/rust-lang/crates.io-index"
204
+ checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
205
+ dependencies = [
206
+ "ppv-lite86",
207
+ "rand_core",
208
+ ]
209
+
210
+ [[package]]
211
+ name = "rand_core"
212
+ version = "0.6.4"
213
+ source = "registry+https://github.com/rust-lang/crates.io-index"
214
+ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
215
+ dependencies = [
216
+ "getrandom",
217
+ ]
218
+
219
+ [[package]]
220
+ name = "sha2"
221
+ version = "0.10.9"
222
+ source = "registry+https://github.com/rust-lang/crates.io-index"
223
+ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
224
+ dependencies = [
225
+ "cfg-if",
226
+ "cpufeatures",
227
+ "digest",
228
+ ]
229
+
230
+ [[package]]
231
+ name = "syn"
232
+ version = "2.0.117"
233
+ source = "registry+https://github.com/rust-lang/crates.io-index"
234
+ checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
235
+ dependencies = [
236
+ "proc-macro2",
237
+ "quote",
238
+ "unicode-ident",
239
+ ]
240
+
241
+ [[package]]
242
+ name = "target-lexicon"
243
+ version = "0.13.5"
244
+ source = "registry+https://github.com/rust-lang/crates.io-index"
245
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
246
+
247
+ [[package]]
248
+ name = "typenum"
249
+ version = "1.19.0"
250
+ source = "registry+https://github.com/rust-lang/crates.io-index"
251
+ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
252
+
253
+ [[package]]
254
+ name = "unicode-ident"
255
+ version = "1.0.24"
256
+ source = "registry+https://github.com/rust-lang/crates.io-index"
257
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
258
+
259
+ [[package]]
260
+ name = "version_check"
261
+ version = "0.9.5"
262
+ source = "registry+https://github.com/rust-lang/crates.io-index"
263
+ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
264
+
265
+ [[package]]
266
+ name = "wasi"
267
+ version = "0.11.1+wasi-snapshot-preview1"
268
+ source = "registry+https://github.com/rust-lang/crates.io-index"
269
+ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
270
+
271
+ [[package]]
272
+ name = "zerocopy"
273
+ version = "0.8.48"
274
+ source = "registry+https://github.com/rust-lang/crates.io-index"
275
+ checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
276
+ dependencies = [
277
+ "zerocopy-derive",
278
+ ]
279
+
280
+ [[package]]
281
+ name = "zerocopy-derive"
282
+ version = "0.8.48"
283
+ source = "registry+https://github.com/rust-lang/crates.io-index"
284
+ checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
285
+ dependencies = [
286
+ "proc-macro2",
287
+ "quote",
288
+ "syn",
289
+ ]
290
+
291
+ [[package]]
292
+ name = "zeroize"
293
+ version = "1.8.2"
294
+ source = "registry+https://github.com/rust-lang/crates.io-index"
295
+ checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
296
+ dependencies = [
297
+ "zeroize_derive",
298
+ ]
299
+
300
+ [[package]]
301
+ name = "zeroize_derive"
302
+ version = "1.4.3"
303
+ source = "registry+https://github.com/rust-lang/crates.io-index"
304
+ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
305
+ dependencies = [
306
+ "proc-macro2",
307
+ "quote",
308
+ "syn",
309
+ ]
@@ -0,0 +1,18 @@
1
+ [package]
2
+ name = "pyquorum_core"
3
+ version = "0.1.0"
4
+ edition = "2024"
5
+
6
+ [lib]
7
+ name = "pyquorum_core"
8
+ crate-type = ["cdylib"]
9
+
10
+ [dependencies]
11
+ pyo3 = { version = "0.28.3", features = ["extension-module", "abi3-py39"] }
12
+ rand = "0.8"
13
+ zeroize = { version = "1.7", features = ["derive"] }
14
+ sha2 = "0.10"
15
+
16
+ [profile.release]
17
+ opt-level = 3
18
+ strip = true
@@ -0,0 +1,184 @@
1
+ use pyo3::exceptions::PyValueError;
2
+ use pyo3::prelude::*;
3
+ use pyo3::types::PyBytes;
4
+ use rand::rngs::OsRng;
5
+ use rand::RngCore;
6
+ use zeroize::{Zeroize, ZeroizeOnDrop};
7
+
8
+ // ── Константы ─────────────────────────────────────────────────────────────
9
+
10
+ const PRIME: u128 = (1u128 << 127) - 1;
11
+ const KEY_LEN: usize = 32;
12
+
13
+ // ── Защищённый тип ────────────────────────────────────────────────────────
14
+
15
+ #[derive(Zeroize, ZeroizeOnDrop, Clone)]
16
+ struct SecretKey([u8; KEY_LEN]);
17
+
18
+ // ── Арифметика GF(p) (без изменений) ──────────────────────────────────────
19
+
20
+ fn mul_mod(a: u128, b: u128, m: u128) -> u128 {
21
+ let mut result = 0u128;
22
+ let mut base = a % m;
23
+ let mut exp = b;
24
+ while exp > 0 {
25
+ if exp & 1 == 1 {
26
+ result = (result + base) % m;
27
+ }
28
+ base = (base << 1) % m;
29
+ exp >>= 1;
30
+ }
31
+ result
32
+ }
33
+
34
+ fn mod_pow(mut base: u128, mut exp: u128, modulus: u128) -> u128 {
35
+ let mut result = 1u128;
36
+ base %= modulus;
37
+ while exp > 0 {
38
+ if exp & 1 == 1 {
39
+ result = mul_mod(result, base, modulus);
40
+ }
41
+ exp >>= 1;
42
+ base = mul_mod(base, base, modulus);
43
+ }
44
+ result
45
+ }
46
+
47
+ fn mod_inv(a: u128, p: u128) -> u128 {
48
+ mod_pow(a, p - 2, p)
49
+ }
50
+
51
+ // ── Shamir's Secret Sharing (внутренняя логика сохранена) ──────────────────
52
+
53
+ fn shamir_split_internal(secret: &[u8; KEY_LEN], k: usize, n: usize) -> Result<Vec<String>, String> {
54
+ if k < 2 || n < k {
55
+ return Err("Invalid k/n parameters".into());
56
+ }
57
+
58
+ let secrets = [
59
+ u64::from_be_bytes(secret[0..8].try_into().unwrap()) as u128,
60
+ u64::from_be_bytes(secret[8..16].try_into().unwrap()) as u128,
61
+ u64::from_be_bytes(secret[16..24].try_into().unwrap()) as u128,
62
+ u64::from_be_bytes(secret[24..32].try_into().unwrap()) as u128,
63
+ ];
64
+
65
+ let mut rng = OsRng;
66
+ let mut rand_coeff = || {
67
+ let lo = rng.next_u64() as u128;
68
+ let hi = (rng.next_u64() as u128) & 0x7FFF_FFFF_FFFF_FFFF;
69
+ (hi << 64 | lo) % PRIME
70
+ };
71
+
72
+ let coeffs: Vec<Vec<u128>> = secrets
73
+ .iter()
74
+ .map(|_| (0..k - 1).map(|_| rand_coeff()).collect())
75
+ .collect();
76
+
77
+ let eval_poly = |s: u128, c_list: &[u128], x: u128| -> u128 {
78
+ let mut result = s;
79
+ let mut x_pow = x;
80
+ for &c in c_list {
81
+ result = (result + mul_mod(c, x_pow, PRIME)) % PRIME;
82
+ x_pow = mul_mod(x_pow, x, PRIME);
83
+ }
84
+ result
85
+ };
86
+
87
+ let shares = (1..=n as u128)
88
+ .map(|x| {
89
+ let res: Vec<String> = secrets
90
+ .iter()
91
+ .zip(&coeffs)
92
+ .map(|(&s, c)| format!("{:032x}", eval_poly(s, c, x)))
93
+ .collect();
94
+ format!("{}:{}", x, res.join(":"))
95
+ })
96
+ .collect();
97
+
98
+ Ok(shares)
99
+ }
100
+
101
+ fn shamir_combine_internal(shares: &[String], k: usize) -> Result<SecretKey, String> {
102
+ if shares.len() < k {
103
+ return Err(format!("Need at least {} shares", k));
104
+ }
105
+
106
+ let mut parsed = Vec::with_capacity(k);
107
+ for s in &shares[..k] {
108
+ let p: Vec<&str> = s.split(':').collect();
109
+ if p.len() != 5 { return Err("Invalid format".into()); }
110
+ let idx = p[0].parse::<u128>().map_err(|e| e.to_string())?;
111
+ let vals = [
112
+ u128::from_str_radix(p[1], 16).map_err(|e| e.to_string())?,
113
+ u128::from_str_radix(p[2], 16).map_err(|e| e.to_string())?,
114
+ u128::from_str_radix(p[3], 16).map_err(|e| e.to_string())?,
115
+ u128::from_str_radix(p[4], 16).map_err(|e| e.to_string())?,
116
+ ];
117
+ parsed.push((idx, vals));
118
+ }
119
+
120
+ let mut key_bytes = [0u8; KEY_LEN];
121
+ for comp in 0..4 {
122
+ let mut result = 0u128;
123
+ for i in 0..k {
124
+ let (xi, yi) = (parsed[i].0, parsed[i].1[comp]);
125
+ let (mut num, mut den) = (1u128, 1u128);
126
+ for j in 0..k {
127
+ if i == j { continue; }
128
+ let xj = parsed[j].0;
129
+ num = mul_mod(num, PRIME - (xj % PRIME), PRIME);
130
+ let diff = if xi > xj { xi - xj } else { PRIME - (xj - xi) % PRIME };
131
+ den = mul_mod(den, diff, PRIME);
132
+ }
133
+ let li = mul_mod(num, mod_inv(den, PRIME), PRIME);
134
+ result = (result + mul_mod(yi, li, PRIME)) % PRIME;
135
+ }
136
+ key_bytes[comp * 8..(comp + 1) * 8].copy_from_slice(&(result as u64).to_be_bytes());
137
+ }
138
+
139
+ Ok(SecretKey(key_bytes))
140
+ }
141
+
142
+ // ── PyO3 bindings (Modern Bound API) ──────────────────────────────────────
143
+
144
+ #[pyfunction]
145
+ /// Генерирует случайный 256-битный ключ. Возвращает bytes.
146
+ fn generate_key(py: Python<'_>) -> PyResult<Bound<'_, PyBytes>> {
147
+ let mut key = [0u8; KEY_LEN];
148
+ OsRng.fill_bytes(&mut key);
149
+
150
+ // В 0.28.3 используем просто .new(), он возвращает Bound
151
+ let result = PyBytes::new(py, &key);
152
+
153
+ key.zeroize();
154
+ Ok(result)
155
+ }
156
+
157
+ #[pyfunction]
158
+ /// Разбивает секрет на n частей.
159
+ fn split_secret(secret: &[u8], k: usize, n: usize) -> PyResult<Vec<String>> {
160
+ let key: [u8; KEY_LEN] = secret
161
+ .try_into()
162
+ .map_err(|_| PyValueError::new_err(format!("Secret must be {} bytes", KEY_LEN)))?;
163
+
164
+ shamir_split_internal(&key, k, n)
165
+ .map_err(|e| PyValueError::new_err(e))
166
+ }
167
+
168
+ #[pyfunction]
169
+ /// Восстанавливает секрет. Возвращает bytes.
170
+ fn combine_shares<'py>(py: Python<'py>, shares: Vec<String>, k: usize) -> PyResult<Bound<'py, PyBytes>> {
171
+ let key = shamir_combine_internal(&shares, k)
172
+ .map_err(|e| PyValueError::new_err(e))?;
173
+
174
+ // Снова используем .new() вместо .new_bound()
175
+ Ok(PyBytes::new(py, &key.0))
176
+ }
177
+
178
+ #[pymodule]
179
+ fn pyquorum_core(m: &Bound<'_, PyModule>) -> PyResult<()> {
180
+ m.add_function(wrap_pyfunction!(generate_key, m)?)?;
181
+ m.add_function(wrap_pyfunction!(split_secret, m)?)?;
182
+ m.add_function(wrap_pyfunction!(combine_shares, m)?)?;
183
+ Ok(())
184
+ }