Remove unnecessary under-the-hood GPG public key cryptography



  1. Some existing programs, such as git-annex (git-annex encryption) and pass, delegate cryptography to GPG, "a complete and free implementation of the OpenPGP standard". More specifically, these programs rely on GPG key id-s as a way to interact with GPG:

    • GPG is used to create and manage GPG public/private key pairs.

    • The program is instructed to use a specific GPG public/private key pair via its key id.

    While relying on GPG is good and commendable, the specific way in which this is being done (via GPG key id-s) leads to an arguably unnecessary use of public key cryptography under the hood.

  2. Public key/asymmetric cryptography addresses some specific use cases that, broadly speaking, involve the secure transfer of information between one trusted and multiple untrusted parties. If somebody decides to use pass to store their own passwords in a public git repository, there is no untrusted party in this scheme to justify the use of asymmetric cryptography.

  3. Traditional/symmetric cryptography is potentially more secure than asymmetric cryptography. E.g., there exist quantum algorithms that break asymmetric cryptography, but there do not exist quantum computers to run them- yet. While such developments are not guaranteed, no similar (potential) weaknesses are known for symmetric cryptography.

  4. Asymmetric cryptography benefits from 2 distinct layers of security:

    (a) the private key is symmetrically encrypted with a passphrase

    (b) the (encrypted) private key is kept in a secure location

    Thus, e.g., (a) adds nothing to the security of asymmetric cryptography as long as (b) holds. It is perfectly valid (though unadvisable) to do asymmetric cryptography with an empty passphrase.

    Normal symmetric cryptography, with a single private key, only provides security of type (a). If one were to replace asymmetric with symmetric cryptography, it would be nice to keep both security levels (a) and (b). One way to do that is with 2 symmetric levels:

    • generate private random string

    • symmetrically encrypt private random string using passphrase, store privately

    • symmetrically encrypt message with private random string, store publicly

  5. The following describes what happens under the hood when a program like pass uses GPG via a GPG key id. (Assume public key cryptography is RSA and symmetric cryptography is AES.) First, GPG is used to:

    (i) create an RSA public/private key pair

    (ii) RSA public key is unencrypted, store privately (hard drive) or publicly (key server)

    (iii) AES-encrypt RSA private key with passphrase, store privately

    Next, following OpenPGP encryption, this is how encryption works:

    (iv) generate random session key

    (v) AES-encrypt message with session key, store publicly

    (vi) RSA-encrypt session key with RSA public key, store publicly

    Observe that the message is compromised by breaking either (v) or (vi).

Unnecessary Potential Vulnerability:

Suppose that in the future quantum computers are developed, and RSA is broken. (For the paranoid, suppose RSA is already broken.) While this will bring in changes to how we do things like online banking, it will also have a completely unnecessary negative impact on the security of the data created by pass/git-annex.

Just how bad this is depends on whether the RSA public key from (ii) above is stored publicly or privately. If it's stored publicly, the data is already compromised: an attacker can decrypt (vi) obtaining the session key, then decrypt (v) with the session key to get the message. If the RSA public key from (ii) is actually kept private, the security of the data is reduced to (b), the security of the medium holding the RSA public key, which is equivalent to doing asymmetric encryption without a passphrase.

This vulnerability, far fetched or not, is completely unnecessary, as it is due to the unnecessary use of public key cryptography inherent in the existing interface between programs like pass/git-annex and GPG (via GPG key id-s).


Is there a "relatively easy" way to:

  • remove the use of asymmetric encryption when GPG is used under the hood through GPG key id-s

  • continue to use GPG to manage keys as before

  • maintain the 2-level security mentioned in 4 as (a) and (b)

The complete schematic I'm after is:

(i') generate private random string

(ii') AES-encrypt private random string with passphrase, store privately

(iii') AES-encrypt message with private random string, store publicly

I understand how to do each of these steps separately, but what I'm asking about is the best way to plug such a "fix" in the way programs might use GPG via GPG key id-s. Specifically, I expect such programs to issue external calls of the form gpg --encrypt --recipient <keyid> and gpg --decrypt.

Conclusion: It seems that there is no magic bullet, and intercepting these calls by using a gpg wrapper script is the only way to go. Perhaps GPG should consider this issue in the future.

Best Answer

Symmetric Cryptography in OpenPGP

Reading your question, I think you're confusing some cryptographic terms and principles. There is no such thing like symmetric encryption using private keys. Symmetric encryption relies on a session key (also called cipher block), while public/private key (asymmetric) cryptography relies on key pairs for encryption (public key) and decryption (private key) operations.

OpenPGP generally uses a hybrid cryptography approach: the message (data, files, ...) is encrypted using symmetric encryption and a random, unique session key. The session key is encrypted through public/private key cryptography. This combines the advantages of both symmetric and public/private key cryptography: symmetric encryption is very fast, but needs a shared secret; while public/private key cryptography enables powerful key management and separate public and private keys while being horribly slow for large amounts of data.

OpenPGP's symmetric encryption does the same, but derives the session key from a passphrase instead. Depending on the configuration, some salt is added in this process (and GnuPG has reasonable defaults here). There are no public or private keys (like you would have with RSA) involved. In GnuPG, exactly this is possible by applying the --symmetric option.

Is there a way to get gpg to perform purely symmetric encryption in such a way that a passphrase is only used to protect a private key, just as in asymmetric encryption? Naturally, both passphrase and private key would be necessary for decryption.

If you're after something like "can I store the session key separately, encrypted by a passphrase instead of relying on OpenPGP's string-to-key-function, and encrypt this session key with a passphrase": no, this is not possible within the OpenPGP protocol. But you can pretty much mimic such operation by having a public/private key pair stored together in a single place, still encrypted with a passphrase. The passphrase would be used to decrypt the private key whenever it will be used.

(As an aside, I believe something similar is achieved by the MK salt stored in a LUKS header: lose that, and passphrase alone cannot decrypt the container.)

No, the salt is stored together in the message's encryption headers (or encryption container in case of LUKS). You cannot compare it with a key, and neither it is secret. The salt is used to prevent rainbow table attacks by joining it with the passphrase before calculating the session key.

Overriding Session Keys

In your updates, you refined your aims.

The complete schematic I'm after is:

(i') generate private random string

(ii') AES-encrypt private random string with passphrase, store privately

(iii') AES-encrypt message with private random string, store publicly

You cannot exactly reach this, but GnuPG knows a (non-standardized) way to extract and define session keys to use. From man gpg:


Display the session key used for one message. See --override-session-key for the counterpart of this option.

We think that Key Escrow is a Bad Thing; however the user should have the freedom to decide whether to go to prison or to reveal the content of one specific message with‐out compromising all messages ever encrypted for one secret key. DON'T USE IT UNLESS YOU ARE REALLY FORCED TO DO SO.

--override-session-key string

Don't use the public key but the session key string. The format of this string is the same as the one printed by --show-session-key. This option is normally not used but comes handy in case someone forces you to reveal the content of an encrypted message; using this option you can do this without handing out the secret key.

You can use these options to extract the session key, encrypt it separately using a passphrase, store it somewhere. To use it, you'll have to encrypt it again using the passphrase, and pass it to GnuPG. Of course, this can be achieved through some wrapper script or similar means. This is the closest you can get through means of GnuPG (this is beyond what OpenPGP specifies, the options are GnuPG-specific). These options are not really considered to be used for daily work by the developers.

I would not misuse GnuPG and OpenPGP for this kind of operation. You can easily use OpenSSL and likely also GnuTLS for this, and without using esoteric non-standardized operation modes and wrapper scripts.

Related Question