{"id":17383745,"url":"https://github.com/krysopath/derive","last_synced_at":"2025-03-27T21:14:59.277Z","repository":{"id":56761802,"uuid":"524788413","full_name":"krysopath/derive","owner":"krysopath","description":"poorman's self sovereign password managment","archived":false,"fork":false,"pushed_at":"2022-10-28T23:17:21.000Z","size":61,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-01T23:36:05.755Z","etag":null,"topics":["kdf","kdf-algorithm","shellscript","ssh","ssh-agent","yubikey"],"latest_commit_sha":null,"homepage":"","language":"Go","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/krysopath.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-08-14T21:56:44.000Z","updated_at":"2022-08-24T22:43:48.000Z","dependencies_parsed_at":"2023-01-20T14:32:49.341Z","dependency_job_id":null,"html_url":"https://github.com/krysopath/derive","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krysopath%2Fderive","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krysopath%2Fderive/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krysopath%2Fderive/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krysopath%2Fderive/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/krysopath","download_url":"https://codeload.github.com/krysopath/derive/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245924515,"owners_count":20694730,"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":["kdf","kdf-algorithm","shellscript","ssh","ssh-agent","yubikey"],"created_at":"2024-10-16T07:43:40.009Z","updated_at":"2025-03-27T21:14:59.229Z","avatar_url":"https://github.com/krysopath.png","language":"Go","readme":"# derive \n\nis an\n\n\u003e hollow anti token container wrapping noise\n\nbased on true random wisdom from `xkcd-pass`\n\n## rationale\n\nThe idea of this is to implement a scriptable way for adding secret keys to\nkeyring agents. Solutions recommended on the web often forget, that on\nmultiuser systems the process tree discloses command arguments to every logged\nuser. They may end up in remote logs and endanger your private keys. Also it is\nnot a solution to write key pass phrases into config files. However a fair\namount of this sin has been committed. Even by accident, it may happen that an\nencrypted file is not encrypted properly on disk and then checked into source\ncontrol. In infrastructure teams often passwordless and shared private keys are\nused. These are all desaster scenarios or severe anti pattern.\n\nIt is recommended to setup an ssh-agent securely, such that it integrates into\nthe os keyring system. People can choose to even integrate a smartcard to host\nthe private keys. But how to deal with private keys, that can not be hosted as\nsuch? You absolutely should store them encrypted (that means they have a\npassphrase guarding their usage)\n\nHowever supplying passphrases to private keys when they are added to the agent\nis a manual effort. The default tooling is build for interactive input. The\ngoal of this project is to find a way to programmatically derive key phrases in\nsuch a way that they can be fed to ssh-agent. This method should be more secure\nthan keys without encryption passphrase.\n\n\n- This tool can derive new keys based on salt and secret key factors via pbkdf2.\n- Use if you cant place a key on smartcard, but also dont want to use passwordless \n  keys\n- Programmatically create and use encrypted key material in pipelines\n- Avoid communicating secret passphrases between departments. Procedurally\n  derive one twice.\n- better even: use a dedicated crypto host and smartcards for when it matters!\n\n\n## installation\n### go\n\n```\ngo install github.com/krysopath/derive/cmd/derive@v1\n```\n\n### compile\n\n```\ngit clone git@github.com:krysopath/derive.git\ncd derive\n\n# checking deps for build\nmake deps\n\n# testing and building and installing code in  workdir\nmake install\n```\n\n\n## roadmap\n\n- a better method to receive a kdf result\n    - to not leak the secret to the consumer OS\n    - but run a KDF inside the smartcard, with a secret from the smartcard\n- statefile for count of operations per key topic\n- blinking yubikey lights\n- more KDF juice\n- when outputting as ascii, it might happen that several hundred bytes of input do not contain printable characters, this MUST be mitigated\n    - when such an unprintable char is received it will be rejected\n    - currently there is a mitigation but no prevention:\n        - we generate 2x as much bytes and discard what does not fit\n        - this is assuming we can fit the requested bytes, though\n        - unlikely as it sounds:\n        - secret with only empty bytes is possible and that would break the security\n        - for such a case a prevention is planned, but not properly implemented\n- audit\n\n## contributions \n\u003e are always welcome\n\n- leave an issue if you are missing something\n- raise a problem if you see one\n- suggest expected usecases\n- `\u003c3`\n\n## setup\n\n```\nDERIVE_SALT=$(openssl rand -base64 48)\ngo install github.com/krysopath/derive/cmd/derive@v1\ncat \u003c\u003cEOF \u003e\u003e ~/.bashrc\nexport DERIVE_SALT=$DERIVE_SALT\nEOF\n\nsource ~/.bashrc\n```\n\n\u003e we assume your ssh-agent is properly setup\n\n## usage\n\n```\nderive [FLAGS] [purpose]\n\nFLAGS:\n  -b int\n    \tlength of derived key in bytes (default 32)\n  -c int\n    \trounds for deriving key (default 4096)\n  -f string\n    \tkey output format: bytes|base64|hex|ascii|ascii@shell (default \"bytes\")\n  -h string\n    \thash for kdf function (default \"sha512\")\n  -k string\n    \tkdf function for deriving key (default \"pbkdf2\")\n  -v string\n    \t'versioned' key  (default \"1000\")\n\n```\n\nsimple run:\n```\n$ derive -b 12 -f hex\n! Enter Secret Token (hold Yubikey 5secs) ...OK\n16F61AD0160EE71CAC668FC3\n```\n\u003e `derive` reads a salt from the environment and waits for a newline character\n\u003e on `/dev/stdin`. It uses those values and the passed arguments to derive a\n\u003e key and emit it to `dev/stdout\n\n\u003e the author used a usb HID rubberduck to output a static secret string\n\n\u003e a yubikey may be programmed to emit a static key of 38 bytes via HID\n\n\u003e a master password can be remembered and concatened with the rubberduck static key, too!\n\n\u003e consider using `| xclip -i -selection clipboard` to capture the results\n\n### derive with yubikey?\n\nYes, you need to configure slot 2 for emitting a static secret:\n\n`ykman otp static --generate --length 38  --keyboard-layout US 2`\n\n\u003e maxlength is 38 bytes\n\n\u003e slot 1 hosts u2f, lets not overwrite it.\n\n\u003e static OTP bytes are static :.(\n\n\n- Such a static key has no smooth rotation\n- if it leaked once (remember it is emitted plaintext stdout), then your secrets are void\n- though together with `derive` that static key can be used to derive many more keys\n- and at the same time it prevents disclosure of that key by accidental stdout shell\n  blooper (because it emits after a long press and is hashed)\n- You only trust your host, not a remote system.\n- However you trust your host enough with your static key. This is always a risk.\n- Be sure to disable the static code feature for the NFC channel tho. Would be embarassing.\n- Be sure to keep a backup rubberduck, yubikey or else with the same static\n  key, else it would embarassing too.\n\n\n### secure ssh pkeys with phrases unlocking automatically?\n\n\u003e quickly, hold your terminals!\n\n\ncreate `$HOME/bin/ssh_give_pass.sh`\n```\ncat \u003c\u003cEOF\u003e ~/bin/ssh_give_pass.sh\n#!/bin/bash\ncat\nEOF\n```\n\u003e magic script is implementing the API of /bin/cat, like an echo server: `cat \u003c/dev/stdin \u003e/dev/stdout`\n\n\u003e key derivation could happen in here, but would be less flexible then.\n\nAfter you add this shell function below to `.bashrc` e.g.\n```\ncat \u003c\u003cEOF\u003e\u003e ~/.bashrc\nadd_keyfile_to_agent() {\n    if [ -n \"\\$1\" -a -r \"\\$1\" ]; then\n        derive -b 32 \\\\\n            -f base64 \\\\\n            -v \\$(basename $1) ssh \\\\\n        | DISPLAY=:0 SSH_ASKPASS=\\$HOME/bin/ssh_give_pass.sh ssh-add \\$1\n    fi\n}\nEOF\n\n# source and run\nsource ~/.bashrc\nadd_keyfile_to_agent ~/.ssh/id_rsa\n```\n\u003e this function will try to open a private key file and add it to the ssh-agent\n\n\u003e this invocation derives a key and passes it via the SSH_ASKPASS script into ssh-add\n\n\u003e this method leaves no passphrases on disk and does not disclose exec arguments in `ps`\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrysopath%2Fderive","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkrysopath%2Fderive","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrysopath%2Fderive/lists"}