pythonpgp

PGPy throwing error when using BCPG public key


I'm new to PGP encryption and I'm trying to understand a problem I'm having. I have this public key (taken from some examples online)

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG v1.58

mI0EWiOMeQEEAImCEQUnSQ54ee+mnkANsjyvZm2QsC1sGIBEpmyJbh2xWuluJ/KV
TIUSqbkLOEq4COIlzG0fhuruUWBM2+ANazq5jkxLrYmHX4AwA2Q6jvd3xE8B1uVj
qT0TEKyZtmBwesEswUxb+vOwVLdWKXpcySXtIQhoKWAUVzG7e5uEawyXABEBAAG0
BWFuaXNoiJwEEAECAAYFAlojjHkACgkQmCS94uDDx9lHewP/UtsSk3lyj5GnHyoT
HZMz+sUFpFlan7agqHf6pV2Pgdb9OMCVauMwl9bjPY9HSHQg/a3gTQ5qNq9txiI2
4Fso2Q3AR6XcVk2wQxS6prJ9imPi1npXarCwZkEgWLXWLuQLHoxRWHf9olUqeW7P
kwQlJ1K9Ib85pCTvx16DN7QwQv8=
=Qteg
-----END PGP PUBLIC KEY BLOCK-----

With this code:

pub_key = pgpy.PGPKey()
pub_key.parse(KEY_PUB)

SOME_TEXT = 'Hello, world'

msg = pgpy.PGPMessage.new(SOME_TEXT)

encrypted_message = pub_key.encrypt(msg)

pgpstr = str(encrypted_message)

print(pgpstr)

I get this error:

pgpy.errors.PGPError: Key 9824BDE2E0C3C7D9 does not have the required usage flag EncryptStorage, EncryptCommunications

But everything works if I use this other key:

-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: Alice's OpenPGP certificate
Comment: https://www.ietf.org/id/draft-bre-openpgp-samples-01.html

mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U
b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE
ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy
MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO
dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4
OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s
E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb
DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn
0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE=
=iIGO
-----END PGP PUBLIC KEY BLOCK-----

So I would like to understand what is the difference between the 2 keys and how can I make the first one work. Thank you in advance!


