Skip to content

PyFrost Documentation

Seyed Ali YaghoubNejad edited this page Jan 4, 2024 · 1 revision

Cryptography Classes & Functions

The FROST cryptographic functions and classes that support DKG (Distributed Key Generation) and represent keys can be found in pyfrost.frost. Below is a detailed discussion of each:

pyfrost.KeyGen(self, dkg_id, threshold, n, node_id, partners, coefficient0=None)
  • Description: This class generates a distributed key using the FROST algorithm. After generating the key, you should save KeyGen.dkg_key_pair and can then dispose of this object.

  • Inputs:

    • dkg_id str: Identifier for the key to be generated.
    • threshold int: The key's threshold, denoted as $t$.
    • n int: Total number of nodes involved in key generation.
    • node_id str: Identifier of the current node.
    • partners List[str]: List of IDs for all nodes participating in key generation.
    • coefficient0 int, default=None: Coefficient of $x^0$ (i.e., $a_0$) in the polynomial from round1().
  • Example:

    >>> dkg_id = # Example data
    >>> threshold = # Example data
    >>> n = # Example data
    >>> node_id = # Example data
    >>> partners = # Example data
    >>> key_gen = KeyGen(dkg_id, threshold, n, node_id, partners)
  • Functions:

    pyfrost.KeyGen.round1(self)
    • Description: Begins the distributed key generation (DKG) process. It generates a key pair for secure communication and a $t$-degree polynomial for the distributed key. It then returns the information that needs to be broadcast to other nodes in the next DKG round.
    • Inputs: None
    • Outputs:
      • A dictionary of data to be broadcast to all nodes in the party.
    • Example:
      >>> key_gen.round1()
      {
          'coefficient0_signature': {
              'nonce': 368942311427213970711406313762610833921296939621501937670638697118598845895953,
              'signature':            '0x52ca633d6e48489339ebe80bf1489d36c99c68ed1f5c61b8db09ecfc917557007812ebeee6dece5c0e1aded3ea573c8e6af7eaad4cd96113668da6ffa0736a22'
          },
          'public_fx': [
              296640523581457280785321718557273115703301708752107592117643511688805891761985,
              319296799114006137153383306229340950225807211995602402243547077190257497474828
          ],
          'public_key': 307729343603318599981084289508680930914727331224976629990450023311552521321408,
          'secret_signature': {
              'nonce': 367543012542554800983038202897799609990958678036817100887904340714911085163518,
              'signature': '0x22b4921be19840bc3c944498f15bca02ad491795b4e55fa4eb3429b6ecca66b55526dc8425f6d49b72ee5370332368251f776d85f49f7cd89a1cda7000c25821'
          },
          'sender_id': '1'
      }
    pyfrost.KeyGen.round2(self, round1_broadcasted_data)
    • Description: Handles the second DKG round by processing round1_broadcasted_data from other nodes. It generates data for inter-node sharing, encrypted with the sender's private key and the receiver's public key from round1.
    • Inputs:
      • round1_broadcasted_data dict: Data broadcast in round1 by all other party nodes.
    • Outputs:
      • A dictionary of data to be sent to each node.
    • Example:
      >>> broadcasted_data = [
          {
              'coefficient0_signature': {
                  'nonce': 368942311427213970711406313762610833921296939621501937670638697118598845895953,
                  'signature': '0x52ca633d6e48489339ebe80bf1489d36c99c68ed1f5c61b8db09ecfc917557007812ebeee6dece5c0e1aded3ea573c8e6af7eaad4cd96113668da6ffa0736a22'
              },
              'public_fx': [
                  296640523581457280785321718557273115703301708752107592117643511688805891761985,
                  319296799114006137153383306229340950225807211995602402243547077190257497474828
              ],
              'public_key': 307729343603318599981084289508680930914727331224976629990450023311552521321408,
              'secret_signature': {
                  'nonce': 367543012542554800983038202897799609990958678036817100887904340714911085163518,
                  'signature': '0x22b4921be19840bc3c944498f15bca02ad491795b4e55fa4eb3429b6ecca66b55526dc8425f6d49b72ee5370332368251f776d85f49f7cd89a1cda7000c25821'
              },
              'sender_id': '1'
          },
          {
              'coefficient0_signature': {
                  'nonce': 359282259074835379588366874028459345193077877777157055998768953650753776127657,
                  'signature': '0x949dcaa392ed3b5eaace5bc0eca84149e377886bcfe70044e773d2b22b44e827c9f48274de0393f2a5160eb34f47bc71b53126559ee49aa3aaa47a5fe1e6f81a'
              },
              'public_fx': [
                  315333036490095729854689446035420143622165005650260428363337601379831192199151,
                  380936697578552696715385406651703109495058306480643748360338356258272131197597
              ],
              'public_key': 402675427562676615467995045686975527154837997106518064654470189954486747681481,
              'secret_signature': {
                  'nonce': 383615891867749631411403307342088317807707823769149008762193706230308867138628,
                  'signature': '0xf531cdf1e708fa8b8d3887eb8698230e99062ad59d204793b84aa4b82678e035399d0205ba340c6457ffe0552bd42f101a553e7082ee6043b31110bdc40bbe6c'
              },
              'sender_id': '2'
          }
      ]
      
      >>> key_gen.round2(broadcasted_data)
      [
          {
              'data': 'gAAAAABliu7Eb9BTXwCmwWD3lX6el4ubygJ0SmyZpWNAMHR3-GEedQ5GuZshqy6VyuuRBrAfnET1iN1y9qeqK21NW-GuIEeNn5YQwVrf4_CTmZdxYjQXXpPAFHkRhJQk4nCCeOj5nRF5mllvpKp88-h_sGCQbaGERBiPjQUfmZNYX93puo1T6h3Yve7__uSuElgyU_WNsEWsKNR04utLEpQI0fCrAUy70A==',
              'receiver_id': '2',
              'sender_id': '1'
          }
      ]
    pyfrost.KeyGen.round3(self, round2_encrypted_data)
    • Description: Computes the node's share of the distributed key. Reports the share and corresponding key, signed with the node's permanent secret for verification. If it detects dishonesty from other nodes, it reports the malicious activity.
    • Inputs:
      • round2_encrypted_data dict: Data from round2 where this node is the receiver.
    • Outputs:
      • A dictionary containing status and data. On SUCCESSFUL status, data includes the distributed key, the node's share, and a signature. On COMPLAINT status, it includes evidence of dishonesty.
    • Example:
      >>> secret_data = [
          {
              'data': 'gAAAAABliu7E9tMtv2EQeobFyDqXfnP024HnQLzWr_NeGKXB0aQhAiuyGOI490sZ031vMYm0yjRYOFcWvcxMXTPEDC9vMzB426NwzIGFWcfdB4KlC2KXctDqltF22v7N1ZbhpXNUpZqHc-iUL0_i7sOx5KYOoTO72Z_v08WzAPI7D6S8hAo2W_WD9l0ThrDqKUjTk1D1Lb1OXJ9XUOxYva9VDMFM67tWcw==',
              'receiver_id': '1',
              'sender_id': '2'
          }
      ]
      
      >>> key_gen.round3(secret_data)
      {
          'data': {
              'dkg_public_key': 254503007646527296878921173802219136565731252590780601714649547124390052407428,
              'public_share': 434857518911403231281957735014346859455075805194991608153904732048854704562605
          },
          'dkg_key_pair': {
              'dkg_public_key': 254503007646527296878921173802219136565731252590780601714649547124390052407428,
              'share': 188845194665176481294923561223805344235489278818067835766967751779434009802288
          },
          'status': 'SUCCESSFUL'
      }
