{"id":20083142,"url":"https://github.com/zaach/5edm","last_synced_at":"2025-05-06T01:31:01.688Z","repository":{"id":63276247,"uuid":"554449634","full_name":"zaach/5edm","owner":"zaach","description":"end-to-end encrypted messaging on deno","archived":false,"fork":false,"pushed_at":"2022-11-30T21:03:54.000Z","size":264,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-04-16T00:26:29.505Z","etag":null,"topics":["chat","deno","e2ee","hpke","privacy"],"latest_commit_sha":null,"homepage":"https://5edm.deno.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zaach.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-10-19T20:46:27.000Z","updated_at":"2024-01-22T09:16:50.000Z","dependencies_parsed_at":"2023-01-22T06:54:05.145Z","dependency_job_id":null,"html_url":"https://github.com/zaach/5edm","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/zaach%2F5edm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaach%2F5edm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaach%2F5edm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaach%2F5edm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zaach","download_url":"https://codeload.github.com/zaach/5edm/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224479122,"owners_count":17318217,"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":["chat","deno","e2ee","hpke","privacy"],"created_at":"2024-11-13T15:46:03.635Z","updated_at":"2024-11-13T15:46:04.145Z","avatar_url":"https://github.com/zaach.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![5EDM](./static/5edm.png)\n\nEphemeral, Edge, End-to-End Encrypted Direct Messaging\n\n5EDM uses the recent [Hybrid Public Key Encryption (HPKE)](https://www.rfc-editor.org/rfc/rfc9180.html) standard to establish an end-to-end\nencrypted and deniable messaging session between two parties. New keys are\ngenerated before each session, providing anonymity and forward-secrecy across sessions. With no persistent storage of keys or messages the app's only dependency is \u003ca href=\"https://deno.com/deploy\"\u003eDeno Deploy\u003c/a\u003e, an edge computing platform with a [cross-region message bus](https://deno.com/deploy/docs/runtime-broadcast-channel).\n\n_Note: This is a proof-of-concept. Use [Signal](https://signal.org/) if you need the real deal._\n\n### Running Locally\n\nIn addition to `deno` you'll need `npx` installed to compile tailwindcss.\n\nThe app is built on [fresh](https://fresh.deno.dev/). To start it, run:\n\n```\ndeno task start\n```\n\nThis will watch the project directory and restart as necessary.\n\n### Deploying\n\nThe app is deployed to Deno Deploy via [github actions](https://deno.com/deploy/docs/deployctl#deployctl-github-action).\n\n## Protocol\n\nThe pseudocode below borrows definitions from the [spec](https://www.rfc-editor.org/rfc/rfc9180.html) unless otherwise defined. The app uses the [hpke-js](https://github.com/dajiaji/hpke-js) implementation of HPKE.\n\n`pkR` is generated by the Recipient and preshared with the Sender over a secure channel.\n\n```\npkS, skS = GenerateKeyPair()\nenc, contextS = SetupAuthS(pkR, info, skS)\nchannelId = LabeledExtract(0, \"channel_id\", pkR)[0:16]\nciphertext = contextS.Seal(channelId, greeting)\nenc2, ciphertext2 = Seal(pkR, info, ciphertext, pkS)\n```\n\nThe Sender generates a key pair and sets up an [authenticated encryption context](https://www.rfc-editor.org/rfc/rfc9180.html#name-authentication-using-an-asy) using the preshared `pkR` and their private key. The context is used to encrypt a greeting using a `channelId` derived from `pkR` as additional data. To [protect metadata](https://www.rfc-editor.org/rfc/rfc9180.html#name-metadata-protection), the [single-shot API](https://www.rfc-editor.org/rfc/rfc9180.html#name-encryption-and-decryption-2) is used to encrypt `pkS` using the first `ciphertext` as additional data.\n\n```\npkS = Open(enc2, skR, info, ciphertext, ciphertext2)\ncontextR = SetupAuthR(enc, skR, info, pkS)\nchannelId = LabeledExtract(0, \"channel_id\", pkR)[0:16]\ngreeting = contextR.Open(channelId, ciphertext)\n```\n\nThe Recipient uses the single-shot API to open `ciphertext2` and obtain the Sender's public key `pkS`. They can then setup their own encryption context and open the greeting ciphertext.\n\n### Bidirectional Encryption\n\nThe initial setup allows the Sender to `seal` messages and the Recipient to `open` them but [additional setup](https://www.rfc-editor.org/rfc/rfc9180.html#bidirectional) is needed to perform the operations in reverse.\n\n```\nkey = contextR.Export(\"5edm key\", 32)\nnonce = contextR.Export(\"5edm nonce\", 32)\nsessionIdR = contextR.Export(\"5edm session id\", 16)\nsessionIdS = contextR.Export(\"5edm session id\", 16)\n\ncontextR.SetupBidirectional(key, nonce)\nciphertext = contextR.Seal(sessionIdS, plaintext)\n\n```\n\n```\nkey = contextS.Export(\"5edm key\", 32)\nnonce = contextS.Export(\"5edm nonce\", 32)\nsessionIdR = contextS.Export(\"5edm recipient session id\", 16)\nsessionIdS = contextS.Export(\"5edm sender session id\", 16)\n\ncontextS.SetupBidirectional(key, nonce)\nciphertext = contextS.Open(sessionIdS, ciphertext)\n\n```\n\nThe Sender context can now `open` and the Recipient context can now `seal`. Session IDs are passed along with the encrypted messages to route them, so they are supplied as additional data when opening/sealing.\n\nThe pseudocode below defines `Context\u003cROLE\u003e.SetupBidirectional`. It aligns with the [hpke-js implementation](https://github.com/dajiaji/hpke-js#base-mode-with-bidirectional-encryption).\n\n```\ndef Context\u003cROLE\u003e.SetupBidirectional(key, base_nonce):\n  self.key_r = key\n  self.base_nonce_r = base_nonce\n\ndef ContextR.Seal(aad, pt):\n  if self.base_nonce_r == Nil:\n    raise SealError\n  ct = Seal(self.key_r, self.ComputeNonce_r(self.seq_r), aad, pt)\n  self.IncrementSeq_r()\n  return ct\n\ndef ContextS.Open(aad, ct):\n  if self.base_nonce_r == Nil:\n    raise OpenError\n  pt = Open(self.key_r, self.ComputeNonce_r(self.seq_r), aad, ct)\n  if pt == OpenError:\n    raise OpenError\n  self.IncrementSeq_r()\n  return pt\n\ndef Context\u003cROLE\u003e.ComputeNonce_r(seq):\n  seq_bytes = I2OSP(seq, Nn)\n  return xor(self.base_nonce_r, seq_bytes)\n\ndef Context\u003cROLE\u003e.IncrementSeq_r():\n  if self.seq_r \u003e= (1 \u003c\u003c (8*Nn)) - 1:\n    raise MessageLimitReachedError\n  self.seq_r += 1\n```\n\n### Caveats\n\n- HPKE [isn't resiliant against dropped or out-of-order messages](https://www.rfc-editor.org/rfc/rfc9180.html#name-message-order-and-message-l) so sessions can easily become out of sync on a shaky connection. A backend queue would help (or an [alternative protocol)](https://datatracker.ietf.org/doc/draft-harkins-cfrg-dnhpke/) but I opted to keep things simple and rely on bare bones Deno Deploy. Instead, clients attempt to recover when they suspect they're out of sync. However, if both directions are out of sync it's game over for that session.\n- I'm not a cryptographer but I did stay at a holiday inn express last night\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzaach%2F5edm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzaach%2F5edm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzaach%2F5edm/lists"}