0
votes

I am working on a go project that will need to verify an OpenPGP public key, to be able to use it to verify file signatures.

I've generated a root key, and another key, which I've signed with the root key (let's call the second key signed). I've exported the public part of the signed key to an armored text file, for easy distribution:

gpg --export -a signed > signed.asc

I've written this go code which illustrates what I want to do, in the end:

package main

import (
    "flag"
    "fmt"
    "golang.org/x/crypto/openpgp"
    "os"
)

func main() {
    var keyringpath string
    var signedkeypath string
    flag.StringVar(&keyringpath, "keyring", "", "keyring")
    flag.StringVar(&signedkeypath, "signedkey", "", "signed key")
    flag.Parse()

    // read the keyring
    keyring, err := os.Open(keyringpath)
    if err != nil {
            panic(err)
    }

    el, err := openpgp.ReadKeyRing(keyring)
    if err != nil {
            panic(err)
    }

    var rootidentity *openpgp.Entity
    for _, entity := range el {
            if _, ok := entity.Identities["root"]; ok {
                    rootidentity = entity
            }
    }

    fmt.Printf("%+v\n", rootidentity)

    // read the public armored key
    signedkey, err := os.Open(signedkeypath)
    if err != nil {
            panic(err)
    }

    el, err = openpgp.ReadArmoredKeyRing(signedkey)
    if err != nil {
            panic(err)
    }

    signed := el[0]

    fmt.Printf("%+v\n", signed)

    // there is only one signature on signed, the one produced by root
    signature := signed.Identities["signed"].Signatures[0]

    err = rootidentity.PrimaryKey.VerifyKeySignature(signed.PrimaryKey, signature)
    if err != nil {
            panic(err)
    }
}

When I run it, I give keyring my public keyring (~/.gnupg/pubring.gpg) and signedkey my exported signed key (signed.asc).

In production, the idea is to also export the root public key from pubring.gpg into armored text, and embed that in the code.

The signature fails to verify with the following error:

panic: openpgp: invalid signature: hash tag doesn't match

Looking at the code of VerifyKeySignature (and especially this comment), I get the feeling that it's meant to only be used to verify signatures on subkeys, rather than other keys.

So, the question is, given two public PGP keys, one signed by the other, how do I verify that signature using the openpgp library?

1
Very likely there is not just a single signature. Usually, there is a self-signature issued to bind user IDs, and store some settings. Have a look at the exported key using gpg --list-packets signed.asc, and you will probably find multiple signatures.Jens Erat
Thanks for the comment! I checked that there is only one signature, and that it is the correct one. The self-signature is stored under SelfSignature, rather than in the Signatures slice, so I'm pretty sure I'm not mistakenly using that, either.FrontierPsycho

1 Answers

2
votes

Not sure whether I should close this question or not: I found the answer. It isn't very clear in the docs, but VerifyKeySignature is indeed probably only used for subkeys. For verifying the signatures on other users' public keys, use VerifyUserIdSignature, like so:

err = rootidentity.PrimaryKey.VerifyUserIdSignature("signed", signed.PrimaryKey, signature)
if err != nil {
        panic(err)
}