pyfrost.Key(self, dkg_key, node_id)
  • Description: Represents a FROST key used for signing. It encapsulates the functionality related to the node's share of the distributed key.

  • Inputs:

    • dkg_key Dict: Contains the node's share of the key and the corresponding distributed public key.
    • node_id str: Identifier of the current node.
  • Example:

    >>> key_pair = # Example data
    >>> peer_id = # Example data
    >>> key = Key(key_pair, peer_id)
  • Functions:

    pyfrost.Key.sign(self, commitments_dict, message, nonces)
    • Description: Generates a part of the signature using the node's share of the distributed key.
    • Inputs:
      • commitments_dict Dict: Contains nonces from all nodes involved in signature generation (should be at least $t$).
      • message str: The message to be signed.
      • nonces Dict: All nonces generated by this node.
    • Outputs:
      • Returns a tuple (signature, used_nonce). signature is a dictionary with the signature part generated by this node. used_nonce indicates which nonce was used and should be discarded.
    • Example:
      >>> nonces_list = {
          '1': {
              'id': 1,
              'public_nonce_d': 349042186269687657903918029097021035946240790024363563457247123869466727724904,
              'public_nonce_e': 248237425400201679908511118467475923008578021798639515998329304887202095480425
          },
          '2': {
              'id': 2,
              'public_nonce_d': 232003305522782618192381500560408473570697895814518143723528305421500512412148,
              'public_nonce_e': 317715046800072131083613892686194614787279733126861218939601214450999918821174
          }
      }
      >>> message_hash = '651ecdfb1cbc7917644f5c3e16f014ac416470b990793e0ed5c2d2eb892d8c7f'
      >>> nonces = [
          {
              'nonce_d_pair': {
                  300218555050360092639063393209864207073501829680650048817838305094710368296737: 50204928300747094776366629272534245963307256198160244371740708017884005477910
              },
              'nonce_e_pair': {
                  409714228498631570327782540584825593750693294519645628405851081581014656246735: 40463726040983254619472478804244838052786844837455652103326243989755717359788
              }
          },
          {
              'nonce_d_pair': {
                  349042186269687657903918029097021035946240790024363563457247123869466727724904: 16993156636626924328333038649772197657544489298745516775023187369640192487374
              },
              'nonce_e_pair': {
                  248237425400201679908511118467475923008578021798639515998329304887202095480425: 100635936258925530229391130527915553787668889110040645976804851747656014449457
              }
          }
      ]
      >>> key.sign(nonces_list, message_hash, nonces)
      (
          {
              'aggregated_public_nonce': 402344336404068161625970842224046931385487524799112280264088906225521846642963,
              'id': 1,
              'public_key': 434857518911403231281957735014346859455075805194991608153904732048854704562605,
              'signature': 102694053038190246370608485838590127748578269475777603993528845385451820908381
          },
          {
              'nonce_d_pair': {
                  349042186269687657903918029097021035946240790024363563457247123869466727724904: 16993156636626924328333038649772197657544489298745516775023187369640192487374
              },
              'nonce_e_pair': {
                  248237425400201679908511118467475923008578021798639515998329304887202095480425: 100635936258925530229391130527915553787668889110040645976804851747656014449457
              }
          }
      )
