{"id":13632427,"url":"https://github.com/ing-bank/threshold-signatures","last_synced_at":"2025-04-18T02:33:05.250Z","repository":{"id":46204297,"uuid":"316285028","full_name":"ing-bank/threshold-signatures","owner":"ing-bank","description":"Threshold Signature Scheme for ECDSA","archived":true,"fork":false,"pushed_at":"2023-09-01T12:01:44.000Z","size":1565,"stargazers_count":199,"open_issues_count":0,"forks_count":41,"subscribers_count":19,"default_branch":"master","last_synced_at":"2024-08-01T22:53:18.364Z","etag":null,"topics":["bitcoin-transaction","cryptocurrency","cryptography","ecdsa","ethereum","homomorphic-encryption","rust","secure-multi-party-computation","zero-knowledge-proofs"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ing-bank.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-11-26T16:32:58.000Z","updated_at":"2024-06-18T08:23:04.000Z","dependencies_parsed_at":"2024-01-13T15:37:22.740Z","dependency_job_id":"7e0e99a6-eb6a-4810-a500-6e0501c20f45","html_url":"https://github.com/ing-bank/threshold-signatures","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ing-bank%2Fthreshold-signatures","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ing-bank%2Fthreshold-signatures/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ing-bank%2Fthreshold-signatures/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ing-bank%2Fthreshold-signatures/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ing-bank","download_url":"https://codeload.github.com/ing-bank/threshold-signatures/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223772227,"owners_count":17199971,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bitcoin-transaction","cryptocurrency","cryptography","ecdsa","ethereum","homomorphic-encryption","rust","secure-multi-party-computation","zero-knowledge-proofs"],"created_at":"2024-08-01T22:03:02.840Z","updated_at":"2024-11-09T00:31:19.622Z","avatar_url":"https://github.com/ing-bank.png","language":"Rust","funding_links":[],"categories":["Rust","TSS list"],"sub_categories":[],"readme":"# Multiparty threshold ECDSA scheme\n\n# Warning\nThis repository is no longer maintained. There are known security issues as pointed [in this article.](https://www.verichains.io/tsshock/) No updates or fixes actively being pursued. The library was implemented for research purposes and not used in production within the organization. ING strongly advises that you do NOT use this library in your code and use this code ONLY as a reference for research.\n\n## Introduction\n\nThe project contains the implementation of the multiparty threshold signature scheme constructed by Rosario Gennaro and Steven Goldfeder [1].\nThe scheme consists of three algorithms performed in a distributed context: a key generation with N parties,\nan arbitrary message signing with a subgroup of [(t+1) of N] parties holding the key,\nand the key resharing with (t+1) parties resulting in a new group of M players holding new shares of the same private signing key.\n\nThis README file focuses on sharing detailed technical information about the library. Check our whitepaper [5] for theoretical backgrounds.  \n\nThe library has been reviewed by Kudelski Security company, see [The security risk assessment report](/docs/report_ing_tss_1.0.pdf).\n\n## Motivation\n\nElliptic curve digital signature algorithm (ECDSA) is used extensively for crypto-currencies such as Bitcoin and  Ethereum to sign transactions.  \nMultiparty threshold signing adds a possibility to create crypto-currency wallets that are controlled by multiple\nparties. Transactions which originate from these wallets require collaborative work to sign.\nThreshold signing produces standard signature output,\nand it preserves the privacy of signers by not disclosing their identities, unlike multi-signature does.\nThe algorithm used in this product eliminates a central dealer( the party which generates the full key and distributes shards of it to parties). \n\nAs the MPC TS technology has become increasingly popular in the last 2 years, there are quite a few open-source implementations.\nThe detailed review of them can be found in [6].\nOur version improves existing solutions by addition of several key features:\n* The protocols collect all results of every verification step instead of aborting that step when a first error occurs.\n All errors are packed in a container and reported back to a caller of the machine after the machine stops. \n Every error is associated with party id (the origin of the error) so that the application may identify malicious behavior or an active attack pattern. \n* Parties are identified in the protocol by PartyId structure. It makes a party identifier unique and independent from other parameters of the protocol, such as x-coordinate in Shamir secret sharing. \n* The list of ids of parties that generated a key is stored next to that key. It allows the signing algorithm to choose a quorum from parties that are known to be online.\n* Protocol timeout detection is supported by the state machine.     \n* The initial partial private key is not copied to the protocol memory. \nInstead, the machine fetches the key via the SecureKeyLoader interface from an extern secure vault.\nFetching happens when the key is needed for the generation of shares, and the key bytes are zeroed afterward.\n* The original protocol is extended with key re-sharing algorithm.       \n \nWe also designed the library with the separation of concern (SoC) principle in mind. \nThis library encompasses the complete protocol logic that includes party identity management, error reporting, and timeout detection. These activities are not delegated to the application, unlike in some other implementations. \nOnly a basic network layer has to be added to wrap the library into a standalone application. This layer has to maintain the mapping between party id and corresponding OS-level connector (e.g. socket),\nand to parse the *destination* attribute of any message.\nIn addition to that, peer-directed messages have to be encrypted and decrypted. The library code does not contain an encryption schema for secret messages,\nas that would restrict schema choices and add schema's key management duties.\n \nThe set of responsibilities of the library layer is determined to the following:\n* use broadcast message type to share messages with all parties. The network layer knows which parties are involved in a session, so it forwards the message to all of them.\n* address peer-to-peer generated messages to correct parties using partyId.\n* check the attribute *source* of type PartyId in every message,  which turns useful for the detection of duplicated or lost messages.\n* do all crypto work.\n\nWe also paid attention to the usage of :\n* broad range of language constructions offered by Rust, e.g. ?-operator, match with conditions, etc. \n* pure functional programming style in coding to improve verifiability of algorithms. \n* nested error code schemas. \n* futures and async/await pattern for running every algorithm.\n* very detailed optional function call tracing.\n\n\n## Signature schema\nThe ECDSA consist of 3 algorithms ([1], 2.1, 2.5):\n\n* Key generation\n* Signing \n* Verification\n\n### Key generation\n\nThe initial partial key is sampled from randomness by each party,\nso that the resulting public key is the product of partial public keys\nand the resulting private key is the sum of partial private keys.\n\nPrivate keys are shared with Shamir's secret sharing schema [2] where its signing threshold becomes the attribute of the resulting key.\nAdditional non-malleable commitment to a partial public key is shared to prevent the classic attack on [2]. \n \nSince the signing algorithm uses additive homomorphic encryption schema (AHE) [3],\nthe setup of range proofs [4] is required ([1] addendum A) at the key generation stage.\n\n### Signing \n\nThe ECDSA requires a random coefficient that needs to be multiplied by private key during the signing.\nThe result of multiplication has to be shared additively in case of a multi-party setup.\nTo get an additive sharing of the multiple, it is sufficient to get the additive sharing of each individual term, but each term contains secrets of two different parties.\nTo overcome the privacy problem the algorithm uses multiplication-to-addition (MtA) 2-party protocol ([1], 3). The MtA protocol uses the AHE scheme [3] with its public key shared before signing. \n\nIn addition to that, several zero-knowledge proofs are generated at the last stages of the signing protocol\nto prevent the algorithm from producing a wrong signature ([1], 4.2, phase 5). Every party receives the complete signature at the end. \n\n### Verification\nThe protocol outputs the standard signature which is publicly verifiable. The verification does not require interactions with parties.\n\n\n## Execution model\n\nThe library implements the dedicated state machine to run protocols.\nEach protocol is a sequence of phases, and each phase consists of three steps: optional message sending, waiting for input, and consuming the collected input.\nThe consumption results in either transitioning to another phase or stopping the machine and returning either the final result or a collection of error codes.\nThe machine may also be stopped by reaching a timeout, or it can be aborted by sending a special message to it. The aborted machine returns no result. \n  \nDue to the asynchronous nature of protocols the state machine has a buffer for messages\nwhich could be awaited by the next phases of the protocol but arrived too early. \n\nThe state machine code can be executed concurrently by independent threads.\nEvery phase of each protocol has a local context that is not shared with other phases and instances of the protocol.\nAs a consequence, the MPC application can run multiple sessions of key generation and sign simultaneously.\nNote that the library itself does not handle sessions, it is the main application that has to run multiple state machines and route network streams to/from these machines accordingly.\n\n## Usage\n\n### Library structure\n\nThe library consist of three main modules:  \n\n* **ecdsa**, where the protocols are implemented  \n* **state machine**, which drives a protocol through its phases\n* **algorithms**, where range proofs are set up, generated and verified\n   \n   \n### Examples\n\n#### Key generation \n\nThe example is taken from the test submodule of keygen module with some minor details omitted.\n\nThe input for the protocol contains:\n*  Values of t and n in the threshold {t,n} \n*  Party index of the node\n*  List of party indexes of all participants, including this node\n*  Initial keys - public part   \n*  Reference to a wallet: storage for initial keys\n*  A secret loader which fetches keys from the wallet\n*  Optional range proof setup ( Note: the signing protocol is proven to be insecure when used without range proofs. Current version of the library returns error if range proof setup is not presented to keygen protocol)\n*  Optional protocol timeout (recommended to be provided). \n \nThe first phase of the protocol is created by calling **Phase1::new()** method, which takes all the parameters above.\n\nThe state machine is created next, with the following input:\n\n* Instance of Phase1\n* Instance of stream and instance of sink.  \n\nTwo distinct versions of the state machine in this library support two types for stream/sink:\n* either StreamExt and SinkExt types from futures crate\n* or Sender and Receiver from crossbeam_channel crate.\n       \n```\n   // set protocol parameters\n   let params = Parameters {\n                share_count: 3,\n                threshold: 1,\n            };\n   // collect list of parties\n   let parties : Vec\u003cPartyIndex\u003e = ...\n   // determine the party index of this node  \n   let own_party_index = ... \n   // create initial random keys\n   let init_keys = InitialKeys::random();\n   let init_pub_keys = InitialPublicKeys::from(\u0026init_keys);\n   // create some wallet (a peristence provider) and the secret loader\n   // secret loader acts as proxy fetching secret keys from the wallet \n   // [see the example in ecds::keygen::test module]      \n   let shared_wallet = MyWallet::new(init_keys);    \n   let secret_loader = SecretKeyLoaderImpl::new(\u0026shared_wallet, own_party_index);\n\n   let start_state = Box::new(Phase1::new(\n                    \u0026params,\n                    init_pub_keys,\n                    range_proof_setup,\n                    parties.as_slice(),\n                    own_party_index,\n                    Arc::new(Box::new(secret_loader)),\n                    None,\n                )?);\n   \n   \n   // Channel to pick messages the state machine sends\n   let (state_machine_sink, state_machine_stream) = mpsc::unbounded();\n   // Channel to feed the machine\n   let (protocol_sink, protocol_stream) = mpsc::unbounded();\n   let state_machine = StateMachine::new(start_phase, protocol_stream, state_machine_sink);\n   \n   ...\n   \n    tokio::spawn(async move {\n        let result = match state_machine.execute().await.transpose() {\n            Ok(Some(fs)) =\u003e Outcome::Keygen(Ok(fs)),\n            Ok(None) =\u003e {\n               Outcome::Keygen(Err(ErrorState::new(vec![KeygenError::GeneralError {\n                        desc: \"Keygen did not produce a result\".to_string(),\n                    }])))\n                }\n            Err(err) =\u003e Outcome::Keygen(Err(err)),\n            };\n            ...\n            \n```\n\n##### Signing\n\nThe input of the protocol contains:\n* The hash of a message \n* The output of keygen protocol, MultiPartyInfo structure\n* Party indexes of participants, including own party index. Note that the latter is stored in MultiPartyInfo. \n\nThe sequence of actions for signing is similar to keygen: first, the phase object is created, and then the state machine is.\nSimilarly to the key generation protocol, state machine requires first phase and communication channels.\n\nNote that the signing protocol expects a message to be hashed outside of this library (see module documentation in signature.rs).   \n\n```\n     let mut hasher = Sha256::new();\n     hasher.input(\"The message we sign\");\n     let msg_hash = ECScalar::from(\u0026BigInt::from(hasher.result().as_slice()));\n\n     let start_phase: BoxedState\u003cInMsg, OutMsg, MachineResult\u003e = Box::new(Phase1::new(\n                    \u0026msg_hash,\n                    multi_party_shared_info,\n                    \u0026quorum.parties,\n                ));\n                let mut main_machine =\n                    StateMachine::new(start_phase, to_main_machine, from_main_machine);\n                let machine_result = main_machine.execute().await;\n                let outcome = match machine_result.transpose() {\n                    Ok(Some(fs)) =\u003e Outcome::Signature(Ok(fs)),\n                    Ok(None) =\u003e {\n                        Outcome::Signature(Err(ErrorState::new(vec![SigningError::GeneralError {\n                            desc: \"Signing algorithm did not produce a result\".to_string(),\n                        }])))\n                    }\n                    Err(err) =\u003e Outcome::Signature(Err(err)),\n                };\n```   \n\n### Building the documentation\n\nThe library uses LaTex mathematical symbols in the documentation so that embedded docs have to be built with predefined HTML header (included into the project):\n\n```\ncargo rustdoc  --  --html-in-header katex.html --document-private-items\n```      \n\nThe nightly build of *rustdoc* is not required: the code is documented using traditional reference link style.\n\n### Using examples\n\n#### The simulator of the key generation protocol \n\nThe complete result of the protocol can be obtained in JSON format by running the command:\n\n```cargo run --example keygen min_number_of_signers total_number_of_signers output_file_name_prefix  [--use-range-proofs]```\n\nThe run results in creating  #total_number_of_signers# files containing MultiPartyInfo structure serialized into JSON.\nFile names can be tweaked by *output_file_name_prefix*.\n\nNote: the signing protocol is proven to be **insecure when used without range proofs**. The keygen example quits with the error if the range proof setup option is not used)\n\n#### The generator of zero knowledge range proof setup\n\nRecall that the ZKRP setup requires safe primes, for which the algorithm is not particularly fast.\nIf the generation of new ZKRP setups for each new key has to be avoided, then setups have to be pre-computed.  \nThe following command generates as many as **n**  setups and prints them to the standard output:\n\n``` cargo run  --release --example  zkp-setup n  \u003eoutfile.json ```\n  \n\n#### The safe prime generator\n\nSafe primes used in ZKRP can also be generated independently by the following command, where n is desired number of pairs of safe primes:\n\n``` cargo run  --release --example safe-primes n  \u003eoutfile.json ```\n\n\n## Securing an application\n\n* The library-wrapping application has to provide a secure reliable way of authenticating nodes during initial connection, as well as a secure messaging.\nFor example, the application can use TLS protocol to establish initial peer-to-peer connections between nodes known to each other  by their public certificates,\nso that the authentication will be based on the certificate chain, and the security will be\nprovided by DHKE and block ciphers.  \n\n* As shown in the examples above, the ecdsa protocol API does not support an authentication or authorization of the caller through API. API is designed without notion of an user of the node. \nAs a result, any node in multiparty setup can initiate key generation, signing, or key refresh, unless other nodes employ a kind of authorization schema. \nIn case a schema is put into place, the responsibility of the application layer will be to verify whether the initiator node has privileges to start a computation.\nSuch schema likely requires preliminary round(s) in the protocol, e.g. sending JoinSession message and receiving ACK/NACK messages. This pre-round is currently not implemented by the library, but \nthe state machine code can be used alone for this purpose. \n\n## Other tech remarks\n\n* The library uses the curve *secp256k1* only. Using other curves is possible but requires the code to be rebuilt.  \n* The library does not implement a network transport layer.\n* The library's internal architecture relies on the notion of a PartyID, an identifier of a party.\n* The library emits messages of 2 types, broadcast and peer2peer so that the transport layer has to forward them to other parties accordingly.\n\n* The application has to deliver broadcasts reliably so that when a party sends a broadcast it's guaranteed that each party receives the same message. \n\n## References\n\\[1\\] Gennaro, R., Goldfeder, S.: [Fast multiparty threshold ecdsa with fast trustless setup.](https://eprint.iacr.org/2019/114.pdf)  \n\n\\[2\\] Shamir, A.: How to share a secret. Communications of the ACM 22(11), 612–613 (1979)\n\n\\[3\\] Paillier, P.: Public-key cryptosystems based on composite degree residuosity classes. In: International Conference on the Theory and Applications of Cryptographic Techniques. pp. 223–238. Springer\n\n\\[4\\] Eiichiro Fujisaki, Tatsuaki Okamoto: Statistical zero knowledge protocols to prove modular polynomial relations. Advances in Cryptology — CRYPTO '97 pp16-30\n\n\\[5\\] Tillem, G., Burundukov, O.: [Threshold signatures using Secure Multiparty Computation.](https://new.ingwb.com/binaries/content/assets/insights/themes/distributed-ledger-technology/ing-releases-multiparty-threshold-signing-library-to-improve-customer-security/threshold-signatures-using-secure-multiparty-computation.pdf)   \n\n\\[6\\] Aumasson, J.P, Hamelink, A., Shlomovits, O.: [A Survey of ECDSA Threshold Signing.](https://eprint.iacr.org/2020/1390.pdf)\n\n## License \nThe product is released under the terms of the MIT license. See LICENSE for more information.\n\n## How to contribute\n\nSee CONTRIBUTING.md\n\n## Contacts\n\n\u003coleg.burundukov@ing.com\u003e\n\n\u003cblockchain@ing.com\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fing-bank%2Fthreshold-signatures","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fing-bank%2Fthreshold-signatures","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fing-bank%2Fthreshold-signatures/lists"}