sphinx(1)

πŸ“¦
version 2.0.4 Β· 3 versions
, in nixos 26.05, nixpkgs unstable, ubuntu stonking

πŸ’– sphinx(1) β€” the command-line client for the SPHINX password manager πŸ’–

okay so bestie, this is sphinx, the command-line client for the SPHINX password manager, and honestly? she's giving cryptography royalty πŸ‘‘. Grab a snack πŸ“, because we're walking through the entire lifecycle of a password together and I am not letting you skip a single flag.


✨ SYNOPSIS ✨

sphinx init

echo β€œpassword” | sphinx create <user> <site> [<u><l><d><s>] [<size>] [<symbols>] [<target password>]

echo β€œpassword” | sphinx get <user> <site>

echo -e β€œoldpassword” | sphinx change <user> <site> [<u><l><d><s>] [<size>] [<symbols>] [<target password>]

[ echo β€œpassword” | ] sphinx commit <user> <site>

[ echo β€œpassword” | ] sphinx undo <user> <site>

[ echo β€œpassword” | ] sphinx delete <user> <site>

sphinx list <site>

sphinx healthcheck

sphinx qr [<svg>] [<key>]

Here's the general vibe, and it's lowkey the most important rule of the whole page: if an operation needs a master (input) password, it expects it on standard input, and any resulting account (output) password gets printed to standard output. πŸ’…

The examples below use echo, but for real life the recommendation is to use getpwd(1) or similar tools to query and pass the input password. (Because echo is a snitch, no cap. πŸ™ˆ)


πŸ¦‹ DESCRIPTION πŸ¦‹

SPHINX stands for password Store that Perfectly Hides from Itself (No Xaggeration) β€” obsessed with that name fr fr. It's an information-theoretically secure cryptographic password storage protocol with strong security guarantees. πŸ’–

