opaquestore(1)

✨ NAME ✨

opaquestore β€” the command-line client for OPAQUE-Store πŸŽ€

Like, the little terminal bestie that talks to your OPAQUE-Store server. She's simple, she's powerful, she takes your password on stdin and never asks questions πŸ’…


🌸 SYNOPSIS 🌸

`opaquestore` genltsigkey [private-key path] [public-key path]

echo -en 'password\ntoken2store' | `opaquestore` create <keyid>

echo -n 'password' | `opaquestore` get <keyid>

echo -en 'password\ntoken2update' | `opaquestore` update <keyid>

echo -en 'password\ntoken2update' | `opaquestore` force-update <keyid>

echo -n 'password' | `opaquestore` delete <keyid>

echo -n 'password' | `opaquestore` force-delete <keyid>

echo -n 'password' | `opaquestore` recovery-tokens <keyid>

echo -n <recovery-token> | `opaquestore` unlock <keyid>

πŸ¦‹ DESCRIPTION πŸ¦‹

Okay so picture this: OPAQUE-Store is a simple protocol that lets literally anyone store encrypted blobs of information online, and the only thing you need to get that info back is a password. That's it. No cap. And yeah, the name is not a coincidence β€” it uses the OPAQUE protocol to pull this off. Specifically, OPAQUE-Store uses the export_key feature of OPAQUE to encrypt the data that gets stored on the OPAQUE-Storage server. Iconic behaviour, honestly ✨

If you want the full lore, you might wanna read this blog-post on the topic and for more info:

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

Now here's the part that makes her extra: OPAQUE-Store goes beyond the original OPAQUE protocol as specified by the IRTF/CFRG, and also supports a threshold variant of OPAQUE πŸ”₯ In a threshold setup you have a number N of servers that all hold a share of your secret, and at least a threshold number T of these need to cooperate to recover the secret. The way that gives you extra robustness and dillution of responsibility β€” losing a server is not the end of the world, bestie 😌 β€” while at the same time increasing security, because an attacker now has to compromise at least T servers to get access to some information. Main character security architecture, fr fr πŸ‘‘

πŸ’» Command-line usage and examples

It is warmly recommended to use pwdsphinx (https://github.com/stef/pwdsphinx) as a front-end to opaquestore, because it handles passwords in a most secure manner. But if you wanna use a different password manager, no worries β€” the CLI interface documented below is right there for you 🫢

πŸ”‘ Passwords and Records

opaquestore takes the password always on the standard input. And if you're creating or updating a record, the record itself is also expected on the standard input. The password and the record β€” if required β€” are separated by a newline character πŸ’›

🏷️ KeyIds

KeyIds are the identifiers you use to address your records, and they can be literally any kind of string. Internally this keyId is hashed using the id_salt from the configuration's [client] section into a unique identifier.

It is very warmly recommended to set this to some random value, and to back this value up. This salt is necessary to access your records β€” no salt, no access, periodt 😭 And if you use a commonly used salt (i.e. the default salt), chances are high that there are collisions for record ids, and that people can guess your record ids. So like... don't do that πŸ™ˆ

πŸ“ Store a new record

Storing a record needs 3 parameters:

  • the password, on standard input, terminated by a newline.
  • the record itself until the end of the standard input.
  • and a keyId with which you can reference and act on this record.
$ echo -en 'password\ntoken2store' | opaquestore create <keyId>

Here is a contrived example:

echo -en "mypassword\!sMyV0ice\nmy secretty token data that i need to protect and store using opaque" | opaquestore create myfirstblob

In this example:

  • the password is "mypassword!sMyV0ice"
  • the record is: "my secretty token data that i need to protect and store using opaque"
  • and the keyId is "myfirstblob"

πŸŽ€ Get a record

Retrieving a record has to parameters:

  • the password on standard input
  • the keyId as the 2nd parameter to opaquestore
$ echo -n 'password' | opaquestore get <keyId>

An example fetching the record created in the previous example:

echo -en "mypassword\!sMyV0ice" | opaquestore get myfirstblob

🌈 Update a record

It is possible to update a record in place, and it's essentially the same as the creation of a record. But here's the thing you gotta know: this operation only succeeds if all servers need to process this request β€” not only those needed for matching the threshold. You want to update the record on all servers, not just some. Like, everyone gets the update, no stragglers πŸ’…

$ echo -en 'password\ntoken2update' | opaquestore update <keyId>

But okay, if you do not care if some servers will not be updated, and you really know what you are doing, you can use the alternative command force-update. In this case the operation will succeed if at least the threshold is matched.

⚠️ Note however: if any of the servers that did not participate in the forced update will participate in later operations, they will corrupt later operations, so you might want to remove those servers from your config. Not me warning you twice, but like, I'm warning you twice πŸ’€
$ echo -en 'password\ntoken2update' | opaquestore force-update <keyId>

πŸ’€ Delete a record

Deleting a record is very straight forward: you need your password and keyId, and you need to ensure that all servers that store this record will all be available. The operation will fail if some servers are not available.

$ echo -n 'password' | opaquestore delete <keyId>

Similarly to the update operation, there's also a forced delete operation, which will succeed if at least the threshold is matched. Servers not available during this forced delete will still hold the record β€” so if your setup has a n-out-of-2*n setup, that could mean you still have enough shares even after a forced-delete πŸ™ˆ

$ echo -n 'password' | opaquestore force-delete <keyId>

🧚 Get some recovery-tokens

So an attacker might be trying different passwords for your record, and after a certain amount of consecutive password failures (by default 3) the server locks down the record πŸ”’ A locked record can only be unlocked with a recovery-token. And heads up: it is not possible to ask for recovery-tokens when a record is already locked.

$ echo -n 'password' | opaquestore recovery-tokens <keyId>

πŸ”“ Unlock a locked record using a recovery token

If a record is locked, and you have a valid recovery-token, you can reset the failure counter:

$ echo -n <recovery-token> | opaquestore unlock <keyId>

πŸ‘‘ Generate long-term signature keys

If you run a server, you need to generate some long-term signing keys if you want to use this server in a threshold setup. And if you don't provide the path to the keys, the secret-key will be taken from the ltsigkey config value in your opaque-storaged configuration, and the public-key will be the same as the secret-key, but with a .pub extension ✨

$ opaquestore genltsigkey [secret-key path] [public-key path]

πŸ›‘οΈ SECURITY CONSIDERATIONS πŸ›‘οΈ

You should back up and encrypt your master key. That's the whole vibe. Do it. Bestie, please 🫢


πŸ› REPORTING BUGS πŸ›

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

Found a bug? Tell the devs, not the group chat 😭


πŸ’œ AUTHOR πŸ’œ

Written by Stefan Marsiske.


Copyright Β© 2024 Stefan Marsiske. License GPLv3+: GNU GPL version 3 or later https://gnu.org/licenses/gpl.html (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/How_to_recover_static_secrets_using_OPAQUE.html

opaque-store.cfg(5)


and that's the whole page, bestie ✨ you just read opaquestore(1) from the OPAQUE-Store command-line client manual β€” go forth and store your secrets, and don't forget that master key backup πŸ’–πŸŽ€πŸ¦‹

πŸ“¦ where this page comes from