diff --git a/.github/actions/upload-coverage/action.yml b/.github/actions/upload-coverage/action.yml index 98e88be..e02012f 100644 --- a/.github/actions/upload-coverage/action.yml +++ b/.github/actions/upload-coverage/action.yml @@ -13,10 +13,10 @@ runs: fi id: coverage-uuid shell: bash - - uses: actions/upload-artifact@v3.1.0 + - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 with: - name: coverage-data + name: coverage-data-${{ steps.coverage-uuid.outputs.COVERAGE_UUID }} path: | .coverage.* *.lcov - if-no-files-found: ignore + if-no-files-found: ignore \ No newline at end of file diff --git a/.github/requirements/build-requirements.in b/.github/requirements/build-requirements.in new file mode 100644 index 0000000..8a1748c --- /dev/null +++ b/.github/requirements/build-requirements.in @@ -0,0 +1,6 @@ +# Must be kept sync with build-system.requires at pyproject.toml +setuptools>=61.0.0 +cffi>=1.12; platform_python_implementation != 'PyPy' + +# WARN: changing the requirements here DOES NOT update the dependencies used for building at the github workflow, as the build process used build-requirements.txt +# To update build-requirements.txt according to the dependencies here, run pip-compile --allow-unsafe --generate-hashes build-requirements.in diff --git a/.github/requirements/build-requirements.txt b/.github/requirements/build-requirements.txt new file mode 100644 index 0000000..a774f40 --- /dev/null +++ b/.github/requirements/build-requirements.txt @@ -0,0 +1,70 @@ +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# pip-compile --allow-unsafe --generate-hashes build-requirements.in +# +cffi==1.16.0 ; platform_python_implementation != "PyPy" \ + --hash=sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc \ + --hash=sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a \ + --hash=sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417 \ + --hash=sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab \ + --hash=sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520 \ + --hash=sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36 \ + --hash=sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743 \ + --hash=sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8 \ + --hash=sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed \ + --hash=sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684 \ + --hash=sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56 \ + --hash=sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324 \ + --hash=sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d \ + --hash=sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235 \ + --hash=sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e \ + --hash=sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088 \ + --hash=sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000 \ + --hash=sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7 \ + --hash=sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e \ + --hash=sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673 \ + --hash=sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c \ + --hash=sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe \ + --hash=sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2 \ + --hash=sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098 \ + --hash=sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8 \ + --hash=sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a \ + --hash=sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0 \ + --hash=sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b \ + --hash=sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896 \ + --hash=sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e \ + --hash=sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9 \ + --hash=sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2 \ + --hash=sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b \ + --hash=sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6 \ + --hash=sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404 \ + --hash=sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f \ + --hash=sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0 \ + --hash=sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4 \ + --hash=sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc \ + --hash=sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936 \ + --hash=sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba \ + --hash=sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872 \ + --hash=sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb \ + --hash=sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614 \ + --hash=sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1 \ + --hash=sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d \ + --hash=sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969 \ + --hash=sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b \ + --hash=sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4 \ + --hash=sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627 \ + --hash=sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956 \ + --hash=sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357 + # via -r build-requirements.in +pycparser==2.22 \ + --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \ + --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc + # via cffi + +# The following packages are considered to be unsafe in a requirements file: +setuptools==72.1.0 \ + --hash=sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1 \ + --hash=sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec + # via -r build-requirements.in diff --git a/.github/requirements/publish-requirements.in b/.github/requirements/publish-requirements.in new file mode 100644 index 0000000..1b92e68 --- /dev/null +++ b/.github/requirements/publish-requirements.in @@ -0,0 +1,5 @@ +twine +requests + +# WARN: changing the requirements here DOES NOT update the dependencies used for publishing at the github workflow, as the process used publish-requirements.txt +# To update publish-requirements.txt according to the dependencies here, run pip-compile --allow-unsafe --generate-hashes publish-requirements.in \ No newline at end of file diff --git a/.github/requirements/publish-requirements.txt b/.github/requirements/publish-requirements.txt new file mode 100644 index 0000000..aaea115 --- /dev/null +++ b/.github/requirements/publish-requirements.txt @@ -0,0 +1,209 @@ +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# pip-compile --allow-unsafe --generate-hashes publish-requirements.in +# +certifi==2024.7.4 \ + --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \ + --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90 + # via requests +charset-normalizer==3.3.2 \ + --hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \ + --hash=sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087 \ + --hash=sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786 \ + --hash=sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8 \ + --hash=sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09 \ + --hash=sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185 \ + --hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \ + --hash=sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e \ + --hash=sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519 \ + --hash=sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898 \ + --hash=sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269 \ + --hash=sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3 \ + --hash=sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f \ + --hash=sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6 \ + --hash=sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8 \ + --hash=sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a \ + --hash=sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73 \ + --hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \ + --hash=sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714 \ + --hash=sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2 \ + --hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \ + --hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \ + --hash=sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d \ + --hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \ + --hash=sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6 \ + --hash=sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269 \ + --hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \ + --hash=sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d \ + --hash=sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a \ + --hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \ + --hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \ + --hash=sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d \ + --hash=sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0 \ + --hash=sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed \ + --hash=sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068 \ + --hash=sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac \ + --hash=sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25 \ + --hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \ + --hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \ + --hash=sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26 \ + --hash=sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2 \ + --hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \ + --hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \ + --hash=sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5 \ + --hash=sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99 \ + --hash=sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c \ + --hash=sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d \ + --hash=sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811 \ + --hash=sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa \ + --hash=sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a \ + --hash=sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03 \ + --hash=sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b \ + --hash=sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04 \ + --hash=sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c \ + --hash=sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001 \ + --hash=sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458 \ + --hash=sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389 \ + --hash=sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99 \ + --hash=sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985 \ + --hash=sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537 \ + --hash=sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238 \ + --hash=sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f \ + --hash=sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d \ + --hash=sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796 \ + --hash=sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a \ + --hash=sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143 \ + --hash=sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8 \ + --hash=sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c \ + --hash=sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5 \ + --hash=sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5 \ + --hash=sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711 \ + --hash=sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4 \ + --hash=sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6 \ + --hash=sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c \ + --hash=sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7 \ + --hash=sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4 \ + --hash=sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b \ + --hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \ + --hash=sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12 \ + --hash=sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c \ + --hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \ + --hash=sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8 \ + --hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \ + --hash=sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b \ + --hash=sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4 \ + --hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \ + --hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 \ + --hash=sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33 \ + --hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \ + --hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561 + # via requests +docutils==0.21.2 \ + --hash=sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f \ + --hash=sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2 + # via readme-renderer +idna==3.7 \ + --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ + --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 + # via requests +importlib-metadata==8.2.0 \ + --hash=sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369 \ + --hash=sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d + # via twine +jaraco-classes==3.4.0 \ + --hash=sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd \ + --hash=sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790 + # via keyring +jaraco-context==5.3.0 \ + --hash=sha256:3e16388f7da43d384a1a7cd3452e72e14732ac9fe459678773a3608a812bf266 \ + --hash=sha256:c2f67165ce1f9be20f32f650f25d8edfc1646a8aeee48ae06fb35f90763576d2 + # via keyring +jaraco-functools==4.0.1 \ + --hash=sha256:3b24ccb921d6b593bdceb56ce14799204f473976e2a9d4b15b04d0f2c2326664 \ + --hash=sha256:d33fa765374c0611b52f8b3a795f8900869aa88c84769d4d1746cd68fb28c3e8 + # via keyring +keyring==25.2.1 \ + --hash=sha256:2458681cdefc0dbc0b7eb6cf75d0b98e59f9ad9b2d4edd319d18f68bdca95e50 \ + --hash=sha256:daaffd42dbda25ddafb1ad5fec4024e5bbcfe424597ca1ca452b299861e49f1b + # via twine +markdown-it-py==3.0.0 \ + --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \ + --hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb + # via rich +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ + --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba + # via markdown-it-py +more-itertools==10.3.0 \ + --hash=sha256:e5d93ef411224fbcef366a6e8ddc4c5781bc6359d43412a65dd5964e46111463 \ + --hash=sha256:ea6a02e24a9161e51faad17a8782b92a0df82c12c1c8886fec7f0c3fa1a1b320 + # via + # jaraco-classes + # jaraco-functools +nh3==0.2.18 \ + --hash=sha256:0411beb0589eacb6734f28d5497ca2ed379eafab8ad8c84b31bb5c34072b7164 \ + --hash=sha256:14c5a72e9fe82aea5fe3072116ad4661af5cf8e8ff8fc5ad3450f123e4925e86 \ + --hash=sha256:19aaba96e0f795bd0a6c56291495ff59364f4300d4a39b29a0abc9cb3774a84b \ + --hash=sha256:34c03fa78e328c691f982b7c03d4423bdfd7da69cd707fe572f544cf74ac23ad \ + --hash=sha256:36c95d4b70530b320b365659bb5034341316e6a9b30f0b25fa9c9eff4c27a204 \ + --hash=sha256:3a157ab149e591bb638a55c8c6bcb8cdb559c8b12c13a8affaba6cedfe51713a \ + --hash=sha256:42c64511469005058cd17cc1537578eac40ae9f7200bedcfd1fc1a05f4f8c200 \ + --hash=sha256:5f36b271dae35c465ef5e9090e1fdaba4a60a56f0bb0ba03e0932a66f28b9189 \ + --hash=sha256:6955369e4d9f48f41e3f238a9e60f9410645db7e07435e62c6a9ea6135a4907f \ + --hash=sha256:7b7c2a3c9eb1a827d42539aa64091640bd275b81e097cd1d8d82ef91ffa2e811 \ + --hash=sha256:8ce0f819d2f1933953fca255db2471ad58184a60508f03e6285e5114b6254844 \ + --hash=sha256:94a166927e53972a9698af9542ace4e38b9de50c34352b962f4d9a7d4c927af4 \ + --hash=sha256:a7f1b5b2c15866f2db413a3649a8fe4fd7b428ae58be2c0f6bca5eefd53ca2be \ + --hash=sha256:c8b3a1cebcba9b3669ed1a84cc65bf005728d2f0bc1ed2a6594a992e817f3a50 \ + --hash=sha256:de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307 \ + --hash=sha256:f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe + # via readme-renderer +pkginfo==1.10.0 \ + --hash=sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297 \ + --hash=sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097 + # via twine +pygments==2.18.0 \ + --hash=sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199 \ + --hash=sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a + # via + # readme-renderer + # rich +readme-renderer==44.0 \ + --hash=sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151 \ + --hash=sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1 + # via twine +requests==2.32.3 \ + --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ + --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 + # via + # -r publish-requirements.in + # requests-toolbelt + # twine +requests-toolbelt==1.0.0 \ + --hash=sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6 \ + --hash=sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06 + # via twine +rfc3986==2.0.0 \ + --hash=sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd \ + --hash=sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c + # via twine +rich==13.7.1 \ + --hash=sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222 \ + --hash=sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432 + # via twine +twine==5.1.1 \ + --hash=sha256:215dbe7b4b94c2c50a7315c0275d2258399280fbb7d04182c7e55e24b5f93997 \ + --hash=sha256:9aa0825139c02b3434d913545c7b847a21c835e11597f5255842d457da2322db + # via -r publish-requirements.in +urllib3==2.2.2 \ + --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \ + --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168 + # via + # requests + # twine +zipp==3.19.2 \ + --hash=sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19 \ + --hash=sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c + # via importlib-metadata diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20803c7..266092d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,21 +18,16 @@ concurrency: jobs: linux: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: fail-fast: false matrix: PYTHON: - - {VERSION: "3.11", TOXENV: "py311", TONGSUO: {VERSION: "8.3.1"}} - - {VERSION: "3.11", TOXENV: "flake", TONGSUO: {VERSION: "8.3.2"}} - - {VERSION: "3.11", TOXENV: "py311-randomorder", TONGSUO: {VERSION: "8.3.2"}} - - {VERSION: "3.11", TOXENV: "py311", TONGSUO: {VERSION: "8.3.2"}} - - {VERSION: "3.10", TOXENV: "py310", TONGSUO: {VERSION: "8.3.2"}} - - {VERSION: "3.9", TOXENV: "py39", TONGSUO: {VERSION: "8.3.2"}} - - {VERSION: "3.8", TOXENV: "py38", TONGSUO: {VERSION: "8.3.2"}} - - {VERSION: "3.7", TOXENV: "py37", TONGSUO: {VERSION: "8.3.2"}} - - {VERSION: "3.6", TOXENV: "py36", TONGSUO: {VERSION: "8.3.2"}} - name: "${{ matrix.PYTHON.TOXENV }} tongsuo-${{ matrix.PYTHON.TONGSUO.VERSION }} on Linux" + - {VERSION: "3.12", NOXSESSION: "flake"} + - {VERSION: "pypy-3.9", NOXSESSION: "tests-nocoverage", TONGSUO: "8.4.0"} + - {VERSION: "pypy-3.10", NOXSESSION: "tests-nocoverage", TONGSUO: "8.4.0"} + - {VERSION: "3.12", NOXSESSION: "tests-randomorder", TONGSUO: "8.4.0"} + - {VERSION: "3.12", NOXSESSION: "tests", TONGSUO: "8.4.0"} timeout-minutes: 15 steps: - uses: actions/checkout@v3.1.0 @@ -41,15 +36,17 @@ jobs: persist-credentials: false - name: Setup python id: setup-python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: ${{ matrix.PYTHON.VERSION }} - - run: python -m pip install tox coverage[toml] + cache: pip + cache-dependency-path: ci-constraints-requirements.txt - name: Set TONGSUO_HOME shell: bash run: echo "TONGSUO_HOME=${GITHUB_WORKSPACE}/tongsuo${VERSION}" >> $GITHUB_ENV env: - VERSION: ${{ matrix.PYTHON.TONGSUO.VERSION }} + VERSION: ${{ matrix.PYTHON.TONGSUO }} + if: matrix.PYTHON.TONGSUO - name: Build custom Tongsuo working-directory: ${{ runner.temp }} run: | @@ -61,24 +58,39 @@ jobs: make install_sw popd env: - VERSION: ${{ matrix.PYTHON.TONGSUO.VERSION }} - - name: Tests + VERSION: ${{ matrix.PYTHON.TONGSUO }} + if: matrix.PYTHON.TONGSUO + + - run: python -m pip install -c ci-constraints-requirements.txt 'nox' 'tomli; python_version < "3.11"' + - name: Create nox environment run: | - tox -vvv -r -- --color=yes + nox -v --install-only env: - TOXENV: ${{ matrix.PYTHON.TOXENV }} + NOXSESSION: ${{ matrix.PYTHON.NOXSESSION }} + - name: Tests + run: | + nox --no-install -- --color=yes + env: + NOXSESSION: ${{ matrix.PYTHON.NOXSESSION }} + COLUMNS: 80 - uses: ./.github/actions/upload-coverage macos: - runs-on: macos-12 + runs-on: ${{ matrix.RUNNER.OS }} strategy: fail-fast: false matrix: + RUNNER: + - {OS: 'macos-13', ARCH: 'x86_64'} + - {OS: 'macos-14', ARCH: 'arm64'} PYTHON: - - {VERSION: "3.6", TOXENV: "py36", TONGSUO: {VERSION: "8.3.2"}} - - {VERSION: "3.10", TOXENV: "py310", TONGSUO: {VERSION: "8.3.2"}} - name: "${{ matrix.PYTHON.TOXENV }} tongsuo-${{ matrix.PYTHON.TONGSUO.VERSION }} on macOS" + - {VERSION: "3.7", NOXSESSION: "tests-nocoverage"} + - {VERSION: "3.12", NOXSESSION: "tests"} + exclude: + # We only test latest Python on arm64. py37 won't work since there's no universal2 binary + - PYTHON: {VERSION: "3.7", NOXSESSION: "tests-nocoverage"} + RUNNER: {OS: 'macos-14', ARCH: 'arm64'} timeout-minutes: 15 steps: - uses: actions/checkout@v3.1.0 @@ -87,15 +99,18 @@ jobs: persist-credentials: false - name: Setup python id: setup-python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: ${{ matrix.PYTHON.VERSION }} - - run: python -m pip install tox coverage[toml] + cache: pip + cache-dependency-path: ci-constraints-requirements.txt + timeout-minutes: 3 + - run: python -m pip install -c ci-constraints-requirements.txt 'nox' 'tomli; python_version < "3.11"' - name: Set TONGSUO_HOME shell: bash run: echo "TONGSUO_HOME=${GITHUB_WORKSPACE}/tongsuo${VERSION}" >> $GITHUB_ENV env: - VERSION: ${{ matrix.PYTHON.TONGSUO.VERSION }} + VERSION: 8.4.0 - name: Build custom Tongsuo run: | wget "https://github.com/Tongsuo-Project/Tongsuo/archive/refs/tags/${VERSION}.tar.gz" @@ -106,14 +121,20 @@ jobs: make install_sw popd env: - VERSION: ${{ matrix.PYTHON.TONGSUO.VERSION }} + VERSION: 8.4.0 + - name: Build nox environment + run: | + nox -v --install-only + env: + NOXSESSION: ${{ matrix.PYTHON.NOXSESSION }} - name: Tests run: | # delete openssl/include installed in macos-12, fix cffi compile failed - rm -rf /usr/local/opt/openssl@1.1/include - tox -vvv -r -- --color=yes + # rm -rf /usr/local/opt/openssl@1.1/include + nox --no-install -- --color=yes env: - TOXENV: ${{ matrix.PYTHON.TOXENV }} + NOXSESSION: ${{ matrix.PYTHON.NOXSESSION }} + COLUMNS: 80 - uses: ./.github/actions/upload-coverage @@ -126,10 +147,9 @@ jobs: - { ARCH: 'x86', WINDOWS: 'win32', CONFIG: 'VC-WIN32' } - { ARCH: 'x64', WINDOWS: 'win64', CONFIG: 'VC-WIN64A' } PYTHON: - - {VERSION: "3.6", TOXENV: "py36", TONGSUO: {VERSION: "8.3.2"}} - - {VERSION: "3.11", TOXENV: "py311", TONGSUO: {VERSION: "8.3.2"}} + - {VERSION: "3.7", NOXSESSION: "tests-nocoverage", TONGSUO: "8.4.0"} + - {VERSION: "3.12", NOXSESSION: "tests", TONGSUO: "8.4.0"} JOB_NUMBER: [0, 1, 2] - name: "${{ matrix.PYTHON.TOXENV }} tongsuo-${{ matrix.PYTHON.TONGSUO.VERSION }} on ${{ matrix.WINDOWS.WINDOWS }} (part ${{ matrix.JOB_NUMBER }})" timeout-minutes: 15 steps: - uses: actions/checkout@v3.1.0 @@ -146,28 +166,31 @@ jobs: - uses: shogo82148/actions-setup-perl@v1 - name: Setup python id: setup-python - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: python-version: ${{ matrix.PYTHON.VERSION }} architecture: ${{ matrix.WINDOWS.ARCH }} - - run: python -m pip install tox coverage[toml] + cache: pip + cache-dependency-path: ci-constraints-requirements.txt + timeout-minutes: 3 + - run: python -m pip install -c ci-constraints-requirements.txt "nox" "tomli; python_version < '3.11'" - name: Export env shell: bash run: | echo "TONGSUO_VERSION=${VERSION}" >> $GITHUB_ENV echo "TONGSUO_HOME=${GITHUB_WORKSPACE}\tongsuo${VERSION}" >> $GITHUB_ENV env: - VERSION: ${{ matrix.PYTHON.TONGSUO.VERSION }} + VERSION: ${{ matrix.PYTHON.TONGSUO }} - name: Download Tongsuo source run: | wget "https://github.com/Tongsuo-Project/Tongsuo/archive/refs/tags/${env:TONGSUO_VERSION}.tar.gz" -OutFile "${env:TONGSUO_VERSION}.tar.gz" shell: powershell - - run: '"C:\Program Files\WinRAR\WinRAR.exe" -INUL x ${{ matrix.PYTHON.TONGSUO.VERSION }}.tar.gz' + - run: '"C:\Program Files\WinRAR\WinRAR.exe" -INUL x ${{ matrix.PYTHON.TONGSUO }}.tar.gz' shell: cmd - name: Build custom Tongsuo shell: cmd run: | - pushd "Tongsuo-${{ matrix.PYTHON.TONGSUO.VERSION }}" + pushd "Tongsuo-${{ matrix.PYTHON.TONGSUO }}" mkdir _build pushd _build perl ..\Configure no-makedepend no-shared ${{ matrix.WINDOWS.CONFIG }} --prefix=%TONGSUO_HOME% @@ -175,13 +198,16 @@ jobs: nmake install_sw popd popd + - name: Build nox environment + run: nox -v --install-only + env: + NOXSESSION: ${{ matrix.PYTHON.NOXSESSION }} - name: Tests run: | - python3 -c "import sys; print(sys.platform);" - tox -vvv -r -- --color=yes --num-shards=3 --shard-id=${{ matrix.JOB_NUMBER }} + nox --no-install -- --color=yes env: - TOXENV: ${{ matrix.PYTHON.TOXENV }} - + NOXSESSION: ${{ matrix.PYTHON.NOXSESSION }} + COLUMNS: 80 - uses: ./.github/actions/upload-coverage all-green: @@ -189,8 +215,9 @@ jobs: runs-on: ubuntu-latest needs: [linux, macos, windows] if: ${{ always() }} + timeout-minutes: 3 steps: - - uses: actions/checkout@v3.1.0 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 timeout-minutes: 3 with: persist-credentials: false @@ -200,16 +227,20 @@ jobs: jobs: ${{ toJSON(needs) }} - name: Setup python if: ${{ always() }} - uses: actions/setup-python@v4.3.0 + uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 with: - python-version: '3.10' - - run: pip install coverage[toml] + python-version: '3.12' + cache: pip + cache-dependency-path: ci-constraints-requirements.txt + timeout-minutes: 3 + - run: pip install -c ci-constraints-requirements.txt coverage[toml] if: ${{ always() }} - name: Download coverage data if: ${{ always() }} - uses: actions/download-artifact@v3.0.1 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: - name: coverage-data + pattern: coverage-data-* + merge-multiple: true - name: Combine coverage and fail if it's <85%. if: ${{ always() }} id: combinecoverage diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml new file mode 100644 index 0000000..9c99eff --- /dev/null +++ b/.github/workflows/pypi-publish.yml @@ -0,0 +1,99 @@ +name: Publish to PyPI + +on: + workflow_dispatch: + inputs: + run_id: + description: The run of wheel-builder to use for finding artifacts. + required: true + environment: + description: Which PyPI environment to upload to + required: true + type: choice + options: ["testpypi", "pypi"] + workflow_run: + workflows: ["Wheel Builder"] + types: [completed] + +env: + PUBLISH_REQUIREMENTS_PATH: .github/requirements/publish-requirements.txt + +permissions: + contents: read + +jobs: + publish: + runs-on: ubuntu-latest + # We're not actually verifying that the triggering push event was for a + # tag, because github doesn't expose enough information to do so. + # wheel-builder.yml currently only has push events for tags. + if: github.event_name == 'workflow_dispatch' || (github.event.workflow_run.event == 'push' && github.event.workflow_run.conclusion == 'success') + permissions: + id-token: "write" + attestations: "write" + steps: + - run: echo "$EVENT_CONTEXT" + env: + EVENT_CONTEXT: ${{ toJson(github.event) }} + - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + with: + python-version: "3.11" + - name: Get publish-requirements.txt from repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + sparse-checkout: | + ${{ env.PUBLISH_REQUIREMENTS_PATH }} + sparse-checkout-cone-mode: false + persist-credentials: false + - name: Install Python dependencies + run: pip install --require-hashes -r ${{ env.PUBLISH_REQUIREMENTS_PATH }} + + - uses: dawidd6/action-download-artifact@bf251b5aa9c2f7eeb574a96ee720e24f801b7c11 # v6 + with: + path: dist/ + run_id: ${{ github.event.inputs.run_id || github.event.workflow_run.id }} + + - run: | + echo "OIDC_AUDIENCE=pypi" >> $GITHUB_ENV + echo "PYPI_DOMAIN=pypi.org" >> $GITHUB_ENV + echo "TWINE_REPOSITORY=pypi" >> $GITHUB_ENV + echo "TWINE_USERNAME=__token__" >> $GITHUB_ENV + if: github.event_name == 'workflow_run' || (github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'pypi') + - run: | + echo "OIDC_AUDIENCE=testpypi" >> $GITHUB_ENV + echo "PYPI_DOMAIN=test.pypi.org" >> $GITHUB_ENV + echo "TWINE_REPOSITORY=testpypi" >> $GITHUB_ENV + echo "TWINE_USERNAME=__token__" >> $GITHUB_ENV + if: github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'testpypi' + + - run: | + import os + + import requests + + response = requests.get( + os.environ["ACTIONS_ID_TOKEN_REQUEST_URL"], + params={"audience": os.environ["OIDC_AUDIENCE"]}, + headers={"Authorization": f"bearer {os.environ['ACTIONS_ID_TOKEN_REQUEST_TOKEN']}"} + ) + response.raise_for_status() + token = response.json()["value"] + + response = requests.post(f"https://{os.environ['PYPI_DOMAIN']}/_/oidc/mint-token", json={"token": token}) + response.raise_for_status() + pypi_token = response.json()["token"] + + with open(os.environ["GITHUB_ENV"], "a") as f: + print(f"::add-mask::{pypi_token}") + f.write(f"TWINE_PASSWORD={pypi_token}\n") + shell: python + + - run: find dist/ -type f -name 'tongsuopy*' -print0 | xargs -0 twine upload --skip-existing + + # Do not perform attestation for things for TestPyPI. This is because + # there's nothing that would prevent a malicious PyPI from serving a + # signed TestPyPI asset in place of a release intended for PyPI. + - uses: actions/attest-build-provenance@5e9cb68e95676991667494a6a4e59b8a2f13e1d0 # v1.3.3 + with: + subject-path: 'dist/**/tongsuopy*' + if: env.TWINE_REPOSITORY == 'pypi' diff --git a/.github/workflows/release-wheel.yml b/.github/workflows/wheel-builder.yml similarity index 57% rename from .github/workflows/release-wheel.yml rename to .github/workflows/wheel-builder.yml index fee3842..051cbb2 100644 --- a/.github/workflows/release-wheel.yml +++ b/.github/workflows/wheel-builder.yml @@ -6,12 +6,18 @@ on: inputs: version: description: The version to build - + # Do not add any non-tag push events without updating pypi-publish.yml. If + # you do, it'll upload wheels to PyPI. + push: + tags: + - '*.*' + - '*.*.*' env: PYPI_NAME: tongsuopy - TONGSUO_VERSION: 8.3.2 + TONGSUO_VERSION: 8.4.0 INNER_WORKSPACE: /workspace INNER_BUILD_DIR: /build + BUILD_REQUIREMENTS_PATH: .github/requirements/build-requirements.txt jobs: sdist: @@ -26,9 +32,9 @@ jobs: - run: python -m venv .venv - name: Install Python dependencies - run: .venv/bin/pip install -U pip cffi + run: .venv/bin/pip install -U pip build - name: Make sdist - run: .venv/bin/python setup.py sdist + run: .venv/bin/python -m build --sdist - uses: actions/upload-artifact@v3.1.1 with: name: "tongsuopy-sdist" @@ -46,37 +52,52 @@ jobs: fail-fast: false matrix: PYTHON: - - { VERSION: "cp36-cp36m", ABI_VERSION: 'cp36' } - - { VERSION: "pp38-pypy38_pp73" } + - { VERSION: "cp311-cp311", ABI_VERSION: 'py37' } + - { VERSION: "cp311-cp311", ABI_VERSION: 'py39' } - { VERSION: "pp39-pypy39_pp73" } + - { VERSION: "pp310-pypy310_pp73" } MANYLINUX: - { NAME: "manylinux2014_x86_64", CONTAINER: "tongsuopy-manylinux2014:x86_64" } - - { NAME: "manylinux_2_24_x86_64", CONTAINER: "tongsuopy-manylinux_2_24:x86_64"} - { NAME: "manylinux_2_28_x86_64", CONTAINER: "tongsuopy-manylinux_2_28:x86_64"} - - { NAME: "musllinux_1_1_x86_64", CONTAINER: "tongsuopy-musllinux_1_1:x86_64"} + - { NAME: "musllinux_1_2_x86_64", CONTAINER: "tongsuopy-musllinux_1_2:x86_64"} exclude: # There are no readily available PyPy distributions - - PYTHON: { VERSION: "pp38-pypy38_pp73" } - MANYLINUX: { NAME: "musllinux_1_1_x86_64", CONTAINER: "tongsuopy-musllinux_1_1:x86_64"} - PYTHON: { VERSION: "pp39-pypy39_pp73" } - MANYLINUX: { NAME: "musllinux_1_1_x86_64", CONTAINER: "tongsuopy-musllinux_1_1:x86_64"} + MANYLINUX: { NAME: "musllinux_1_2_x86_64", CONTAINER: "tongsuopy-musllinux_1_2:x86_64" } + - PYTHON: { VERSION: "pp310-pypy310_pp73" } + MANYLINUX: { NAME: "musllinux_1_2_x86_64", CONTAINER: "tongsuopy-musllinux_1_2:x86_64" } + + # We also don't build pypy wheels for anything except the latest manylinux + - PYTHON: { VERSION: "pp39-pypy39_pp73" } + MANYLINUX: { NAME: "manylinux2014_x86_64", CONTAINER: "tongsuopy-manylinux2014:x86_64" } + - PYTHON: { VERSION: "pp310-pypy310_pp73" } + MANYLINUX: { NAME: "manylinux2014_x86_64", CONTAINER: "tongsuopy-manylinux2014:x86_64" } name: "${{ matrix.PYTHON.VERSION }} for ${{ matrix.MANYLINUX.NAME }}" steps: - - uses: actions/download-artifact@v3.0.1 + - name: Get build-requirements.txt from repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: - name: tongsuopy-sdist + # The tag to build or the tag received by the tag event + ref: ${{ github.event.inputs.version || github.ref }} + persist-credentials: false + sparse-checkout: | + ${{ env.BUILD_REQUIREMENTS_PATH }} + sparse-checkout-cone-mode: false + - run: /opt/python/${{ matrix.PYTHON.VERSION }}/bin/python -m venv .venv - name: Install Python dependencies - run: .venv/bin/pip install -U pip wheel cffi - - run: tar zxvf tongsuopy*.tar.gz && rm tongsuopy*.tar.gz && mkdir tmpwheelhouse + run: .venv/bin/pip install --require-hashes -r ${{ env.BUILD_REQUIREMENTS_PATH }} + + - uses: actions/download-artifact@v4.1.8 + with: + name: tongsuopy-sdist + - run: mkdir tmpwheelhouse - name: Build the wheel run: | if [ -n "${{ matrix.PYTHON.ABI_VERSION }}" ]; then - PY_LIMITED_API="--py-limited-api=${{ matrix.PYTHON.ABI_VERSION }}" + PY_LIMITED_API="--config-settings=build-args=--features=pyo3/abi3-${{ matrix.PYTHON.ABI_VERSION }} --no-build-isolation" fi - cd tongsuopy* - ../.venv/bin/python setup.py bdist_wheel $PY_LIMITED_API && mv dist/tongsuopy*.whl ../tmpwheelhouse - + .venv/bin/python -m pip wheel -v --no-deps $PY_LIMITED_API tongsuopy*.tar.gz -w dist/ && mv dist/tongsuopy*.whl tmpwheelhouse - run: auditwheel repair --plat ${{ matrix.MANYLINUX.NAME }} tmpwheelhouse/tongsuopy*.whl -w wheelhouse/ - run: unzip wheelhouse/*.whl -d execstack.check - run: | @@ -87,17 +108,15 @@ jobs: else exit 0 fi - - run: .venv/bin/pip install ${{ env.PYPI_NAME }} --no-index -f wheelhouse/ + - run: .venv/bin/pip install tongsuopy --no-index -f wheelhouse/ - run: | .venv/bin/python -c "from tongsuopy.backends.tongsuo.backend import backend;print('Loaded: ' + backend.openssl_version_text());print('Linked Against: ' + backend._ffi.string(backend._lib.OPENSSL_VERSION_TEXT).decode('ascii'))" - run: mkdir tongsuopy-wheelhouse - run: mv wheelhouse/tongsuopy*.whl tongsuopy-wheelhouse/ - - name: Publish tongsuopy to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 with: - password: ${{ secrets.PYPI_API_TOKEN }} - packages_dir: tongsuopy-wheelhouse/ - print_hash: true + name: "tongsuopy-${{ github.event.inputs.version }}-${{ matrix.MANYLINUX.NAME }}-${{ matrix.PYTHON.VERSION }}-${{ matrix.PYTHON.ABI_VERSION }}" + path: tongsuopy-wheelhouse/ manylinux-aarch64: needs: [ sdist ] @@ -106,25 +125,39 @@ jobs: fail-fast: false matrix: PYTHON: - - { VERSION: "cp36-cp36m", ABI_VERSION: 'cp36' } - - { VERSION: "pp38-pypy38_pp73" } + - { VERSION: "cp311-cp311", ABI_VERSION: 'py37' } + - { VERSION: "cp311-cp311", ABI_VERSION: 'py39' } - { VERSION: "pp39-pypy39_pp73" } + - { VERSION: "pp310-pypy310_pp73" } MANYLINUX: - { NAME: "manylinux2014_aarch64", CONTAINER: "tongsuopy-manylinux2014:aarch64" } - - { NAME: "manylinux_2_24_aarch64", CONTAINER: "tongsuopy-manylinux_2_24:aarch64" } - - { NAME: "manylinux_2_28_aarch64", CONTAINER: "tongsuopy-manylinux_2_28:aarch64" } - - { NAME: "musllinux_1_1_aarch64", CONTAINER: "tongsuopy-musllinux_1_1:aarch64" } + - { NAME: "manylinux_2_28_aarch64", CONTAINER: "tongsuopy-manylinux_2_28:aarch64"} + - { NAME: "musllinux_1_2_x86_64", CONTAINER: "tongsuopy-musllinux_1_2:aarch64"} exclude: # There are no readily available PyPy distributions - - PYTHON: { VERSION: "pp38-pypy38_pp73" } - MANYLINUX: { NAME: "musllinux_1_1_aarch64", CONTAINER: "tongsuopy-musllinux_1_1:aarch64" } - PYTHON: { VERSION: "pp39-pypy39_pp73" } - MANYLINUX: { NAME: "musllinux_1_1_aarch64", CONTAINER: "tongsuopy-musllinux_1_1:aarch64" } + MANYLINUX: { NAME: "musllinux_1_2_aarch64", CONTAINER: "tongsuopy-musllinux_1_2:aarch64" } + - PYTHON: { VERSION: "pp310-pypy310_pp73" } + MANYLINUX: { NAME: "musllinux_1_2_aarch64", CONTAINER: "tongsuopy-musllinux_1_2:aarch64" } + # We also don't build pypy wheels for anything except the latest manylinux + - PYTHON: { VERSION: "pp39-pypy39_pp73" } + MANYLINUX: { NAME: "manylinux2014_aarch64", CONTAINER: "tongsuopy-manylinux2014:aarch64" } + - PYTHON: { VERSION: "pp310-pypy310_pp73" } + MANYLINUX: { NAME: "manylinux2014_aarch64", CONTAINER: "tongsuopy-manylinux2014:aarch64" } name: "${{ matrix.PYTHON.VERSION }} for ${{ matrix.MANYLINUX.NAME }}" steps: - - uses: actions/download-artifact@v3.0.1 + - name: Get build-requirements.txt from repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + # The tag to build or the tag received by the tag event + ref: ${{ github.event.inputs.version || github.ref }} + persist-credentials: false + sparse-checkout: | + ${{ env.BUILD_REQUIREMENTS_PATH }} + sparse-checkout-cone-mode: false + - uses: actions/download-artifact@v4.1.8 with: name: tongsuopy-sdist path: ${{ github.workspace }} @@ -156,16 +189,15 @@ jobs: run: | cd ${INNER_BUILD_DIR} /opt/python/${PYTHON_VERSION}/bin/python -m venv .venv - .venv/bin/pip install -U pip wheel cffi + .venv/bin/pip install --require-hashes -r ${{ env.INNER_WORKSPACE }}/${{ env.BUILD_REQUIREMENTS_PATH }} cd ${INNER_WORKSPACE} - tar zxvf tongsuopy*.tar.gz && rm tongsuopy*.tar.gz && mkdir tmpwheelhouse + mkdir tmpwheelhouse PY_LIMITED_API="" if [ -n "${PYTHON_ABI_VERSION}" ]; then - PY_LIMITED_API="--py-limited-api=${PYTHON_ABI_VERSION}" + PY_LIMITED_API="--config-settings=build-args=--features=pyo3/abi3-${PYTHON_ABI_VERSION} --no-build-isolation" fi - cd tongsuopy* - ${INNER_BUILD_DIR}/.venv/bin/python setup.py bdist_wheel ${PY_LIMITED_API} && mv dist/tongsuopy*.whl ../tmpwheelhouse + ${INNER_BUILD_DIR}/.venv/bin/python -m pip wheel -v --no-deps ${PY_LIMITED_API} tongsuopy*.tar.gz -w dist/ && mv dist/tongsuopy*.whl tmpwheelhouse cd ${INNER_WORKSPACE} auditwheel repair --plat ${MANYLINUX_NAME} tmpwheelhouse/tongsuopy*.whl -w wheelhouse/ @@ -178,30 +210,28 @@ jobs: fi cd ${INNER_WORKSPACE} - ${INNER_BUILD_DIR}/.venv/bin/pip install ${{ env.PYPI_NAME }} --no-index -f wheelhouse/ + ${INNER_BUILD_DIR}/.venv/bin/pip install tongsuopy --no-index -f wheelhouse/ ${INNER_BUILD_DIR}/.venv/bin/python -c "from tongsuopy.backends.tongsuo.backend import backend;print('Loaded: ' + backend.openssl_version_text());print('Linked Against: ' + backend._ffi.string(backend._lib.OPENSSL_VERSION_TEXT).decode('ascii'))" mkdir tongsuopy-wheelhouse mv wheelhouse/tongsuopy*.whl tongsuopy-wheelhouse/ - - name: Publish tongsuopy to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 with: - password: ${{ secrets.PYPI_API_TOKEN }} - packages_dir: ${{ github.workspace }}/tongsuopy-wheelhouse/ - print_hash: true + name: "tongsuopy-${{ github.event.inputs.version }}-${{ matrix.MANYLINUX.NAME }}-${{ matrix.PYTHON.VERSION }}-${{ matrix.PYTHON.ABI_VERSION }}" + path: tongsuopy-wheelhouse/ macos: needs: [sdist] - runs-on: macos-12 + runs-on: macos-13 strategy: fail-fast: false matrix: PYTHON: - - VERSION: '3.10' - ABI_VERSION: 'cp36' + - VERSION: '3.11' + ABI_VERSION: 'py37' # Despite the name, this is built for the macOS 11 SDK on arm64 and 10.9+ on intel - DOWNLOAD_URL: 'https://www.python.org/ftp/python/3.10.0/python-3.10.0post2-macos11.pkg' - BIN_PATH: '/Library/Frameworks/Python.framework/Versions/3.10/bin/python3' + DOWNLOAD_URL: 'https://www.python.org/ftp/python/3.11.3/python-3.11.3-macos11.pkg' + BIN_PATH: '/Library/Frameworks/Python.framework/Versions/3.11/bin/python3' DEPLOYMENT_TARGET: '10.12' # This archflags is default, but let's be explicit ARCHFLAGS: '-arch x86_64 -arch arm64' @@ -209,28 +239,35 @@ jobs: # This will change in the future as we change the base Python we # build against _PYTHON_HOST_PLATFORM: 'macosx-10.9-universal2' - - VERSION: '3.10' - ABI_VERSION: 'cp36' - DOWNLOAD_URL: 'https://www.python.org/ftp/python/3.10.0/python-3.10.0post2-macos11.pkg' - BIN_PATH: '/Library/Frameworks/Python.framework/Versions/3.10/bin/python3' + - VERSION: '3.11' + ABI_VERSION: 'py39' + DOWNLOAD_URL: 'https://www.python.org/ftp/python/3.11.3/python-3.11.3-macos11.pkg' + BIN_PATH: '/Library/Frameworks/Python.framework/Versions/3.11/bin/python3' DEPLOYMENT_TARGET: '10.12' - # We continue to build a non-universal2 for a bit to see metrics on - # download counts (this is a proxy for pip version since universal2 - # requires a 21.x pip) - ARCHFLAGS: '-arch x86_64' - _PYTHON_HOST_PLATFORM: 'macosx-10.9-x86_64' - - VERSION: 'pypy-3.8' + # This archflags is default, but let's be explicit + ARCHFLAGS: '-arch x86_64 -arch arm64' + _PYTHON_HOST_PLATFORM: 'macosx-10.9-universal2' + - VERSION: 'pypy-3.9' BIN_PATH: 'pypy3' DEPLOYMENT_TARGET: '10.12' _PYTHON_HOST_PLATFORM: 'macosx-10.9-x86_64' ARCHFLAGS: '-arch x86_64' - - VERSION: 'pypy-3.9' + - VERSION: 'pypy-3.10' BIN_PATH: 'pypy3' DEPLOYMENT_TARGET: '10.12' _PYTHON_HOST_PLATFORM: 'macosx-10.9-x86_64' ARCHFLAGS: '-arch x86_64' name: "${{ matrix.PYTHON.VERSION }} ABI ${{ matrix.PYTHON.ABI_VERSION }} macOS ${{ matrix.PYTHON.ARCHFLAGS }}" steps: + - name: Get build-requirements.txt from repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + # The tag to build or the tag received by the tag event + ref: ${{ github.event.inputs.version || github.ref }} + persist-credentials: false + sparse-checkout: | + ${{ env.BUILD_REQUIREMENTS_PATH }} + sparse-checkout-cone-mode: false - uses: actions/download-artifact@v3.0.1 with: name: tongsuopy-sdist @@ -256,22 +293,25 @@ jobs: wget "https://github.com/Tongsuo-Project/Tongsuo/archive/refs/tags/${TONGSUO_VERSION}.tar.gz" tar zxf "${TONGSUO_VERSION}.tar.gz" cd "Tongsuo-${TONGSUO_VERSION}" - ./config no-shared enable-ntls --release --prefix=${TONGSUO_HOME} + ./config no-shared enable-ntls enable-trace enable-ssl-trace --release --prefix=${TONGSUO_HOME} make -s -j4 make install_sw cd - - run: ${{ matrix.PYTHON.BIN_PATH }} -m venv venv - - run: venv/bin/pip install -U pip wheel cffi twine - - run: tar zxvf tongsuopy*.tar.gz && mkdir wheelhouse + - name: Install Python dependencies + run: venv/bin/pip install --require-hashes -r ${{ env.BUILD_REQUIREMENTS_PATH }} + - run: mkdir wheelhouse - name: Build the wheel run: | - cd tongsuopy* - ../venv/bin/python setup.py bdist_wheel --py-limited-api=${{ matrix.PYTHON.ABI_VERSION }} && mv dist/tongsuopy*.whl ../wheelhouse + if [ -n "${{ matrix.PYTHON.ABI_VERSION }}" ]; then + PY_LIMITED_API="--config-settings=build-args=--features=pyo3/abi3-${{ matrix.PYTHON.ABI_VERSION }} --no-build-isolation" + fi + venv/bin/python -m pip wheel -v --no-deps $PY_LIMITED_API tongsuopy*.tar.gz -w dist/ && mv dist/tongsuopy*.whl wheelhouse env: MACOSX_DEPLOYMENT_TARGET: ${{ matrix.PYTHON.DEPLOYMENT_TARGET }} ARCHFLAGS: ${{ matrix.PYTHON.ARCHFLAGS }} _PYTHON_HOST_PLATFORM: ${{ matrix.PYTHON._PYTHON_HOST_PLATFORM }} - - run: venv/bin/pip install -f wheelhouse --no-index ${{ env.PYPI_NAME }} + - run: venv/bin/pip install -f wheelhouse --no-index tonguopy - name: Show the wheel's minimum macOS SDK and architectures run: | find venv/lib/*/site-packages/tongsuopy/backends -name '*.so' -exec vtool -show {} \; @@ -280,12 +320,12 @@ jobs: - run: mkdir tongsuopy-wheelhouse - run: mv wheelhouse/tongsuopy*.whl tongsuopy-wheelhouse/ - - name: Publish tongsuopy to PyPI - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} - run: | - venv/bin/twine upload --repository pypi tongsuopy-wheelhouse/* + - run: | + echo "TONGSUOPY_WHEEL_NAME=$(basename $(ls tongsuopy-wheelhouse/tongsuopy*.whl))" >> $GITHUB_ENV + - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + with: + name: "${{ env.TONGSUOPY_WHEEL_NAME }}" + path: tongsuopy-wheelhouse/ windows: needs: [sdist] @@ -297,17 +337,27 @@ jobs: - {ARCH: 'x86', WINDOWS: 'win32', CONFIG: 'VC-WIN32'} - {ARCH: 'x64', WINDOWS: 'win64', CONFIG: 'VC-WIN64A'} PYTHON: - - {VERSION: "3.8", "ABI_VERSION": "cp36"} - - {VERSION: "pypy-3.8"} + - {VERSION: "3.11", "ABI_VERSION": "py37"} + - {VERSION: "3.11", "ABI_VERSION": "py39"} - {VERSION: "pypy-3.9"} + - {VERSION: "pypy-3.10"} exclude: # We need to exclude the below configuration because there is no 32-bit pypy3 - - WINDOWS: {ARCH: 'x86', WINDOWS: 'win32', CONFIG: 'VC-WIN32'} - PYTHON: {VERSION: "pypy-3.8"} - WINDOWS: {ARCH: 'x86', WINDOWS: 'win32', CONFIG: 'VC-WIN32'} PYTHON: {VERSION: "pypy-3.9"} + - WINDOWS: {ARCH: 'x86', WINDOWS: 'win32', CONFIG: 'VC-WIN32'} + PYTHON: {VERSION: "pypy-3.10"} name: "${{ matrix.PYTHON.VERSION }} ${{ matrix.WINDOWS.WINDOWS }} ${{ matrix.PYTHON.ABI_VERSION }}" steps: + - name: Get build-requirements.txt from repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + # The tag to build or the tag received by the tag event + ref: ${{ github.event.inputs.version || github.ref }} + persist-credentials: false + sparse-checkout: | + ${{ env.BUILD_REQUIREMENTS_PATH }} + sparse-checkout-cone-mode: false - uses: actions/download-artifact@v3.0.1 with: name: tongsuopy-sdist @@ -340,25 +390,29 @@ jobs: pushd "Tongsuo-${{ env.TONGSUO_VERSION }}" mkdir _build pushd _build - perl ..\Configure no-makedepend no-shared enable-ntls ${{ matrix.WINDOWS.CONFIG }} --prefix=%TONGSUO_HOME% + perl ..\Configure no-makedepend no-shared enable-ntls enable-trace enable-ssl-trace ${{ matrix.WINDOWS.CONFIG }} --prefix=%TONGSUO_HOME% nmake /S nmake install_sw popd popd - - run: python -m pip install -U pip wheel twine - - run: python -m pip install cffi - - run: tar zxvf tongsuopy*.tar.gz && mkdir wheelhouse + - name: Install Python dependencies + run: python -m pip install --require-hashes -r ${{ env.BUILD_REQUIREMENTS_PATH }} + - run: mkdir wheelhouse shell: bash - - run: cd tongsuopy* && python setup.py bdist_wheel --py-limited-api=${{ matrix.PYTHON.ABI_VERSION }} && mv dist/tongsuopy*.whl ../wheelhouse - - run: pip install -f wheelhouse --no-index ${{ env.PYPI_NAME }} - - name: Print the OpenSSL we built and linked against + - run: | + if [ -n "${{ matrix.PYTHON.ABI_VERSION }}" ]; then + PY_LIMITED_API="--config-settings=build-args=--features=pyo3/abi3-${{ matrix.PYTHON.ABI_VERSION }} --no-build-isolation" + fi + + python -m pip wheel -v --no-deps tongsuopy*.tar.gz $PY_LIMITED_API -w dist/ && mv dist/tongsuopy*.whl wheelhouse/ + shell: bash + - run: pip install -f wheelhouse --no-index tongsuopy + - name: Print the Tongsuo we built and linked against run: | python -c "from tongsuopy.backends.tongsuo.backend import backend;print('Loaded: ' + backend.openssl_version_text());print('Linked Against: ' + backend._ffi.string(backend._lib.OPENSSL_VERSION_TEXT).decode('ascii'))" - run: mkdir tongsuopy-wheelhouse - run: move wheelhouse\tongsuopy*.whl tongsuopy-wheelhouse\ - - name: Publish tongsuopy to PyPI - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} - run: | - twine upload --repository pypi tongsuopy-wheelhouse\* + - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + with: + name: "tongsuopy-${{ github.event.inputs.version }}-${{ matrix.WINDOWS.WINDOWS }}-${{ matrix.PYTHON.VERSION }}-${{ matrix.PYTHON.ABI_VERSION }}" + path: tongsuopy-wheelhouse\ diff --git a/LICENSE b/LICENSE index 62589ed..6f25184 100644 --- a/LICENSE +++ b/LICENSE @@ -1,202 +1,3 @@ - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to tongsuo-python-sdk are +made under the terms of *both* these licenses. diff --git a/LICENSE.APACHE b/LICENSE.APACHE new file mode 100644 index 0000000..62589ed --- /dev/null +++ b/LICENSE.APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE.BSD b/LICENSE.BSD new file mode 100644 index 0000000..ec1a29d --- /dev/null +++ b/LICENSE.BSD @@ -0,0 +1,27 @@ +Copyright (c) Individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of PyCA Cryptography nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MANIFEST.in b/MANIFEST.in index 037e784..e939bfb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,8 @@ include CHANGES.md include LICENSE +include LICENSE.APACHE +include LICENSE.BSD + include README.md include pyproject.toml diff --git a/ci-constraints-requirements.txt b/ci-constraints-requirements.txt new file mode 100644 index 0000000..b0b9c37 --- /dev/null +++ b/ci-constraints-requirements.txt @@ -0,0 +1,83 @@ +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# pip-compile --extra=nox --extra=pep8test --extra=sdist --extra=test --extra=test-randomorder --strip-extras --unsafe-package=cffi --unsafe-package=pycparser --unsafe-package=setuptools pyproject.toml +# +argcomplete + # via nox +attrs==24.1.0 + # via pytest-subtests +build==1.2.1 + # via + # check-sdist + # tongsuopy (pyproject.toml) +certifi==2024.7.4 + # via tongsuopy (pyproject.toml) +check-sdist==0.1.3 + # via tongsuopy (pyproject.toml) +click==8.1.7 + # via tongsuopy (pyproject.toml) +colorlog==6.8.2 + # via nox +coverage + # via pytest-cov +distlib==0.3.8 + # via virtualenv +execnet + # via pytest-xdist +filelock + # via virtualenv +iniconfig==2.0.0 + # via pytest +mypy==1.11.1 + # via tongsuopy (pyproject.toml) +mypy-extensions==1.0.0 + # via mypy +nox==2024.4.15 + # via tongsuopy (pyproject.toml) +packaging + # via + # build + # nox + # pytest +pathspec==0.12.1 + # via check-sdist +platformdirs + # via virtualenv +pluggy + # via pytest +pretend==1.0.9 + # via tongsuopy (pyproject.toml) +py-cpuinfo==9.0.0 + # via pytest-benchmark +pyproject-hooks==1.1.0 + # via build +pytest + # via + # pytest-benchmark + # pytest-cov + # pytest-randomly + # pytest-subtests + # pytest-xdist + # tongsuopy (pyproject.toml) +pytest-benchmark==4.0.0 + # via tongsuopy (pyproject.toml) +pytest-cov + # via tongsuopy (pyproject.toml) +pytest-randomly==3.15.0 + # via tongsuopy (pyproject.toml) +pytest-subtests==0.13.1 + # via tongsuopy (pyproject.toml) +pytest-xdist + # via tongsuopy (pyproject.toml) +ruff==0.5.6 + # via tongsuopy (pyproject.toml) +typing-extensions + # via mypy +virtualenv==20.26.3 + # via nox + +# The following packages are considered to be unsafe in a requirements file: +# cffi +# pycparser diff --git a/demos/sm2_encrypt_decrypt.py b/demos/sm2_encrypt_decrypt.py index d32569f..a3f7119 100644 --- a/demos/sm2_encrypt_decrypt.py +++ b/demos/sm2_encrypt_decrypt.py @@ -5,19 +5,18 @@ from tongsuopy.crypto import hashes from tongsuopy.crypto.asymciphers import ec - -msg = b"message digest" -d = b"3945208F7B2144B13F36E38AC6D39F95889393692860B51A42FB81EF4DF7C5B8" -Qx = b"09F9DF311E5421A150DD7D161E4BC5C672179FAD1833FC076BB08FF356F35020" -Qy = b"CCEA490CE26775A52DC6EA718CC1AA600AED05FBF35E084A6632F6072DA9AD13" +msg = "message digest" +d = "3945208F7B2144B13F36E38AC6D39F95889393692860B51A42FB81EF4DF7C5B8" +Qx = "09F9DF311E5421A150DD7D161E4BC5C672179FAD1833FC076BB08FF356F35020" +Qy = "CCEA490CE26775A52DC6EA718CC1AA600AED05FBF35E084A6632F6072DA9AD13" key = ec.EllipticCurvePrivateNumbers( int(d, 16), ec.EllipticCurvePublicNumbers(int(Qx, 16), int(Qy, 16), ec.SM2()), ).private_key() -signature = key.sign(msg, ec.ECDSA(hashes.SM3())) +signature = key.sign(msg.encode(), ec.ECDSA(hashes.SM3())) pubkey = key.public_key() -ciphertext = pubkey.encrypt(msg) +ciphertext = pubkey.encrypt(msg.encode()) decrypt_text = key.decrypt(ciphertext) -assert decrypt_text == msg +assert decrypt_text == msg.encode() diff --git a/demos/sm2_encrypt_decrypt_from_pem.py b/demos/sm2_encrypt_decrypt_from_pem.py index d2b1e4d..296ac95 100644 --- a/demos/sm2_encrypt_decrypt_from_pem.py +++ b/demos/sm2_encrypt_decrypt_from_pem.py @@ -5,7 +5,7 @@ from tongsuopy.crypto import serialization from tongsuopy.crypto.asymciphers import ec -msg = b"hello" +msg = "hello" key = ec.generate_private_key(ec.SM2()) pem = key.public_key().public_bytes( @@ -14,6 +14,6 @@ ) pubkey = serialization.load_pem_public_key(pem) -ciphertext = pubkey.encrypt(msg) +ciphertext = pubkey.encrypt(msg.encode()) decrypt_text = key.decrypt(ciphertext) -assert decrypt_text == msg +assert decrypt_text == msg.encode() diff --git a/demos/sm2_sign.py b/demos/sm2_sign.py index b0a595a..16de2a3 100644 --- a/demos/sm2_sign.py +++ b/demos/sm2_sign.py @@ -5,17 +5,16 @@ from tongsuopy.crypto import hashes from tongsuopy.crypto.asymciphers import ec - -msg = b"message digest" -d = b"3945208F7B2144B13F36E38AC6D39F95889393692860B51A42FB81EF4DF7C5B8" -Qx = b"09F9DF311E5421A150DD7D161E4BC5C672179FAD1833FC076BB08FF356F35020" -Qy = b"CCEA490CE26775A52DC6EA718CC1AA600AED05FBF35E084A6632F6072DA9AD13" +msg = "message digest" +d = "3945208F7B2144B13F36E38AC6D39F95889393692860B51A42FB81EF4DF7C5B8" +Qx = "09F9DF311E5421A150DD7D161E4BC5C672179FAD1833FC076BB08FF356F35020" +Qy = "CCEA490CE26775A52DC6EA718CC1AA600AED05FBF35E084A6632F6072DA9AD13" key = ec.EllipticCurvePrivateNumbers( int(d, 16), ec.EllipticCurvePublicNumbers(int(Qx, 16), int(Qy, 16), ec.SM2()), ).private_key() -signature = key.sign(msg, ec.ECDSA(hashes.SM3())) +signature = key.sign(msg.encode(), ec.ECDSA(hashes.SM3())) pubkey = key.public_key() -pubkey.verify(signature, msg, ec.ECDSA(hashes.SM3())) +pubkey.verify(signature, msg.encode(), ec.ECDSA(hashes.SM3())) diff --git a/demos/sm2_sign_verify.py b/demos/sm2_sign_verify.py index 6f9745f..5dc9d3a 100644 --- a/demos/sm2_sign_verify.py +++ b/demos/sm2_sign_verify.py @@ -5,7 +5,7 @@ from tongsuopy.crypto import hashes, serialization from tongsuopy.crypto.asymciphers import ec -msg = b"hello" +msg = "hello" key = ec.generate_private_key(ec.SM2()) pem = key.public_key().public_bytes( @@ -14,5 +14,5 @@ ) pubkey = serialization.load_pem_public_key(pem) -signature = key.sign(msg, ec.ECDSA(hashes.SM3())) -pubkey.verify(signature, msg, ec.ECDSA(hashes.SM3())) +signature = key.sign(msg.encode(), ec.ECDSA(hashes.SM3())) +pubkey.verify(signature, msg.encode(), ec.ECDSA(hashes.SM3())) diff --git a/demos/sm2_verify.py b/demos/sm2_verify.py index 3d9660f..f63d433 100644 --- a/demos/sm2_verify.py +++ b/demos/sm2_verify.py @@ -6,16 +6,15 @@ from tongsuopy.crypto.asymciphers import ec from tongsuopy.crypto.asymciphers.utils import encode_dss_signature - -msg = b"message digest" -Qx = b"09F9DF311E5421A150DD7D161E4BC5C672179FAD1833FC076BB08FF356F35020" -Qy = b"CCEA490CE26775A52DC6EA718CC1AA600AED05FBF35E084A6632F6072DA9AD13" -R = b"F5A03B0648D2C4630EEAC513E1BB81A15944DA3827D5B74143AC7EACEEE720B3" -S = b"B1B6AA29DF212FD8763182BC0D421CA1BB9038FD1F7F42D4840B69C485BBC1AA" +msg = "message digest" +Qx = "09F9DF311E5421A150DD7D161E4BC5C672179FAD1833FC076BB08FF356F35020" +Qy = "CCEA490CE26775A52DC6EA718CC1AA600AED05FBF35E084A6632F6072DA9AD13" +R = "F5A03B0648D2C4630EEAC513E1BB81A15944DA3827D5B74143AC7EACEEE720B3" +S = "B1B6AA29DF212FD8763182BC0D421CA1BB9038FD1F7F42D4840B69C485BBC1AA" signature = encode_dss_signature(int(R, 16), int(S, 16)) pubkey = ec.EllipticCurvePublicNumbers( int(Qx, 16), int(Qy, 16), ec.SM2() ).public_key() -pubkey.verify(signature, msg, ec.ECDSA(hashes.SM3())) +pubkey.verify(signature, msg.encode(), ec.ECDSA(hashes.SM3())) diff --git a/demos/sm3.py b/demos/sm3.py index 5603314..ddbd331 100644 --- a/demos/sm3.py +++ b/demos/sm3.py @@ -6,11 +6,13 @@ from tongsuopy.crypto import hashes +data = "abc" +expected = "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0" + h = hashes.Hash(hashes.SM3()) -h.update(b"abc") +h.update(data.encode()) res = h.finalize() -assert ( - binascii.hexlify(res) - == b"66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0" -) +assert binascii.hexlify(res).decode() == expected + +print("SM3({})={}".format(data, binascii.hexlify(res).decode("utf-8"))) diff --git a/demos/sm4_cbc.py b/demos/sm4_cbc.py index 81ef8cb..a151771 100644 --- a/demos/sm4_cbc.py +++ b/demos/sm4_cbc.py @@ -6,11 +6,13 @@ from tongsuopy.crypto.ciphers import Cipher, algorithms, modes -key = b"0123456789ABCDEFFEDCBA9876543210" -iv = b"0123456789ABCDEFFEDCBA9876543210" -plaintext = b"0123456789ABCDEFFEDCBA98765432100123456789ABCDEFFEDCBA9876543210" -ciphertext = ( - b"2677F46B09C122CC975533105BD4A22AF6125F7275CE552C3A2BBCF533DE8A3B" +key = "0123456789ABCDEFFEDCBA9876543210" +iv = "0123456789ABCDEFFEDCBA9876543210" +plaintext = "0123456789ABCDEFFEDCBA98765432100123456789ABCDEFFEDCBA9876543210" +ciphertext = "2677F46B09C122CC975533105BD4A22AF6125F7275CE552C3A2BBCF533DE8A3B" + +print( + f"SM4-CBC\nkey={key}\niv={iv}\nplaintext={plaintext}\nciphertext={ciphertext}" ) c = Cipher( @@ -21,10 +23,10 @@ actual_ciphertext = enc.update(binascii.unhexlify(plaintext)) actual_ciphertext += enc.finalize() -assert binascii.hexlify(actual_ciphertext).upper() == ciphertext +assert binascii.hexlify(actual_ciphertext).decode().upper() == ciphertext dec = c.decryptor() actual_plaintext = dec.update(binascii.unhexlify(ciphertext)) actual_plaintext += dec.finalize() -assert binascii.hexlify(actual_plaintext).upper() == plaintext +assert binascii.hexlify(actual_plaintext).decode().upper() == plaintext diff --git a/demos/sm4_cbc_padding.py b/demos/sm4_cbc_padding.py new file mode 100644 index 0000000..9f96052 --- /dev/null +++ b/demos/sm4_cbc_padding.py @@ -0,0 +1,21 @@ +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. See the LICENSE file +# in the root of this repository for complete details. + +import binascii +import os + +from tongsuopy.crypto.ciphers import Cipher, algorithms, modes + +key = os.urandom(16) +iv = os.urandom(16) +plaintext = "hello" + +cipher = Cipher(algorithms.SM4(key), modes.CBC(iv), padding=True) + +enc = cipher.encryptor() +ciphertext = enc.update(plaintext.encode()) + enc.finalize() + +print( + f"SM4-CBC\nkey={binascii.hexlify(key).decode()}\niv={binascii.hexlify(iv).decode()}\nplaintext={plaintext}\nciphertext={binascii.hexlify(ciphertext).decode()}" +) diff --git a/demos/sm4_ecb.py b/demos/sm4_ecb.py new file mode 100644 index 0000000..07ae66e --- /dev/null +++ b/demos/sm4_ecb.py @@ -0,0 +1,27 @@ +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. See the LICENSE file +# in the root of this repository for complete details. + +import binascii + +from tongsuopy.crypto.ciphers import Cipher, algorithms, modes + +key = "0123456789ABCDEFFEDCBA9876543210" +plaintext = "0123456789ABCDEFFEDCBA9876543210" +ciphertext = "681EDF34D206965E86B3E94F536E4246" + +cipher = Cipher(algorithms.SM4(binascii.unhexlify(key)), modes.ECB()) + +enc = cipher.encryptor() +actual_ciphertext = enc.update(binascii.unhexlify(plaintext)) +actual_ciphertext += enc.finalize() + +assert binascii.hexlify(actual_ciphertext).decode().upper() == ciphertext + +print(f"SM4-ECB\nkey={key}\nplaintext={plaintext}\nciphertext={ciphertext}") + +dec = cipher.decryptor() +actual_plaintext = dec.update(binascii.unhexlify(ciphertext)) +actual_plaintext += dec.finalize() + +assert binascii.hexlify(actual_plaintext).decode().upper() == plaintext diff --git a/demos/sm4_gcm.py b/demos/sm4_gcm.py index 7805fb2..0f3f891 100644 --- a/demos/sm4_gcm.py +++ b/demos/sm4_gcm.py @@ -6,41 +6,44 @@ from tongsuopy.crypto.ciphers import Cipher, algorithms, modes -key = b"0123456789ABCDEFFEDCBA9876543210" -iv = b"00001234567800000000ABCD" -aad = b"FEEDFACEDEADBEEFFEEDFACEDEADBEEFABADDAD2" -tag = b"83DE3541E4C2B58177E065A9BF7B62EC" +key = "0123456789ABCDEFFEDCBA9876543210" +iv = "00001234567800000000ABCD" +aad = "FEEDFACEDEADBEEFFEEDFACEDEADBEEFABADDAD2" +tag = "83DE3541E4C2B58177E065A9BF7B62EC" plaintext = ( - b"AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCC" - b"DDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFF" - b"EEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCC" + "DDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFF" + "EEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAA" ) ciphertext = ( - b"17F399F08C67D5EE19D0DC9969C4BB7D5FD46FD37564890" - b"69157B282BB200735D82710CA5C22F0CCFA7CBF93D496AC" - b"15A56834CBCF98C397B4024A2691233B8D" + "17F399F08C67D5EE19D0DC9969C4BB7D5FD46FD37564890" + "69157B282BB200735D82710CA5C22F0CCFA7CBF93D496AC" + "15A56834CBCF98C397B4024A2691233B8D" ) +print( + f"SM4-GCM\nkey={key}\niv={iv}\naad={aad}\nplaintext={plaintext}\ntag={tag}\nciphertext={ciphertext}" +) -c = Cipher( +cipher = Cipher( algorithms.SM4(binascii.unhexlify(key)), modes.GCM(binascii.unhexlify(iv)) ) -enc = c.encryptor() +enc = cipher.encryptor() enc.authenticate_additional_data(binascii.unhexlify(aad)) actual_ciphertext = enc.update(binascii.unhexlify(plaintext)) actual_ciphertext += enc.finalize() -assert binascii.hexlify(enc.tag).upper() == tag -assert binascii.hexlify(actual_ciphertext).upper() == ciphertext +assert binascii.hexlify(enc.tag).decode().upper() == tag +assert binascii.hexlify(actual_ciphertext).decode().upper() == ciphertext -c = Cipher( +cipher = Cipher( algorithms.SM4(binascii.unhexlify(key)), modes.GCM(binascii.unhexlify(iv), binascii.unhexlify(tag)), ) -dec = c.decryptor() +dec = cipher.decryptor() dec.authenticate_additional_data(binascii.unhexlify(aad)) actual_plaintext = dec.update(binascii.unhexlify(ciphertext)) actual_plaintext += dec.finalize() -assert binascii.hexlify(actual_plaintext).upper() == plaintext +assert binascii.hexlify(actual_plaintext).decode().upper() == plaintext diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index e3349f0..0000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -click -tox >= 2.4.1 -twine >= 1.8.0 --e .[test,pep8test] - diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..ed0a085 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,137 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import pathlib + +import nox + +try: + import tomllib +except ImportError: + import tomli as tomllib # type: ignore[import-not-found,no-redef] + +nox.options.reuse_existing_virtualenvs = True + + +def install( + session: nox.Session, + *args: str, + verbose: bool = True, +) -> None: + if verbose: + args += ("-v",) + session.install( + "-c", + "ci-constraints-requirements.txt", + *args, + silent=False, + ) + + +def load_pyproject_toml() -> dict: + with (pathlib.Path(__file__).parent / "pyproject.toml").open("rb") as f: + return tomllib.load(f) + + +@nox.session +@nox.session(name="tests-randomorder") +@nox.session(name="tests-nocoverage") +def tests(session: nox.Session) -> None: + extras = "test" + if session.name == "tests-randomorder": + extras += ",test-randomorder" + + install(session, f".[{extras}]") + + session.run("pip", "list") + + if session.name != "tests-nocoverage": + cov_args = [ + "--cov=tongsuopy", + "--cov=tests", + ] + else: + cov_args = [] + + if session.posargs: + tests = session.posargs + else: + tests = ["tests/"] + + session.run( + "pytest", + "-n", + "auto", + "--dist=worksteal", + *cov_args, + "--durations=10", + *tests, + ) + + +@nox.session +def flake(session: nox.Session) -> None: + # TODO: Ideally there'd be a pip flag to install just our dependencies, + # but not install us. + pyproject_data = load_pyproject_toml() + install( + session, + *pyproject_data["build-system"]["requires"], + *pyproject_data["project"]["optional-dependencies"]["pep8test"], + *pyproject_data["project"]["optional-dependencies"]["test"], + *pyproject_data["project"]["optional-dependencies"]["nox"], + ) + + session.run("ruff", "check", ".") + session.run("ruff", "format", "--check", ".") + session.run( + "mypy", + "src/tongsuopy/", + "tests/", + "release.py", + "noxfile.py", + ) + session.run("check-sdist", "--no-isolation") + + +@nox.session(venv_backend="uv") +def local(session): + pyproject_data = load_pyproject_toml() + install( + session, + *pyproject_data["build-system"]["requires"], + *pyproject_data["project"]["optional-dependencies"]["pep8test"], + *pyproject_data["project"]["optional-dependencies"]["test"], + *pyproject_data["project"]["optional-dependencies"]["nox"], + verbose=False, + ) + + session.run("ruff", "format", ".") + session.run("ruff", "check", ".") + + session.run( + "mypy", + "demos", + "src/tongsuopy/", + "tests/", + "release.py", + "noxfile.py", + "setup.py", + ) + + if session.posargs: + tests = session.posargs + else: + tests = ["tests/"] + + session.run( + "pytest", + "-n", + "auto", + "--dist=worksteal", + "--durations=10", + *tests, + ) diff --git a/pyproject.toml b/pyproject.toml index c5be180..58862e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,80 @@ [build-system] +# These requirements must be kept sync with the requirements in +# ./github/requirements/build-requirements.{in,txt} requires = [ - # The minimum setuptools version is specific to the PEP 517 backend, - # and may be stricter than the version required in `setup.cfg` - "setuptools>=40.6.0,!=60.9.0", - "wheel", - # Must be kept in sync with the `install_requirements` in `setup.cfg` + # Must be kept in sync with `project.dependencies` "cffi>=1.12; platform_python_implementation != 'PyPy'", + # Needed because cffi imports distutils, and in Python 3.12, distutils has + # been removed from the stdlib, but installing setuptools puts it back. + "setuptools", ] build-backend = "setuptools.build_meta" -[tool.black] -line-length = 79 -target-version = ["py36"] +[project] +name = "tongsuopy" +authors = [ + {name = "Tongsuo Project Authors", email = "tongsuo-dev@tongsuo.net"} +] +description = "Tongsuo Python SDK" +readme = "README.md" +license = {text = "Apache-2.0 OR BSD-3-Clause"} +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX", + "Operating System :: POSIX :: BSD", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Security :: Cryptography", +] +requires-python = ">=3.7" +dependencies = [ + # Must be kept in sync with `build-system.requires` + "cffi>=1.12; platform_python_implementation != 'PyPy'", +] +dynamic = ["version"] + +[project.urls] +homepage = "https://github.com/Tongsuo-Project/tongsuo-python-sdk" +documentation = "https://www.tongsuo.net/docs/" +source = "https://github.com/Tongsuo-Project/tongsuo-python-sdk" +issues = "https://github.com/Tongsuo-Project/tongsuo-python-sdk/issues" + +[project.optional-dependencies] +# All the following are used for our own testing. +nox = ["nox"] +test = [ + "pytest >=6.2.0", + "pytest-benchmark", + "pytest-cov", + "pytest-xdist", + "pytest-subtests", + "pretend", + "certifi", +] +test-randomorder = ["pytest-randomly"] +sdist = ["build"] +# `click` included because its needed to type check `release.py` +pep8test = ["ruff", "mypy", "check-sdist", "click"] [tool.pytest.ini_options] -addopts = "-r s --capture=no --strict-markers" +addopts = "-r s --capture=no --strict-markers --benchmark-disable" +console_output_style = "progress-even-when-capture-no" markers = [ "skip_fips: this test is not executed in FIPS mode", "supported: parametrized test requiring only_if and skip_message", @@ -26,6 +86,8 @@ check_untyped_defs = true no_implicit_reexport = true warn_redundant_casts = true warn_unused_ignores = true +warn_unused_configs = true +strict_equality = true [[tool.mypy.overrides]] module = [ @@ -44,9 +106,9 @@ source = [ [tool.coverage.paths] source = [ "src/tongsuopy", - "*.tox/*/lib*/python*/site-packages/tongsuopy", - "*.tox\\*\\Lib\\site-packages\\tongsuopy", - "*.tox/pypy/site-packages/tongsuopy", + "*.nox/*/lib*/python*/site-packages/tongsuopy", + "*.nox\\*\\Lib\\site-packages\\tongsuopy", + "*.nox/pypy/site-packages/tongsuopy", ] tests =[ "tests/", @@ -60,3 +122,24 @@ exclude_lines = [ "@typing.overload", "if typing.TYPE_CHECKING", ] + +[tool.ruff] +line-length = 79 + +lint.ignore = ['N818'] +lint.select = ['E', 'F', 'I', 'N', 'W', 'UP', 'RUF'] + +[tool.ruff.lint.isort] +known-first-party = ["tongsuopy", "tests"] + +[tool.check-sdist] +git-only = [ + "infra/", + "ci-constraints-requirements.txt", + "release.py", + ".gitattributes", + ".gitignore", +] + +[tool.setuptools.dynamic] +version = {attr = "tongsuopy.__version__"} diff --git a/release.py b/release.py new file mode 100644 index 0000000..9f26726 --- /dev/null +++ b/release.py @@ -0,0 +1,75 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import pathlib +import re +import subprocess + +import click +import tomllib +from packaging.version import Version + + +def run(*args: str) -> None: + print(f"[running] {list(args)}") + subprocess.check_call(list(args)) + + +@click.group() +def cli(): + pass + + +@cli.command() +def release() -> None: + base_dir = pathlib.Path(__file__).parent + with (base_dir / "pyproject.toml").open("rb") as f: + pyproject = tomllib.load(f) + version = pyproject["project"]["version"] + + if Version(version).is_prerelease: + raise RuntimeError( + f"Can't release, pyproject.toml version is pre-release: {version}" + ) + + # Tag and push the tag (this will trigger the wheel builder in Actions) + run("git", "tag", "-s", version, "-m", f"{version} release") + run( + "git", + "push", + "--tags", + "git@github.com:Tongsuo-Project/tongsuo-python-sdk.git", + ) + + +def replace_pattern(p: pathlib.Path, pattern: str, replacement: str) -> None: + content = p.read_text() + match = re.search(pattern, content, re.MULTILINE) + assert match is not None + + start, end = match.span() + new_content = content[:start] + replacement + content[end:] + p.write_text(new_content) + + +def replace_version( + p: pathlib.Path, variable_name: str, new_version: str +) -> None: + replace_pattern( + p, rf"^{variable_name}\s*=\s*.*$", f'{variable_name} = "{new_version}"' + ) + + +@cli.command() +@click.argument("new_version") +def bump_version(new_version: str) -> None: + base_dir = pathlib.Path(__file__).parent + + replace_version( + base_dir / "src/tongsuopy/__about__.py", "__version__", new_version + ) + + +if __name__ == "__main__": + cli() diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 6d46e54..0000000 --- a/setup.cfg +++ /dev/null @@ -1,72 +0,0 @@ -[metadata] -name = tongsuopy -version = attr: tongsuopy.__version__ -description = Tongsuo Python SDK -long_description = file: README.md -long_description_content_type = text/markdown -license = Apache-2.0 -url = https://github.com/Tongsuo-Project/tongsuo-python-sdk -author = Tongsuo Project Authors -author_email = tongsuo-dev@tongsuo.net -project_urls = - Documentation=https://www.yuque.com/tsdoc/ts - Source=https://github.com/Tongsuo-Project/tongsuo-python-sdk - Issues=https://github.com/Tongsuo-Project/tongsuo-python-sdk/issues -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Developers - License :: OSI Approved :: Apache Software License - Natural Language :: English - Operating System :: MacOS :: MacOS X - Operating System :: POSIX - Operating System :: POSIX :: BSD - Operating System :: POSIX :: Linux - Operating System :: Microsoft :: Windows - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: Implementation :: CPython - Programming Language :: Python :: Implementation :: PyPy - Topic :: Security :: Cryptography - -[options] -python_requires = >=3.6 -include_package_data = True -zip_safe = False -package_dir = - =src -packages = find: -# `install_requires` must be kept in sync with `pyproject.toml` -install_requires = - cffi >=1.12 - -[options.packages.find] -where = src -exclude = - _cffi - _cffi.* - -[options.extras_require] -test = - pytest>=6.2.0 - pytest-benchmark - pytest-cov - pytest-subtests - pytest-xdist -pep8test = - black - flake8 - flake8-import-order - pep8-naming - -[flake8] -ignore = E203,E211,W503,W504,N818 -exclude = .tox,*.egg,.git,_build,.hypothesis,.venv -select = E,W,F,N,I -application-import-names = tongsuopy,tests diff --git a/setup.py b/setup.py index a6df7a1..2097fe7 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ import sys import warnings -from setuptools import setup +from setuptools import setup # type: ignore # distutils emits this warning if you pass `setup()` an unknown option. This # is what happens if you somehow run this file without `cffi` installed: diff --git a/src/_cffi/build_tongsuo.py b/src/_cffi/build_tongsuo.py index 8fd3181..2a7f163 100644 --- a/src/_cffi/build_tongsuo.py +++ b/src/_cffi/build_tongsuo.py @@ -42,7 +42,7 @@ def _extra_objects(platform): suffix = ".a" return [ - os.path.join(lib_dir, "libssl{}".format(suffix)), + os.path.join(lib_dir, f"libssl{suffix}"), os.path.join(lib_dir, "libcrypto{}").format(suffix), ] diff --git a/src/_cffi/utils.py b/src/_cffi/utils.py index 4eac206..84c7562 100644 --- a/src/_cffi/utils.py +++ b/src/_cffi/utils.py @@ -9,7 +9,6 @@ from cffi import FFI - # Load the tongsuopy/__about__ to get the current package version base_src = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) about = {} diff --git a/src/tongsuopy/__about__.py b/src/tongsuopy/__about__.py index bfe0679..b19731f 100644 --- a/src/tongsuopy/__about__.py +++ b/src/tongsuopy/__about__.py @@ -11,4 +11,4 @@ __version__ = "1.0.1" __author__ = "The Tongsuo Project Authors" -__copyright__ = "Copyright 2023 {}. All Rights Reserved.".format(__author__) +__copyright__ = f"Copyright 2023-2024 {__author__}. All Rights Reserved." diff --git a/src/tongsuopy/__init__.py b/src/tongsuopy/__init__.py index 2e48486..0f4a081 100644 --- a/src/tongsuopy/__init__.py +++ b/src/tongsuopy/__init__.py @@ -12,7 +12,6 @@ ) from tongsuopy.crypto.utils import CryptographyDeprecationWarning - __all__ = [ "__version__", "__author__", diff --git a/src/tongsuopy/backends/tongsuo/aead.py b/src/tongsuopy/backends/tongsuo/aead.py index cdea2cc..c3f532c 100644 --- a/src/tongsuopy/backends/tongsuo/aead.py +++ b/src/tongsuopy/backends/tongsuo/aead.py @@ -6,12 +6,11 @@ from tongsuopy.crypto.exceptions import InvalidTag - if typing.TYPE_CHECKING: from tongsuopy.backends.tongsuo.backend import Backend from tongsuopy.crypto.ciphers.aead import ( - SM4GCM, SM4CCM, + SM4GCM, ) _AEAD_TYPES = typing.Union[ diff --git a/src/tongsuopy/backends/tongsuo/backend.py b/src/tongsuopy/backends/tongsuo/backend.py index b6374d7..796266d 100644 --- a/src/tongsuopy/backends/tongsuo/backend.py +++ b/src/tongsuopy/backends/tongsuo/backend.py @@ -27,12 +27,11 @@ CTR, ECB, GCM, - Mode, OFB, + Mode, ) from tongsuopy.crypto.exceptions import UnsupportedAlgorithm, _Reasons - _MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"]) @@ -53,10 +52,8 @@ def __init__(self): self._register_default_ciphers() def __repr__(self) -> str: - return "".format( - self.openssl_version_text(), - self._binding._legacy_provider_loaded, - ) + return f"" def openssl_assert( self, @@ -80,14 +77,18 @@ def openssl_version_number(self) -> int: return self._lib.OpenSSL_version_num() def create_symmetric_encryption_ctx( - self, cipher: CipherAlgorithm, mode: Mode + self, cipher: CipherAlgorithm, mode: Mode, padding: bool ) -> _CipherContext: - return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) + return _CipherContext( + self, cipher, mode, padding, _CipherContext._ENCRYPT + ) def create_symmetric_decryption_ctx( - self, cipher: CipherAlgorithm, mode: Mode + self, cipher: CipherAlgorithm, mode: Mode, padding: bool ) -> _CipherContext: - return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) + return _CipherContext( + self, cipher, mode, padding, _CipherContext._DECRYPT + ) def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool: try: @@ -100,9 +101,7 @@ def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool: def register_cipher_adapter(self, cipher_cls, mode_cls, adapter): if (cipher_cls, mode_cls) in self._cipher_registry: raise ValueError( - "Duplicate registration for: {} {}.".format( - cipher_cls, mode_cls - ) + f"Duplicate registration for: {cipher_cls} {mode_cls}." ) self._cipher_registry[cipher_cls, mode_cls] = adapter @@ -349,8 +348,8 @@ def _load_key( else: assert userdata.error == -2 raise ValueError( - "Passwords longer than {} bytes are not supported " - "by this backend.".format(userdata.maxsize - 1) + f"Passwords longer than {userdata.maxsize - 1} bytes " + f"are not supported by this backend." ) else: self._handle_key_loading_error() @@ -471,7 +470,7 @@ def _elliptic_curve_to_nid(self, curve: ec.EllipticCurve) -> int: curve_nid = self._lib.OBJ_sn2nid(curve_name.encode()) if curve_nid == self._lib.NID_undef: raise UnsupportedAlgorithm( - "{} is not a supported elliptic curve".format(curve.name), + f"{curve.name} is not a supported elliptic curve", _Reasons.UNSUPPORTED_ELLIPTIC_CURVE, ) return curve_nid @@ -521,7 +520,7 @@ def generate_elliptic_curve_private_key( return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) else: raise UnsupportedAlgorithm( - "Backend object does not support {}.".format(curve.name), + f"Backend object does not support {curve.name}.", _Reasons.UNSUPPORTED_ELLIPTIC_CURVE, ) diff --git a/src/tongsuopy/backends/tongsuo/binding.py b/src/tongsuopy/backends/tongsuo/binding.py index 74a73c6..31bc646 100644 --- a/src/tongsuopy/backends/tongsuo/binding.py +++ b/src/tongsuopy/backends/tongsuo/binding.py @@ -14,10 +14,12 @@ from tongsuopy.backends.tongsuo._conditional import CONDITIONAL_NAMES from tongsuopy.crypto.exceptions import InternalError -_OpenSSLErrorWithText = typing.NamedTuple( - "_OpenSSLErrorWithText", - [("code", int), ("lib", int), ("reason", int), ("reason_text", bytes)], -) + +class _OpenSSLErrorWithText(typing.NamedTuple): + code: int + lib: int + reason: int + reason_text: bytes class _OpenSSLError: @@ -94,9 +96,8 @@ def _openssl_assert( "OpenSSL try disabling it before reporting a bug. Otherwise " "please file an issue at " "https://github.com/Tongsuo-Project/tongsuo-python-sdk/issues " - "with information on how to reproduce this. ({0!r})".format( - errors_with_text - ), + "with information on how to reproduce this. " + f"({errors_with_text!r})", errors_with_text, ) @@ -222,8 +223,8 @@ def _verify_package_version(version: str) -> None: "The version of tongsuopy does not match the loaded shared " "object. This can happen if you have multiple copies of tongsuopy " "installed in your Python path. Please try creating a new virtual " - "environment to resolve this issue. Loaded python version: {}, " - "shared object version: {}".format(version, so_package_version) + "environment to resolve this issue. Loaded python version: " + f"{version}, shared object version: {so_package_version}" ) diff --git a/src/tongsuopy/backends/tongsuo/ciphers.py b/src/tongsuopy/backends/tongsuo/ciphers.py index b2f8b35..830621e 100644 --- a/src/tongsuopy/backends/tongsuo/ciphers.py +++ b/src/tongsuopy/backends/tongsuo/ciphers.py @@ -12,7 +12,6 @@ _Reasons, ) - if typing.TYPE_CHECKING: from tongsuopy.backends.tongsuo.backend import Backend @@ -23,7 +22,7 @@ class _CipherContext: _MAX_CHUNK_SIZE = 2**30 - 1 def __init__( - self, backend: "Backend", cipher, mode, operation: int + self, backend: "Backend", cipher, mode, padding, operation: int ) -> None: self._backend = backend self._cipher = cipher @@ -46,22 +45,21 @@ def __init__( adapter = registry[type(cipher), type(mode)] except KeyError: raise UnsupportedAlgorithm( - "cipher {} in {} mode is not supported " - "by this backends.".format( - cipher.name, mode.name if mode else mode - ), + f"cipher {cipher.name} in {mode.name if mode else mode} mode " + "is not supported by this backends.", _Reasons.UNSUPPORTED_CIPHER, ) evp_cipher = adapter(self._backend, cipher, mode) if evp_cipher == self._backend._ffi.NULL: - msg = "cipher {0.name} ".format(cipher) + msg = f"cipher {cipher.name} " if mode is not None: - msg += "in {0.name} mode ".format(mode) + msg += f"in {mode.name} mode " msg += ( "is not supported by this backends (Your version of OpenSSL " - "may be too old. Current version: {}.)" - ).format(self._backend.openssl_version_text()) + "may be too old. Current version: " + f"{self._backend.openssl_version_text()}.)" + ) raise UnsupportedAlgorithm(msg, _Reasons.UNSUPPORTED_CIPHER) if isinstance(mode, modes.ModeWithInitializationVector): @@ -138,9 +136,11 @@ def __init__( self._backend.openssl_assert(res != 0, errors=errors) - # We purposely disable padding here as it's handled higher up in the - # API. - self._backend._lib.EVP_CIPHER_CTX_set_padding(ctx, 0) + if padding: + self._backend._lib.EVP_CIPHER_CTX_set_padding(ctx, 1) + else: + self._backend._lib.EVP_CIPHER_CTX_set_padding(ctx, 0) + self._ctx = ctx def update(self, data: bytes) -> bytes: @@ -152,8 +152,10 @@ def update_into(self, data: bytes, buf: bytes) -> int: total_data_len = len(data) if len(buf) < (total_data_len + self._block_size_bytes - 1): raise ValueError( - "buffer must be at least {} bytes for this " - "payload".format(len(data) + self._block_size_bytes - 1) + "buffer must be at least " + f"{len(data) + self._block_size_bytes - 1} bytes for this " + "" + "payload" ) data_processed = 0 @@ -246,15 +248,13 @@ def finalize_with_tag(self, tag: bytes) -> bytes: tag_len = len(tag) if tag_len < self._mode._min_tag_length: raise ValueError( - "Authentication tag must be {} bytes or longer.".format( - self._mode._min_tag_length - ) + f"Authentication tag must be {self._mode._min_tag_length} " + "bytes or longer." ) elif tag_len > self._block_size_bytes: raise ValueError( - "Authentication tag cannot be more than {} bytes.".format( - self._block_size_bytes - ) + "Authentication tag cannot be more than " + f"{self._block_size_bytes} bytes." ) res = self._backend._lib.EVP_CIPHER_CTX_ctrl( self._ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag diff --git a/src/tongsuopy/backends/tongsuo/ec.py b/src/tongsuopy/backends/tongsuo/ec.py index 3e3aacd..cd40c3b 100644 --- a/src/tongsuopy/backends/tongsuo/ec.py +++ b/src/tongsuopy/backends/tongsuo/ec.py @@ -7,8 +7,7 @@ from tongsuopy.backends.tongsuo.utils import ( _evp_pkey_derive, ) -from tongsuopy.crypto import hashes -from tongsuopy.crypto import serialization +from tongsuopy.crypto import hashes, serialization from tongsuopy.crypto.asymciphers import ec from tongsuopy.crypto.exceptions import ( InvalidSignature, @@ -16,7 +15,6 @@ _Reasons, ) - if typing.TYPE_CHECKING: from tongsuopy.backends.tongsuo.backend import Backend @@ -95,7 +93,7 @@ def _sn_to_elliptic_curve(backend: "Backend", sn: str) -> ec.EllipticCurve: return ec._CURVE_TYPES[sn]() except KeyError: raise UnsupportedAlgorithm( - "{} is not a supported elliptic curve".format(sn), + f"{sn} is not a supported elliptic curve", _Reasons.UNSUPPORTED_ELLIPTIC_CURVE, ) @@ -168,9 +166,7 @@ def _ecdsa_sig_setup( evp_md = backend._evp_md_from_algorithm(algorithm) if evp_md == backend._ffi.NULL: raise UnsupportedAlgorithm( - "{} is not a supported hash on this backend.".format( - algorithm.name - ), + f"{algorithm.name} is not a supported hash on this backend.", _Reasons.UNSUPPORTED_HASH, ) @@ -218,7 +214,7 @@ def _sm2_crypt_setup( action = ("encrypt", "decrypt")[ exec_func == backend._lib.EVP_PKEY_decrypt ] - raise ValueError("Failed to {action}".format(action=action), errors) + raise ValueError(f"Failed to {action}", errors) return backend._ffi.buffer(buf)[: buflen[0]] diff --git a/src/tongsuopy/backends/tongsuo/hashes.py b/src/tongsuopy/backends/tongsuo/hashes.py index 987b465..b79d57d 100644 --- a/src/tongsuopy/backends/tongsuo/hashes.py +++ b/src/tongsuopy/backends/tongsuo/hashes.py @@ -7,7 +7,6 @@ from tongsuopy.crypto import hashes from tongsuopy.crypto.exceptions import UnsupportedAlgorithm, _Reasons - if typing.TYPE_CHECKING: from tongsuopy.backends.tongsuo.backend import Backend @@ -28,9 +27,8 @@ def __init__( evp_md = self._backend._evp_md_from_algorithm(algorithm) if evp_md == self._backend._ffi.NULL: raise UnsupportedAlgorithm( - "{} is not a supported hash on this backend.".format( - algorithm.name - ), + f"{algorithm.name} is not a supported hash on this " + "backend.", _Reasons.UNSUPPORTED_HASH, ) res = self._backend._lib.EVP_DigestInit_ex( diff --git a/src/tongsuopy/backends/tongsuo/utils.py b/src/tongsuopy/backends/tongsuo/utils.py index 9db8816..ebbeae2 100644 --- a/src/tongsuopy/backends/tongsuo/utils.py +++ b/src/tongsuopy/backends/tongsuo/utils.py @@ -4,7 +4,6 @@ import typing - if typing.TYPE_CHECKING: from tongsuopy.backends.tongsuo.backend import Backend diff --git a/src/tongsuopy/crypto/_serialization.py b/src/tongsuopy/crypto/_serialization.py index 235335e..a33109d 100644 --- a/src/tongsuopy/crypto/_serialization.py +++ b/src/tongsuopy/crypto/_serialization.py @@ -71,7 +71,7 @@ class NoEncryption(KeySerializationEncryption): pass -class KeySerializationEncryptionBuilder(object): +class KeySerializationEncryptionBuilder: def __init__( self, format: PrivateFormat, diff --git a/src/tongsuopy/crypto/asymciphers/dh.py b/src/tongsuopy/crypto/asymciphers/dh.py index bf2cf1f..d145989 100644 --- a/src/tongsuopy/crypto/asymciphers/dh.py +++ b/src/tongsuopy/crypto/asymciphers/dh.py @@ -8,7 +8,6 @@ from tongsuopy.crypto import _serialization - _MIN_MODULUS_SIZE = 512 @@ -24,7 +23,7 @@ def __init__(self, p: int, g: int, q: typing.Optional[int] = None) -> None: if p.bit_length() < _MIN_MODULUS_SIZE: raise ValueError( - "p (modulus) must be at least {}-bit".format(_MIN_MODULUS_SIZE) + f"p (modulus) must be at least {_MIN_MODULUS_SIZE}-bit" ) self._p = p diff --git a/src/tongsuopy/crypto/asymciphers/ec.py b/src/tongsuopy/crypto/asymciphers/ec.py index f906e8b..ddc6ab5 100644 --- a/src/tongsuopy/crypto/asymciphers/ec.py +++ b/src/tongsuopy/crypto/asymciphers/ec.py @@ -263,8 +263,8 @@ def __hash__(self) -> int: def __repr__(self) -> str: return ( - "".format(self) + f"" ) diff --git a/src/tongsuopy/crypto/cipheralgorithm.py b/src/tongsuopy/crypto/cipheralgorithm.py index 9981f54..6da346e 100644 --- a/src/tongsuopy/crypto/cipheralgorithm.py +++ b/src/tongsuopy/crypto/cipheralgorithm.py @@ -6,7 +6,6 @@ import abc import typing - # This exists to break an import cycle. It is normally accessible from the # ciphers module. diff --git a/src/tongsuopy/crypto/ciphers/__init__.py b/src/tongsuopy/crypto/ciphers/__init__.py index 2cbb9b5..aceb6d3 100644 --- a/src/tongsuopy/crypto/ciphers/__init__.py +++ b/src/tongsuopy/crypto/ciphers/__init__.py @@ -15,7 +15,6 @@ CipherContext, ) - __all__ = [ "Cipher", "CipherAlgorithm", diff --git a/src/tongsuopy/crypto/ciphers/algorithms.py b/src/tongsuopy/crypto/ciphers/algorithms.py index 8859520..c10a63a 100644 --- a/src/tongsuopy/crypto/ciphers/algorithms.py +++ b/src/tongsuopy/crypto/ciphers/algorithms.py @@ -16,9 +16,7 @@ def _verify_key_size(algorithm: CipherAlgorithm, key: bytes) -> bytes: # Verify that the key size matches the expected key size if len(key) * 8 not in algorithm.key_sizes: raise ValueError( - "Invalid key size ({}) for {}.".format( - len(key) * 8, algorithm.name - ) + f"Invalid key size ({len(key) * 8}) for {algorithm.name}." ) return key diff --git a/src/tongsuopy/crypto/ciphers/base.py b/src/tongsuopy/crypto/ciphers/base.py index 95adf9b..b6ec2bf 100644 --- a/src/tongsuopy/crypto/ciphers/base.py +++ b/src/tongsuopy/crypto/ciphers/base.py @@ -14,7 +14,6 @@ NotYetFinalized, ) - if typing.TYPE_CHECKING: from tongsuopy.backends.tongsuo.ciphers import ( _CipherContext as _BackendCipherContext, @@ -79,6 +78,7 @@ def __init__( self, algorithm: CipherAlgorithm, mode: Mode, + padding: bool = False, backend: typing.Any = None, ): if not isinstance(algorithm, CipherAlgorithm): @@ -92,18 +92,17 @@ def __init__( self.algorithm = algorithm self.mode = mode + self.padding = padding @typing.overload def encryptor( self: "Cipher[modes.ModeWithAuthenticationTag]", - ) -> AEADEncryptionContext: - ... + ) -> AEADEncryptionContext: ... @typing.overload def encryptor( self: "_CIPHER_TYPE", - ) -> CipherContext: - ... + ) -> CipherContext: ... def encryptor(self): if isinstance(self.mode, modes.ModeWithAuthenticationTag): @@ -114,27 +113,25 @@ def encryptor(self): from tongsuopy.backends.tongsuo import backend ctx = backend.create_symmetric_encryption_ctx( - self.algorithm, self.mode + self.algorithm, self.mode, self.padding ) return self._wrap_ctx(ctx, encrypt=True) @typing.overload def decryptor( self: "Cipher[modes.ModeWithAuthenticationTag]", - ) -> AEADDecryptionContext: - ... + ) -> AEADDecryptionContext: ... @typing.overload def decryptor( self: "_CIPHER_TYPE", - ) -> CipherContext: - ... + ) -> CipherContext: ... def decryptor(self): from tongsuopy.backends.tongsuo import backend ctx = backend.create_symmetric_decryption_ctx( - self.algorithm, self.mode + self.algorithm, self.mode, self.padding ) return self._wrap_ctx(ctx, encrypt=False) @@ -205,9 +202,8 @@ def _check_limit(self, data_size: int) -> None: self._bytes_processed += data_size if self._bytes_processed > self._ctx._mode._MAX_ENCRYPTED_BYTES: raise ValueError( - "{} has a maximum encrypted byte limit of {}".format( - self._ctx._mode.name, self._ctx._mode._MAX_ENCRYPTED_BYTES - ) + f"{self._ctx._mode.name} has a maximum encrypted byte limit " + f"of {self._ctx._mode._MAX_ENCRYPTED_BYTES}" ) def update(self, data: bytes) -> bytes: @@ -239,9 +235,8 @@ def authenticate_additional_data(self, data: bytes) -> None: self._aad_bytes_processed += len(data) if self._aad_bytes_processed > self._ctx._mode._MAX_AAD_BYTES: raise ValueError( - "{} has a maximum AAD byte limit of {}".format( - self._ctx._mode.name, self._ctx._mode._MAX_AAD_BYTES - ) + f"{self._ctx._mode.name} has a maximum AAD byte limit of " + f"{self._ctx._mode._MAX_AAD_BYTES}" ) self._ctx.authenticate_additional_data(data) diff --git a/src/tongsuopy/crypto/ciphers/modes.py b/src/tongsuopy/crypto/ciphers/modes.py index 3c7a108..056e181 100644 --- a/src/tongsuopy/crypto/ciphers/modes.py +++ b/src/tongsuopy/crypto/ciphers/modes.py @@ -73,9 +73,8 @@ def _check_iv_length( ) -> None: if len(self.initialization_vector) * 8 != algorithm.block_size: raise ValueError( - "Invalid IV size ({}) for {}.".format( - len(self.initialization_vector), self.name - ) + f"Invalid IV size ({len(self.initialization_vector)}) for " + f"{self.name}." ) @@ -88,9 +87,7 @@ def _check_nonce_length( _Reasons.UNSUPPORTED_CIPHER, ) if len(nonce) * 8 != algorithm.block_size: - raise ValueError( - "Invalid nonce size ({}) for {}.".format(len(nonce), name) - ) + raise ValueError(f"Invalid nonce size ({len(nonce)}) for {name}.") def _check_iv_and_key_length( @@ -238,9 +235,8 @@ def __init__( raise ValueError("min_tag_length must be >= 4") if len(tag) < min_tag_length: raise ValueError( - "Authentication tag must be {} bytes or longer.".format( - min_tag_length - ) + f"Authentication tag must be {min_tag_length} bytes or " + "longer." ) self._tag = tag self._min_tag_length = min_tag_length @@ -263,7 +259,6 @@ def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: block_size_bytes = algorithm.block_size // 8 if self._tag is not None and len(self._tag) > block_size_bytes: raise ValueError( - "Authentication tag[{}] cannot be more than {} bytes.".format( - len(self._tag), block_size_bytes - ) + f"Authentication tag[{len(self._tag)}] cannot be more than " + f"{block_size_bytes} bytes." ) diff --git a/src/tongsuopy/crypto/exceptions.py b/src/tongsuopy/crypto/exceptions.py index 8cbc6be..4433c61 100644 --- a/src/tongsuopy/crypto/exceptions.py +++ b/src/tongsuopy/crypto/exceptions.py @@ -32,7 +32,7 @@ class UnsupportedAlgorithm(Exception): def __init__( self, message: str, reason: typing.Optional[_Reasons] = None ) -> None: - super(UnsupportedAlgorithm, self).__init__(message) + super().__init__(message) self._reason = reason @@ -60,7 +60,7 @@ class InternalError(Exception): def __init__( self, msg: str, err_code: typing.List["_OpenSSLErrorWithText"] ) -> None: - super(InternalError, self).__init__(msg) + super().__init__(msg) self.err_code = err_code diff --git a/src/tongsuopy/crypto/serialization/__init__.py b/src/tongsuopy/crypto/serialization/__init__.py index 3b3c833..3ba2fed 100644 --- a/src/tongsuopy/crypto/serialization/__init__.py +++ b/src/tongsuopy/crypto/serialization/__init__.py @@ -19,7 +19,6 @@ load_pem_public_key, ) - __all__ = [ "load_der_private_key", "load_der_public_key", diff --git a/src/tongsuopy/crypto/utils.py b/src/tongsuopy/crypto/utils.py index da79280..212d8e7 100644 --- a/src/tongsuopy/crypto/utils.py +++ b/src/tongsuopy/crypto/utils.py @@ -24,14 +24,14 @@ class CryptographyDeprecationWarning(UserWarning): def _check_bytes(name: str, value: bytes) -> None: if not isinstance(value, bytes): - raise TypeError("{} must be bytes".format(name)) + raise TypeError(f"{name} must be bytes") def _check_byteslike(name: str, value: bytes) -> None: try: memoryview(value) except TypeError: - raise TypeError("{} must be bytes-like".format(name)) + raise TypeError(f"{name} must be bytes-like") def int_to_bytes(integer: int, length: typing.Optional[int] = None) -> bytes: diff --git a/src/tongsuopy/py.typed b/src/tongsuopy/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py index d7e5c3b..570766c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,7 +12,7 @@ def pytest_configure(config): def pytest_report_header(config): return "\n".join( [ - "OpenSSL: {}".format(openssl_backend.openssl_version_text()), + f"OpenSSL: {openssl_backend.openssl_version_text()}", ] ) diff --git a/tests/crypto/test_aead.py b/tests/crypto/test_aead.py index 6b54129..6952578 100644 --- a/tests/crypto/test_aead.py +++ b/tests/crypto/test_aead.py @@ -11,11 +11,11 @@ from tongsuopy.crypto.ciphers.aead import SM4CCM, SM4GCM from tongsuopy.crypto.exceptions import InvalidTag -from .utils import _load_all_params from ..utils import ( load_nist_ccm_vectors, load_nist_vectors, ) +from .utils import _load_all_params class FakeData(bytes): diff --git a/tests/crypto/test_sm2.py b/tests/crypto/test_sm2.py index 2b31fe3..3692717 100644 --- a/tests/crypto/test_sm2.py +++ b/tests/crypto/test_sm2.py @@ -41,9 +41,7 @@ class DummySignatureAlgorithm(ec.EllipticCurveSignatureAlgorithm): def _skip_curve_unsupported(backend, curve): if not backend.elliptic_curve_supported(curve): pytest.skip( - "Curve {} is not supported by this backend {}".format( - curve.name, backend - ) + f"Curve {curve.name} is not supported by this backend {backend}" ) @@ -52,9 +50,8 @@ def _skip_ecdsa_vector(backend, curve_type, hash_type): ec.ECDSA(hash_type()), curve_type() ): pytest.skip( - "ECDSA not supported with this hash {} and curve {}.".format( - hash_type().name, curve_type().name - ) + f"ECDSA not supported with this hash {hash_type().name} and curve" + f" {curve_type().name}." ) @@ -907,7 +904,8 @@ def test_from_encoded_point_empty_byte_string(self): def test_from_encoded_point_not_a_curve(self): with pytest.raises(TypeError): ec.EllipticCurvePublicKey.from_encoded_point( - "notacurve", b"\x04data" # type: ignore[arg-type] + "notacurve", # type: ignore[arg-type] + b"\x04data", ) def test_from_encoded_point_unsupported_encoding(self): diff --git a/tests/crypto/test_sm3.py b/tests/crypto/test_sm3.py index a308d30..2591dec 100644 --- a/tests/crypto/test_sm3.py +++ b/tests/crypto/test_sm3.py @@ -9,16 +9,16 @@ from tongsuopy.crypto import hashes from tongsuopy.crypto.exceptions import AlreadyFinalized, _Reasons -from .utils import generate_base_hash_test, generate_hash_test from ..doubles import DummyHashAlgorithm from ..utils import load_hash_vectors, raises_unsupported_algorithm +from .utils import generate_base_hash_test, generate_hash_test class TestHashContext: def test_hash_reject_unicode(self, backend): m = hashes.Hash(hashes.SM3(), backend=backend) with pytest.raises(TypeError): - m.update("\u00FC") # type: ignore[arg-type] + m.update("\u00fc") # type: ignore[arg-type] def test_hash_algorithm_instance(self, backend): with pytest.raises(TypeError): diff --git a/tests/crypto/test_sm4.py b/tests/crypto/test_sm4.py index d71ead4..464ba84 100644 --- a/tests/crypto/test_sm4.py +++ b/tests/crypto/test_sm4.py @@ -10,8 +10,8 @@ from tongsuopy.crypto.ciphers import algorithms, modes -from .utils import generate_encrypt_test from ..utils import load_nist_vectors +from .utils import generate_encrypt_test @pytest.mark.supported( @@ -25,7 +25,7 @@ class TestSM4ModeECB: load_nist_vectors, os.path.join("ciphers", "SM4"), ["draft-ribose-cfrg-sm4-10-ecb.txt"], - lambda key, **kwargs: algorithms.SM4(binascii.unhexlify((key))), + lambda key, **kwargs: algorithms.SM4(binascii.unhexlify(key)), lambda **kwargs: modes.ECB(), ) @@ -41,7 +41,7 @@ class TestSM4ModeCBC: load_nist_vectors, os.path.join("ciphers", "SM4"), ["draft-ribose-cfrg-sm4-10-cbc.txt"], - lambda key, **kwargs: algorithms.SM4(binascii.unhexlify((key))), + lambda key, **kwargs: algorithms.SM4(binascii.unhexlify(key)), lambda iv, **kwargs: modes.CBC(binascii.unhexlify(iv)), ) @@ -57,7 +57,7 @@ class TestSM4ModeOFB: load_nist_vectors, os.path.join("ciphers", "SM4"), ["draft-ribose-cfrg-sm4-10-ofb.txt"], - lambda key, **kwargs: algorithms.SM4(binascii.unhexlify((key))), + lambda key, **kwargs: algorithms.SM4(binascii.unhexlify(key)), lambda iv, **kwargs: modes.OFB(binascii.unhexlify(iv)), ) @@ -73,7 +73,7 @@ class TestSM4ModeCFB: load_nist_vectors, os.path.join("ciphers", "SM4"), ["draft-ribose-cfrg-sm4-10-cfb.txt"], - lambda key, **kwargs: algorithms.SM4(binascii.unhexlify((key))), + lambda key, **kwargs: algorithms.SM4(binascii.unhexlify(key)), lambda iv, **kwargs: modes.CFB(binascii.unhexlify(iv)), ) @@ -89,6 +89,6 @@ class TestSM4ModeCTR: load_nist_vectors, os.path.join("ciphers", "SM4"), ["draft-ribose-cfrg-sm4-10-ctr.txt"], - lambda key, **kwargs: algorithms.SM4(binascii.unhexlify((key))), + lambda key, **kwargs: algorithms.SM4(binascii.unhexlify(key)), lambda iv, **kwargs: modes.CTR(binascii.unhexlify(iv)), ) diff --git a/tests/crypto/test_sm4_gcm.py b/tests/crypto/test_sm4_gcm.py index 3da2ccc..38b56bc 100644 --- a/tests/crypto/test_sm4_gcm.py +++ b/tests/crypto/test_sm4_gcm.py @@ -10,8 +10,8 @@ from tongsuopy.crypto.ciphers import algorithms, base, modes -from .utils import generate_aead_test from ..utils import load_nist_vectors +from .utils import generate_aead_test @pytest.mark.supported( diff --git a/tests/doubles.py b/tests/doubles.py index 1fccc01..626adfa 100644 --- a/tests/doubles.py +++ b/tests/doubles.py @@ -2,8 +2,7 @@ # this file except in compliance with the License. See the LICENSE file # in the root of this repository for complete details. -from tongsuopy.crypto import hashes -from tongsuopy.crypto import serialization +from tongsuopy.crypto import hashes, serialization from tongsuopy.crypto.ciphers import ( CipherAlgorithm, ) diff --git a/tests/utils.py b/tests/utils.py index 4b2ba2e..a229570 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -12,10 +12,8 @@ import pytest from tests import open_vector_file - from tongsuopy.crypto.exceptions import UnsupportedAlgorithm - HashVector = collections.namedtuple("HashVector", ["message", "digest"]) KeyedHashVector = collections.namedtuple( "KeyedHashVector", ["message", "digest", "key"] @@ -66,7 +64,7 @@ def load_nist_vectors(vector_data): continue # Build our data using a simple Key = Value format - name, value = [c.strip() for c in line.split("=")] + name, value = (c.strip() for c in line.split("=")) # Some tests (PBKDF2) contain \0, which should be interpreted as a # null character rather than literal. @@ -253,7 +251,7 @@ def load_nist_ccm_vectors(vector_data): # Some of the CCM vectors have global values for this. They are always # at the top before the first section header (see: VADT, VNT, VPT) if line.startswith(("Alen", "Plen", "Nlen", "Tlen")): - name, value = [c.strip() for c in line.split("=")] + name, value = (c.strip() for c in line.split("=")) global_data[name.lower()] = int(value) continue @@ -264,11 +262,11 @@ def load_nist_ccm_vectors(vector_data): section = line[1:-1] items = [c.strip() for c in section.split(",")] for item in items: - name, value = [c.strip() for c in item.split("=")] + name, value = (c.strip() for c in item.split("=")) section_data[name.lower()] = int(value) continue - name, value = [c.strip() for c in line.split("=")] + name, value = (c.strip() for c in line.split("=")) if name.lower() in ("key", "nonce") and new_section: section_data[name.lower()] = value.encode("ascii") @@ -320,7 +318,7 @@ def load_decrypt_vectors(vector_data): if not line or line.startswith("#"): continue - name, value = [c.strip() for c in line.split("=")] + name, value = (c.strip() for c in line.split("=")) name = name.lower() if name in ["input"]: diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 2e46162..0000000 --- a/tox.ini +++ /dev/null @@ -1,31 +0,0 @@ -[tox] -minversion = 2.4 -isolated_build = True - -[testenv] -allowlist_externals = * -passenv = * -extras = - test -deps = - pytest-shard>=0.1.2 - randomorder: pytest-randomly -commands = - pip list - !nocoverage: pytest -n auto --cov=tongsuopy --cov=tests --durations=10 {posargs} tests/ - nocoverage: pytest -n auto --durations=10 {posargs} tests/ - -[testenv:flake] -basepython = python3 -extras = - pep8test - test -deps = - mypy - types-pytz - check-manifest -commands = - flake8 . - black --check . - check-manifest - mypy src/tongsuopy/ tests/