├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README.md ├── Rocket.toml ├── benches ├── multi_party_ecdsa │ └── gg18 │ │ └── keygen.rs └── two_party_ecdsa │ └── lindell_2017 │ └── keygen.rs ├── demo ├── MP-ECDSA demo.gif └── run.sh ├── doc └── diagrams │ ├── keygen1.dot │ └── keygen2.dot ├── kzen-dev-setup.sh ├── params └── src ├── bin ├── gg18_keygen_client.rs ├── gg18_sign_client.rs └── sm_manager.rs ├── lib.rs └── protocols ├── mod.rs ├── multi_party_ecdsa ├── gg_2018 │ ├── mod.rs │ ├── mta.rs │ ├── party_i.rs │ └── test.rs └── mod.rs └── two_party_ecdsa ├── lindell_2017 ├── mod.rs ├── party_one.rs ├── party_two.rs └── test.rs └── mod.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | .idea 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | rust: 4 | - nightly 5 | 6 | before_script: 7 | - rustup component add rustfmt-preview 8 | - cargo fmt --all -- --check 9 | 10 | script: 11 | - cargo build --verbose 12 | - cargo test --verbose 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to the Multi-party ECDSA project 2 | ===================================== 3 | 4 | Pull requests are always welcome, and the KZen dev team appreciates any help the community can 5 | give to help make Multi-party ECDSA project better. 6 | 7 | Contributor Agreement (CA) 8 | ---------------- 9 | 10 | Any contributor must sign the Contributor Agreement (CA). 11 | 12 | ### How to sign the Contributor Agreement (CA)? 13 | 14 | Please send an email to [github@kzencorp.com](mailto:github@kzencorp.com) containing your github username, the CA will be send to you by email. 15 | After signature you will be added to the team as a contributor. 16 | 17 | Communication Channels 18 | ---------------- 19 | 20 | * Most communication about KZen cryptography happens on Telegram, feel free to send us an email with your contact details. 21 | 22 | * Discussion about code base improvements happens in GitHub issues and on pull requests. 23 | 24 | Contributor Workflow 25 | ---------------- 26 | 27 | The codebase is maintained using the "contributor workflow" where everyone contributes patch proposals using "pull requests". This facilitates social contribution, easy testing and peer review. 28 | 29 | To contribute a patch, the workflow is as follows: 30 | 31 | * Fork repository 32 | * Create topic branch 33 | * Commit patches 34 | * Push changes to your fork 35 | * Create pull request 36 | 37 | Make sure to provide a clear description in your Pull Request (PR). 38 | 39 | ### Header 40 | 41 | Make sure to include the following header (by configuring your IDE) in all files: 42 | 43 | ```rust 44 | /* 45 | Multi-party ECDSA 46 | 47 | Copyright 2018 by Kzen Networks 48 | 49 | This file is part of Multi-party ECDSA library 50 | (https://github.com/KZen-networks/multi-party-ecdsa) 51 | 52 | Multi-party ECDSA is free software: you can redistribute 53 | it and/or modify it under the terms of the GNU General Public 54 | License as published by the Free Software Foundation, either 55 | version 3 of the License, or (at your option) any later version. 56 | 57 | @license GPL-3.0+ 58 | */ 59 | ``` 60 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "multi-party-ecdsa" 3 | version = "0.1.0" 4 | authors = [ 5 | "Gary ", 6 | "Omer " 7 | ] 8 | keywords = [ 9 | "ecdsa", 10 | "multi-party-ecdsa", 11 | "signature", 12 | "rust", 13 | "secret-shares", 14 | "blockchain", 15 | "cryptography", 16 | "cryptocurrency"] 17 | 18 | homepage = "https://github.com/KZen-networks/multi-party-ecdsa" 19 | repository = "https://github.com/KZen-networks/multi-party-ecdsa" 20 | license = "GPL-3.0-or-later" 21 | categories = ["cryptography"] 22 | 23 | [lib] 24 | crate-type = ["lib"] 25 | 26 | [dependencies] 27 | paillier = { git = "https://github.com/KZen-networks/rust-paillier"} 28 | zk-paillier = { git = "https://github.com/KZen-networks/zk-paillier"} 29 | 30 | 31 | hex = "0.3.2" 32 | subtle = {version = "2", features = ["nightly"]} 33 | serde = "1.0" 34 | serde_derive = "1.0" 35 | serde_json = "1.0" 36 | reqwest = "0.9.5" 37 | rocket = "0.4.0" 38 | rocket_contrib = "0.4.0" 39 | uuid = { version = "0.7", features = ["v4"] } 40 | rust-crypto = "^0.2" 41 | 42 | [dependencies.curv] 43 | git = "https://github.com/KZen-networks/curv" 44 | features = ["ec_secp256k1"] 45 | 46 | [dependencies.centipede] 47 | git = "https://github.com/KZen-networks/centipede" 48 | 49 | [patch.crates-io] 50 | rust-gmp = { version = "0.5.0", features = ["serde_support"], git = "https://github.com/KZen-networks/rust-gmp" } 51 | 52 | [dev-dependencies] 53 | criterion = "0.2" 54 | 55 | [[bench]] 56 | name = "keygen" 57 | path = "benches/multi_party_ecdsa/gg18/keygen.rs" 58 | harness = false 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/KZen-networks/multi-party-ecdsa.svg?branch=master)](https://travis-ci.com/KZen-networks/multi-party-ecdsa) 2 | [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) 3 | 4 | Multi-party ECDSA 5 | ===================================== 6 | 7 | This project is a Rust implementation of {t,n}-threshold ECDSA (elliptic curve digital signature algorithm). 8 | 9 | Threshold ECDSA includes two protocols: 10 | 11 | * Key Generation for creating secret shares. 12 | * Signing for using the secret shares to generate a signature. 13 | 14 | ECDSA is used extensively for crypto-currencies such as Bitcoin, Ethereum (secp256k1 curve), NEO (NIST P-256 curve) and much more. 15 | This library can be used to create MultiSig and ThresholdSig crypto wallet. 16 | 17 | Project Status 18 | ------- 19 | * The library supports **2p-ecdsa** based on Lindell's crypto 2017 paper [1]. Project [Gotham-city](https://github.com/KZen-networks/gotham-city) is a proof of concept for a full two-party Bitcoin wallet that uses this library. See benchmarks and white paper there. 20 | 21 | * The library supports Gennaro and Goldfeder CCS 2018 protocol [2] for **{t,n}-threshold ECDSA**. 22 | 23 | Run 24 | ------- 25 | The following steps are for setup, key generation with `n` parties and signing with `t+1` parties. 26 | 27 | **Setup** 28 | 1) We use shared state machine architecture (see [white city](https://github.com/KZen-networks/white-city)). The parameters `parties` and `threshold` can be configured by changing the file: `param`. a keygen will run with `parties` parties and signing will run with any subset of `threshold + 1` parties. `param` file should be located in the same path of the client softwares. 29 | 2) Install [Rust](https://www.rust-lang.org/en-US/install.html),[Nightly Rust](https://doc.rust-lang.org/1.5.0/book/nightly-rust.html). Run `cargo build --release` ( it will build into `/target/release`) 30 | 3) Run the shared state machine: `./sm_manager`. Currently configured to be in `127.0.0.1:8001`, this can be changed in `Rocket.toml` file. The `Rocket.toml` file should be in the same folder you run `sm_manager` from. 31 | 32 | **KeyGen** 33 | 34 | run `gg18_keygen_client` as follows: `./gg18_keygen_client http://127.0.0.1:8001 keys.store`. Replace IP and port with the ones configured in setup. Once `n` parties join the application will run till finish. At the end each party will get a local keys file `keys.store` (change filename in command line). This contain secret and public data of the party after keygen. The file therefore should remain private. 35 | 36 | **Sign** 37 | 38 | Run `./gg18_sign_client`. The application should be in the same folder as the `keys.store` file (or custom filename generated in keygen). the application takes three arguments: `IP:port` as in keygen, `filename` and message to be signed: `./gg18_sign_client http://127.0.0.1:8001 keys.store "KZen Networks"`. The same message should be used by all signers. Once `t+1` parties join the protocol will run and will output to screen signatue (R,s). 39 | 40 | **Full demo** 41 | 42 | Run `./run.sh` (located in `/demo` folder) in the same folder as the excutables (usually `/target/release`). It will spawn a shared state machine, clients in the number of parties and signing requests for the `threshold + 1` first parties. 43 | 44 | 45 | 46 | |![Demo](https://raw.githubusercontent.com/KZen-networks/multi-party-ecdsa/master/demo/MP-ECDSA%20demo.gif "Multiparty ECDSA Demo")| 47 | |:--:| 48 | | *A 5 parties setup with 3 signers (threshold = 2)* | 49 | 50 | 51 | Contributions & Development Process 52 | ------------------- 53 | The contribution workflow is described in [CONTRIBUTING.md](CONTRIBUTING.md), in addition **the [Rust utilities wiki](https://github.com/KZen-networks/rust-utils/wiki) contains information on workflow and environment set-up**. 54 | 55 | License 56 | ------- 57 | Multi-party ECDSA is released under the terms of the GPL-3.0 license. See [LICENSE](LICENSE) for more information. 58 | 59 | Contact 60 | ------------------- 61 | Feel free to [reach out](mailto:github@kzencorp.com) or join the KZen Research [Telegram]( https://t.me/kzen_research) for discussions on code and research. 62 | 63 | References 64 | ------------------- 65 | 66 | [1] https://eprint.iacr.org/2017/552.pdf 67 | 68 | [2] https://eprint.iacr.org/2019/114.pdf 69 | -------------------------------------------------------------------------------- /Rocket.toml: -------------------------------------------------------------------------------- 1 | [development] 2 | address = "127.0.0.1" 3 | port = 8001 4 | workers = 12 5 | keep_alive = 5 6 | log = "normal" 7 | hi = "Hello!" # this is an unused extra; maybe application specific? 8 | is_extra = true # this is an unused extra; maybe application specific? 9 | 10 | [staging] 11 | address = "127.0.0.1" 12 | port = 8001 13 | workers = 8 14 | keep_alive = 5 15 | log = "normal" 16 | # don't use this key! generate your own and keep it private! 17 | secret_key = "8Xui8SN4mI+7egV/9dlfYYLGQJeEx4+DwmSQLwDVXJg=" 18 | 19 | [production] 20 | address = "127.0.0.1" 21 | port = 8001 22 | workers = 12 23 | keep_alive = 5 24 | log = "critical" 25 | # don't use this key! generate your own and keep it private! 26 | secret_key = "hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk=" 27 | -------------------------------------------------------------------------------- /benches/multi_party_ecdsa/gg18/keygen.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | extern crate curv; 4 | extern crate multi_party_ecdsa; 5 | 6 | mod bench { 7 | use criterion::Criterion; 8 | use curv::cryptographic_primitives::secret_sharing::feldman_vss::VerifiableSS; 9 | use curv::elliptic::curves::traits::*; 10 | use curv::{FE, GE}; 11 | use multi_party_ecdsa::protocols::multi_party_ecdsa::gg_2018::party_i::*; 12 | pub fn bench_full_keygen_party_one_two(c: &mut Criterion) { 13 | c.bench_function("keygen t=1 n=2", move |b| { 14 | b.iter(|| { 15 | keygen_t_n_parties(1, 2); 16 | }) 17 | }); 18 | } 19 | pub fn bench_full_keygen_party_two_three(c: &mut Criterion) { 20 | c.bench_function("keygen t=2 n=3", move |b| { 21 | b.iter(|| { 22 | keygen_t_n_parties(2, 3); 23 | }) 24 | }); 25 | } 26 | pub fn keygen_t_n_parties( 27 | t: usize, 28 | n: usize, 29 | ) -> (Vec, Vec, Vec, GE, VerifiableSS) { 30 | let parames = Parameters { 31 | threshold: t, 32 | share_count: n.clone(), 33 | }; 34 | let party_keys_vec = (0..n.clone()) 35 | .map(|i| Keys::create(i)) 36 | .collect::>(); 37 | 38 | let mut bc1_vec = Vec::new(); 39 | let mut decom_vec = Vec::new(); 40 | for i in 0..n.clone() { 41 | let (bc1, decom1) = party_keys_vec[i].phase1_broadcast_phase3_proof_of_correct_key(); 42 | bc1_vec.push(bc1); 43 | decom_vec.push(decom1); 44 | } 45 | 46 | let y_vec = (0..n.clone()) 47 | .map(|i| decom_vec[i].y_i.clone()) 48 | .collect::>(); 49 | let mut y_vec_iter = y_vec.iter(); 50 | let head = y_vec_iter.next().unwrap(); 51 | let tail = y_vec_iter; 52 | let y_sum = tail.fold(head.clone(), |acc, x| acc + x); 53 | let mut vss_scheme_vec = Vec::new(); 54 | let mut secret_shares_vec = Vec::new(); 55 | let mut index_vec = Vec::new(); 56 | for i in 0..n.clone() { 57 | let (vss_scheme, secret_shares, index) = party_keys_vec[i] 58 | .phase1_verify_com_phase3_verify_correct_key_phase2_distribute( 59 | ¶mes, &decom_vec, &bc1_vec, 60 | ) 61 | .expect("invalid key"); 62 | vss_scheme_vec.push(vss_scheme); 63 | secret_shares_vec.push(secret_shares); 64 | index_vec.push(index); 65 | } 66 | let vss_scheme_for_test = vss_scheme_vec.clone(); 67 | 68 | let party_shares = (0..n.clone()) 69 | .map(|i| { 70 | (0..n.clone()) 71 | .map(|j| { 72 | let vec_j = &secret_shares_vec[j]; 73 | vec_j[i].clone() 74 | }) 75 | .collect::>() 76 | }) 77 | .collect::>>(); 78 | 79 | let mut shared_keys_vec = Vec::new(); 80 | let mut dlog_proof_vec = Vec::new(); 81 | for i in 0..n.clone() { 82 | let (shared_keys, dlog_proof) = party_keys_vec[i] 83 | .phase2_verify_vss_construct_keypair_phase3_pok_dlog( 84 | ¶mes, 85 | &y_vec, 86 | &party_shares[i], 87 | &vss_scheme_vec, 88 | &(&index_vec[i] + 1), 89 | ) 90 | .expect("invalid vss"); 91 | shared_keys_vec.push(shared_keys); 92 | dlog_proof_vec.push(dlog_proof); 93 | } 94 | 95 | let pk_vec = (0..n.clone()) 96 | .map(|i| dlog_proof_vec[i].pk.clone()) 97 | .collect::>(); 98 | 99 | //both parties run: 100 | Keys::verify_dlog_proofs(¶mes, &dlog_proof_vec, &y_vec).expect("bad dlog proof"); 101 | 102 | //test 103 | let xi_vec = (0..t.clone() + 1) 104 | .map(|i| shared_keys_vec[i].x_i.clone()) 105 | .collect::>(); 106 | let x = vss_scheme_for_test[0] 107 | .clone() 108 | .reconstruct(&index_vec[0..t.clone() + 1], &xi_vec); 109 | let sum_u_i = party_keys_vec 110 | .iter() 111 | .fold(FE::zero(), |acc, x| acc + &x.u_i); 112 | assert_eq!(x, sum_u_i); 113 | 114 | ( 115 | party_keys_vec, 116 | shared_keys_vec, 117 | pk_vec, 118 | y_sum, 119 | vss_scheme_for_test[0].clone(), 120 | ) 121 | } 122 | 123 | criterion_group! { 124 | name = keygen; 125 | config = Criterion::default().sample_size(10); 126 | targets = 127 | self::bench_full_keygen_party_one_two, 128 | self::bench_full_keygen_party_two_three} 129 | } 130 | 131 | criterion_main!(bench::keygen); 132 | -------------------------------------------------------------------------------- /benches/two_party_ecdsa/lindell_2017/keygen.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | extern crate curv; 4 | extern crate multi_party_ecdsa; 5 | 6 | mod bench { 7 | use criterion::Criterion; 8 | use curv::arithmetic::traits::Samplable; 9 | use curv::elliptic::curves::traits::*; 10 | use curv::BigInt; 11 | use multi_party_ecdsa::protocols::two_party_ecdsa::lindell_2017::*; 12 | 13 | pub fn bench_full_keygen_party_one_two(c: &mut Criterion) { 14 | c.bench_function("keygen", move |b| { 15 | b.iter(|| { 16 | let (party_one_first_message, comm_witness, ec_key_pair_party1) = 17 | party_one::KeyGenFirstMsg::create_commitments_with_fixed_secret_share( 18 | ECScalar::from(&BigInt::sample(253)), 19 | ); 20 | let (party_two_first_message, _ec_key_pair_party2) = 21 | party_two::KeyGenFirstMsg::create_with_fixed_secret_share(ECScalar::from( 22 | &BigInt::from(10), 23 | )); 24 | let party_one_second_message = party_one::KeyGenSecondMsg::verify_and_decommit( 25 | comm_witness, 26 | &party_two_first_message.d_log_proof, 27 | ) 28 | .expect("failed to verify and decommit"); 29 | 30 | let _party_two_second_message = 31 | party_two::KeyGenSecondMsg::verify_commitments_and_dlog_proof( 32 | &party_one_first_message, 33 | &party_one_second_message, 34 | ) 35 | .expect("failed to verify commitments and DLog proof"); 36 | 37 | // init paillier keypair: 38 | let paillier_key_pair = 39 | party_one::PaillierKeyPair::generate_keypair_and_encrypted_share( 40 | &ec_key_pair_party1, 41 | ); 42 | 43 | let party_one_private = party_one::Party1Private::set_private_key( 44 | &ec_key_pair_party1, 45 | &paillier_key_pair, 46 | ); 47 | 48 | let party_two_paillier = party_two::PaillierPublic { 49 | ek: paillier_key_pair.ek.clone(), 50 | encrypted_secret_share: paillier_key_pair.encrypted_share.clone(), 51 | }; 52 | 53 | let correct_key_proof = 54 | party_one::PaillierKeyPair::generate_ni_proof_correct_key(&paillier_key_pair); 55 | party_two::PaillierPublic::verify_ni_proof_correct_key( 56 | correct_key_proof, 57 | &party_two_paillier.ek, 58 | ) 59 | .expect("bad paillier key"); 60 | // zk proof of correct paillier key 61 | 62 | // zk range proof 63 | let range_proof = party_one::PaillierKeyPair::generate_range_proof( 64 | &paillier_key_pair, 65 | &party_one_private, 66 | ); 67 | 68 | let _result = party_two::PaillierPublic::verify_range_proof( 69 | &party_two_paillier, 70 | &range_proof, 71 | ) 72 | .expect("range proof error"); 73 | 74 | // pdl proof minus range proof 75 | let (party_two_pdl_first_message, pdl_chal_party2) = party_two_paillier 76 | .pdl_challenge(&party_one_second_message.comm_witness.public_share); 77 | 78 | let (party_one_pdl_first_message, pdl_decommit_party1) = 79 | party_one::PaillierKeyPair::pdl_first_stage( 80 | &party_one_private, 81 | &party_two_pdl_first_message, 82 | ); 83 | 84 | let party_two_pdl_second_message = 85 | party_two::PaillierPublic::pdl_decommit_c_tag_tag(&pdl_chal_party2); 86 | let party_one_pdl_second_message = party_one::PaillierKeyPair::pdl_second_stage( 87 | &party_one_pdl_first_message, 88 | &party_two_pdl_first_message, 89 | &party_two_pdl_second_message, 90 | party_one_private, 91 | pdl_decommit_party1, 92 | ) 93 | .expect("pdl error party2"); 94 | 95 | party_two::PaillierPublic::verify_pdl( 96 | &pdl_chal_party2, 97 | &party_one_pdl_first_message, 98 | &party_one_pdl_second_message, 99 | ) 100 | .expect("pdl error party1") 101 | }) 102 | }); 103 | } 104 | 105 | criterion_group! { 106 | name = keygen; 107 | config = Criterion::default().sample_size(10); 108 | targets =self::bench_full_keygen_party_one_two} 109 | } 110 | 111 | criterion_main!(bench::keygen); 112 | -------------------------------------------------------------------------------- /demo/MP-ECDSA demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trepca/multi-party-ecdsa/d660e73354028b856ba8f5be28d0afcf0e53c2ad/demo/MP-ECDSA demo.gif -------------------------------------------------------------------------------- /demo/run.sh: -------------------------------------------------------------------------------- 1 | params=`cat params` 2 | regex='{\"parties\":\"(.*)\",\"threshold\":\"(.*)\"}' 3 | [[ $params =~ $regex ]] 4 | 5 | n=${BASH_REMATCH[1]} 6 | t=${BASH_REMATCH[2]} 7 | 8 | echo "$0: Multi-party ECDSA parties:$n threshold:$t" 9 | #clean 10 | sleep 1 11 | 12 | rm keys?.store 13 | 14 | 15 | kill -9 $(lsof -t -i:8001) 16 | 17 | 18 | ./sm_manager& 19 | 20 | sleep 2 21 | echo "keygen part" 22 | 23 | for i in $(seq 1 $n) 24 | do 25 | 26 | echo "key gen for client $i out of $n" 27 | ./gg18_keygen_client http://127.0.0.1:8001 keys$i.store & 28 | sleep 3 29 | done 30 | 31 | 32 | 33 | sleep 5 34 | echo "sign" 35 | 36 | for i in $(seq 1 $((t+1))); 37 | do 38 | echo "signing for client $i out of $((t+1))" 39 | ./gg18_sign_client http://127.0.0.1:8001 keys$i.store "KZen Networks" & 40 | 41 | sleep 2 42 | done 43 | 44 | -------------------------------------------------------------------------------- /doc/diagrams/keygen1.dot: -------------------------------------------------------------------------------- 1 | // Lindell 2party ECDSA keygen party 1 2 | digraph { 3 | start [shape=doublecircle] 4 | stop [shape=doublecircle] 5 | A 6 | B 7 | C 8 | D 9 | E 10 | start -> A [label="create commitments, ID"] 11 | "check1" [shape=diamond] 12 | "check2" [shape=diamond] 13 | A -> "check1" [label="verify_and_decommit, ID"] 14 | "check1" -> B [label="yes,ID"] 15 | "check1" -> stop [label=no] 16 | B -> C [label="generate_keypair_and_encrypted_share,ID"] 17 | C -> "check2" [label="generate_proof_correct_key, ID"] 18 | "check2" -> D [label="yes,ID"] 19 | "check2" -> stop [label=no] 20 | D -> E [label="generate_range_proof,ID"] 21 | E -> stop [label="write_to_db,ID"] 22 | 23 | } 24 | -------------------------------------------------------------------------------- /doc/diagrams/keygen2.dot: -------------------------------------------------------------------------------- 1 | // Lindell 2party ECDSA keygen party 2 2 | digraph { 3 | start [shape=doublecircle] 4 | stop [shape=doublecircle] 5 | A 6 | B 7 | C 8 | D 9 | E 10 | start -> A [label="begin session"] 11 | "check1" [shape=diamond] 12 | "check2" [shape=diamond] 13 | "check3" [shape=diamond] 14 | A -> B [label="create, ID"] 15 | B -> "check1" [label="verify_commitments_and_dlog_proof, ID"] 16 | "check1" -> C [label="yes,ID"] 17 | "check1" -> stop [label=no] 18 | C -> D [label="generate_correct_key_challenge,ID"] 19 | D -> "check2" [label="verify_correct_key, ID"] 20 | "check2" -> E [label="yes,ID"] 21 | "check2" -> stop [label=no] 22 | E -> "check3" [label="verify_range_proof, ID"] 23 | "check3" -> F [label="yes,ID"] 24 | "check3" -> stop [label=no] 25 | F -> stop [label="write_to_local_storage,ID"] 26 | 27 | } 28 | -------------------------------------------------------------------------------- /kzen-dev-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "KZen dev Setup" 4 | echo "Installing Git hooks..." 5 | git clone https://github.com/KZen-networks/scripts.git 6 | chmod -R +x scripts/git/hooks/rust/ 7 | cp scripts/git/hooks/rust/* .git/hooks/ 8 | rm -rf scripts 9 | echo "Done." -------------------------------------------------------------------------------- /params: -------------------------------------------------------------------------------- 1 | {"parties":"4","threshold":"2"} -------------------------------------------------------------------------------- /src/bin/gg18_keygen_client.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | extern crate crypto; 3 | extern crate curv; 4 | /// to run: 5 | /// 1: go to rocket_server -> cargo run 6 | /// 2: cargo run from PARTIES number of terminals 7 | extern crate multi_party_ecdsa; 8 | extern crate paillier; 9 | extern crate reqwest; 10 | #[macro_use] 11 | extern crate serde_derive; 12 | 13 | extern crate serde_json; 14 | 15 | use crypto::aead::AeadDecryptor; 16 | use crypto::aead::AeadEncryptor; 17 | use crypto::aes::KeySize::KeySize256; 18 | use crypto::aes_gcm::AesGcm; 19 | use curv::arithmetic::traits::Converter; 20 | use curv::cryptographic_primitives::proofs::sigma_dlog::DLogProof; 21 | use curv::cryptographic_primitives::secret_sharing::feldman_vss::VerifiableSS; 22 | use curv::elliptic::curves::traits::*; 23 | use curv::BigInt; 24 | use curv::{FE, GE}; 25 | use multi_party_ecdsa::protocols::multi_party_ecdsa::gg_2018::party_i::*; 26 | use paillier::EncryptionKey; 27 | use reqwest::Client; 28 | use std::env; 29 | use std::fs; 30 | use std::iter::repeat; 31 | use std::time::Duration; 32 | use std::{thread, time}; 33 | 34 | #[derive(Hash, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] 35 | pub struct TupleKey { 36 | pub first: String, 37 | pub second: String, 38 | pub third: String, 39 | pub fourth: String, 40 | } 41 | 42 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 43 | pub struct AEAD { 44 | pub ciphertext: Vec, 45 | pub tag: Vec, 46 | } 47 | 48 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 49 | pub struct PartySignup { 50 | pub number: u32, 51 | pub uuid: String, 52 | } 53 | 54 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 55 | pub struct Index { 56 | pub key: TupleKey, 57 | } 58 | 59 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 60 | pub struct Entry { 61 | pub key: TupleKey, 62 | pub value: String, 63 | } 64 | 65 | #[derive(Serialize, Deserialize)] 66 | pub struct Params { 67 | pub parties: String, 68 | pub threshold: String, 69 | } 70 | fn main() { 71 | if env::args().nth(3).is_some() { 72 | panic!("too many arguments") 73 | } 74 | if env::args().nth(2).is_none() { 75 | panic!("too few arguments") 76 | } 77 | //read parameters: 78 | let data = fs::read_to_string("params") 79 | .expect("Unable to read params, make sure config file is present in the same folder "); 80 | let params: Params = serde_json::from_str(&data).unwrap(); 81 | let PARTIES: u32 = params.parties.parse::().unwrap(); 82 | let THRESHOLD: u32 = params.threshold.parse::().unwrap(); 83 | 84 | let client = Client::new(); 85 | // delay: 86 | let delay = time::Duration::from_millis(25); 87 | let parames = Parameters { 88 | threshold: THRESHOLD as usize, 89 | share_count: PARTIES as usize, 90 | }; 91 | //signup: 92 | let party_i_signup_result = signup(&client); 93 | assert!(party_i_signup_result.is_ok()); 94 | let party_i_signup = party_i_signup_result.unwrap(); 95 | println!("{:?}", party_i_signup.clone()); 96 | let party_num_int = party_i_signup.number.clone(); 97 | let uuid = party_i_signup.uuid; 98 | 99 | let party_keys = Keys::create(party_num_int.clone() as usize); 100 | let (bc_i, decom_i) = party_keys.phase1_broadcast_phase3_proof_of_correct_key(); 101 | 102 | ////////////////////////////////////////////////////////////////////////////// 103 | 104 | // send commitment to ephemeral public keys, get round 1 commitments of other parties 105 | assert!(broadcast( 106 | &client, 107 | party_num_int.clone(), 108 | "round1", 109 | serde_json::to_string(&bc_i).unwrap(), 110 | uuid.clone() 111 | ) 112 | .is_ok()); 113 | let round1_ans_vec = poll_for_broadcasts( 114 | &client, 115 | party_num_int.clone(), 116 | PARTIES, 117 | delay.clone(), 118 | "round1", 119 | uuid.clone(), 120 | ); 121 | 122 | let mut j = 0; 123 | let bc1_vec = (1..PARTIES + 1) 124 | .map(|i| { 125 | if i == party_num_int { 126 | bc_i.clone() 127 | } else { 128 | let bc1_j: KeyGenBroadcastMessage1 = 129 | serde_json::from_str(&round1_ans_vec[j]).unwrap(); 130 | j = j + 1; 131 | bc1_j 132 | } 133 | }) 134 | .collect::>(); 135 | 136 | // round 2: send ephemeral public keys and check commitments correctness 137 | assert!(broadcast( 138 | &client, 139 | party_num_int.clone(), 140 | "round2", 141 | serde_json::to_string(&decom_i).unwrap(), 142 | uuid.clone() 143 | ) 144 | .is_ok()); 145 | let round2_ans_vec = poll_for_broadcasts( 146 | &client, 147 | party_num_int.clone(), 148 | PARTIES, 149 | delay.clone(), 150 | "round2", 151 | uuid.clone(), 152 | ); 153 | 154 | ////////////////////////////////////////////////////////////////////////////// 155 | 156 | let mut j = 0; 157 | let mut y_vec: Vec = Vec::new(); 158 | let mut decom_vec: Vec = Vec::new(); 159 | let mut enc_keys: Vec = Vec::new(); 160 | for i in 1..PARTIES + 1 { 161 | if i == party_num_int { 162 | y_vec.push(party_keys.y_i.clone()); 163 | decom_vec.push(decom_i.clone()); 164 | } else { 165 | let decom_j: KeyGenDecommitMessage1 = serde_json::from_str(&round2_ans_vec[j]).unwrap(); 166 | y_vec.push(decom_j.y_i.clone()); 167 | decom_vec.push(decom_j.clone()); 168 | enc_keys.push( 169 | (party_keys.y_i.clone() + decom_j.y_i.clone()) 170 | .x_coor() 171 | .unwrap(), 172 | ); 173 | j = j + 1; 174 | } 175 | } 176 | 177 | let mut y_vec_iter = y_vec.iter(); 178 | let head = y_vec_iter.next().unwrap(); 179 | let tail = y_vec_iter; 180 | let y_sum = tail.fold(head.clone(), |acc, x| acc + x); 181 | 182 | let (vss_scheme, secret_shares, _index) = party_keys 183 | .phase1_verify_com_phase3_verify_correct_key_phase2_distribute( 184 | ¶mes, &decom_vec, &bc1_vec, 185 | ) 186 | .expect("invalid key"); 187 | 188 | ////////////////////////////////////////////////////////////////////////////// 189 | 190 | let mut j = 0; 191 | let mut k = 0; 192 | let round = 3; 193 | for i in 1..PARTIES + 1 { 194 | if i != party_num_int { 195 | // prepare encrypted ss for party i: 196 | let key_i = BigInt::to_vec(&enc_keys[j]); 197 | let nonce: Vec = repeat(round).take(12).collect(); 198 | let aad: [u8; 0] = []; 199 | let mut gcm = AesGcm::new(KeySize256, &key_i[..], &nonce[..], &aad); 200 | let plaintext = BigInt::to_vec(&secret_shares[k].to_big_int()); 201 | let mut out: Vec = repeat(0).take(plaintext.len()).collect(); 202 | let mut out_tag: Vec = repeat(0).take(16).collect(); 203 | gcm.encrypt(&plaintext[..], &mut out[..], &mut out_tag[..]); 204 | let aead_pack_i = AEAD { 205 | ciphertext: out.to_vec(), 206 | tag: out_tag.to_vec(), 207 | }; 208 | assert!(sendp2p( 209 | &client, 210 | party_num_int.clone(), 211 | i, 212 | "round3", 213 | serde_json::to_string(&aead_pack_i).unwrap(), 214 | uuid.clone() 215 | ) 216 | .is_ok()); 217 | j = j + 1; 218 | } 219 | k = k + 1; 220 | } 221 | 222 | let round3_ans_vec = poll_for_p2p( 223 | &client, 224 | party_num_int.clone(), 225 | PARTIES, 226 | delay.clone(), 227 | "round3", 228 | uuid.clone(), 229 | ); 230 | 231 | let mut j = 0; 232 | let mut party_shares: Vec = Vec::new(); 233 | for i in 1..PARTIES + 1 { 234 | if i == party_num_int { 235 | party_shares.push(secret_shares[(i - 1) as usize].clone()); 236 | } else { 237 | let aead_pack: AEAD = serde_json::from_str(&round3_ans_vec[j]).unwrap(); 238 | let mut out: Vec = repeat(0).take(aead_pack.ciphertext.len()).collect(); 239 | let key_i = BigInt::to_vec(&enc_keys[j]); 240 | let nonce: Vec = repeat(round).take(12).collect(); 241 | let aad: [u8; 0] = []; 242 | let mut gcm = AesGcm::new(KeySize256, &key_i[..], &nonce[..], &aad); 243 | let result = gcm.decrypt(&aead_pack.ciphertext[..], &mut out, &aead_pack.tag[..]); 244 | assert!(result); 245 | let out_bn = BigInt::from(&out[..]); 246 | let out_fe = ECScalar::from(&out_bn); 247 | party_shares.push(out_fe); 248 | 249 | j = j + 1; 250 | } 251 | } 252 | ////////////////////////////////////////////////////////////////////////////// 253 | 254 | // round 4: send vss commitments 255 | assert!(broadcast( 256 | &client, 257 | party_num_int.clone(), 258 | "round4", 259 | serde_json::to_string(&vss_scheme).unwrap(), 260 | uuid.clone() 261 | ) 262 | .is_ok()); 263 | let round4_ans_vec = poll_for_broadcasts( 264 | &client, 265 | party_num_int.clone(), 266 | PARTIES, 267 | delay.clone(), 268 | "round4", 269 | uuid.clone(), 270 | ); 271 | 272 | let mut j = 0; 273 | let mut vss_scheme_vec: Vec = Vec::new(); 274 | for i in 1..PARTIES + 1 { 275 | if i == party_num_int { 276 | vss_scheme_vec.push(vss_scheme.clone()); 277 | } else { 278 | let vss_scheme_j: VerifiableSS = serde_json::from_str(&round4_ans_vec[j]).unwrap(); 279 | vss_scheme_vec.push(vss_scheme_j); 280 | j = j + 1; 281 | } 282 | } 283 | 284 | let (shared_keys, dlog_proof) = party_keys 285 | .phase2_verify_vss_construct_keypair_phase3_pok_dlog( 286 | ¶mes, 287 | &y_vec, 288 | &party_shares, 289 | &vss_scheme_vec, 290 | &(party_num_int as usize), 291 | ) 292 | .expect("invalid vss"); 293 | 294 | ////////////////////////////////////////////////////////////////////////////// 295 | // round 5: send vss commitments 296 | assert!(broadcast( 297 | &client, 298 | party_num_int.clone(), 299 | "round5", 300 | serde_json::to_string(&dlog_proof).unwrap(), 301 | uuid.clone() 302 | ) 303 | .is_ok()); 304 | let round5_ans_vec = poll_for_broadcasts( 305 | &client, 306 | party_num_int.clone(), 307 | PARTIES, 308 | delay.clone(), 309 | "round5", 310 | uuid.clone(), 311 | ); 312 | 313 | let mut j = 0; 314 | let mut dlog_proof_vec: Vec = Vec::new(); 315 | for i in 1..PARTIES + 1 { 316 | if i == party_num_int { 317 | dlog_proof_vec.push(dlog_proof.clone()); 318 | } else { 319 | let dlog_proof_j: DLogProof = serde_json::from_str(&round5_ans_vec[j]).unwrap(); 320 | dlog_proof_vec.push(dlog_proof_j); 321 | j = j + 1; 322 | } 323 | } 324 | Keys::verify_dlog_proofs(¶mes, &dlog_proof_vec, &y_vec).expect("bad dlog proof"); 325 | ////////////////////////////////////////////////////////////////////////////// 326 | //save key to file: 327 | 328 | let paillier_key_vec = (0..PARTIES) 329 | .map(|i| bc1_vec[i as usize].e.clone()) 330 | .collect::>(); 331 | 332 | let keygen_json = serde_json::to_string(&( 333 | party_keys, 334 | shared_keys, 335 | party_num_int, 336 | vss_scheme_vec, 337 | paillier_key_vec, 338 | y_sum, 339 | )) 340 | .unwrap(); 341 | 342 | fs::write(env::args().nth(2).unwrap(), keygen_json).expect("Unable to save !"); 343 | } 344 | 345 | ////////////////////////////////////////////////////////////////////////////// 346 | ////////////////////////////////////////////////////////////////////////////// 347 | ////////////////////////////////////////////////////////////////////////////// 348 | ////////////////////////////////////////////////////////////////////////////// 349 | pub fn postb(client: &Client, path: &str, body: T) -> Option 350 | where 351 | T: serde::ser::Serialize, 352 | { 353 | let addr = env::args() 354 | .nth(1) 355 | .unwrap_or("http://127.0.0.1:8001".to_string()); 356 | let res = client 357 | .post(&format!("{}/{}", addr, path)) 358 | .json(&body) 359 | .send(); 360 | Some(res.unwrap().text().unwrap()) 361 | } 362 | 363 | pub fn signup(client: &Client) -> Result<(PartySignup), ()> { 364 | let key = TupleKey { 365 | first: "signup".to_string(), 366 | second: "keygen".to_string(), 367 | third: "".to_string(), 368 | fourth: "".to_string(), 369 | }; 370 | 371 | let res_body = postb(&client, "signupkeygen", key).unwrap(); 372 | let answer: Result<(PartySignup), ()> = serde_json::from_str(&res_body).unwrap(); 373 | return answer; 374 | } 375 | 376 | pub fn broadcast( 377 | client: &Client, 378 | party_num: u32, 379 | round: &str, 380 | data: String, 381 | uuid: String, 382 | ) -> Result<(), ()> { 383 | let key = TupleKey { 384 | first: party_num.to_string(), 385 | second: round.to_string(), 386 | third: uuid, 387 | fourth: "".to_string(), 388 | }; 389 | let entry = Entry { 390 | key: key.clone(), 391 | value: data, 392 | }; 393 | 394 | let res_body = postb(&client, "set", entry).unwrap(); 395 | let answer: Result<(), ()> = serde_json::from_str(&res_body).unwrap(); 396 | return answer; 397 | } 398 | 399 | pub fn sendp2p( 400 | client: &Client, 401 | party_from: u32, 402 | party_to: u32, 403 | round: &str, 404 | data: String, 405 | uuid: String, 406 | ) -> Result<(), ()> { 407 | let key = TupleKey { 408 | first: party_from.to_string(), 409 | second: round.to_string(), 410 | third: uuid, 411 | fourth: party_to.to_string(), 412 | }; 413 | let entry = Entry { 414 | key: key.clone(), 415 | value: data, 416 | }; 417 | 418 | let res_body = postb(&client, "set", entry).unwrap(); 419 | let answer: Result<(), ()> = serde_json::from_str(&res_body).unwrap(); 420 | return answer; 421 | } 422 | 423 | pub fn poll_for_broadcasts( 424 | client: &Client, 425 | party_num: u32, 426 | n: u32, 427 | delay: Duration, 428 | round: &str, 429 | uuid: String, 430 | ) -> Vec { 431 | let mut ans_vec = Vec::new(); 432 | for i in 1..n + 1 { 433 | if i != party_num { 434 | let key = TupleKey { 435 | first: i.to_string(), 436 | second: round.to_string(), 437 | third: uuid.clone(), 438 | fourth: "".to_string(), 439 | }; 440 | let index = Index { key }; 441 | loop { 442 | // add delay to allow the server to process request: 443 | thread::sleep(delay); 444 | let res_body = postb(client, "get", index.clone()).unwrap(); 445 | let answer: Result = serde_json::from_str(&res_body).unwrap(); 446 | if answer.is_ok() { 447 | ans_vec.push(answer.unwrap().value); 448 | println!("party {:?} {:?} read success", i, round); 449 | break; 450 | } 451 | } 452 | } 453 | } 454 | ans_vec 455 | } 456 | 457 | pub fn poll_for_p2p( 458 | client: &Client, 459 | party_num: u32, 460 | n: u32, 461 | delay: Duration, 462 | round: &str, 463 | uuid: String, 464 | ) -> Vec { 465 | let mut ans_vec = Vec::new(); 466 | for i in 1..n + 1 { 467 | if i != party_num { 468 | let key = TupleKey { 469 | first: i.to_string(), 470 | second: round.to_string(), 471 | third: uuid.clone(), 472 | fourth: party_num.to_string(), 473 | }; 474 | let index = Index { key }; 475 | loop { 476 | // add delay to allow the server to process request: 477 | thread::sleep(delay); 478 | let res_body = postb(client, "get", index.clone()).unwrap(); 479 | let answer: Result = serde_json::from_str(&res_body).unwrap(); 480 | if answer.is_ok() { 481 | ans_vec.push(answer.unwrap().value); 482 | println!("party {:?} {:?} read success", i, round); 483 | break; 484 | } 485 | } 486 | } 487 | } 488 | ans_vec 489 | } 490 | -------------------------------------------------------------------------------- /src/bin/gg18_sign_client.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | extern crate crypto; 3 | extern crate curv; 4 | /// to run: 5 | /// 1: go to rocket_server -> cargo run 6 | /// 2: cargo run from PARTIES number of terminals 7 | extern crate multi_party_ecdsa; 8 | extern crate paillier; 9 | extern crate reqwest; 10 | #[macro_use] 11 | extern crate serde_derive; 12 | extern crate hex; 13 | extern crate serde_json; 14 | 15 | use curv::cryptographic_primitives::proofs::sigma_correct_homomorphic_elgamal_enc::HomoELGamalProof; 16 | use curv::cryptographic_primitives::proofs::sigma_dlog::DLogProof; 17 | use curv::cryptographic_primitives::secret_sharing::feldman_vss::VerifiableSS; 18 | use curv::elliptic::curves::traits::*; 19 | use curv::BigInt; 20 | use curv::{FE, GE}; 21 | use multi_party_ecdsa::protocols::multi_party_ecdsa::gg_2018::mta::*; 22 | use multi_party_ecdsa::protocols::multi_party_ecdsa::gg_2018::party_i::*; 23 | use paillier::*; 24 | use reqwest::Client; 25 | use std::env; 26 | use std::fs; 27 | use std::time::Duration; 28 | use std::{thread, time}; 29 | 30 | #[derive(Hash, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] 31 | pub struct TupleKey { 32 | pub first: String, 33 | pub second: String, 34 | pub third: String, 35 | pub fourth: String, 36 | } 37 | 38 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 39 | pub struct AEAD { 40 | pub ciphertext: Vec, 41 | pub tag: Vec, 42 | } 43 | 44 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 45 | pub struct PartySignup { 46 | pub number: u32, 47 | pub uuid: String, 48 | } 49 | 50 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 51 | pub struct Index { 52 | pub key: TupleKey, 53 | } 54 | 55 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 56 | pub struct Entry { 57 | pub key: TupleKey, 58 | pub value: String, 59 | } 60 | #[derive(Serialize, Deserialize)] 61 | pub struct Params { 62 | pub parties: String, 63 | pub threshold: String, 64 | } 65 | 66 | fn main() { 67 | if env::args().nth(4).is_some() { 68 | panic!("too many arguments") 69 | } 70 | if env::args().nth(3).is_none() { 71 | panic!("too few arguments") 72 | } 73 | let message_str = env::args().nth(3).unwrap_or("".to_string()); 74 | let message = match hex::decode(message_str.clone()) { 75 | Ok(x) => x, 76 | Err(_e) => message_str.as_bytes().to_vec(), 77 | }; 78 | let message = &message[..]; 79 | let client = Client::new(); 80 | // delay: 81 | let delay = time::Duration::from_millis(25); 82 | // read key file 83 | let data = fs::read_to_string(env::args().nth(2).unwrap()) 84 | .expect("Unable to load keys, did you run keygen first? "); 85 | let (party_keys, shared_keys, party_id, vss_scheme_vec, paillier_key_vector, y_sum): ( 86 | Keys, 87 | SharedKeys, 88 | u32, 89 | Vec, 90 | Vec, 91 | GE, 92 | ) = serde_json::from_str(&data).unwrap(); 93 | 94 | //read parameters: 95 | let data = fs::read_to_string("params") 96 | .expect("Unable to read params, make sure config file is present in the same folder "); 97 | let params: Params = serde_json::from_str(&data).unwrap(); 98 | let THRESHOLD: u32 = params.threshold.parse::().unwrap(); 99 | 100 | ////////////////////////////////////////////////////////////////////////////// 101 | //signup: 102 | let party_i_signup_result = signup(&client); 103 | assert!(party_i_signup_result.is_ok()); 104 | let party_i_signup = party_i_signup_result.unwrap(); 105 | println!("{:?}", party_i_signup.clone()); 106 | let party_num_int = party_i_signup.number.clone(); 107 | let uuid = party_i_signup.uuid; 108 | 109 | ////////////////////////////////////////////////////////////////////////////// 110 | // round 0: collect signers IDs 111 | assert!(broadcast( 112 | &client, 113 | party_num_int.clone(), 114 | "round0", 115 | serde_json::to_string(&party_id).unwrap(), 116 | uuid.clone() 117 | ) 118 | .is_ok()); 119 | let round0_ans_vec = poll_for_broadcasts( 120 | &client, 121 | party_num_int.clone(), 122 | THRESHOLD + 1, 123 | delay.clone(), 124 | "round0", 125 | uuid.clone(), 126 | ); 127 | 128 | let mut j = 0; 129 | let mut signers_vec: Vec = Vec::new(); 130 | for i in 1..THRESHOLD + 2 { 131 | if i == party_num_int { 132 | signers_vec.push((party_id - 1) as usize); 133 | } else { 134 | let signer_j: u32 = serde_json::from_str(&round0_ans_vec[j]).unwrap(); 135 | signers_vec.push((signer_j - 1) as usize); 136 | j = j + 1; 137 | } 138 | } 139 | // signers_vec.sort(); 140 | 141 | let private = PartyPrivate::set_private(party_keys.clone(), shared_keys); 142 | 143 | let sign_keys = SignKeys::create( 144 | &private, 145 | &vss_scheme_vec[signers_vec[(party_num_int - 1) as usize]], 146 | signers_vec[(party_num_int - 1) as usize], 147 | &signers_vec, 148 | ); 149 | 150 | let xi_com_vec = Keys::get_commitments_to_xi(&vss_scheme_vec); 151 | ////////////////////////////////////////////////////////////////////////////// 152 | let (com, decommit) = sign_keys.phase1_broadcast(); 153 | let m_a_k = MessageA::a(&sign_keys.k_i, &party_keys.ek); 154 | assert!(broadcast( 155 | &client, 156 | party_num_int.clone(), 157 | "round1", 158 | serde_json::to_string(&(com.clone(), m_a_k.clone())).unwrap(), 159 | uuid.clone() 160 | ) 161 | .is_ok()); 162 | let round1_ans_vec = poll_for_broadcasts( 163 | &client, 164 | party_num_int.clone(), 165 | THRESHOLD + 1, 166 | delay.clone(), 167 | "round1", 168 | uuid.clone(), 169 | ); 170 | 171 | let mut j = 0; 172 | let mut bc1_vec: Vec = Vec::new(); 173 | let mut m_a_vec: Vec = Vec::new(); 174 | 175 | for i in 1..THRESHOLD + 2 { 176 | if i == party_num_int { 177 | bc1_vec.push(com.clone()); 178 | // m_a_vec.push(m_a_k.clone()); 179 | } else { 180 | // if signers_vec.contains(&(i as usize)) { 181 | let (bc1_j, m_a_party_j): (SignBroadcastPhase1, MessageA) = 182 | serde_json::from_str(&round1_ans_vec[j]).unwrap(); 183 | bc1_vec.push(bc1_j); 184 | m_a_vec.push(m_a_party_j); 185 | 186 | j = j + 1; 187 | // } 188 | } 189 | } 190 | assert_eq!(signers_vec.len(), bc1_vec.len()); 191 | 192 | ////////////////////////////////////////////////////////////////////////////// 193 | let mut m_b_gamma_send_vec: Vec = Vec::new(); 194 | let mut beta_vec: Vec = Vec::new(); 195 | let mut m_b_w_send_vec: Vec = Vec::new(); 196 | let mut ni_vec: Vec = Vec::new(); 197 | let mut j = 0; 198 | for i in 1..THRESHOLD + 2 { 199 | if i != party_num_int { 200 | let (m_b_gamma, beta_gamma) = MessageB::b( 201 | &sign_keys.gamma_i, 202 | &paillier_key_vector[signers_vec[(i - 1) as usize]], 203 | m_a_vec[j].clone(), 204 | ); 205 | let (m_b_w, beta_wi) = MessageB::b( 206 | &sign_keys.w_i, 207 | &paillier_key_vector[signers_vec[(i - 1) as usize]], 208 | m_a_vec[j].clone(), 209 | ); 210 | m_b_gamma_send_vec.push(m_b_gamma); 211 | m_b_w_send_vec.push(m_b_w); 212 | beta_vec.push(beta_gamma); 213 | ni_vec.push(beta_wi); 214 | j = j + 1; 215 | } 216 | } 217 | 218 | let mut j = 0; 219 | for i in 1..THRESHOLD + 2 { 220 | if i != party_num_int { 221 | assert!(sendp2p( 222 | &client, 223 | party_num_int.clone(), 224 | i.clone(), 225 | "round2", 226 | serde_json::to_string(&(m_b_gamma_send_vec[j].clone(), m_b_w_send_vec[j].clone())) 227 | .unwrap(), 228 | uuid.clone() 229 | ) 230 | .is_ok()); 231 | j = j + 1; 232 | } 233 | } 234 | 235 | let round2_ans_vec = poll_for_p2p( 236 | &client, 237 | party_num_int.clone(), 238 | THRESHOLD + 1, 239 | delay.clone(), 240 | "round2", 241 | uuid.clone(), 242 | ); 243 | 244 | let mut m_b_gamma_rec_vec: Vec = Vec::new(); 245 | let mut m_b_w_rec_vec: Vec = Vec::new(); 246 | 247 | for i in 0..THRESHOLD { 248 | // if signers_vec.contains(&(i as usize)) { 249 | let (m_b_gamma_i, m_b_w_i): (MessageB, MessageB) = 250 | serde_json::from_str(&round2_ans_vec[i as usize]).unwrap(); 251 | m_b_gamma_rec_vec.push(m_b_gamma_i); 252 | m_b_w_rec_vec.push(m_b_w_i); 253 | // } 254 | } 255 | 256 | let mut alpha_vec: Vec = Vec::new(); 257 | let mut miu_vec: Vec = Vec::new(); 258 | 259 | let mut j = 0; 260 | for i in 1..THRESHOLD + 2 { 261 | if i != party_num_int { 262 | let m_b = m_b_gamma_rec_vec[j].clone(); 263 | 264 | let alpha_ij_gamma = m_b 265 | .verify_proofs_get_alpha(&party_keys.dk, &sign_keys.k_i) 266 | .expect("wrong dlog or m_b"); 267 | let m_b = m_b_w_rec_vec[j].clone(); 268 | let alpha_ij_wi = m_b 269 | .verify_proofs_get_alpha(&party_keys.dk, &sign_keys.k_i) 270 | .expect("wrong dlog or m_b"); 271 | alpha_vec.push(alpha_ij_gamma); 272 | miu_vec.push(alpha_ij_wi); 273 | let g_w_i = Keys::update_commitments_to_xi( 274 | &xi_com_vec[signers_vec[(i - 1) as usize]], 275 | &vss_scheme_vec[signers_vec[(i - 1) as usize]], 276 | signers_vec[(i - 1) as usize], 277 | &signers_vec, 278 | ); 279 | assert_eq!(m_b.b_proof.pk.clone(), g_w_i); 280 | j = j + 1; 281 | } 282 | } 283 | ////////////////////////////////////////////////////////////////////////////// 284 | let delta_i = sign_keys.phase2_delta_i(&alpha_vec, &beta_vec); 285 | let sigma = sign_keys.phase2_sigma_i(&miu_vec, &ni_vec); 286 | 287 | assert!(broadcast( 288 | &client, 289 | party_num_int.clone(), 290 | "round3", 291 | serde_json::to_string(&delta_i).unwrap(), 292 | uuid.clone() 293 | ) 294 | .is_ok()); 295 | let round3_ans_vec = poll_for_broadcasts( 296 | &client, 297 | party_num_int.clone(), 298 | THRESHOLD + 1, 299 | delay.clone(), 300 | "round3", 301 | uuid.clone(), 302 | ); 303 | let mut delta_vec: Vec = Vec::new(); 304 | format_vec_from_reads( 305 | &round3_ans_vec, 306 | party_num_int.clone() as usize, 307 | delta_i, 308 | &mut delta_vec, 309 | ); 310 | let delta_inv = SignKeys::phase3_reconstruct_delta(&delta_vec); 311 | 312 | ////////////////////////////////////////////////////////////////////////////// 313 | // decommit to gamma_i 314 | assert!(broadcast( 315 | &client, 316 | party_num_int.clone(), 317 | "round4", 318 | serde_json::to_string(&decommit).unwrap(), 319 | uuid.clone() 320 | ) 321 | .is_ok()); 322 | let round4_ans_vec = poll_for_broadcasts( 323 | &client, 324 | party_num_int.clone(), 325 | THRESHOLD + 1, 326 | delay.clone(), 327 | "round4", 328 | uuid.clone(), 329 | ); 330 | 331 | let mut decommit_vec: Vec = Vec::new(); 332 | format_vec_from_reads( 333 | &round4_ans_vec, 334 | party_num_int.clone() as usize, 335 | decommit, 336 | &mut decommit_vec, 337 | ); 338 | let decomm_i = decommit_vec.remove((party_num_int - 1) as usize); 339 | bc1_vec.remove((party_num_int - 1) as usize); 340 | let b_proof_vec = (0..m_b_gamma_rec_vec.len()) 341 | .map(|i| &m_b_gamma_rec_vec[i].b_proof) 342 | .collect::>(); 343 | let R = SignKeys::phase4(&delta_inv, &b_proof_vec, decommit_vec, &bc1_vec) 344 | .expect("bad gamma_i decommit"); 345 | 346 | // adding local g_gamma_i 347 | let R = R + decomm_i.g_gamma_i * &delta_inv; 348 | 349 | // we assume the message is already hashed (by the signer). 350 | let message_bn = BigInt::from(message); 351 | let two = BigInt::from(2); 352 | let message_bn = message_bn.modulus(&two.pow(256)); 353 | let local_sig = 354 | LocalSignature::phase5_local_sig(&sign_keys.k_i, &message_bn, &R, &sigma, &y_sum); 355 | 356 | let (phase5_com, phase_5a_decom, helgamal_proof) = local_sig.phase5a_broadcast_5b_zkproof(); 357 | 358 | //phase (5A) broadcast commit 359 | assert!(broadcast( 360 | &client, 361 | party_num_int.clone(), 362 | "round5", 363 | serde_json::to_string(&phase5_com).unwrap(), 364 | uuid.clone() 365 | ) 366 | .is_ok()); 367 | let round5_ans_vec = poll_for_broadcasts( 368 | &client, 369 | party_num_int.clone(), 370 | THRESHOLD + 1, 371 | delay.clone(), 372 | "round5", 373 | uuid.clone(), 374 | ); 375 | 376 | let mut commit5a_vec: Vec = Vec::new(); 377 | format_vec_from_reads( 378 | &round5_ans_vec, 379 | party_num_int.clone() as usize, 380 | phase5_com, 381 | &mut commit5a_vec, 382 | ); 383 | 384 | //phase (5B) broadcast decommit and (5B) ZK proof 385 | assert!(broadcast( 386 | &client, 387 | party_num_int.clone(), 388 | "round6", 389 | serde_json::to_string(&(phase_5a_decom.clone(), helgamal_proof.clone())).unwrap(), 390 | uuid.clone() 391 | ) 392 | .is_ok()); 393 | let round6_ans_vec = poll_for_broadcasts( 394 | &client, 395 | party_num_int.clone(), 396 | THRESHOLD + 1, 397 | delay.clone(), 398 | "round6", 399 | uuid.clone(), 400 | ); 401 | 402 | let mut decommit5a_and_elgamal_vec: Vec<(Phase5ADecom1, HomoELGamalProof)> = Vec::new(); 403 | format_vec_from_reads( 404 | &round6_ans_vec, 405 | party_num_int.clone() as usize, 406 | (phase_5a_decom.clone(), helgamal_proof.clone()), 407 | &mut decommit5a_and_elgamal_vec, 408 | ); 409 | let decommit5a_and_elgamal_vec_includes_i = decommit5a_and_elgamal_vec.clone(); 410 | decommit5a_and_elgamal_vec.remove((party_num_int - 1) as usize); 411 | commit5a_vec.remove((party_num_int - 1) as usize); 412 | let phase_5a_decomm_vec = (0..THRESHOLD) 413 | .map(|i| decommit5a_and_elgamal_vec[i as usize].0.clone()) 414 | .collect::>(); 415 | let phase_5a_elgamal_vec = (0..THRESHOLD) 416 | .map(|i| decommit5a_and_elgamal_vec[i as usize].1.clone()) 417 | .collect::>(); 418 | let (phase5_com2, phase_5d_decom2) = local_sig 419 | .phase5c( 420 | &phase_5a_decomm_vec, 421 | &commit5a_vec, 422 | &phase_5a_elgamal_vec, 423 | &phase_5a_decom.V_i, 424 | &R.clone(), 425 | ) 426 | .expect("error phase5"); 427 | 428 | ////////////////////////////////////////////////////////////////////////////// 429 | assert!(broadcast( 430 | &client, 431 | party_num_int.clone(), 432 | "round7", 433 | serde_json::to_string(&phase5_com2).unwrap(), 434 | uuid.clone() 435 | ) 436 | .is_ok()); 437 | let round7_ans_vec = poll_for_broadcasts( 438 | &client, 439 | party_num_int.clone(), 440 | THRESHOLD + 1, 441 | delay.clone(), 442 | "round7", 443 | uuid.clone(), 444 | ); 445 | 446 | let mut commit5c_vec: Vec = Vec::new(); 447 | format_vec_from_reads( 448 | &round7_ans_vec, 449 | party_num_int.clone() as usize, 450 | phase5_com2, 451 | &mut commit5c_vec, 452 | ); 453 | 454 | //phase (5B) broadcast decommit and (5B) ZK proof 455 | assert!(broadcast( 456 | &client, 457 | party_num_int.clone(), 458 | "round8", 459 | serde_json::to_string(&phase_5d_decom2).unwrap(), 460 | uuid.clone() 461 | ) 462 | .is_ok()); 463 | let round8_ans_vec = poll_for_broadcasts( 464 | &client, 465 | party_num_int.clone(), 466 | THRESHOLD + 1, 467 | delay.clone(), 468 | "round8", 469 | uuid.clone(), 470 | ); 471 | 472 | let mut decommit5d_vec: Vec = Vec::new(); 473 | format_vec_from_reads( 474 | &round8_ans_vec, 475 | party_num_int.clone() as usize, 476 | phase_5d_decom2.clone(), 477 | &mut decommit5d_vec, 478 | ); 479 | 480 | let phase_5a_decomm_vec_includes_i = (0..THRESHOLD + 1) 481 | .map(|i| decommit5a_and_elgamal_vec_includes_i[i as usize].0.clone()) 482 | .collect::>(); 483 | let s_i = local_sig 484 | .phase5d( 485 | &decommit5d_vec, 486 | &commit5c_vec, 487 | &phase_5a_decomm_vec_includes_i, 488 | ) 489 | .expect("bad com 5d"); 490 | 491 | ////////////////////////////////////////////////////////////////////////////// 492 | assert!(broadcast( 493 | &client, 494 | party_num_int.clone(), 495 | "round9", 496 | serde_json::to_string(&s_i).unwrap(), 497 | uuid.clone() 498 | ) 499 | .is_ok()); 500 | let round9_ans_vec = poll_for_broadcasts( 501 | &client, 502 | party_num_int.clone(), 503 | THRESHOLD + 1, 504 | delay.clone(), 505 | "round9", 506 | uuid.clone(), 507 | ); 508 | 509 | let mut s_i_vec: Vec = Vec::new(); 510 | format_vec_from_reads( 511 | &round9_ans_vec, 512 | party_num_int.clone() as usize, 513 | s_i, 514 | &mut s_i_vec, 515 | ); 516 | 517 | s_i_vec.remove((party_num_int - 1) as usize); 518 | let sig = local_sig 519 | .output_signature(&s_i_vec) 520 | .expect("verification failed"); 521 | println!(" \n"); 522 | println!("party {:?} Output Signature: \n", party_num_int); 523 | println!("R: {:?}", sig.r.get_element()); 524 | println!("s: {:?} \n", sig.s.get_element()); 525 | let sign_json = serde_json::to_string(&( 526 | "r", 527 | (BigInt::from(&(sig.r.get_element())[..])).to_str_radix(16), 528 | "s", 529 | (BigInt::from(&(sig.s.get_element())[..])).to_str_radix(16), 530 | )) 531 | .unwrap(); 532 | 533 | fs::write("signature".to_string(), sign_json).expect("Unable to save !"); 534 | } 535 | 536 | fn format_vec_from_reads<'a, T: serde::Deserialize<'a> + Clone>( 537 | ans_vec: &'a Vec, 538 | party_num: usize, 539 | value_i: T, 540 | new_vec: &'a mut Vec, 541 | ) { 542 | let mut j = 0; 543 | for i in 1..ans_vec.len() + 2 { 544 | if i == party_num { 545 | new_vec.push(value_i.clone()); 546 | } else { 547 | let value_j: T = serde_json::from_str(&ans_vec[j]).unwrap(); 548 | new_vec.push(value_j); 549 | j = j + 1; 550 | } 551 | } 552 | } 553 | 554 | pub fn postb(client: &Client, path: &str, body: T) -> Option 555 | where 556 | T: serde::ser::Serialize, 557 | { 558 | let addr = env::args() 559 | .nth(1) 560 | .unwrap_or("http://127.0.0.1:8001".to_string()); 561 | let res = client 562 | .post(&format!("{}/{}", addr, path)) 563 | .json(&body) 564 | .send(); 565 | Some(res.unwrap().text().unwrap()) 566 | } 567 | 568 | pub fn signup(client: &Client) -> Result<(PartySignup), ()> { 569 | let key = TupleKey { 570 | first: "signup".to_string(), 571 | second: "sign".to_string(), 572 | third: "".to_string(), 573 | fourth: "".to_string(), 574 | }; 575 | 576 | let res_body = postb(&client, "signupsign", key).unwrap(); 577 | let answer: Result<(PartySignup), ()> = serde_json::from_str(&res_body).unwrap(); 578 | return answer; 579 | } 580 | 581 | pub fn broadcast( 582 | client: &Client, 583 | party_num: u32, 584 | round: &str, 585 | data: String, 586 | uuid: String, 587 | ) -> Result<(), ()> { 588 | let key = TupleKey { 589 | first: party_num.to_string(), 590 | second: round.to_string(), 591 | third: uuid, 592 | fourth: "".to_string(), 593 | }; 594 | let entry = Entry { 595 | key: key.clone(), 596 | value: data, 597 | }; 598 | 599 | let res_body = postb(&client, "set", entry).unwrap(); 600 | let answer: Result<(), ()> = serde_json::from_str(&res_body).unwrap(); 601 | return answer; 602 | } 603 | 604 | pub fn sendp2p( 605 | client: &Client, 606 | party_from: u32, 607 | party_to: u32, 608 | round: &str, 609 | data: String, 610 | uuid: String, 611 | ) -> Result<(), ()> { 612 | let key = TupleKey { 613 | first: party_from.to_string(), 614 | second: round.to_string(), 615 | third: uuid, 616 | fourth: party_to.to_string(), 617 | }; 618 | let entry = Entry { 619 | key: key.clone(), 620 | value: data, 621 | }; 622 | 623 | let res_body = postb(&client, "set", entry).unwrap(); 624 | let answer: Result<(), ()> = serde_json::from_str(&res_body).unwrap(); 625 | return answer; 626 | } 627 | 628 | pub fn poll_for_broadcasts( 629 | client: &Client, 630 | party_num: u32, 631 | n: u32, 632 | delay: Duration, 633 | round: &str, 634 | uuid: String, 635 | ) -> Vec { 636 | let mut ans_vec = Vec::new(); 637 | for i in 1..n + 1 { 638 | if i != party_num { 639 | let key = TupleKey { 640 | first: i.to_string(), 641 | second: round.to_string(), 642 | third: uuid.clone(), 643 | fourth: "".to_string(), 644 | }; 645 | let index = Index { key }; 646 | loop { 647 | // add delay to allow the server to process request: 648 | thread::sleep(delay); 649 | let res_body = postb(client, "get", index.clone()).unwrap(); 650 | let answer: Result = serde_json::from_str(&res_body).unwrap(); 651 | if answer.is_ok() { 652 | ans_vec.push(answer.unwrap().value); 653 | println!("party {:?} {:?} read success", i, round); 654 | break; 655 | } 656 | } 657 | } 658 | } 659 | ans_vec 660 | } 661 | 662 | pub fn poll_for_p2p( 663 | client: &Client, 664 | party_num: u32, 665 | n: u32, 666 | delay: Duration, 667 | round: &str, 668 | uuid: String, 669 | ) -> Vec { 670 | let mut ans_vec = Vec::new(); 671 | for i in 1..n + 1 { 672 | if i != party_num { 673 | let key = TupleKey { 674 | first: i.to_string(), 675 | second: round.to_string(), 676 | third: uuid.clone(), 677 | fourth: party_num.to_string(), 678 | }; 679 | let index = Index { key }; 680 | loop { 681 | // add delay to allow the server to process request: 682 | thread::sleep(delay); 683 | let res_body = postb(client, "get", index.clone()).unwrap(); 684 | let answer: Result = serde_json::from_str(&res_body).unwrap(); 685 | if answer.is_ok() { 686 | ans_vec.push(answer.unwrap().value); 687 | println!("party {:?} {:?} read success", i, round); 688 | break; 689 | } 690 | } 691 | } 692 | } 693 | ans_vec 694 | } 695 | -------------------------------------------------------------------------------- /src/bin/sm_manager.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_hygiene, decl_macro)] 2 | 3 | #[macro_use] 4 | extern crate rocket; 5 | 6 | extern crate reqwest; 7 | extern crate rocket_contrib; 8 | extern crate uuid; 9 | 10 | #[macro_use] 11 | extern crate serde_derive; 12 | extern crate serde; 13 | extern crate serde_json; 14 | 15 | use rocket::State; 16 | use rocket_contrib::json::Json; 17 | use std::collections::HashMap; 18 | use std::fs; 19 | use std::str; 20 | use std::sync::RwLock; 21 | use uuid::Uuid; 22 | 23 | #[derive(Hash, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] 24 | pub struct TupleKey { 25 | pub first: String, 26 | pub second: String, 27 | pub third: String, 28 | pub fourth: String, 29 | } 30 | 31 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 32 | pub struct PartySignup { 33 | pub number: u32, 34 | pub uuid: String, 35 | } 36 | 37 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 38 | pub struct Index { 39 | pub key: TupleKey, 40 | } 41 | 42 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 43 | pub struct Entry { 44 | pub key: TupleKey, 45 | pub value: String, 46 | } 47 | #[derive(Serialize, Deserialize)] 48 | pub struct Params { 49 | pub parties: String, 50 | pub threshold: String, 51 | } 52 | #[post("/get", format = "json", data = "")] 53 | fn get( 54 | db_mtx: State>>, 55 | request: Json, 56 | ) -> Json> { 57 | let index: Index = request.0; 58 | let hm = db_mtx.read().unwrap(); 59 | match hm.get(&index.key) { 60 | Some(v) => { 61 | let entry = Entry { 62 | key: index.key, 63 | value: format!("{}", v.clone()), 64 | }; 65 | Json(Ok(entry)) 66 | } 67 | None => Json(Err(())), 68 | } 69 | } 70 | 71 | #[post("/set", format = "json", data = "")] 72 | fn set( 73 | db_mtx: State>>, 74 | request: Json, 75 | ) -> Json> { 76 | let entry: Entry = request.0; 77 | let mut hm = db_mtx.write().unwrap(); 78 | hm.insert(entry.key.clone(), entry.value.clone()); 79 | Json(Ok(())) 80 | } 81 | 82 | #[post("/signupkeygen", format = "json")] 83 | fn signup_keygen( 84 | db_mtx: State>>, 85 | ) -> Json> { 86 | let data = fs::read_to_string("params") 87 | .expect("Unable to read params, make sure config file is present in the same folder "); 88 | let params: Params = serde_json::from_str(&data).unwrap(); 89 | let parties: u32 = params.parties.parse::().unwrap(); 90 | let key = TupleKey { 91 | first: "signup".to_string(), 92 | second: "keygen".to_string(), 93 | third: "".to_string(), 94 | fourth: "".to_string(), 95 | }; 96 | let party_signup: PartySignup; 97 | { 98 | let hm = db_mtx.read().unwrap(); 99 | let value = hm.get(&key).unwrap(); 100 | let party_i_minus1_signup: PartySignup = serde_json::from_str(&value).unwrap(); 101 | if party_i_minus1_signup.number < parties { 102 | let party_num = party_i_minus1_signup.number + 1; 103 | party_signup = PartySignup { 104 | number: party_num.clone(), 105 | uuid: party_i_minus1_signup.uuid, 106 | }; 107 | } else { 108 | let uuid = Uuid::new_v4().to_string(); 109 | let party1 = 1; 110 | party_signup = PartySignup { 111 | number: party1, 112 | uuid, 113 | }; 114 | } 115 | } 116 | let mut hm = db_mtx.write().unwrap(); 117 | hm.insert(key, serde_json::to_string(&party_signup).unwrap()); 118 | return Json(Ok(party_signup)); 119 | } 120 | 121 | #[post("/signupsign", format = "json")] 122 | fn signup_sign(db_mtx: State>>) -> Json> { 123 | //read parameters: 124 | let data = fs::read_to_string("params") 125 | .expect("Unable to read params, make sure config file is present in the same folder "); 126 | let params: Params = serde_json::from_str(&data).unwrap(); 127 | let threshold: u32 = params.threshold.parse::().unwrap(); 128 | let key = TupleKey { 129 | first: "signup".to_string(), 130 | second: "sign".to_string(), 131 | third: "".to_string(), 132 | fourth: "".to_string(), 133 | }; 134 | let party_signup: PartySignup; 135 | { 136 | let hm = db_mtx.read().unwrap(); 137 | let value = hm.get(&key).unwrap(); 138 | let party_i_minus1_signup: PartySignup = serde_json::from_str(&value).unwrap(); 139 | if party_i_minus1_signup.number < threshold + 1 { 140 | let party_num = party_i_minus1_signup.number + 1; 141 | party_signup = PartySignup { 142 | number: party_num.clone(), 143 | uuid: party_i_minus1_signup.uuid, 144 | }; 145 | } else { 146 | let uuid = Uuid::new_v4().to_string(); 147 | let party1 = 1; 148 | party_signup = PartySignup { 149 | number: party1, 150 | uuid, 151 | }; 152 | } 153 | } 154 | let mut hm = db_mtx.write().unwrap(); 155 | hm.insert(key, serde_json::to_string(&party_signup).unwrap()); 156 | return Json(Ok(party_signup)); 157 | } 158 | 159 | //refcell, arc 160 | 161 | fn main() { 162 | // let mut my_config = Config::development(); 163 | // my_config.set_port(18001); 164 | let db: HashMap = HashMap::new(); 165 | let db_mtx = RwLock::new(db); 166 | //rocket::custom(my_config).mount("/", routes![get, set]).manage(db_mtx).launch(); 167 | 168 | ///////////////////////////////////////////////////////////////// 169 | //////////////////////////init signups:////////////////////////// 170 | ///////////////////////////////////////////////////////////////// 171 | 172 | let keygen_key = TupleKey { 173 | first: "signup".to_string(), 174 | second: "keygen".to_string(), 175 | third: "".to_string(), 176 | fourth: "".to_string(), 177 | }; 178 | let sign_key = TupleKey { 179 | first: "signup".to_string(), 180 | second: "sign".to_string(), 181 | third: "".to_string(), 182 | fourth: "".to_string(), 183 | }; 184 | let uuid_keygen = Uuid::new_v4().to_string(); 185 | let uuid_sign = Uuid::new_v4().to_string(); 186 | 187 | let party1 = 0; 188 | let party_signup_keygen = PartySignup { 189 | number: party1.clone(), 190 | uuid: uuid_keygen, 191 | }; 192 | let party_signup_sign = PartySignup { 193 | number: party1.clone(), 194 | uuid: uuid_sign, 195 | }; 196 | { 197 | let mut hm = db_mtx.write().unwrap(); 198 | hm.insert( 199 | keygen_key, 200 | serde_json::to_string(&party_signup_keygen).unwrap(), 201 | ); 202 | hm.insert(sign_key, serde_json::to_string(&party_signup_sign).unwrap()); 203 | } 204 | ///////////////////////////////////////////////////////////////// 205 | rocket::ignite() 206 | .mount("/", routes![get, set, signup_keygen, signup_sign]) 207 | .manage(db_mtx) 208 | .launch(); 209 | } 210 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Multi-party ECDSA 3 | 4 | Copyright 2018 by Kzen Networks 5 | 6 | This file is part of Multi-party ECDSA library 7 | (https://github.com/KZen-networks/multi-party-ecdsa) 8 | 9 | Multi-party ECDSA is free software: you can redistribute 10 | it and/or modify it under the terms of the GNU General Public 11 | License as published by the Free Software Foundation, either 12 | version 3 of the License, or (at your option) any later version. 13 | 14 | @license GPL-3.0+ 15 | */ 16 | 17 | #[macro_use] 18 | extern crate serde_derive; 19 | extern crate serde; 20 | extern crate serde_json; 21 | extern crate subtle; 22 | 23 | extern crate centipede; 24 | extern crate curv; 25 | extern crate paillier; 26 | extern crate zk_paillier; 27 | pub mod protocols; 28 | 29 | #[derive(Copy, PartialEq, Eq, Clone, Debug)] 30 | pub enum Error { 31 | InvalidKey, 32 | InvalidSS, 33 | InvalidCom, 34 | InvalidSig, 35 | } 36 | -------------------------------------------------------------------------------- /src/protocols/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Multi-party ECDSA 3 | 4 | Copyright 2018 by Kzen Networks 5 | 6 | This file is part of Multi-party ECDSA library 7 | (https://github.com/KZen-networks/multi-party-ecdsa) 8 | 9 | Multi-party ECDSA is free software: you can redistribute 10 | it and/or modify it under the terms of the GNU General Public 11 | License as published by the Free Software Foundation, either 12 | version 3 of the License, or (at your option) any later version. 13 | 14 | @license GPL-3.0+ 15 | */ 16 | 17 | pub mod multi_party_ecdsa; 18 | pub mod two_party_ecdsa; 19 | -------------------------------------------------------------------------------- /src/protocols/multi_party_ecdsa/gg_2018/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Multi-party ECDSA 3 | 4 | Copyright 2018 by Kzen Networks 5 | 6 | This file is part of Multi-party ECDSA library 7 | (https://github.com/KZen-networks/multi-party-ecdsa) 8 | 9 | Multi-party ECDSA is free software: you can redistribute 10 | it and/or modify it under the terms of the GNU General Public 11 | License as published by the Free Software Foundation, either 12 | version 3 of the License, or (at your option) any later version. 13 | 14 | @license GPL-3.0+ 15 | */ 16 | 17 | pub mod mta; 18 | pub mod party_i; 19 | pub mod test; 20 | -------------------------------------------------------------------------------- /src/protocols/multi_party_ecdsa/gg_2018/mta.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Multi-party ECDSA 3 | 4 | Copyright 2018 by Kzen Networks 5 | 6 | This file is part of Multi-party ECDSA library 7 | (https://github.com/KZen-networks/multi-party-ecdsa) 8 | 9 | Multi-party ECDSA is free software: you can redistribute 10 | it and/or modify it under the terms of the GNU General Public 11 | License as published by the Free Software Foundation, either 12 | version 3 of the License, or (at your option) any later version. 13 | 14 | @license GPL-3.0+ 15 | */ 16 | use curv::arithmetic::traits::Samplable; 17 | use curv::cryptographic_primitives::proofs::sigma_dlog::{DLogProof, ProveDLog}; 18 | use curv::elliptic::curves::traits::*; 19 | use curv::BigInt; 20 | use curv::FE; 21 | use curv::GE; 22 | use paillier::{Add, Decrypt, Encrypt, Mul}; 23 | use paillier::{DecryptionKey, EncryptionKey, Paillier, RawCiphertext, RawPlaintext}; 24 | 25 | use protocols::multi_party_ecdsa::gg_2018::party_i::PartyPrivate; 26 | use Error::{self, InvalidKey}; 27 | 28 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 29 | pub struct MessageA { 30 | pub c: BigInt, // paillier encryption 31 | } 32 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 33 | pub struct MessageB { 34 | pub c: BigInt, // paillier encryption 35 | pub b_proof: DLogProof, 36 | pub beta_tag_proof: DLogProof, 37 | } 38 | 39 | impl MessageA { 40 | pub fn a(a: &FE, alice_ek: &EncryptionKey) -> MessageA { 41 | let c_a = Paillier::encrypt(alice_ek, RawPlaintext::from(a.to_big_int())); 42 | MessageA { 43 | c: c_a.0.clone().into_owned(), 44 | } 45 | } 46 | } 47 | 48 | impl MessageB { 49 | pub fn b(b: &FE, alice_ek: &EncryptionKey, c_a: MessageA) -> (MessageB, FE) { 50 | let beta_tag = BigInt::sample_below(&alice_ek.n); 51 | let beta_tag_fe: FE = ECScalar::from(&beta_tag); 52 | let c_beta_tag = Paillier::encrypt(alice_ek, RawPlaintext::from(beta_tag)); 53 | 54 | let b_bn = b.to_big_int(); 55 | let b_c_a = Paillier::mul( 56 | alice_ek, 57 | RawCiphertext::from(c_a.c), 58 | RawPlaintext::from(b_bn), 59 | ); 60 | let c_b = Paillier::add(alice_ek, b_c_a, c_beta_tag); 61 | let beta = FE::zero().sub(&beta_tag_fe.get_element()); 62 | let dlog_proof_b = DLogProof::prove(b); 63 | let dlog_proof_beta_tag = DLogProof::prove(&beta_tag_fe); 64 | 65 | ( 66 | MessageB { 67 | c: c_b.0.clone().into_owned(), 68 | b_proof: dlog_proof_b, 69 | beta_tag_proof: dlog_proof_beta_tag, 70 | }, 71 | beta, 72 | ) 73 | } 74 | 75 | pub fn verify_proofs_get_alpha(&self, dk: &DecryptionKey, a: &FE) -> Result { 76 | let alice_share = Paillier::decrypt(dk, &RawCiphertext::from(self.c.clone())); 77 | let g: GE = ECPoint::generator(); 78 | let alpha: FE = ECScalar::from(&alice_share.0); 79 | let g_alpha = g * α 80 | let ba_btag = &self.b_proof.pk * a + &self.beta_tag_proof.pk; 81 | match DLogProof::verify(&self.b_proof).is_ok() 82 | && DLogProof::verify(&self.beta_tag_proof).is_ok() 83 | && ba_btag.get_element() == g_alpha.get_element() 84 | { 85 | true => Ok(alpha), 86 | false => Err(InvalidKey), 87 | } 88 | } 89 | 90 | // another version, supportion PartyPrivate therefore binding mta to gg18. 91 | // with the regular version mta can be used in general 92 | pub fn verify_proofs_get_alpha_gg18( 93 | &self, 94 | private: &PartyPrivate, 95 | a: &FE, 96 | ) -> Result { 97 | let alice_share = private.decrypt(self.c.clone()); 98 | let g: GE = ECPoint::generator(); 99 | let alpha: FE = ECScalar::from(&alice_share.0); 100 | let g_alpha = g * α 101 | let ba_btag = &self.b_proof.pk * a + &self.beta_tag_proof.pk; 102 | 103 | match DLogProof::verify(&self.b_proof).is_ok() 104 | && DLogProof::verify(&self.beta_tag_proof).is_ok() 105 | && ba_btag.get_element() == g_alpha.get_element() 106 | { 107 | true => Ok(alpha), 108 | false => Err(InvalidKey), 109 | } 110 | } 111 | 112 | pub fn verify_b_against_public(public_gb: &GE, mta_gb: &GE) -> bool { 113 | public_gb.get_element() == mta_gb.get_element() 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/protocols/multi_party_ecdsa/gg_2018/party_i.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | /* 4 | Multi-party ECDSA 5 | 6 | Copyright 2018 by Kzen Networks 7 | 8 | This file is part of Multi-party ECDSA library 9 | (https://github.com/KZen-networks/multi-party-ecdsa) 10 | 11 | Multi-party ECDSA is free software: you can redistribute 12 | it and/or modify it under the terms of the GNU General Public 13 | License as published by the Free Software Foundation, either 14 | version 3 of the License, or (at your option) any later version. 15 | 16 | @license GPL-3.0+ 17 | */ 18 | 19 | use paillier::KeyGeneration; 20 | use paillier::Paillier; 21 | use paillier::{DecryptionKey, EncryptionKey}; 22 | use zk_paillier::zkproofs::NICorrectKeyProof; 23 | use Error::{self, InvalidCom, InvalidKey, InvalidSS, InvalidSig}; 24 | 25 | use curv::arithmetic::traits::*; 26 | 27 | use curv::elliptic::curves::traits::*; 28 | 29 | use centipede::juggling::proof_system::{Helgamalsegmented, Witness}; 30 | use centipede::juggling::segmentation::Msegmentation; 31 | use curv::cryptographic_primitives::commitments::hash_commitment::HashCommitment; 32 | use curv::cryptographic_primitives::commitments::traits::Commitment; 33 | use curv::cryptographic_primitives::hashing::hash_sha256::HSha256; 34 | use curv::cryptographic_primitives::hashing::traits::Hash; 35 | use curv::cryptographic_primitives::proofs::sigma_correct_homomorphic_elgamal_enc::*; 36 | use curv::cryptographic_primitives::proofs::sigma_dlog::{DLogProof, ProveDLog}; 37 | use curv::cryptographic_primitives::secret_sharing::feldman_vss::VerifiableSS; 38 | use curv::BigInt; 39 | use curv::FE; 40 | use curv::GE; 41 | use paillier::{Decrypt, RawCiphertext, RawPlaintext}; 42 | 43 | const SECURITY: usize = 256; 44 | 45 | #[derive(Clone, Serialize, Deserialize)] 46 | pub struct Keys { 47 | pub u_i: FE, 48 | pub y_i: GE, 49 | pub dk: DecryptionKey, 50 | pub ek: EncryptionKey, 51 | pub party_index: usize, 52 | } 53 | 54 | #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] 55 | pub struct PartyPrivate { 56 | u_i: FE, 57 | x_i: FE, 58 | dk: DecryptionKey, 59 | } 60 | 61 | #[derive(Clone, Serialize, Deserialize)] 62 | pub struct KeyGenBroadcastMessage1 { 63 | pub e: EncryptionKey, 64 | pub com: BigInt, 65 | pub correct_key_proof: NICorrectKeyProof, 66 | } 67 | 68 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 69 | pub struct KeyGenDecommitMessage1 { 70 | pub blind_factor: BigInt, 71 | pub y_i: GE, 72 | } 73 | 74 | #[derive(Debug)] 75 | pub struct Parameters { 76 | pub threshold: usize, //t 77 | pub share_count: usize, //n 78 | } 79 | 80 | #[derive(Clone, Serialize, Deserialize)] 81 | pub struct SharedKeys { 82 | pub y: GE, 83 | pub x_i: FE, 84 | } 85 | 86 | #[derive(Clone, Copy, Serialize, Deserialize)] 87 | pub struct SignKeys { 88 | pub w_i: FE, 89 | pub g_w_i: GE, 90 | pub k_i: FE, 91 | pub gamma_i: FE, 92 | pub g_gamma_i: GE, 93 | } 94 | 95 | #[derive(Clone, Serialize, Deserialize)] 96 | pub struct SignBroadcastPhase1 { 97 | pub com: BigInt, 98 | } 99 | 100 | #[derive(Clone, Serialize, Deserialize)] 101 | pub struct SignDecommitPhase1 { 102 | pub blind_factor: BigInt, 103 | pub g_gamma_i: GE, 104 | } 105 | 106 | #[derive(Clone, Serialize, Deserialize)] 107 | pub struct LocalSignature { 108 | pub l_i: FE, 109 | pub rho_i: FE, 110 | pub R: GE, 111 | pub s_i: FE, 112 | pub m: BigInt, 113 | pub y: GE, 114 | } 115 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 116 | pub struct Phase5Com1 { 117 | pub com: BigInt, 118 | } 119 | 120 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 121 | pub struct Phase5Com2 { 122 | pub com: BigInt, 123 | } 124 | 125 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 126 | pub struct Phase5ADecom1 { 127 | pub V_i: GE, 128 | pub A_i: GE, 129 | pub B_i: GE, 130 | pub blind_factor: BigInt, 131 | } 132 | 133 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 134 | pub struct Phase5DDecom2 { 135 | pub u_i: GE, 136 | pub t_i: GE, 137 | pub blind_factor: BigInt, 138 | } 139 | 140 | #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] 141 | pub struct Signature { 142 | pub r: FE, 143 | pub s: FE, 144 | } 145 | 146 | impl Keys { 147 | pub fn create(index: usize) -> Keys { 148 | let u: FE = ECScalar::new_random(); 149 | let y = &ECPoint::generator() * &u; 150 | let (ek, dk) = Paillier::keypair().keys(); 151 | 152 | Keys { 153 | u_i: u, 154 | y_i: y, 155 | dk, 156 | ek, 157 | party_index: index.clone(), 158 | } 159 | } 160 | 161 | pub fn create_from(u: FE, index: usize) -> Keys { 162 | let y = &ECPoint::generator() * &u; 163 | let (ek, dk) = Paillier::keypair().keys(); 164 | 165 | Keys { 166 | u_i: u, 167 | y_i: y, 168 | dk, 169 | ek, 170 | party_index: index.clone(), 171 | } 172 | } 173 | 174 | pub fn phase1_broadcast_phase3_proof_of_correct_key( 175 | &self, 176 | ) -> (KeyGenBroadcastMessage1, KeyGenDecommitMessage1) { 177 | let blind_factor = BigInt::sample(SECURITY); 178 | let correct_key_proof = NICorrectKeyProof::proof(&self.dk); 179 | let com = HashCommitment::create_commitment_with_user_defined_randomness( 180 | &self.y_i.bytes_compressed_to_big_int(), 181 | &blind_factor, 182 | ); 183 | let bcm1 = KeyGenBroadcastMessage1 { 184 | e: self.ek.clone(), 185 | com, 186 | correct_key_proof, 187 | }; 188 | let decom1 = KeyGenDecommitMessage1 { 189 | blind_factor, 190 | y_i: self.y_i.clone(), 191 | }; 192 | (bcm1, decom1) 193 | } 194 | 195 | pub fn phase1_verify_com_phase3_verify_correct_key_phase2_distribute( 196 | &self, 197 | params: &Parameters, 198 | decom_vec: &Vec, 199 | bc1_vec: &Vec, 200 | ) -> Result<(VerifiableSS, Vec, usize), Error> { 201 | // test length: 202 | assert_eq!(decom_vec.len(), params.share_count); 203 | assert_eq!(bc1_vec.len(), params.share_count); 204 | // test paillier correct key and test decommitments 205 | let correct_key_correct_decom_all = (0..bc1_vec.len()) 206 | .map(|i| { 207 | HashCommitment::create_commitment_with_user_defined_randomness( 208 | &decom_vec[i].y_i.bytes_compressed_to_big_int(), 209 | &decom_vec[i].blind_factor, 210 | ) == bc1_vec[i].com 211 | && bc1_vec[i].correct_key_proof.verify(&bc1_vec[i].e).is_ok() 212 | }) 213 | .all(|x| x == true); 214 | 215 | let (vss_scheme, secret_shares) = 216 | VerifiableSS::share(params.threshold, params.share_count, &self.u_i); 217 | match correct_key_correct_decom_all { 218 | true => Ok((vss_scheme, secret_shares, self.party_index.clone())), 219 | false => Err(InvalidKey), 220 | } 221 | } 222 | 223 | pub fn phase2_verify_vss_construct_keypair_phase3_pok_dlog( 224 | &self, 225 | params: &Parameters, 226 | y_vec: &Vec, 227 | secret_shares_vec: &Vec, 228 | vss_scheme_vec: &Vec, 229 | index: &usize, 230 | ) -> Result<(SharedKeys, DLogProof), Error> { 231 | assert_eq!(y_vec.len(), params.share_count); 232 | assert_eq!(secret_shares_vec.len(), params.share_count); 233 | assert_eq!(vss_scheme_vec.len(), params.share_count); 234 | 235 | let correct_ss_verify = (0..y_vec.len()) 236 | .map(|i| { 237 | vss_scheme_vec[i] 238 | .validate_share(&secret_shares_vec[i], *index) 239 | .is_ok() 240 | && vss_scheme_vec[i].commitments[0].get_element() == y_vec[i].get_element() 241 | }) 242 | .all(|x| x == true); 243 | 244 | match correct_ss_verify { 245 | true => { 246 | let mut y_vec_iter = y_vec.iter(); 247 | let y0 = y_vec_iter.next().unwrap(); 248 | let y = y_vec_iter.fold(y0.clone(), |acc, x| acc + x); 249 | let x_i = secret_shares_vec.iter().fold(FE::zero(), |acc, x| acc + x); 250 | let dlog_proof = DLogProof::prove(&x_i); 251 | Ok((SharedKeys { y, x_i }, dlog_proof)) 252 | } 253 | false => Err(InvalidSS), 254 | } 255 | } 256 | 257 | pub fn get_commitments_to_xi(vss_scheme_vec: &Vec) -> Vec { 258 | let len = vss_scheme_vec.len(); 259 | let xi_points_vec = (1..len + 1) 260 | .map(|i| { 261 | let xij_points_vec = (0..len) 262 | .map(|j| vss_scheme_vec[j].get_point_commitment(i)) 263 | .collect::>(); 264 | 265 | let mut xij_points_iter = xij_points_vec.iter(); 266 | let first = xij_points_iter.next().unwrap(); 267 | 268 | let tail = xij_points_iter; 269 | tail.fold(first.clone(), |acc, x| acc + x) 270 | }) 271 | .collect::>(); 272 | 273 | xi_points_vec 274 | } 275 | 276 | pub fn update_commitments_to_xi( 277 | comm: &GE, 278 | vss_scheme: &VerifiableSS, 279 | index: usize, 280 | s: &Vec, 281 | ) -> GE { 282 | let li = vss_scheme.map_share_to_new_params(index, s); 283 | comm * &li 284 | } 285 | 286 | pub fn verify_dlog_proofs( 287 | params: &Parameters, 288 | dlog_proofs_vec: &Vec, 289 | y_vec: &Vec, 290 | ) -> Result<(), Error> { 291 | assert_eq!(y_vec.len(), params.share_count); 292 | assert_eq!(dlog_proofs_vec.len(), params.share_count); 293 | let xi_dlog_verify = (0..y_vec.len()) 294 | .map(|i| DLogProof::verify(&dlog_proofs_vec[i]).is_ok()) 295 | .all(|x| x == true); 296 | 297 | match xi_dlog_verify { 298 | true => Ok(()), 299 | false => Err(InvalidKey), 300 | } 301 | } 302 | } 303 | 304 | impl PartyPrivate { 305 | pub fn set_private(key: Keys, shared_key: SharedKeys) -> PartyPrivate { 306 | let key_private = PartyPrivate { 307 | u_i: key.u_i, 308 | x_i: shared_key.x_i, 309 | dk: key.dk, 310 | }; 311 | key_private 312 | } 313 | 314 | pub fn y_i(&self) -> GE { 315 | let g: GE = ECPoint::generator(); 316 | g * self.u_i 317 | } 318 | 319 | pub fn decrypt(&self, ciphertext: BigInt) -> RawPlaintext { 320 | Paillier::decrypt(&self.dk, &RawCiphertext::from(ciphertext)) 321 | } 322 | 323 | pub fn refresh_private_key(&self, factor: &FE, index: usize) -> Keys { 324 | let u: FE = self.u_i + factor; 325 | let y = &ECPoint::generator() * &u; 326 | let (ek, dk) = Paillier::keypair().keys(); 327 | 328 | Keys { 329 | u_i: u, 330 | y_i: y, 331 | dk, 332 | ek, 333 | party_index: index.clone(), 334 | } 335 | } 336 | 337 | // used for verifiable recovery 338 | pub fn to_encrypted_segment( 339 | &self, 340 | segment_size: &usize, 341 | num_of_segments: usize, 342 | pub_ke_y: &GE, 343 | g: &GE, 344 | ) -> (Witness, Helgamalsegmented) { 345 | Msegmentation::to_encrypted_segments(&self.u_i, &segment_size, num_of_segments, pub_ke_y, g) 346 | } 347 | 348 | pub fn update_private_key(&self, factor_u_i: &FE, factor_x_i: &FE) -> Self { 349 | PartyPrivate { 350 | u_i: self.u_i + factor_u_i, 351 | x_i: self.x_i + factor_x_i, 352 | dk: self.dk.clone(), 353 | } 354 | } 355 | } 356 | 357 | impl SignKeys { 358 | pub fn create( 359 | private: &PartyPrivate, 360 | vss_scheme: &VerifiableSS, 361 | index: usize, 362 | s: &Vec, 363 | ) -> SignKeys { 364 | let li = vss_scheme.map_share_to_new_params(index, s); 365 | let w_i = li * &private.x_i; 366 | let g: GE = ECPoint::generator(); 367 | let g_w_i = &g * &w_i; 368 | let gamma_i: FE = ECScalar::new_random(); 369 | let g_gamma_i = &g * &gamma_i; 370 | SignKeys { 371 | w_i, 372 | g_w_i, 373 | k_i: ECScalar::new_random(), 374 | gamma_i, 375 | g_gamma_i, 376 | } 377 | } 378 | 379 | pub fn phase1_broadcast(&self) -> (SignBroadcastPhase1, SignDecommitPhase1) { 380 | let blind_factor = BigInt::sample(SECURITY); 381 | let g: GE = ECPoint::generator(); 382 | let g_gamma_i = g * &self.gamma_i; 383 | let com = HashCommitment::create_commitment_with_user_defined_randomness( 384 | &g_gamma_i.bytes_compressed_to_big_int(), 385 | &blind_factor, 386 | ); 387 | 388 | ( 389 | SignBroadcastPhase1 { com }, 390 | SignDecommitPhase1 { 391 | blind_factor, 392 | g_gamma_i: self.g_gamma_i.clone(), 393 | }, 394 | ) 395 | } 396 | 397 | pub fn phase2_delta_i(&self, alpha_vec: &Vec, beta_vec: &Vec) -> FE { 398 | let vec_len = alpha_vec.len(); 399 | assert_eq!(alpha_vec.len(), beta_vec.len()); 400 | // assert_eq!(alpha_vec.len(), self.s.len() - 1); 401 | let ki_gamma_i = self.k_i.mul(&self.gamma_i.get_element()); 402 | let sum = (0..vec_len) 403 | .map(|i| alpha_vec[i].add(&beta_vec[i].get_element())) 404 | .fold(ki_gamma_i, |acc, x| acc + x); 405 | return sum; 406 | } 407 | 408 | pub fn phase2_sigma_i(&self, miu_vec: &Vec, ni_vec: &Vec) -> FE { 409 | let vec_len = miu_vec.len(); 410 | assert_eq!(miu_vec.len(), ni_vec.len()); 411 | //assert_eq!(miu_vec.len(), self.s.len() - 1); 412 | let ki_w_i = self.k_i.mul(&self.w_i.get_element()); 413 | let sum = (0..vec_len) 414 | .map(|i| miu_vec[i].add(&ni_vec[i].get_element())) 415 | .fold(ki_w_i, |acc, x| acc + x); 416 | return sum; 417 | } 418 | 419 | pub fn phase3_reconstruct_delta(delta_vec: &Vec) -> FE { 420 | let sum = delta_vec.iter().fold(FE::zero(), |acc, x| acc + x); 421 | sum.invert() 422 | } 423 | 424 | pub fn phase4( 425 | delta_inv: &FE, 426 | b_proof_vec: &Vec<&DLogProof>, 427 | phase1_decommit_vec: Vec, 428 | // blind_vec: &Vec, 429 | // g_gamma_i_vec: &Vec, 430 | bc1_vec: &Vec, 431 | ) -> Result { 432 | let test_b_vec_and_com = (0..b_proof_vec.len()) 433 | .map(|i| { 434 | b_proof_vec[i].pk.get_element() == phase1_decommit_vec[i].g_gamma_i.get_element() 435 | && HashCommitment::create_commitment_with_user_defined_randomness( 436 | &phase1_decommit_vec[i] 437 | .g_gamma_i 438 | .bytes_compressed_to_big_int(), 439 | &phase1_decommit_vec[i].blind_factor, 440 | ) == bc1_vec[i].com 441 | }) 442 | .all(|x| x == true); 443 | 444 | let mut g_gamma_i_iter = phase1_decommit_vec.iter(); 445 | let head = g_gamma_i_iter.next().unwrap(); 446 | let tail = g_gamma_i_iter; 447 | match test_b_vec_and_com { 448 | true => Ok({ 449 | let gamma_sum = tail.fold(head.g_gamma_i.clone(), |acc, x| acc + &x.g_gamma_i); 450 | let R = gamma_sum * delta_inv; 451 | R 452 | }), 453 | false => Err(InvalidKey), 454 | } 455 | } 456 | } 457 | 458 | impl LocalSignature { 459 | pub fn phase5_local_sig( 460 | k_i: &FE, 461 | message: &BigInt, 462 | R: &GE, 463 | sigma_i: &FE, 464 | pubkey: &GE, 465 | ) -> LocalSignature { 466 | let m_fe: FE = ECScalar::from(message); 467 | let r: FE = ECScalar::from(&R.x_coor().unwrap().mod_floor(&FE::q())); 468 | let s_i = m_fe * k_i + r * sigma_i; 469 | let l_i: FE = ECScalar::new_random(); 470 | let rho_i: FE = ECScalar::new_random(); 471 | LocalSignature { 472 | l_i, 473 | rho_i, 474 | R: R.clone(), 475 | s_i, 476 | m: message.clone(), 477 | y: pubkey.clone(), 478 | } 479 | } 480 | 481 | pub fn phase5a_broadcast_5b_zkproof(&self) -> (Phase5Com1, Phase5ADecom1, HomoELGamalProof) { 482 | let blind_factor = BigInt::sample(SECURITY); 483 | let g: GE = ECPoint::generator(); 484 | let A_i = &g * &self.rho_i; 485 | let l_i_rho_i = self.l_i.mul(&self.rho_i.get_element()); 486 | let B_i = &g * &l_i_rho_i; 487 | let V_i = &self.R * &self.s_i + &g * &self.l_i; 488 | let input_hash = HSha256::create_hash_from_ge(&[&V_i, &A_i, &B_i]).to_big_int(); 489 | let com = HashCommitment::create_commitment_with_user_defined_randomness( 490 | &input_hash, 491 | &blind_factor, 492 | ); 493 | let witness = HomoElGamalWitness { 494 | r: self.l_i.clone(), 495 | x: self.s_i.clone(), 496 | }; 497 | let delta = HomoElGamalStatement { 498 | G: A_i.clone(), 499 | H: self.R.clone(), 500 | Y: g, 501 | D: V_i.clone(), 502 | E: B_i.clone(), 503 | }; 504 | let proof = HomoELGamalProof::prove(&witness, &delta); 505 | 506 | ( 507 | Phase5Com1 { com }, 508 | Phase5ADecom1 { 509 | V_i, 510 | A_i, 511 | B_i, 512 | blind_factor, 513 | }, 514 | proof, 515 | ) 516 | } 517 | 518 | pub fn phase5c( 519 | &self, 520 | decom_vec: &Vec, 521 | com_vec: &Vec, 522 | elgamal_proofs: &Vec, 523 | v_i: &GE, 524 | R: &GE, 525 | ) -> Result<(Phase5Com2, Phase5DDecom2), Error> { 526 | assert_eq!(decom_vec.len(), com_vec.len()); 527 | 528 | let g: GE = ECPoint::generator(); 529 | let test_com_elgamal = (0..com_vec.len()) 530 | .map(|i| { 531 | let delta = HomoElGamalStatement { 532 | G: decom_vec[i].A_i.clone(), 533 | H: R.clone(), 534 | Y: g.clone(), 535 | D: decom_vec[i].V_i.clone(), 536 | E: decom_vec[i].B_i.clone(), 537 | }; 538 | let input_hash = HSha256::create_hash_from_ge(&[ 539 | &decom_vec[i].V_i, 540 | &decom_vec[i].A_i, 541 | &decom_vec[i].B_i, 542 | ]) 543 | .to_big_int(); 544 | 545 | HashCommitment::create_commitment_with_user_defined_randomness( 546 | &input_hash, 547 | &decom_vec[i].blind_factor, 548 | ) == com_vec[i].com 549 | && elgamal_proofs[i].verify(&delta).is_ok() 550 | }) 551 | .all(|x| x == true); 552 | 553 | let v_vec = (0..com_vec.len()) 554 | .map(|i| &decom_vec[i].V_i) 555 | .collect::>(); 556 | let a_vec = (0..com_vec.len()) 557 | .map(|i| &decom_vec[i].A_i) 558 | .collect::>(); 559 | 560 | let v = v_vec.iter().fold(v_i.clone(), |acc, x| acc + *x); 561 | // V = -mG -ry - sum (vi) 562 | let mut a_i_iter = a_vec.iter(); 563 | let head = a_i_iter.next().unwrap(); 564 | let tail = a_i_iter; 565 | let a = tail.fold((*head).clone(), |acc, x| acc.add_point(&(*x).get_element())); 566 | 567 | let r: FE = ECScalar::from(&self.R.x_coor().unwrap().mod_floor(&FE::q())); 568 | let yr = &self.y * &r; 569 | let g: GE = ECPoint::generator(); 570 | let m_fe: FE = ECScalar::from(&self.m); 571 | let gm = &g * &m_fe; 572 | let v = v.sub_point(&gm.get_element()).sub_point(&yr.get_element()); 573 | let u_i = &v * &self.rho_i; 574 | let t_i = &a * &self.l_i; 575 | let input_hash = HSha256::create_hash_from_ge(&[&u_i, &t_i]).to_big_int(); 576 | let blind_factor = BigInt::sample(SECURITY); 577 | let com = HashCommitment::create_commitment_with_user_defined_randomness( 578 | &input_hash, 579 | &blind_factor, 580 | ); 581 | 582 | match test_com_elgamal { 583 | true => Ok({ 584 | ( 585 | Phase5Com2 { com }, 586 | Phase5DDecom2 { 587 | u_i, 588 | t_i, 589 | blind_factor, 590 | }, 591 | ) 592 | }), 593 | false => Err(InvalidCom), 594 | } 595 | } 596 | 597 | pub fn phase5d( 598 | &self, 599 | decom_vec2: &Vec, 600 | com_vec2: &Vec, 601 | decom_vec1: &Vec, 602 | ) -> Result { 603 | assert_eq!(decom_vec2.len(), decom_vec1.len()); 604 | assert_eq!(decom_vec2.len(), com_vec2.len()); 605 | 606 | let test_com = (0..com_vec2.len()) 607 | .map(|i| { 608 | let input_hash = 609 | HSha256::create_hash_from_ge(&[&decom_vec2[i].u_i, &decom_vec2[i].t_i]) 610 | .to_big_int(); 611 | HashCommitment::create_commitment_with_user_defined_randomness( 612 | &input_hash, 613 | &decom_vec2[i].blind_factor, 614 | ) == com_vec2[i].com 615 | }) 616 | .all(|x| x == true); 617 | 618 | let t_vec = (0..com_vec2.len()) 619 | .map(|i| &decom_vec2[i].t_i) 620 | .collect::>(); 621 | let u_vec = (0..com_vec2.len()) 622 | .map(|i| &decom_vec2[i].u_i) 623 | .collect::>(); 624 | let b_vec = (0..decom_vec1.len()) 625 | .map(|i| &decom_vec1[i].B_i) 626 | .collect::>(); 627 | 628 | let g: GE = ECPoint::generator(); 629 | let biased_sum_tb = t_vec 630 | .iter() 631 | .zip(b_vec) 632 | .fold(g.clone(), |acc, x| acc + *x.0 + x.1); 633 | let biased_sum_tb_minus_u = u_vec 634 | .iter() 635 | .fold(biased_sum_tb, |acc, x| acc.sub_point(&x.get_element())); 636 | match test_com { 637 | true => { 638 | if g == biased_sum_tb_minus_u { 639 | Ok(self.s_i.clone()) 640 | } else { 641 | Err(InvalidKey) 642 | } 643 | } 644 | false => Err(InvalidCom), 645 | } 646 | } 647 | pub fn output_signature(&self, s_vec: &Vec) -> Result { 648 | let s = s_vec.iter().fold(self.s_i.clone(), |acc, x| acc + x); 649 | let r: FE = ECScalar::from(&self.R.x_coor().unwrap().mod_floor(&FE::q())); 650 | let sig = Signature { r, s }; 651 | let ver = verify(&sig, &self.y, &self.m).is_ok(); 652 | match ver { 653 | true => Ok(sig), 654 | false => Err(InvalidSig), 655 | } 656 | } 657 | } 658 | 659 | pub fn verify(sig: &Signature, y: &GE, message: &BigInt) -> Result<(), Error> { 660 | let b = sig.s.invert(); 661 | let a: FE = ECScalar::from(message); 662 | let u1 = a * &b; 663 | let u2 = sig.r.clone() * &b; 664 | 665 | let g: GE = ECPoint::generator(); 666 | let gu1 = &g * &u1; 667 | let yu2 = y * &u2; 668 | // can be faster using shamir trick 669 | 670 | ; 671 | if sig.r.clone() == ECScalar::from(&(gu1 + yu2).x_coor().unwrap().mod_floor(&FE::q())) { 672 | Ok(()) 673 | } else { 674 | Err(InvalidSig) 675 | } 676 | } 677 | -------------------------------------------------------------------------------- /src/protocols/multi_party_ecdsa/gg_2018/test.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | /* 4 | Multi-party ECDSA 5 | 6 | Copyright 2018 by Kzen Networks 7 | 8 | This file is part of Multi-party ECDSA library 9 | (https://github.com/KZen-networks/multi-party-ecdsa) 10 | 11 | Multi-party ECDSA is free software: you can redistribute 12 | it and/or modify it under the terms of the GNU General Public 13 | License as published by the Free Software Foundation, either 14 | version 3 of the License, or (at your option) any later version. 15 | 16 | @license GPL-3.0+ 17 | */ 18 | 19 | #[cfg(test)] 20 | mod tests { 21 | 22 | use curv::cryptographic_primitives::hashing::hash_sha256::HSha256; 23 | use curv::cryptographic_primitives::hashing::traits::Hash; 24 | use curv::cryptographic_primitives::proofs::sigma_dlog::DLogProof; 25 | use curv::cryptographic_primitives::secret_sharing::feldman_vss::VerifiableSS; 26 | use curv::elliptic::curves::traits::*; 27 | use curv::{FE, GE}; 28 | use paillier::*; 29 | use protocols::multi_party_ecdsa::gg_2018::mta::*; 30 | use protocols::multi_party_ecdsa::gg_2018::party_i::*; 31 | 32 | #[test] 33 | fn test_keygen_t1_n2() { 34 | keygen_t_n_parties(1, 2); 35 | } 36 | 37 | #[test] 38 | fn test_keygen_t2_n3() { 39 | keygen_t_n_parties(2, 3); 40 | } 41 | 42 | #[test] 43 | fn test_keygen_t2_n4() { 44 | keygen_t_n_parties(2, 4); 45 | } 46 | 47 | #[test] 48 | fn test_sign_n5_t2_ttag4() { 49 | sign(2, 5, 4, vec![0, 2, 3, 4]) 50 | } 51 | #[test] 52 | fn test_sign_n8_t4_ttag6() { 53 | sign(4, 8, 6, vec![0, 1, 2, 4, 6, 7]) 54 | } 55 | 56 | pub fn keygen_t_n_parties( 57 | t: usize, 58 | n: usize, 59 | ) -> (Vec, Vec, Vec, GE, VerifiableSS) { 60 | let parames = Parameters { 61 | threshold: t, 62 | share_count: n.clone(), 63 | }; 64 | let party_keys_vec = (0..n.clone()) 65 | .map(|i| Keys::create(i)) 66 | .collect::>(); 67 | 68 | let mut bc1_vec = Vec::new(); 69 | let mut decom_vec = Vec::new(); 70 | for i in 0..n.clone() { 71 | let (bc1, decom1) = party_keys_vec[i].phase1_broadcast_phase3_proof_of_correct_key(); 72 | bc1_vec.push(bc1); 73 | decom_vec.push(decom1); 74 | } 75 | 76 | let y_vec = (0..n.clone()) 77 | .map(|i| decom_vec[i].y_i.clone()) 78 | .collect::>(); 79 | let mut y_vec_iter = y_vec.iter(); 80 | let head = y_vec_iter.next().unwrap(); 81 | let tail = y_vec_iter; 82 | let y_sum = tail.fold(head.clone(), |acc, x| acc + x); 83 | let mut vss_scheme_vec = Vec::new(); 84 | let mut secret_shares_vec = Vec::new(); 85 | let mut index_vec = Vec::new(); 86 | for i in 0..n.clone() { 87 | let (vss_scheme, secret_shares, index) = party_keys_vec[i] 88 | .phase1_verify_com_phase3_verify_correct_key_phase2_distribute( 89 | ¶mes, &decom_vec, &bc1_vec, 90 | ) 91 | .expect("invalid key"); 92 | vss_scheme_vec.push(vss_scheme); 93 | secret_shares_vec.push(secret_shares); 94 | index_vec.push(index); 95 | } 96 | let vss_scheme_for_test = vss_scheme_vec.clone(); 97 | 98 | let party_shares = (0..n.clone()) 99 | .map(|i| { 100 | (0..n.clone()) 101 | .map(|j| { 102 | let vec_j = &secret_shares_vec[j]; 103 | vec_j[i].clone() 104 | }) 105 | .collect::>() 106 | }) 107 | .collect::>>(); 108 | 109 | let mut shared_keys_vec = Vec::new(); 110 | let mut dlog_proof_vec = Vec::new(); 111 | for i in 0..n.clone() { 112 | let (shared_keys, dlog_proof) = party_keys_vec[i] 113 | .phase2_verify_vss_construct_keypair_phase3_pok_dlog( 114 | ¶mes, 115 | &y_vec, 116 | &party_shares[i], 117 | &vss_scheme_vec, 118 | &(&index_vec[i] + 1), 119 | ) 120 | .expect("invalid vss"); 121 | shared_keys_vec.push(shared_keys); 122 | dlog_proof_vec.push(dlog_proof); 123 | } 124 | 125 | let pk_vec = (0..n.clone()) 126 | .map(|i| dlog_proof_vec[i].pk.clone()) 127 | .collect::>(); 128 | 129 | //both parties run: 130 | Keys::verify_dlog_proofs(¶mes, &dlog_proof_vec, &y_vec).expect("bad dlog proof"); 131 | 132 | //test 133 | let xi_vec = (0..t.clone() + 1) 134 | .map(|i| shared_keys_vec[i].x_i.clone()) 135 | .collect::>(); 136 | let x = vss_scheme_for_test[0] 137 | .clone() 138 | .reconstruct(&index_vec[0..t.clone() + 1], &xi_vec); 139 | let sum_u_i = party_keys_vec 140 | .iter() 141 | .fold(FE::zero(), |acc, x| acc + &x.u_i); 142 | assert_eq!(x, sum_u_i); 143 | 144 | ( 145 | party_keys_vec, 146 | shared_keys_vec, 147 | pk_vec, 148 | y_sum, 149 | vss_scheme_for_test[0].clone(), 150 | ) 151 | } 152 | 153 | #[test] 154 | fn test_mta() { 155 | let alice_input: FE = ECScalar::new_random(); 156 | let (ek_alice, dk_alice) = Paillier::keypair().keys(); 157 | let bob_input: FE = ECScalar::new_random(); 158 | let m_a = MessageA::a(&alice_input, &ek_alice); 159 | let (m_b, beta) = MessageB::b(&bob_input, &ek_alice, m_a); 160 | let alpha = m_b 161 | .verify_proofs_get_alpha(&dk_alice, &alice_input) 162 | .expect("wrong dlog or m_b"); 163 | 164 | let left = alpha + beta; 165 | let right = alice_input * bob_input; 166 | assert_eq!(left.get_element(), right.get_element()); 167 | } 168 | 169 | fn sign(t: usize, n: usize, ttag: usize, s: Vec) { 170 | // full key gen emulation 171 | let (party_keys_vec, shared_keys_vec, _pk_vec, y, vss_scheme) = 172 | keygen_t_n_parties(t.clone(), n); 173 | 174 | let private_vec = (0..shared_keys_vec.len()) 175 | .map(|i| { 176 | PartyPrivate::set_private(party_keys_vec[i].clone(), shared_keys_vec[i].clone()) 177 | }) 178 | .collect::>(); 179 | // make sure that we have t t); 183 | assert_eq!(s.len(), ttag); 184 | 185 | // each party creates a signing key. This happens in parallel IRL. In this test we 186 | // create a vector of signing keys, one for each party. 187 | // throughout i will index parties 188 | let sign_keys_vec = (0..ttag) 189 | .map(|i| SignKeys::create(&private_vec[s[i]], &vss_scheme, s[i], &s)) 190 | .collect::>(); 191 | 192 | // each party computes [Ci,Di] = com(g^gamma_i) and broadcast the commitments 193 | let mut bc1_vec = Vec::new(); 194 | let mut decommit_vec1 = Vec::new(); 195 | for i in 0..ttag.clone() { 196 | let (com, decommit_phase_1) = sign_keys_vec[i].phase1_broadcast(); 197 | bc1_vec.push(com); 198 | decommit_vec1.push(decommit_phase_1); 199 | } 200 | 201 | // each party i sends encryption of k_i under her Paillier key 202 | // m_a_vec = [ma_0;ma_1;,...] 203 | let mut m_a_vec = Vec::new(); 204 | for i in 0..ttag.clone() { 205 | let m_a_k = MessageA::a(&sign_keys_vec[i].k_i, &party_keys_vec[s[i]].ek); 206 | 207 | m_a_vec.push(m_a_k); 208 | } 209 | 210 | // each party i sends responses to m_a_vec she received (one response with input gamma_i and one with w_i) 211 | // m_b_gamma_vec_all is a matrix where column i is a vector of message_b's that party i answers to all ma_{j!=i} using paillier key of party j to answer to ma_j 212 | 213 | // aggregation of the n messages of all parties 214 | let mut m_b_gamma_vec_all = Vec::new(); 215 | let mut beta_vec_all = Vec::new(); 216 | let mut m_b_w_vec_all = Vec::new(); 217 | let mut ni_vec_all = Vec::new(); 218 | 219 | for i in 0..ttag.clone() { 220 | let mut m_b_gamma_vec = Vec::new(); 221 | let mut beta_vec = Vec::new(); 222 | let mut m_b_w_vec = Vec::new(); 223 | let mut ni_vec = Vec::new(); 224 | 225 | for j in 0..ttag - 1 { 226 | let ind = if j < i { j } else { j + 1 }; 227 | 228 | let (m_b_gamma, beta_gamma) = MessageB::b( 229 | &sign_keys_vec[i].gamma_i, 230 | &party_keys_vec[s[ind]].ek, 231 | m_a_vec[ind].clone(), 232 | ); 233 | let (m_b_w, beta_wi) = MessageB::b( 234 | &sign_keys_vec[i].w_i, 235 | &party_keys_vec[s[ind]].ek, 236 | m_a_vec[ind].clone(), 237 | ); 238 | 239 | m_b_gamma_vec.push(m_b_gamma); 240 | beta_vec.push(beta_gamma); 241 | m_b_w_vec.push(m_b_w); 242 | ni_vec.push(beta_wi); 243 | } 244 | m_b_gamma_vec_all.push(m_b_gamma_vec.clone()); 245 | beta_vec_all.push(beta_vec.clone()); 246 | m_b_w_vec_all.push(m_b_w_vec.clone()); 247 | ni_vec_all.push(ni_vec.clone()); 248 | } 249 | 250 | // Here we complete the MwA protocols by taking the mb matrices and starting with the first column generating the appropriate message 251 | // for example for index i=0 j=0 we need party at index s[1] to answer to mb that party s[0] sent, completing a protocol between s[0] and s[1]. 252 | // for index i=1 j=0 we need party at index s[0] to answer to mb that party s[1]. etc. 253 | // IRL each party i should get only the mb messages that other parties sent in response to the party i ma's. 254 | // TODO: simulate as IRL 255 | let mut alpha_vec_all = Vec::new(); 256 | let mut miu_vec_all = Vec::new(); 257 | 258 | for i in 0..ttag.clone() { 259 | let mut alpha_vec = Vec::new(); 260 | let mut miu_vec = Vec::new(); 261 | 262 | let m_b_gamma_vec_i = &m_b_gamma_vec_all[i]; 263 | let m_b_w_vec_i = &m_b_w_vec_all[i]; 264 | 265 | for j in 0..ttag - 1 { 266 | let ind = if j < i { j } else { j + 1 }; 267 | let m_b = m_b_gamma_vec_i[j].clone(); 268 | 269 | let alpha_ij_gamma = m_b 270 | .verify_proofs_get_alpha(&party_keys_vec[s[ind]].dk, &sign_keys_vec[ind].k_i) 271 | .expect("wrong dlog or m_b"); 272 | let m_b = m_b_w_vec_i[j].clone(); 273 | let alpha_ij_wi = m_b 274 | .verify_proofs_get_alpha(&party_keys_vec[s[ind]].dk, &sign_keys_vec[ind].k_i) 275 | .expect("wrong dlog or m_b"); 276 | 277 | // since we actually run two MtAwc each party needs to make sure that the values B are the same as the public values 278 | // here for b=w_i the parties already know W_i = g^w_i for each party so this check is done here. for b = gamma_i the check will be later when g^gamma_i will become public 279 | // currently we take the W_i from the other parties signing keys 280 | // TODO: use pk_vec (first change from x_i to w_i) for this check. 281 | assert_eq!(m_b.b_proof.pk.clone(), sign_keys_vec[i].g_w_i.clone()); 282 | 283 | alpha_vec.push(alpha_ij_gamma); 284 | miu_vec.push(alpha_ij_wi); 285 | } 286 | alpha_vec_all.push(alpha_vec.clone()); 287 | miu_vec_all.push(miu_vec.clone()); 288 | } 289 | 290 | let mut delta_vec = Vec::new(); 291 | let mut sigma_vec = Vec::new(); 292 | 293 | for i in 0..ttag.clone() { 294 | let delta = sign_keys_vec[i].phase2_delta_i(&alpha_vec_all[i], &beta_vec_all[i]); 295 | let sigma = sign_keys_vec[i].phase2_sigma_i(&miu_vec_all[i], &ni_vec_all[i]); 296 | delta_vec.push(delta); 297 | sigma_vec.push(sigma); 298 | } 299 | 300 | // all parties broadcast delta_i and compute delta_i ^(-1) 301 | let delta_inv = SignKeys::phase3_reconstruct_delta(&delta_vec); 302 | 303 | // de-commit to g^gamma_i from phase1, test comm correctness, and that it is the same value used in MtA. 304 | // Return R 305 | 306 | let g_gamma_i_vec = (0..ttag) 307 | .map(|i| sign_keys_vec[i].g_gamma_i.clone()) 308 | .collect::>(); 309 | 310 | let R_vec = (0..ttag) 311 | .map(|_| { 312 | // each party i tests all B = g^b = g ^ gamma_i she received. 313 | let b_proof_vec = (0..ttag) 314 | .map(|j| { 315 | let b_gamma_vec = &m_b_gamma_vec_all[j]; 316 | &b_gamma_vec[0].b_proof 317 | }) 318 | .collect::>(); 319 | let R = SignKeys::phase4(&delta_inv, &b_proof_vec, decommit_vec1.clone(), &bc1_vec) 320 | .expect("bad gamma_i decommit"); 321 | R 322 | }) 323 | .collect::>(); 324 | 325 | let message: [u8; 4] = [79, 77, 69, 82]; 326 | let message_bn = HSha256::create_hash(&vec![&BigInt::from(&message[..])]); 327 | let mut local_sig_vec = Vec::new(); 328 | 329 | // each party computes s_i but don't send it yet. we start with phase5 330 | for i in 0..ttag.clone() { 331 | let local_sig = LocalSignature::phase5_local_sig( 332 | &sign_keys_vec[i].k_i, 333 | &message_bn, 334 | &R_vec[i], 335 | &sigma_vec[i], 336 | &y, 337 | ); 338 | local_sig_vec.push(local_sig); 339 | } 340 | 341 | let mut phase5_com_vec: Vec = Vec::new(); 342 | let mut phase_5a_decom_vec: Vec = Vec::new(); 343 | let mut helgamal_proof_vec = Vec::new(); 344 | // we notice that the proof for V= R^sg^l, B = A^l is a general form of homomorphic elgamal. 345 | for i in 0..ttag.clone() { 346 | let (phase5_com, phase_5a_decom, helgamal_proof) = 347 | local_sig_vec[i].phase5a_broadcast_5b_zkproof(); 348 | phase5_com_vec.push(phase5_com); 349 | phase_5a_decom_vec.push(phase_5a_decom); 350 | helgamal_proof_vec.push(helgamal_proof); 351 | } 352 | 353 | let mut phase5_com2_vec = Vec::new(); 354 | let mut phase_5d_decom2_vec = Vec::new(); 355 | for i in 0..ttag.clone() { 356 | let mut phase_5a_decom_vec_clone = phase_5a_decom_vec.clone(); 357 | let mut phase_5a_com_vec_clone = phase5_com_vec.clone(); 358 | let mut phase_5b_elgamal_vec_clone = helgamal_proof_vec.clone(); 359 | 360 | let _decom_i = phase_5a_decom_vec_clone.remove(i); 361 | let _com_i = phase_5a_com_vec_clone.remove(i); 362 | let _elgamal_i = phase_5b_elgamal_vec_clone.remove(i); 363 | // for j in 0..s_minus_i.len() { 364 | let (phase5_com2, phase_5d_decom2) = local_sig_vec[i] 365 | .phase5c( 366 | &phase_5a_decom_vec_clone, 367 | &phase_5a_com_vec_clone, 368 | &phase_5b_elgamal_vec_clone, 369 | &phase_5a_decom_vec[i].V_i, 370 | &R_vec[0], 371 | ) 372 | .expect("error phase5"); 373 | phase5_com2_vec.push(phase5_com2); 374 | phase_5d_decom2_vec.push(phase_5d_decom2); 375 | // } 376 | } 377 | 378 | // assuming phase5 checks passes each party sends s_i and compute sum_i{s_i} 379 | let mut s_vec: Vec = Vec::new(); 380 | for i in 0..ttag.clone() { 381 | let s_i = local_sig_vec[i] 382 | .phase5d(&phase_5d_decom2_vec, &phase5_com2_vec, &phase_5a_decom_vec) 383 | .expect("bad com 5d"); 384 | s_vec.push(s_i); 385 | } 386 | 387 | // here we compute the signature only of party i=0 to demonstrate correctness. 388 | s_vec.remove(0); 389 | let _sig = local_sig_vec[0] 390 | .output_signature(&s_vec) 391 | .expect("verification failed"); 392 | } 393 | 394 | } 395 | -------------------------------------------------------------------------------- /src/protocols/multi_party_ecdsa/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Multi-party ECDSA 3 | 4 | Copyright 2018 by Kzen Networks 5 | 6 | This file is part of Multi-party ECDSA library 7 | (https://github.com/KZen-networks/multi-party-ecdsa) 8 | 9 | Multi-party ECDSA is free software: you can redistribute 10 | it and/or modify it under the terms of the GNU General Public 11 | License as published by the Free Software Foundation, either 12 | version 3 of the License, or (at your option) any later version. 13 | 14 | @license GPL-3.0+ 15 | */ 16 | 17 | pub mod gg_2018; 18 | -------------------------------------------------------------------------------- /src/protocols/two_party_ecdsa/lindell_2017/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Multi-party ECDSA 3 | 4 | Copyright 2018 by Kzen Networks 5 | 6 | This file is part of Multi-party ECDSA library 7 | (https://github.com/KZen-networks/multi-party-ecdsa) 8 | 9 | Multi-party ECDSA is free software: you can redistribute 10 | it and/or modify it under the terms of the GNU General Public 11 | License as published by the Free Software Foundation, either 12 | version 3 of the License, or (at your option) any later version. 13 | 14 | @license GPL-3.0+ 15 | */ 16 | 17 | const SECURITY_BITS: usize = 256; 18 | 19 | pub mod party_one; 20 | pub mod party_two; 21 | 22 | mod test; 23 | -------------------------------------------------------------------------------- /src/protocols/two_party_ecdsa/lindell_2017/party_one.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Multi-party ECDSA 3 | 4 | Copyright 2018 by Kzen Networks 5 | 6 | This file is part of Multi-party ECDSA library 7 | (https://github.com/KZen-networks/multi-party-ecdsa) 8 | 9 | Multi-party ECDSA is free software: you can redistribute 10 | it and/or modify it under the terms of the GNU General Public 11 | License as published by the Free Software Foundation, either 12 | version 3 of the License, or (at your option) any later version. 13 | 14 | @license GPL-3.0+ 15 | */ 16 | use paillier::Paillier; 17 | use paillier::{Decrypt, EncryptWithChosenRandomness, KeyGeneration}; 18 | use paillier::{DecryptionKey, EncryptionKey, Randomness, RawCiphertext, RawPlaintext}; 19 | use std::cmp; 20 | use std::ops::Shl; 21 | use zk_paillier::zkproofs::{NICorrectKeyProof, RangeProofNi}; 22 | 23 | use super::SECURITY_BITS; 24 | use curv::arithmetic::traits::*; 25 | 26 | use curv::elliptic::curves::traits::*; 27 | 28 | use curv::cryptographic_primitives::commitments::hash_commitment::HashCommitment; 29 | use curv::cryptographic_primitives::commitments::traits::Commitment; 30 | use curv::cryptographic_primitives::hashing::hash_sha256::HSha256; 31 | use curv::cryptographic_primitives::hashing::traits::Hash; 32 | use curv::cryptographic_primitives::proofs::sigma_dlog::*; 33 | use curv::cryptographic_primitives::proofs::sigma_ec_ddh::*; 34 | use curv::cryptographic_primitives::proofs::ProofError; 35 | use protocols::two_party_ecdsa::lindell_2017::party_two::EphKeyGenFirstMsg as Party2EphKeyGenFirstMessage; 36 | use protocols::two_party_ecdsa::lindell_2017::party_two::EphKeyGenSecondMsg as Party2EphKeyGenSecondMessage; 37 | use protocols::two_party_ecdsa::lindell_2017::party_two::PDLFirstMessage as Party2PDLFirstMessage; 38 | use protocols::two_party_ecdsa::lindell_2017::party_two::PDLSecondMessage as Party2PDLSecondMessage; 39 | 40 | use centipede::juggling::proof_system::{Helgamalsegmented, Witness}; 41 | use centipede::juggling::segmentation::Msegmentation; 42 | use protocols::multi_party_ecdsa::gg_2018::mta::MessageB; 43 | 44 | use curv::BigInt; 45 | use curv::FE; 46 | use curv::GE; 47 | 48 | use Error::{self, InvalidSig}; 49 | 50 | use subtle::ConstantTimeEq; 51 | 52 | //****************** Begin: Party One structs ******************// 53 | #[derive(Clone, Debug, Serialize, Deserialize)] 54 | pub struct EcKeyPair { 55 | pub public_share: GE, 56 | secret_share: FE, 57 | } 58 | 59 | #[derive(Clone, Debug, Serialize, Deserialize)] 60 | pub struct CommWitness { 61 | pub pk_commitment_blind_factor: BigInt, 62 | pub zk_pok_blind_factor: BigInt, 63 | pub public_share: GE, 64 | pub d_log_proof: DLogProof, 65 | } 66 | 67 | #[derive(Clone, Debug, Serialize, Deserialize)] 68 | pub struct KeyGenFirstMsg { 69 | pub pk_commitment: BigInt, 70 | pub zk_pok_commitment: BigInt, 71 | } 72 | 73 | #[derive(Debug, Serialize, Deserialize)] 74 | pub struct KeyGenSecondMsg { 75 | pub comm_witness: CommWitness, 76 | } 77 | 78 | #[derive(Debug, Serialize, Deserialize)] 79 | pub struct PaillierKeyPair { 80 | pub ek: EncryptionKey, 81 | dk: DecryptionKey, 82 | pub encrypted_share: BigInt, 83 | randomness: BigInt, 84 | } 85 | 86 | #[derive(Debug, Serialize, Deserialize)] 87 | pub struct SignatureRecid { 88 | pub s: BigInt, 89 | pub r: BigInt, 90 | pub recid: u8, 91 | } 92 | 93 | #[derive(Debug, Serialize, Deserialize)] 94 | pub struct Signature { 95 | pub s: BigInt, 96 | pub r: BigInt, 97 | } 98 | 99 | #[derive(Serialize, Deserialize, Clone)] 100 | pub struct Party1Private { 101 | x1: FE, 102 | paillier_priv: DecryptionKey, 103 | c_key_randomness: BigInt, 104 | } 105 | 106 | #[derive(Debug, Serialize, Deserialize)] 107 | pub struct PDLFirstMessage { 108 | pub alpha: BigInt, 109 | pub c_hat: BigInt, 110 | } 111 | 112 | #[derive(Debug, Serialize, Deserialize)] 113 | pub struct PDLdecommit { 114 | pub q_hat: GE, 115 | pub blindness: BigInt, 116 | } 117 | 118 | #[derive(Debug, Serialize, Deserialize)] 119 | pub struct PDLSecondMessage { 120 | pub decommit: PDLdecommit, 121 | } 122 | 123 | #[derive(Clone, Debug, Serialize, Deserialize)] 124 | pub struct EphEcKeyPair { 125 | pub public_share: GE, 126 | secret_share: FE, 127 | } 128 | 129 | #[derive(Debug, Serialize, Deserialize)] 130 | pub struct EphKeyGenFirstMsg { 131 | pub d_log_proof: ECDDHProof, 132 | pub public_share: GE, 133 | pub c: GE, //c = secret_share * base_point2 134 | } 135 | 136 | #[derive(Debug, Serialize, Deserialize)] 137 | pub struct EphKeyGenSecondMsg {} 138 | 139 | //****************** End: Party One structs ******************// 140 | 141 | impl KeyGenFirstMsg { 142 | pub fn create_commitments() -> (KeyGenFirstMsg, CommWitness, EcKeyPair) { 143 | let base: GE = ECPoint::generator(); 144 | 145 | let secret_share: FE = ECScalar::new_random(); 146 | //in Lindell's protocol range proof works only for x1 (KeyGenFirstMsg, CommWitness, EcKeyPair) { 189 | //in Lindell's protocol range proof works only for x1 Result { 237 | DLogProof::verify(proof)?; 238 | Ok(KeyGenSecondMsg { comm_witness }) 239 | } 240 | } 241 | 242 | pub fn compute_pubkey(party_one_private: &Party1Private, other_share_public_share: &GE) -> GE { 243 | other_share_public_share * &party_one_private.x1 244 | } 245 | 246 | impl Party1Private { 247 | pub fn set_private_key(ec_key: &EcKeyPair, paillier_key: &PaillierKeyPair) -> Party1Private { 248 | Party1Private { 249 | x1: ec_key.secret_share.clone(), 250 | paillier_priv: paillier_key.dk.clone(), 251 | c_key_randomness: paillier_key.randomness.clone(), 252 | } 253 | } 254 | pub fn refresh_private_key( 255 | party_one_private: &Party1Private, 256 | factor: &BigInt, 257 | ) -> ( 258 | EncryptionKey, 259 | BigInt, 260 | Party1Private, 261 | NICorrectKeyProof, 262 | RangeProofNi, 263 | ) { 264 | let (ek_new, dk_new) = Paillier::keypair().keys(); 265 | let randomness = Randomness::sample(&ek_new); 266 | let factor_fe: FE = ECScalar::from(&factor); 267 | let x1_new = party_one_private.x1.clone() * &factor_fe; 268 | let three = BigInt::from(3); 269 | let c_key_new = Paillier::encrypt_with_chosen_randomness( 270 | &ek_new, 271 | RawPlaintext::from(x1_new.to_big_int().clone()), 272 | &randomness, 273 | ) 274 | .0 275 | .into_owned(); 276 | let correct_key_proof_new = NICorrectKeyProof::proof(&dk_new); 277 | 278 | let range_proof_new = RangeProofNi::prove( 279 | &ek_new, 280 | &(FE::q() * three.clone()), 281 | &c_key_new, 282 | &x1_new.to_big_int(), 283 | &randomness.0, 284 | ); 285 | 286 | let party_one_private_new = Party1Private { 287 | x1: x1_new.clone(), 288 | paillier_priv: dk_new.clone(), 289 | c_key_randomness: randomness.0, 290 | }; 291 | 292 | ( 293 | ek_new, 294 | c_key_new, 295 | party_one_private_new, 296 | correct_key_proof_new, 297 | range_proof_new, 298 | ) 299 | } 300 | 301 | // used for verifiable recovery 302 | pub fn to_encrypted_segment( 303 | &self, 304 | segment_size: &usize, 305 | num_of_segments: usize, 306 | pub_ke_y: &GE, 307 | g: &GE, 308 | ) -> (Witness, Helgamalsegmented) { 309 | Msegmentation::to_encrypted_segments(&self.x1, &segment_size, num_of_segments, pub_ke_y, g) 310 | } 311 | 312 | // used to transform lindell master key to gg18 master key 313 | pub fn to_mta_message_b(&self, message_b: MessageB) -> Result { 314 | message_b.verify_proofs_get_alpha(&self.paillier_priv, &self.x1) 315 | } 316 | } 317 | 318 | impl PaillierKeyPair { 319 | pub fn generate_keypair_and_encrypted_share(keygen: &EcKeyPair) -> PaillierKeyPair { 320 | let (ek, dk) = Paillier::keypair().keys(); 321 | let randomness = Randomness::sample(&ek); 322 | 323 | let encrypted_share = Paillier::encrypt_with_chosen_randomness( 324 | &ek, 325 | RawPlaintext::from(keygen.secret_share.to_big_int()), 326 | &randomness, 327 | ) 328 | .0 329 | .into_owned(); 330 | 331 | PaillierKeyPair { 332 | ek, 333 | dk, 334 | encrypted_share, 335 | randomness: randomness.0, 336 | } 337 | } 338 | 339 | pub fn generate_encrypted_share_from_fixed_paillier_keypair( 340 | ek: &EncryptionKey, 341 | dk: &DecryptionKey, 342 | keygen: &EcKeyPair, 343 | ) -> PaillierKeyPair { 344 | let randomness = Randomness::sample(ek); 345 | 346 | let encrypted_share = Paillier::encrypt_with_chosen_randomness( 347 | ek, 348 | RawPlaintext::from(keygen.secret_share.to_big_int()), 349 | &randomness, 350 | ) 351 | .0 352 | .into_owned(); 353 | 354 | PaillierKeyPair { 355 | ek: ek.clone(), 356 | dk: dk.clone(), 357 | encrypted_share, 358 | randomness: randomness.0, 359 | } 360 | } 361 | 362 | pub fn generate_range_proof( 363 | paillier_context: &PaillierKeyPair, 364 | party_one_private: &Party1Private, 365 | ) -> RangeProofNi { 366 | let range_proof = RangeProofNi::prove( 367 | &paillier_context.ek, 368 | &FE::q(), 369 | &paillier_context.encrypted_share.clone(), 370 | &party_one_private.x1.to_big_int(), 371 | &paillier_context.randomness, 372 | ); 373 | range_proof 374 | } 375 | 376 | pub fn generate_ni_proof_correct_key(paillier_context: &PaillierKeyPair) -> NICorrectKeyProof { 377 | NICorrectKeyProof::proof(&paillier_context.dk) 378 | } 379 | 380 | pub fn pdl_first_stage( 381 | party_one_private: &Party1Private, 382 | pdl_first_message: &Party2PDLFirstMessage, 383 | ) -> (PDLFirstMessage, PDLdecommit) { 384 | let c_tag = pdl_first_message.c_tag.clone(); 385 | let alpha = Paillier::decrypt( 386 | &party_one_private.paillier_priv.clone(), 387 | &RawCiphertext::from(c_tag.clone()), 388 | ); 389 | let alpha_fe: FE = ECScalar::from(&alpha.0); 390 | let g: GE = ECPoint::generator(); 391 | let q_hat = g * &alpha_fe; 392 | let blindness = BigInt::sample_below(&FE::q()); 393 | let c_hat = HashCommitment::create_commitment_with_user_defined_randomness( 394 | &q_hat.bytes_compressed_to_big_int(), 395 | &blindness, 396 | ); 397 | ( 398 | PDLFirstMessage { 399 | alpha: alpha.0.into_owned(), 400 | c_hat, 401 | }, 402 | PDLdecommit { blindness, q_hat }, 403 | ) 404 | } 405 | 406 | pub fn pdl_second_stage( 407 | pdl_party_one_first_message: &PDLFirstMessage, 408 | pdl_party_two_first_message: &Party2PDLFirstMessage, 409 | pdl_party_two_second_message: &Party2PDLSecondMessage, 410 | party_one_private: Party1Private, 411 | pdl_decommit: PDLdecommit, 412 | ) -> Result<(PDLSecondMessage), ()> { 413 | let a = pdl_party_two_second_message.decommit.a.clone(); 414 | let b = pdl_party_two_second_message.decommit.b.clone(); 415 | let blindness = pdl_party_two_second_message.decommit.blindness.clone(); 416 | 417 | let ab_concat = a.clone() + b.clone().shl(a.bit_length()); 418 | let c_tag_tag_test = 419 | HashCommitment::create_commitment_with_user_defined_randomness(&ab_concat, &blindness); 420 | let ax1 = a.clone() * party_one_private.x1.to_big_int(); 421 | let alpha_test = ax1 + b.clone(); 422 | if alpha_test == pdl_party_one_first_message.alpha 423 | && pdl_party_two_first_message.c_tag_tag.clone() == c_tag_tag_test 424 | { 425 | Ok(PDLSecondMessage { 426 | decommit: pdl_decommit, 427 | }) 428 | } else { 429 | Err(()) 430 | } 431 | } 432 | } 433 | 434 | impl EphKeyGenFirstMsg { 435 | pub fn create() -> (EphKeyGenFirstMsg, EphEcKeyPair) { 436 | let base: GE = ECPoint::generator(); 437 | let secret_share: FE = ECScalar::new_random(); 438 | let public_share = &base * &secret_share; 439 | let h: GE = GE::base_point2(); 440 | let w = ECDDHWitness { 441 | x: secret_share.clone(), 442 | }; 443 | let c = &h * &secret_share; 444 | let delta = ECDDHStatement { 445 | g1: base.clone(), 446 | h1: public_share.clone(), 447 | g2: h.clone(), 448 | h2: c.clone(), 449 | }; 450 | let d_log_proof = ECDDHProof::prove(&w, &delta); 451 | let ec_key_pair = EphEcKeyPair { 452 | public_share: public_share.clone(), 453 | secret_share, 454 | }; 455 | ( 456 | EphKeyGenFirstMsg { 457 | d_log_proof, 458 | public_share, 459 | c, 460 | }, 461 | ec_key_pair, 462 | ) 463 | } 464 | } 465 | 466 | impl EphKeyGenSecondMsg { 467 | pub fn verify_commitments_and_dlog_proof( 468 | party_two_first_message: &Party2EphKeyGenFirstMessage, 469 | party_two_second_message: &Party2EphKeyGenSecondMessage, 470 | ) -> Result { 471 | let party_two_pk_commitment = &party_two_first_message.pk_commitment; 472 | let party_two_zk_pok_commitment = &party_two_first_message.zk_pok_commitment; 473 | let party_two_zk_pok_blind_factor = 474 | &party_two_second_message.comm_witness.zk_pok_blind_factor; 475 | let party_two_public_share = &party_two_second_message.comm_witness.public_share; 476 | let party_two_pk_commitment_blind_factor = &party_two_second_message 477 | .comm_witness 478 | .pk_commitment_blind_factor; 479 | let party_two_d_log_proof = &party_two_second_message.comm_witness.d_log_proof; 480 | let mut flag = true; 481 | match party_two_pk_commitment 482 | == &HashCommitment::create_commitment_with_user_defined_randomness( 483 | &party_two_public_share.bytes_compressed_to_big_int(), 484 | &party_two_pk_commitment_blind_factor, 485 | ) { 486 | false => flag = false, 487 | true => flag = flag, 488 | }; 489 | match party_two_zk_pok_commitment 490 | == &HashCommitment::create_commitment_with_user_defined_randomness( 491 | &HSha256::create_hash_from_ge(&[ 492 | &party_two_d_log_proof.a1, 493 | &party_two_d_log_proof.a2, 494 | ]) 495 | .to_big_int(), 496 | &party_two_zk_pok_blind_factor, 497 | ) { 498 | false => flag = false, 499 | true => flag = flag, 500 | }; 501 | assert!(flag); 502 | let delta = ECDDHStatement { 503 | g1: GE::generator(), 504 | h1: party_two_public_share.clone(), 505 | g2: GE::base_point2(), 506 | h2: party_two_second_message.comm_witness.c.clone(), 507 | }; 508 | party_two_d_log_proof.verify(&delta)?; 509 | Ok(EphKeyGenSecondMsg {}) 510 | } 511 | } 512 | 513 | impl Signature { 514 | pub fn compute( 515 | party_one_private: &Party1Private, 516 | partial_sig_c3: &BigInt, 517 | ephemeral_local_share: &EphEcKeyPair, 518 | ephemeral_other_public_share: &GE, 519 | ) -> Signature { 520 | //compute r = k2* R1 521 | let mut r = ephemeral_other_public_share.clone(); 522 | r = r.scalar_mul(&ephemeral_local_share.secret_share.get_element()); 523 | 524 | let rx = r.x_coor().unwrap().mod_floor(&FE::q()); 525 | let k1_inv = &ephemeral_local_share 526 | .secret_share 527 | .to_big_int() 528 | .invert(&FE::q()) 529 | .unwrap(); 530 | let s_tag = Paillier::decrypt( 531 | &party_one_private.paillier_priv, 532 | &RawCiphertext::from(partial_sig_c3), 533 | ); 534 | let s_tag_tag = BigInt::mod_mul(&k1_inv, &s_tag.0, &FE::q()); 535 | let s = cmp::min(s_tag_tag.clone(), FE::q().clone() - s_tag_tag.clone()); 536 | Signature { s, r: rx } 537 | } 538 | 539 | pub fn compute_with_recid( 540 | party_one_private: &Party1Private, 541 | partial_sig_c3: &BigInt, 542 | ephemeral_local_share: &EphEcKeyPair, 543 | ephemeral_other_public_share: &GE, 544 | ) -> SignatureRecid { 545 | //compute r = k2* R1 546 | let mut r = ephemeral_other_public_share.clone(); 547 | r = r.scalar_mul(&ephemeral_local_share.secret_share.get_element()); 548 | 549 | let rx = r.x_coor().unwrap().mod_floor(&FE::q()); 550 | let ry = r.y_coor().unwrap().mod_floor(&FE::q()); 551 | let k1_inv = &ephemeral_local_share 552 | .secret_share 553 | .to_big_int() 554 | .invert(&FE::q()) 555 | .unwrap(); 556 | let s_tag = Paillier::decrypt( 557 | &party_one_private.paillier_priv, 558 | &RawCiphertext::from(partial_sig_c3), 559 | ); 560 | let s_tag_tag = BigInt::mod_mul(&k1_inv, &s_tag.0, &FE::q()); 561 | let s = cmp::min(s_tag_tag.clone(), FE::q().clone() - s_tag_tag.clone()); 562 | 563 | /* 564 | Calculate recovery id - it is not possible to compute the public key out of the signature 565 | itself. Recovery id is used to enable extracting the public key uniquely. 566 | 1. id = R.y & 1 567 | 2. if (s > curve.q / 2) id = id ^ 1 568 | */ 569 | let is_ry_odd = ry.tstbit(0); 570 | let mut recid = if is_ry_odd { 1 } else { 0 }; 571 | if s_tag_tag.clone() > FE::q() - s_tag_tag.clone() { 572 | recid = recid ^ 1; 573 | } 574 | 575 | SignatureRecid { s, r: rx, recid } 576 | } 577 | } 578 | 579 | pub fn verify(signature: &Signature, pubkey: &GE, message: &BigInt) -> Result<(), Error> { 580 | let s_fe: FE = ECScalar::from(&signature.s); 581 | let rx_fe: FE = ECScalar::from(&signature.r); 582 | 583 | let s_inv_fe = s_fe.invert(); 584 | let e_fe: FE = ECScalar::from(&message.mod_floor(&FE::q())); 585 | let u1 = GE::generator() * e_fe * s_inv_fe; 586 | let u2 = *pubkey * rx_fe * s_inv_fe; 587 | 588 | // second condition is against malleability 589 | let rx_bytes = &BigInt::to_vec(&signature.r)[..]; 590 | let u1_plus_u2_bytes = &BigInt::to_vec(&(u1 + u2).x_coor().unwrap())[..]; 591 | 592 | if rx_bytes.ct_eq(&u1_plus_u2_bytes).unwrap_u8() == 1 593 | && signature.s < FE::q() - signature.s.clone() 594 | { 595 | Ok(()) 596 | } else { 597 | Err(InvalidSig) 598 | } 599 | } 600 | -------------------------------------------------------------------------------- /src/protocols/two_party_ecdsa/lindell_2017/party_two.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Multi-party ECDSA 3 | 4 | Copyright 2018 by Kzen Networks 5 | 6 | This file is part of Multi-party ECDSA library 7 | (https://github.com/KZen-networks/multi-party-ecdsa) 8 | 9 | Multi-party ECDSA is free software: you can redistribute 10 | it and/or modify it under the terms of the GNU General Public 11 | License as published by the Free Software Foundation, either 12 | version 3 of the License, or (at your option) any later version. 13 | 14 | @license GPL-3.0+ 15 | */ 16 | use super::SECURITY_BITS; 17 | use curv::arithmetic::traits::*; 18 | use std::ops::Shl; 19 | 20 | use curv::cryptographic_primitives::commitments::hash_commitment::HashCommitment; 21 | use curv::cryptographic_primitives::commitments::traits::Commitment; 22 | use curv::cryptographic_primitives::hashing::hash_sha256::HSha256; 23 | use curv::cryptographic_primitives::hashing::traits::Hash; 24 | use curv::cryptographic_primitives::proofs::sigma_dlog::*; 25 | use curv::cryptographic_primitives::proofs::sigma_ec_ddh::*; 26 | use curv::cryptographic_primitives::proofs::ProofError; 27 | 28 | use curv::elliptic::curves::traits::*; 29 | 30 | use curv::BigInt; 31 | use curv::FE; 32 | use curv::GE; 33 | use protocols::two_party_ecdsa::lindell_2017::party_one::EphKeyGenFirstMsg as Party1EphKeyGenFirstMsg; 34 | use protocols::two_party_ecdsa::lindell_2017::party_one::KeyGenFirstMsg as Party1KeyGenFirstMessage; 35 | use protocols::two_party_ecdsa::lindell_2017::party_one::KeyGenSecondMsg as Party1KeyGenSecondMessage; 36 | use protocols::two_party_ecdsa::lindell_2017::party_one::PDLFirstMessage as Party1PDLFirstMessage; 37 | use protocols::two_party_ecdsa::lindell_2017::party_one::PDLSecondMessage as Party1PDLSecondMessage; 38 | 39 | use paillier::Paillier; 40 | use paillier::{Add, Encrypt, Mul}; 41 | use paillier::{EncryptionKey, RawCiphertext, RawPlaintext}; 42 | use zk_paillier::zkproofs::{ 43 | CorrectKeyProofError, NICorrectKeyProof, RangeProofError, RangeProofNi, 44 | }; 45 | 46 | use centipede::juggling::proof_system::{Helgamalsegmented, Witness}; 47 | use centipede::juggling::segmentation::Msegmentation; 48 | use protocols::multi_party_ecdsa::gg_2018::mta::{MessageA, MessageB}; 49 | 50 | //****************** Begin: Party Two structs ******************// 51 | 52 | #[derive(Clone, Debug, Serialize, Deserialize)] 53 | pub struct EcKeyPair { 54 | pub public_share: GE, 55 | secret_share: FE, 56 | } 57 | 58 | #[derive(Clone, Debug, Serialize, Deserialize)] 59 | pub struct KeyGenFirstMsg { 60 | pub d_log_proof: DLogProof, 61 | pub public_share: GE, 62 | } 63 | 64 | #[derive(Debug, Serialize, Deserialize)] 65 | pub struct KeyGenSecondMsg {} 66 | 67 | #[derive(Debug, Serialize, Deserialize)] 68 | pub struct PaillierPublic { 69 | pub ek: EncryptionKey, 70 | pub encrypted_secret_share: BigInt, 71 | } 72 | 73 | #[derive(Debug, Serialize, Deserialize)] 74 | pub struct PartialSig { 75 | pub c3: BigInt, 76 | } 77 | 78 | #[derive(Serialize, Deserialize)] 79 | pub struct Party2Private { 80 | x2: FE, 81 | } 82 | #[derive(Debug)] 83 | pub struct PDLchallenge { 84 | pub c_tag: BigInt, 85 | pub c_tag_tag: BigInt, 86 | a: BigInt, 87 | b: BigInt, 88 | blindness: BigInt, 89 | q_tag: GE, 90 | } 91 | 92 | #[derive(Debug, Serialize, Deserialize)] 93 | pub struct PDLFirstMessage { 94 | pub c_tag: BigInt, 95 | pub c_tag_tag: BigInt, 96 | } 97 | 98 | #[derive(Debug, Serialize, Deserialize)] 99 | pub struct PDLdecommit { 100 | pub a: BigInt, 101 | pub b: BigInt, 102 | pub blindness: BigInt, 103 | } 104 | 105 | #[derive(Debug, Serialize, Deserialize)] 106 | pub struct PDLSecondMessage { 107 | pub decommit: PDLdecommit, 108 | } 109 | #[derive(Clone, Debug, Serialize, Deserialize)] 110 | pub struct EphEcKeyPair { 111 | pub public_share: GE, 112 | secret_share: FE, 113 | } 114 | 115 | #[derive(Clone, Debug, Serialize, Deserialize)] 116 | pub struct EphCommWitness { 117 | pub pk_commitment_blind_factor: BigInt, 118 | pub zk_pok_blind_factor: BigInt, 119 | pub public_share: GE, 120 | pub d_log_proof: ECDDHProof, 121 | pub c: GE, //c = secret_share * base_point2 122 | } 123 | 124 | #[derive(Clone, Debug, Serialize, Deserialize)] 125 | pub struct EphKeyGenFirstMsg { 126 | pub pk_commitment: BigInt, 127 | pub zk_pok_commitment: BigInt, 128 | } 129 | 130 | #[derive(Debug, Serialize, Deserialize)] 131 | pub struct EphKeyGenSecondMsg { 132 | pub comm_witness: EphCommWitness, 133 | } 134 | 135 | //****************** End: Party Two structs ******************// 136 | 137 | impl KeyGenFirstMsg { 138 | pub fn create() -> (KeyGenFirstMsg, EcKeyPair) { 139 | let base: GE = ECPoint::generator(); 140 | let secret_share: FE = ECScalar::new_random(); 141 | let public_share = base * &secret_share; 142 | let d_log_proof = DLogProof::prove(&secret_share); 143 | let ec_key_pair = EcKeyPair { 144 | public_share: public_share.clone(), 145 | secret_share, 146 | }; 147 | ( 148 | KeyGenFirstMsg { 149 | d_log_proof, 150 | public_share, 151 | }, 152 | ec_key_pair, 153 | ) 154 | } 155 | 156 | pub fn create_with_fixed_secret_share(secret_share: FE) -> (KeyGenFirstMsg, EcKeyPair) { 157 | let base: GE = ECPoint::generator(); 158 | let public_share = base * &secret_share; 159 | let d_log_proof = DLogProof::prove(&secret_share); 160 | let ec_key_pair = EcKeyPair { 161 | public_share: public_share.clone(), 162 | secret_share, 163 | }; 164 | ( 165 | KeyGenFirstMsg { 166 | d_log_proof, 167 | public_share, 168 | }, 169 | ec_key_pair, 170 | ) 171 | } 172 | } 173 | 174 | impl KeyGenSecondMsg { 175 | pub fn verify_commitments_and_dlog_proof( 176 | party_one_first_message: &Party1KeyGenFirstMessage, 177 | party_one_second_message: &Party1KeyGenSecondMessage, 178 | ) -> Result { 179 | let party_one_pk_commitment = &party_one_first_message.pk_commitment; 180 | let party_one_zk_pok_commitment = &party_one_first_message.zk_pok_commitment; 181 | let party_one_zk_pok_blind_factor = 182 | &party_one_second_message.comm_witness.zk_pok_blind_factor; 183 | let party_one_public_share = &party_one_second_message.comm_witness.public_share; 184 | let party_one_pk_commitment_blind_factor = &party_one_second_message 185 | .comm_witness 186 | .pk_commitment_blind_factor; 187 | let party_one_d_log_proof = &party_one_second_message.comm_witness.d_log_proof; 188 | 189 | let mut flag = true; 190 | match party_one_pk_commitment 191 | == &HashCommitment::create_commitment_with_user_defined_randomness( 192 | &party_one_public_share.bytes_compressed_to_big_int(), 193 | &party_one_pk_commitment_blind_factor, 194 | ) { 195 | false => flag = false, 196 | true => flag = flag, 197 | }; 198 | match party_one_zk_pok_commitment 199 | == &HashCommitment::create_commitment_with_user_defined_randomness( 200 | &party_one_d_log_proof 201 | .pk_t_rand_commitment 202 | .bytes_compressed_to_big_int(), 203 | &party_one_zk_pok_blind_factor, 204 | ) { 205 | false => flag = false, 206 | true => flag = flag, 207 | }; 208 | assert!(flag); 209 | DLogProof::verify(&party_one_d_log_proof)?; 210 | Ok(KeyGenSecondMsg {}) 211 | } 212 | } 213 | 214 | pub fn compute_pubkey(local_share: &EcKeyPair, other_share_public_share: &GE) -> GE { 215 | let pubkey = other_share_public_share.clone(); 216 | pubkey.scalar_mul(&local_share.secret_share.get_element()) 217 | } 218 | 219 | impl Party2Private { 220 | pub fn set_private_key(ec_key: &EcKeyPair) -> Party2Private { 221 | Party2Private { 222 | x2: ec_key.secret_share.clone(), 223 | } 224 | } 225 | 226 | pub fn update_private_key(party_two_private: &Party2Private, factor: &BigInt) -> Party2Private { 227 | let factor_fe: FE = ECScalar::from(factor); 228 | Party2Private { 229 | x2: party_two_private.x2.mul(&factor_fe.get_element()), 230 | } 231 | } 232 | 233 | // used for verifiable recovery 234 | pub fn to_encrypted_segment( 235 | &self, 236 | segment_size: &usize, 237 | num_of_segments: usize, 238 | pub_ke_y: &GE, 239 | g: &GE, 240 | ) -> (Witness, Helgamalsegmented) { 241 | Msegmentation::to_encrypted_segments(&self.x2, &segment_size, num_of_segments, pub_ke_y, g) 242 | } 243 | 244 | // used to transform lindell master key to gg18 master key 245 | pub fn to_mta_message_b(&self, ek: &EncryptionKey, ciphertext: &BigInt) -> (MessageB, FE) { 246 | let message_a = MessageA { 247 | c: ciphertext.clone(), 248 | }; 249 | MessageB::b(&self.x2, &ek, message_a) 250 | } 251 | } 252 | 253 | impl PaillierPublic { 254 | pub fn pdl_challenge(&self, other_share_public_share: &GE) -> (PDLFirstMessage, PDLchallenge) { 255 | let a_fe: FE = ECScalar::new_random(); 256 | let a = a_fe.to_big_int(); 257 | let q = FE::q(); 258 | let q_sq = q.pow(2); 259 | let b = BigInt::sample_below(&q_sq); 260 | let b_fe: FE = ECScalar::from(&b); 261 | let b_enc = Paillier::encrypt(&self.ek, RawPlaintext::from(b.clone())); 262 | let ac = Paillier::mul( 263 | &self.ek, 264 | RawCiphertext::from(self.encrypted_secret_share.clone()), 265 | RawPlaintext::from(a.clone()), 266 | ); 267 | let c_tag = Paillier::add(&self.ek, ac, b_enc).0.into_owned(); 268 | let ab_concat = a.clone() + b.clone().shl(a.bit_length()); 269 | let blindness = BigInt::sample_below(&q); 270 | let c_tag_tag = 271 | HashCommitment::create_commitment_with_user_defined_randomness(&ab_concat, &blindness); 272 | let g: GE = ECPoint::generator(); 273 | let q_tag = other_share_public_share.clone() * a_fe + g * b_fe; 274 | 275 | ( 276 | PDLFirstMessage { 277 | c_tag: c_tag.clone(), 278 | c_tag_tag: c_tag_tag.clone(), 279 | }, 280 | PDLchallenge { 281 | c_tag, 282 | c_tag_tag, 283 | a, 284 | b, 285 | blindness, 286 | q_tag, 287 | }, 288 | ) 289 | } 290 | 291 | pub fn pdl_decommit_c_tag_tag(pdl_chal: &PDLchallenge) -> PDLSecondMessage { 292 | let decommit = PDLdecommit { 293 | a: pdl_chal.a.clone(), 294 | b: pdl_chal.b.clone(), 295 | blindness: pdl_chal.blindness.clone(), 296 | }; 297 | PDLSecondMessage { decommit } 298 | } 299 | 300 | pub fn verify_pdl( 301 | pdl_chal: &PDLchallenge, 302 | party_one_pdl_first_message: &Party1PDLFirstMessage, 303 | party_one_pdl_second_message: &Party1PDLSecondMessage, 304 | ) -> Result<(), ()> { 305 | let c_hat = party_one_pdl_first_message.c_hat.clone(); 306 | let q_hat = party_one_pdl_second_message.decommit.q_hat.clone(); 307 | let blindness = party_one_pdl_second_message.decommit.blindness.clone(); 308 | let c_hat_test = HashCommitment::create_commitment_with_user_defined_randomness( 309 | &q_hat.bytes_compressed_to_big_int(), 310 | &blindness, 311 | ); 312 | if c_hat.clone() == c_hat_test 313 | && q_hat.get_element().clone() == pdl_chal.q_tag.get_element().clone() 314 | { 315 | Ok(()) 316 | } else { 317 | Err(()) 318 | } 319 | } 320 | 321 | pub fn verify_range_proof( 322 | paillier_context: &PaillierPublic, 323 | range_proof: &RangeProofNi, 324 | ) -> Result<(), RangeProofError> { 325 | let result = range_proof.verify( 326 | &paillier_context.ek, 327 | &paillier_context.encrypted_secret_share, 328 | ); 329 | return result; 330 | } 331 | 332 | pub fn verify_ni_proof_correct_key( 333 | proof: NICorrectKeyProof, 334 | ek: &EncryptionKey, 335 | ) -> Result<(), CorrectKeyProofError> { 336 | proof.verify(&ek) 337 | } 338 | } 339 | 340 | impl EphKeyGenFirstMsg { 341 | pub fn create_commitments() -> (EphKeyGenFirstMsg, EphCommWitness, EphEcKeyPair) { 342 | let base: GE = ECPoint::generator(); 343 | 344 | let secret_share: FE = ECScalar::new_random(); 345 | 346 | let public_share = base.scalar_mul(&secret_share.get_element()); 347 | 348 | let h: GE = GE::base_point2(); 349 | let w = ECDDHWitness { 350 | x: secret_share.clone(), 351 | }; 352 | let c = &h * &secret_share; 353 | let delta = ECDDHStatement { 354 | g1: base.clone(), 355 | h1: public_share.clone(), 356 | g2: h.clone(), 357 | h2: c.clone(), 358 | }; 359 | let d_log_proof = ECDDHProof::prove(&w, &delta); 360 | 361 | // we use hash based commitment 362 | let pk_commitment_blind_factor = BigInt::sample(SECURITY_BITS); 363 | let pk_commitment = HashCommitment::create_commitment_with_user_defined_randomness( 364 | &public_share.bytes_compressed_to_big_int(), 365 | &pk_commitment_blind_factor, 366 | ); 367 | 368 | let zk_pok_blind_factor = BigInt::sample(SECURITY_BITS); 369 | let zk_pok_commitment = HashCommitment::create_commitment_with_user_defined_randomness( 370 | &HSha256::create_hash_from_ge(&[&d_log_proof.a1, &d_log_proof.a2]).to_big_int(), 371 | &zk_pok_blind_factor, 372 | ); 373 | 374 | let ec_key_pair = EphEcKeyPair { 375 | public_share, 376 | secret_share, 377 | }; 378 | ( 379 | EphKeyGenFirstMsg { 380 | pk_commitment, 381 | zk_pok_commitment, 382 | }, 383 | EphCommWitness { 384 | pk_commitment_blind_factor, 385 | zk_pok_blind_factor, 386 | public_share: ec_key_pair.public_share.clone(), 387 | d_log_proof, 388 | c, 389 | }, 390 | ec_key_pair, 391 | ) 392 | } 393 | } 394 | 395 | impl EphKeyGenSecondMsg { 396 | pub fn verify_and_decommit( 397 | comm_witness: EphCommWitness, 398 | party_one_first_message: &Party1EphKeyGenFirstMsg, 399 | ) -> Result { 400 | let delta = ECDDHStatement { 401 | g1: GE::generator(), 402 | h1: party_one_first_message.public_share.clone(), 403 | g2: GE::base_point2(), 404 | h2: party_one_first_message.c.clone(), 405 | }; 406 | party_one_first_message.d_log_proof.verify(&delta)?; 407 | Ok(EphKeyGenSecondMsg { comm_witness }) 408 | } 409 | } 410 | 411 | impl PartialSig { 412 | pub fn compute( 413 | ek: &EncryptionKey, 414 | encrypted_secret_share: &BigInt, 415 | local_share: &Party2Private, 416 | ephemeral_local_share: &EphEcKeyPair, 417 | ephemeral_other_public_share: &GE, 418 | message: &BigInt, 419 | ) -> PartialSig { 420 | let q = FE::q(); 421 | //compute r = k2* R1 422 | let mut r: GE = ephemeral_other_public_share.clone(); 423 | r = r.scalar_mul(&ephemeral_local_share.secret_share.get_element()); 424 | 425 | let rx = r.x_coor().unwrap().mod_floor(&q); 426 | let rho = BigInt::sample_below(&q.pow(2)); 427 | let k2_inv = &ephemeral_local_share 428 | .secret_share 429 | .to_big_int() 430 | .invert(&q) 431 | .unwrap(); 432 | let partial_sig = rho * &q + BigInt::mod_mul(&k2_inv, message, &q); 433 | let c1 = Paillier::encrypt(ek, RawPlaintext::from(partial_sig)); 434 | let v = BigInt::mod_mul( 435 | &k2_inv, 436 | &BigInt::mod_mul(&rx, &local_share.x2.to_big_int(), &q), 437 | &q, 438 | ); 439 | let c2 = Paillier::mul( 440 | ek, 441 | RawCiphertext::from(encrypted_secret_share.clone()), 442 | RawPlaintext::from(v), 443 | ); 444 | //c3: 445 | PartialSig { 446 | c3: Paillier::add(ek, c2, c1).0.into_owned(), 447 | } 448 | } 449 | } 450 | -------------------------------------------------------------------------------- /src/protocols/two_party_ecdsa/lindell_2017/test.rs: -------------------------------------------------------------------------------- 1 | // For integration tests, please add your tests in /tests instead 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | 6 | use curv::arithmetic::traits::Samplable; 7 | use curv::elliptic::curves::traits::*; 8 | use curv::BigInt; 9 | use protocols::two_party_ecdsa::lindell_2017::*; 10 | 11 | #[test] 12 | fn test_d_log_proof_party_two_party_one() { 13 | let (party_one_first_message, comm_witness, _ec_key_pair_party1) = 14 | party_one::KeyGenFirstMsg::create_commitments(); 15 | let (party_two_first_message, _ec_key_pair_party2) = party_two::KeyGenFirstMsg::create(); 16 | let party_one_second_message = party_one::KeyGenSecondMsg::verify_and_decommit( 17 | comm_witness, 18 | &party_two_first_message.d_log_proof, 19 | ) 20 | .expect("failed to verify and decommit"); 21 | 22 | let _party_two_second_message = 23 | party_two::KeyGenSecondMsg::verify_commitments_and_dlog_proof( 24 | &party_one_first_message, 25 | &party_one_second_message, 26 | ) 27 | .expect("failed to verify commitments and DLog proof"); 28 | } 29 | 30 | #[test] 31 | 32 | fn test_full_key_gen() { 33 | let (party_one_first_message, comm_witness, ec_key_pair_party1) = 34 | party_one::KeyGenFirstMsg::create_commitments_with_fixed_secret_share(ECScalar::from( 35 | &BigInt::sample(253), 36 | )); 37 | let (party_two_first_message, _ec_key_pair_party2) = 38 | party_two::KeyGenFirstMsg::create_with_fixed_secret_share(ECScalar::from( 39 | &BigInt::from(10), 40 | )); 41 | let party_one_second_message = party_one::KeyGenSecondMsg::verify_and_decommit( 42 | comm_witness, 43 | &party_two_first_message.d_log_proof, 44 | ) 45 | .expect("failed to verify and decommit"); 46 | 47 | let _party_two_second_message = 48 | party_two::KeyGenSecondMsg::verify_commitments_and_dlog_proof( 49 | &party_one_first_message, 50 | &party_one_second_message, 51 | ) 52 | .expect("failed to verify commitments and DLog proof"); 53 | 54 | // init paillier keypair: 55 | let paillier_key_pair = 56 | party_one::PaillierKeyPair::generate_keypair_and_encrypted_share(&ec_key_pair_party1); 57 | 58 | let party_one_private = 59 | party_one::Party1Private::set_private_key(&ec_key_pair_party1, &paillier_key_pair); 60 | 61 | let party_two_paillier = party_two::PaillierPublic { 62 | ek: paillier_key_pair.ek.clone(), 63 | encrypted_secret_share: paillier_key_pair.encrypted_share.clone(), 64 | }; 65 | 66 | let correct_key_proof = 67 | party_one::PaillierKeyPair::generate_ni_proof_correct_key(&paillier_key_pair); 68 | party_two::PaillierPublic::verify_ni_proof_correct_key( 69 | correct_key_proof, 70 | &party_two_paillier.ek, 71 | ) 72 | .expect("bad paillier key"); 73 | // zk proof of correct paillier key 74 | 75 | // zk range proof 76 | let range_proof = party_one::PaillierKeyPair::generate_range_proof( 77 | &paillier_key_pair, 78 | &party_one_private, 79 | ); 80 | let _result = 81 | party_two::PaillierPublic::verify_range_proof(&party_two_paillier, &range_proof) 82 | .expect("range proof error"); 83 | 84 | // pdl proof minus range proof 85 | let (party_two_pdl_first_message, pdl_chal_party2) = 86 | party_two_paillier.pdl_challenge(&party_one_second_message.comm_witness.public_share); 87 | 88 | let (party_one_pdl_first_message, pdl_decommit_party1) = 89 | party_one::PaillierKeyPair::pdl_first_stage( 90 | &party_one_private, 91 | &party_two_pdl_first_message, 92 | ); 93 | 94 | let party_two_pdl_second_message = 95 | party_two::PaillierPublic::pdl_decommit_c_tag_tag(&pdl_chal_party2); 96 | let party_one_pdl_second_message = party_one::PaillierKeyPair::pdl_second_stage( 97 | &party_one_pdl_first_message, 98 | &party_two_pdl_first_message, 99 | &party_two_pdl_second_message, 100 | party_one_private, 101 | pdl_decommit_party1, 102 | ) 103 | .expect("pdl error party2"); 104 | 105 | party_two::PaillierPublic::verify_pdl( 106 | &pdl_chal_party2, 107 | &party_one_pdl_first_message, 108 | &party_one_pdl_second_message, 109 | ) 110 | .expect("pdl error party1") 111 | } 112 | 113 | #[test] 114 | fn test_two_party_sign() { 115 | // assume party1 and party2 engaged with KeyGen in the past resulting in 116 | // party1 owning private share and paillier key-pair 117 | // party2 owning private share and paillier encryption of party1 share 118 | let (_party_one_private_share_gen, _comm_witness, ec_key_pair_party1) = 119 | party_one::KeyGenFirstMsg::create_commitments(); 120 | let (party_two_private_share_gen, ec_key_pair_party2) = party_two::KeyGenFirstMsg::create(); 121 | 122 | let keypair = 123 | party_one::PaillierKeyPair::generate_keypair_and_encrypted_share(&ec_key_pair_party1); 124 | 125 | // creating the ephemeral private shares: 126 | 127 | let (eph_party_two_first_message, eph_comm_witness, eph_ec_key_pair_party2) = 128 | party_two::EphKeyGenFirstMsg::create_commitments(); 129 | let (eph_party_one_first_message, eph_ec_key_pair_party1) = 130 | party_one::EphKeyGenFirstMsg::create(); 131 | let eph_party_two_second_message = party_two::EphKeyGenSecondMsg::verify_and_decommit( 132 | eph_comm_witness, 133 | &eph_party_one_first_message, 134 | ) 135 | .expect("party1 DLog proof failed"); 136 | 137 | let _eph_party_one_second_message = 138 | party_one::EphKeyGenSecondMsg::verify_commitments_and_dlog_proof( 139 | &eph_party_two_first_message, 140 | &eph_party_two_second_message, 141 | ) 142 | .expect("failed to verify commitments and DLog proof"); 143 | let party2_private = party_two::Party2Private::set_private_key(&ec_key_pair_party2); 144 | let message = BigInt::from(1234); 145 | let partial_sig = party_two::PartialSig::compute( 146 | &keypair.ek, 147 | &keypair.encrypted_share, 148 | &party2_private, 149 | &eph_ec_key_pair_party2, 150 | &eph_party_one_first_message.public_share, 151 | &message, 152 | ); 153 | 154 | let party1_private = 155 | party_one::Party1Private::set_private_key(&ec_key_pair_party1, &keypair); 156 | 157 | let signature = party_one::Signature::compute( 158 | &party1_private, 159 | &partial_sig.c3, 160 | &eph_ec_key_pair_party1, 161 | &eph_party_two_second_message.comm_witness.public_share, 162 | ); 163 | 164 | let pubkey = 165 | party_one::compute_pubkey(&party1_private, &party_two_private_share_gen.public_share); 166 | party_one::verify(&signature, &pubkey, &message).expect("Invalid signature") 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /src/protocols/two_party_ecdsa/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Multi-party ECDSA 3 | 4 | Copyright 2018 by Kzen Networks 5 | 6 | This file is part of Multi-party ECDSA library 7 | (https://github.com/KZen-networks/multi-party-ecdsa) 8 | 9 | Multi-party ECDSA is free software: you can redistribute 10 | it and/or modify it under the terms of the GNU General Public 11 | License as published by the Free Software Foundation, either 12 | version 3 of the License, or (at your option) any later version. 13 | 14 | @license GPL-3.0+ 15 | */ 16 | 17 | // Fast Secure Two-Party ECDSA Signing by Yehuda Lindell (https://eprint.iacr.org/2017/552.pdf). 18 | pub mod lindell_2017; 19 | --------------------------------------------------------------------------------