1 Introduction
sopass
manages passwords on the command line using the stateless
OpenPGP interface
(SOP)
for encryption. It is a re-interpretation of the concept championed by
pass
, but using SOP. It does not try to be compatible with pass
,
only to fit in the same ecological niche.
pass
, also known as
"passwordstore" and described as "the standard unix password manager"
is a command line password manager that uses GnuPG for encryption.
sopass
prefers SOP to avoid a lock-in to a specific implementation.
1.1 Example
This section gives a taste of using sopass
. We first initialize the
password store. This creates an empty store as
~/.local/state/sopass/passwords.sopass
, and also copies the key file
to that directory so that later invocations of sopass
find it.
$ sopass init --name mine --key my-openpgpg.key
We can now add a value and show it.
$ echo my secret password | sopass value add --text my/password $ sopass value show my/password my secret password $
We can list all passwords in the store.
$ sopass value list my/password $
Finally, we can remove a value.
$ sopass value remove my/password $ sopass value list $
2 Software architecture
sopass
is a command line tool that runs a SOP implementation as a
sub-process, but does not otherwise interact with other software. It
stores key/value pairs in a password store, which is a JSON file
that has been encrypted with OpenPGP using an implementation of the
SOP interface. The clear text data is never stored on persistently.
The OpenPGP key is stored next to the encrypted store. Whenever data
is accessed, the store is decrypted using the key. Whenever data is
modified, the store is re-encrypted using the key.
Some justifications for the architecture:
- "command line tool"
- this is inherent in the purpose of the tool
- "SOP as a sub-process"
- we want to avoid locking in the user of
sopass
to a specific implementation of cryptography, so we use the SOP interface to access the implementation we use
- we want to avoid locking in the user of
- "does not interact with other software"
- there is no daemon, background process, online service, or other
subsystem that
sopass
uses, because we want to keep it simple to deploy: only thesopass
binary, the store file and the OpenPGP key are needed
- there is no daemon, background process, online service, or other
subsystem that
- "JSON"
- JSON is flexible, well understood, widely supported, and quite sufficient for this use
- JSON is not great at storing large amounts of binary data, but we
don't expect
sopass
to need to do that; if such a need arises later, it's easy enough to change the clear text format to anything supported by the Rustserde
ecosystem
- "OpenPGP"
- OpenPGP is criticized and debated, but works well enough, and the SOP interface makes it easy to use
- we don't expect to change away from OpenPGP or SOP
- "key stored next to store"
- this is simplicity for user: the statelessness of SOP means the key location needs to be specified explicitly, and it isn't implicit the way GnuPG does; this is a big part of why SOP is nicer to use from other programs than GnuPG is
- "never stored persistently"
- this reduces the likelihood of accidentally leaking secrets in clear text
sopass
exchanges data with the SOP implementation via Unix pipes
Some compromises made at this stage development:
- keys without passphrases
- it's simpler to deal with such keys, as it avoids needing machinery to ask the user for a passphrase
- this will change, as it's obviously an unacceptable compromise
- we also aim to support keys backed by hardware modules such as trusted platform or OpenPGP cards
- store is only encrypted
- this is for simplicity
- we will change this so that the store is also always signed so
- single encryption key
- for simplicity
- we will change this so that the store will store all the
certificates that it should be encrypted for, similar to the
.gpg-id
file in thepass
store
- no Git support
- we will add support to automatically commit a modified store to
Git, if the store directory is version controlled with Git; this
is similar to what
pass
does - we will also make it easy to manage the Git repository via
sopass
to mimicpass
more
- we will add support to automatically commit a modified store to
Git, if the store directory is version controlled with Git; this
is similar to what
- no configuration file
- this is temporary, for simplicity
- we will add a configuration file to specify things like store and key location
3 Acceptance criteria
This chapter documents explicit acceptance criteria for sopass
, and
how we verify that the implementation meets them. The verification is
done using "scenarios", and the Subplot
software turns those into executable code. Running the code verifies
the implementation.
3.1 Data files
3.1.1 Pre-generated keys
This is a pre-generated key. We want to avoid generating a new key for each test run, for speed.
-----BEGIN PGP PRIVATE KEY BLOCK----- xVgEZ2+sZBYJKwYBBAHaRw8BAQdAesm4pX4NYlVa3XUImN1VoGYqlV5wrc+0ChK2 nHQJbagAAQCNaRb0BIm+FvnSehQ+eiGlYt7XkHQEpstH0h6IaMIrsg+PzQpsaXdA bGl3LmZpwo8EEBYIADcCGQEFAmdvrGQCGwMICwkIBwoNDAsFFQoJCAsCFgIBJxYh BD3Ix4Uvg4E82hngYARnFaLt6wu0AAoJEARnFaLt6wu03aABANDExR2u4LmA1Ibb DtTyQnxieLRvVeucpgIWIyR6N6i8AP0Uh6M/yIPJKf9+TPXzyX/pW2hPqFgX4Eqt XzFP/OrBCcddBGdvrGQSCisGAQQBl1UBBQEBB0DU+xruTz4AOT0hsLSu6Ji5Onkq xCKtzdW7upJKzInEWAMBCAcAAP9m/VjK+jQOvMF8aBBthpE/nU44qkHwTlORSTFl anrreBBKwngEGBYIACAFAmdvrGQCGwwWIQQ9yMeFL4OBPNoZ4GAEZxWi7esLtAAK CRAEZxWi7esLtL42AP9maHm227K9/V68GOLXLgykAwlwqhx7KKbAcyRJPOhq5gEA z28qv1cXrs0ctv3VAqsMd/OTV+ODDFvwOVvNdDZuYQ0= =AYJk -----END PGP PRIVATE KEY BLOCK-----
This is a second pre-generated key.
-----BEGIN PGP PRIVATE KEY BLOCK----- Comment: BC2B ADD9 3C89 D080 8E98 5A1F 8962 5382 6F49 4D3A xVgEZ3gPORYJKwYBBAHaRw8BAQdARrDdx/wzj8P9F40LJWYpT3wdy9yLvf1U069q vc6M4M4AAQCwrh2W3cRSGCCciDWgMo210pLeq3Os/faaFXaPslu+pRHNwsALBB8W CgB9BYJneA85AwsJBwkQiWJTgm9JTTpHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMu c2VxdW9pYS1wZ3Aub3JnCkala0J2+OcEzzc7NfBJGv0HsiACp/2nWQe54bOlwZUD FQoIApsBAh4BFiEEvCut2TyJ0ICOmFofiWJTgm9JTToAABnwAP9rn+AciFLaN/Up leuz6jVhRfYG33IGj13n35rvs4qm+gEA3uHf9y5T9emmEv95MhhazGlp3xCugki/ i9WkEhTEvALHWARneA85FgkrBgEEAdpHDwEBB0CWv7+ruWlq8GTIo2QG/ycWJMz7 mFBQS9Uibyv7RiB4CAAA/igjj1gIsn4lP5TfzK4aQhx9arUI5b5qYFjgUVCAO3EG Dk3CwL8EGBYKATEFgmd4DzkJEIliU4JvSU06RxQAAAAAAB4AIHNhbHRAbm90YXRp b25zLnNlcXVvaWEtcGdwLm9yZ5Z1FkiL1r/pJIlRW3bogHpL5xt010SBHQWC6eR+ jx3xApsCvqAEGRYKAG8Fgmd4DzkJEA5YzpG5RD+ARxQAAAAAAB4AIHNhbHRAbm90 YXRpb25zLnNlcXVvaWEtcGdwLm9yZ+CQJbbTcTmFrGiLf4ts77eIQ9rYMljuvzwY p/iEYJzgFiEEaZkggLtCLYWSMdahDljOkblEP4AAAPeXAP9C3R7Hn0jqm+xsC2Ym Pv+H4d3eRtfndDCQTR2p3bJwkAD+IRMUXNS/Hg2zoVY4Y6LJMz6sdzi4dgzoUo+q I1tkQAAWIQS8K63ZPInQgI6YWh+JYlOCb0lNOgAAJz8A/jIRrHZZ6lt9LecBzc+Y 7sG75m1XlvY/b8FEhE2ac9bqAQCyK4FVI1tmYmQ2Ji+wMwyRQ6iK8Brd85GSIcFI FVfmBMddBGd4DzkSCisGAQQBl1UBBQEBB0Cr2zlOc4zZQiYg8gIQTBZX4xAJKTin 0JFL8ttuUcLNDAMBCAcAAP9qOp+iWiZMDuekZ8jdQC802NVZZXIe9JNK0YMp+Wc7 kBBswsAABBgWCgByBYJneA85CRCJYlOCb0lNOkcUAAAAAAAeACBzYWx0QG5vdGF0 aW9ucy5zZXF1b2lhLXBncC5vcmf6TAclvwInoVzGCDeyXLkdwBQ4zRdcr+KAXnIW P1CwuAKbDBYhBLwrrdk8idCAjphaH4liU4JvSU06AAAE0wD/eCxv/xXYqgju+noA a/VAyUkJsRGHkLPyK8YX1r565G8BAOpeUkkWeAyOJLVXyZ650xJkoEXvLz3+XGft fU5QwskF =lebt -----END PGP PRIVATE KEY BLOCK-----
The certificate extracted from other.key
:
-----BEGIN PGP PUBLIC KEY BLOCK----- Comment: BC2B ADD9 3C89 D080 8E98 5A1F 8962 5382 6F49 4D3A xjMEZ3gPORYJKwYBBAHaRw8BAQdARrDdx/wzj8P9F40LJWYpT3wdy9yLvf1U069q vc6M4M7CwAsEHxYKAH0Fgmd4DzkDCwkHCRCJYlOCb0lNOkcUAAAAAAAeACBzYWx0 QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmcKRqVrQnb45wTPNzs18Eka/QeyIAKn /adZB7nhs6XBlQMVCggCmwECHgEWIQS8K63ZPInQgI6YWh+JYlOCb0lNOgAAGfAA /2uf4ByIUto39SmV67PqNWFF9gbfcgaPXeffmu+ziqb6AQDe4d/3LlP16aYS/3ky GFrMaWnfEK6CSL+L1aQSFMS8As4zBGd4DzkWCSsGAQQB2kcPAQEHQJa/v6u5aWrw ZMijZAb/JxYkzPuYUFBL1SJvK/tGIHgIwsC/BBgWCgExBYJneA85CRCJYlOCb0lN OkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmeWdRZIi9a/ 6SSJUVt26IB6S+cbdNdEgR0Fgunkfo8d8QKbAr6gBBkWCgBvBYJneA85CRAOWM6R uUQ/gEcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmfgkCW2 03E5haxoi3+LbO+3iEPa2DJY7r88GKf4hGCc4BYhBGmZIIC7Qi2FkjHWoQ5YzpG5 RD+AAAD3lwD/Qt0ex59I6pvsbAtmJj7/h+Hd3kbX53QwkE0dqd2ycJAA/iETFFzU vx4Ns6FWOGOiyTM+rHc4uHYM6FKPqiNbZEAAFiEEvCut2TyJ0ICOmFofiWJTgm9J TToAACc/AP4yEax2WepbfS3nAc3PmO7Bu+ZtV5b2P2/BRIRNmnPW6gEAsiuBVSNb ZmJkNiYvsDMMkUOoivAa3fORkiHBSBVX5gTOOARneA85EgorBgEEAZdVAQUBAQdA q9s5TnOM2UImIPICEEwWV+MQCSk4p9CRS/LbblHCzQwDAQgHwsAABBgWCgByBYJn eA85CRCJYlOCb0lNOkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBn cC5vcmf6TAclvwInoVzGCDeyXLkdwBQ4zRdcr+KAXnIWP1CwuAKbDBYhBLwrrdk8 idCAjphaH4liU4JvSU06AAAE0wD/eCxv/xXYqgju+noAa/VAyUkJsRGHkLPyK8YX 1r565G8BAOpeUkkWeAyOJLVXyZ650xJkoEXvLz3+XGftfU5QwskF =Gwpf -----END PGP PUBLIC KEY BLOCK-----
3.2 Reports its version
Want: The sopass
program reports its version when requested, and
it's in the format used my semantic versioning.
Why: This is partly a smoke test: if this doesn't work, we can't
expect anything else to work either. But it's also useful in
situations when someone else is using sopass
and we want to know
what version they have.
Common current Unix practice is to have a --version
option, so we
support that, but we also support a version
subcommand, as we have
an interface based on subcommands.
3.3 Reports a default configuration
Want: The sopass
program reports its default configuration when
requested.
Why: This is useful so that users can see what the configuration is, even if they haven't set on themselves.
3.4 Loads default configuration file
Want: The sopass
program loads its default configuration when it
exists.
Why: This is useful so that users don't need to always specify which configuration file to use.
store: /over/the/rainbow
3.5 Loads specified configuration file
Want: The sopass
program loads a configuration file requested by user.
Why: This is useful so that user can use a non-default configuration file.
sop: soppy
3.6 Initializes the password store
Want: The program initializes the password store.
Why: This is fundamental to how we want the software to be used.
3.7 Manages values
Want: The user can add and remove a value and list all values.
Why: This is fundamental for the purpose of the software.
3.8 Showing value that does not exist fails
What: Trying to show a value that does not exist in the store fails.
Why: If the command doesn't fail, the user may think the value is the empty string.
3.9 Renames values
Want: The user can rename a value.
Why: This is very handy.
3.10 Manages certificates
Want: The password store contains certificates for which to encrypt.
Why: This allows the store to be shared between devices without sharing the encryption key.