From d3f76d41e8637a1812d96724dc8bf64086e0a7f7 Mon Sep 17 00:00:00 2001 From: Johannes Schauer Marin Rodrigues Date: Tue, 16 Jul 2024 18:09:11 +0200 Subject: [PATCH 1/7] tests/test_CLI.py: create $GNUPGHOME on the fly - avoid expiration of keys by re-creating them - prevent gnupg version being incompatible with $GNUPGHOME in git Storing binary data is bad because: - git is not good at handling binary data - binary data is harder to inspect (remember the xz incident) --- ...7FF9746434704C5774BE648D49DFB1163BDFB4.rev | 35 ------ ...4E440F8FDA066F62DBD7FE72FDD5E4F64B2C3B.key | 42 ------- ...E64D7E26AFF87DFDD758119B30F6F57B1B6D4D.key | 42 ------- tests/test-data/gnupg/pubring.kbx | Bin 2018 -> 0 bytes tests/test-data/gnupg/pubring.kbx~ | Bin 32 -> 0 bytes tests/test-data/gnupg/random_seed | Bin 600 -> 0 bytes tests/test-data/gnupg/trustdb.gpg | Bin 1280 -> 0 bytes .../signatures/test.image.bmap.v2.0.asc | 114 ------------------ .../test.image.bmap.v2.0.sig-by-wrong-key | Bin 438 -> 0 bytes .../signatures/test.image.bmap.v2.0.valid-sig | Bin 438 -> 0 bytes tests/test_CLI.py | 62 +++++++++- 11 files changed, 56 insertions(+), 239 deletions(-) delete mode 100644 tests/test-data/gnupg/openpgp-revocs.d/927FF9746434704C5774BE648D49DFB1163BDFB4.rev delete mode 100644 tests/test-data/gnupg/private-keys-v1.d/6F4E440F8FDA066F62DBD7FE72FDD5E4F64B2C3B.key delete mode 100644 tests/test-data/gnupg/private-keys-v1.d/CBE64D7E26AFF87DFDD758119B30F6F57B1B6D4D.key delete mode 100644 tests/test-data/gnupg/pubring.kbx delete mode 100644 tests/test-data/gnupg/pubring.kbx~ delete mode 100644 tests/test-data/gnupg/random_seed delete mode 100644 tests/test-data/gnupg/trustdb.gpg delete mode 100644 tests/test-data/signatures/test.image.bmap.v2.0.asc delete mode 100644 tests/test-data/signatures/test.image.bmap.v2.0.sig-by-wrong-key delete mode 100644 tests/test-data/signatures/test.image.bmap.v2.0.valid-sig diff --git a/tests/test-data/gnupg/openpgp-revocs.d/927FF9746434704C5774BE648D49DFB1163BDFB4.rev b/tests/test-data/gnupg/openpgp-revocs.d/927FF9746434704C5774BE648D49DFB1163BDFB4.rev deleted file mode 100644 index 6adad84..0000000 --- a/tests/test-data/gnupg/openpgp-revocs.d/927FF9746434704C5774BE648D49DFB1163BDFB4.rev +++ /dev/null @@ -1,35 +0,0 @@ -This is a revocation certificate for the OpenPGP key: - -pub rsa3072 2022-06-13 [SC] [expires: 2024-06-12] - 927FF9746434704C5774BE648D49DFB1163BDFB4 -uid Testkey bmaptool (Do not use, private key published!) - -A revocation certificate is a kind of "kill switch" to publicly -declare that a key shall not anymore be used. It is not possible -to retract such a revocation certificate once it has been published. - -Use it to revoke this key in case of a compromise or loss of -the secret key. However, if the secret key is still accessible, -it is better to generate a new revocation certificate and give -a reason for the revocation. For details see the description of -of the gpg command "--generate-revocation" in the GnuPG manual. - -To avoid an accidental use of this file, a colon has been inserted -before the 5 dashes below. Remove this colon with a text editor -before importing and publishing this revocation certificate. - -:-----BEGIN PGP PUBLIC KEY BLOCK----- -Comment: This is a revocation certificate - -iQG2BCABCgAgFiEEkn/5dGQ0cExXdL5kjUnfsRY737QFAmKnmGQCHQAACgkQjUnf -sRY737Q/cAv/RqUa+sVKgLyqKpk1scDlzvGeyCYSnOUbf8SHAHI9X1ZUlV4Vcy0J -LcJghlfDvffjenzTmoALaNJRrTMYjpE/Sl47qwEsI84kNscZumJvFabyYIl4hdmD -KH6dZ7X0asPpgNJ1K8Cp0hndkudxxU8DuePTxyO5fKhnvEqU+eaW/i2zEC36BQZi -WB9smT/UrqxDUFTQ4Oo84d36lqnHaHnz3acSHY6Rb8eKFq609NgFfKVTN4+Gxl28 -jwCGpzJm9PIj8bMQgyB/1Fjvt9pzhLU3OqVx9wrmDrir6ecmR+rxSqgOKAwzn92E -JLPF1wYKI89UW6t413DSDfuYUICO+QWhW8tyomw3KcPDwks7C+YGu8W2YGyWhdlM -+ac6WUAaHrmkEXqTb9+sAmyNhq3D6EiAnaZndofJxgiM7AuC+hXZjnRf7eJ1Agu9 -PXNo0/DPiB2aAY2lhT5dkYYMzwEK9v5BPe9h7HI6Ou2f8fGqwUTidg3nNAntqqFQ -ecS9iokUZOkJ -=qWLD ------END PGP PUBLIC KEY BLOCK----- diff --git a/tests/test-data/gnupg/private-keys-v1.d/6F4E440F8FDA066F62DBD7FE72FDD5E4F64B2C3B.key b/tests/test-data/gnupg/private-keys-v1.d/6F4E440F8FDA066F62DBD7FE72FDD5E4F64B2C3B.key deleted file mode 100644 index 942dae7..0000000 --- a/tests/test-data/gnupg/private-keys-v1.d/6F4E440F8FDA066F62DBD7FE72FDD5E4F64B2C3B.key +++ /dev/null @@ -1,42 +0,0 @@ -Key: (private-key (rsa (n #00C679F02C9F5BE4787D937D65C98D856462F544F7C3 - 58937303C7096AD97E8EB27E27986AF47B8D017773A52B20D66E117B7D0CE94552A586 - AC819E3BEC7E136FE18273C96F407D1680AF9A3B6FB79C4B1B0EC736A7F3DF3BF8FB47 - C57549985972A494F8412231308EB67BB83DBB17807352976A5F1C120A61BC7D0E9339 - B5354D92ED5395B5555DEFAD823C124CED3424F12A710FC73A2523E3A81B929C7BAD8A - 269409EA27B3B1CF3196AE0DC0E687633BAB43DA1ABB7227B853173149D0A782C5A25B - C44F25D5B55542FFFD78CDABB6D20DF10EA8B2CF069DD10E35C32BEDE755724222CEBD - B2E187E048A629A1D536803A547C332D8DB1AC83CA2492843335F6594CAF1A6EC6B251 - 5EAB26720C4107CAAC9AE1E43F47AA429AA8E916451F3A8C608FE315FA20E746F369B7 - 56F5EFFB5F66F1F108573606E8002B319AE042D77A3B861308FFE3BFD0F958B792AD4B - B4C6A28E28511289581E2A54A14ECCBEFCAEEC25309620EE4564B68CE40E8F9DEC73D9 - 0D4CE75A01D769B3716A5E1331#)(e #010001#)(d - #0A0FE383B79E86822F479B6B5E20FD1ABE4395F803A0C974E2C3A3F0154FABB753DA - F908AF15566C351C994A8AF3240861DCD09B40E6F43A54238C1C989C39AB09E13DB280 - 1FE257923594D99F8BCCF227D0837BFB5ECB39F4A0F49E0798B00F14D7503017C93E7A - 2A3A0922A98A83220176B5F37017F1B8320546C7C6E1FD9452B6F8AE2CA051501E570B - 0BB597CDCCBF74B4849FDAA7517BB4CE707B69D081574826682C4550005B3335E2E412 - 9BFD502415C62E57FD99C776BA4BAA35864CE8B590CD55AC67761F7EDE2C94BAD0818B - AD929BE2C8C8EE7F71D219F9A2849087D1D66772D4F4646550DED35D50FD3B3AA267E2 - 1B1207066D8AE5509DAF766056DC787403B0C14FB0794D0CDACA942943CFABCF447470 - F430C8E71A6C4D1F6025901174714CF1590EAB7FBFF935C230A6606F1BB4248FA7C157 - 09C11CB7985E4FED0F55EE8E3DC99C0A7AF1DB0495FFF9520289EE45D172A551186DBF - BE982C5C1B28294DB57B91C27C327503FBC6F33B0D1FE949AEFC953E9307FF0AA6F8BB - #)(p #00C71001D7F41A281376C036F046C9B6E257760BB789B1D7DB100C7E475E17B1 - 00176D10DF954AB327BF32447437169D6A4FDE9372A24DDB141358534529454F05140C - 0CFA0BFB101FA0D28234D14144E2F803851CE36055964D755DBA2A27CEB9EC370634CD - D476CA0B7D00852C056F7E48D17CA364BA80C801602094143951BCCB0E5C2FCD5CCC54 - 6C6AB39780D790DFD42EE8AC1ADD2C8A4DEFBBBE6FA32DBAADAC39DC587FD8AED46834 - 86171805FFC6C4BF4A94029F3906413056F2A85B57#)(q - #00FF3F01CC7946963D4C1AF299A4294FB2F9A4CC99CD57093D0E14FFBB6BB906943F - FCDBCBC9154D6F4D8A07D61E389D3AF2867D81595D8D4E770BF73B486C4AF65465DCDE - DB9E4589D821DC06C6A7042C18B881604F35A2FD2966E29ABACE47C26D1936B81A0AB8 - 00D4948F1D555D83A38B408ECC8192DA10B6C33BC5001FDD5A742DFB1366992E9A02AE - A996118522C6006A6E6AAD6DC184022648C45578EDA67A6D5A24550A6EFC49A7CBA1C0 - EBADB71C86B0C943A3DF04D3C1B8AFB3EC78B7#)(u - #00FA1D07140F156D32EFA7249DE7DCBF10EEA8C9E9C5D97BABEA0323FEDD17F6D8EB - 1E0F83FE07263541EA5C24E0C2D049B355628F022F68059C53E0C4788BEBD698734D19 - 91CC0F7F72EEE831D8875C5FF9A288B8AE6EE774B0A611FC7D4AE17A5F042C49F9E281 - 6C62B06E8D73B64578D4CFE2E14255665588C9D71DC39FB5B7FADD767F82F0D7505654 - 9A2B9383617FE093E7CCC83AFA063A0DBAE41CDEBAA972B14BEF89CB539733D0BB96E4 - F2D3B005273FA9CFCAD2763363CE38D231A3FF#))) -Created: 20220613T200440 diff --git a/tests/test-data/gnupg/private-keys-v1.d/CBE64D7E26AFF87DFDD758119B30F6F57B1B6D4D.key b/tests/test-data/gnupg/private-keys-v1.d/CBE64D7E26AFF87DFDD758119B30F6F57B1B6D4D.key deleted file mode 100644 index a377c2e..0000000 --- a/tests/test-data/gnupg/private-keys-v1.d/CBE64D7E26AFF87DFDD758119B30F6F57B1B6D4D.key +++ /dev/null @@ -1,42 +0,0 @@ -Key: (private-key (rsa (n #00E3F42F3C2AB6BCD2D0253FB78EF87BEDFA1B2BDFE5 - 0EF573FA3D5147B0A9E7E34A9E74754AA77D812A4E79370D825745350BEE383C9E2CB9 - 7F4670D15834D479AF6DB01BD6B03B10096F235EEBC756FCA3A86918CEEDEABDEF2F08 - 4927D019970ED593E86BEB821C00346B5C1D5C1C6F68882807029B455D23B3BF9B2F8C - 1E6BD8211D35CED39C67097067C9759C7E4F2DBAF361D68FE95C7D89150FBC89E4A940 - 30655A83CD837969FF8C484BCEA606B28F370878E7192F0C109A280B1090E512E4EDFB - 676A68793D8617A70DBFD1BD5C5A8B06C2043FAA5F5B7F7A28FAD06A7CEFADCD2473A4 - CA11A1DF9532C90F662CAC5679F171A1822B25DF0BA8A940E6DCDFD64CAC6DFD4B97F5 - 3AB4095812627769065A00B019EFDB5004188F27096675FF759102FD6D14F34B16DE92 - 2BC73C45F0B5B39384FBD320443A4B6733643EAFC63F9E29D369EC83133DDBCF30CD8E - 1E36C4F4BBEF1A5B6B47100B0F9FE635711C2A861006E10BDA735D1DDC2CDA05964232 - 7943B2733B54EDF1982F620551#)(e #010001#)(d - #03162E4CDE298AF20A7347C662A06C752C60655447A7B52148974CB8E7107EBC20C8 - 5CFA4CC809ADCFD36DA5CB164FBC0034432FE5577995D642C566DBDE74EB526A46D04C - FF8758CBBA1D37FAE3E39894195B08C07213F07052942085E09FF0E8E6F602229A4F54 - 311D3525B5A5B8604C2943D1C3A74E3E00DD0854B6E3DD7B3E7E8BBE1FB61BB10E0887 - 07859023805400B616C1A638BB4D5D8445F0B9963DEB7E58F567881AF9F48E46C61C40 - 65804C8B03D645B447CD890AF1329B78FD4C27540862B31E4F08CE19FE20119D130747 - 8F00B5F9F5789303847FDF7D6F4B1D33E89A6B6B49937E2E95680C672503E257027402 - 27F6C52A471FE83E7E11E88266021815535CB4CC331F91E9069DC16F55E09E91C3987E - 871F06194299436F54A248A3D09F41932C3F16FCF2A9FD576F207BE96048FB10F59AD8 - E504DE9D97EF733289FAB35B73D8CBBACD2423C011BE6F1F659FBD0378992993DC57AB - 591B395559C31B3F8E94036F13D094CE7CAD9C9266542F05F76D30C68C17BBC16C085D - #)(p #00E415B09A77BA36DEA2F523E865D420506EAED1660965850251DBEBC93FB807 - 628B1B7E117F2A33AC351ADCF7AEC8E11E7749117C50E6D93F8F6B5EE5BB30C0806ECC - 62017BA06A44D41E6AB65CF9765B74FCA085D0D7F0EC3E7099AAD81424317C930983A3 - 33D53FEDBEBEBB008CA7409523FDC360D6322E031A6AD6DB8254A90675810D8DA41C92 - E368F4F3F787463CD8A2600D9B395318386535D90511195AFF8AEB870EE45895919CB9 - 9F15D08D03E05B528812EFD7F9B506B796B549F43B#)(q - #00FFDA64D91DCAEDB0664031367847C800FA390597DFA4FDD25A5FC578BA6BB34AB2 - 30D2A0A49CA07CAB560E199AF2CD91311D85A72E55F4134105EF931C5B74EBF4AC619C - 4B345DACCFF12C786EEAC16F07C4B14CB6A37DFCCF0644BC3080F3E3B9804EFFDD6353 - F63AFA7B887EA667574D1FC7F2620A87FF1D8958695221821406C92364C89B2A063C79 - 9E6E860FDB0CC81C3DC290D171EDC4AD3BA5D9ABB12453CE7D95427B0A7912CFACA306 - 1D20174DAB9922289F6587F32704CF6F8D0FE3#)(u - #37330A6F7F514DAEC91F9E15D9A3DFA0B83A36AC5EDC6C6B1A5A842A9B6E4F92F393 - E7A245C1C7963153DEE7EA5980A26FC276CEE94D2452651C1BA3100D38ED07AB7D2091 - A940E50A8AC06064D42E29155E7105D9507CF1D33D1117F9D9D68A399DAE123ECBBDF8 - C77592D540096795DF7E4820C09D24CB6B9F4BDD8BBFCF051F555CF86AAB9E70EA0032 - CC8F191E069DEB7DC88E8128FA749AD8F10DADBAB6C557832603D55CEA0E10C2341137 - 2D33030E53CA0AB3250A8E333C6DE61E20DC#))) -Created: 20220613T200440 diff --git a/tests/test-data/gnupg/pubring.kbx b/tests/test-data/gnupg/pubring.kbx deleted file mode 100644 index e05cb284bf0e1182f0b1dfa64e240bbb9a98e4cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2018 zcmai!2T;??7RLVxNgxn<7sN#A4;KfR6xAa1ux*ez=JErL}UO#xWEf~@4Po}-po5QyZh}qXLfdGzuy7?fCPa6Fy5Nz z2k^tEBu{^D?CGB&_!12Ip<)0)&;S4f%9*j>S!81d!G+aMPQ^{u%3&rO_#Mc-F4xf$ zj&g4l*PJHA1h}^)MNsxVUi`G#UX33h0097C6}}?;*nZ@lUk$2vW_*H*k&^NqzF7?)n)Ba@orPKLo=mH znFUc**VVJ(iyHN{{pj2$q8*>E`eG_hjPGj+(`Y^?kH!s@a5^8jzdoxr(&%KpyB*nE z(KIBwELK|A4=WrLGkK-Mn{x`YKG@e*H+^;L^kc1}k;7L^&tEpuORcSpf2*3AU}W;m zl~BDeq^r)st3o{tVI}ysGJks3;#9eHe(8c7_JC>1h4dNOP2}9k71~qhwWTd@zvX2i zm&34mKnI;aW&Pn2CP`Xocjm>wcjD8`DttrNgEUPCsbr#x_W2^ackO>Z;i?(rBEMkC zO)0Zt>4jY8m?&Y+4fKIl9~$5#jRqt^0H}uldQ=AWryqa@!g&gl6-Z$tt%FGnz4LT> z5K_~Ijtrr*kl{>c>+K=G2?q$`_s_v5-gndfN81>xQpXcp3Bx7|+ zk&yyjn#4(m7*JfX!v$x_uhAEF`JizHwDlYPYiwESLNe91eqjCo# zIDU(CW(A)Lpukc@y%K!6N?ekU z4f9yH?IXEsJ!V|Y17c@^J)%rnP$*C-NK}r;O4SuLr>ZyCzEq-mp)zWVu`?aw?D`9IGX=93~8j+7z zcUj!k8m4jMrOhY$4SLg54!>S)S=#3wcuGP<{N5*%P-X2T3D~sAIMZ|gN8NE~uC*ci zL>&`zp0~{Z>Y)xlz)l9WLKOJ8ssA6`5I=GIPpsOpe5@R~x1>@IOy!W0IyqHMhSYgI zPeMkb_KxLE$uWlhvl8Aq?I@HMp%;a|nQxN55HnavY1e(&CyPniSkLO!A)V~7vzaCw z&^01i`3;U6q3yKoW6PyG3`Sn5)O3IJoy#p}jw4ks6l*mRez!pl`Q}xf&NL@Wm(|6g zUu3!Om_sv-8k@eo?5hDLV#jfdmj$MCJWIS2B3-^)#S!(tU0^?pg?ArZzspgaPR*;f z#Y7o@DH(U%z6cwj>)c|luST_u^@zsUSE5Lp)rkzFk};k9=D}3r!?TIu3RYyJ_E$_R zb3({8Zp=tK;_LlHb|1b1_jrxfWw$HeD4$5;Ocrc7A)Ma_JI|!64-e|k*X_)(zucY@ z2D?6gzW+9QW-Pk0#0<3)SX*5);e>CDot>9&PwPmr?965-3(7fu~Jo5F(y({xhorgQ$9Bf?DRHJ<~-6@3n+xmSfA9}RKE3_Jx c`5P-E3uOt7$<}uw@(!JqCtN%@uI~5#1p@bRWB>pF diff --git a/tests/test-data/gnupg/pubring.kbx~ b/tests/test-data/gnupg/pubring.kbx~ deleted file mode 100644 index fe1742ab5b88fc5199fe64e5de0453723c86eb05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32 ecmZQzU{GLWWMJ}kib!Jsf~4g$^g$TJ1_1z11qBcQ diff --git a/tests/test-data/gnupg/random_seed b/tests/test-data/gnupg/random_seed deleted file mode 100644 index 92eea16ced8873bd18e80dfb6b950c5b79643c05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 600 zcmV-e0;l~!plVS*imiey$XIQ`x4}Y4F#s5SFZ-GFgQ7e6aEfW9>60hj6 z?RCuY-^Iu<;Jgq|DpRVEPKqd|H&(ltQI*PR)XB38l86TyodnCg0?`b#kyY%RVf#72 zvj8|5Dzj18c*a)#3Svd;=3Bgm%EzW+a{fwK$pzdfS!mlglj+L_s=He3_P9-rNLlL- z8vi33ufS3NYkl{zolL7H*0hlji~0*n&R=IO(nG(M$6<~^dHuCmi|5hB7yI4bNbxO9 z;W+3qW366hF7^?%pD%t*W7h!S&H|@Yg$$}2_@84dWNa2@HFrDM)| zO1J_?j66Tv22VI95~~_XH(W6wXaX}7*+?%=qFnT7FNTgjuViQ3Bs=Se~Pa`HA$=+2C5b_LSY*N~ZIrY|jExMWI m)bCSc^k6vHYqst#+gFpP!^6~jdiyICO7(Qw8d zHp3-cco-(t|13!{Dews|*_YDmd4Hpr_5Ceu5Kqe^{Lae|Qfi+jFB|zPezU?-$2TIC OS1NM%L)9UaF#rI~z8UTS diff --git a/tests/test-data/signatures/test.image.bmap.v2.0.asc b/tests/test-data/signatures/test.image.bmap.v2.0.asc deleted file mode 100644 index 8dc84fa..0000000 --- a/tests/test-data/signatures/test.image.bmap.v2.0.asc +++ /dev/null @@ -1,114 +0,0 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA512 - - - - - - - 821752 - - - 4096 - - - 201 - - - 117 - - - sha256 - - - d9cf7d44790d04fcbb089c5eeec7700e9233439ab6e4bd759035906e20f90070 - - - - 0-1 - 3-5 - 9-10 - 12 - 15-18 - 20 - 22 - 24 - 30-32 - 34-35 - 40 - 42-43 - 45 - 47 - 49-50 - 52-53 - 55-56 - 60-63 - 65-67 - 70 - 72 - 78-80 - 82-83 - 85 - 88 - 90-91 - 96 - 98-105 - 111 - 114-116 - 119-133 - 135 - 137 - 140 - 142-144 - 146-147 - 150-151 - 155 - 157 - 159-160 - 163-174 - 177 - 181-186 - 188-189 - 191 - 193 - 195 - 198-199 - - ------BEGIN PGP SIGNATURE----- - -iQGzBAEBCgAdFiEEkn/5dGQ0cExXdL5kjUnfsRY737QFAmKp2hMACgkQjUnfsRY7 -37R5kgwAvvGyq3BRzJiA+JoZbKTvQe7RA6t0mFjVozBfg8ZxpQAcqgJUR3qL72k4 -0FbOJOKrECwwxj6hfsjGHrC6cako7oqJDYwh1pal10o0sjzMT1HQiwqcmTk+VgtS -R46zB4Mz1R4IWoQcAjvXkBoxeQ+vw6SxVBPTO6a6Aa4INSFX9szxcQeh+7POGlIi -DZeWU6mLClws2OExSlcsNjttLF3EBJP7qXBPUCjiSZ1rVLtgvoVXzADYn0Em2y0+ -u2NfLOcAPAWqBJdNhXSOY+5vGfSkAN2WcQlmJiPceOlygiIVZj1WRhw6hpoAU5cM -wq2QLA0l0UQ6gq5PrF/GAnLpYlHzID6agxyGbDpcuzUq4d8IsuyF3W38SJpuDf3u -UcS/TR7l4c8t8EjxMG/L731D3n9nRy0mcHLEDKi5Afa/ppyrbp4GKmM/PO8JU1W2 -Uk8P+oUr3JFFVPdj0svHpHd9LTOjLiaWWFNiW72mSB9offswIZVBbznO+p7VYtYG -mERuBkXP -=7GH3 ------END PGP SIGNATURE----- diff --git a/tests/test-data/signatures/test.image.bmap.v2.0.sig-by-wrong-key b/tests/test-data/signatures/test.image.bmap.v2.0.sig-by-wrong-key deleted file mode 100644 index beea6af8eae46a9574055cc217b4602d495a135e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 438 zcmV;n0ZIOe0kZ@E0SW*e79j++iUIBHOP~ohV^Y9)!SVh`%60?=0%EDw8vqIk5O~4y z{z%Gp1fI$Z{ybF?ua6%T`o44*#3Re<@sy2Baw0^C3ledgrYO*aUqH_^1Z zLhyhKF~9}1VY_ms5Ba=Dd!_LE@z^d~3cwAlAwY=o5Gn zm3Q8jmr5iXn=&@MJN}ndV`R+KqphNIys$>0qimM;CvH?aw7L#Nsy61u+DzBI?@VPE z`hy&joY15~1==IO)A!>JH5SmFytwCFM1w{ERo#3W zkFwM=r<6JN&W^kBpc{&6mw-SdjG*S6O^RJ`DBk&RxlSMYxuus8*27u^^jo@zFoT+Q zXE@rI`%vM9hHZTPpmls`>MLu^K@I#2hFIwAzuc;Re-geN9e+&J3pxW&M@~ diff --git a/tests/test-data/signatures/test.image.bmap.v2.0.valid-sig b/tests/test-data/signatures/test.image.bmap.v2.0.valid-sig deleted file mode 100644 index 728e5c04017cf56c1930f2403e9d28ca7ce16f0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 438 zcmV;n0ZIOe0kZ@E0SW*e79j+ZfBAG|G;mB;biQPbN#C&+JKwYg0%EDuu>cAQ5RFOS zu@*buv`h#L0K^AGO=oV?$DM@SLO4IRp_d}ratm-B{>3xFAJpjFFgKV|CBUcx%^lw$ zSKTb%7 z4;}e&8*o%d<3`wNV%noa^qKbO8ohztq0{74HYZs?(4}z@(!Nv+n@an}%_^X45p7H9 zY3)^QpT3?x#LNaS8$Lw|mgIB5RY*d(X#G;x{9U`$Au^&sSL0nuAmuA}vc5ZpcsYcE z%bcWw8AOq2s>7hF@;~OQ>%=4x+%nZMavJ!PNFrSbtLF^!2O0sOjVHzp8|+o=wJ+V6 g4wfAs+uHhrc*q+ut#*E?5j?xTO>E#SN~aSLS<4*P^#A|> diff --git a/tests/test_CLI.py b/tests/test_CLI.py index 4683960..9b104d1 100644 --- a/tests/test_CLI.py +++ b/tests/test_CLI.py @@ -21,6 +21,8 @@ import sys import tempfile import tests.helpers +import gpg +import shutil class TestCLI(unittest.TestCase): @@ -32,7 +34,7 @@ def test_valid_signature(self): "--bmap", "tests/test-data/test.image.bmap.v2.0", "--bmap-sig", - "tests/test-data/signatures/test.image.bmap.v2.0.valid-sig", + "tests/test-data/signatures/test.image.bmap.v2.0correct.asc", "tests/test-data/test.image.gz", self.tmpfile, ], @@ -50,7 +52,7 @@ def test_unknown_signer(self): "--bmap", "tests/test-data/test.image.bmap.v2.0", "--bmap-sig", - "tests/test-data/signatures/test.image.bmap.v2.0.sig-by-wrong-key", + "tests/test-data/signatures/test.image.bmap.v2.0imposter.asc", "tests/test-data/test.image.gz", self.tmpfile, ], @@ -68,7 +70,7 @@ def test_wrong_signature(self): "--bmap", "tests/test-data/test.image.bmap.v1.4", "--bmap-sig", - "tests/test-data/signatures/test.image.bmap.v2.0.valid-sig", + "tests/test-data/signatures/test.image.bmap.v2.0correct.asc", "tests/test-data/test.image.gz", self.tmpfile, ], @@ -86,7 +88,7 @@ def test_wrong_signature_uknown_signer(self): "--bmap", "tests/test-data/test.image.bmap.v1.4", "--bmap-sig", - "tests/test-data/signatures/test.image.bmap.v2.0.sig-by-wrong-key", + "tests/test-data/signatures/test.image.bmap.v2.0imposter.asc", "tests/test-data/test.image.gz", self.tmpfile, ], @@ -102,7 +104,7 @@ def test_clearsign(self): "bmaptool", "copy", "--bmap", - "tests/test-data/signatures/test.image.bmap.v2.0.asc", + "tests/test-data/signatures/test.image.bmap.v2.0correct.det.asc", "tests/test-data/test.image.gz", self.tmpfile, ], @@ -110,14 +112,62 @@ def test_clearsign(self): stderr=subprocess.STDOUT, check=False, ) - self.assertEqual(completed_process.returncode, 1, completed_process.stdout) + self.assertEqual(completed_process.returncode, 0, completed_process.stdout) def setUp(self): + os.makedirs("tests/test-data/signatures", exist_ok=True) + for gnupghome, userid in [ + ("tests/test-data/gnupg/", "correct "), + ("tests/test-data/gnupg2/", "imposter "), + ]: + if os.path.exists(gnupghome): + shutil.rmtree(gnupghome) + os.makedirs(gnupghome) + context = gpg.Context(home_dir=gnupghome, armor=True) + dmkey = context.create_key( + userid, + algorithm="rsa3072", + expires_in=31536000, + sign=True, + certify=True, + ) + for bmapv in ["2.0", "1.4"]: + testp = "tests/test-data" + imbn = "test.image.bmap.v" + with open(f"{testp}/{imbn}{bmapv}", "rb") as bmapf, open( + f"{testp}/signatures/{imbn}{bmapv}{userid.split()[0]}.asc", + "wb", + ) as sigf, open( + f"{testp}/signatures/{imbn}{bmapv}{userid.split()[0]}.det.asc", + "wb", + ) as detsigf: + bmapcontent = bmapf.read() + signed_data, result = context.sign( + bmapcontent, mode=gpg.constants.sig.mode.DETACH + ) + sigf.write(signed_data) + signed_data, result = context.sign( + bmapcontent, mode=gpg.constants.sig.mode.CLEAR + ) + detsigf.write(signed_data) os.environ["GNUPGHOME"] = "tests/test-data/gnupg/" self.tmpfile = tempfile.mkstemp(prefix="testfile_", dir=".")[1] def tearDown(self): os.unlink(self.tmpfile) + for gnupghome, userid in [ + ("tests/test-data/gnupg/", "correct "), + ("tests/test-data/gnupg2/", "imposter "), + ]: + shutil.rmtree(gnupghome) + for bmapv in ["2.0", "1.4"]: + testp = "tests/test-data" + imbn = "test.image.bmap.v" + os.unlink(f"{testp}/signatures/{imbn}{bmapv}{userid.split()[0]}.asc") + os.unlink( + f"{testp}/signatures/{imbn}{bmapv}{userid.split()[0]}.det.asc" + ) + os.rmdir("tests/test-data/signatures") if __name__ == "__main__": From 9b13bb6da91d6817c05cf7d667cdde919f5dedaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Sommer?= Date: Fri, 23 Feb 2024 09:15:48 +0100 Subject: [PATCH 2/7] TransRead.read: pass on -1 instead of 0xFFFFFFFFFFFFFFFF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Passing 0xFFFFFFFFFFFFFFFF to read causes python to complain about: OverflowError: cannot fit 'int' into an index-sized integer Signed-off-by: Jörg Sommer --- src/bmaptool/TransRead.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bmaptool/TransRead.py b/src/bmaptool/TransRead.py index 3e53256..a9f95f6 100644 --- a/src/bmaptool/TransRead.py +++ b/src/bmaptool/TransRead.py @@ -658,8 +658,6 @@ def read(self, size=-1): necessary. """ - if size < 0: - size = 0xFFFFFFFFFFFFFFFF buf = self._f_objs[-1].read(size) self._pos += len(buf) From acf0c509c5d41cb9ba4c87300fcbc7b16e1f3fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Sommer?= Date: Sat, 9 Mar 2024 20:44:44 +0100 Subject: [PATCH 3/7] tests: Rework CLI tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current tests do not take into account whether the `gpg` package has been installed or not. If it is missing, the tests should be skipped. Furthermore, the output of the tests must be checked in order to decide whether tests fail due to an exception or whether the desired error message is displayed. Signed-off-by: Jörg Sommer --- tests/test_CLI.py | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/tests/test_CLI.py b/tests/test_CLI.py index 9b104d1..c4c00a4 100644 --- a/tests/test_CLI.py +++ b/tests/test_CLI.py @@ -21,7 +21,6 @@ import sys import tempfile import tests.helpers -import gpg import shutil @@ -39,10 +38,14 @@ def test_valid_signature(self): self.tmpfile, ], stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, + stderr=subprocess.PIPE, check=False, ) - self.assertEqual(completed_process.returncode, 0, completed_process.stdout) + self.assertEqual(completed_process.returncode, 0) + self.assertEqual(completed_process.stdout, b"") + self.assertIn( + b"successfully verified bmap file signature", completed_process.stderr + ) def test_unknown_signer(self): completed_process = subprocess.run( @@ -57,10 +60,12 @@ def test_unknown_signer(self): self.tmpfile, ], stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, + stderr=subprocess.PIPE, check=False, ) - self.assertEqual(completed_process.returncode, 1, completed_process.stdout) + self.assertEqual(completed_process.returncode, 1) + self.assertEqual(completed_process.stdout, b"") + self.assertIn(b"discovered a BAD GPG signature", completed_process.stderr) def test_wrong_signature(self): completed_process = subprocess.run( @@ -75,10 +80,12 @@ def test_wrong_signature(self): self.tmpfile, ], stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, + stderr=subprocess.PIPE, check=False, ) - self.assertEqual(completed_process.returncode, 1, completed_process.stdout) + self.assertEqual(completed_process.returncode, 1) + self.assertEqual(completed_process.stdout, b"") + self.assertIn(b"discovered a BAD GPG signature", completed_process.stderr) def test_wrong_signature_uknown_signer(self): completed_process = subprocess.run( @@ -93,10 +100,12 @@ def test_wrong_signature_uknown_signer(self): self.tmpfile, ], stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, + stderr=subprocess.PIPE, check=False, ) - self.assertEqual(completed_process.returncode, 1, completed_process.stdout) + self.assertEqual(completed_process.returncode, 1) + self.assertEqual(completed_process.stdout, b"") + self.assertIn(b"discovered a BAD GPG signature", completed_process.stderr) def test_clearsign(self): completed_process = subprocess.run( @@ -109,12 +118,21 @@ def test_clearsign(self): self.tmpfile, ], stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, + stderr=subprocess.PIPE, check=False, ) - self.assertEqual(completed_process.returncode, 0, completed_process.stdout) + self.assertEqual(completed_process.returncode, 0) + self.assertEqual(completed_process.stdout, b"") + self.assertIn( + b"successfully verified bmap file signature", completed_process.stderr + ) def setUp(self): + try: + import gpg + except ImportError: + self.skipTest("python module 'gpg' missing") + os.makedirs("tests/test-data/signatures", exist_ok=True) for gnupghome, userid in [ ("tests/test-data/gnupg/", "correct "), From 8f1e6c5c6bd8781dcaff5c1b8f44e43ed5ce5253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Sommer?= Date: Fri, 23 Feb 2024 09:21:15 +0100 Subject: [PATCH 4/7] CLI: rework PGP verification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The verification of PGP signatures had some flaws and didn't work, because the Python API and the GPG interface have changed. Inline signatures were not detected, because of a comparison of string and byte array. And even after this the code failed, because `sig.status` is no longer available. Signed-off-by: Jörg Sommer --- src/bmaptool/CLI.py | 212 +++++++++++++++----------------------------- 1 file changed, 71 insertions(+), 141 deletions(-) diff --git a/src/bmaptool/CLI.py b/src/bmaptool/CLI.py index 5a0924b..01a09f6 100644 --- a/src/bmaptool/CLI.py +++ b/src/bmaptool/CLI.py @@ -129,74 +129,58 @@ def open_block_device(path): return NamedFile(file_obj, path) -def report_verification_results(context, sigs): - """ - This is a helper function which reports the GPG signature verification - results. The 'context' argument is the gpg context object, and the 'sigs' - argument contains the results of the 'gpg.verify()' function. +def verify_bmap_signature(args, bmap_obj, bmap_path): """ + Verify GPG signature of the bmap file if it is present. The signature may + be in a separate file (detached) or it may be inside the bmap file itself + (clearsign signature). - import gpg - - for sig in sigs: - if (sig.summary & gpg.constants.SIGSUM_VALID) != 0: - key = context.get_key(sig.fpr) - author = "%s <%s>" % (key.uids[0].name, key.uids[0].email) - log.info( - "successfully verified bmap file signature of %s " - "(fingerprint %s)" % (author, sig.fpr) - ) - else: - error_out( - "signature verification failed (fingerprint %s): %s\n" - "Either fix the problem or use --no-sig-verify to " - "disable signature verification", - sig.fpr, - sig.status[2].lower(), - ) - + If user specifies the --bmap-sig option, the signature is assumed to be + detached and is taken from the user-specified file. Otherwise, this + function verifies whether the bmap file has clearsign signature, and if + not, it tries to automatically discover the detached signature by searching + for a ".sig" or ".asc" file at the same path and with the same basename as + the bmap file. This function then verifies the signature and reports the + results. -def verify_detached_bmap_signature(args, bmap_obj, bmap_path): - """ - This is a helper function for 'verify_bmap_signature()' which handles the - detached signature case. + In case of the clearsign signature, the bmap file has "invalid" format, + meaning that the proper bmap XML contents is in the GPG clearsign + container. The XML contents has to be extracted from the container before + further processing. And this is done even if user specified the + --no-sig-verify option. This function returns an open file object with the + extracted XML bmap file contents in this case. Otherwise, this function + returns None. """ - if args.no_sig_verify: + if not bmap_obj: return None - if args.bmap_sig: + clearsign_marker = b"-----BEGIN PGP SIGNED MESSAGE-----" + buf = bmap_obj.read(len(clearsign_marker)) + bmap_obj.seek(0) + + if buf == clearsign_marker: + log.info("discovered inline signature") + detached_sig = None + elif args.no_sig_verify: + return None + elif args.bmap_sig: try: - sig_obj = TransRead.TransRead(args.bmap_sig) + detached_sig = TransRead.TransRead(args.bmap_sig) except TransRead.Error as err: error_out("cannot open bmap signature file '%s':\n%s", args.bmap_sig, err) - sig_path = args.bmap_sig else: # Check if there is a stand-alone signature file try: - sig_path = bmap_path + ".asc" - sig_obj = TransRead.TransRead(sig_path) + detached_sig = TransRead.TransRead(bmap_path + ".asc") except TransRead.Error: try: - sig_path = bmap_path + ".sig" - sig_obj = TransRead.TransRead(sig_path) + detached_sig = TransRead.TransRead(bmap_path + ".sig") except TransRead.Error: - # No signatures found + # No detached signatures found return None - log.info("discovered signature file for bmap '%s'" % sig_path) - - # If the stand-alone signature file is not local, make a local copy - if sig_obj.is_url: - try: - tmp_obj = tempfile.NamedTemporaryFile("wb+") - except IOError as err: - error_out("cannot create a temporary file for the signature:\n%s", err) - - shutil.copyfileobj(sig_obj, tmp_obj) - tmp_obj.seek(0) - sig_obj.close() - sig_obj = tmp_obj + log.info("discovered signature file for bmap '%s'" % detached_sig.name) try: import gpg @@ -208,60 +192,18 @@ def verify_detached_bmap_signature(args, bmap_obj, bmap_path): ) try: - context = gpg.Context() - signature = io.FileIO(sig_obj.name) - signed_data = io.FileIO(bmap_obj.name) - sigs = context.verify(signed_data, signature, None)[1].signatures - except gpg.errors.GPGMEError as err: - error_out( - "failure when trying to verify GPG signature: %s\n" - 'Make sure file "%s" has proper GPG format', - err.getstring(), - sig_path, - ) - except gpg.errors.BadSignatures as err: - error_out("discovered a BAD GPG signature: %s\n", sig_path) - - sig_obj.close() - - if len(sigs) == 0: - log.warning( - 'the "%s" signature file does not actually contain ' - "any valid signatures" % sig_path - ) - else: - report_verification_results(context, sigs) - - return None - + bmap_data = bmap_obj.read() + bmap_obj.seek(0) -def verify_clearsign_bmap_signature(args, bmap_obj): - """ - This is a helper function for 'verify_bmap_signature()' which handles the - clearsign signature case. - """ - - if args.bmap_sig: - error_out( - "the bmap file has clearsign format and already contains " - "the signature, so --bmap-sig option should not be used" - ) - - try: - import gpg - except ImportError: - error_out( - 'cannot verify the signature because the python "gpg"' - "module is not installed on your system\nCannot extract " - "block map from the bmap file which has clearsign format, " - "please, install the module" - ) + if detached_sig: + det_sig_data = detached_sig.read() + detached_sig.close() + else: + det_sig_data = None - try: context = gpg.Context() - signature = io.FileIO(bmap_obj.name) - plaintext = io.BytesIO() - sigs = context.verify(plaintext, signature, None) + plaintext, sigs = context.verify(bmap_data, det_sig_data) + sigs = sigs.signatures except gpg.errors.GPGMEError as err: error_out( "failure when trying to verify GPG signature: %s\n" @@ -269,63 +211,51 @@ def verify_clearsign_bmap_signature(args, bmap_obj): err[2].lower(), ) except gpg.errors.BadSignatures as err: - error_out("discovered a BAD GPG signature: %s\n", sig_path) + error_out( + "discovered a BAD GPG signature: %s\n", + detached_sig.name if detached_sig else bmap_obj.name + ) if not args.no_sig_verify: if len(sigs) == 0: log.warning( + 'the "%s" signature file does not actually contain ' + "any valid signatures" % detached_sig.name if detached_sig + else "the bmap file clearsign signature does not actually " "contain any valid signatures" ) else: - report_verification_results(context, sigs) + for sig in sigs: + if (sig.summary & gpg.constants.SIGSUM_VALID) != 0: + key = context.get_key(sig.fpr) + author = "%s <%s>" % (key.uids[0].name, key.uids[0].email) + log.info( + "successfully verified bmap file signature of %s " + "(fingerprint %s)" % (author, sig.fpr) + ) + else: + error_out( + "signature verification failed (fingerprint %s)\n" + "Either fix the problem or use --no-sig-verify to " + "disable signature verification", + sig.fpr, + ) + + if detached_sig: + # for detached signatures we are done + return None try: - tmp_obj = tempfile.TemporaryFile("w+") + tmp_obj = tempfile.TemporaryFile("w+b") except IOError as err: error_out("cannot create a temporary file for bmap:\n%s", err) - tmp_obj.write(plaintext.getvalue()) + tmp_obj.write(plaintext) tmp_obj.seek(0) return tmp_obj -def verify_bmap_signature(args, bmap_obj, bmap_path): - """ - Verify GPG signature of the bmap file if it is present. The signature may - be in a separate file (detached) or it may be inside the bmap file itself - (clearsign signature). - - If user specifies the --bmap-sig option, the signature is assumed to be - detached and is taken from the user-specified file. Otherwise, this - function verifies whether the bmap file has clearsign signature, and if - not, it tries to automatically discover the detached signature by searching - for a ".sig" or ".asc" file at the same path and with the same basename as - the bmap file. This function then verifies the signature and reports the - results. - - In case of the clearsign signature, the bmap file has "invalid" format, - meaning that the proper bmap XML contents is in the GPG clearsign - container. The XML contents has to be extracted from the container before - further processing. And this is done even if user specified the - --no-sig-verify option. This function returns an open file object with the - extracted XML bmap file contents in this case. Otherwise, this function - returns None. - """ - - if not bmap_obj: - return None - - clearsign_marker = "-----BEGIN PGP SIGNED MESSAGE-----" - buf = bmap_obj.read(len(clearsign_marker)) - bmap_obj.seek(0) - - if buf == clearsign_marker: - return verify_clearsign_bmap_signature(args, bmap_obj) - else: - return verify_detached_bmap_signature(args, bmap_obj, bmap_path) - - def find_and_open_bmap(args): """ This is a helper function for 'open_files()' which discovers and opens the From 215e38f431eb446e85bd793e65c0af73ce0751ac Mon Sep 17 00:00:00 2001 From: Johannes Schauer Marin Rodrigues Date: Wed, 17 Jul 2024 07:38:38 +0200 Subject: [PATCH 5/7] tests/test_CLI.py: factor out verification logic into its own function --- src/bmaptool/CLI.py | 102 +++++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 40 deletions(-) diff --git a/src/bmaptool/CLI.py b/src/bmaptool/CLI.py index 01a09f6..cf91a9e 100644 --- a/src/bmaptool/CLI.py +++ b/src/bmaptool/CLI.py @@ -39,6 +39,7 @@ import shutil import io import pathlib +from typing import NamedTuple from . import BmapCreate, BmapCopy, BmapHelpers, TransRead VERSION = "3.8.0" @@ -129,6 +130,60 @@ def open_block_device(path): return NamedFile(file_obj, path) +class Signature(NamedTuple): + valid: bool + fpr: str + uid: str + + +def verify_bmap_signature_gpgme(bmap_obj, detached_sig): + try: + import gpg + except ImportError: + error_out( + 'cannot verify the signature because the python "gpg" ' + "module is not installed on your system\nPlease, either " + "install the module or use --no-sig-verify" + ) + + try: + bmap_data = bmap_obj.read() + + if detached_sig: + det_sig_data = detached_sig.read() + detached_sig.close() + else: + det_sig_data = None + + context = gpg.Context() + plaintext, sigs = context.verify(bmap_data, det_sig_data) + sigs = sigs.signatures + except gpg.errors.GPGMEError as err: + error_out( + "failure when trying to verify GPG signature: %s\n" + "make sure the bmap file has proper GPG format", + err[2].lower(), + ) + except gpg.errors.BadSignatures as err: + error_out( + "discovered a BAD GPG signature: %s\n", + detached_sig.name if detached_sig else bmap_obj.name, + ) + + def fpr2uid(fpr): + key = context.get_key(fpr) + return "%s <%s>" % (key.uids[0].name, key.uids[0].email) + + return plaintext, [ + Signature( + (sig.summary & gpg.constants.SIGSUM_VALID) != 0, + sig.fpr, + fpr2uid(sig.fpr), + ) + for sig in sigs + ] + + def verify_bmap_signature(args, bmap_obj, bmap_path): """ Verify GPG signature of the bmap file if it is present. The signature may @@ -182,57 +237,24 @@ def verify_bmap_signature(args, bmap_obj, bmap_path): log.info("discovered signature file for bmap '%s'" % detached_sig.name) - try: - import gpg - except ImportError: - error_out( - 'cannot verify the signature because the python "gpg" ' - "module is not installed on your system\nPlease, either " - "install the module or use --no-sig-verify" - ) - - try: - bmap_data = bmap_obj.read() - bmap_obj.seek(0) - - if detached_sig: - det_sig_data = detached_sig.read() - detached_sig.close() - else: - det_sig_data = None - - context = gpg.Context() - plaintext, sigs = context.verify(bmap_data, det_sig_data) - sigs = sigs.signatures - except gpg.errors.GPGMEError as err: - error_out( - "failure when trying to verify GPG signature: %s\n" - "make sure the bmap file has proper GPG format", - err[2].lower(), - ) - except gpg.errors.BadSignatures as err: - error_out( - "discovered a BAD GPG signature: %s\n", - detached_sig.name if detached_sig else bmap_obj.name - ) + plaintext, sigs = verify_bmap_signature_gpgme(bmap_obj, detached_sig) + bmap_obj.seek(0) if not args.no_sig_verify: if len(sigs) == 0: log.warning( 'the "%s" signature file does not actually contain ' - "any valid signatures" % detached_sig.name if detached_sig - else - "the bmap file clearsign signature does not actually " + "any valid signatures" % detached_sig.name + if detached_sig + else "the bmap file clearsign signature does not actually " "contain any valid signatures" ) else: for sig in sigs: - if (sig.summary & gpg.constants.SIGSUM_VALID) != 0: - key = context.get_key(sig.fpr) - author = "%s <%s>" % (key.uids[0].name, key.uids[0].email) + if sig.valid: log.info( "successfully verified bmap file signature of %s " - "(fingerprint %s)" % (author, sig.fpr) + "(fingerprint %s)" % (sig.uid, sig.fpr) ) else: error_out( From dc89fd4c1d495d45dc2b0c28423228f7c8b2d01a Mon Sep 17 00:00:00 2001 From: Johannes Schauer Marin Rodrigues Date: Wed, 17 Jul 2024 11:15:21 +0200 Subject: [PATCH 6/7] add gpg verification methods using gpg and gpgv binaries --- .github/workflows/ci.yml | 2 +- src/bmaptool/CLI.py | 102 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0d1485..a93eefe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - sudo apt-get install -y pbzip2 pigz lzop liblz4-tool libgpgme11-dev + sudo apt-get install -y pbzip2 pigz lzop liblz4-tool libgpgme11-dev gpg gpgv python3-gpg python -m pip install --upgrade pip pip install build - name: Build package diff --git a/src/bmaptool/CLI.py b/src/bmaptool/CLI.py index cf91a9e..3332b86 100644 --- a/src/bmaptool/CLI.py +++ b/src/bmaptool/CLI.py @@ -39,6 +39,8 @@ import shutil import io import pathlib +import subprocess +import re from typing import NamedTuple from . import BmapCreate, BmapCopy, BmapHelpers, TransRead @@ -184,6 +186,79 @@ def fpr2uid(fpr): ] +def verify_bmap_signature_gpgbin(bmap_obj, detached_sig, gpgargv): + with tempfile.TemporaryDirectory(suffix=".bmaptool.gnupg") as td: + if detached_sig: + with open(f"{td}/sig", "wb") as f: + shutil.copyfileobj(detached_sig, f) + gpgargv.append(f"{td}/sig") + with open(f"{td}/bmap", "wb") as f: + shutil.copyfileobj(bmap_obj, f) + gpgargv.append(f"{td}/bmap") + sp = subprocess.Popen( + gpgargv, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + (output, error) = sp.communicate() + if sp.returncode > 0: + if error.find(b"[GNUPG:] NO_PUBKEY "): + error_out("No matching key found") + error_out("Failed to validate PGP signature") + + # regexes are from patatt and b4 + short_fpr = None + uid = None + gs_matches = re.search( + rb"^\[GNUPG:] GOODSIG ([0-9A-F]+)\s+(.*)$", error, flags=re.M + ) + if gs_matches: + good = True + short_fpr, uid = gs_matches.groups() + vs_matches = re.search( + rb"^\[GNUPG:] VALIDSIG ([0-9A-F]+) (\d{4}-\d{2}-\d{2}) (\d+)", + error, + flags=re.M, + ) + if vs_matches: + valid = True + fpr, signdate, signepoch = vs_matches.groups() + if not fpr.endswith(short_fpr): + error_out("good fingerprint does not match valid fingerprint") + if (b': Good signature from "' + uid + b'"') not in error: + log.warning("Unable to find good signature in gpg stderr output") + return output, [ + Signature( + good and valid, + fpr.decode(), + uid.decode(), + ) + ] + + +def verify_bmap_signature_gpgv(bmap_obj, detached_sig): + return verify_bmap_signature_gpgbin( + bmap_obj, detached_sig, ["gpgv", "--status-fd=2"] + ) + + +def verify_bmap_signature_gpg(bmap_obj, detached_sig): + return verify_bmap_signature_gpgbin( + bmap_obj, + detached_sig, + [ + "gpg", + "--batch", + "--no-auto-key-retrieve", + "--no-auto-check-trustdb", + "--verify", + "--output", + "-", + "--status-fd=2", + ], + ) + + def verify_bmap_signature(args, bmap_obj, bmap_path): """ Verify GPG signature of the bmap file if it is present. The signature may @@ -237,7 +312,32 @@ def verify_bmap_signature(args, bmap_obj, bmap_path): log.info("discovered signature file for bmap '%s'" % detached_sig.name) - plaintext, sigs = verify_bmap_signature_gpgme(bmap_obj, detached_sig) + methods = { + "gpgme": verify_bmap_signature_gpgme, + "gpg": verify_bmap_signature_gpg, + "gpgv": verify_bmap_signature_gpgv, + } + have_method = set() + try: + import gpg + + have_method.add("gpgme") + except ImportError: + pass + if shutil.which("gpg") is not None: + have_method.add("gpg") + if shutil.which("gpgv") is not None: + have_method.add("gpgv") + + if not have_method: + error_out("Cannot verify GPG signature without GPG") + + for method in ["gpgme", "gpgv", "gpg"]: + log.info(f"Trying to verify signature using {method}") + if method not in have_method: + continue + plaintext, sigs = methods[method](bmap_obj, detached_sig) + break bmap_obj.seek(0) if not args.no_sig_verify: From aa39af0573cd99f36626f498b656ab819ad7650c Mon Sep 17 00:00:00 2001 From: Joshua Watt Date: Tue, 14 Jan 2025 09:08:29 -0700 Subject: [PATCH 7/7] ci: Fix GPG tests Fixes up the way that the GPG tests work by adding a new "native" python test version. This is required because the python 'gpg' module *must* come from the host package in order to patch libgpgme (e.g. 'python3-gpg'). It's not possible to get this module installed with the pre-canned python versions provided by GitHub Actions, so the gpg tests are skipped for this version, but using the host native python can. --- .github/workflows/ci.yml | 28 +++++++++++++++++++++------- pyproject.toml | 5 ++++- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a93eefe..e0fdde5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,26 +16,40 @@ jobs: - "3.10" - "3.11" - "3.12" + # Testing with native host python is required in order to test the + # GPG code, since it must use the host python3-gpg package + - "native" steps: - uses: actions/checkout@v4 - - name: Setup Python ${{ matrix.python-version }} + + - if: matrix.python-version != 'native' + name: Setup Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + + - if: matrix.python-version == 'native' + name: Setup Native Python + run: | + sudo apt-get install -y python3 python3-pip libgpgme11-dev python3-gpg + - name: Install dependencies run: | - sudo apt-get install -y pbzip2 pigz lzop liblz4-tool libgpgme11-dev gpg gpgv python3-gpg - python -m pip install --upgrade pip - pip install build + sudo apt-get install -y pbzip2 pigz lzop liblz4-tool + python3 -m pip install --upgrade pip + python3 -m pip install build + - name: Build package run: | - python -m build + python3 -m build + - name: Install package run: | - pip install -e .[dev] + python3 -m pip install -e .[dev] + - name: Run tests run: | - python -m unittest -vb + python3 -m unittest -vb lint: runs-on: ubuntu-latest diff --git a/pyproject.toml b/pyproject.toml index 14ae192..6cf449a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,10 @@ name = "bmaptool" description = "BMAP tools" dynamic = ["version"] dependencies = [ - "gpg >= 1.10.0", + # NOTE: gpg is not installed because it must come from the system GPG package + # (e.g. python3-gpg on Ubuntu) and not from PyPi. The PyPi version is very old + # and no longer functions correctly + #"gpg >= 1.10.0", ] required-python = ">= 3.8" authors = [