pyfrost.create_nonces(node_id, number_of_nonces)
  • Description: Generates a set of nonces for use in signature issuance.
  • Inputs:
    • node_id int: Identifier of the current node.
    • number_of_nonces int, default=10: Number of nonces to be generated.
  • Outputs:
    • Returns a tuple (nonce_publics, nonce_privates). nonce_publics is a dictionary with the public part of the nonce to be sent to the Signature Aggregator (SA). nonce_privates contains the private part of the nonce, which should be stored in the Node.
  • Example:
    >>> node_id = 1
    >>> number_of_nonces = 2
    >>> pyfrost.create_nonces(node_id, number_of_nonces)
      (
          [
              {
                  'id': 1,
                  'public_nonce_d': 300218555050360092639063393209864207073501829680650048817838305094710368296737,
                  'public_nonce_e': 409714228498631570327782540584825593750693294519645628405851081581014656246735
              },
              {
                  'id': 1,
                  'public_nonce_d': 349042186269687657903918029097021035946240790024363563457247123869466727724904,
                  'public_nonce_e': 248237425400201679908511118467475923008578021798639515998329304887202095480425
              }
          ],
          [
              {
                  'nonce_d_pair': {
                      300218555050360092639063393209864207073501829680650048817838305094710368296737: 50204928300747094776366629272534245963307256198160244371740708017884005477910
                  },
                  'nonce_e_pair': {
                      409714228498631570327782540584825593750693294519645628405851081581014656246735: 40463726040983254619472478804244838052786844837455652103326243989755717359788
                  }
              },
              {
                  'nonce_d_pair': {
                      349042186269687657903918029097021035946240790024363563457247123869466727724904: 16993156636626924328333038649772197657544489298745516775023187369640192487374
                      },
                  'nonce_e_pair': {
                      248237425400201679908511118467475923008578021798639515998329304887202095480425: 100635936258925530229391130527915553787668889110040645976804851747656014449457
                  }
              }
          ]
      )
