├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── CHANGELOG ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples ├── mpvss_all.rs └── mpvss_sub.rs ├── rustfmt.toml ├── src ├── dleq.rs ├── lib.rs ├── mpvss.rs ├── participant.rs ├── polynomial.rs ├── sharebox.rs └── util.rs └── tests └── mpvss_tests.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | - uses: actions-rs/toolchain@v1 21 | with: 22 | toolchain: stable 23 | override: true 24 | components: clippy, rustfmt 25 | - name: Check format 26 | run: cargo fmt --all -- --check 27 | - name: Check with clippy 28 | run: cargo clippy --all 29 | - name: Build without features 30 | run: cargo build --no-default-features 31 | - name: Build 32 | run: cargo build --all --verbose 33 | - name: Run tests 34 | run: cargo test --all --verbose 35 | 36 | # examples 37 | - name: Check examples format 38 | run: cargo fmt --all -- --check 39 | working-directory: ./examples 40 | - name: Check examples with clippy 41 | run: cargo clippy --all 42 | working-directory: ./examples 43 | - name: Build examples 44 | run: cargo build --all --verbose 45 | working-directory: ./examples 46 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: [release] 5 | paths: 6 | - '**/Cargo.toml' 7 | - '.github/workflows/release.yml' 8 | 9 | jobs: 10 | publish: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions-rs/toolchain@v1 14 | with: 15 | toolchain: stable 16 | override: true 17 | - name: Checkout 18 | uses: actions/checkout@v2 19 | - name: cargo login 20 | run: cargo login ${{ secrets.CRATES_IO }} 21 | - name: cargo package 22 | run: | 23 | echo "Cargo Packaging" 24 | cargo package 25 | echo "Cargo Packaged" 26 | - name: Publish 27 | run: | 28 | echo "Cargo Publishing" 29 | cargo publish --no-verify 30 | echo "Cargo Published" 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | # Version 0.2.7 2 | 3 | ## Improvements 4 | 5 | - Remove useless clone() 6 | - Fix warnings for clippy 7 | 8 | # Version 0.2.5 9 | 10 | ## Fixs 11 | 12 | - Change license 13 | 14 | # Version 0.2.4 15 | 16 | ## Fixs 17 | 18 | - Fix clippy errors 19 | 20 | # Version 0.2.3 21 | 22 | ## Improvements 23 | 24 | - Use borrowed types for arguments 25 | 26 | # Version 0.2.2 27 | 28 | ## Fixs 29 | 30 | - Fix clippy warning 31 | 32 | # Version 0.2.1 33 | 34 | ## Fixs 35 | 36 | - Make API easier to use 37 | 38 | # Version 0.2.0 39 | 40 | ## Features 41 | 42 | - Add reconstruct_parallelized API for improve reconstruct performance 43 | 44 | # Version 0.1.0 45 | 46 | ## Improvements 47 | 48 | - Upgrade dependencies version 49 | - Improve doc 50 | - Improve mpvss-rs's API 51 | 52 | 53 | # Version 0.0.4 54 | 55 | ## Improvements 56 | 57 | - Improve example mpvss_sub 58 | 59 | # Version 0.0.3 60 | 61 | ## Improvements 62 | 63 | - Add publish workflow for cargo publish 64 | 65 | # Version 0.0.2 66 | 67 | ## Improvements 68 | 69 | - Add more exmpales. 70 | - Add crate-level doc. 71 | 72 | # Version 0.0.1 73 | 74 | ## Features 75 | 76 | - Support secret shares reconstruction. 77 | - Support threshhold (t, n) scheme. 78 | - Secret Reconstruction in single-thread. (multi-thread reconstruction will be coming soon) -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["MathxH Chen "] 3 | description = "A Simple Publicly Verifiable Secret Sharing Library" 4 | edition = "2021" 5 | keywords = ["pvss-scheme", "secret-sharing", "threshold-crypto", "zero-knowledge-proof"] 6 | license = "MIT OR Apache-2.0" 7 | name = "mpvss-rs" 8 | readme = "README.md" 9 | repository = "https://github.com/AlexiaChen/mpvss-rs" 10 | version = "0.2.7" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | num-bigint = {version = "0.2.6", features = ["rand"]} 16 | num-integer = "0.1.44" 17 | num-primes = "0.1.2" 18 | num-traits = "0.2.14" 19 | rand = "0.5.6" 20 | rayon = "1.5.0" 21 | sha2 = "0.9.2" 22 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 MathxH Chen 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) MathxH Chen 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MPVSS - A Simple Publicly Verifiable Secret Sharing Library 2 | 3 | ![CI](https://github.com/AlexiaChen/mpvss-rs/workflows/CI/badge.svg?branch=master) 4 | ![crates.io](https://github.com/AlexiaChen/mpvss-rs/workflows/Release/badge.svg?branch=release) 5 | [![Crates.io](https://img.shields.io/crates/v/mpvss-rs)](https://crates.io/crates/mpvss-rs) 6 | 7 | The library implements a simple PVSS scheme in Rust. 8 | 9 | ## What is PVSS? 10 | 11 | Secret sharing means a dealer can break a secret into secret shares among a group of participants which can reconstruct the secret only by collaboratively joining their parts of the secret. The library also implements threshold cryptography so that the dealer can decide whether all of the receiving participants need to collaborate or if a smaller subgroup of participants is sufficient to reconstruct the secret. 12 | 13 | In addition to the plain secret sharing scheme PVSS adds verifiability in the following way: All the parts the secret is split into are encrypted with the receivers' public keys respectively. The dealer publishes all the encrypted shares along with a non-interactive zero-knowledge proof that allows everbody (not only the receiving participants) to verify that the decrypted shares indeed can be used to reconstruct the secret. The participants then decrypt all their shares and exchange them along with another non-interactive zero-knowledge proof that allows the receiving participant to verify that the share is actually the result of the decryption. 14 | 15 | Thus PVSS can be used to share a secret among a group of participants so that either the secret can be reconstructed by the participants who all play fair or a participant that received a faked share can identify the malicious party. 16 | 17 | ## Build 18 | 19 | ```bash 20 | cargo build --release 21 | ``` 22 | 23 | ## Test 24 | 25 | ```bash 26 | cargo test --release 27 | ``` 28 | 29 | ## Example 30 | 31 | ```rust 32 | cargo run --release --example mpvss_all 33 | cargo run --release --example mpvss_sub 34 | ``` 35 | 36 | ### Usage 37 | 38 | #### Initialization 39 | 40 | At first we convert our secret message into a numeric value if necessary. When creating the dealer a PVSS instance is created as well which holds all the global parameters that every participant needs to know. 41 | 42 | ```rust 43 | let secret_message = String::from("Hello MPVSS."); 44 | 45 | let mut dealer = Participant::new(); 46 | dealer.initialize(); 47 | 48 | let mut p1 = Participant::new(); 49 | let mut p2 = Participant::new(); 50 | let mut p3 = Participant::new(); 51 | 52 | p1.initialize(); 53 | p2.initialize(); 54 | p3.initialize(); 55 | ``` 56 | 57 | #### Distribution & Verification 58 | 59 | The dealer splits the secret into shares, encrypts them and creates a proof so that everybody can verify that the shares (once decrypted) can be used to reconstruct the secret. The threshold determines how many shares are necessary for the reconstruction. The encrypted shares and the proof are then bundled together. 60 | 61 | ```rust 62 | // Dealer that shares the secret among p1, p2 and p3. 63 | let distribute_shares_box = dealer.distribute_secret( 64 | &string_to_secret(&secret_message), 65 | &vec![p1.publickey, p2.publickey, p3.publickey], 66 | 3, 67 | ); 68 | 69 | // p1 verifies distribution shares box containing encryted shares and proof of zero-knowlege. [p2 and p3 do this as well.] 70 | assert_eq!( 71 | p1.verify_distribution_shares(&distribute_shares_box), 72 | true 73 | ); 74 | assert_eq!( 75 | p2.verify_distribution_shares(&distribute_shares_box), 76 | true 77 | ); 78 | assert_eq!( 79 | p3.verify_distribution_shares(&distribute_shares_box), 80 | true 81 | ); 82 | ``` 83 | 84 | #### Exchange & Verification 85 | 86 | The participants extract their shares from the distribution shares box and decrypt them. They bundle them together with a proof that allows the receiver to verify that the share is indeed the result of the decryption. 87 | 88 | ```rust 89 | // p1 extracts the share. [p2 and p3 do this as well.] 90 | let s1 = p1 91 | .extract_secret_share(&distribute_shares_box, &p1.privatekey) 92 | .unwrap(); 93 | 94 | // p1, p2 and p3 exchange their descrypted shares. 95 | // ... 96 | let s2 = p2 97 | .extract_secret_share(&distribute_shares_box, &p2.privatekey) 98 | .unwrap(); 99 | let s3 = p3 100 | .extract_secret_share(&distribute_shares_box, &p3.privatekey) 101 | .unwrap(); 102 | 103 | // p1 verifies the share received from p2. [Actually everybody verifies every received share.] 104 | assert_eq!( 105 | p1.verify_share(&s2, &distribute_shares_box, &p2.publickey), 106 | true 107 | ); 108 | assert_eq!( 109 | p2.verify_share(&s3, &distribute_shares_box, &p3.publickey), 110 | true 111 | ); 112 | assert_eq!( 113 | p3.verify_share(&s1, &distribute_shares_box, &s1.publickey), 114 | true 115 | ); 116 | ``` 117 | 118 | #### Reconstruction 119 | 120 | Once a participant collected at least `threshold` shares the secret can be reconstructed. 121 | 122 | ```rust 123 | let share_boxs = [s1, s2, s3]; 124 | let r1 = p1 125 | .reconstruct(&share_boxs, &distribute_shares_box) 126 | .unwrap(); 127 | let r2 = p2 128 | .reconstruct(&share_boxs, &distribute_shares_box) 129 | .unwrap(); 130 | let r3 = p3 131 | .reconstruct(&share_boxs, &distribute_shares_box) 132 | .unwrap(); 133 | 134 | let r1_str = string_from_secret(&r1); 135 | assert_eq!(secret_message.clone(), r1_str); 136 | let r2_str = string_from_secret(&r2); 137 | assert_eq!(secret_message.clone(), r2_str); 138 | let r3_str = string_from_secret(&r3); 139 | assert_eq!(secret_message.clone(), r3_str); 140 | ``` 141 | 142 | ## In the futures 143 | 144 | Add more Elliptic Curves groups. 145 | 146 | ## Related References: 147 | 148 | - Berry Schoenmakers. [A Simple Publicly Verifiable Secret Sharing Scheme and its Application to Electronic Voting](https://www.win.tue.nl/~berry/papers/crypto99.pdf) 149 | 150 | - Adi Shamir. [How to share a secret](http://users.cms.caltech.edu/~vidick/teaching/101_crypto/Shamir1979.pdf) 151 | 152 | - Tal Rabin. [Verifiable Secret Sharing and Multiparty Protocols with Honest Majority](https://www.cs.umd.edu/users/gasarch/TOPICS/secretsharing/rabinVSS.pdf) 153 | 154 | - Markus Stadler. [Publicly Verifiable Secret Sharing](https://link.springer.com/content/pdf/10.1007%2F3-540-68339-9_17.pdf) 155 | 156 | - bitcoinwiki-org. [Publicly Verifiable Secret Sharing](https://en.bitcoinwiki.org/wiki/Publicly_Verifiable_Secret_Sharing) 157 | 158 | ## Non-Related References 159 | 160 | Because the ploynomial commitments does not Pedersen commitment and DLEQ is only computaional secure, not information-theoretic secure in this project. 161 | 162 | - crypto-stackexchange. [What is a Pedersen commitment?](https://crypto.stackexchange.com/questions/64437/what-is-a-pedersen-commitment) 163 | 164 | - Torben Pryds Pedersen. [Non-Interactive and Information-Theoretic Secure Verifiable Secret Sharing](https://link.springer.com/content/pdf/10.1007%2F3-540-46766-1_9.pdf) 165 | 166 | - Chunming Tang. Dingyi Pei. [Non-Interactive and Information-Theoretic Secure Publicly Verifiable Secret Sharing](https://eprint.iacr.org/2004/201.pdf) 167 | 168 | ## License 169 | Dual-licensed to be compatible with the Rust project. 170 | 171 | Licensed under the Apache License, Version 2.0 [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) or the MIT license [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT), at your option. This file may not be copied, modified, or distributed except according to those terms. -------------------------------------------------------------------------------- /examples/mpvss_all.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 MathxH Chen. 2 | // 3 | // Code is licensed under GPLv3.0 License. 4 | 5 | use mpvss_rs::Participant; 6 | use mpvss_rs::{string_from_secret, string_to_secret}; 7 | 8 | fn main() { 9 | let secret_message = String::from("Hello MPVSS Example."); 10 | let mut dealer = Participant::new(); 11 | dealer.initialize(); 12 | let mut p1 = Participant::new(); 13 | let mut p2 = Participant::new(); 14 | let mut p3 = Participant::new(); 15 | p1.initialize(); 16 | p2.initialize(); 17 | p3.initialize(); 18 | 19 | let distribute_shares_box = dealer.distribute_secret( 20 | &string_to_secret(&secret_message), 21 | &vec![ 22 | p1.publickey.clone(), 23 | p2.publickey.clone(), 24 | p3.publickey.clone(), 25 | ], 26 | 3, 27 | ); 28 | 29 | assert_eq!(p1.verify_distribution_shares(&distribute_shares_box), true); 30 | 31 | assert_eq!(p2.verify_distribution_shares(&distribute_shares_box), true); 32 | 33 | assert_eq!(p3.verify_distribution_shares(&distribute_shares_box), true); 34 | 35 | // p1 extracts the share. [p2 and p3 do this as well.] 36 | let s1 = p1 37 | .extract_secret_share(&distribute_shares_box, &p1.privatekey) 38 | .unwrap(); 39 | 40 | // p1, p2 and p3 exchange their descrypted shares. 41 | // ... 42 | let s2 = p2 43 | .extract_secret_share(&distribute_shares_box, &p2.privatekey) 44 | .unwrap(); 45 | let s3 = p3 46 | .extract_secret_share(&distribute_shares_box, &p3.privatekey) 47 | .unwrap(); 48 | 49 | // p1 verifies the share received from p2. [Actually everybody verifies every received share.] 50 | 51 | assert_eq!( 52 | p1.verify_share(&s2, &distribute_shares_box, &p2.publickey), 53 | true 54 | ); 55 | 56 | assert_eq!( 57 | p2.verify_share(&s3, &distribute_shares_box, &p3.publickey), 58 | true 59 | ); 60 | 61 | assert_eq!( 62 | p3.verify_share(&s1, &distribute_shares_box, &s1.publickey), 63 | true 64 | ); 65 | 66 | let share_boxs = [s1, s2, s3]; 67 | let r1 = p1.reconstruct(&share_boxs, &distribute_shares_box).unwrap(); 68 | let r2 = p2.reconstruct(&share_boxs, &distribute_shares_box).unwrap(); 69 | let r3 = p3.reconstruct(&share_boxs, &distribute_shares_box).unwrap(); 70 | 71 | let r1_str = string_from_secret(&r1); 72 | assert_eq!(secret_message.clone(), r1_str); 73 | let r2_str = string_from_secret(&r2); 74 | assert_eq!(secret_message.clone(), r2_str); 75 | let r3_str = string_from_secret(&r3); 76 | assert_eq!(secret_message.clone(), r3_str); 77 | 78 | println!("secret message: {}", secret_message); 79 | println!("r1 str: {}", r1_str); 80 | println!("r2 str: {}", r2_str); 81 | println!("r3 str: {}", r3_str); 82 | } 83 | -------------------------------------------------------------------------------- /examples/mpvss_sub.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 MathxH Chen. 2 | // 3 | // Code is licensed under GPLv3.0 License. 4 | 5 | use mpvss_rs::Participant; 6 | use mpvss_rs::{string_from_secret, string_to_secret}; 7 | 8 | fn main() { 9 | let secret_message = String::from("Hello Sub MPVSS Example."); 10 | let mut dealer = Participant::new(); 11 | dealer.initialize(); 12 | let mut p1 = Participant::new(); 13 | let mut p2 = Participant::new(); 14 | let mut p3 = Participant::new(); 15 | let mut p4 = Participant::new(); 16 | p1.initialize(); 17 | p2.initialize(); 18 | p3.initialize(); 19 | p4.initialize(); 20 | 21 | let distribute_shares_box = dealer.distribute_secret( 22 | &string_to_secret(&secret_message), 23 | &vec![ 24 | p1.publickey.clone(), 25 | p2.publickey.clone(), 26 | p3.publickey.clone(), 27 | p4.publickey.clone(), 28 | ], 29 | 3, 30 | ); 31 | 32 | assert_eq!(p1.verify_distribution_shares(&distribute_shares_box), true); 33 | 34 | assert_eq!(p2.verify_distribution_shares(&distribute_shares_box), true); 35 | 36 | assert_eq!(p3.verify_distribution_shares(&distribute_shares_box), true); 37 | 38 | assert_eq!(p4.verify_distribution_shares(&distribute_shares_box), true); 39 | 40 | // p1 extracts the share. [p2 and p3 do this as well.] 41 | let s1 = p1 42 | .extract_secret_share(&distribute_shares_box, &p1.privatekey) 43 | .unwrap(); 44 | 45 | // p1, p2, p3 p4 exchange their descrypted shares. 46 | // ... 47 | let s2 = p2 48 | .extract_secret_share(&distribute_shares_box, &p2.privatekey) 49 | .unwrap(); 50 | let s3 = p3 51 | .extract_secret_share(&distribute_shares_box, &p3.privatekey) 52 | .unwrap(); 53 | 54 | let s4 = p4 55 | .extract_secret_share(&distribute_shares_box, &p4.privatekey) 56 | .unwrap(); 57 | 58 | // p1 verifies the share received from p2. [Actually everybody verifies every received share.] 59 | 60 | assert_eq!( 61 | p1.verify_share(&s2, &distribute_shares_box, &p2.publickey), 62 | true 63 | ); 64 | 65 | assert_eq!( 66 | p2.verify_share(&s3, &distribute_shares_box, &p3.publickey), 67 | true 68 | ); 69 | 70 | assert_eq!( 71 | p3.verify_share(&s1, &distribute_shares_box, &s1.publickey), 72 | true 73 | ); 74 | 75 | assert_eq!( 76 | p4.verify_share(&s2, &distribute_shares_box, &s2.publickey), 77 | true 78 | ); 79 | 80 | let share_boxs = [s1.clone(), s2.clone(), s4.clone()]; 81 | let r1 = p1.reconstruct(&share_boxs, &distribute_shares_box).unwrap(); 82 | let r2 = p2.reconstruct(&share_boxs, &distribute_shares_box).unwrap(); 83 | let r3 = p3.reconstruct(&share_boxs, &distribute_shares_box).unwrap(); 84 | let r4 = p4.reconstruct(&share_boxs, &distribute_shares_box).unwrap(); 85 | 86 | let r1_str = string_from_secret(&r1); 87 | assert_eq!(secret_message.clone(), r1_str); 88 | let r2_str = string_from_secret(&r2); 89 | assert_eq!(secret_message.clone(), r2_str); 90 | let r3_str = string_from_secret(&r3); 91 | assert_eq!(secret_message.clone(), r3_str); 92 | let r4_str = string_from_secret(&r4); 93 | assert_eq!(secret_message.clone(), r4_str); 94 | 95 | println!("secret message: {}", secret_message); 96 | println!("r1 str: {}", r1_str); 97 | println!("r2 str: {}", r2_str); 98 | println!("r3 str: {}", r3_str); 99 | println!("r3 str: {}", r4_str); 100 | } 101 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2018" 2 | hard_tabs = false 3 | max_width = 80 4 | tab_spaces = 4 5 | -------------------------------------------------------------------------------- /src/dleq.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 MathxH Chen. 2 | // 3 | // Code is licensed under GPLv3.0 License. 4 | 5 | use num_bigint::{BigInt, BigUint, ToBigInt}; 6 | use num_integer::Integer; 7 | use num_primes::Generator; 8 | use num_traits::identities::{One, Zero}; 9 | use sha2::{Digest, Sha256}; 10 | use std::option::Option; 11 | 12 | struct Prover {} 13 | 14 | impl Prover { 15 | fn send(g: &BigInt, w: &BigInt, q: &BigInt) -> BigInt { 16 | g.modpow(w, q) 17 | } 18 | 19 | fn response( 20 | w: &BigInt, 21 | alpha: &BigInt, 22 | c: &Option, 23 | q: &BigInt, 24 | ) -> Option { 25 | // response r 26 | match c { 27 | None => None, 28 | Some(c) => { 29 | let r: BigInt = 30 | w.to_bigint().unwrap() - (alpha * c).to_bigint().unwrap(); 31 | let result = r.mod_floor( 32 | &(q.clone().to_bigint().unwrap() - BigInt::one()), 33 | ); 34 | Some(result) 35 | } 36 | } 37 | } 38 | } 39 | 40 | struct Verifier {} 41 | impl Verifier { 42 | #[allow(dead_code)] 43 | fn send() -> BigInt { 44 | BigInt::zero() 45 | } 46 | fn check(c: &BigInt, q: &BigInt, challenge_hasher: &Sha256) -> bool { 47 | // Calculate challenge 48 | let challenge_hash = challenge_hasher.clone().finalize(); 49 | let challenge_big_uint = BigUint::from_bytes_be(&challenge_hash[..]) 50 | .mod_floor(&(q.clone().to_biguint().unwrap() - BigUint::one())); 51 | challenge_big_uint == (*c).to_biguint().unwrap() 52 | } 53 | 54 | #[allow(clippy::too_many_arguments)] 55 | fn update( 56 | g1: &BigInt, 57 | h1: &BigInt, 58 | g2: &BigInt, 59 | h2: &BigInt, 60 | response: &BigInt, 61 | c: &BigInt, 62 | q: &BigInt, 63 | challenge_hasher: &mut Sha256, 64 | ) { 65 | // Calc a1 a2 66 | let a1 = (g1.modpow(response, q) * h1.modpow(c, q)) % q; 67 | let a2 = (g2.modpow(response, q) * h2.modpow(c, q)) % q; 68 | // Update hash 69 | challenge_hasher 70 | .update(h1.to_biguint().unwrap().to_str_radix(10).as_bytes()); 71 | challenge_hasher 72 | .update(h2.to_biguint().unwrap().to_str_radix(10).as_bytes()); 73 | challenge_hasher 74 | .update(a1.to_biguint().unwrap().to_str_radix(10).as_bytes()); 75 | challenge_hasher 76 | .update(a2.to_biguint().unwrap().to_str_radix(10).as_bytes()); 77 | } 78 | } 79 | 80 | /// Chaum and Pedersen Scheme 81 | /// 82 | /// To prove that log_g1(h1)= log_g2(h2), for generators g1,h1,g2,h2 ∈ Gq, Gq is group of order q and q is prime 83 | /// 84 | /// We denote this protocol by DLEQ(g1,h1,g2,h2), and it consists of the following steps, where the prover knows α(alpha) such that h1 = g1^α and h2 = g2^α: 85 | /// 86 | /// - The prover sends a1 = g1^w and a2 = g2^w to the verifier, with w ∈ R Zq 87 | /// - The verifier sends a random challenge c ∈ R Zq to the prover. 88 | /// - The prover responds with r = w − αc (mod q). 89 | /// - The verifier checks that a1 = (g1^r) * (h1^c) and a2 = (g2^r) * (h2^c). 90 | #[allow(clippy::upper_case_acronyms)] 91 | #[derive(Debug, Clone, Default)] 92 | pub struct DLEQ { 93 | pub g1: BigInt, 94 | pub h1: BigInt, 95 | pub g2: BigInt, 96 | pub h2: BigInt, 97 | 98 | pub w: BigInt, 99 | pub q: BigInt, 100 | pub alpha: BigInt, 101 | pub c: Option, 102 | pub a1: BigInt, 103 | pub a2: BigInt, 104 | pub r: Option, 105 | } 106 | 107 | impl DLEQ { 108 | /// new DLEQ instance 109 | pub fn new() -> Self { 110 | DLEQ { 111 | g1: BigInt::zero(), 112 | h1: BigInt::zero(), 113 | g2: BigInt::zero(), 114 | h2: BigInt::zero(), 115 | w: BigInt::zero(), 116 | q: BigInt::zero(), 117 | alpha: BigInt::zero(), 118 | 119 | a1: BigInt::zero(), 120 | a2: BigInt::zero(), 121 | c: None, 122 | r: None, 123 | } 124 | } 125 | 126 | #[allow(clippy::too_many_arguments)] 127 | #[allow(dead_code)] 128 | pub fn init( 129 | &mut self, 130 | g1: BigInt, 131 | h1: BigInt, 132 | g2: BigInt, 133 | h2: BigInt, 134 | length: u32, 135 | q: BigInt, 136 | alpha: BigInt, 137 | ) { 138 | let w: BigUint = Generator::new_prime(length as usize) 139 | .mod_floor(&q.to_biguint().unwrap()); 140 | self.init2(g1, h1, g2, h2, q, alpha, w.to_bigint().unwrap()); 141 | } 142 | 143 | #[allow(clippy::too_many_arguments)] 144 | pub fn init2( 145 | &mut self, 146 | g1: BigInt, 147 | h1: BigInt, 148 | g2: BigInt, 149 | h2: BigInt, 150 | q: BigInt, 151 | alpha: BigInt, 152 | w: BigInt, 153 | ) { 154 | self.g1 = g1; 155 | self.h1 = h1; 156 | self.g2 = g2; 157 | self.h2 = h2; 158 | self.w = w; 159 | self.q = q; 160 | self.alpha = alpha; 161 | } 162 | 163 | /// get a1 value 164 | pub fn get_a1(&self) -> BigInt { 165 | Prover::send(&self.g1, &self.w, &self.q) 166 | } 167 | 168 | /// get a2 value 169 | pub fn get_a2(&self) -> BigInt { 170 | Prover::send(&self.g2, &self.w, &self.q) 171 | } 172 | 173 | /// get response r value 174 | pub fn get_r(&self) -> Option { 175 | Prover::response(&self.w, &self.alpha, &self.c, &self.q) 176 | } 177 | 178 | #[allow(dead_code)] 179 | /// send a random challenge c 180 | pub fn get_c(&self) -> BigInt { 181 | Verifier::send() 182 | } 183 | 184 | /// Update challenge hash 185 | pub fn update_hash(&self, challenge_hasher: &mut Sha256) { 186 | Verifier::update( 187 | &self.g1, 188 | &self.h1, 189 | &self.g2, 190 | &self.h2, 191 | &self.r.clone().unwrap(), 192 | &self.c.clone().unwrap(), 193 | &self.q, 194 | challenge_hasher, 195 | ) 196 | } 197 | /// check and verify 198 | pub fn check(&self, challenge_hasher: &Sha256) -> bool { 199 | Verifier::check(&self.c.clone().unwrap(), &self.q, challenge_hasher) 200 | } 201 | } 202 | 203 | #[cfg(test)] 204 | mod tests { 205 | use super::DLEQ; 206 | use num_bigint::BigInt; 207 | #[test] 208 | fn test_dleq() { 209 | let g1 = BigInt::from(8443); 210 | let h1 = BigInt::from(531216); 211 | let g2 = BigInt::from(1299721); 212 | let h2 = BigInt::from(14767239); 213 | let w = BigInt::from(81647); 214 | let q = BigInt::from(15487469); 215 | let alpha = BigInt::from(163027); 216 | let length = 64_i64; 217 | 218 | drop(length); 219 | 220 | let mut dleq = DLEQ::new(); 221 | dleq.init2(g1, h1, g2, h2, q.clone(), alpha, w); 222 | 223 | let a1 = BigInt::from(14735247); 224 | let a2 = BigInt::from(5290058); 225 | assert_eq!(dleq.get_a1(), a1); 226 | assert_eq!(dleq.get_a2(), a2); 227 | 228 | let c = BigInt::from(127997); 229 | dleq.c = Some(c); 230 | let r = BigInt::from(10221592); 231 | assert_eq!(r, dleq.get_r().unwrap()); 232 | assert_eq!( 233 | a1, 234 | (dleq.g1.modpow(&dleq.get_r().unwrap(), &dleq.q) 235 | * dleq.h1.modpow(&dleq.c.clone().unwrap(), &dleq.q)) 236 | % q.clone() 237 | ); 238 | assert_eq!( 239 | a2, 240 | (dleq.g2.modpow(&dleq.get_r().unwrap(), &dleq.q) 241 | * dleq.h2.modpow(&dleq.c.clone().unwrap(), &dleq.q)) 242 | % q.clone() 243 | ) 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 MathxH Chen. 2 | // 3 | // Code is licensed under GPLv3.0 License. 4 | 5 | //! # MPVSS - A Simple Publicly Verifiable Secret Sharing Library 6 | //! 7 | //! The library implements a simple PVSS scheme in Rust. 8 | //! 9 | //! ## What is PVSS? 10 | //! 11 | //! Secret sharing means a dealer can break a secret into secret shares among a group of participants which can reconstruct the secret only by collaboratively joining their parts of the secret. The library also implements threshold cryptography so that the dealer can decide whether all of the receiving participants need to collaborate or if a smaller subgroup of participants is sufficient to reconstruct the secret. 12 | //! 13 | //! In addition to the plain secret sharing scheme PVSS adds verifiability in the following way: All the parts the secret is split into are encrypted with the receivers' public keys respectively. The dealer publishes all the encrypted shares along with a non-interactive zero-knowledge proof that allows everbody (not only the receiving participants) to verify that the decrypted shares indeed can be used to reconstruct the secret. The participants then decrypt all their shares and exchange them along with another non-interactive zero-knowledge proof that allows the receiving participant to verify that the share is actually the result of the decryption. 14 | //! 15 | //! Thus PVSS can be used to share a secret among a group of participants so that either the secret can be reconstructed by the participants who all play fair or a participant that received a faked share can identify the malicious party. 16 | //! 17 | //! ## Documents 18 | //! 19 | //! See [Github README](https://github.com/AlexiaChen/mpvss-rs/blob/master/README.md) 20 | 21 | mod dleq; 22 | mod mpvss; 23 | mod participant; 24 | mod polynomial; 25 | mod sharebox; 26 | mod util; 27 | 28 | pub use participant::Participant; 29 | pub use sharebox::{DistributionSharesBox, ShareBox}; 30 | 31 | use num_bigint::{BigInt, BigUint, ToBigInt}; 32 | 33 | pub fn string_to_secret(message: &str) -> BigInt { 34 | BigUint::from_bytes_be(message.as_bytes()) 35 | .to_bigint() 36 | .unwrap() 37 | } 38 | 39 | pub fn string_from_secret(secret: &BigInt) -> String { 40 | String::from_utf8(secret.to_biguint().unwrap().to_bytes_be()).unwrap() 41 | } 42 | -------------------------------------------------------------------------------- /src/mpvss.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 MathxH Chen. 2 | // 3 | // Code is licensed under GPLv3.0 License. 4 | 5 | #![allow(non_snake_case)] 6 | #![allow(dead_code)] 7 | 8 | use crate::dleq::DLEQ; 9 | use crate::sharebox::{DistributionSharesBox, ShareBox}; 10 | use crate::util::Util; 11 | use num_bigint::{BigInt, BigUint, RandBigInt, ToBigInt}; 12 | use num_integer::Integer; 13 | use num_primes::Generator; 14 | use num_traits::identities::{One, Zero}; 15 | use rayon::prelude::*; 16 | use sha2::{Digest, Sha256}; 17 | use std::clone::Clone; 18 | use std::collections::BTreeMap; 19 | 20 | /// 2048-bit MODP Group 21 | /// New Modular Exponential (MODP) Diffie-Hellman groups 22 | /// 23 | /// This group is assigned id 14. 24 | /// 25 | /// This prime is: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } 26 | /// 27 | /// Its hexadecimal value is: 28 | /// 29 | /// FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 30 | /// 29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD 31 | /// EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245 32 | /// E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED 33 | /// EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D 34 | /// C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F 35 | /// 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D 36 | /// 670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B 37 | /// E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 38 | /// DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510 39 | /// 15728E5A 8AACAA68 FFFFFFFF FFFFFFFF 40 | /// 41 | /// The generator is: 2. 42 | 43 | #[allow(clippy::upper_case_acronyms)] 44 | #[derive(Debug, Clone, Default)] 45 | pub struct MPVSS { 46 | pub q: BigInt, 47 | pub g: BigInt, 48 | pub G: BigInt, 49 | 50 | pub length: u32, 51 | } 52 | 53 | impl MPVSS { 54 | /// `q` is a safe prime of length 2048 bit RFC3526 https://tools.ietf.org/html/rfc3526. 55 | /// `2` and the corresponding sophie germain prime are generators. 56 | /// sophie germain prime is p if 2*p + 1 is also prime, let 2*p + 1 = q 57 | pub fn new() -> Self { 58 | let q: BigUint = BigUint::parse_bytes(b"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff", 16).unwrap(); 59 | let g: BigUint = (q.clone() - BigUint::one()) / BigUint::from(2_u64); 60 | MPVSS { 61 | q: q.to_bigint().unwrap(), 62 | g: g.to_bigint().unwrap(), 63 | G: BigInt::from(2_i64), 64 | length: 2048, 65 | } 66 | } 67 | 68 | /// Initializes a MPVSS by generating a safe prime of `length` bit length. 69 | /// 70 | /// - Parameter length: Number of bits used for choosing numbers and doing calculations. 71 | pub fn init(length: u32) -> Self { 72 | let q: BigUint = Generator::safe_prime(length as usize); 73 | let g: BigUint = (q.clone() - BigUint::one()) / BigUint::from(2_u64); 74 | MPVSS { 75 | q: q.to_bigint().unwrap(), 76 | g: g.to_bigint().unwrap(), 77 | G: BigInt::from(2_i64), 78 | length, 79 | } 80 | } 81 | 82 | pub fn generate_private_key(&self) -> BigInt { 83 | let mut rng = rand::thread_rng(); 84 | let mut privkey: BigUint = 85 | rng.gen_biguint_below(&self.q.to_biguint().unwrap()); 86 | // We need the private key and q-1 to be coprime so that we can calculate 1/key mod (q-1) during secret reconstruction. 87 | while privkey.gcd(&(self.q.to_biguint().unwrap() - BigUint::one())) 88 | != BigUint::one() 89 | { 90 | privkey = rng.gen_biguint_below(&self.q.to_biguint().unwrap()); 91 | } 92 | privkey.to_bigint().unwrap() 93 | } 94 | 95 | /// generate public key from private key 96 | /// P = G^k over the Group of the order q 97 | pub fn generate_public_key(&self, privkey: &BigInt) -> BigInt { 98 | // publicKey = G^privKey mod q 99 | self.G.modpow(privkey, &self.q) 100 | } 101 | 102 | /// Verifies if the share in the distribution share box was decrypted correctly by the respective participant. 103 | /// 104 | /// - Parameters: 105 | /// - shareBox: The share box containing the share to be verified. 106 | /// - distributionShareBox: The distribution share box that contains the share. 107 | /// - publicKey: The public key of the sender of the share bundle. 108 | /// - Returns: Returns `true` if the share in the distribution share box matches the decryption of the encrypted share and `false` otherwise. 109 | pub fn verify_share( 110 | &self, 111 | sharebox: &ShareBox, 112 | distribution_sharebox: &DistributionSharesBox, 113 | publickey: &BigInt, 114 | ) -> bool { 115 | let encrypted_share = distribution_sharebox.shares.get(publickey); 116 | if encrypted_share.is_none() { 117 | return false; 118 | } 119 | self.verify(sharebox, encrypted_share.unwrap()) 120 | } 121 | 122 | fn verify(&self, sharebox: &ShareBox, encrypted_share: &BigInt) -> bool { 123 | // Verification of the share. 124 | // Using publickey,encrypted_hsare,decrypted_share,response and c as input, the verifier computes a_1i,a_2i as: 125 | // a_1i = G^r * publickey^c, a_2i = decrypted_shar^r * encrypted_share^c 126 | // and checks that the hash of publickey,encrypted_hsare,decrypted_share,response matches c. 127 | let mut dleq = DLEQ::new(); 128 | let mut challenge_haser = Sha256::new(); 129 | dleq.g1 = self.G.clone(); 130 | dleq.h1 = sharebox.publickey.clone(); 131 | dleq.g2 = sharebox.share.clone(); 132 | dleq.h2 = encrypted_share.clone(); 133 | dleq.r = Some(sharebox.response.clone()); 134 | dleq.c = Some(sharebox.challenge.clone()); 135 | dleq.q = self.q.clone(); 136 | dleq.update_hash(&mut challenge_haser); 137 | dleq.check(&challenge_haser) 138 | } 139 | 140 | /// Verifies that the shares the distribution shares box consists are consistent so that they can be used to reconstruct the secret later. 141 | /// 142 | /// - Parameter distribute_sharesbox: The distribution shares box whose consistency is to be verified. 143 | /// - Returns: Returns `true` if the shares are correct and `false` otherwise. 144 | pub fn verify_distribution_shares( 145 | &self, 146 | distribute_sharesbox: &DistributionSharesBox, 147 | ) -> bool { 148 | // Verification of the shares. 149 | // The verifier computes X_i = ∏(j = 0 -> t - 1): (C_j)^(i^j) from the C_j values. 150 | // Using y_i,X_i,Y_i,r_i, 1 ≤ i ≤ n and c as input, the verifier computes a_1i,a_2i as: 151 | // a_1i = g^(r_i) * X_i^c, a_2i = y_i^(r_i) * Y_i^c 152 | // and checks that the hash of X_i,Y_i, a_1i, a_2i, 1 ≤ i ≤ n, matches c. 153 | let mut dleq = DLEQ::new(); 154 | let mut challenge_hasher = Sha256::new(); 155 | for publickey in &distribute_sharesbox.publickeys { 156 | let position = distribute_sharesbox.positions.get(publickey); 157 | let response = distribute_sharesbox.responses.get(publickey); 158 | let encrypted_share = distribute_sharesbox.shares.get(publickey); 159 | if position.is_none() 160 | || response.is_none() 161 | || encrypted_share.is_none() 162 | { 163 | return false; 164 | } 165 | 166 | // Calculate X_i 167 | let mut x: BigInt = BigInt::one(); 168 | let mut exponent: BigInt = BigInt::one(); 169 | for j in 0..distribute_sharesbox.commitments.len() { 170 | x = (x * distribute_sharesbox.commitments[j] 171 | .modpow(&exponent, &self.q)) 172 | % &self.q; 173 | exponent = (exponent * BigInt::from(*position.unwrap() as i64)) 174 | % &(self.q.clone() - BigInt::one()); 175 | } 176 | 177 | // Calculate a_1i, a_2i and update hash 178 | dleq.g1 = self.g.clone(); 179 | dleq.h1 = x; 180 | dleq.g2 = publickey.clone(); 181 | dleq.h2 = encrypted_share.unwrap().clone(); 182 | dleq.r = Some(response.unwrap().clone()); 183 | dleq.c = Some(distribute_sharesbox.challenge.clone()); 184 | dleq.q = self.q.clone(); 185 | dleq.update_hash(&mut challenge_hasher); 186 | } // end for participant's public keys 187 | 188 | // Calculate challenge and check if it is match c 189 | dleq.check(&challenge_hasher) 190 | } 191 | 192 | fn compute_factor( 193 | &self, 194 | position: i64, 195 | share: &BigInt, 196 | values: &[i64], 197 | ) -> BigInt { 198 | let mut exponent = BigInt::one(); 199 | let lagrangeCoefficient = Util::lagrange_coefficient(&position, values); 200 | if &lagrangeCoefficient.0 % &lagrangeCoefficient.1 == BigInt::zero() { 201 | // Lagrange coefficient is an integer 202 | exponent = 203 | &lagrangeCoefficient.0 / Util::abs(&lagrangeCoefficient.1); 204 | } else { 205 | // Lagrange coefficient is a proper fraction 206 | // Cancel fraction if possible 207 | let mut numerator = lagrangeCoefficient.0.to_biguint().unwrap(); 208 | let mut denominator = 209 | Util::abs(&lagrangeCoefficient.1).to_biguint().unwrap(); 210 | let gcd = numerator.gcd(&denominator); 211 | numerator /= &gcd; 212 | denominator /= &gcd; 213 | 214 | let q1 = &self.q - BigInt::one(); 215 | let inverseDenominator = Util::mod_inverse( 216 | &denominator.to_bigint().unwrap(), 217 | &q1.to_bigint().unwrap(), 218 | ); 219 | if let Some(inverseDenom) = inverseDenominator { 220 | exponent = (numerator.to_bigint().unwrap() * inverseDenom) 221 | % q1.to_bigint().unwrap(); 222 | } else { 223 | eprintln!("ERROR: Denominator of Lagrange coefficient fraction does not have an inverse. Share cannot be processed."); 224 | } 225 | } 226 | let mut factor = share 227 | .to_bigint() 228 | .unwrap() 229 | .modpow(&exponent, &self.q.to_bigint().unwrap()); 230 | if lagrangeCoefficient.0 * lagrangeCoefficient.1 < BigInt::zero() { 231 | // Lagrange coefficient was negative. S^(-lambda) = 1/(S^lambda) 232 | let inverseFactor = 233 | Util::mod_inverse(&factor, &self.q.to_bigint().unwrap()); 234 | if let Some(inversefactor) = inverseFactor { 235 | factor = inversefactor; 236 | } else { 237 | eprintln!("ERROR: Lagrange coefficient was negative and does not have an inverse. Share cannot be processed.") 238 | } 239 | } 240 | 241 | factor 242 | } 243 | 244 | /// Reconstruct secret from share boxs 245 | pub fn reconstruct( 246 | &self, 247 | share_boxs: &[ShareBox], 248 | distribute_share_box: &DistributionSharesBox, 249 | ) -> Option { 250 | if share_boxs.len() < distribute_share_box.commitments.len() { 251 | return None; 252 | } 253 | let mut shares: BTreeMap = BTreeMap::new(); 254 | for share_box in share_boxs.iter() { 255 | let position = 256 | distribute_share_box.positions.get(&share_box.publickey); 257 | position?; 258 | shares.insert(*position.unwrap(), share_box.share.clone()); 259 | } 260 | // Pooling the shares. Suppose 261 | // w.l.o.g. that participantsPiproduce correctvalues for S_i, for i= 1,...,t. 262 | // The secret G^s is obtained by Lagrange interpolation: 263 | // ∏(i=1->t)(S^λ_i) = ∏(i=1->t)(G^p(i))^λ_i = G^(∑(i=1->t)p(i)*λ_i = G^p(0) = G^s, 264 | let mut secret: BigInt = BigInt::one(); 265 | let values: Vec = shares.keys().copied().collect(); 266 | let shares_vec: Vec<(i64, BigInt)> = shares 267 | .into_iter() 268 | .map(|(postion, share)| (postion, share)) 269 | .collect(); 270 | let shares_slice = shares_vec.as_slice(); 271 | let factors: Vec = shares_slice 272 | .par_iter() 273 | .map(|(position, share)| { 274 | self.compute_factor(*position, share, values.as_slice()) 275 | }) 276 | .collect(); 277 | 278 | secret = factors 279 | .into_iter() 280 | .fold(secret, |acc, factor| (acc * factor) % &self.q); 281 | 282 | // Reconstruct the secret = H(G^s) xor U 283 | let secret_hash = sha2::Sha256::digest( 284 | secret.to_biguint().unwrap().to_str_radix(10).as_bytes(), 285 | ); 286 | let hash_big_uint = BigUint::from_bytes_be(&secret_hash[..]) 287 | .mod_floor(&self.q.to_biguint().unwrap()); 288 | let decrypted_secret = 289 | hash_big_uint ^ distribute_share_box.U.to_biguint().unwrap(); 290 | Some(decrypted_secret.to_bigint().unwrap()) 291 | } 292 | } 293 | 294 | #[cfg(test)] 295 | mod tests { 296 | use super::MPVSS; 297 | use num_bigint::{BigInt, BigUint, ToBigInt}; 298 | use num_integer::Integer; 299 | use num_primes::Verification; 300 | use num_traits::One; 301 | #[test] 302 | fn test_new() { 303 | let mpvss = MPVSS::new(); 304 | assert!(Verification::is_safe_prime(&mpvss.q.to_biguint().unwrap())); 305 | assert!(Verification::is_prime(&mpvss.g.to_biguint().unwrap())); 306 | assert!(!Verification::is_safe_prime(&mpvss.g.to_biguint().unwrap())); 307 | } 308 | 309 | #[test] 310 | fn test_init() { 311 | let mpvss = MPVSS::init(64); 312 | assert!(Verification::is_safe_prime(&mpvss.q.to_biguint().unwrap())); 313 | assert!(Verification::is_prime(&mpvss.g.to_biguint().unwrap())); 314 | assert!(!Verification::is_safe_prime(&mpvss.g.to_biguint().unwrap())); 315 | 316 | let mpvss = MPVSS::init(32); 317 | assert!(Verification::is_prime(&mpvss.q.to_biguint().unwrap())); 318 | assert!(Verification::is_prime(&mpvss.g.to_biguint().unwrap())); 319 | assert_eq!( 320 | mpvss.g, 321 | ((mpvss.q - BigInt::one()).to_biguint().unwrap() 322 | / BigUint::from(2_u32)) 323 | .to_bigint() 324 | .unwrap() 325 | ) 326 | } 327 | 328 | #[test] 329 | fn test_gen_priv_key() { 330 | let mut mpvss = MPVSS::new(); 331 | mpvss.q = BigInt::from(49999_i32); 332 | assert!(Verification::is_prime(&mpvss.q.to_biguint().unwrap())); 333 | let priv_key = mpvss.generate_private_key(); 334 | assert_eq!( 335 | priv_key.gcd(&(mpvss.q.clone() - BigInt::one())), 336 | BigInt::one() 337 | ); 338 | } 339 | 340 | #[test] 341 | fn test_gen_public_key() { 342 | use super::BigInt; 343 | use super::MPVSS; 344 | let q: BigInt = BigInt::from(179426549); 345 | let g: BigInt = BigInt::from(1301081); 346 | let G: BigInt = BigInt::from(15486487); 347 | 348 | let length = 64_i64; 349 | drop(length); 350 | 351 | let mut mpvss = MPVSS::new(); 352 | mpvss.q = q; 353 | mpvss.g = g; 354 | mpvss.G = G; 355 | 356 | let privatekey = BigInt::from(105929); 357 | let publickey = mpvss.generate_public_key(&privatekey); 358 | assert_eq!(publickey, BigInt::from(148446388)); 359 | } 360 | } 361 | -------------------------------------------------------------------------------- /src/participant.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 MathxH Chen. 2 | // 3 | // Code is licensed under GPLv3.0 License. 4 | 5 | #![allow(non_snake_case)] 6 | 7 | use crate::dleq::DLEQ; 8 | use crate::mpvss::MPVSS; 9 | use crate::polynomial::Polynomial; 10 | use crate::sharebox::{DistributionSharesBox, ShareBox}; 11 | use crate::util::Util; 12 | use num_bigint::{BigInt, BigUint, RandBigInt, ToBigInt}; 13 | use num_integer::Integer; 14 | use num_primes::Generator; 15 | use num_traits::identities::{One, Zero}; 16 | use sha2::{Digest, Sha256}; 17 | use std::clone::Clone; 18 | use std::collections::BTreeMap; 19 | use std::option::Option; 20 | 21 | /// A participant represents one party in the secret sharing scheme. The participant can share a secret among a group of other participants and it is then called the "dealer". 22 | /// The receiving participants that receive a part of the secret can use it to reconstruct the secret Therefore the partticipants need to collaborate and exchange their parts. 23 | /// A participant represents as a Node in the Distributed Public NetWork 24 | #[derive(Debug, Clone, Default)] 25 | pub struct Participant { 26 | mpvss: MPVSS, 27 | pub privatekey: BigInt, 28 | pub publickey: BigInt, 29 | } 30 | 31 | impl Participant { 32 | /// Create A default participant 33 | /// 34 | /// ## Example 35 | /// 36 | /// ```rust 37 | /// use mpvss_rs::Participant; 38 | /// let mut dealer = Participant::new(); 39 | /// ``` 40 | pub fn new() -> Self { 41 | Participant { 42 | mpvss: MPVSS::new(), 43 | privatekey: BigInt::zero(), 44 | publickey: BigInt::zero(), 45 | } 46 | } 47 | /// Initializes a new participant with the default MPVSS. 48 | /// 49 | /// ## Example 50 | /// 51 | /// ```rust 52 | /// use mpvss_rs::Participant; 53 | /// let mut dealer = Participant::new(); 54 | /// dealer.initialize(); 55 | /// ``` 56 | pub fn initialize(&mut self) { 57 | self.privatekey = self.mpvss.generate_private_key(); 58 | self.publickey = self.mpvss.generate_public_key(&self.privatekey); 59 | } 60 | 61 | fn distribute( 62 | &mut self, 63 | secret: &BigInt, 64 | publickeys: &[BigInt], 65 | threshold: u32, 66 | polynomial: &Polynomial, 67 | w: &BigInt, 68 | ) -> DistributionSharesBox { 69 | assert!(threshold <= publickeys.len() as u32); 70 | // Data the distribution shares box is going to be consisting of 71 | let mut commitments: Vec = Vec::new(); 72 | let mut positions: BTreeMap = BTreeMap::new(); 73 | let mut X: BTreeMap = BTreeMap::new(); 74 | let mut shares: BTreeMap = BTreeMap::new(); 75 | let mut challenge_hasher = Sha256::new(); 76 | 77 | // Temp variable 78 | let mut sampling_points: BTreeMap = BTreeMap::new(); 79 | let mut a: BTreeMap = BTreeMap::new(); 80 | let mut dleq_w: BTreeMap = BTreeMap::new(); 81 | let mut position: i64 = 1; 82 | 83 | // Calculate Ploynomial Coefficients Commitments C_j = g^(a_j) under group of prime q, and 0 <= j < threshold 84 | for j in 0..threshold { 85 | commitments.push( 86 | self.mpvss.g.modpow( 87 | &polynomial.coefficients[j as usize], 88 | &self.mpvss.q, 89 | ), 90 | ) 91 | } 92 | 93 | // Calculate Every Encrypted shares with every participant's public key generated from their own private key 94 | // Y_i = (y_i)^p(i) X_i = g^p(i) = C_0^(i^0) * C_1^(i^1) * C_2^(i^2) * ... * C_j^(i^j) and 1 <= i <= n 0 <= j <= threshhold - 1 95 | // n is participant current total number 96 | // p(i) is secret share without encrypt on the ploynomial of the degree t - 1 97 | // y_i is participant public key 98 | // Y_i is encrypted secret share 99 | for pubkey in publickeys { 100 | positions.insert(pubkey.clone(), position); 101 | // calc P(position) % (q - 1), from P(1) to P(n), actually is from share 1 to share n 102 | let secret_share = polynomial.get_value(&BigInt::from(position)) 103 | % (&self.mpvss.q - BigInt::one()); 104 | sampling_points.insert(pubkey.clone(), secret_share.clone()); 105 | 106 | // Calc X_i 107 | let mut x: BigInt = BigInt::one(); 108 | let mut exponent: BigInt = BigInt::one(); 109 | for j in 0..=threshold - 1 { 110 | x = (x * commitments[j as usize] 111 | .modpow(&exponent, &self.mpvss.q)) 112 | % &self.mpvss.q; 113 | exponent = (exponent * BigInt::from(position)) 114 | % (&self.mpvss.q - BigInt::one()); 115 | } 116 | 117 | X.insert(pubkey.clone(), x.clone()); 118 | 119 | // Calc Y_i 120 | let encrypted_secret_share = 121 | pubkey.modpow(&secret_share, &self.mpvss.q); 122 | shares.insert(pubkey.clone(), encrypted_secret_share.clone()); 123 | 124 | // DLEQ(g1,h1,g2,h2) => DLEQ(g,X_i,y_i,Y_i) => DLEQ(g,commintment_with_secret_share,pubkey,enrypted_secret_share_from_pubkey) 125 | // Prove That g^alpha = commintment_with_secret_share and pubkey^alpha = encrypted_secret_share_from_pubkey has same alpha value 126 | let mut dleq = DLEQ::new(); 127 | dleq.init2( 128 | self.mpvss.g.clone(), 129 | x.clone(), 130 | pubkey.clone(), 131 | encrypted_secret_share.clone(), 132 | self.mpvss.q.clone(), 133 | secret_share.clone(), 134 | w.clone(), 135 | ); 136 | 137 | dleq_w.insert(pubkey.clone(), dleq.w.clone()); 138 | // Calc a_1i, a_2i 139 | a.insert(pubkey.clone(), (dleq.get_a1(), dleq.get_a2())); 140 | 141 | // Update challenge hash 142 | // the challenge c for the protocol is computed as a cryptographic hash of X_i,Y_i,a_1i,a_2i, 1 <= i <= n 143 | challenge_hasher 144 | .update(x.to_biguint().unwrap().to_str_radix(10).as_bytes()); 145 | challenge_hasher.update( 146 | encrypted_secret_share 147 | .to_biguint() 148 | .unwrap() 149 | .to_str_radix(10) 150 | .as_bytes(), 151 | ); 152 | challenge_hasher.update( 153 | dleq.get_a1() 154 | .to_biguint() 155 | .unwrap() 156 | .to_str_radix(10) 157 | .as_bytes(), 158 | ); 159 | challenge_hasher.update( 160 | dleq.get_a2() 161 | .to_biguint() 162 | .unwrap() 163 | .to_str_radix(10) 164 | .as_bytes(), 165 | ); 166 | position += 1; 167 | } // end for participant's publickeys 168 | 169 | // the common challenge c 170 | let challenge_hash = challenge_hasher.finalize(); 171 | let challenge_big_uint = BigUint::from_bytes_be(&challenge_hash[..]) 172 | .mod_floor(&(self.mpvss.q.to_biguint().unwrap() - BigUint::one())); 173 | 174 | // Calc response r_i 175 | let mut responses: BTreeMap = BTreeMap::new(); 176 | for pubkey in publickeys { 177 | // DLEQ(g1,h2,g2,h2) => DLEQ(g,X_i,y_i,Y_i) => DLEQ(g,commintment_with_secret_share,pubkey,encrypted_secret_share_from_pubkey) 178 | let x_i = X.get(pubkey).unwrap(); 179 | let encrypted_secret_share = shares.get(pubkey).unwrap(); 180 | let secret_share = sampling_points.get(pubkey).unwrap(); 181 | let w = dleq_w.get(pubkey).unwrap(); 182 | let mut dleq = DLEQ::new(); 183 | dleq.init2( 184 | self.mpvss.g.clone(), 185 | x_i.clone(), 186 | pubkey.clone(), 187 | encrypted_secret_share.clone(), 188 | self.mpvss.q.clone(), 189 | secret_share.clone(), 190 | w.clone(), 191 | ); 192 | dleq.c = Some(challenge_big_uint.to_bigint().unwrap()); 193 | let response = dleq.get_r().unwrap(); 194 | responses.insert(pubkey.clone(), response); 195 | } // end for pubkeys Calc r_i 196 | 197 | // Calc U = secret xor SHA256(G^s) = secret xor SHA256(G^p(0)). 198 | // [Section 4] 199 | // σ ∈ Σ, where 2 ≤ |Σ| ≤ q. 200 | // the general procedure is to let the dealer first run the distribution protocol for a random value s ∈ Zq, and then publish U = σ ⊕ H(G^s), 201 | // where H is an appropriate cryptographic hash function. The reconstruction protocol will yield G^s, from which we obtain σ = U ⊕ H(G^s). 202 | let shared_value = self.mpvss.G.modpow( 203 | &polynomial.get_value(&BigInt::zero()).mod_floor( 204 | &(self.mpvss.q.to_bigint().unwrap() - BigInt::one()), 205 | ), 206 | &self.mpvss.q, 207 | ); 208 | let sha256_hash = sha2::Sha256::digest( 209 | shared_value 210 | .to_biguint() 211 | .unwrap() 212 | .to_str_radix(10) 213 | .as_bytes(), 214 | ); 215 | let hash_big_uint = BigUint::from_bytes_be(&sha256_hash[..]) 216 | .mod_floor(&self.mpvss.q.to_biguint().unwrap()); 217 | let U = secret.to_biguint().unwrap() ^ hash_big_uint; 218 | 219 | // The proof consists of the common challenge c and the n responses r_i. 220 | let mut shares_box = DistributionSharesBox::new(); 221 | shares_box.init( 222 | &commitments, 223 | positions, 224 | shares, 225 | publickeys, 226 | &challenge_big_uint.to_bigint().unwrap(), 227 | responses, 228 | &U.to_bigint().unwrap(), 229 | ); 230 | shares_box 231 | } 232 | 233 | /// Takes a secret as input and returns the distribution shares Box which is going to be submitted to all the participants the secret is going to be shared with. 234 | /// Those participants are specified by their public keys. They use the distribution shares box to verify that the shares are correct (without learning anything about the shares that are not supposed to be decrypted by them) 235 | /// and extract their encrypted shares. In fact, the distribution shares box can be published to everyone allowing even external parties to verify the integrity of the shares. 236 | /// 237 | /// - Parameters: 238 | /// - secret: The value that is going to be shared among the other participants. 239 | /// - publicKeys: Array of public keys of each participant the secret is to be shared with. 240 | /// - threshold: The number of shares that is needed in order to reconstruct the secret. It must not be greater than the total number of participants. 241 | /// - Requires: `threshold` <= number of participants 242 | /// - Returns: The distribution shares Box that is published to everyone (especially but not only the participants) can check the shares' integrity. Furthermore the participants extract their shares from it. 243 | /// 244 | /// ## Example 245 | /// 246 | /// ```rust 247 | /// use mpvss_rs::Participant; 248 | /// use num_bigint::{BigUint, ToBigInt}; 249 | /// 250 | /// let secret_message = String::from("Hello MPVSS Example."); 251 | /// let secret = BigUint::from_bytes_be(&secret_message.as_bytes()); 252 | /// let mut dealer = Participant::new(); 253 | /// dealer.initialize(); 254 | /// let mut p1 = Participant::new(); 255 | /// let mut p2 = Participant::new(); 256 | /// let mut p3 = Participant::new(); 257 | /// p1.initialize(); 258 | /// p2.initialize(); 259 | /// p3.initialize(); 260 | /// 261 | /// let distribute_shares_box = dealer.distribute_secret( 262 | /// &secret.to_bigint().unwrap(), 263 | /// &vec![ 264 | /// p1.publickey.clone(), 265 | /// p2.publickey.clone(), 266 | /// p3.publickey.clone(), 267 | /// ], 268 | /// 3, 269 | /// ); 270 | /// ``` 271 | pub fn distribute_secret( 272 | &mut self, 273 | secret: &BigInt, 274 | publickeys: &[BigInt], 275 | threshold: u32, 276 | ) -> DistributionSharesBox { 277 | let mut polynomial = Polynomial::new(); 278 | polynomial 279 | .init((threshold - 1) as i32, &self.mpvss.q.to_bigint().unwrap()); 280 | 281 | let mut rng = rand::thread_rng(); 282 | let w: BigUint = 283 | rng.gen_biguint_below(&self.mpvss.q.to_biguint().unwrap()); 284 | self.distribute( 285 | secret, 286 | publickeys, 287 | threshold, 288 | &polynomial, 289 | &w.to_bigint().unwrap(), 290 | ) 291 | } 292 | 293 | fn extract_share( 294 | &self, 295 | shares_box: &DistributionSharesBox, 296 | private_key: &BigInt, 297 | w: &BigInt, 298 | ) -> Option { 299 | let public_key = self.mpvss.generate_public_key(private_key); 300 | let encrypted_secret_share = 301 | shares_box.shares.get(&public_key).unwrap(); 302 | 303 | // Decryption of the shares. 304 | // Using its private key x_i, each participant finds the decrypted share S_i = G^p(i) from Y_i by computing S_i = Y_i^(1/x_i). 305 | // Y_i is encrypted share: Y_i = y_i^p(i) 306 | // find modular multiplicative inverses of private key 307 | let privkey_inverse = 308 | Util::mod_inverse(private_key, &(&self.mpvss.q - BigInt::one())) 309 | .unwrap(); 310 | let decrypted_share = 311 | encrypted_secret_share.modpow(&privkey_inverse, &self.mpvss.q); 312 | 313 | // To this end it suffices to prove knowledge of an α such that y_i= G^α and Y_i= S_i^α, which is accomplished by the non-interactive version of the protocol DLEQ(G,y_i,S_i,Y_i). 314 | // DLEQ(G,y_i,S_i,Y_i) => DLEQ(G, publickey, decrypted_share, encryted_share) 315 | // All of this is to prove and tell participants that the decrypted share is must use your own public key encrypted, 316 | // and only you can decrypt the share with your own private key and verify the share's proof 317 | let mut dleq = DLEQ::new(); 318 | dleq.init2( 319 | self.mpvss.G.clone(), 320 | public_key.clone(), 321 | decrypted_share.clone(), 322 | encrypted_secret_share.clone(), 323 | self.mpvss.q.clone(), 324 | private_key.clone(), 325 | w.clone(), 326 | ); 327 | 328 | let mut challenge_hasher = Sha256::new(); 329 | challenge_hasher.update( 330 | public_key.to_biguint().unwrap().to_str_radix(10).as_bytes(), 331 | ); 332 | challenge_hasher.update( 333 | encrypted_secret_share 334 | .to_biguint() 335 | .unwrap() 336 | .to_str_radix(10) 337 | .as_bytes(), 338 | ); 339 | challenge_hasher.update( 340 | dleq.get_a1() 341 | .to_biguint() 342 | .unwrap() 343 | .to_str_radix(10) 344 | .as_bytes(), 345 | ); 346 | challenge_hasher.update( 347 | dleq.get_a2() 348 | .to_biguint() 349 | .unwrap() 350 | .to_str_radix(10) 351 | .as_bytes(), 352 | ); 353 | 354 | // the challenge c 355 | let challenge_hash = challenge_hasher.finalize(); 356 | let challenge_big_uint = BigUint::from_bytes_be(&challenge_hash[..]) 357 | .mod_floor(&(self.mpvss.q.to_biguint().unwrap() - BigUint::one())); 358 | dleq.c = Some(challenge_big_uint.to_bigint().unwrap()); 359 | 360 | let mut share_box = ShareBox::new(); 361 | share_box.init( 362 | public_key, 363 | decrypted_share, 364 | challenge_big_uint.to_bigint().unwrap(), 365 | dleq.get_r().unwrap(), 366 | ); 367 | Some(share_box) 368 | } 369 | 370 | /// Extracts the share from a given distribution shares box that is addressed to the calling participant. 371 | /// The extracted share is boxed with a proof which allows the other participants to verify the share's correctness. 372 | /// 373 | /// - Parameters: 374 | /// - shares_box: The distribution shares box that consists the share to be extracted. 375 | /// - private_key: The participant's private key used to decrypt the share. 376 | /// - Returns: The share box that is to be submitted to all the other participants in order to reconstruct the secret. 377 | /// It consists of the share itself and the proof that allows the receiving participant to verify its correctness. 378 | /// Return `None` if the distribution shares box does not contain a share for the participant. 379 | /// 380 | /// ## Example 381 | /// 382 | /// ```rust 383 | /// use mpvss_rs::Participant; 384 | /// use num_bigint::{BigUint, ToBigInt}; 385 | /// 386 | /// let secret_message = String::from("Hello MPVSS Example."); 387 | /// let secret = BigUint::from_bytes_be(&secret_message.as_bytes()); 388 | /// let mut dealer = Participant::new(); 389 | /// dealer.initialize(); 390 | /// let mut p1 = Participant::new(); 391 | /// let mut p2 = Participant::new(); 392 | /// let mut p3 = Participant::new(); 393 | /// p1.initialize(); 394 | /// p2.initialize(); 395 | /// p3.initialize(); 396 | /// 397 | /// let distribute_shares_box = dealer.distribute_secret( 398 | /// &secret.to_bigint().unwrap(), 399 | /// &vec![ 400 | /// p1.publickey.clone(), 401 | /// p2.publickey.clone(), 402 | /// p3.publickey.clone(), 403 | /// ], 404 | /// 3, 405 | /// ); 406 | /// 407 | /// let s1 = p1 408 | /// .extract_secret_share(&distribute_shares_box, &p1.privatekey) 409 | /// .unwrap(); 410 | /// let s2 = p2 411 | /// .extract_secret_share(&distribute_shares_box, &p2.privatekey) 412 | /// .unwrap(); 413 | /// let s3 = p3 414 | /// .extract_secret_share(&distribute_shares_box, &p3.privatekey) 415 | /// .unwrap(); 416 | /// ``` 417 | pub fn extract_secret_share( 418 | &self, 419 | shares_box: &DistributionSharesBox, 420 | private_key: &BigInt, 421 | ) -> Option { 422 | let w = Generator::new_uint(self.mpvss.length as usize) 423 | .mod_floor(&self.mpvss.q.to_biguint().unwrap()); 424 | self.extract_share(shares_box, private_key, &w.to_bigint().unwrap()) 425 | } 426 | 427 | /// Verifies that the shares the distribution shares box consists are consistent so that they can be used to reconstruct the secret later. 428 | /// 429 | /// - Parameter distribute_sharesbox: The distribution shares box whose consistency is to be verified. 430 | /// - Returns: Returns `true` if the shares are correct and `false` otherwise. 431 | /// 432 | /// ## Example 433 | /// 434 | /// ```rust 435 | /// use mpvss_rs::Participant; 436 | /// use num_bigint::{BigUint, ToBigInt}; 437 | /// let secret_message = String::from("Hello MPVSS Example."); 438 | /// let secret = BigUint::from_bytes_be(&secret_message.as_bytes()); 439 | /// let mut dealer = Participant::new(); 440 | /// dealer.initialize(); 441 | /// let mut p1 = Participant::new(); 442 | /// let mut p2 = Participant::new(); 443 | /// let mut p3 = Participant::new(); 444 | /// p1.initialize(); 445 | /// p2.initialize(); 446 | /// p3.initialize(); 447 | /// 448 | /// let distribute_shares_box = dealer.distribute_secret( 449 | /// &secret.to_bigint().unwrap(), 450 | /// &vec![ 451 | /// p1.publickey.clone(), 452 | /// p2.publickey.clone(), 453 | /// p3.publickey.clone(), 454 | /// ], 455 | /// 3, 456 | /// ); 457 | /// 458 | /// assert_eq!( 459 | /// p1.verify_distribution_shares(&distribute_shares_box), 460 | /// true 461 | /// ); 462 | /// 463 | /// assert_eq!( 464 | /// p2.verify_distribution_shares(&distribute_shares_box), 465 | /// true 466 | /// ); 467 | 468 | /// assert_eq!( 469 | /// p3.verify_distribution_shares(&distribute_shares_box), 470 | /// true 471 | /// ); 472 | /// ``` 473 | pub fn verify_distribution_shares( 474 | &self, 475 | distribute_sharesbox: &DistributionSharesBox, 476 | ) -> bool { 477 | self.mpvss.verify_distribution_shares(distribute_sharesbox) 478 | } 479 | 480 | /// Verifies if the share in the distribution share box was decrypted correctly by the respective participant. 481 | /// 482 | /// - Parameters: 483 | /// - shareBox: The share box containing the share to be verified. 484 | /// - distributionShareBox: The distribution share box that contains the share. 485 | /// - publicKey: The public key of the sender of the share bundle. 486 | /// - Returns: Returns `true` if the share in the distribution share box matches the decryption of the encrypted share and `false` otherwise. 487 | /// 488 | /// ## Example 489 | /// 490 | /// ```rust 491 | /// use mpvss_rs::Participant; 492 | /// use num_bigint::{BigUint, ToBigInt}; 493 | /// 494 | /// let secret_message = String::from("Hello MPVSS Example."); 495 | /// let secret = BigUint::from_bytes_be(&secret_message.as_bytes()); 496 | /// let mut dealer = Participant::new(); 497 | /// dealer.initialize(); 498 | /// let mut p1 = Participant::new(); 499 | /// let mut p2 = Participant::new(); 500 | /// let mut p3 = Participant::new(); 501 | /// p1.initialize(); 502 | /// p2.initialize(); 503 | /// p3.initialize(); 504 | /// 505 | /// let distribute_shares_box = dealer.distribute_secret( 506 | /// &secret.to_bigint().unwrap(), 507 | /// &vec![ 508 | /// p1.publickey.clone(), 509 | /// p2.publickey.clone(), 510 | /// p3.publickey.clone(), 511 | /// ], 512 | /// 3, 513 | /// ); 514 | /// 515 | /// let s1 = p1 516 | /// .extract_secret_share(&distribute_shares_box, &p1.privatekey) 517 | /// .unwrap(); 518 | /// let s2 = p2 519 | /// .extract_secret_share(&distribute_shares_box, &p2.privatekey) 520 | /// .unwrap(); 521 | /// let s3 = p3 522 | /// .extract_secret_share(&distribute_shares_box, &p3.privatekey) 523 | /// .unwrap(); 524 | /// 525 | /// assert_eq!( 526 | /// p1.verify_share(&s2, &distribute_shares_box, &p2.publickey), 527 | /// true 528 | /// ); 529 | /// 530 | /// assert_eq!( 531 | /// p2.verify_share(&s3, &distribute_shares_box, &p3.publickey), 532 | /// true 533 | /// ); 534 | /// 535 | /// assert_eq!( 536 | /// p3.verify_share(&s1, &distribute_shares_box, &s1.publickey), 537 | /// true 538 | /// ); 539 | /// ``` 540 | pub fn verify_share( 541 | &self, 542 | sharebox: &ShareBox, 543 | distribution_sharebox: &DistributionSharesBox, 544 | publickey: &BigInt, 545 | ) -> bool { 546 | self.mpvss 547 | .verify_share(sharebox, distribution_sharebox, publickey) 548 | } 549 | 550 | /// Reconstruct secret from share boxs 551 | /// 552 | /// ## Example 553 | /// 554 | /// ```rust 555 | /// use mpvss_rs::Participant; 556 | /// use num_bigint::{BigUint, ToBigInt}; 557 | /// let secret_message = String::from("Hello MPVSS Example."); 558 | /// let secret = BigUint::from_bytes_be(&secret_message.as_bytes()); 559 | /// let mut dealer = Participant::new(); 560 | /// dealer.initialize(); 561 | /// let mut p1 = Participant::new(); 562 | /// let mut p2 = Participant::new(); 563 | /// let mut p3 = Participant::new(); 564 | /// p1.initialize(); 565 | /// p2.initialize(); 566 | /// p3.initialize(); 567 | /// 568 | /// let distribute_shares_box = dealer.distribute_secret( 569 | /// &secret.to_bigint().unwrap(), 570 | /// &vec![ 571 | /// p1.publickey.clone(), 572 | /// p2.publickey.clone(), 573 | /// p3.publickey.clone(), 574 | /// ], 575 | /// 3, 576 | /// ); 577 | /// 578 | /// assert_eq!( 579 | /// p1.verify_distribution_shares(&distribute_shares_box), 580 | /// true 581 | /// ); 582 | /// 583 | /// assert_eq!( 584 | /// p2.verify_distribution_shares(&distribute_shares_box), 585 | /// true 586 | /// ); 587 | /// 588 | /// assert_eq!( 589 | /// p3.verify_distribution_shares(&distribute_shares_box), 590 | /// true 591 | /// ); 592 | /// 593 | /// 594 | /// let s1 = p1 595 | /// .extract_secret_share(&distribute_shares_box, &p1.privatekey) 596 | /// .unwrap(); 597 | /// 598 | /// let s2 = p2 599 | /// .extract_secret_share(&distribute_shares_box, &p2.privatekey) 600 | /// .unwrap(); 601 | /// let s3 = p3 602 | /// .extract_secret_share(&distribute_shares_box, &p3.privatekey) 603 | /// .unwrap(); 604 | /// 605 | /// assert_eq!( 606 | /// p1.verify_share(&s2, &distribute_shares_box, &p2.publickey), 607 | /// true 608 | /// ); 609 | /// 610 | /// assert_eq!( 611 | /// p2.verify_share(&s3, &distribute_shares_box, &p3.publickey), 612 | /// true 613 | /// ); 614 | /// 615 | /// assert_eq!( 616 | /// p3.verify_share(&s1, &distribute_shares_box, &s1.publickey), 617 | /// true 618 | /// ); 619 | /// 620 | /// let share_boxs = [s1, s2, s3]; 621 | /// let r1 = p1 622 | /// .reconstruct(&share_boxs, &distribute_shares_box) 623 | /// .unwrap(); 624 | /// let r2 = p2 625 | /// .reconstruct(&share_boxs, &distribute_shares_box) 626 | /// .unwrap(); 627 | /// let r3 = p3 628 | /// .reconstruct(&share_boxs, &distribute_shares_box) 629 | /// .unwrap(); 630 | /// 631 | /// let r1_str = 632 | /// String::from_utf8(r1.to_biguint().unwrap().to_bytes_be()).unwrap(); 633 | /// assert_eq!(secret_message.clone(), r1_str); 634 | /// let r2_str = 635 | /// String::from_utf8(r2.to_biguint().unwrap().to_bytes_be()).unwrap(); 636 | /// assert_eq!(secret_message.clone(), r2_str); 637 | /// let r3_str = 638 | /// String::from_utf8(r3.to_biguint().unwrap().to_bytes_be()).unwrap(); 639 | /// assert_eq!(secret_message.clone(), r3_str); 640 | /// ``` 641 | pub fn reconstruct( 642 | &self, 643 | share_boxs: &[ShareBox], 644 | distribute_share_box: &DistributionSharesBox, 645 | ) -> Option { 646 | self.mpvss.reconstruct(share_boxs, distribute_share_box) 647 | } 648 | } 649 | 650 | #[cfg(test)] 651 | mod tests { 652 | 653 | use super::BTreeMap; 654 | use super::BigInt; 655 | use super::Participant; 656 | use super::Polynomial; 657 | use super::MPVSS; 658 | use super::{DistributionSharesBox, ShareBox}; 659 | use num_traits::{One, Zero}; 660 | 661 | struct Setup { 662 | pub mpvss: MPVSS, 663 | pub privatekey: BigInt, 664 | pub secret: BigInt, 665 | } 666 | 667 | impl Setup { 668 | fn new() -> Self { 669 | let q = BigInt::from(179426549); 670 | let g = BigInt::from(1301081); 671 | let G = BigInt::from(15486487); 672 | 673 | let length: i64 = 64_i64; 674 | let mut mpvss = MPVSS::new(); 675 | mpvss.length = length as u32; 676 | mpvss.g = g; 677 | mpvss.G = G; 678 | mpvss.q = q; 679 | 680 | return Setup { 681 | mpvss: mpvss, 682 | privatekey: BigInt::from(105929), 683 | secret: BigInt::from(1234567890), 684 | }; 685 | } 686 | } 687 | 688 | // Use Fixed distribution shares box for tests 689 | fn get_distribute_shares_box() -> DistributionSharesBox { 690 | let setup = Setup::new(); 691 | let mut dealer = Participant::new(); 692 | dealer.mpvss = setup.mpvss.clone(); 693 | dealer.privatekey = setup.privatekey.clone(); 694 | dealer.publickey = setup.mpvss.generate_public_key(&setup.privatekey); 695 | 696 | let mut polynomial = Polynomial::new(); 697 | polynomial.init_coefficients(&vec![ 698 | BigInt::from(164102006), 699 | BigInt::from(43489589), 700 | BigInt::from(98100795), 701 | ]); 702 | let threshold: i32 = 3; 703 | // from participant 1 to 3 704 | let privatekeys = 705 | [BigInt::from(7901), BigInt::from(4801), BigInt::from(1453)]; 706 | let mut publickeys = vec![]; 707 | let w = BigInt::from(6345); 708 | 709 | for key in privatekeys.iter() { 710 | publickeys.push(setup.mpvss.generate_public_key(key)); 711 | } 712 | 713 | return dealer.distribute( 714 | &setup.secret, 715 | &publickeys, 716 | threshold as u32, 717 | &polynomial, 718 | &w, 719 | ); 720 | } 721 | 722 | // Use Fixed Share box for tests 723 | fn get_share_box() -> ShareBox { 724 | let distribution_shares_box = get_distribute_shares_box(); 725 | // Use Participant 1's private key 726 | let private_key = BigInt::from(7901); 727 | let w = BigInt::from(1337); 728 | let mut participant = Participant::new(); 729 | let setup = Setup::new(); 730 | participant.mpvss = setup.mpvss.clone(); 731 | participant.privatekey = private_key.clone(); 732 | participant.publickey = setup.mpvss.generate_public_key(&private_key); 733 | 734 | participant 735 | .extract_share(&distribution_shares_box, &private_key, &w) 736 | .unwrap() 737 | } 738 | 739 | #[test] 740 | fn test_distribution() { 741 | let distribution = get_distribute_shares_box(); 742 | 743 | let commitments = vec![ 744 | BigInt::from(92318234), 745 | BigInt::from(76602245), 746 | BigInt::from(63484157), 747 | ]; 748 | let mut shares: BTreeMap = BTreeMap::new(); 749 | shares 750 | .insert(distribution.publickeys[0].clone(), BigInt::from(42478042)); 751 | shares 752 | .insert(distribution.publickeys[1].clone(), BigInt::from(80117658)); 753 | shares 754 | .insert(distribution.publickeys[2].clone(), BigInt::from(86941725)); 755 | 756 | let challenge = BigInt::from(41963410); 757 | let mut responses: BTreeMap = BTreeMap::new(); 758 | responses.insert( 759 | distribution.publickeys[0].clone(), 760 | BigInt::from(151565889), 761 | ); 762 | responses.insert( 763 | distribution.publickeys[1].clone(), 764 | BigInt::from(146145105), 765 | ); 766 | responses 767 | .insert(distribution.publickeys[2].clone(), BigInt::from(71350321)); 768 | 769 | assert_eq!(distribution.publickeys[0], distribution.publickeys[0]); 770 | assert_eq!(distribution.publickeys[1], distribution.publickeys[1]); 771 | assert_eq!(distribution.publickeys[2], distribution.publickeys[2]); 772 | 773 | assert_eq!(distribution.challenge, challenge); 774 | 775 | for i in 0..=2 { 776 | assert_eq!(distribution.commitments[i], commitments[i]); 777 | assert_eq!( 778 | distribution.shares[&distribution.publickeys[i]], 779 | shares[&distribution.publickeys[i]] 780 | ); 781 | assert_eq!( 782 | distribution.responses[&distribution.publickeys[i]], 783 | responses[&distribution.publickeys[i]] 784 | ); 785 | } 786 | } 787 | 788 | #[test] 789 | fn test_distribution_verify() { 790 | let setup = Setup::new(); 791 | let distribution = get_distribute_shares_box(); 792 | assert_eq!(setup.mpvss.verify_distribution_shares(&distribution), true); 793 | } 794 | 795 | #[test] 796 | fn test_extract_share() { 797 | let share_box = get_share_box(); 798 | assert_eq!(share_box.share, BigInt::from(164021044)); 799 | assert_eq!(share_box.challenge, BigInt::from(134883166)); 800 | assert_eq!(share_box.response, BigInt::from(81801891)); 801 | } 802 | 803 | #[test] 804 | fn test_share_box_verify() { 805 | // participant 1 private key 806 | let private_key = BigInt::from(7901); 807 | let distribution_shares_box = get_distribute_shares_box(); 808 | let share_box = get_share_box(); 809 | 810 | let setup = Setup::new(); 811 | assert_eq!( 812 | setup.mpvss.verify_share( 813 | &share_box, 814 | &distribution_shares_box, 815 | &setup.mpvss.generate_public_key(&private_key) 816 | ), 817 | true 818 | ); 819 | } 820 | 821 | #[test] 822 | fn test_reconstruction_with_all_participants() { 823 | let distribution_shares_box = get_distribute_shares_box(); 824 | let share_box1 = get_share_box(); 825 | let mut share_box2 = ShareBox::new(); 826 | share_box2.init( 827 | BigInt::from(132222922), 828 | BigInt::from(157312059), 829 | BigInt::zero(), 830 | BigInt::zero(), 831 | ); 832 | let mut share_box3 = ShareBox::new(); 833 | share_box3.init( 834 | BigInt::from(65136827), 835 | BigInt::from(63399333), 836 | BigInt::zero(), 837 | BigInt::zero(), 838 | ); 839 | 840 | let setup = Setup::new(); 841 | let share_boxs = [share_box1, share_box2, share_box3]; 842 | let reconstructed_secret = setup 843 | .mpvss 844 | .reconstruct(&share_boxs, &distribution_shares_box) 845 | .unwrap(); 846 | assert_eq!(reconstructed_secret, setup.secret); 847 | } 848 | 849 | // (3,4) threshhold reconstruct, participant 3 is not available, 1,2,4 is available 850 | #[test] 851 | fn test_reconstruction_with_sub_group() { 852 | let share_box1 = get_share_box(); 853 | let mut share_box2 = ShareBox::new(); 854 | share_box2.init( 855 | BigInt::from(132222922), 856 | BigInt::from(157312059), 857 | BigInt::zero(), 858 | BigInt::zero(), 859 | ); 860 | 861 | let public_key4 = BigInt::from(42); 862 | let mut share_box4 = ShareBox::new(); 863 | share_box4.init( 864 | public_key4.clone(), 865 | BigInt::from(59066181), 866 | BigInt::zero(), 867 | BigInt::zero(), 868 | ); 869 | 870 | let mut positions = BTreeMap::new(); 871 | positions.insert(share_box1.clone().publickey, 1_i64); 872 | positions.insert(share_box2.clone().publickey, 2_i64); 873 | positions.insert(share_box4.clone().publickey, 4_i64); 874 | 875 | let mut distribution_shares_box = DistributionSharesBox::new(); 876 | distribution_shares_box.init( 877 | &vec![BigInt::zero(), BigInt::one(), BigInt::from(2)], 878 | positions, 879 | BTreeMap::new(), 880 | &vec![], 881 | &BigInt::zero(), 882 | BTreeMap::new(), 883 | &BigInt::from(1284073502), 884 | ); 885 | 886 | let setup = Setup::new(); 887 | let share_boxs = [share_box1, share_box2, share_box4]; 888 | let reconstructed_secret = setup 889 | .mpvss 890 | .reconstruct(&share_boxs, &distribution_shares_box) 891 | .unwrap(); 892 | assert_eq!(reconstructed_secret, setup.secret); 893 | } 894 | } 895 | -------------------------------------------------------------------------------- /src/polynomial.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 MathxH Chen. 2 | // 3 | // Code is licensed under GPLv3.0 License. 4 | 5 | use num_bigint::{BigInt, RandBigInt, ToBigInt}; 6 | use num_traits::pow::Pow; 7 | use std::clone::Clone; 8 | use std::ops::*; 9 | use std::vec::Vec; 10 | 11 | /// Shamir Secret Shares on Polynomial, (k,n) threshhod secheme 12 | /// P(x) = a_0*x^0 + a_1*x^1 + a_2*x^2 + ... + a_n*x^(k-1) 13 | /// degree is k - 1 14 | /// s = P(0) = a_0 15 | /// s_i = P(i) 16 | 17 | #[derive(Debug, Clone, Default)] 18 | pub struct Polynomial { 19 | pub coefficients: Vec, 20 | } 21 | 22 | impl Polynomial { 23 | /// Create empty Polynomial instance 24 | pub fn new() -> Self { 25 | Polynomial { 26 | coefficients: Vec::new(), 27 | } 28 | } 29 | 30 | pub fn init_coefficients(&mut self, coefficients: &[BigInt]) { 31 | self.coefficients = coefficients.to_vec(); 32 | } 33 | 34 | pub fn init(&mut self, degree: i32, q: &BigInt) { 35 | let mut coefficients = vec![]; 36 | let mut rng = rand::thread_rng(); 37 | // [0,degree] not [0,degree) 38 | for _ in 0..=degree { 39 | let coefficient = rng 40 | .gen_biguint_below(&q.to_biguint().unwrap()) 41 | .to_bigint() 42 | .unwrap(); 43 | coefficients.push(coefficient); 44 | } 45 | 46 | self.init_coefficients(&coefficients); 47 | } 48 | 49 | /// Get P(x) = value 50 | pub fn get_value(&self, x: &BigInt) -> BigInt { 51 | // a_0 52 | let mut result = self.coefficients[0].clone(); 53 | // a0+ a_1*x^1 + a_2*x^2 + ... + a_n*x^n 54 | for i in 1..self.coefficients.len() { 55 | result += self.coefficients[i].clone().mul(x.pow(i)); 56 | } 57 | result 58 | } 59 | } 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | use super::BigInt; 64 | use super::Polynomial; 65 | use num_bigint::ToBigInt; 66 | #[test] 67 | fn test_init_polynomial() { 68 | let mut polynomial = Polynomial::new(); 69 | let degree = 3; 70 | polynomial.init(degree, &BigInt::from(5_i32)); 71 | 72 | assert_eq!(polynomial.coefficients.len(), (degree + 1) as usize); 73 | } 74 | #[test] 75 | fn test_get_value() { 76 | // a_0 = 3, a_1 = 2, a_2 = 2, a_3 = 4 77 | let mut polynomial = Polynomial::new(); 78 | polynomial.init_coefficients(&vec![ 79 | 3.to_bigint().unwrap(), 80 | 2.to_bigint().unwrap(), 81 | 2.to_bigint().unwrap(), 82 | 4.to_bigint().unwrap(), 83 | ]); 84 | 85 | // P(0) = a_0 = 3 86 | assert_eq!( 87 | polynomial.get_value(&0.to_bigint().unwrap()), 88 | BigInt::from(3_i32) 89 | ); 90 | 91 | // P(1) = 11 92 | assert_eq!( 93 | polynomial.get_value(&1.to_bigint().unwrap()), 94 | BigInt::from(11_i32) 95 | ); 96 | 97 | // P(2) = 47 98 | assert_eq!( 99 | polynomial.get_value(&2.to_bigint().unwrap()), 100 | BigInt::from(47_i32) 101 | ); 102 | 103 | // P(3) = 135 104 | assert_eq!( 105 | polynomial.get_value(&3.to_bigint().unwrap()), 106 | BigInt::from(135_i32) 107 | ); 108 | } 109 | 110 | #[test] 111 | fn test_get_value2() { 112 | let q: BigInt = BigInt::from(15486967); 113 | let coefficients = vec![ 114 | BigInt::from(105211), 115 | BigInt::from(1548877), 116 | BigInt::from(892134), 117 | BigInt::from(3490857), 118 | BigInt::from(324), 119 | BigInt::from(14234735), 120 | ]; 121 | let x: BigInt = BigInt::from(278); 122 | let mut polynomial = Polynomial::new(); 123 | polynomial.init_coefficients(&coefficients); 124 | assert_eq!(polynomial.get_value(&x) % q, BigInt::from(4115179)); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/sharebox.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 MathxH Chen. 2 | // 3 | // Code is licensed under GPLv3.0 License. 4 | 5 | #![allow(non_snake_case)] 6 | 7 | use num_bigint::BigInt; 8 | use num_traits::identities::Zero; 9 | use std::collections::BTreeMap; 10 | use std::vec::Vec; 11 | 12 | #[derive(Debug, Clone, Default)] 13 | pub struct ShareBox { 14 | pub publickey: BigInt, 15 | pub share: BigInt, 16 | pub challenge: BigInt, 17 | pub response: BigInt, 18 | } 19 | 20 | impl ShareBox { 21 | pub fn new() -> Self { 22 | ShareBox { 23 | publickey: BigInt::zero(), 24 | share: BigInt::zero(), 25 | challenge: BigInt::zero(), 26 | response: BigInt::zero(), 27 | } 28 | } 29 | 30 | pub fn init( 31 | &mut self, 32 | publickey: BigInt, 33 | share: BigInt, 34 | challenge: BigInt, 35 | response: BigInt, 36 | ) { 37 | self.publickey = publickey; 38 | self.share = share; 39 | self.challenge = challenge; 40 | self.response = response; 41 | } 42 | } 43 | 44 | /// the dealer wishes to distribute a secret among participants P1,...,Pn. 45 | /// The dealer picks a randompolynomialp of degree at most t−1 with coefficients in Z_q 46 | #[derive(Debug, Clone, Default)] 47 | pub struct DistributionSharesBox { 48 | pub commitments: Vec, 49 | pub positions: BTreeMap, 50 | pub shares: BTreeMap, 51 | pub publickeys: Vec, 52 | pub challenge: BigInt, 53 | pub responses: BTreeMap, 54 | pub U: BigInt, 55 | } 56 | 57 | impl DistributionSharesBox { 58 | pub fn new() -> Self { 59 | DistributionSharesBox { 60 | commitments: Vec::new(), 61 | positions: BTreeMap::new(), 62 | shares: BTreeMap::new(), 63 | publickeys: Vec::new(), 64 | challenge: BigInt::zero(), 65 | responses: BTreeMap::new(), 66 | U: BigInt::zero(), 67 | } 68 | } 69 | 70 | #[allow(clippy::too_many_arguments)] 71 | pub fn init( 72 | &mut self, 73 | commitments: &[BigInt], 74 | positions: BTreeMap, 75 | shares: BTreeMap, 76 | publickeys: &[BigInt], 77 | challenge: &BigInt, 78 | responses: BTreeMap, 79 | U: &BigInt, 80 | ) { 81 | self.commitments = commitments.to_vec(); 82 | self.positions = positions; 83 | self.shares = shares; 84 | self.publickeys = publickeys.to_vec(); 85 | self.challenge = challenge.clone(); 86 | self.responses = responses; 87 | self.U = U.clone(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 MathxH Chen. 2 | // 3 | // Code is licensed under GPLv3.0 License. 4 | 5 | use num_bigint::{BigInt, Sign}; 6 | use num_traits::identities::{One, Zero}; 7 | use std::clone::Clone; 8 | 9 | pub struct Util {} 10 | 11 | impl Util { 12 | /// Finds the greatest common denominator of two integers *a* and *b*, and two 13 | /// integers *x* and *y* such that *ax* + *by* is the greatest common 14 | /// denominator of *a* and *b* (Bézout coefficients). 15 | /// 16 | /// This function is an implementation of the [extended Euclidean 17 | /// algorithm](https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm). 18 | pub fn extend_gcd(a: &BigInt, b: &BigInt) -> (BigInt, BigInt, BigInt) { 19 | if a == &BigInt::zero() { 20 | (b.clone(), BigInt::zero(), BigInt::one()) 21 | } else { 22 | let (g, x, y) = Util::extend_gcd(&(b % a), a); 23 | (g, y - (b / a) * &x, x) 24 | } 25 | } 26 | 27 | /// Calculates the [modular multiplicative 28 | /// inverse](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse) *x* 29 | /// of an integer *a* such that *ax* ≡ 1 (mod *m*). 30 | /// 31 | /// Such an integer may not exist. If so, this function will return `None`. 32 | /// Otherwise, the inverse will be returned wrapped up in a `Some`. 33 | pub fn mod_inverse(a: &BigInt, modular: &BigInt) -> Option { 34 | let (g, x, _) = Util::extend_gcd(a, modular); 35 | if g != BigInt::one() { 36 | None 37 | } else { 38 | let result = (&x % modular + modular) % modular; 39 | Some(result) 40 | } 41 | } 42 | 43 | /// returned (numerator: BigInt, denominator: BigInt) represent as numerator/denominator 44 | /// 45 | /// where λ_i= ∏ j≠i = j/(j−i) is a Lagrange coefficient. 46 | /// 1 <= i <= threshold 0 <= j < threshold 47 | pub fn lagrange_coefficient(i: &i64, values: &[i64]) -> (BigInt, BigInt) { 48 | if !values.contains(i) { 49 | return (BigInt::zero(), BigInt::one()); 50 | } 51 | 52 | let mut numerator = BigInt::one(); 53 | let mut denominator = BigInt::one(); 54 | 55 | let vec_to = values.to_vec(); 56 | let max = vec_to.iter().max().unwrap(); 57 | for j in 1..=*max { 58 | if j != *i && values.contains(&j) { 59 | numerator *= j; 60 | denominator *= j - *i; 61 | } 62 | } 63 | (numerator, denominator) 64 | } 65 | 66 | /// return abs value 67 | pub fn abs(n: &BigInt) -> BigInt { 68 | match n.sign() { 69 | Sign::Minus => BigInt::new(Sign::Plus, n.to_u32_digits().1), 70 | Sign::Plus => n.clone(), 71 | Sign::NoSign => n.clone(), 72 | } 73 | } 74 | } 75 | 76 | #[cfg(test)] 77 | mod tests { 78 | use super::Util; 79 | use num_bigint::{BigInt, BigUint}; 80 | use num_traits::Num; 81 | use num_traits::{One, Zero}; 82 | use sha2::{Digest, Sha256}; 83 | #[test] 84 | fn test_extend_gcd() { 85 | let a = BigInt::from(26); 86 | let b = BigInt::from(3); 87 | let (g, x, y) = Util::extend_gcd(&a, &b); 88 | 89 | assert_eq!(g, BigInt::one()); 90 | assert_eq!(x, BigInt::from(-1)); 91 | assert_eq!(y, BigInt::from(9)); 92 | assert_eq!((a.clone() * x) + (b.clone() * y), g); 93 | } 94 | 95 | #[test] 96 | fn test_mod_inverse() { 97 | // 3*inverse = 1 mod 26 98 | let does_exist = Util::mod_inverse(&BigInt::from(3), &BigInt::from(26)); 99 | // 4*inverse = 1 mod 32 100 | let does_not_exist = 101 | Util::mod_inverse(&BigInt::from(4), &BigInt::from(32)); 102 | 103 | match does_exist { 104 | Some(x) => assert_eq!(x, BigInt::from(9)), 105 | None => panic!("mod_inverse() didn't work as expected"), 106 | } 107 | 108 | match does_not_exist { 109 | Some(x) => { 110 | drop(x); 111 | panic!("mod_inverse() found an inverse when it shouldn't have") 112 | } 113 | None => {} 114 | } 115 | } 116 | 117 | #[test] 118 | fn test_lagrange_coefficient() { 119 | let i_array = [9, 1, 2, 3]; 120 | let values = [0, 1, 2, 3, 4, 5, 6]; 121 | let result = Util::lagrange_coefficient(&i_array[0], &values); 122 | assert_eq!(result, (BigInt::zero(), BigInt::one())); 123 | 124 | // 0..=6 j/(j-1) = (2/1) * (3/2) * (4/3) * (5/4) * (6/5) = 720 / 120 125 | let result = Util::lagrange_coefficient(&i_array[1], &values); 126 | assert_eq!(result, (BigInt::from(720), BigInt::from(120))); 127 | 128 | // 0..=6 j/(j-2) = (1/-1) * (3/1) * (4/2) * (5/3) * (6/4) = 360 / -24 129 | let result = Util::lagrange_coefficient(&i_array[2], &values); 130 | assert_eq!(result, (BigInt::from(360), BigInt::from(-24))); 131 | 132 | // 0..=6 j/(j-3) = (1/-2) * (2/-1) * (4/1) * (5/2) * (6/3) = 240 / 12 133 | let result = Util::lagrange_coefficient(&i_array[3], &values); 134 | assert_eq!(result, (BigInt::from(240), BigInt::from(12))); 135 | 136 | let result = Util::lagrange_coefficient(&3, &[1, 3, 4]); 137 | assert_eq!(result, (BigInt::from(4), BigInt::from(-2))); 138 | } 139 | 140 | #[test] 141 | fn test_abs() { 142 | let minus = BigInt::from(-100); 143 | assert_eq!(Util::abs(&minus), BigInt::from(100)); 144 | 145 | let minus = BigInt::from(-0); 146 | assert_eq!(Util::abs(&minus), BigInt::from(0)); 147 | 148 | let plus = BigInt::from(0); 149 | assert_eq!(Util::abs(&plus), BigInt::from(0)); 150 | 151 | let plus = BigInt::from(100); 152 | assert_eq!(Util::abs(&plus), BigInt::from(100)); 153 | } 154 | 155 | #[test] 156 | fn test_xor() { 157 | let a = BigUint::from(1337_u32); 158 | let b = BigUint::from(42_u32); 159 | let xor = a ^ b; 160 | assert_eq!(xor, BigUint::from(1299_u32)); 161 | } 162 | 163 | #[test] 164 | fn test_hash() { 165 | let mut sha256 = Sha256::new(); 166 | let value_1 = 167 | BigInt::from_str_radix("43589072349864890574839", 10).unwrap(); 168 | let value_2 = 169 | BigInt::from_str_radix("14735247304952934566", 10).unwrap(); 170 | let value_1_uint = value_1.to_biguint().unwrap(); 171 | let value_2_uint = value_2.to_biguint().unwrap(); 172 | sha256.update(value_1_uint.to_str_radix(10).as_bytes()); 173 | sha256.update(value_2_uint.to_str_radix(10).as_bytes()); 174 | 175 | let result = sha256.finalize(); 176 | let challenge_big_uint = BigUint::from_bytes_be(&result[..]); 177 | 178 | assert_eq!( 179 | challenge_big_uint.to_str_radix(16), 180 | "e25e5b7edf4ea66e5238393fb4f183e0fc1593c69a522f9255a51bd0bc2b7ba7" 181 | ); 182 | 183 | assert_eq!( 184 | BigInt::from_str_radix(&challenge_big_uint.to_str_radix(16), 16), 185 | BigInt::from_str_radix( 186 | "102389418883295205726805934198606438410316463205994911160958467170744727731111", 187 | 10 188 | ) 189 | ); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /tests/mpvss_tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 MathxH Chen. 2 | // 3 | // Code is licensed under GPLv3.0 License. 4 | 5 | use mpvss_rs::Participant; 6 | use mpvss_rs::{string_from_secret, string_to_secret}; 7 | 8 | #[test] 9 | fn test_mpvss_distribute_verify_reconstruct() { 10 | let secret_message = String::from("Hello MPVSS."); 11 | let mut dealer = Participant::new(); 12 | dealer.initialize(); 13 | let mut p1 = Participant::new(); 14 | let mut p2 = Participant::new(); 15 | let mut p3 = Participant::new(); 16 | p1.initialize(); 17 | p2.initialize(); 18 | p3.initialize(); 19 | 20 | let distribute_shares_box = dealer.distribute_secret( 21 | &string_to_secret(&secret_message), 22 | &vec![ 23 | p1.publickey.clone(), 24 | p2.publickey.clone(), 25 | p3.publickey.clone(), 26 | ], 27 | 3, 28 | ); 29 | 30 | assert_eq!(p1.verify_distribution_shares(&distribute_shares_box), true); 31 | 32 | assert_eq!(p2.verify_distribution_shares(&distribute_shares_box), true); 33 | 34 | assert_eq!(p3.verify_distribution_shares(&distribute_shares_box), true); 35 | 36 | // p1 extracts the share. [p2 and p3 do this as well.] 37 | let s1 = p1 38 | .extract_secret_share(&distribute_shares_box, &p1.privatekey) 39 | .unwrap(); 40 | 41 | // p1, p2 and p3 exchange their descrypted shares. 42 | // ... 43 | let s2 = p2 44 | .extract_secret_share(&distribute_shares_box, &p2.privatekey) 45 | .unwrap(); 46 | let s3 = p3 47 | .extract_secret_share(&distribute_shares_box, &p3.privatekey) 48 | .unwrap(); 49 | 50 | // p1 verifies the share received from p2. [Actually everybody verifies every received share.] 51 | 52 | assert_eq!( 53 | p1.verify_share(&s2, &distribute_shares_box, &p2.publickey), 54 | true 55 | ); 56 | 57 | assert_eq!( 58 | p2.verify_share(&s3, &distribute_shares_box, &p3.publickey), 59 | true 60 | ); 61 | 62 | assert_eq!( 63 | p3.verify_share(&s1, &distribute_shares_box, &s1.publickey), 64 | true 65 | ); 66 | 67 | let share_boxs = [s1, s2, s3]; 68 | let r1 = p1.reconstruct(&share_boxs, &distribute_shares_box).unwrap(); 69 | let r2 = p2.reconstruct(&share_boxs, &distribute_shares_box).unwrap(); 70 | let r3 = p3.reconstruct(&share_boxs, &distribute_shares_box).unwrap(); 71 | 72 | let r1_str = string_from_secret(&r1); 73 | assert_eq!(secret_message.clone(), r1_str); 74 | let r2_str = string_from_secret(&r2); 75 | assert_eq!(secret_message.clone(), r2_str); 76 | let r3_str = string_from_secret(&r3); 77 | assert_eq!(secret_message.clone(), r3_str); 78 | } 79 | --------------------------------------------------------------------------------