Solution

  • The error message explicitly tells you that the key that fails doesn't describe itself as suitable for encrypted storage or communications.

    Per the signature subpacket specification, we know that this information lives in a subpacket of type 27.

    Per the detailed specification on that subpacket, we know that the desired flags are 0x04 and 0x08, respectively.


    To compare these keys, you can use gpg --list-packets --verbose.

    For the first, non-working key, we get:

    gpg: armor header: Version: BCPG v1.58
    # off=0 ctb=98 tag=6 hlen=2 plen=141
    :public key packet:
            version 4, algo 1, created 1512279161, expires 0
            pkey[0]: 8982110527490E7879EFA69E400DB23CAF666D90B02D6C188044A66C896E1DB15AE96E27F2954C8512A9B90B384AB808E225CC6D1F86EAEE51604CDBE00D6B3AB98E4C4BAD89875F803003643A8EF777C44F01D6E563A93D1310AC99B660707AC12CC14C5BFAF3B054B756297A5CC925ED2108682960145731BB7B9B846B0C97
            pkey[1]: 010001
            keyid: 9824BDE2E0C3C7D9
    # off=143 ctb=b4 tag=13 hlen=2 plen=5
    :user ID packet: "anish"
    # off=150 ctb=88 tag=2 hlen=2 plen=156
    :signature packet: algo 1, keyid 9824BDE2E0C3C7D9
            version 4, created 1512279161, md5len 0, sigclass 0x10
            digest algo 2, begin of digest 47 7b
            hashed subpkt 2 len 4 (sig created 2017-12-03)
            subpkt 16 len 8 (issuer key ID 9824BDE2E0C3C7D9)
            data: 52DB129379728F91A71F2A131D9333FAC505A4595A9FB6A0A877FAA55D8F81D6FD38C0956AE33097D6E33D8F47487420FDADE04D0E6A36AF6DC62236E05B28D90DC047A5DC564DB04314BAA6B27D8A63E2D67A576AB0B066412058B5D62EE40B1E8C515877FDA2552A796ECF9304252752BD21BF39A424EFC75E8337B43042FF
    

    This key contains no signature subpackets of type 27 at all.


    For the second, working key, we get:

    gpg: armor header: Comment: Alice's OpenPGP certificate
    gpg: armor header: Comment: https://www.ietf.org/id/draft-bre-openpgp-samples-01.html
    # off=0 ctb=98 tag=6 hlen=2 plen=51
    :public key packet:
            version 4, algo 22, created 1548158185, expires 0
            pkey[0]: 092B06010401DA470F01 ed25519 (1.3.6.1.4.1.11591.15.1)
            pkey[1]: 40AE35B0937140AB28856C504A4F84F35DC541A8F4C1DE09B3942FD46FB3B5BB5D
            keyid: F231550C4F47E38E
    # off=53 ctb=b4 tag=13 hlen=2 plen=38
    :user ID packet: "Alice Lovelace <alice@openpgp.example>"
    # off=93 ctb=88 tag=2 hlen=2 plen=144
    :signature packet: algo 22, keyid F231550C4F47E38E
            version 4, created 1571135290, md5len 0, sigclass 0x13
            digest algo 8, begin of digest e9 2b
            hashed subpkt 27 len 1 (key flags: 03)
            hashed subpkt 11 len 4 (pref-sym-algos: 9 8 7 2)
            hashed subpkt 21 len 5 (pref-hash-algos: 10 9 8 11 2)
            hashed subpkt 22 len 3 (pref-zip-algos: 2 3 1)
            hashed subpkt 30 len 1 (features: 01)
            hashed subpkt 23 len 1 (keyserver preferences: 80)
            hashed subpkt 33 len 21 (issuer fpr v4 EB85BB5FA33A75E15E944E63F231550C4F47E38E)
            hashed subpkt 2 len 4 (sig created 2019-10-15)
            subpkt 16 len 8 (issuer key ID F231550C4F47E38E)
            data: 039C7052A203A6B073AC77DA86B226698119CE772A6F6E6F90B5766AB61F6BBF
            data: 1DC0C2D2D0BE3C702C3BB3938754B54C2F51889B2F6B001F08F69066879DF202
    # off=239 ctb=b8 tag=14 hlen=2 plen=56
    :public sub key packet:
            version 4, algo 18, created 1548158185, expires 0
            pkey[0]: 0A2B060104019755010501 cv25519 (1.3.6.1.4.1.3029.1.5.1)
            pkey[1]: 4042FF0621ADAB493CE0A9B5C2A430D8322291562B42B32DB4DF1DEC13DF9EBE22
            pkey[2]: 03010807
            keyid: 4766F6B9D5F21EB6
    # off=297 ctb=88 tag=2 hlen=2 plen=120
    :signature packet: algo 22, keyid F231550C4F47E38E
            version 4, created 1548158185, md5len 0, sigclass 0x18
            digest algo 8, begin of digest 59 d0
            hashed subpkt 33 len 21 (issuer fpr v4 EB85BB5FA33A75E15E944E63F231550C4F47E38E)
            hashed subpkt 2 len 4 (sig created 2019-01-22)
            hashed subpkt 27 len 1 (key flags: 0C)
            subpkt 16 len 8 (issuer key ID F231550C4F47E38E)
            data: C51D4B3A4C8731ACD4ED1191635059B53343C9D439E0FDC0D6E3137CD1FB27D1
            data: DB62ABED59028DE0040B4F15493795F9016C9B3E79FE59ED5A4C185A19AF3A01
    

    Here, the main key is configured only for signing data and certifying other keys (value is 3 == 2 (signing data) + 1 (certifying other keys)); but it has subkey with a flag of 0x0C, aka decimal 12, aka 8 (encrypting data) + 4 (encrypting storage).


    Finally, inside PGPy, the encrypt method has a decorator @KeyAction(KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage, is_public=True), which actually validates the flags in question.

    It is conceivable that the if len(self.flags) clause therein is intended to skip this check for keys that don't have any subpacket listing usage flags at all. If this is the case, it is conceivable that the above would be accepted as a bug report, insofar as this code is performing usage checks against a key with no usage data present.