Missing Public Key detection when validating signatures.

29 views
Skip to first unread message

Jim Klo (SRI)

unread,
May 10, 2011, 10:02:07 PM5/10/11
to python-gnupg
Great work on this module by the way!

I'm trying to figure out a way to detect if I need to fetch a public
key or not when verifying a signature.

In my test scenario, I'm generating a key... signing a message...
exporting the public and private keys... then deleting pub/private
keys... then trying to validate the signature...
I'd expect some sort of Error listing that the key could not be found,
as I have the resources to fetch the key from other sources and import
but need a way to determine which key was used to sign.

gpg = gnupg.GPG(gnupghome=self.gnupgHome, gpgbinary=self.gpgbin)

input = gpg.gen_key_input(name_email=self.privateEmail,
passphrase=self.genericPassphrase)
privateKey = self.gpg.gen_key(input)

result = gpg.sign(msg, **signPrefs)
gpg.delete_keys([privateKey.fingerprint], secret=True)
gpg.delete_keys([privateKey.fingerprint], secret=False)
verified = gpg.verify(result)

verified.data contains a message stating the keyid, but I'm not sure
if that's a consistent message across GPG implementations as I think
that's a response from the specific key.

Is there as way for me to list the keyid's or fingerprints in
signature before validating? Or at least a more reliable way to tell
if the failure is due to a missing public key?

BTW: I'm on OS X, but don't think that matters much... I'm having the
same issue on Ubuntu.

Thanks,

- Jim

The log and error of just the gpg.verify step:

2011-05-10 18:38:20,149 DEBUG gnupg MainThread verify_file:
<_io.BytesIO object at 0x101757fb0>, None
2011-05-10 18:38:20,150 DEBUG gnupg MainThread /usr/local/bin/gpg
--status-fd 2 --no-tty --homedir "~/.gnupg" --verify
2011-05-10 18:38:20,160 DEBUG gnupg MainThread data copier:
<Thread(Thread-269, initial daemon)>, <_io.BytesIO object at
0x101757fb0>, <open file '<fdopen>', mode 'wb' at 0x101771c00>
2011-05-10 18:38:20,161 DEBUG gnupg Thread-269 sending chunk
(465): '-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA1\n
\nef1b3b63adc663602c7a3c7595951b2761b34f5f6490ea1acee3df0fd97db03c
\n-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v1.4.11 (Darwin)
\nComment: GPGTools - http://gpgtools.org\n\niJwEAQECAAYFAk3J6FkACgkQdhzusp/WxY2jRwP/Y'
2011-05-10 18:38:20,162 DEBUG gnupg Thread-269 closed output, 465
bytes sent
2011-05-10 18:38:20,163 DEBUG gnupg MainThread stderr reader:
<Thread(Thread-270, initial daemon)>
2011-05-10 18:38:20,164 DEBUG gnupg MainThread stdout reader:
<Thread(Thread-271, initial daemon)>
2011-05-10 18:38:20,620 DEBUG gnupg Thread-270 gpg: Signature
made Tue May 10 18:37:29 2011 PDT using RSA key ID 9FD6C58D
2011-05-10 18:38:20,621 DEBUG gnupg Thread-271 chunk: 'gpgkeys:
key 761CEEB29FD6C58D not found on keyserver\n'
2011-05-10 18:38:20,622 DEBUG gnupg Thread-270 gpg: requesting
key 9FD6C58D from hkp server keys.gnupg.net
2011-05-10 18:38:20,622 DEBUG gnupg Thread-270 gpg: no valid
OpenPGP data found.
2011-05-10 18:38:20,623 DEBUG gnupg Thread-270 gpg: Total number
processed: 0
2011-05-10 18:38:20,624 DEBUG gnupg Thread-270 [GNUPG:]
IMPORT_RES 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Exception in thread Thread-270:
Traceback (most recent call last):
File "/usr/local/Cellar/python/2.7.1/lib/python2.7/threading.py",
line 530, in __bootstrap_inner
self.run()
File "/usr/local/Cellar/python/2.7.1/lib/python2.7/threading.py",
line 483, in run
self.__target(*self.__args, **self.__kwargs)
File "/Users/jklo/pyenv/learnreg2/lib/python2.7/site-packages/
gnupg.py", line 202, in _read_response
result.handle_status(keyword, value)
File "/Users/jklo/pyenv/learnreg2/lib/python2.7/site-packages/
gnupg.py", line 731, in handle_status
raise ValueError("Unknown status message: %r" % key)
ValueError: Unknown status message: u'IMPORT_RES'

Vinay Sajip

unread,
May 11, 2011, 4:17:51 AM5/11/11
to python-gnupg
On May 11, 3:02 am, "Jim Klo (SRI)" <jim....@sri.com> wrote:

> Is there as way for me to list the keyid's or fingerprints in
> signature before validating? Or at least a more reliable way to tell
> if the failure is due to a missing public key?

At the moment there's no easy way for you to do this. I believe GnuPG
should generate an ERRSIG and/or NO_PUBKEY message, but there's no way
your code can trap it.

In the future I will look at making the currently internal classes
used (Verify, Crypt, etc.) usable and subclassable by client code, so
that users can hook in their own logic into the process. But that's a
little way off :-(

Regards,

Vinay Sajip

Vinay Sajip

unread,
May 15, 2011, 5:34:05 PM5/15/11
to python-gnupg

On May 11, 3:02 am, "Jim Klo (SRI)" <jim....@sri.com> wrote:

> In my test scenario, I'm generating a key... signing a message...
> exporting the public and private keys... then deleting pub/private
> keys... then trying to validate the signature...
> I'd expect some sort of Error listing that the key could not be found,
> as I have the resources to fetch the key from other sources and import
> but need a way to determine which key was used to sign.

Further to my earlier post: I will change things so that in the next
release, when a verify operation fails because of a missing public
key, the object returned from the verify call will have the following
attributes:

valid: False
status: 'no public key'
key_id: '5C00B4E417959411' (for example - in practice, the actual
missing key id will be here)

I believe the current version will still return valid and key_id, but
no status attribute to indicate what went wrong.

Regards,

Vinay Sajip

Jim Klo

unread,
May 15, 2011, 6:35:21 PM5/15/11
to python...@googlegroups.com
I don't think it's returning key_id, but valid yes.

What I've done for now, when I validate, if valid is false and key_id is None, then I assume missing key, else if valid is false and key_id is not None, then I assume bad signature.  That seems to follow my test case.




def verify(self, envelope):
        '''
Verify integrity of the provided envelope.
Returns:
None if no signature block exists
True if signature & integrity check pass
False if signature & integrity check do not pass
Raises:
'''
        
        sigInfo = self._getSignatureInfo(envelope)
        
        if sigInfo != None:
            verified = self.gpg.verify(sigInfo["signature"])
            if verified.valid == True:
                verifiedHash = self._extractHashFromSignature(sigInfo["signature"])
                
                if self.get_message(envelope) == verifiedHash:
                    return True
                else:
                    return False
            elif verified.valid == False and verified.key_id == None:
                raise errors.MissingPublicKey(message=verified.data)
            else:
                return False
        return None


- Jim


Jim Klo
Senior Software Engineer
Center for Software Engineering
SRI International



Reply all
Reply to author
Forward
0 new messages