docs.sigit.io/technical.md
zver 220ad4e5a5
All checks were successful
Release to Production / build_and_release (push) Successful in 27s
fix: stuff
2024-07-06 16:04:44 +01:00

5.1 KiB

Document Signing

A proposal for signing documents in a secure and private way. Implemented by https://sigit.io

Agreement

A signing round may occur for one or more documents, and involve one or more counterparties. This collection of documents, counterparties, and associated metadata constitutes a single "Agreement".

It is possible to complete an Agreement offline, or online without any external observer knowing that a counterparty was involved in an Agreement.

Roles

When it comes to signing documents, the following roles (counterparties) are relevant:

  • Creator - uploads the files, defines the remaining counterparties and signing order
  • Signer - validates previous signatures and adds their own
  • Viewer - has ability to view the Agreement

Privacy

It should not be possible for observers to know the members or contents of an Agreement. This is achieved by using ephemeral keys for each counterparty, and encrypting all documents (before uploading to blossom) and metadata (before uploading to relays).

Creation Process

The files themselves are zipped, and this files.zip file is then encrypted and uploaded to Blossom.

The Creator prepares an Agreement by creating and signing (but NOT publishing) a Kind 938 event. The content field contains an object with the following information:

  • title (string)
  • signers (array) - list of npubs which can sign the Agreement
  • viewers (array) - list of npubs which can view the Agreement
  • filehashes (array) - series of objects containing the file name and file hash
  • zipUrl (string) - the location of the encrypted zip file (blossom server)
  • markConfig (object) - the template by which counterparties will make Annotations in any PDFs

This object is formed as follows:

{
  "signers":["npub1nqulz...","npub1d0csynr..."],
  "viewers":["npub1viewr.."],
  "fileHashes":[{
      "name":"d51fe683-4c37-40b-b63c-d503387b0b75.png",
      "hash":"8c4f41e76be536a4d6cc180cf1a6014fa0bacbb6cc5e7a1e12dde41f8f5158a8"
   }],
   "title": "our amazing agreement",
   "zipUrl":"https://blossom.one/asdfafafaer23wsdf343fff4343fs"
}

Once the creator has signed, the meta.json file will contain the createObject (above) and a keys object that contains the decryption key for the zip file, NIP-44 encrypted to each counterparty.

{
  "createSignature": "{\n  \"kind\": 938,\n  \"content\": \"{\\\"signers\\\":[\\\"npub1d0csynr..\\\",\\\"npub1nqulz..\\\"],\\\"viewers\\\":[\\\"npub1viewr..\\\"]...}\",\n  \"created_at\": 1716564780,\n  \"tags\": [],\n  \"pubkey\": \"6bf1024...\",\n  \"id\": \"...\",\n  \"sig\": \"...\"\n}",
  "keys": {"npub1nqulz...":"ENCRYPTD","npub1d0csynr...":"ENCRYPTD","npub1viewr..":"ENCRYPTD"},
  "docSignatures": {
  }
}

This meta.json file is now sealed (using unsigned Kind 938 to differentiate from DMs and speed up decryptions) and gift wrapped (with some PoW) per NIP-59, for each recipient, and the gift wrap is broadcast to each recipients relays.

Kind 4 DMs (from an ephemeral key) may optionally be broadcast, with a link to the signing application.

Signing Process

Before signing, the client should first verify the signature from the create event, ensure the create event has a timestamp in the past, download / decrypt the files.zip from the zipUrl, and verify the hashes of each document.

A Kind 938 event can now be signed that contains an object with the following attributes:

  • prevSig (string) - the signature of the previous Kind 938 event (mandatory)
  • marks (object) - any pdf annotations, as described in the Annotations section

After signing, the meta.json can now be updated, eg as follows:

{
  "createSignature": "{\n  \"kind\": 938,\n  \"content\": \"{\\\"signers\\\":[\\\"npub1d0csynr..\\\",\\\"npub1nqulz..\\\"],\\\"viewers\\\":[\\\"npub1viewr..\\\"]...}\",\n  \"created_at\": 1716564780,\n  \"tags\": [],\n  \"pubkey\": \"6bf1024...\",\n  \"id\": \"...\",\n  \"sig\": \"...\"\n}",
  "keys": {"npub1nqulz...":"ENCRYPTD","npub1d0csynr...":"ENCRYPTD","npub1viewr..":"ENCRYPTD"},
  "docSignatures": {
    "npub1d0csynrrxcynkcedktdzrdj6gnras2psg48mf46kxjazs8skrjgq9uzhlq": "{\n  \"kind\": 938,\n  \"content\": \"{\\\"prevSig\\\":\\\"a76fdd0..",
    "npub1nqulzcxcj0d2uesusstgupl5du8pa96xl6uy8xndweeckjkn964qjs23sn": "{\n  \"kind\": 938,\n  \"content\": \"{\\\"prevSig\\\":\\\"585c134.."
  },
}

After each signature, this file is sealed using Kind 938 and gift wrapped to each counterparty.

Storing App Data

App data (list of all sigits) is stored as an encrypted file on Blossom. The file also contains a list of 'processed' id's from Kind 1059 events (to avoid having to continually decrypt when logging in, as well as enabling notifications).

A Kind 30078 is also created, which contains a link to the blossom server, and an ephemeral key pair that can be used to sign the blossom requests. The user should be shown this npub so they can whitelist it on their blossom server.

NIP-78 requires a d-tag to provide some application context. To avoid revealing metadata, the d tag will be the first 32 chars of the encrypted result of the string "938" plus the users npub.