The protocol is described in the 2015 paper β€œDevice-Enhanced Password Protocols with Optimal Online-Offline Protection” by Jarecki, Krawczyk, Shirvanian, and Saxena (https://ia.cr/2015/1099). Go read it, it ate. πŸ”₯


🌸 OPERATIONS 🌸

sphinx is the command-line client for the SPHINX protocol. It gives you access to all the operations over the lifecycle of a password: init, create, get, change, undo, commit, delete, and list. ✨

Every single one of those operations requires a username and a site that the password belongs to β€” even if they're just empty strings. Yes bestie, even then. 😭

On top of that, it also provides operations that make these features more user-friendly: listing of users associated with a host, and export of the configuration using a QR code. πŸŽ€

And she's multidimensional β€” sphinx doesn't only handle passwords, it also handles Time-based One-Time Password (TOTP) Two-Factor Authentication (2FA) and age keys. πŸ’œ

Additionally, if installed, sphinx also provides access to OPAQUE-Store, a simple tool that allows one to store secrets that need encrypted storage (like keys, phrases, or other data). Slay.


🧚 INITIALIZING A CLIENT

sphinx init

This creates a new masterkey for the client, which is used to address records on the SPHINX server and authorize management operations on those records. πŸ”‘

You SHOULD back up and encrypt this masterkey. Like, please. πŸ™

If you want to use SPHINX on a different device, you want to copy this masterkey there as well. For copying this key (and other settings) to the Android client, androsphinx, see the qr operation below. πŸ“±

This init operation also creates a fixed healthcheck record on the server(s), as described in the HEALTHCHECK section below. 🩺

Additionally, if ~/.mozilla or ~/.config/chromium directories are found, the init command will automatically set up the web extensions for Firefox and Chromium-based browsers respectively. She's proactive like that. πŸ’…


πŸŽ€ CREATE PASSWORD

To create a new password for a site, provide your master password via standard input to the client, along with the required parameters. For example:

echo -n 'my input password' | sphinx create username example.com ulsd 0 ' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'

The parameters are:

  • Master password on standard input: Unlike traditional password managers that use a single master password for the entire database, you can use different input passwords for different username/site combinations. Main character energy, honestly. ✨
  • create: Specifies the operation type.
  • username: Your username for the site.
  • example.com: The target site.
  • Password constraints: Rules for generating the password. See sections PASSWORD RULES and PREDETERMINED PASSWORDS for more info.

If the command runs successfully, the resulting new high-entropy output password, generated according to the given rules, is printed to the console. 🫢


πŸ’œ GET PASSWORD

To retrieve a stored password from the SPHINX oracle, run:

echo -n 'my master password' | sphinx get username example.com

You provide your master password via standard input, followed by:

  • get: Specifies the operation.
  • username: Your username.
  • example.com: The site.

The corresponding password is printed to standard output. πŸŽ‰


πŸ”₯ CHANGE PASSWORD

If you need to change a password (whether the site required it or you just felt like it πŸ’…), you can do so without changing your master password β€” although you may change both if desired. For example:

echo -en 'my master password\nnew masterpassword' | sphinx change username example.com 'ulsd' 0 ' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'

The parameters are:

  • On standard input, provide your current master password, followed by a newline, then your new master password. The new master password can be identical to the old one if you only want to update the site password. πŸ‘―
  • change: Specifies the operation.
  • username: Your username.
  • example.com: The site.
  • Password generation rules: Same format as when creating the original password. Adjust these if the site now requires different constraints (see PASSWORD RULES and PREDETERMINED PASSWORDS).

The newly generated password will be printed to standard output.

IMPORTANT: This command only generates the new password β€” it does not activate it. The get command will still return your old password until you run a commit operation to make the change effective. See the next section for details. πŸ’€ (don't say I didn't warn you)

⭐ COMMITTING A CHANGED PASSWORD

After changing a password, the get operation will still return the old password. To start using the new password, you must commit the change by running:

echo -n 'my master password' | sphinx commit username example.com

Depending on your rwd_keys configuration (explained in the SECURITY CONSIDERATIONS section below), you may need to provide your input password on standard input for this operation. πŸ”‘

If the operation is successful, there will be no output. If an error occurs, an error message will be displayed and the command will exit with a non-zero status code. 😭


🌈 UNDOING A PASSWORD COMMIT

If you need to revert to the old password after committing a new one, run:

echo -n 'my master password' | sphinx undo username example.com

Depending on your rwd_keys configuration, you may need to provide your master password on standard input for this operation.

If successful, there will be no output. Otherwise, an error message and a non-zero exit code will indicate a problem. πŸ™ˆ


πŸ¦‹ DELETING PASSWORDS

To delete a password, use the following command:

echo -n "my master password" | sphinx delete username example.com

Here:

  • delete specifies the operation.
  • username is your account name.
  • example.com is the target site.

If the operation succeeds, there will be no output. If it fails, you will see an error message and a non-zero exit code. Depending on your rwd_keys configuration, you may need to provide your master password via standard input. πŸ’…


πŸ“± QR CODE CONFIG

If you want to use your phone with the same SPHINX server, you can export your configuration to the phone using a QR code.

sphinx qr

This displays a QR code containing configuration information from your [servers] section and client settings such as userlist, validate_password, delete_upgraded, and threshold. This is useful for sharing your setup with trusted friends or family. 🫢

To connect your own phone to the setup used with pwdsphinx, include your client secret in the QR code:

sphinx qr key
This QR code contains sensitive information, so keep it private and ensure no cameras can capture it while it's displayed. πŸ‘€ no screenshots bestie

To display the QR code as an SVG instead of text, append the svg keyword to the sphinx qr command:

sphinx qr svg

🩺 HEALTHCHECK

If you have run the sphinx init command, it has created a fixed healthcheck record. You can verify your setup by running:

sphinx healthcheck

This checks connectivity to the server without affecting rate limits. ✨

Alternatively, you can test with a get operation:

echo -n 'all ok?' |  env/bin/sphinx get healthcheck "sphinx servers"

This should output β€œeverything works fine”.

The difference is that healthcheck only fetches rate-limiting challenges and then stops, while get will count against your rate limit if used too frequently. So like, use the right one. 😭


πŸŽ€ PASSWORD RULES

When creating or changing passwords, you can specify rules limiting the size and characters allowed in the output password.

The letters u, l, s, and d represent the following character classes:

  • u: Upper-case letters
  • l: Lower-case letters
  • s: Symbols
  • d: Digits

The s shortcut includes all supported symbols. If you are limited by the server to specific symbols, you can explicitly specify them.

Supported symbols (note the leading space character πŸ‘€) are:

 !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

When using these symbols on the command line, be sure to escape quotes and possibly the backslash character. In the create username example above, symbols are correctly escaped for copy/paste. 🫢

For examples, see the sections CREATE PASSWORD and CHANGE PASSWORD.


⭐ DEFAULT RULES

If you do not specify password rules, they will default to ulsd with the maximum possible length of 77 characters. This means passwords will use all four character classes, providing 507 bits of entropy. This is far more than necessary for most use cases. πŸ”₯


When using the ulsd character classes, it is recommended to limit the output password length to a maximum of 12 characters. This is especially useful if you ever need to type the password on a TV remote or in other stressful situations. 😭 (the way that TV keyboards are a personal attack)

A 12-character password with full entropy using all printable ASCII characters offers almost 80 bits of entropy, making it computationally impossible to brute force with current password-cracking hardware. For comparison, a 15-character password offers nearly 99 bits of entropy. πŸ’…


🌸 PREDETERMINED OUTPUT PASSWORDS

If you cannot use random passwords for your account, or if you need to store a fixed β€œpassword” such as a PIN code or a shared passphrase, you can set a predetermined password (up to 77 characters). The SPHINX client will generate it for you. ✨

For example, the command below (which also works for the change operation) sets a fixed password:

echo -n 'my master password' | sphinx create username example.com "correct_battery-horse#staple"

In this case, you cannot specify character classes, size, or symbols. These values will be inferred from the predetermined password itself. πŸ¦‹

πŸ’Ύ Backward compatibility with v1 SPHINX servers/records πŸ’Ύ

okay so bestie, if you've still got old v1 SPHINX records sitting on the server that were made back in the v1 era and you actually wanna keep using them?? you gotta specify that server in the client section exactly like you did in v1. no shortcuts, no vibes-only approach.

Here's the cute part tho: if no v2 record turns up during a get operation, SPHINX will automatically go looking for a v1-style record. And if it finds one? It just makes a brand-new v2 record for you on the spot ✨. So you literally do not need to manually go hunting for v1 records in that situation. it's giving self-care automation.

Now, if you're not using other v1-only clients (hi androsphinx πŸ‘‹), those upgraded v1 records can get automatically deleted after a successful migration over to v2. To turn that on, set delete_upgraded to true in the [client] section of your SPHINX configuration. This keeps your server database all clean and sparkly 🧼 and makes it super easy to track how many v1 records are still hanging around.


✨ OUTPUT PLUGINS (TOTP & AGE) ✨

SPHINX can also "store" TOTP secrets and age secret keys πŸŽ€. And when you store a secret like that, SPHINX automatically handles it the right way (like, outputting the TOTP verification code instead of the secret itself). Obsessed.

To make this happen, prefix your username with otp:// for TOTP support, or age:// for age key support. And for age keys specifically, the query will return a properly formatted private key. no cap.

Currently, the following converters are supported:

πŸ”’ TOTP πŸ”’

Import a TOTP secret:

% getpwd | sphinx create otp://username example.com ABCDEF1234567890

Get a TOTP PIN:

% getpwd | sphinx get otp://username example.com

✍️ minisign ✍️

Create a new key and store the public key at /tmp/minisig.pub:

% getpwd \
    | sphinx create minisig://user example.com >/tmp/minisig.pub

Create and Change SPHINX operations automatically return a public key. πŸ’…

Sign a file filetosign:

% getpwd \
    | sphinx get minisig://user example.com \
    | pipe2tmpfile minisign -S -s @@keyfile@@ -m filetosign

The Get SPHINX operation returns a private key. πŸ”‘

🧬 Age 🧬

Generate an age key and store the public key:

% getpwd \
    | sphinx create age://user example.com >/tmp/age.pub

Create and Change SPHINX operations automatically return a public key.

Decrypt a file using an age key from SPHINX:

% getpwd \
    | sphinx get age://user localhost \
    | pipe2tmpfile age --decrypt -i @@keyfile@@ encryptedfile

The Get SPHINX operation returns a private key.

πŸ¦‹ SSH-ED25519 πŸ¦‹

Create a key and save the public key:

% getpwd \
   | sphinx create ssh-ed25519://test asdf >pubkey

Create and Change SPHINX operations automatically return a public key.

Sign a file:

% getpwd \
   | sphinx get ssh-ed25519://test asdf \
   | pipe2tmpfile ssh-keygen -Y sign -n file -f @@keyfile@@ content.txt > content.txt.sig

The Get SPHINX operation returns a private key.

Verify a file with a public key:

% ssh-keygen -Y check-novalidate -n file -f /tmp/ssh-ed.pubkey -s /tmp/content.txt.sig </tmp/content.txt

πŸ” OPAQUE-Store INTEGRATION πŸ”

If you have OPAQUE-Store installed and configured correctly (see GitHub for that, and also see opaque-stored.cfg(5)), you unlock a whole extra set of commands that let you store encrypted blobs of data 🫢. These commands only show up if OPAQUE-Store is properly set up, jsyk:

echo -n 'password' | sphinx store <keyid> file-to-store
echo -n 'password' | sphinx read <keyid>
echo -n 'password' | sphinx replace [force] <keyid> file-to-store
echo -n 'password' | sphinx edit [force] <keyid>
echo -n 'password' | sphinx changepwd [force] <keyid>
echo -n 'password' | sphinx erase [force] <keyid>
echo -n 'password' | sphinx recovery-tokens <keyid>
echo -n 'password' | sphinx unlock <keyid> <recovery-token>

πŸ€” How does OPAQUE-Store SPHINX integration work

In all OPAQUE-Store commands, a SPHINX get operation runs first. This generates the password used with OPAQUE, which means the OPAQUE password ends up extremely strong and nearly impossible to brute-force unless somebody goes through SPHINX. slay, security-wise.

BUT β€” online brute-force attempts via SPHINX are still possible 😭. OPAQUE can detect invalid passwords and lock records after a configured number of failed attempts. Just know that this lock does not apply to the operator of an OPAQUE server, who could bypass it. πŸ’€

⚠️ A WARNING: Don't let one entity control enough of your SPHINX and OPAQUE-Store servers ⚠️

Every OPAQUE-Store operation requires a password via standard input. That password gets run through SPHINX, and the output password is then used in the OPAQUE protocol as the input password.

So if both SPHINX and OPAQUE-Store are hosted on the same server, or controlled by the same third party, they could potentially perform an offline brute-force attack against your SPHINX master password. πŸ‘€ If you use a threshold setup where different entities control the servers, you stay secure as long as no single entity controls enough servers to meet the threshold. That's the whole point fr fr.

🧾 OPAQUE-Store CLI Parameters 🧾

KeyId β€” every OPAQUE-Store integration command needs a keyid parameter, which references your stored record. The client uses the id_salt configuration value together with the OPAQUE-Store server's name to hash the keyid into a record ID. So if you lose or change either id_salt or the server name, your record IDs will change and your data becomes inaccessible 😭. Back up your configuration file so this never happens to you. And while id_salt doesn't need to be secret, keeping it private adds an extra layer of security. πŸ’œ

Forced operations β€” in threshold setups, certain commands (replace, edit, changepwd, and erase) require that all servers participate successfully in the operation. Why? To avoid situations where some servers become out of sync (corrupted) from the others πŸ™ˆ. If you're certain it's safe, you can add the force flag to let the operation succeed with only the minimum number of servers specified by your threshold setting.


πŸ“¦ Store an encrypted blob

getpwd | sphinx store <keyid> file-to-store

Stores the specified file-to-store in encrypted form on the OPAQUE-Store server, using a password derived from SPHINX.

Heads up bestie: this command also outputs a recovery token, and you should keep that safe in case the record is ever locked. Don't lose it!! πŸ”‘

πŸ“€ Retrieve an encrypted OPAQUE-Store blob

getpwd | sphinx read <keyid>

Retrieves the record stored at keyid and outputs it to standard output.

♻️ Overwrite an encrypted OPAQUE-Store blob

getpwd | sphinx replace [force] <keyid> file-to-store

Replaces the record stored at keyid with an encrypted version of file-to-store. This command only works if a record already exists at keyid, so don't come at it empty-handed.

By default, all servers must participate successfully. If some are unavailable, you can use force to proceed as long as the threshold is met β€” but just know that unavailable servers will become out of sync (corrupted) from this point on. πŸ’€ you've been warned.

πŸ“ Edit an OPAQUE-Store blob

getpwd | sphinx edit [force] <keyid>

This operation fetches the file stored at keyid, opens it in your default editor (specified by the EDITOR environment variable), and stores the saved changes back at the same keyid, overwriting the original. Very main character of it ngl.

πŸ”„ Change your password on an OPAQUE-Store blob

getpwd | sphinx changepwd [force] <keyid>

This operation rotates all passwords and keys for the record at keyid. And here's the fun bit: even if your own password (the one you supplied to getpwd) stays exactly the same, SPHINX will generate a new key, which in turn produces a new OPAQUE password. That gives you a completely new encryption key for your file, and the file gets re-encrypted with it. glow up πŸ”₯

πŸ—‘οΈ Delete a stored OPAQUE-Store blob

getpwd | sphinx erase [force] <keyid>

Deletes the record stored at keyid. gone, periodt.

🎟️ Get a recovery token

getpwd | sphinx recovery-tokens <keyid>

This operation generates and returns an additional recovery token β€” as long as the record isn't currently locked. 🀞

πŸ”“ Unlock a locked OPAQUE-Store blob

getpwd | sphinx unlock <keyid> <recovery-token>

This operation unlocks a record that has been locked by the servers (like, say, after repeated failed login attempts, or if you forgot your master password 😭). It requires a valid recovery token. Once unlocked, the record is automatically retrieved β€” unless the wrong password is provided again. πŸ™ˆ

✨ SPHINX CONFIGURATION ✨

okay so the client is configurable through any of these files, and bestie, the order matters, they're read in this order:

  • /etc/sphinx/config
  • ~/.sphinxrc
  • ~/.config/sphinx/config
  • ./sphinx.cfg

Later files override earlier ones πŸ’… which is literally the whole point β€” it lets global settings get overridden by per-user and per-directory settings. slay.

πŸŽ€ Client Settings ([client] section)

  • datadir (default: ~/.sphinx) β€” the directory where client parameters live. Specifically, it holds a masterkey that's used to derive secrets. If it's missing, the init command generates it. You SHOULD back up and encrypt this master key πŸ‘‘ no cap.
  • rwd_keys (default: False) β€” toggles whether the master password is required for authentication of management operations. The oracle is completely oblivious to this setting, it's purely a client-side toggle. In theory you could have different settings for different "records" on the oracle.
  • validate_password (default: True) β€” stores a 5-bit check digit on the server that helps detect most master password typos, while slightly reducing security.
  • userlist (default: True) β€” when disabled, it stops the server from correlating all usernames belonging to the same SPHINX user for the same host. If you disable it, the user has to remember which username is used for each host πŸ™ˆ.
  • address / port β€” these two are only used for backward compatibility with old v1 servers. If no record is found with a v2 get operation, SPHINX will then attempt a v1-style get request and see if the record is available from "old times". If a v1 record is found, a new v2-style record is created. After that, a v1 get request for this particular record is not needed anymore.
  • delete_upgraded (default: False) β€” enables automatic deletion of v1 records after upgrading to v2. This setting is recommended unless you're using v1-only clients like androsphinx. It lets server operators see if their users are finally completely v2, so that they can disable v1 support πŸ”₯.
  • threshold β€” the number of servers required for SPHINX operations. If the [servers] section contains more than two entries, this value must be greater than 1 and less than the number of servers listed in the [servers] section: 1 < threshold < len(servers).

🌸 Server Settings ([servers] section)

The [servers] section contains one subsection per server. For example:

[servers]
[servers.zero]
host="localhost"
port=10000
ltsigkey = "32byteBase64EncodedValue=="
  • Section format: [servers.<name>]. The name can be freely chosen and can be a public value. It is important to never change it, as long as you want to access your passwords on this server. This name value is used together with other values to create unique record IDs. If you change the name, the record IDs change, and you will not be able to access your old records 😭 so like, don't do that.
  • host and port β€” should match what you set (or its admin publishes) in the oracle(1) server.
  • ltsigkey β€” this is the server's long-term signing key for threshold operations. It should be a base64-encoded value; however, you can also store the raw binary key in a file and use the ltsigkey_path option instead. This key is only needed for threshold setup, and is not required in single-server mode.

See oracle(1) for more configuration parameters.


πŸ”’ SECURITY CONSIDERATIONS πŸ”’

  • You SHOULD back up and encrypt your masterkey. You can do this using the qr key operation, recording other important details as well. Backing up webauthn_data_dir from the [websphinx] section is also recommended if you're using the web extension and WebAuthn πŸ’–.
  • rwd_keys setting:
    • If False: protects against offline master password brute-force attacks (a security guarantee of SPHINX).
    • If True: prevents denial-of-service attacks where an attacker could change/delete records for known (host, username) pairs if they have the masterkey, but removes the offline brute-force protection.

This is a trade-off between account password availability and master password confidentiality 🫢.

  • validate_password: stores a 5-bit check digit on the server that helps to detect most master password typos. If enabled, this setting decreases security slightly but is generally safe to enable.
  • userlist (default: enabled): this configuration setting allows a server operator to correlate records belonging to the same SPHINX user for the same online service. If multiple accounts on the same service are managed by the same SPHINX server, the server operator can detect when a userlist record is updated and which SPHINX record belongs to this operation. This leaks some information that can be used by an adversarial server operator to correlate records πŸ’€.

πŸ“ Password Entry Example

In this documentation, echo is used only for demonstration. For real usage, use getpwd(1) from the contrib directory if you are not interested in customizing, or consider something like:

echo GETPIN | pinentry | grep '^D' | cut -c3- | sphinx create username example.com ulsd 0

Using pinentry, you can enable double password input, and password quality checks. It's quite versatile ✨.


πŸ› REPORTING BUGS πŸ›

https://github.com/stef/pwdsphinx/issues/

πŸ‘‘ AUTHOR πŸ‘‘

Written by Stefan Marsiske.

Copyright Β© 2024 Stefan Marsiske. License GPLv3+: GNU GPL version 3 or later https://gnu.org/licenses/gpl.html. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.

πŸ”— SEE ALSO πŸ”—

https://www.ctrlc.hu/~stef/blog/posts/sphinx.html

https://www.ctrlc.hu/~stef/blog/posts/oprf.html

https://github.com/stef/opaque-store/

oracle(1), getpwd(1), opaquestore(1)


and that's the whole page bestie, we ate πŸ’…βœ¨ this is sphinx(1) from the pwdsphinx project πŸ’– go protect those passwords, ily, mwah πŸŽ€πŸ¦‹πŸŒˆ

πŸ“¦ where this page comes from