Sequoia-PGP – A new OpenPGP implementation in Rust
sequoia-pgp.org
sequoia-pgp.org
At work, we use it to store shared secrets. We can encrypt a file that can be decrypted by multiple keys.
It's a bit hard to remember all the commands so I made a webUI to manage everything.
The feature I like the most is that I can list which files you can access and which files you cannot.
The thing is, to do that, I need to pass tons of obscure and magic nonsense parameters IE:
gpg --list-only --no-default-keyring --secret-keyring /dev/null ops.gpg
and then parse the completely un-parsable output to know which keys can decrypt the file.So far so good. The problem is that; this magic trick only work with gpg 2.0.30 or lower. If you have the latest version, you can see every keys that can decrypt a file... except yours. There is no way to know if "You" can decrypt a file anymore ! (how great is this)
I now have to tell people that if they want to use the nice UI they cannot have the latest version of gpg, which is troubling.
I can't believe that in 27 years, it is still impossible to know which keys can decrypt a file. Or even just have some parsable output instead of the pile of crap the gpg tool can vomit out.
So I'm pretty excited about Sequoia :)
This reminds me so much about a tip in Effective Java .. 'Always provide an option for users to access every relevant part of your object. If not people will start to parse your toString output and you will have created an inofficial API that you have to support whether you want or not' (paraphrased from my faulty memory). That programmers make that mistake again and again is just sad. :(
At least in Java, the reflection API serves as a "break glass" barrier to make sure folks understand they're doing something they shouldn't. Someone will still do it, and they'll still blame you when their app breaks later... but I like to believe it at least scares some folks away.
The common example: programmer grabs a library to solve a problem; a few weeks later they realize it doesn't really solve the problem and requires modifications to the library to do some custom job; they make the modifications. That's normally the end of the story for several years. No point in upgrading the library unless it really solves a business problem.
That constitutes the vast majority of developers.
Of course there are other organizations that always want the latest and greatest version and are constantly upgrading. To them, I'd say sure, stick to the public methods, otherwise you're creating a big headache down the road. But I wouldn't require it of them.
Take for instance a socket class in C++, you might have a private method that deals with the low level details of the libc's socket calls. It's called at construction time and never later, and calling it would cause the current socket to be replaced by the new one (leaking the fd in the process) because it's only meant to be used at init time. Clearly it's not "use at your own risk", it's "code that calls this from the outside is fundamentally broken". Having the compiler enforce this invariant is a useful feature.
Meanwhile you can also have "use at your own risk" methods, for instance "get_raw_fd" if you want to be able to access the underlying socket. It makes it easy to break things but has legitimate use cases.
Of course you could say that you could just tag these private in a certain way and let coder discipline do the rest, but then again you could say that of pretty much all static validation (which, I suppose, makes sense if you like very dynamic languages like Python).
That's what double underscores are for:
$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo:
... def foo(self):
... print("I'm a public method.")
... def _foo(self):
... print("I'm a private method.")
... def __foo(self):
... print("I'm so private that you have to really know what you're doing to even call me.")
...
>>> foo = Foo()
>>> foo.foo()
I'm a public method.
>>> foo._foo()
I'm a private method.
>>> foo.__foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__foo'
>>> foo._Foo__foo()
I'm so private that you have to really know what you're doing to even call me.
>>>
Now in theory it's not quite what you're talking about because it does rely on coder discipline to some extent, but in practice I've never seen it become an issue (although arguably that could be because not many people know that you can access double underscore variable from outside a class).Which has been the source of a number of bugs in various GPG clients in the past (a few even on the HN frontpage for a few moments) and probably will into the future until GPG is no longer used.
tbh, GPG should just provide an RPC option so applications can securely pass data back and forth and receive proper error messages and codes. But I bet this won't happen because GNU fears that sort of things considering they won't split up the GCC compiler.
But yes I mostly parse colon separated text.
That's unfounded, there are plenty of GNU projects that have an API.
I can see that the same reasoning here would be valid; someone could write a proprietary GPG frontend.
Furthermore the GCC decision is well documented in mailing list posts, have you ever seen anyone involved in GPG development claim that they won't allow a library/frontend split for fear of someone writing a proprietary frontend?
With a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviors of your system will be depended on by somebody.
I'm in a team where all the consumers are only other internal teams and yet this still happens. I've found that it doesn't matter if you explicitly state to not rely on a particular behavior in your documentation, clients still will. It's always your fault if you break it because "it was your change that broke this client" and "it was working fine yesterday".
For example in java the iteration order of hash maps or the behavior of sorts in presence of non-reflexive comparators changed and people did depend on that. Sun was able to change it because it was not part of the API contract.
Once set up, accessing the secrets is a matter of using the pass command line tool:
pass edit some/secret
# Copy the first line of a secret; by convention
# this is meant for a password:
pass -c some/secret
# Show the whole secret file:
pass some/secret
# Combined with git:
pass git pull
# Generate a 24-character random password:
pass generate some/othersecret 24
pass git push
We use pass to maintain a set of shared secrets with a small team. The (encrypted) files are pushed to private git repository (pass supports this out of the box).>can also it encrypt .env files or some sort of it?
What are .env files? You mean the config dotfiles in your home directory? If so you'll probably have to use something like EncFS to encrypt these files. Personally I don't encrypt them but I also avoid storing cleartext passwords in them as much as possible, many unix programs support getting passwords from an application, for example in my muttrc I have:
set imap_pass = `pass mail/myemail`<quote>
I use:
git init --bare $HOME/.myconf
alias config='/usr/bin/git --git-dir=$HOME/.myconf/ --work-tree=$HOME'
config config status.showUntrackedFiles no
where my ~/.myconf directory is a git bare repository. Then any file within the home folder can be versioned with normal commands like: config status
config add .vimrc
config commit -m "Add vimrc"
config add .config/redshift.conf
config commit -m "Add redshift config"
config push
And so one…No extra tooling, no symlinks, files are tracked on a version control system, you can use different branches for different computers, you can replicate you configuration easily on new installation.
</quote>
But synchronizing shared configuration is clunky (you have to cherry pick commits between branches I guess).
I use NixOS and Nix on my MacBook, which allows you to store and version your whole system configuration. I have factored out different parts of my configuration (emacs, zsh, etc.) in different .nix files. So, I just have one file per machine where I import the relevant configurations and specify the packages that I want to have available. E.g. this is my user configuration on NixOS:
https://github.com/danieldk/nix-home/blob/master/machines/mi...
and macOS:
https://github.com/danieldk/nix-home/blob/master/machines/ma...
On the flip-side, at least we've (partially?) succeeded in taming the beast, and the end result is something moderately usable that you happily recommend to folks on HN. So that's good I suppose. :)
But, I suspect that some people will insist on a shell script. So, we'll probably go this route sooner rather than later to avoid developers trying to parse the output of sq in an ad-hoc manner.
The sq frontend uses git style subcommands to clearly separate actions from options. This is something that gpg doesn't do too well. For instance, commands (e.g., -e) look like options (e.g., -r). If no command is given, gpg tries to guess what you meant, which is perhaps good for users, but bad for programmers. And if an option isn't relevant to a command, it is often just ignored, which again, is perhaps reasonable for users, but bad for programmers.
https://gnupg.org/software/gpgme/index.html
... which internally calls gnpg using the --with-colons argument which is how you're supposed to get machine-readable output.
That said, maybe your use case isn't very accessible via the standard gpg commands. It sounds like you want to parse the ciphertext and extract the recipient keyids? You can do that with gpg --list-packets, but this is really intended for debugging, not automated consumption. That said it doesn't look too hard to parse, but who knows how stable it will be in the future.
gpg --list-only --list-packets foo | gawk '/^:pubkey\>/ { print $9 }'
The magic you are missing is `--quiet --status-fd 1`, then look for ENC_TO lines - the documentation can be found in https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;...
Based on my testing this works with gnupg 2.1 and 2.2, so I think it should help with your problem.
And then there was Ubuntu 16 that couldn't seem to import private keys at all or must have required some kind of super secret commandline option to allow it.
Honestly at this point I've been waiting for the inevitable article about how GPG has been maintained by one starving guy in his basement for the past 20 years and it turns out it's a total mess and nobody noticed because nobody was looking. Basically OpenSSL all over again.
> Of course imported keys default to the lowest (most useless) level so you have to change it
You don't need to adjust trust levels, just sign the key locally (lsign) and it'll be valid. (there is a difference between key validity and trust, check out this excellent resource https://www.linux.com/learn/pgp-web-trust-core-concepts-behi... ).
For a key to be considered valid it must be either ultimately trusted, signed by ultimately trusted key or one fully trusted valid key or 3 marginally trusted valid keys.
It seems to me there are only two properties to track and quite easy calculation to do (fizz buzz level).
It's like ACLs. On the surface they seem so easy, but in practice they're a nightmare to manage because you have to track so many dependencies to figure out a simple yes/no question.
I'd be very happy to see a simpler solution to the problem of decentralized authenticity.
Some projects do rely on Web of Trust, for example Linux kernel (https://www.kernel.org/signature.html#kernel-org-web-of-trus...) or Arch Linux (https://www.archlinux.org/master-keys/).
Perhaps you missed it in 2015:
https://www.propublica.org/article/the-worlds-email-encrypti...
The current funding page is at https://gnupg.org/donate/
This works for gpg 2.1.18
gpg -u ${MYHEX} --batch --passphrase-file /path/to/some/hard-coded-passphrase --compress-level 1 --cipher-algo AES256 --sign --encrypt -r ${MYHEX} -r ${RECP1} -r ${RECP2} -r ${RECPn} -o ${OUTPUTFILE}.gpg ${INPUTFILE}
It uses OpenPGP.js (maintained by ProtonMail) and golang's crypto/x/openpgp instead of gpg.
I kept looking for "install" or "download".
It's in "repos".
I'm glad you are making a PGP replacement, but I'm worried that this will head in the same direction. I want simpler, auditable and understandable systems for crypto, not complex beasts with hidden interactions.
Nowadays unfortunately it's a complex system of key management, account authentication, chat, file encryption, and more. I have no way to fully understand that system.
Simplicity is an under-appreciated feature.
I loved the concept of verifiable identity and that I could use it even with "plain" gpg commands without having to trust their client.
Unsurprisingly, there was no way to monetize this, so they've had to pivot to being SlackDropboxIDontKnowWhat.app
With that said, after a quick navigation around the site, I don't see any way to compensate keybase for your work. This always makes me nervous, as I hate SV Startup culture and immediately have concerns about who is funding keybase, and how they will put dinner on the table tomorrow; or what compromises keybase might make if dinner is problematic.
I would love a way to contribute to a revenue stream. I love products that are paid for. So please, develop a normal revenue stream. Less startup, more business, maybe with a flavor of non-profit if you really want to tickle my fancy.
Thank you all for the work, and please help us help you :)
1: I checked for proper RSA base blinding, a secure CPRNG, lack of Bleichenbacher Oracles and lack of invalid curve attack vectors. It uses GMP for bignum stuff so carry propagation bugs are unlikely. There are some things that aren't super nice. The CPRNG does not reseed on forks, the included AES doesn't look particularity time constant and the library doesn't use mlock() nor zeros secrets after use.
The existing Botan C API is in fact sufficient for OpenPGP already, https://github.com/riboseinc/rnp is in C++ now but was originally C and uses Botan's C API.
But Nettle is IMO quite solid and the developer is very skilled, so full steam ahead.
Is there any relation between Sequoia and the BoringPGP spec?
[0] https://blog.cryptographyengineering.com/2014/08/13/whats-ma...
Key Exchange / Key Management
This isn't really a problem with the OpenPGP protocol or an OpenPGP implementation. This is inherent to any system that tries to protect you from active adversaries. If you are willing to use centralization, then you can do something like X509 (what is what TLS uses), but there are many, many cases of CAs issuing bad certificates either by accident or maliciously, e.g., the TURKTRUST incident. You can also do something like Signal with its verified key servers. But, if you want to be decentralized, then somehow you have to get the user involved. So, in my opinion, this is more a criticism of decentralization than of OpenPGP.Now, that doesn't mean that OpenPGP tooling can't help. In fact, about 4 years ago, several initiatives began working on mechanisms to make key discovery much easier, and mostly transparent for users primarily concerned about privacy (as opposed to those whose threat model includes active adversaries, like activists, lawyers, or journalists). See, in particular, the work that pep (https://pep.foundation) and Autocrypt (https://autocrypt.org) have been doing.
Forward Secrecy
As I've written before (https://arstechnica.com/information-technology/2016/12/signa...), I don't think that forward secrecy is actually fixing a problem that most people have. Particularly in the case of OpenPGP where most people are interested in encryption of data at rest (messages stored on an IMAP server), which forward secrecy doesn't help (forward secrecy, because it throws away old key material, only makes sense for protecting data in motion).But, that doesn't mean that we haven't given some thought to the problem. In fact, at the very same gathering, Justus, who is also working on Sequoia, presented a proposal for adding forward secrecy to OpenPGP in a backwards compatible manner. You can watch the presentation (https://www.youtube.com/watch?v=an6oYjikAPY), or read an early version of the proposal (https://mailarchive.ietf.org/arch/msg/openpgp/mk8_FSS-n4DVGf...).
The short version is: OpenPGP already has mechanisms to mark encryption keys as being appropriate for data at rest or data in motion. Until now, no implementation has bothered with this distinction. We propose creating two encryption-capable subkeys, one for data at rest, and one for data in motion, and rotating the one for data in motion once a week. To ensure that a sender has a non-expired encryption key we pre-generate keys, and distribute them via the keyserver network.
The OpenPGP format and defaults suck
It is true that OpenPGP has standardized a number of ciphers that are no longer sensible, includes compression support, etc. But, OpenPGP is over 30 years old. In that time there have been many improvements. But Matt is right that these improvements come slowly. This is partly due to the lack of funding: the industry choose S/MIME over OpenPGP. (Although S/MIME is cryptographically worse than OpenPGP. See EFAIL for a critical example of why.)A major difficult to deprecating old ciphers is that OpenPGP is used for data at rest. And people rightly expect, I think, to be able to decrypt data and verify signatures from X years ago. This means we can't completely drop support for, say, CAST5: people wouldn't be able to decrypt old messages. Matt seems to ignore this bit, and focuses primarily on real-time communication (e.g., Signal), which only needs encryption for data in motion, i.e., the encryption is stripped and only archived on a trusted device (e.g., not an IMAP server).
One thing that we are consider in Sequoia is requiring the caller to provide a timestamp when verifying or decrypting a message. The timestamp can be used to choose defaults that are appropriate for when the message was allegedly created. The timestamp can be double checked with the timestamp in the signature. In this way, if someone tries to send you an email using a deprecated cipher, they'll also have to set the timestamp in the email to, say, 1997, which would hopefully be suspicious. Likewise, something like a can be shown when the message doesn't meet the current standard.
I hope that helps! If you have any other questions, you're welcome to ask here, or on irc (#sequoia on freenode) or on our mailing list (devel@sequoia-pgp.org).
:) Neal
(Yes, GPG has the MDC -- but as the fairly recent "PGP/MIME is broken" bugs showed, the handling of MDC was broken in several ways and it can only be detected after outputting all of the data. AEAD is far more cryptographically sound.)
It also will output to --output even if an MDC validation error occurs at any point, which is pretty insane. But from memory this is actually a larger architectural problem (GPG doesn't appear to buffer anything -- which means that when you tell it to write to --output it doesn't write anywhere else first).
> AEAD, as far as I understand, suffers from a similar, though less severe problem: decryption can fail at any point due to a validation error. You can be certain that anything up to the failure hasn’t been tampered with, but the question of what to do with the partial plaintext remains.
That is also a problem, though it would avoid most attacks where programs don't check GPG error codes (which is what the email vulnerabilities a few months ago were about). The only practical attack is what you've described -- some sort of known-plaintext attack where the message already contains the attack payload as a prefix. Ultimately AEAD (over MDC) is a protection against people accidentally doing the wrong thing when using crypto tools -- because users should not trust any of the decrypted text if the message didn't validate.
It's (also) from a former GPG dev, bases on the GPG source where some of the targets are: * switch to C++, allowing to reuse the legacy code, with lots of code thrown out * move to a single binary (again)
Now, C++ is naturally not your fancy new language and the rewrite-it-in-rust people may run to their pitchforks but he has a blog post entry where he argues his choice: https://neopg.io/blog/cplusplus/
TLDR: He can build upon the well established GPG, C++ is mature and it's flaws are known and can be avoided an worked around.
He even mentions the Sequoia Project in the last paragraph, and envies them a bit as they can use Rust.
(for the record, nothing against rust or sequoia, just wanted to show a related project)
Making developers avoid C style coding on C++ code is a continuous fight.
The tl;dr is that we’re actually pretty far: Sequoia is already being tested with the p≡p engine, and other projects, like Delta Chat, have begun replacing GnuPG or NetPGP with Sequoia.
> opmsg is a replacement for gpg which can encrypt/sign/verify your mails or create/verify detached signatures of local files. Even though the opmsg output looks similar, the concept is entirely different.
Support for multiple recipients is inherently a mess in that kind of model.
No effort is made to even try to solve the identity problem
PFS aside it doesn't seem to do anything you couldn't do with GPG - e.g. there's nothing to stop you verifying GPG key fingerprints by hand or using different keys to communicate with different recipients. In particular it doesn't seem to offer any improvement on the big problem that this is about - good MUA integration.
Ultimately that looks like a good project that takes those techniques as far as they can go, but also shows exactly why those techniques never made sense for an email-like environment.
You will be surprised.
https://en.wikipedia.org/wiki/List_of_languages_by_number_of...
https://www.babbel.com/en/magazine/the-10-most-spoken-langua...
On that matter, there are some languages which are clearly much more common (over twice as common) as second languages than native languages. English, French, Malay, and Swahili are obvious candidates for this: English is a common secondary language, well, everywhere; French is common second language in Africa; Swahili in East Africa, and Malay in Malaysia/Indonesia.
Although vowel centering makes it difficult to distinguish the pronunciation from that of a hypothetical "siquoye" or similar, a quick look at the list of translations indicates that most languages use equivalent vowels, unless they have an etymologically completely unrelated word.
As far as names go, that one seems to be of the easier kind.