{"id":38351711,"url":"https://github.com/tiabc/doubleratchet","last_synced_at":"2026-01-17T03:11:12.795Z","repository":{"id":87379336,"uuid":"93928476","full_name":"tiabc/doubleratchet","owner":"tiabc","description":"The Double Ratchet Algorithm implementation in Go","archived":false,"fork":false,"pushed_at":"2018-08-20T05:41:01.000Z","size":99,"stargazers_count":5,"open_issues_count":2,"forks_count":3,"subscribers_count":0,"default_branch":"master","last_synced_at":"2024-06-20T09:12:49.771Z","etag":null,"topics":["cryptographic-algorithms","golang","golang-library"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tiabc.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":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-10T09:17:01.000Z","updated_at":"2021-12-20T16:56:29.000Z","dependencies_parsed_at":null,"dependency_job_id":"1a93b1d8-1852-441f-be0d-2cca5827acf6","html_url":"https://github.com/tiabc/doubleratchet","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tiabc/doubleratchet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiabc%2Fdoubleratchet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiabc%2Fdoubleratchet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiabc%2Fdoubleratchet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiabc%2Fdoubleratchet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tiabc","download_url":"https://codeload.github.com/tiabc/doubleratchet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tiabc%2Fdoubleratchet/sbom","scorecard":{"id":884021,"data":{"date":"2025-08-11","repo":{"name":"github.com/tiabc/doubleratchet","commit":"9d7f37c630e2bf368f380032c57186efe4230bbe"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-24T09:20:38.180Z","repository_id":87379336,"created_at":"2025-08-24T09:20:38.180Z","updated_at":"2025-08-24T09:20:38.180Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28492612,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T02:39:23.645Z","status":"ssl_error","status_checked_at":"2026-01-17T02:34:19.649Z","response_time":85,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["cryptographic-algorithms","golang","golang-library"],"created_at":"2026-01-17T03:11:12.632Z","updated_at":"2026-01-17T03:11:12.787Z","avatar_url":"https://github.com/tiabc.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# doubleratchet\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/tiabc/doubleratchet)](https://goreportcard.com/report/github.com/tiabc/doubleratchet)\n[![Build Status](https://travis-ci.org/tiabc/doubleratchet.svg?branch=master)](https://travis-ci.org/tiabc/doubleratchet)\n[![Coverage Status](https://coveralls.io/repos/github/tiabc/doubleratchet/badge.svg?branch=master)](https://coveralls.io/github/tiabc/doubleratchet?branch=master)\n[![GoDoc](https://godoc.org/github.com/tiabc/doubleratchet?status.svg)](https://godoc.org/github.com/tiabc/doubleratchet)\n\n[The Double Ratchet Algorithm](https://whispersystems.org/docs/specifications/doubleratchet) is used\nby two parties to exchange encrypted messages based on a shared secret key. Typically the parties\nwill use some key agreement protocol (such as X3DH) to agree on the shared secret key.\nFollowing this, the parties will use the Double Ratchet to send and receive encrypted messages.\n\nThe parties derive new keys for every Double Ratchet message so that earlier keys cannot be calculated\nfrom later ones. The parties also send Diffie-Hellman public values attached to their messages.\nThe results of Diffie-Hellman calculations are mixed into the derived keys so that later keys cannot\nbe calculated from earlier ones. These properties gives some protection to earlier or later encrypted \nmessages in case of a compromise of a party's keys.\n\n## Project status\n\nThe library is in beta version and ready for integration into production projects with care.\nLet me know if you face any problems or have any questions or suggestions.\n\n## Implementation notes\n\n### The Double Ratchet logic\n\n1. No more than 1000 messages can be skipped in a single chain.\n1. Skipped messages from a single ratchet step are deleted after 100 ratchet steps.\n1. Both parties' sending and receiving chains are initialized with the shared key so that both\nof them could message each other from the very beginning.\n1. Both plain and encrypted header versions are implemented.\n\n### Cryptographic primitives \n\n1. **GENERATE_DH():** Curve25519\n1. **KDF_RK(rk, dh_out):** HKDF with SHA-256\n1. **KDF_CK(ck):** HMAC with SHA-256 and constant inputs\n1. **ENCRYPT(mk, pt, associated_data):** AES-256-CTR with HMAC-SHA-256 and IV derived alongside an encryption key\n\n## Installation\n\n    go get github.com/tiabc/doubleratchet\n\nthen `cd` into the project directory and install dependencies:\n\n    glide up\n    \nIf `glide` is not installed, [install it](https://github.com/Masterminds/glide).\n\n## Usage\n\n### Basic usage example\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/tiabc/doubleratchet\"\n)\n\nfunc main() {\n\t// The shared key both parties have already agreed upon before the communication.\n\tsk := [32]byte{\n\t\t0xeb, 0x8, 0x10, 0x7c, 0x33, 0x54, 0x0, 0x20,\n\t\t0xe9, 0x4f, 0x6c, 0x84, 0xe4, 0x39, 0x50, 0x5a,\n\t\t0x2f, 0x60, 0xbe, 0x81, 0xa, 0x78, 0x8b, 0xeb,\n\t\t0x1e, 0x2c, 0x9, 0x8d, 0x4b, 0x4d, 0xc1, 0x40,\n\t}\n\n\t// Diffie-Hellman key pair generated by one of the parties during key exchange or\n\t// by any other means. The public key MUST be sent to another party for initialization\n\t// before the communication begins.\n\tkeyPair, err := doubleratchet.DefaultCrypto{}.GenerateDH()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Bob MUST be created with the shared secret and a DH key pair.\n\tbob, err := doubleratchet.New(sk, keyPair)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Alice MUST be created with the shared secret and Bob's public key.\n\talice, err := doubleratchet.NewWithRemoteKey(sk, keyPair.PublicKey())\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Alice can now encrypt messages under the Double Ratchet session.\n\tm := alice.RatchetEncrypt([]byte(\"Hi Bob!\"), nil)\n\n\t// Which Bob can decrypt.\n\tplaintext, err := bob.RatchetDecrypt(m, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Println(string(plaintext))\n}\n```\n\n### Options\n\nAdditional options can be passed to constructors to customize the algorithm behavior:\n\n```go\ndoubleratchet.New(\n    sk, keyPair,\n    \n    // Your own cryptography supplement implementing doubleratchet.Crypto.\n    WithCrypto(c),\n    \n    // Custom storage for skipped keys implementing doubleratchet.KeysStorage.\n    WithKeysStorage(ks),\n    \n    // The maximum number of skipped keys. Error will be raised in an attempt to store more keys\n    // in a single chain while decrypting.\n    WithMaxSkip(1200),\n    \n    // The number of Diffie-Hellman ratchet steps skipped keys will be stored.\n    WithMaxKeep(90),\n)\n```\n\n### Header encryption\n\nIf you don't want anybody to see message ordering and your ratchet keys, you can utilize\nheader encryption. It makes your communication even more secure in a sense that an eavesdropper\ncan only see ciphertexts and nothing else. However, it adds more complexity to the implementation,\nnamely:\n\n1. Parties should agree on 2 more secret keys for encrypting headers before the double ratchet\nsession.\n1. When a recipient receives a message she must first associate the message with its relevant\nDouble Ratchet session (assuming she has different sessions with different parties).\nHow this is done is outside of the scope of this library, although [the Pond protocol](https://github.com/agl/pond) offers some\nideas as stated in the Double Ratchet specification.\n1. Header encryption makes messages 48 bytes longer. For example, if you're sending message\n`how are you?` in a version without header encryption, it will be encrypted into\n`iv + len(pt) + signature = 16 + 12 + 32 = 60` bytes plus a header `rk + pn + n = 32 + 4 + 4 = 40` bytes\nwith 100 bytes in total. In case of the header encryption modification the header will also\nbe encrypted which will add 48 more bytes with the total of 148 bytes. Note that the longer\nyour message, the more resulting length it takes.\n1. It does a bit more computations especially for skipped messages and will work more slowly.\n\n#### Example\n\nIn order to create a header-encrypted session, parties should agree upon 3 different shared keys\nand Alice should know Bob's public key:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/tiabc/doubleratchet\"\n)\n\nfunc main() {\n\t// Shared keys both parties have already agreed upon before the communication.\n\tvar (\n\t\t// The key for message keys derivation.\n\t\tsk = [32]byte{\n\t\t\t0xeb, 0x8, 0x10, 0x7c, 0x33, 0x54, 0x0, 0x20,\n\t\t\t0xe9, 0x4f, 0x6c, 0x84, 0xe4, 0x39, 0x50, 0x5a,\n\t\t\t0x2f, 0x60, 0xbe, 0x81, 0xa, 0x78, 0x8b, 0xeb,\n\t\t\t0x1e, 0x2c, 0x9, 0x8d, 0x4b, 0x4d, 0xc1, 0x40,\n\t\t}\n\n\t\t// Header encryption keys.\n\t\tsharedHka = [32]byte{\n\t\t\t0xbd, 0x29, 0x18, 0xcb, 0x18, 0x6c, 0x26, 0x32,\n\t\t\t0xd5, 0x82, 0x41, 0x2d, 0x11, 0xa4, 0x55, 0x87,\n\t\t\t0x1e, 0x5b, 0xa3, 0xb5, 0x5a, 0x6d, 0xe1, 0x97,\n\t\t\t0xde, 0xf7, 0x5e, 0xc3, 0xf2, 0xec, 0x1d, 0xd,\n\t\t}\n\t\tsharedNhkb = [32]byte{\n\t\t\t0x32, 0x89, 0x3a, 0xed, 0x4b, 0xf0, 0xbf, 0xc1,\n\t\t\t0xa5, 0xa9, 0x53, 0x73, 0x5b, 0xf9, 0x76, 0xce,\n\t\t\t0x70, 0x8e, 0xe1, 0xa, 0xed, 0x98, 0x1d, 0xe3,\n\t\t\t0xb4, 0xe9, 0xa9, 0x88, 0x54, 0x94, 0xaf, 0x23,\n\t\t}\n\t)\n\n\tkeyPair, err := doubleratchet.DefaultCrypto{}.GenerateDH()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Bob MUST be created with the shared secret, shared header keys and a DH key pair.\n\tbob, err := doubleratchet.NewHE(sk, sharedHka, sharedNhkb, keyPair)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Alic MUST be created with the shared secret, shared header keys and Bob's public key.\n\talice, err := doubleratchet.NewHEWithRemoteKey(sk, sharedHka, sharedNhkb, keyPair.PublicKey())\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Encryption and decryption is done the same way as in the basic version.\n\tm := alice.RatchetEncrypt([]byte(\"Hi Bob!\"), nil)\n\n\tplaintext, err := bob.RatchetDecrypt(m, nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Println(string(plaintext))\n}\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftiabc%2Fdoubleratchet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftiabc%2Fdoubleratchet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftiabc%2Fdoubleratchet/lists"}