Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/crypto/openpgp: GnuPG s2k gnu-dummy offline subkey breaks packet parser #23226

Closed
paultag opened this issue Dec 23, 2017 · 2 comments
Closed
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@paultag
Copy link

paultag commented Dec 23, 2017

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go version go1.9.2 linux/amd64

Does this issue reproduce with the latest release?

Yes, including a fresh git clone of x/crypto.

What operating system and processor architecture are you using (go env)?

Nothing but the finest Debian GNU/Linux has to offer

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/paultag/go"
GORACE=""
GOROOT="/usr/lib/go-1.9"
GOTOOLDIR="/usr/lib/go-1.9/pkg/tool/linux_amd64"
GCCGO="/usr/bin/gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build475381706=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"

What did you do?

Try to parse an exported GnuPG secret cryptoblob, with a subkey that does not have private key material (such as a hardware token, or a subkey that isn't present on that machine)

What did you expect to see?

A wonderful cryptoblob that I can use to post screeds to my local mailing lists.

What did you see instead?

openpgp: unsupported feature: hash for S2K function: 0

More details

When GnuPG exports the secret chunks of a key, it will also export the subkey private material. This will come as wholly uninteresting to everyone involved, I'm sure.

However, the interesting part is when that subkey does not have a private component associated with it (e.g. a hardware token, or subkey that has a key on another machine), it is marked "offline".

From the GPG manpage:

       -K     List  the specified secret keys.  If no keys are specified, then
              all known secret keys are listed.  A # after  the  initial  tags
              sec  or ssb means that the secret key or subkey is currently not
              usable.  We also say that this key has been taken  offline  (for
              example,  a primary key can be taken offline by exported the key
              using the command --export-secret-subkeys).   A  >  after  these
              tags  indicate  that the key is stored on a smartcard.  See also
              --list-keys.

So, I made a key, and added a subkey with the golang.org/x/crypto/openpgp library, and discarded the subkey privatekey material (since gpg can't know if that's stored somewhere real or not), and imported that into the keyring.

I then added a new key and let it keep the key material.

$ gpg -K
sec   rsa4096 2017-12-23 [SC] [expires: 2018-12-23]
      AAC04B92503C27027E9FA3727456ABE798DA7527
uid           [ultimate] foo bar don't use me <example@example.com>
ssb#  rsa2048 2017-12-23 [S] [expires: 2017-12-24]
ssb   rsa2048 2017-12-23 [S] [expires: 2018-12-23]

And, now the interesting part of --list-packets:

[other packets that aren't germane to my point]

:secret sub key packet:
	version 4, algo 1, created 1513992502, expires 0
	pkey[0]: [2048 bits]
	pkey[1]: [17 bits]
	gnu-dummy S2K, algo: 0, simple checksum, hash: 0
	protect IV: 
	keyid: C3A17CA0B9DFF1E5

[other packets that aren't germane to my point]

:secret sub key packet:
	version 4, algo 1, created 1513993077, expires 0
	pkey[0]: [2048 bits]
	pkey[1]: [17 bits]
	iter+salt S2K, algo: 7, SHA1 protection, hash: 2, salt: 87557FFC8163000E
	protect count: 65011712 (255)
	protect IV:  8e db a3 af 4e 71 11 5a e5 d7 48 97 40 e3 a6 c9
	skey[2]: [v4 protected]
	keyid: 26E9A61F6F84F502

Now, on the real subkey packet, it has an S2K packet that looks legit, and the "offline" key has a "gnu-dummy" subkey on it. Neat!

Seems like it's exporting shim (and empty) secret packets for the keys it doesn't have the private half to, which would explain why it's coming up with anyone doing stuff with Yubikeys.

This is a really wackado thing for GnuPG to do, and I'm kinda creeped out that it does this rather than just outputting the public key packet. I'm sure there's a totally plausible and mature reason to do this rather than it being easier to make a custom secret packet type.

I guess the behavior of /x/crypto/openpgp ought to be ignoring the gnu-dummy packets, pull the public key material out of it, and go on about your day, but it's apparently trying to validate what seems to this one humble engineer as a batshit value.

If it didn't crash there, it would for sure blow up when it tried to read the actual private key material, so maybe this is better? Who can know.

Here's another bug report that I think is related, and I posted there, until I thought to file it here, too.

keybase/keybase-issues#1885 (comment)

@gopherbot gopherbot added this to the Unreleased milestone Dec 23, 2017
@titanous titanous changed the title x/crypto: GnuPG s2k gnu-dummy offline subkey breaks openpgp packet parser x/crypto/openpgp: GnuPG s2k gnu-dummy offline subkey breaks packet parser Dec 23, 2017
@titanous titanous added the NeedsFix The path to resolution is known, but the work has not been done. label Dec 23, 2017
@paultag
Copy link
Author

paultag commented Dec 23, 2017

The following workaround can be used to work around parts of this being a problem for very specific use-cases:

Since the problem is the subkey, let's export the secret key for the root, but not any of the subkeys. Or you can use the --export-secret-subkes flag to get those. That's done by adding a ! after the Key ID.

$ gpg --export-secret-keys KEYID!

That will export the secret key as something x/crypto/openpgp can grok again, but it will not have any subkeys on it. Depending on your use-case this may not be OK, but if you can read from this keyring for signing key use, and a pubkeyring (using boring --export), you can put it back together, to some degree.

@FiloSottile
Copy link
Contributor

Per the accepted #44226 proposal and due to lack of maintenance, the golang.org/x/crypto/openpgp package is now frozen and deprecated. No new changes will be accepted except for security fixes. The package will not be removed.

If this is a security issue, please email security@golang.org and we will assess it and provide a fix.

If you're looking for alternatives, consider the crypto/ed25519 package for simple signatures, golang.org/x/mod/sumdb/note for inline signatures, or filippo.io/age for encryption. You can read a summary of OpenPGP issues and alternatives here.

If you are required to interoperate with OpenPGP systems and need a maintained package, we suggest considering one of multiple community forks of golang.org/x/crypto/openpgp. We don't endorse any specific one.

@golang golang locked and limited conversation to collaborators Mar 29, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants