SecurityInnovation / PGPy

Pretty Good Privacy for Python
BSD 3-Clause "New" or "Revised" License
317 stars 99 forks source link

pgpy.errors.PGPError: 109 is not a valid ECPointFormat #320

Open vollkorn1982 opened 4 years ago

vollkorn1982 commented 4 years ago

I am learning how to use PGPy and for that purpose I wrote a small program to import all public keys from my keyring.

import pgpy

pgpy.PGPKeyring('./mypubkeys.asc')

This fails with the following error message:

pgpy.errors.PGPError: 109 is not a valid ECPointFormat

Importing single test keys works fine.

I assume this is a problem with a specific key. Is there an easy way to find out which key would cause this?

Full stack trace:

Traceback (most recent call last):
  File "/home/jan/Projekte/pgpy/venv/lib/python3.6/site-packages/pgpy/types.py", line 556, in __call__
    obj.parse(packet)
  File "/home/jan/Projekte/pgpy/venv/lib/python3.6/site-packages/pgpy/packet/packets.py", line 854, in parse
    self.keymaterial.parse(packet[:pend])
  File "/home/jan/Projekte/pgpy/venv/lib/python3.6/site-packages/pgpy/packet/fields.py", line 639, in parse
    self.p = ECPoint(packet)
  File "/home/jan/Projekte/pgpy/venv/lib/python3.6/site-packages/pgpy/packet/fields.py", line 488, in __init__
    self.format = ECPointFormat(xy[0])
  File "/usr/lib/python3.6/enum.py", line 293, in __call__
    return cls.__new__(cls, value)
  File "/usr/lib/python3.6/enum.py", line 535, in __new__
    return cls._missing_(value)
  File "/usr/lib/python3.6/enum.py", line 548, in _missing_
    raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: 109 is not a valid ECPointFormat

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "./minimal_test.py", line 5, in <module>
    pgpy.PGPKeyring('./mypubkeys.asc')
  File "/home/jan/Projekte/pgpy/venv/lib/python3.6/site-packages/pgpy/pgp.py", line 2406, in __init__
    self.load(*args)
  File "/home/jan/Projekte/pgpy/venv/lib/python3.6/site-packages/pgpy/pgp.py", line 2525, in load
    _key, keys = PGPKey.from_file(key)
  File "/home/jan/Projekte/pgpy/venv/lib/python3.6/site-packages/pgpy/types.py", line 189, in from_file
    po = obj.parse(data)
  File "/home/jan/Projekte/pgpy/venv/lib/python3.6/site-packages/pgpy/pgp.py", line 2363, in parse
    [ operator.ior(pgpobj, PGPSignature() | sig) for sig in group if not isinstance(sig, Opaque) ]
  File "/home/jan/Projekte/pgpy/venv/lib/python3.6/site-packages/pgpy/pgp.py", line 2363, in <listcomp>
    [ operator.ior(pgpobj, PGPSignature() | sig) for sig in group if not isinstance(sig, Opaque) ]
  File "/home/jan/Projekte/pgpy/venv/lib/python3.6/site-packages/pgpy/pgp.py", line 2333, in <lambda>
    _getpkt = lambda d: (Packet(d) if d else None)  # flake8: noqa
  File "/home/jan/Projekte/pgpy/venv/lib/python3.6/site-packages/pgpy/types.py", line 559, in __call__
    six.raise_from(PGPError(str(ex)), ex)
  File "<string>", line 3, in raise_from
pgpy.errors.PGPError: 109 is not a valid ECPointFormat
vollkorn1982 commented 4 years ago

I wrote a little script to export all public keys of my keyring separately from gnupg and import them one by one with pgpy. I found the key which gave me the above error and it has an Elgamal and a DSA subkey, as can be seen with gnupg:

[...]
sub   rsa2048 2009-09-01 [S]
sub   rsa2048 2009-09-01 [E]
sub   rsa2048 2009-09-01 [A]
sub   elg4096 2009-09-01 [E]
sub   dsa3072 2009-09-01 [S]
sub   ed25519 2014-04-06 [S]

So I guess the problem I am having is, that this error message is not very helpful. I would like, if possible, get the following information:

Unfortunately, I do not understand the pgp key format or import code sufficiently enough to create such a pull request or determine if this is even possible.

vollkorn1982 commented 4 years ago

I had a closer look at the offending key. It is the ed25519 key, which causes the above error. The packet looks like this:

bytearray(b'\t+\x06\x01\x04\x01\xdaG\x0f\x01\x00\xffm\x18\xb1*\xb01\xae\xc9$\x7f\xae@\xa7\x1c5\xe0\xac\x11j\xe4\xd1Po#\xd7\x19T\xc8x\xbfh\xc6\x88u\x04\x10\x16\x08\x00\x1d\x16!\x04%U+\x89\xd3:\xa6\xf6\x820\x1e\x82\xae:\xe4VB%Q\x9a\x05\x02Zye\x1f\x00\n\t\x10\xae:\xe4VB%Q\x9a\x171\x01\x00\xbc\x80\xe6\x8b\xa0\xf3\xf8\xb4\x1f\x10\xb2\xbdK\x9a\x0c\xbdV\x9d\xdd|X#vj\xe7\x02\x8c\xf3\xe9X\xa6\x1d\x00\xfe;F\xab\x87k\xc7E\xb0O\xfa\xea|A\x81/\xbc\xb2Q"\xcd\xd4\x92O\xd7)\xe3A\xbb\x07\xecQ\x01\x88u\x04\x10\x16\x08\x00\x1d\x16!\x04%U+\x89\xd3:\xa6\xf6\x820\x1e\x82\xae:\xe4VB%Q\x9a\x05\x02Zye\x1f\x00\n\t\x10\xae:\xe4VB%Q\x9a\x7f\x9e\x00\xfd\x10\xed\xe6\x7fl\x97\xea?\xd7.\x86=\xd8U/\x11w3\xa9w\xfc\xf5\xed\xa4\xefIGJ\xb8\x0e\xe7\x1d\x00\xfd\x13\x7f\x1b\xa02h\xfa7\xe2\xbb/Q\x02\x1d\xc4\xd4]=\xc90\xad \x17\x86\x83\xd1\x1b\xa0\xb9~h\x02\x88u\x04\x10\x16\n\x00\x1d\x16!\x04MQ\x90\x00\xd6)\xccTW\x00\t\xb7\x07\x03\xc0\\\x83\xa2 [\x05\x02Z~\xe4\x18\x00\n\t\x10\x07\x03\xc0\\\x83\xa2 [\xa3\xd1\x01\x00\xf7*\xde\x01\x04\xe0/0\xca\xfd\xeet\xbd\xafOl\xb2\t\xb3\xde\x84\xc7\xf03xAt\xa9\xf5\xa71)\x00\xffM\x88\x8ej?\xa0:wg\xff\xca\xb7\xa1\x8c\xa1\xb5\xfa\x8cM\x96\x0c\xf2\x9d\xcd\x05\x17A\xcb\xfd\xc7\x19\x03\x88u\x04\x10\x16\n\x00\x1d\x16!\x04MQ\x90\x00\xd6)\xccTW\x00\t\xb7\x07\x03\xc0\\\x83\xa2 [\x05\x02Z~\xe4\x18\x00\n\t\x10\x07\x03\xc0\\\x83\xa2 [\xab%\x00\xffJ8\nmY\xc7\x9av=;\'\x81\x1e\x05\x1f\xd5J\x8c\xe7j\x94\xbe\xa8\xf2\xd1i\xc78$h\xb1\x03\x00\xfd\x10\xaa\x97y\x8e\xaa\xf5\x1b\r\x05\xbe\xca\x82\\R\xea\xe8\x97\xcc\x03\xed\xcbw\xc4\xefg\xd9#\xac\xccG\x06\x89\x02\x7f\x04\x18\x01\x08\x00\t\x05\x02S@\xb5\xa8\x02\x1b\x02\x00j\t\x10\xa9\xc8l\x8d\xd3\xae\x8d:_ \x04\x19\x16\x08\x00\x06\x05\x02S@\xb5\xa8\x00\n\t\x10\x9b\x8e+U\r\xac\xd3_\xb5Y\x01\x00Ro\xac88\xd60\xfa\xb8\x9f\xfa\xe6b\xa1-\xd4\xd8q\x85\xf3lY\x9dS@\xa8\x94\x85\r\x11\xb6\xaf\x01\x00\xf5\xfaO\x08\xdbf\x01\x9c7Ow#tB\x04\xd0\xaf\xd5\xaa\xdep\x88\x00ZV@\x8dZ\x8a\x0b\xb9\x01\xde~\x0f\xfd\x10uJ\x13\x84\xd1m\x14\xb6\xab\xe8\x1d|\xdf\xbfit\x17!\x17:\x0c\x9a\xb7\x1c3\xd0n\xbe\x94\x8b\x03\xe9Gg\x8aW\x8e\r\x19y\xf2\xa2\\\x9e\xa4\xaa\x1d\x8ev\xc9\xd0@\xbbg/H\x8c\xeev\x1dk\xa2\xe5h\xd3-\x9f\xfe\xe9l/\x1e\x82\xfd|\x1e\xcf\xc7\x1a\xf1\xc8;\x04Q\xea\xd7\xb3\xc0Y\x0e]\xa3SEC~\xc27\xc0\x88D\xabA\xde\xee\xa5:\xc4\x0b>\x9b\x02u\xe6r\xd9\x8b\xf0)d\xe3[\x05\xa4B`\xa5\x88O^\xbdWT-_\xae\xeb\xf5A\x7fQ\xa07\xf9\xcf\xa4\x9e\xbf%\xf043>\x87e\xeb\x19\x16^\xad\xcc\xe5\x16\xb6\xb1\x93\x1bw_5\x7f\xedz\xf9\xdd\x18RX\x99?\xe7\x7f@\x80\xfbD\xe96\xbeG\xd3\x1d\x8d\x1a.\xd3\xb4O\xf6; B@\x0e\x91<\xddZ7?\xc0?\xb3m\x10\x04b\xb9s\xb4\x83\xf8\x96\xc4\x94\xb7\x02 \x18\xb8L\x8e\x94\xa0\xc16\xbb\xc3\x9f\x1eQ\x08\x9e\xd0\xe4AN$F\xec\xf4\x89\x7fS\xf5\xb4\x02\x8e\xdb-\xc3\xf6\x81\x02F\xd4\x1a\x0e[q\xc2@\x0f\xa9\x11\xf5w?\x16\xc2\x1a\x00Q.\x1dV\x8b0B\xcf\'\x96\x15u\xf0\x01Y\xb3\xbc\x14N\xf5))I5j\x10\xednE\xc7G\xb4[V\xecS)/8MA\x84E\xdd\xf8\xfai<\x886\x93{t\xb1wE1\xf7\xa6\x98\xaa;\x8ch#\xa7Y\x80\x01\x04A\x1f\x1d\xb7b[\xa5\xd5!\xb0\x95\x8e\xa8\xd4\xa6\xd5_\x9f\x82?\x02_\xb5$\xd3s\xa2\xa2\xb1\'4\xbbG\xd14,\xf3o\x83\xc3?t0+\xa2z\x8d\xe3d\x92W\xf7\x13\x1b5\xdc\x19*\xd0\xab*\xa8\x91wv\x15\x9c\x9f\xd7\xc7,\x8c9s\xc9hz\xd9\xae\x0bxB4\xabK\x90\x01\xc5&\xdbu\x10\xfc\'\x986\x90\x07\x1a\xf3\x9c\x96bQ\x8d\xa8@;\xc1\xc9c)A\t\xdc\x87UJ4\xddx\xf6\xd0\xc4\xfa\x97kg\xc1\x88\xb1\x0f@\xfd:>\xdd\x1cN\x97\xe0\xdb@\r\x10\xf7\xb3W\xe3k\x8e\x8bY\x1e3NQ\xd2\xef\x88u\x04\x10\x16\x08\x00\x1d\x16!\x04]^\xb5j\xa44\xce&j\xee\x8cM\xdf\x07\xd8\x97eH0\xdf\x05\x02\\_P\xaa\x00\n\t\x10\xdf\x07\xd8\x97eH0\xdf\x82\xb1\x00\xffd;r\x8c;NUa\xd2J\x19\x8bW\x82\x03\xc5\x8c\xe5\x19)zy\x96\xef\r\xa7\x9c\xf2\xf60U\x11\x01\x00\xa3\x8aD\xe6\xd4\x87\x1b\x9e.\xbb\xe9s\x06iG\x9aUJ\xd5\xbey\xc0U"/e\x97R.\xd4\x00\x0f\x88u\x04\x10\x16\x08\x00\x1d\x16!\x04]^\xb5j\xa44\xce&j\xee\x8cM\xdf\x07\xd8\x97eH0\xdf\x05\x02\\_P\xb7\x00\n\t\x10\xdf\x07\xd8\x97eH0\xdf%\xf9\x00\xfe2\xfa\x88\xbfS\xf8\xf5=\x1e\xbd\x93\t5\xe7t\x11\x8e\xee1\x80\x86\xd4a\xb5\\Q\x89\xa5\x06\xb1t\x98\x01\x00\x81@\x94g\xc8\xd5\x06\xde\xb8\xb4\xe3\x15h\x02\xc8\xb2\xbe\x18#\xd5`\xbd#\xc8\x80\x83\xe0j\x04\xfc\x88\x0c\x88u\x04\x10\x16\x08\x00\x1d\x16!\x04]^\xb5j\xa44\xce&j\xee\x8cM\xdf\x07\xd8\x97eH0\xdf\x05\x02\\_P\xb8\x00\n\t\x10\xdf\x07\xd8\x97eH0\xdfJ\xba\x00\xfe*\xf2\xb3\x17\xee\xf1\xfa\x80@\xac\x14\x134i\x97 M\\|\xf2}\xb8\xdbr\\4.?\xa1\x98?\x17\x01\x00\xfe\xa5/\xd9\xeby\xca\x90\x86P\x98\x02\x9b\xfb\xed\x95\xea\x9d\x0e\xb12\x8d\xba\xf6V\xed \xbd\xe6\x03d\x0e\x88u\x04\x12\x16\n\x00\x1d\x16!\x04\x8d\xeb\xc6\x12Hz\x04\x81\xe2\x1e\xcbW\xc9\x02[\xa2\xe8f7\x17\x05\x02\\Y\xfcp\x00\n\t\x10\xc9\x02[\xa2\xe8f7\x17\x12\x88\x01\x00\xd5J\xca\x9b\xf1\xb3E7\x0e)\xed\x93BWj=\xe4\xd3\xd4v\xed\xa9h@.X\x0b\xcdp\xf8p\r\x00\xfd\x14\x7fl\xe5R\xce;\xae\xeb\r\xa8\x0b\xd0Q\xb1\x01U\xafiZ\xbd\xd26\xd7\x0e\xcet\x87\x9b\xb3$\x08\x88u\x04\x12\x16\n\x00\x1d\x16!\x04\x8d\xeb\xc6\x12Hz\x04\x81\xe2\x1e\xcbW\xc9\x02[\xa2\xe8f7\x17\x05\x02\\Y\xfc\x81\x00\n\t\x10\xc9\x02[\xa2\xe8f7\x17L0\x00\xf9\x01\x82\xedm\xed\xaeY\x8b"\x98\x9b\xae\x80>\xf4\xd4G\xcfo\xa4\xe0\xb0&\x9b\xceGZf#MI7\x01\x00\xa3"\x80\xf6\\]J\xf3\x98 \xb6[\xed\xed\x1a\xa9\xd0\xa6\xadBK\xa56\x8a<\xaa-\xad\xcb\x8b\x80\x0b\x88\xb0\x04\x12\x16\n\x00X\x16!\x04\x1fZ\xa1\x02\xd2d\xe5D\xd0\x9c\xe2{,\xc5l \x1eA\'y\x05\x02\\]\x95\x00:\x1ahttp://www.db3ydh.de/~daniel/gpg-cert-policy-20190205.txt\x00\n\t\x10,\xc5l \x1eA\'y\xcb\xee\x00\xfd\x10\xf1\xf1\xe1\x11+\xe4=\xa5\x1d^\x97\xb3}\xaf\x017\xf1\xa7X]p\x18\xc9u\x0c\xd1\xb1\xa6\xdegj\x00\xfc\x0b\x8aA\x8c\x1eA\x7fW\x05%!\xb1r]yN\x02{_\xb2F"\x19\x02u\xf4\xdfy\x96*\xf4\x07\x88\xb0\x04\x12\x16\n\x00X\x16!\x04\x1fZ\xa1\x02\xd2d\xe5D\xd0\x9c\xe2{,\xc5l \x1eA\'y\x05\x02\\]\x95\x0b:\x1ahttp://www.db3ydh.de/~daniel/gpg-cert-policy-20190205.txt\x00\n\t\x10,\xc5l \x1eA\'y\x96\xdc\x01\x00\xc6\n{0PW/79\xb2\xd9a\xcf\x86\x15\xf8V\xd3,vl\xaa\x9c\xa4\x8b[\xe3\x85\x8aw5A\x00\xfe)\xb2\x8f\xfc\xf7^=\x13\xf5\x0e\xb8=/\x88\xc0\x9b\x88\x7f\xfeM\x9b\xca\xd1z\x11\x03/(\xa5b3\x06\x88\xb0\x04\x12\x16\n\x00X\x16!\x04\x1fZ\xa1\x02\xd2d\xe5D\xd0\x9c\xe2{,\xc5l \x1eA\'y\x05\x02\\]\x95\x0c:\x1ahttp://www.db3ydh.de/~daniel/gpg-cert-policy-20190205.txt\x00\n\t\x10,\xc5l \x1eA\'y\xc1\x83\x00\xffu.\x98lg\xd0\xc7\xc0\\\x18\x12\xf2\xe1\x0e\xb1\xac\xaf{x\x87\xcb\x81\x02;d\xa5Rl\x113\xf6\xcf\x01\x00\x94\xd0\xefA&\xfb\x12x"A\xb1\'\x85VnF\xed\xd4\xa2\xb1\xb9\xe9\x1er\xa2\xf5m\x1f\x8f\x14h\n')

And the above exception throws at the following part in the beginning:

bytearray(b'm\x18\xb1*\xb01\xae\xc9$\x7f\xae@\xa7\x1c5\xe0\xac\x11j\xe4\xd1Po#\xd7\x19T\xc8x\xbfh\xc6')

According to https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07#section-5.10 'm' or 109 denotes a MIME packet. I'm not sure why that would start within another packet, but that's so far the best lead I have. Also there's some URLs later in the packet.

The problematic key can be found on the keyservers, for example at: https://ha.pool.sks-keyservers.net/pks/lookup?op=get&search=0xA9C86C8DD3AE8D3A

Is this a malformed key? Why does gnupg import this key without a hitch then? What would we need to change in PGPy to use this key?