Bug 570501 - [GPG signing] Support GPG's Extended Private Key Format (needs AES/OCB/NoPadding)
Summary: [GPG signing] Support GPG's Extended Private Key Format (needs AES/OCB/NoPadd...
Status: RESOLVED FIXED
Alias: None
Product: JGit
Classification: Technology
Component: JGit (show other bugs)
Version: 5.10   Edit
Hardware: All All
: P3 enhancement (vote)
Target Milestone: 5.11   Edit
Assignee: Project Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-01-20 04:05 EST by Thomas Wolf CLA
Modified: 2021-02-22 06:42 EST (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Thomas Wolf CLA 2021-01-20 04:05:43 EST
Newer GPG version may store private keys in .gnupg/private-keys-v1.d/ in a format that JGit cannot handle. This may affect any *.key file in that directory.

This can occur is gpg-agent 2.2.22 or newer is used, or even with older gpg-agent versions if option enable-extended-key-format is given, either on the command line or in the gpg-agent.conf.[1]

The new format is described at [2]. BouncyCastle has no support yet for this.[3]

The old format is a binary serialized s-expression for the (possibly encrypted) private key.

There are several problems here:

1. The new format is a key-value format similar to e-mail headers (or to
   headers in git objects), with continuation lines. The key is just one of
   the values. One must parse this key-value format with continuation lines
   to extract the "Key:" value. Note that GPG matches keys case-insensitively,
   so the existing RawParseUtils cannot be used. But it is of course possible
   to parse this format.

2. The key value itself is a human-readable externalization of the s-expression;
   it cannot be passed as is to BouncyCastle's SExprParser. The value needs to
   be converted first to the binary serialized form. This is also possible, but
   means one has to write a fairly complete s-expression scanner that handles
   at least lists, tokens, hex values, and quoted strings. (I think that's all
   of the atoms that occur in GPG private keys.) The quoted string format does
   not correspond to any of the existing QuotedString formats in JGit. Again,
   this is doable.

3. Encrypted keys are encrypted using AES/OCB/NoPadding. This requires a custom
   PBEProtectionRemoverFactory; the default JcePBEProtectionRemoverFactory is
   hard-wired to AES/CBC/NoPadding. Also doable.

But then there's a show-stopper: OCB has been removed from the BouncyCastle bundle in Orbit![4] Probably due to concerns over the patents on OCB, which, AFAIK, are valid in the U.S. only.

* Are these concerns still valid after the move of the Eclipse Foundation to
  Europe? Or could we now include a full-fledged BC in Orbit?
* BC apparently is fine with having OCB in their code base based on the
  license grant for open-source at [5]. Why is Eclipse not happy with this?
  After, Orbit only OSGi-fies the BC bundles. We're not even trying to
  implement OCB; we just want to _use_ it.

If Eclipse cannot have a BC that includes OCB, what other possibilities would there be to solve this problem?

[1] https://www.gnupg.org/documentation/manuals/gnupg/Agent-Options.html
[2] https://github.com/gpg/gnupg/blob/master/agent/keyformat.txt
[3] https://github.com/bcgit/bc-java/issues/794
[4] https://dev.eclipse.org/ipzilla/show_bug.cgi?id=9421#c10
[5] https://www.cs.ucdavis.edu/~rogaway/ocb/license.htm
Comment 1 Matthias Sohn CLA 2021-01-20 06:51:05 EST
In order to discuss with the Eclipse lawyers if we can include OCB in BC in Orbit and use it from JGit we would need to open a CQ.

We could try to use BC as a works-with dependency and not ship it from Eclipse. The user would have to download and install it separately. For that we would need to convince upstream to add OSGi headers to the manifest. And it would be still bad from usability aspect. Don't know if that's feasible at all.
Comment 2 Thomas Wolf CLA 2021-01-20 11:29:48 EST
(In reply to Matthias Sohn from comment #1)
> In order to discuss with the Eclipse lawyers if we can include OCB in BC in
> Orbit and use it from JGit we would need to open a CQ.
> 
> We could try to use BC as a works-with dependency and not ship it from
> Eclipse. The user would have to download and install it separately. For that
> we would need to convince upstream to add OSGi headers to the manifest. And
> it would be still bad from usability aspect. Don't know if that's feasible
> at all.

That would run into problems if the Orbit pcprov bundle was installed in an Eclipse (perhaps via some other project, not EGit/JGit). There's be two copies, and both might provide the same IUs with maybe even the same versions, and both would want to register their security provider with the name "BC".

Here's another idea that also doesn't work:

* Use Eclipse's "Create plug-in from existing jar", using the upstream BC prov jar from maven. Give the new plug-in a MANIFEST.MF that exports all packages with right version, and make it an OSGi _fragment_ with the Orbit BC prov bundle as host.  If that worked, one could distribute such a bundle from outside Eclipse. Evidently this _does_ make the missing classes available, but attempting to load any of them then produces an exception because the loader sees different signatures on files in the same package. :-(

Another idea (haven't tried it yet) is perhaps to detect the "openpgp-s2k3-ocb-aes" in the key value after having extracted it (point 1 in comment 0), throw a special exception, catch it in the top-level GpgSigner.signObject(), and then handle it by shelling out to gpg. Which of course adds complications for finding the right gpg executable, spawning an external process, passing the data and getting the result. And it comes with a non-negligible performance cost. There is git config gpg.program where the user could specify which executable to use.

Or we could always shell out to gpg for signing. But firing up an external process for each signature may be a bit expensive?
Comment 3 Thomas Wolf CLA 2021-01-21 11:41:14 EST
(In reply to Thomas Wolf from comment #2)
> Another idea (haven't tried it yet) 
[... shelling out to gpg for AES-OCB encrypted secret keys]

Tried it now. That would be a possibility. A prototype works.

> Or we could always shell out to gpg for signing.

This is maybe not such a good idea. The problem is that we'd like to ask for a passphrase only if needed. If we keep the integration with the JGit CredentialsProvider, we have no good way to hook that into the external GPG process; so we might have to ask for a passphrase always, even if it wouldn't be needed because in the end the key is not encrypted.

Or we could remove all passphrase handling for GPG signing completely, and let GPG do its thing. It usually comes with a GUI prompt helper (pinentry-mac and likewise on other systems), so it would pop up its own prompt dialog when needed.
Comment 4 Thomas Wolf CLA 2021-01-22 11:24:33 EST
(In reply to Thomas Wolf from comment #3)
> (In reply to Thomas Wolf from comment #2)
> > Another idea (haven't tried it yet) 
> [... shelling out to gpg for AES-OCB encrypted secret keys]
> 
> Tried it now. That would be a possibility. A prototype works.
> 
> > Or we could always shell out to gpg for signing.
> 
> This is maybe not such a good idea.
> [...]
> Or we could remove all passphrase handling for GPG signing completely, and
> let GPG do its thing.

After more thoughts, I think we should:

* For JGit: in org.eclipse.jgit.gpg.bc, go the full mile and do steps 1, 2,
  and 3 from comment 0. It'll work in environments where a full-fledged BC
  is available.

* For EGit: implement the "external GPG" approach for signing; use BC only for
  signature verification. Don't use org.eclipse.jgit.gpg.bc but our own
  implementation. This would sign by shelling out to GPG, and leave all
  passphrase handling also to GPG. I tried that, and it works great on Mac.

  It also has the big advantage that one doesn't have to rely on internals of
  GPG, and it should in one fell swoop solve all (or most) of the bug reports
  we got about "no signing key found" or "integrate with the keystore": we'll
  get integration with the keystore and with gpg-agent "for free".
Comment 5 Thomas Wolf CLA 2021-01-24 17:58:21 EST
(In reply to Thomas Wolf from comment #4)
> After more thoughts, I think we should:
> 
> * For JGit: in org.eclipse.jgit.gpg.bc, go the full mile and do steps 1, 2,
>   and 3 from comment 0. It'll work in environments where a full-fledged BC
>   is available.

After more experiments: to make it work, we'd also have to copy two BouncyCastle helper classes (SExprParser and SXprUtils) and then adapt SExprParser. That class doesn't work with keys decrypted from AES/OCB-encrypted data. Such keys don't have a hash (they don't need one, OCB takes care of that), but the BouncyCastle SExprParser always expects one.

The alternative for JGit is to wait for a fix for upstream issue 794.[1]

[1] https://github.com/bcgit/bc-java/issues/794
Comment 6 Eclipse Genie CLA 2021-01-25 11:05:21 EST
New Gerrit change created: https://git.eclipse.org/r/c/jgit/jgit/+/175332
Comment 7 Thomas Wolf CLA 2021-01-25 11:12:08 EST
(In reply to Eclipse Genie from comment #6)
> New Gerrit change created: https://git.eclipse.org/r/c/jgit/jgit/+/175332

And the change for EGit is https://git.eclipse.org/r/c/egit/egit/+/175336 .
Comment 9 Eclipse Genie CLA 2021-02-22 03:44:29 EST
New Gerrit change created: https://git.eclipse.org/r/c/jgit/jgit/+/176595
Comment 11 Thomas Wolf CLA 2021-02-22 06:42:09 EST
Fixed in JGit so that it works if used together with the standard maven artifacts of BouncyCastle.

The Eclipse version of the Bouncy Castle bundles excludes OCB; but Egit now has the option to use an external GPG executable.