pyfrost.aggregate_nonce(message, commitments_dict, group_key)
  • Description: Aggregates nonces and calculates the aggregated nonce for verifying the aggregated signature.
  • Inputs:
    • message str: The message that was signed.
    • commitments_dict Dict: Nonces from all nodes contributing to the signature.
    • group_key Point: The distributed key generated using KeyGen.
  • Outputs:
    • Returns a Point representing the aggregated nonce.
  • Example:
    >>> str_message = '651ecdfb1cbc7917644f5c3e16f014ac416470b990793e0ed5c2d2eb892d8c7f'
    >>> nonces_list = {
      '1': {
          'id': 1, 
          'public_nonce_d': 349042186269687657903918029097021035946240790024363563457247123869466727724904, 
          'public_nonce_e': 248237425400201679908511118467475923008578021798639515998329304887202095480425
      }, 
      '2': {
          'id': 2, 
          'public_nonce_d': 232003305522782618192381500560408473570697895814518143723528305421500512412148, 
          'public_nonce_e': 317715046800072131083613892686194614787279733126861218939601214450999918821174
          }
      }
    >>> dkg_public_key = 254503007646527296878921173802219136565731252590780601714649547124390052407428
    >>> pyfrost.aggregate_nonce(str_message, nonces_list, dkg_public_key)
    X: 0x7986d308d799825889680f6424a9e82141c2c70433ff832466425032036fe913
    Y: 0xfd598e318ca0a073f2a067a51aaca79b13264be5b9c23c8132a5e3f7d16ab71d
    (On curve <secp256k1>)
pyfrost.aggregate_signatures(message, single_signatures, aggregated_public_nonce, group_key)
  • Description: Aggregates individual signatures into a single verifiable signature.
  • Inputs:
    • message str: The message that was signed.
    • single_signatures List[Dict[str, int]]: Shares of the signature from each node.
    • aggregated_public_nonce Point: The aggregated nonce.
    • group_key int: The distributed key from KeyGen.
  • Outputs:
    • Returns a dictionary with data necessary to verify the aggregated signature.
  • Example:
    >>> str_message = '651ecdfb1cbc7917644f5c3e16f014ac416470b990793e0ed5c2d2eb892d8c7f'
    >>> signs = [
          {
              'id': 1, 
              'signature': 102694053038190246370608485838590127748578269475777603993528845385451820908381, 
              'public_key': 434857518911403231281957735014346859455075805194991608153904732048854704562605, 
              'aggregated_public_nonce': 402344336404068161625970842224046931385487524799112280264088906225521846642963
          }, 
          {
              'id': 2, 
              'signature': 46567393939448326584842681154363617834812892542107946395797044035032337911003, 
              'public_key': 245652561208681254624868566825006163495824337574238825910552301225881840535843, 
              'aggregated_public_nonce': 402344336404068161625970842224046931385487524799112280264088906225521846642963
          }
      ]
    >>> aggregated_public_nonce = X: 0x7986d308d799825889680f6424a9e82141c2c70433ff832466425032036fe913
                                  Y: 0xfd598e318ca0a073f2a067a51aaca79b13264be5b9c23c8132a5e3f7d16ab71d
                                  (On curve <secp256k1>)
    >>> dkg_public_key = 254503007646527296878921173802219136565731252590780601714649547124390052407428
    >>> pyfrost.aggregate_signatures(str_message, signs, aggregated_public_nonce, dkg_public_key)
      {
          'nonce': '0x4002743DBbB944415e22F987B1B0c1b57F3F01c5', 
          'public_key': {
              'x': '0x32ab98fd4f3959e532d73d93a9611edaf60ea456166ef8f333fe7c6784caa884', 
              'y_parity': 0
          }, 
          'signature': 33469357740322377531880181984265837730553597738810646006720726278965997325047, 
          'message_hash': HexBytes('0x260f59626b383fe31873d9bef3f6a5262af6206e8eb50088da9e41fb9e87dc41')
      }
