{"id":50305797,"url":"https://github.com/vertigo6622/obsidian-keykeeper","last_synced_at":"2026-05-28T16:01:50.717Z","repository":{"id":351934595,"uuid":"1192405612","full_name":"vertigo6622/obsidian-keykeeper","owner":"vertigo6622","description":"a secure, tor-over-clearnet online key provisioning and digital rights management framework. ","archived":false,"fork":false,"pushed_at":"2026-05-16T22:40:08.000Z","size":479,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-17T00:40:50.999Z","etag":null,"topics":["anonymity","anti-capitalist","anti-piracy","cryptocurrency","digital-rights-management","i2p","javascript","litecoin","ltc","monero","obsidian","obsidian-protector","privacy","speck","speck-128","tor-hidden-service","tor-hidden-services","tor-over-clearnet","tor-over-clearnet-over-i2p","xmr"],"latest_commit_sha":null,"homepage":"https://obsidian.st","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vertigo6622.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-26T07:21:44.000Z","updated_at":"2026-05-16T22:40:11.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vertigo6622/obsidian-keykeeper","commit_stats":null,"previous_names":["vertigo6622/keykeeper","vertigo6622/obsidian-keykeeper"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vertigo6622/obsidian-keykeeper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vertigo6622%2Fobsidian-keykeeper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vertigo6622%2Fobsidian-keykeeper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vertigo6622%2Fobsidian-keykeeper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vertigo6622%2Fobsidian-keykeeper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vertigo6622","download_url":"https://codeload.github.com/vertigo6622/obsidian-keykeeper/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vertigo6622%2Fobsidian-keykeeper/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33615490,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["anonymity","anti-capitalist","anti-piracy","cryptocurrency","digital-rights-management","i2p","javascript","litecoin","ltc","monero","obsidian","obsidian-protector","privacy","speck","speck-128","tor-hidden-service","tor-hidden-services","tor-over-clearnet","tor-over-clearnet-over-i2p","xmr"],"created_at":"2026-05-28T16:01:49.698Z","updated_at":"2026-05-28T16:01:50.708Z","avatar_url":"https://github.com/vertigo6622.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp\u003e\n \u003cimg width=\"800\" height=\"100%\" src=\"img/stars-only.png\" alt=\"keykeeper-logo\"\u003e\n\u003c/p\u003e\n\u003cp\u003e\n \u003cb\u003eobsidian keykeeper\u003c/b\u003e\n \u003cbr/\u003e\n \u003csup\u003eanonymous digital rights management\u003c/sup\u003e\n\u003c/p\u003e\n\u003cp\u003e\n  \u003ca href=\"https://status.obsidian.st/\" target=\"_blank\"\u003e\n    \u003cimg alt=\"Static Badge\" src=\"https://img.shields.io/badge/keykeeper-indigo?logo=javascript\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://metrics.torproject.org/rs.html#details/714FCD4134044155A16A4EB440E30EF1F52B19B1\" target=\"_blank\"\u003e\n    \u003cimg alt=\"Static Badge\" src=\"https://img.shields.io/badge/tor%20native-purple?logo=torproject\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://code.obsidian.st/vertigo\" target=\"_blank\"\u003e\n    \u003cimg alt=\"Static Badge\" src=\"https://img.shields.io/badge/gogs-grey?logo=github\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e \n  \u003ca href=\"https://obsidian.st\" target=\"_blank\"\u003eclearnet\u003c/a\u003e • \u003ca href=\"http://obsidiand244wsh2jnmqvnim2becs73dims5wc5hzse3o5ywvw6ryoyd.onion/\" target=\"_blank\"\u003etor\u003c/a\u003e • \u003ca href=\"http://obsidian.i2p/?i2paddresshelper=edexepj4bnni4ct5otbivq73tjmyontztz5qa56qrni2rdldh4rq.b32.i2p\" target=\"_blank\"\u003ei2p\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\na cryptocurrency-based software licensing and digital rights management platform built with privacy-first principles. users can purchase software licenses using monero (xmr) or litecoin (ltc), with licenses bound to hardware ids for copy-protection. it is the backend of the obsidian pro and commerical licensing, but can be adapted for any application that requires securely handling licensing and drm.\n\nsecurity features include: tor-over-clearnet backend, recently configured to also work tor-over-clearnet-over-i2p, and a site pgp key with pgp signed ltc and xmr addresses to protect from phishing. users are identified with an account number. keykeeper does not support email sign-up at this time. this way, licensing protections can be upheld without collecting excessive user data. there are also automatic suspensions for any detected tampering with the licensing code using cryptographically secure SPECK-CBC-MAC hashes which prevent spoofing. \n\nthis model requires that the stub (the small section of code that executes the obsidian-packed payload) contact keykeeper first, in order to acquire the decryption key for the obsidian pro/commerical product. this happens through the clearnet proxy, redirecting them through tor to the keykeeper server. \n\nthe server receives both the stubs calculated hash as well as the components that make up the hash. it then calculates the hash independently and compares across three domains: database hardware id, server-calculated hardware id, and the stubs returned hardware id. any mismatch and the server will refuse to issue the key.\n\n---\n\n## tor-over-clearnet-over-i2p architecture\n\n\n```  \n ┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐\n │ i2p client  │◄────│  i2p p2p    │◄────│   nginx     │◄────│  proxy      │◄─────┐\n │  browser    │────►│  network    │────►│(port 443/80)│────►│ (port 8888) │────┐ │      \n └─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘    │ │ tor \n                     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐    │ │ circuit\n                     │  admin      │────►│  backend    │◄────│  tor        │◄───┘ │\n                     │ ipc shell   │ ┌──►│ (node.js)   │────►│ rendezvous  │──────┘\n                     └─────────────┘ │   └──────▲──────┘     └─────────────┘\n                            ┌────────┘      ┌───┴────────┬────────────┐\n ┌─────────────┐ tor ┌──────▼──────┐   ┌────▼────┐  ┌────▼────┐  ┌────▼────┐\n │ client      │◄────│ obsidian    │◄──│ sqlite  │  │ monero  │  │litecoin │\n │(tor browser)│────►│ onion site  │──►│database │  │  wallet │  │ wallet  │\n └─────────────┘     └──────▲──────┘   └─────────┘  └────▲────┘  └────▲────┘\n                            └────────────────────────────┴────────────┘\n```\n\n---\n\n## features\n\n- **security and privacy:**\n  - pgp-signed payment address\n  - encrypted user/license database (AES-256)\n  - rate limiting and connection filtering\n  - extensive input validation and regex\n  - SPECK-128-CBC-MAC for hwid integrity verification\n  - auto-delete XMR transactions after 7 days\n  - no emails, users identified by account numbers\n\n- **tor-over-clearnet:**\n  - http -\u003e socks5 proxy routes clearnet traffic through tor\n  - websockets allow for real-time, low-latency interactions\n  - increases privacy and security of the backend\n  - doesn't require tor browser\n\n- **tor-over-clearnet-over-i2p:**\n  - website accessible over i2p\n  - websockets over i2p fully functional\n  - wireguard protects inter-server communication\n\n- **admin shell:**\n  - local-only admin ipc shell\n  - directly manipulate the database\n  - withdraw from the server wallet\n  - create/discard licenses\n  - view user info, suspend accounts, etc\n\n- **hardware-bound tor-over-clearnet licensing:**\n  - hwid verification using SPECK-128-CBC-MAC\n  - client packer stub connects to proxy on frontend port 8888\n  - accesses backend through proxy (which routes through tor)\n  - license relinking for hardware changes (max 3 times)\n  - per-user SPECK encryption keys\n  - takes 2-5 seconds on average round trip + computation\n \n- **license types (configurable):**\n  - **pro license**: 6-month subscription for individual users\n  - **commercial license**: 6-month subscription for commercial use\n\n- **payment options:**\n  - monero (xmr)\n  - litecoin (ltc)\n\n---\n\n## clearnet architecture\n\n```  \n  ┌─────────────┐     ┌─────────────┐     ┌─────────────┐\n  │  client     │◄────│   nginx     │◄────│  proxy      │◄─────┐\n  │  (browser)  │────►│(port 443/80)│────►│ (port 8888) │────┐ │      \n  └─────────────┘     └─────────────┘     └─────────────┘    │ │ tor \n  ┌─────────────┐     ┌─────────────┐     ┌─────────────┐    │ │ circuit\n  │  admin      │────►│  backend    │◄────│  tor        │◄───┘ │\n  │ ipc shell   │     │ (node.js)   │────►│ rendezvous  │──────┘\n  └─────────────┘     └──────▲──────┘     └─────────────┘\n                ┌────────────┼────────────┐          \n           ┌────▼────┐  ┌────▼────┐  ┌────▼────┐\n           │ sqlite  │  │ monero  │  │litecoin │\n           │database │  │  wallet │  │ wallet  │\n           └─────────┘  └─────────┘  └─────────┘\n```\n\n**socket.io websockets:**\n1. client opens a websocket to `obsidian.st/socket.io/...` or `obsidian.i2p/socket.io/...`\n2. proxy routes request through socks5 (tor) to the hidden service\n3. hidden service accepts connection and keeps circuit alive\n\n**GET/POST requests:**\n1. client sents http request to `obsidian.st/keykeeper/...` or `obsidian.i2p/keykeeper/...`\n2. proxy routes request to specified endpoint through tor\n3. hidden service responds with requested data\n\n## obsidian pro verification process:\n```\n┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐\n│   client    │────►│ verify      │────►│  proxy      │────►│ tor network │────►│  keykeeper  │\n│ (obsidian)  │◄────│ subdomain   │◄────│ (port 8888) │◄────│ (3 relays)  │◄────│ (rendezvous)│\n└─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘\n```\n**hardware ids are computed from client machine information:**\n1. stub collects machine info (hardware serials, cpuid, tpm-ek)\n2. compute SPECK-128-CBC-MAC using the user's integrity key\n3. constructs a json payload with the machine info and MAC\n4. transmits json payload over tor to keykeeper via clearnet subdomain\n5. hwid is verified by re-computing the MAC serverside\n6. stub receives speck key and decrypts payload\n\n---\n### SPECK-128\n\nSPECK is a lightweight block cipher designed by the NSA. it is up to 5x faster than AES as it uses a simple ARX (add-rotate-xor) set of operations rather than the expensive AES-NI operations that require special hardware to properly execute. obsidian pro uses this algorithm to encrypt/decrypt the payload, and as part of its CBC-MAC integrity and license verification checks. see more about CBC-MAC below.\n\n**parameters:**\n- block size: 128 bits\n- key size: 128 bits\n- rounds: 34\n\n### SPECK encrypt rounds\n\nthe encrypt function has 34 rounds where it operates on two 64-bit words (x, y) using:\n1. **right rotation** (ror64): `(x \u003e\u003e r) | (x \u003c\u003c (64 - r))`\n2. **modular addition**: `(x + y) mod 2^64`\n3. **xor**: `x ^ k`\n4. **left rotation** (rol64): `(x \u003c\u003c r) | (x \u003e\u003e (64 - r))`\n5. **xor** `x ^ y`\n\n```javascript\nfunction speckEncryptBlock(x, y, roundKeys) {\n  for (let i = 0; i \u003c SPECK_ROUNDS; i++) {\n    x = (ror64(x, 8n) + y) \u0026 0xFFFFFFFFFFFFFFFFn;\n    x = (x ^ roundKeys[i]);\n    y = (rol64(y, 3n) ^ x);\n  }\n  return [x, y];\n}\n```\n\n### key schedule\n\nthe key schedule expands a 128-bit key into 34 round keys:\n\n```javascript\nfunction speckKeySchedule(key) {\n  const roundKeys = new Array(SPECK_ROUNDS);\n  let b = key[1];\n  roundKeys[0] = key[0];\n  \n  for (let i = 0; i \u003c SPECK_ROUNDS - 1; i++) {\n    b = (ror64(b, 8n) + roundKeys[i]) \u0026 0xFFFFFFFFFFFFFFFFn;\n    b = (b ^ BigInt(i));\n    roundKeys[i + 1] = (rol64(roundKeys[i], 3n) ^ b);\n  }\n  \n  return roundKeys;\n}\n```\n---\n### CBC-MAC\n\nobsidian keykeeper uses a custom SPECK-based 128bit CBC-MAC (Cipher Block Chaining Message Authentication Code) to ensure license verification integrity:\n\n1. data is padded to a multiple of 128 bits (16 bytes)\n2. each 128-bit block is XORed with the previous ciphertext\n3. the block is encrypted with SPECK-128\n4. the final 128-bit output is the MAC\n\n```javascript\nfunction speckCbcMac(data, keyHex) { // simplified\n  let chain0 = 0n, chain1 = 0n;\n  \n  for (let i = 0; i \u003c fullBlocks; i++) {\n    block0 = block0 ^ chain0;\n    block1 = block1 ^ chain1;\n    [chain0, chain1] = speckEncryptBlock(block0, block1, roundKeys);\n  }\n  \n  return [chain0, chain1]; // 128-bit MAC\n}\n```\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://deepwiki.com/vertigo6622/obsidian-keykeeper\"\u003e\u003cimg src=\"https://deepwiki.com/badge.svg\" alt=\"Ask DeepWiki\"\u003e\u003c/a\u003e\n  \u003ca href=\"/LICENSE\"\u003e\u003cimg alt=\"License\" src=\"https://img.shields.io/badge/license-ACSL%201.4-indigo\"\u003e\u003c/a\u003e\n  \u003cimg alt=\"GitHub commit activity\" src=\"https://img.shields.io/github/commit-activity/m/vertigo6622/obsidian-keykeeper\"\u003e\n  \u003ca href=\"https://obsidian.st/\"\u003e\u003cimg alt=\"Website\" src=\"https://img.shields.io/website?url=https%3A%2F%2Fstatus.obsidian.st\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvertigo6622%2Fobsidian-keykeeper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvertigo6622%2Fobsidian-keykeeper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvertigo6622%2Fobsidian-keykeeper/lists"}