Transfer subkey to other keyring

Jack Bates di44vq at nottheoilrig.com
Fri Sep 13 01:18:32 CEST 2013


On 07/09/13 07:10 AM, Peter Lebbing wrote:
> On 27/06/13 18:55, Jack Bates wrote:
>> except that I am using the key id of a subkey, with an exclamation
>> mark, to export just one subkey instead of all the subkeys belonging to the
>> primary key. The subkey with that key id definitely doesn't already exist in the
>> destination keyring, although the destination keyring does already contain a
>> different subkey.
>
> I believe once GnuPG has a secret key, it won't update it anymore with any
> subsequent imports. So to get the additional subkey, re-export the whole thing,
> delete the existing one on the other system and import your re-exported whole thing.
>
> I'm also wondering why you're being so explicit about it in the first place,
> with transferring little chunks at a time using the exclamation mark instead of
> the whole thing. Is there something specific you're trying to achieve?
>
>>     gpg:  secret keys unchanged: 1
>
> This message to me implies it is actually possible to change something about a
> secret key. I haven't figured out what yet.

Thank you for following up Peter, I looked again and I think it's a 
difference between public vs. secret subkeys. It will merge a new public 
subkey with existing subkeys, but it won't merge a new secret subkey 
with existing ones. Here is a first crack at a patch to merge new secret 
subkeys similar to how it already handles public ones:

    http://nottheoilrig.com/gnupg/201309120/merge.patch

I'd love to contribute a change like this to GnuPG, would a change like 
this be welcome? (considered for inclusion?) If so, can anyone help me 
review it and get it into shape? What is the right way to submit a patch 
to GnuPG?

Here's what GnuPG does before and after the patch:

    # Generate primary key
    $ gpg2 --gen-key

    pub   4096R/B575FAD1 2013-09-12
          Key fingerprint = 19C1 3488 6B2A A80C E30A  6A96 4CB2 EAFB 
B575 FAD1
    uid                  John Doe <jdoe at example.com>

    # Add subkey
    $ gpg2 --edit-key B575FAD1 addkey

    pub  4096R/B575FAD1  created: 2013-09-12  expires: never 
usage: SC
                         trust: ultimate      validity: ultimate
    sub  4096R/3BEA5E48  created: 2013-09-12  expires: 2013-09-13 
usage: S

    # Export secret subkey
    $ gpg2 --export-secret-subkeys 3BEA5E48! > 3BEA5E48

    # Add another subkey
    gpg2 --edit-key B575FAD1 addkey

    pub  4096R/B575FAD1  created: 2013-09-12  expires: never 
usage: SC
                         trust: ultimate      validity: ultimate
    sub  4096R/3BEA5E48  created: 2013-09-12  expires: 2013-09-13 
usage: S
    sub  4096R/1F01C58C  created: 2013-09-12  expires: 2013-09-13 
usage: S

    # Export other secret subkey
    $ gpg2 --export-secret-subkeys 1F01C58C! > 1F01C58C

    # Start over with an empty keyring
    $ rm -r ~/.gnupg

    # Import first subkey
    $ gpg2 --import 3BEA5E48

    gpg: key B575FAD1: secret key imported
    gpg: key B575FAD1: public key "John Doe <jdoe at example.com>" imported
    gpg: Total number processed: 1
    gpg:               imported: 1  (RSA: 1)
    gpg:       secret keys read: 1
    gpg:   secret keys imported: 1

Here's what it does before the patch:

    # Import second subkey
    $ gpg2 --import 1F01C58C

    gpg: key B575FAD1: already in secret keyring
    gpg: Total number processed: 1
    gpg:       secret keys read: 1
    gpg:  secret keys unchanged: 1

    $ gpg2 -K

    sec#  4096R/B575FAD1 2013-09-12
    uid                  John Doe <jdoe at example.com>
    ssb   4096R/3BEA5E48 2013-09-12

And here's what happens after the patch:

    # Import second subkey
    $ gpg2 --import 1F01C58C

    gpg: key B575FAD1: "John Doe <jdoe at example.com>" 1 new signature
    gpg: key B575FAD1: "John Doe <jdoe at example.com>" 1 new subkey
    gpg: key B575FAD1: "John Doe <jdoe at example.com>" 1 new signature
    gpg: key B575FAD1: "John Doe <jdoe at example.com>" 1 new subkey
    gpg: Total number processed: 1
    gpg:            new subkeys: 2
    gpg:         new signatures: 2
    gpg:       secret keys read: 1

    $ gpg2 -K

    sec#  4096R/B575FAD1 2013-09-12
    uid                  John Doe <jdoe at example.com>
    ssb   4096R/3BEA5E48 2013-09-12
    ssb   4096R/1F01C58C 2013-09-12

I looked at the code in gnupg/g10/import.c and when you import 
something, the import() procedure gets called once by 
import_keys_internal(), etc. The import_one() procedure gets called if 
import() finds a PKT_PUBLIC_KEY keyblock and import_secret_one() gets 
called if it finds a PKT_SECRET_KEY keyblock. After import_secret_one() 
imports a new secret key, it converts it to a public key with 
sec_to_pub_keyblock() and passes that to import_one().

import_one() and import_secret_one() both check if the key already 
exists in the keyring. If import_one() finds that it does, it reads the 
existing keyblock, calls merge_blocks(), and if anything changed it 
writes back the updated keyblock.

However if import_secret_one() finds that the key already exists, 
currently it just says:

1273     else if( !rc )
1274       { /* we can't merge secret keys */
1275         log_error( _("key %s: already in secret keyring\n"),
1276                    keystr_from_sk(sk));
1277         stats->secret_dups++;
1278         if (is_status_enabled ())
1279           print_import_ok (NULL, sk, 16);
1280
1281         /* TODO: if we ever do merge secret keys, make sure to handle
1282            the sec_to_pub_keyblock feature as well. */
1283       }

To make the patch, I cargo-culted a combination of code from where 
import_secret_one() imports new secret keys and where import_one() 
merges new public subkeys with existing ones. It reads the existing 
keyblock, calls merge_blocks(), and writes back the updated keyblock if 
anything changed.

I am grateful for feedback or help getting the patch into shape, if this 
is a welcome change.

Thanks!



More information about the Gnupg-users mailing list