diff --git a/bip85ssh/README.md b/bip85keys/README.md similarity index 56% rename from bip85ssh/README.md rename to bip85keys/README.md index 7a21fbf..b4e4e4c 100644 --- a/bip85ssh/README.md +++ b/bip85keys/README.md @@ -1,11 +1,14 @@ -# bip85ssh +# bip85keys -`bip85ssh` turns a BIP-39 mnemonic (or a BIP-32 `xprv`) into SSH key pairs that -can be recreated from that secret at any time. The same secret and the same -index always give the same key. Only ed25519 keys are produced. +`bip85keys` turns a BIP-39 mnemonic (or a BIP-32 `xprv`) into key pairs that can +be recreated from that secret at any time. The same secret, key type and index +always give the same key. It uses the BIP-85 entropy deriver from `git.eeqj.de/sneak/secret/pkg/bip85` and -follows the same steps as that repository's `agehd` package. +takes the same steps as that repository's `agehd` package. + +Commands are grouped by key type: `bip85keys ssh ...` for ed25519 SSH keys (in +this PR) and `bip85keys age ...` for age identities (planned, see below). ## Derivation @@ -15,20 +18,44 @@ The secret is turned into a key like this: 2. seed -> BIP-32 master key; 3. master key -> 64 bytes of BIP-85 entropy at the path below; 4. entropy -> BIP-85 DRNG (SHAKE256); read 32 bytes; -5. those 32 bytes are the ed25519 seed. +5. those 32 bytes become the key in the way the key type needs. The path is: ``` -m/83696968'/592366788'/1822331379'/' +m/83696968'/592366788'/'/' ``` - `83696968` is the fixed BIP-85 purpose. - `592366788` is the vendor id, `sha256("berlin.sneak") & 0x7fffffff`, the same one `agehd` uses. -- `1822331379` is the application id, `sha256("bip85ssh") & 0x7fffffff`. +- `app` is the application id of the key type; each type has its own. - `n` is the key index: flag `--index` / `-n`, default `0`. +## Giving it the secret + +The secret is never taken as a command-line argument. It is looked for in this +order; the first one found wins: + +1. `--mnemonic-file ` or `--xprv-file `: the file holds the secret + on one line. +2. Environment variable `BIP85KEYS_MNEMONIC` or `BIP85KEYS_XPRV`. +3. A prompt on the terminal with echo turned off. The prompt accepts either a + mnemonic or an `xprv`; a value starting with `xprv` is treated as an `xprv`. + +If none of these is available and standard input is not a terminal, the tool +refuses and exits with status 1. + +Every command takes `--index` / `-n` and the secret flags above, and has +`--help`. `bip85keys --version` prints the version set at build time. + +## SSH keys: `bip85keys ssh` + +Only ed25519 keys are produced. The application id is `1822331379`, +`sha256("bip85ssh") & 0x7fffffff`, so the path is +`m/83696968'/592366788'/1822331379'/'`. The 32 bytes from step 4 are the +ed25519 seed. + Test vector, mnemonic `abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about`: @@ -37,26 +64,7 @@ index 0: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA1esgfi4OeaywgKh0o5r/8lMOUlUD/N+Yo index 1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB26T7hdDuUF6wfTQ7NpIpyeTgGha4NlhQjaAhap5dqs ``` -## Giving it the secret - -The secret is never taken as a command-line argument. It is looked for in this -order; the first one found wins: - -1. `--mnemonic-file ` or `--xprv-file `: the file holds the secret - on one line. -2. Environment variable `BIP85SSH_MNEMONIC` or `BIP85SSH_XPRV`. -3. A prompt on the terminal with echo turned off. The prompt accepts either a - mnemonic or an `xprv`; a value starting with `xprv` is treated as an `xprv`. - -If none of these is available and standard input is not a terminal, the tool -refuses and exits with status 1. - -## Commands - -Every command takes `--index` / `-n` and the secret flags above, and has -`--help`. `bip85ssh --version` prints the version set at build time. - -### `bip85ssh pub` +### `bip85keys ssh pub` Prints one `authorized_keys` line to standard output: @@ -64,16 +72,16 @@ Prints one `authorized_keys` line to standard output: ssh-ed25519 ``` -The comment defaults to `bip85ssh/`; change it with `--comment`. +The comment defaults to `bip85keys/ssh/`; change it with `--comment`. -### `bip85ssh priv` +### `bip85keys ssh priv` Prints the unencrypted private key in OpenSSH format (the `-----BEGIN OPENSSH PRIVATE KEY-----` block that `ssh` reads) to standard output and nothing else, so it can be redirected into a file. The key's comment is the same as for `pub`. -### `bip85ssh install <[user@]host> [-- ssh options...]` +### `bip85keys ssh install <[user@]host> [-- ssh options...]` Runs the system `ssh` to the host and, on the host: @@ -85,7 +93,7 @@ It then prints `added` or `already present`. How this `ssh` connection authenticates is up to the user's normal `ssh` setup (existing keys, agent, password). Anything after `--` is passed to `ssh` unchanged. -### `bip85ssh ssh [ssh arguments...]` +### `bip85keys ssh ssh [ssh arguments...]` Derives the key, serves it from an SSH agent that runs inside the tool on a unix socket in a new private `0700` temporary directory, then runs the system `ssh` @@ -93,23 +101,40 @@ with `-o IdentityAgent=` followed by the host and all remaining arguments unchanged. The tool exits with `ssh`'s exit status and removes the socket and directory on the way out. The private key is never written to disk. +## age identities: `bip85keys age` (planned) + +Not in this PR, which delivers the SSH type first; it is the next type to add. +`bip85keys age pub` and `bip85keys age priv` will derive exactly what +`sneak/secret` derives with its `agehd` package: application id `733482323` +(`sha256("secret") & 0x7fffffff`), so the path is +`m/83696968'/592366788'/733482323'/'`; the 32 bytes from step 4 are clamped +as X25519 requires and encoded as an `AGE-SECRET-KEY-1...` identity. An age key +from this tool for a given mnemonic and index will equal the one `secret` +derives. + +## Adding a key type + +Adding a key type is one package under `internal/` that turns the 32 derived +bytes into that type's key, plus one cobra subcommand under `internal/cli/` that +groups its commands. + ## Errors -Errors go to standard error and the exit status is 1, except for the `ssh` -command, which passes through `ssh`'s own exit status. +Errors go to standard error and the exit status is 1, except for `ssh ssh`, +which passes through `ssh`'s own exit status. ## Building and running ``` -make build # produces ./bip85ssh +make build # produces ./bip85keys make check # fmt-check, lint (golangci-lint) and tests ``` Examples: ``` -bip85ssh pub -n 3 -bip85ssh priv > ~/.ssh/id_bip85_3 -bip85ssh install -n 3 user@example.com -bip85ssh ssh -n 3 user@example.com uptime +bip85keys ssh pub -n 3 +bip85keys ssh priv -n 3 > ~/.ssh/id_bip85_3 +bip85keys ssh install -n 3 user@example.com +bip85keys ssh ssh -n 3 user@example.com uptime ```