Mailing List Archive

Generating bitwise identical keyrings with GnuPG 1 + 2
Hi


I am working on a software package alike to Debian's debian-archive-keyring.
Within that, public keys are stored in a jetring-compatible format. At build
time, these keys are essentially read/imported and concatenated in a
non-kbx-keyring.

Maintainers, for sanity, are supposed to build the keyrings locally and sign
them. The signature (only) is then included in the package.

At build time, the keyrings are generated on-the-fly using jetring and the
signature checked.

Obviously, for this to work correctly, the generated keyrings must be bitwise
identical.

Now the fun part: gpg 1.4 (which is used by older distro versions... and even if
it weren't, I'd only have gpg 2.0 as an alternative which likewise doesn't
include the changes from 2.1) and 2.2 generate different data and I'm trying to
find a way to get 2.2 to generate identical data to what 1.4 would spit out.

All of this is best illustrated by an example, emulating what jetring-build
would do (for sake of simplicity, I'll use file sizes here, even if that is not
a replacement for secure hash functions, of course):

rm -f keyringfile-gpg2.2.gpg*
# It took me quite a while to figure out that this is CRUCIAL. If the target
# keyring does not exist, gpg 2.1+ will create new keybox files by default.
# If it exists, however, they will just append data to the (empty) keyring when
# importing keys, which more or less corresponds to the old keyring format.
touch keyringfile-gpg2.2.gpg
# The tail ... pipe just reads in ASCII-armored public key data.
for i in active-keys/add-{d,n,g,X}*; do gpg2 --command-fd 0
--no-auto-check-trustdb --options /dev/null --no-default-keyring --batch --yes
--keyring ./keyringfile-gpg2.2.gpg --secret-keyring ./seckeyring.gpg --import
<(tail -n +5 $i | sed -e 's/^ \+//g'); done; rm -f keyringfile-gpg2.2.gpg~
wc -c keyringfile-gpg2.2.gpg

Output:

gpg: key E1F958385BFE2B6E: public key "X2go Debian/Ubuntu Packaging
<debian@x2go.org>" imported
gpg: Total number processed: 1
gpg: imported: 1
gpg: key C509840B96F89133: public key "Oleksandr Shneyder
<oleksandr.shneyder@obviously-nice.de>" imported
gpg: Total number processed: 1
gpg: imported: 1
gpg: key F4A7678C9C6B0B2B: public key "X2go Git Administrator
<git-admin@x2go.org>" imported
gpg: Total number processed: 1
gpg: imported: 1
gpg: key 817F33270D65FBF2: public key "X2Go Packages Automatic Signing Key
<x2go-admin@x2go.org>" imported
gpg: Total number processed: 1
gpg: imported: 1
5521 keyringfile-gpg2.2.gpg


Okay, good. That's about the same thing jetring-build would have done, with
minor difference that we don't care about. For instance, jetring-build would run
each import command in a new, empty, temporary GNUPGHOME environment and also
put seckeyring.gpg in there. It doesn't matter here because we'll not be using
anything within GNUPGHOME anyway and have no secret keys.

Now, gnupg 2.1.14 introduced a new filter functionality that can be used to
sanitize keyrings and remove "bad" data. Let's use that:

gpg2 --no-options --no-default-keyring --no-auto-check-trustdb --trustdb-name
./trustdb.gpg --no-keyring --import-options import-export --import <
./keyringfile-gpg2.2.gpg > ./keyringfile-gpg2.2.gpg.san
wc -c keyringfile-gpg2.2.gpg.san

Output:

gpg: Total number processed: 4
5345 ./keyringfile-gpg2.2.gpg.san


This is the final keyring I care for, generated with gpg 2.2 and sets our
baseline. 5345 bytes in size.


Let's do the same thing with gpg 1.4:

rm -f keyringfile-gpg1.4.gpg*
# Not actually needed for 1.4, but better safe than sorry.
touch keyringfile-gpg1.4.gpg
for i in active-keys/add-{d,n,g,X}*; do gpg1 --command-fd 0
--no-auto-check-trustdb --options /dev/null --no-default-keyring --batch --yes
--keyring ./keyringfile-gpg1.4.gpg --secret-keyring ./seckeyring.gpg --import
<(tail -n +5 $i | sed -e 's/^ \+//g'); done; rm -f keyringfile-gpg1.4.gpg~
wc -c keyringfile-gpg1.4.gpg

Output (I'll drop gnupg's output since that is not interesting):

5377 keyringfile-gpg1.4.gpg


Crap, that doesn't match the previous 5521 bytes as generated by gpg 2.2.

Sanitizing the keyring is also difficult, because 1.4 naturally doesn't have the
import-export import functionality/filter, but just for the sake of it, we'll
try to emulate it:

gpg1 --no-options --no-default-keyring --no-auto-check-trustdb --trustdb-name
./trustdb.gpg --keyring ./keyringfile-gpg1.4.gpg --export | gpg1 --no-options
--no-default-keyring --no-auto-check-trustdb --trustdb-name ./trustdb.gpg
--keyring ./keyringfile-gpg1.4.gpg.san --import; rm -f keyringfile-gpg1.4.gpg.san~
wc -c keyringfile-gpg1.4.gpg.san

Output:

5377 keyringfile-gpg1.4.gpg.san


Crap2, this seems to essentially have been a no-op, and it's confirmed by checksums:

c0943200ee62d1b2cac320e53d19a8d07f3d66c7f0f3d36d95a0b96dcb982024
*keyringfile-gpg1.4.gpg
c0943200ee62d1b2cac320e53d19a8d07f3d66c7f0f3d36d95a0b96dcb982024
*keyringfile-gpg1.4.gpg.san


Also note that the keyring generated by gpg1 doesn't match neither the first
keyring generated by gpg 2.2 nor the sanitized one.


I wonder what would happen if we'd just take the sanitized version generated by
2.2 and read it through gpg 1.4...

gpg1 --no-options --no-default-keyring --no-auto-check-trustdb --trustdb-name
./trustdb.gpg --keyring ./keyringfile-gpg2.2.gpg.san --export | gpg1
--no-options --no-default-keyring --no-auto-check-trustdb --trustdb-name
./trustdb.gpg --keyring ./keyringfile-gpg1.4.from2.2san.gpg --import; rm -f
keyringfile-gpg1.4.from2.2san.gpg~
wc -c keyringfile-gpg1.4.from2.2san.gpg

Output:

5377 keyringfile-gpg1.4.from2.2san.gpg

Oh, interesting. Same size as all the other keyring files generated through gpg
1.4. Does the checksum match as well?

c0943200ee62d1b2cac320e53d19a8d07f3d66c7f0f3d36d95a0b96dcb982024
*keyringfile-gpg1.4.gpg
c0943200ee62d1b2cac320e53d19a8d07f3d66c7f0f3d36d95a0b96dcb982024
*keyringfile-gpg1.4.from2.2san.gpg


So, to summarize, if I process a keyring file generated by gpg 2.2 with a 1.4
binary, i.e., read-in the former, export all keys and import it again, gpg 1.4
generates exactly the same file as it would when importing the keys directly.

I'm looking for a way to generate exactly the same file through gpg 2.2, though.
Is that possible? Not having to force maintainers to install gpg 1.4 would be great.



Mihai
Re: Generating bitwise identical keyrings with GnuPG 1 + 2 [ In reply to ]
On 2019-08-18 at 08:24 +0200, Mihai Moldovan wrote:
> So, to summarize, if I process a keyring file generated by gpg 2.2
> with a 1.4 binary, i.e., read-in the former, export all keys and
> import it again, gpg 1.4 generates exactly the same file as it would
> when importing the keys directly.

I'm baffled by this.

Could you run gpg2 --list-packets on both keyrings and compare their
outputs?

That should hint which packets are being included by 1.4 that are not by
2.2

Best regards


_______________________________________________
Gnupg-users mailing list
Gnupg-users@gnupg.org
http://lists.gnupg.org/mailman/listinfo/gnupg-users
Re: Generating bitwise identical keyrings with GnuPG 1 + 2 [ In reply to ]
* On 9/6/19 12:33 AM, Ángel wrote:
> I'm baffled by this.
>
> Could you run gpg2 --list-packets on both keyrings and compare their
> outputs?
>
> That should hint which packets are being included by 1.4 that are not by
> 2.2

Hmm, interesting indeed.

The output is *almost* the same.

A diff looks like that (truncated, but you'll get the general idea):


--- keyringdump.gpg2 2019-09-13 20:50:26.839951269 +0200
+++ keyringdump.gpg1 2019-09-13 20:50:44.186005825 +0200
@@ -19,13 +19,15 @@
hashed subpkt 23 len 1 (keyserver preferences: 80)
subpkt 16 len 8 (issuer key ID E1F958385BFE2B6E)
data: [2046 bits]
-# off=635 ctb=b9 tag=14 hlen=3 plen=269
+# off=635 ctb=b0 tag=12 hlen=2 plen=2
+:trust packet: sig flag=00 sigcache=03
+# off=639 ctb=b9 tag=14 hlen=3 plen=269
:public sub key packet:
version 4, algo 1, created 1299793310, expires 0
pkey[0]: [2048 bits]
pkey[1]: [17 bits]
keyid: 71F21F68F489CDCF
-# off=907 ctb=89 tag=2 hlen=3 plen=287
+# off=911 ctb=89 tag=2 hlen=3 plen=287
:signature packet: algo 1, keyid E1F958385BFE2B6E
version 4, created 1299793310, md5len 0, sigclass 0x18
digest algo 2, begin of digest 77 f5
@@ -33,7 +35,9 @@
hashed subpkt 27 len 1 (key flags: 0C)
subpkt 16 len 8 (issuer key ID E1F958385BFE2B6E)
data: [2044 bits]
-# off=1197 ctb=99 tag=6 hlen=3 plen=418
+# off=1201 ctb=b0 tag=12 hlen=2 plen=2
+:trust packet: sig flag=00 sigcache=03
+# off=1205 ctb=99 tag=6 hlen=3 plen=418
:public key packet:
version 4, algo 17, created 1234173545, expires 0
pkey[0]: [1024 bits]


It looks like the gpg1 output has additional "trust" packets. Are that owner
trust values? I wonder why gpg2 doesn't generate these packets?

According to RFC 4880 these are really owner trust values that SHOULD NOT be
exported to files that are supposed to be handed to other users, but GPG can't
determine whether such a keyring file will be used locally or not.

Either way, my best guess is that GPG 2.2+ drops the trust packets because the
trust is not explicitly set (i.e., default value) - as an optimization. Can I
instruct gpg2 to not do that? --export-ownertrust doesn't seem appropriate and
then there's also the special concept of a trustdb, so I don't quite understand
why trust packets would be exported to keyrings in the first place?



Mihai

_______________________________________________
Gnupg-users mailing list
Gnupg-users@gnupg.org
http://lists.gnupg.org/mailman/listinfo/gnupg-users
Re: Generating bitwise identical keyrings with GnuPG 1 + 2 [ In reply to ]
On Fri, 13 Sep 2019 21:28, ionic@ionic.de said:

> Either way, my best guess is that GPG 2.2+ drops the trust packets
> because the trust is not explicitly set (i.e., default value) - as an

The trust packets are for internal use of gpg and are never exported.
These packets are one of the reasons why we stated for decades that the
interface is "gpg --export" and that the files in ~/.gnupg are internal
to gnupg.

gnupg/doc/DETAILS tells this about the trust packets:

* Format of the OpenPGP TRUST packet

According to RFC4880 (5.10), the trust packet (aka ring trust) is
only used within keyrings and contains data that records the user's
specifications of which key holds trusted introducers. The RFC also
states that the format of this packet is _implementation defined_ and
SHOULD NOT be emitted to output streams or should be ignored on
import. GnuPG uses this packet in several additional ways:

- 1 octet :: Trust-Value (only used by Subtype SIG)
- 1 octet :: Signature-Cache (only used by Subtype SIG; value must
be less than 128)
- 3 octets :: Fixed value: "gpg"
- 1 octet :: Subtype
- 0 :: Signature cache (SIG)
- 1 :: Key source on the primary key (KEY)
- 2 :: Key source on a user id (UID)
- 1 octet :: Key Source; i.e. the origin of the key:
- 0 :: Unknown source.
- 1 :: Public keyserver.
- 2 :: Preferred keyserver.
- 3 :: OpenPGP DANE.
- 4 :: Web Key Directory.
- 5 :: Import from a trusted URL.
- 6 :: Import from a trusted file.
- 7 :: Self generated.
- 4 octets :: Time of last update. This is a a four-octet scalar
with the seconds since Epoch.
- 1 octet :: Scalar with the length of the following field.
- N octets :: String with the URL of the source. This may be a
zero-length string.

If the packets contains only two octets a Subtype of 0 is assumed;
this is the only format recognized by GnuPG versions < 2.1.18.
Trust-Value and Signature-Cache must be zero for all subtypes other
than SIG.

If you use "--export-options backup" these trust packets are exported
anyway so that they can be imported with "--import-otions restore".

Salam-Shalom,

Werner

--
Die Gedanken sind frei. Ausnahmen regelt ein Bundesgesetz.
Re: Generating bitwise identical keyrings with GnuPG 1 + 2 [ In reply to ]
* On 9/15/19 3:56 PM, Werner Koch wrote:
> The trust packets are for internal use of gpg and are never exported.

But... that's the whole point. gpg 1.4 seems to export them, while gpg 2.x does not.


> These packets are one of the reasons why we stated for decades that the
> interface is "gpg --export" and that the files in ~/.gnupg are internal to
> gnupg.

I understand that this might be considered implementation defined, but getting
unreproducible output for a specific operation is a bit weird. I don't know if
the format GnuPG generates with the --export command is considered stable, though.


> The RFC also states that the format of this packet is _implementation
> defined_ and SHOULD NOT be emitted to output streams or should be ignored on
> import.

So it looks like GnuPG 1.x didn't adhere to this recommendation, while GnuPG 2.x
does.


> If you use "--export-options backup" these trust packets are exported anyway
> so that they can be imported with "--import-otions restore".

That might be a valid workaround for gpg >= 2.1.18 to make it spit out trust
packets.

I basically need to find a way to
- either make gpg 1.4 NOT output trust packets
- or make gpg 2.x generate them.

Initially, I thought about using --export-options export-minimal, because it's
supported by even ancient 1.4 versions and could have been the better solution
here, given that a package archive keyring doesn't need to ship additional
signatures (other than the most recent selfsigs). This said, I tried that option
and it does not seem to force gpg 1.4 to drop trust packets, so that's sadly not
a viable option. Haven't any other option that would stop gpg 1.4 from
generating them either.


Using --export-options backup, which seems to be supported by at least gpg
2.1.18+, made gpg 2.2 write out trust packets alright, but... more than gpg 1.4
generates. :(


1.4 seems to generate trust packets *only* after signatures, while 2.2, when
used with the --export-options backup option, generates trust packets after key,
user and signature packets.

Excerpt:

--- keyringdump.gpg1 2019-09-16 11:58:56.506758876 +0200
+++ keyringdump.gpg2 2019-09-16 12:02:14.967564877 +0200
@@ -4,9 +4,13 @@
pkey[0]: [2048 bits]
pkey[1]: [17 bits]
keyid: E1F958385BFE2B6E
-# off=272 ctb=b4 tag=13 hlen=2 plen=46
+# off=272 ctb=b0 tag=12 hlen=2 plen=12
+:trust packet: key upd=0 src=0
+# off=286 ctb=b4 tag=13 hlen=2 plen=46
:user ID packet: "X2go Debian/Ubuntu Packaging <debian@x2go.org>"
-# off=320 ctb=89 tag=2 hlen=3 plen=312
+# off=334 ctb=b0 tag=12 hlen=2 plen=12
+:trust packet: uid upd=0 src=0
+# off=348 ctb=89 tag=2 hlen=3 plen=312
:signature packet: algo 1, keyid E1F958385BFE2B6E
version 4, created 1299793310, md5len 0, sigclass 0x13
digest algo 2, begin of digest a8 73
@@ -19,15 +23,15 @@
hashed subpkt 23 len 1 (keyserver preferences: 80)
subpkt 16 len 8 (issuer key ID E1F958385BFE2B6E)
data: [2046 bits]
-# off=635 ctb=b0 tag=12 hlen=2 plen=2
+# off=663 ctb=b0 tag=12 hlen=2 plen=6
:trust packet: sig flag=00 sigcache=03
-# off=639 ctb=b9 tag=14 hlen=3 plen=269
+# off=671 ctb=b9 tag=14 hlen=3 plen=269
:public sub key packet:
version 4, algo 1, created 1299793310, expires 0
pkey[0]: [2048 bits]
pkey[1]: [17 bits]
keyid: 71F21F68F489CDCF
-# off=911 ctb=89 tag=2 hlen=3 plen=287
+# off=943 ctb=89 tag=2 hlen=3 plen=287
:signature packet: algo 1, keyid E1F958385BFE2B6E
version 4, created 1299793310, md5len 0, sigclass 0x18
digest algo 2, begin of digest 77 f5


Looks like I'm stuck.

The source code is also enlightening - build_packet_and_meta (which is used with
backup) creates trust packets for KEY, SIG, and USER packets, while keyring.c in
1.4 ignores/skips over any trust packets but those coming right after a SIG packet.



Mihai
Re: Generating bitwise identical keyrings with GnuPG 1 + 2 [ In reply to ]
On Mon, 16 Sep 2019 15:41, ionic@ionic.de said:
> * On 9/15/19 3:56 PM, Werner Koch wrote:
>> The trust packets are for internal use of gpg and are never exported.
>
> But... that's the whole point. gpg 1.4 seems to export them, while gpg
> 2.x does not.

I just checked the code and I can't see how they get exported. In the
loop over the packets you find:

/* Make sure that ring_trust packets never get exported. */
if (node->pkt->pkttype == PKT_RING_TRUST)
continue;

which should skip them while exporting. Can you please provide a test
keyring and tell us the exact gpg 1.4 version you are using?

> unreproducible output for a specific operation is a bit weird. I don't know if
> the format GnuPG generates with the --export command is considered
> stable, though.

Yes it is the interchange format as specified by RFC-4880.

> I basically need to find a way to
> - either make gpg 1.4 NOT output trust packets

The solution is simple; Do not use gpg 1.4 except for decrypting legacy
data which either does not use MDC or is encrypted with a v3 key.
There is no other use case for gpg 1.4.

> 1.4 seems to generate trust packets *only* after signatures, while 2.2, when
> used with the --export-options backup option, generates trust packets after key,

They are implementation defined and thus do not go into the key
interchange format (transferable public/secret key). The backup/restore
options are an exception for, well, backup and restore of *GnuPG*'s
internal key data storage.


Salam-Shalom,

Werner


--
Die Gedanken sind frei. Ausnahmen regelt ein Bundesgesetz.