Skip to content

[BUG] PgpPublicKeyRing.GetPublicKeys() incorrectly sets isEncryptionKey on Primary Key #632

@its-vincent

Description

@its-vincent

Describe the Bug

When looping through the PgpPublicKeyRing.GetPublicKeys in a supplied Public Key File, the Primary Key is reported to possess a capability of the Subkey (Encryption)

GPG generates key pairs where the Primary is used for Authentication and Certification, and the subkey is used for Encryption.

According to https://github.com/gpg/gnupg/blob/master/doc/DETAILS which describes the output of a key listing --with-colums

Field 12 - Key capabilities
The defined capabilities are:

e
Encrypt
s
Sign
c
Certify
a
Authentication
r
Restricted encryption (subkey only use)
t
Timestamping
g
Group key
?
Unknown capability
A key may have any combination of them in any order. In addition to these letters, the primary key has uppercase versions of the letters to denote the usable capabilities of the entire key, and a potential letter ‘D’ to indicate a disabled key.

For a recipient of my encrypted file the output from :> gpg --list-keys --with-colons
is

pub:f:4096:1:AAAAAAAAAAAAAAAA:1706608025:1864460825::-:::**scESC**::::::23::0:
fpr:::::::::<redacted>:
uid:f::::1706608027::<redacted>::<redacted>::::::::::0:
sub:f:4096:1:BBBBBBBBBBBBBBBB:1706608025:1864460825:::::**e**::::::23:
fpr:::::::::<redacted>:

The Primary key AAAAAAAAAAAAAAAA has Capabilities (s)ign and (c)ertify in the Capability Field highlighted with the ** characters (Bold doesn't work in Code). It also includes the overall key capabilities (ESC) which indicates that in addition to it's own (s) and (c), one of it's subkeys will have the (e)ncryption capability.

Subkey BBBBBBBBBBBBBBBB has Capability (e)

When looping through the PgpPublicKeyRing.GetPublicKeys using the following function I was originally checking for the first key that had the Capability IsEncryptionKey reported - BUT this was returning the Primary key. I had to work around the issue by adding additional code to check for a subkey with the encryption capability, and to return that.

`
Private Function ReadPublicKeyFromFile(fileName As String) As PgpPublicKey
Using keyIn = File.OpenRead(fileName)
Console.WriteLine("")
Console.WriteLine("Seeking Encryption Key ")
Console.WriteLine("")
Dim masterPublicKey As PgpPublicKey = Nothing
Dim subPublicKey As PgpPublicKey = Nothing

        Dim pubRings = New PgpPublicKeyRingBundle(PgpUtilities.GetDecoderStream(keyIn))
        For Each kRing As PgpPublicKeyRing In pubRings.GetKeyRings()
            For Each key As PgpPublicKey In kRing.GetPublicKeys()
                Console.WriteLine("Check Public KeyId: " & key.KeyId.ToString("X16"))
                Console.WriteLine("       IsMasterKey: " & key.IsMasterKey.ToString)
                Console.WriteLine("      IsEncrpytKey: " & key.IsEncryptionKey.ToString)
                Console.WriteLine("        PubKeyAlgo: " & key.Algorithm.ToString)
                Console.WriteLine("    PubKeyStrength: " & key.BitStrength.ToString)
                Console.WriteLine("     PubKeyVersion: " & key.Version.ToString)
                Console.WriteLine("")

                'GPG default behaviour is to use a subkey for encryption/decryption - so make sure to check for a subkey with encryption capability
                If key.IsEncryptionKey Then
                    If key.IsMasterKey Then
                        masterPublicKey = key
                    Else
                        subPublicKey = key
                        Console.WriteLine("Using SubKey KeyId: " & subPublicKey.KeyId.ToString("X16"))
                        Return key
                    End If
                End If
            Next
        Next
        'GPG default behaviour is to use a subkey for encryption/decryption
        'If no subkey was found with the encryptionKey attribute set ten return the master key if that is capable of encrypting
        If masterPublicKey IsNot Nothing Then
            Console.WriteLine("Using Master KeyId: " & masterPublicKey.KeyId.ToString("X16"))
            Return masterPublicKey
        End If
    End Using
    Throw New ArgumentException("Can't find encryption key in key ring.")
End Function

`

This is the console output from the code

Seeking Encryption Key

Check Public KeyId: AAAAAAAAAAAAAAAA
       IsMasterKey: True
      **IsEncrpytKey: True**
        PubKeyAlgo: RsaGeneral
    PubKeyStrength: 4096
     PubKeyVersion: 4

Check Public KeyId: BBBBBBBBBBBBBBBB
       IsMasterKey: False
      IsEncrpytKey: True
        PubKeyAlgo: RsaGeneral
    PubKeyStrength: 4096
     PubKeyVersion: 4

Using SubKey KeyId: BBBBBBBBBBBBBBBB

As you can see from the output the Primary key (IsMasterKey: True) reports it has the encryption capability (IsEncryptKey: True) even though that capability belongs to the subkey. For the Primary key - IsEncryptKey should be False

The Subkey correctly reports that it is not the master key and it has the encryption capability

The result of this issue was that files got encrypted with the Primary key, and the recipient had to manually decrypt them as the referenced key does not have the (e)ncryption capability

To Reproduce

Steps to reproduce the behavior:
Run the code above - or the equivalent in C#

Expected Behavior

The original function return
If key.IsEncryptionKey Then Return key End If
should not be seeing the Primary key as having the (e)ncryption capability and should not have returned until it was on the subkey.

Screenshots and Logs

If applicable, add screenshots and logs to help explain your problem.

Product Deployment

Please complete the following information:

  • Deployment format: [Nuget package for Visual Studio 2022]
  • Version [Latest stable 2.6.1]

Desktop

Please complete the following information:

  • OS: [Windows 11 64-bit]
  • Browser [N/A]
  • Version [N/A]

Additional Context

Add any other context about the problem here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions