{"id":16753528,"url":"https://github.com/cgaebel/lifecrypt","last_synced_at":"2025-07-20T08:39:05.619Z","repository":{"id":66108400,"uuid":"312815190","full_name":"cgaebel/lifecrypt","owner":"cgaebel","description":"Store a root of trust for your life.","archived":false,"fork":false,"pushed_at":"2020-11-15T17:12:25.000Z","size":97,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-16T05:41:26.461Z","etag":null,"topics":["backup","cryptography"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/cgaebel.png","metadata":{"files":{"readme":"README","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":"2020-11-14T12:44:05.000Z","updated_at":"2022-04-22T20:19:56.000Z","dependencies_parsed_at":"2023-02-23T20:00:36.340Z","dependency_job_id":null,"html_url":"https://github.com/cgaebel/lifecrypt","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cgaebel/lifecrypt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgaebel%2Flifecrypt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgaebel%2Flifecrypt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgaebel%2Flifecrypt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgaebel%2Flifecrypt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cgaebel","download_url":"https://codeload.github.com/cgaebel/lifecrypt/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cgaebel%2Flifecrypt/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266093058,"owners_count":23875548,"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":["backup","cryptography"],"created_at":"2024-10-13T02:50:25.939Z","updated_at":"2025-07-20T08:39:05.557Z","avatar_url":"https://github.com/cgaebel.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"Overview\n--------\nLifecrypt stores your encrypted personal root of trust. Some examples of what\nto include in it are 2fa keys and password manager master keys.\n\nLifecrypt supports three operations over this data:\n\n[lifecrypt edit] opens the stored data with vim, for editing.\n[lifecrypt view] prints the stored data to stdout.\n[lifecrypt change-password] changes the encryption password.\n\nKey-derivation is provided by scrypt. Encryption and authentication is provided\nChaCha20Poly1305.\n\nTo learn more about scrypt, see the original tarsnap paper:\nhttps://www.tarsnap.com/scrypt/scrypt.pdf\n\nChaCha20Poly1305 authenticated encryption is defined in RFC 7539,\nwhich can be found at https://tools.ietf.org/html/rfc7539.\n\nQuick Start\n-----------\ngit clone https://github.com/cgaebel/lifecrypt.git\ncd lifecrypt\ncargo build --release\nmkdir -p vault\ncp ./target/release/lifecrypt vault\nrm -rf target\n./vault/lifecrypt edit ./vault/encrypted\n# type a password, then add some data to encrypt\n\n# decrypt the vault\nvault/lifecrypt view vault/encrypted\n\nIf you move the vault to another system you might need to rebuild lifecrypt,\nbut the encrypted file doesn't need to be regenerated -- it's portable.\n\nMotivation\n----------\nSuppose every electronic device you own is destroyed. How will you log in to\ngmail? How will you log in to anything? You're using 2FA, right? Those keys\nare gone.\n\nWith lifecrypt, you can store recovery codes for all that stuff on a buddy's\ncomputer. If all your computers are kill, you might still stand a chance.\n\nThreat Model\n------------\nThe on-disk data is available to the attacker. They would like to read it.\nThe attacker is likely a friend or family member, or someone with access to\nthem.\n\nOr the file could be leaked onto the internet, and a smart 14-year kid with a\nlot of free time will try disproportionally hard to break into it.\n\nThe attacker does not have access to the computer on which the file is stored\nat the time it's edited or viewed. They only have access long after creation.\n\nConstraints\n-----------\nIt needs to be easy to interpret the file even if the code that would do that\nhas entirely rotted. Even if there's no rust compiler in the future, the words\nin this document and a copy of the file should be enough to decrypt it.\n\nWe wrote the first working version of this tool in less than 5 hours. If all\nwe needed was to decrypt a file, it would've been even faster.\n\nDisk Format\n-----------\nThe on-disk format is a single JSON object with the following keys:\n\n= salt =\nA salt to use with scrypt.\n32 bytes base64 encoded.\n\n= nonce =\nA nonce to use with ChaCha20Poly1305.\n8 bytes base64 encoded.\n\n= ciphertext =\nCiphertext generated by ChaCha20Poly1305.\nVariable-length Base64 encoded.\n\n= tag =\nAn authentication tag generated by ChaCha20Poly1305.\n16 bytes base64 encoded.\n\nThe Base64 alphabet is \"[A-Z][a-b][0-9]+/.\". If you need a more formal\ndefinition see Section 3 of RFC3548. Lifecrypt does no padding.\n\nHere's an example of what this file might look like on disk:\n\n{\n  \"salt\": \"efx8L9xEQgqKCd8/+jPAlO8oRO+oxB0bkH/Dv/jAIzg\",\n  \"nonce\": \"YLnDhkib/HU\",\n  \"ciphertext\": \"sNiY30NV\",\n  \"tag\": \"xyX+f3N0U084zthdY70VhQ\"\n}\n\nDecoding\n--------\nTo view the plaintext you must know the password and have the on-disk data.\nThen,\n\nkey = scrypt(password, disk.salt, log_n=14, r=8, p=1)\nplaintext = ChaCha20Poly1305::decrypt(disk.ciphertext, disk.nonce, disk.tag, key)\n\nEncoding\n--------\nEncoding directly follows from the decoding process:\n\npassword = get_password_input()\nplaintext = get_plaintext()\nsalt = random_bytes(32)\nkey = scrypt(password, salt, log_n=14, r=8, p=1)\nnonce = random_bytes(8)\nciphertext, tag = ChaCha20Poly1305::encrypt(plaintext, nonce, key)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcgaebel%2Flifecrypt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcgaebel%2Flifecrypt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcgaebel%2Flifecrypt/lists"}