Skip to content

coracle-social/promenade

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

66 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

promenade

I forgot what was the meaning of this acronym.

implementation details

the signature algorithm is implemented roughly as described in the FROST paper, and most of it was copied from https://github.com/bytemare/frost and adapted to use github.com/btcsuite/btcd/btcec, but it has the following substantial changes:

  • for BIP-340 compatibility, the key dealing algorithm negates the secret key before sharding if its public key’s y is odd;

  • for BIP-340 compatibility, when creating partial signatures, signers have to compute the group commitment, and if it’s y is odd then all the public nonces and their own private nonce is negated;

  • for BIP-340 compatibility, then signing challenge is computed with taggedhash("BIP0340/challenge", group-commitment-x || user-pubkey-x || event_id);

  • because it felt appropriate, other parts of the algorithm that would use hashes also use taggedhash() with different tags, the code will speak better than I can.

internal protocol flow

key distribution

  1. client generates a user-secret-key locally — or the user inputs a preexisting key;

  2. client shards the key into as n shards, using the TrustedKeyDeal() function under frost/key_dealer.go or trustedKeyDeal() under js-dealer/shardkey.ts, it also has to pick a number m, which will be the minimum threshold for generating signatures using FROST;

  3. if it doesn’t have one already, client signs and publishes a kind:10002 relay list with some inbox relays;

  4. client picks a number n of signers, identified by their public keys, each of which will receive a shard;

  5. client fetches kind:10002 relays for each of the signers;

  6. client picks a coordinator that acts as a relay;

  7. client builds a kind:26428 "shard event" for each signer, as follows:

    {
      "kind": 26428,
      "pubkey": "<user-pubkey>",
      "tags": [
        ["p", "<signer-pubkey>"],
        ["coordinator", "<coordinator-url>"],
      ],
      "content": nip44_encrypt("<hex-encoded-secret-key-shard>")
    }
    where `<hex-encoded-secret-key-shard>` is given by the hex-encoded of the concatenation of
      - [encoded-public-shard]: given by:
        - [public-shard-id]: 2-bytes (little-endian)
        - [number-of-vss-commits]: 4-bytes (little-endian)
        - [shard-public-key]: 33-bytes (compressed)
        - <number-of-vss-commits> * [vss-commit]: 33-bytes (compressed) each
      - [shard-secret-key]: 32-bytes (big-endian)
      - [user-pubkey]: 33-bytes (compressed)
  8. client builds NIP-13 proof-of-work into that event of at least 20 bits;

  9. client sends the signed "shard event" to the each desired signer in their "read" relays as given by their kind:10002;

  10. client starts listening on their own "read" relays for replies from signer;

  11. signer receives the event from client, checks the proof-of-work, decrypts and validates the <encoded-secret-key-shard>, then saves that information locally somehow;

  12. signer builds a kind:26429 "shard ack event", as follows:

    {
      "kind": 26429,
      "pubkey": "<signer-pubkey>",
      "tags": [
        ["e", "<shard-event-id>"],
        ["p", "<user-pubkey>"]
      ]
    }
  13. signer fetches kind:10002 relays for <user-pubkey> and sends the "shard ack event" to the "read" relays;

  14. upon receiving the "shard ack event" from all the signers, client builds a kind:16430 "account registration event" as follows:

    {
      "kind": 16430,
      "pubkey": "<user-pubkey>",
      "tags": [
        ["handlersecret", "<random-private-key>"],
        ["h", "<public-key-corresponding-to-handlersecret>"],
        ["threshold", "<m>"],
        ["p", "<signer-pubkey>", "<hex-encoded-public-shard>"] * n
      ]
    }
    in which the `"p"` tag is repeated once for each signer, and "<hex-encoded-public-shard>" is encoded just as above.
  15. upon receiving the "account registration event", coordinator stores it and keeps it secret;

  16. coordinator should now listen for NIP-46 calls directed at its own relay, targeting <public-key-corresponding-to-handlersecret>.

signing

  1. coordinator listens for all NIP-46 events targeting <public-key-corresponding-to-handlersecret>;

  2. upon receiving a NIP-46 request, coordinator matches it against its p tag with a stored kind:16430, uses handlersecret to decrypt (and later sign and encrypt the response);

  3. for get_public_key coordinator can just answer immediately;

  4. for sign_event coordinator then finds out what signers are online and connected, chooses m of them and initiates a signing session;

  5. coordinator sends a kind:26430 "configuration event" to each chosen signer in the form:

    {
      "kind": 26430,
      "pubkey": "<coordinator-pubkey>",
      "tags": [
        ["p", "<signer-pubkey>"]
      ],
      "content": "<hex-encoded-configuration-object>"
    }
    where <hex-encoded-configuration-object> is given by the hex-encoded concatenation of
      - [m]: 2-bytes (little-endian)
      - [n]: 2-bytes (little-endian)
      - [number-of-signers]: 2-bytes (little-endian)
      - [user-pubkey]: 33-bytes (compressed)
      - <number-of-signers> * [encoded-public-shard] (as above)
  6. upon receiving this, signer generates its local commitments, or a pair of public and private nonces, and sends the public parts to coordinator in a kind:26431 "commit event", as follows:

    {
      "kind": 26431,
      "pubkey": "<signer-pubkey>",
      "tags": [
        ["e", "<configuration-event-id>"],
        ["p", "<user-pubkey>"]
      ],
      "content": "<hex-encoded-commit>"
    }
    where <hex-encoded-commit> is given by the hex-encoded concatenation of
      - [commit-id]: 8-bytes (little-endian)
      - [signer-id]: 2-bytes (little-endian)
      - [binding-nonce-point]: 33-bytes (compressed)
      - [hiding-nonce-point]: 33-bytes (compressed)
  7. upon receiving commits from all signers, coordinator then aggregates the commits into a group commit and sends it back to all the signers:

    {
      "kind": 26432,
      "pubkey": "<coordinator-pubkey>",
      "tags": [
        ["e", "<configuration-event-id>"],
        ["p", "<signer-pubkey>"]
      ],
      "content": "<hex-encoded-group-commit>"
    }
    where <hex-encoded-group-commit> is given by the hex-encoded concatenation of
      - [first-nonce]: 33-bytes (compressed)
      - [second-nonce]: 33-bytes (compressed)
  8. then coordinator sends the event that is to be signed to all signers in a kind:26432 event, in the form:

    {
      "kind": 26433,
      "pubkey": "<coordinator-pubkey>",
      "tags": [
        ["e", "<configuration-event-id>"],
        ["p", "<signer-pubkey>"]
      ],
      "content": "<json-encoded-event-to-be-signed>"
    }
  9. finally, each signer groups together all commits and uses these together with their secret nonces and the hash of the event to be signed to produce a <partial-signature> and sends that back to coordinator in a kind:26433 event, as follows:

    {
      "kind": 26434,
      "pubkey": "<signer-pubkey>",
      "tags": [
        ["e", "<configuration-event-id>"],
        ["p", "<user-pubkey>"]
      ],
      "content": "<hex-encoded-partial-signature>"
    }
    where <hex-encoded-partial-signature> is given by the hex-encoded concatenation of:
      - [signer-id]: 2-bytes (little-endian)
      - [partial-signature-scalar]: 32-bytes (big-endian)
  10. coordinator assembles all the partial signatures and builds the aggregated signature which can then be put into the event and sent as a response to the sign_event NIP-46 request.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published