pyfrost.verify_group_signature(aggregated_signature)
  • Description: Verifies the aggregated signature.
  • Inputs:
    • aggregated_signature Dict: Contains nonce, public_key, aggregated_signature, and message_hash.
  • Outputs:
    • Returns a boolean indicating the verification status of the signature.
  • Example:
    >>> aggregated_sign = {
          'nonce': '0x4002743DBbB944415e22F987B1B0c1b57F3F01c5', 
          'public_key': {
              'x': '0x32ab98fd4f3959e532d73d93a9611edaf60ea456166ef8f333fe7c6784caa884', 
              'y_parity': 0
          }, 
          'signature': 33469357740322377531880181984265837730553597738810646006720726278965997325047, 
          'message_hash': HexBytes('0x260f59626b383fe31873d9bef3f6a5262af6206e8eb50088da9e41fb9e87dc41')
      }
    >>> pyfrost.frost.verify_group_signature(aggregated_sign)
    True
pyfrost.verify_single_signature(id, message, commitments, aggregated_nonce, public_key_share, signature, group_key)
  • Description: Verifies each node's share of the signature.
  • Inputs:
    • id int: The node's ID.
    • message str: The signed message.
    • commitments Dict: Nonces from all nodes involved in signature generation.
    • aggregated_nonce Point: The aggregated nonce.
    • public_key_share int: The node's share of the distributed key.
    • signature Dict: The node's signature share.
    • group_key Point: The distributed key from KeyGen.
  • Outputs:
    • Returns a boolean indicating the verification status of the signature share.
  • Example:
    >>> id = 1
    >>> str_message = '651ecdfb1cbc7917644f5c3e16f014ac416470b990793e0ed5c2d2eb892d8c7f'
    >>> nonces_list = {
          '1': {
              'id': 1, 
              'public_nonce_d': 349042186269687657903918029097021035946240790024363563457247123869466727724904, 
              'public_nonce_e': 248237425400201679908511118467475923008578021798639515998329304887202095480425
          }, 
          '2': {
              'id': 2, 
              'public_nonce_d': 232003305522782618192381500560408473570697895814518143723528305421500512412148, 
              'public_nonce_e': 317715046800072131083613892686194614787279733126861218939601214450999918821174
          }
      }
    >>> aggregated_public_nonce = X: 0x7986d308d799825889680f6424a9e82141c2c70433ff832466425032036fe913
                                  Y: 0xfd598e318ca0a073f2a067a51aaca79b13264be5b9c23c8132a5e3f7d16ab71d
                                  (On curve <secp256k1>)
    >>> public_share = 434857518911403231281957735014346859455075805194991608153904732048854704562605
    >>> sign = {
          'id': 1, 
          'signature': 102694053038190246370608485838590127748578269475777603993528845385451820908381, 
          'public_key': 434857518911403231281957735014346859455075805194991608153904732048854704562605, 
          'aggregated_public_nonce': 402344336404068161625970842224046931385487524799112280264088906225521846642963
      }
    >>> dkg_public_key = 254503007646527296878921173802219136565731252590780601714649547124390052407428
    >>> pyfrost.pyfrost.verify_single_signature(id, str_message, nonces_list, aggregated_public_nonce, public_share, sign, dkg_public_key)
    True
Clone this wiki locally